even more strongly typed returns
This commit is contained in:
parent
8cf3213267
commit
3f1458e1be
|
@ -4,7 +4,7 @@ mod methods;
|
|||
mod openapi;
|
||||
mod structs;
|
||||
|
||||
use heck::ToSnakeCase;
|
||||
use heck::{ToSnakeCase, ToPascalCase};
|
||||
use openapi::*;
|
||||
|
||||
fn main() -> eyre::Result<()> {
|
||||
|
@ -51,6 +51,9 @@ fn schema_ref_type_name(spec: &OpenApiV2, schema: &MaybeRef<Schema>) -> eyre::Re
|
|||
} else {
|
||||
None
|
||||
};
|
||||
if name == Some("LanguageStatistics") {
|
||||
eprintln!("lang stats found");
|
||||
}
|
||||
let schema = schema.deref(spec)?;
|
||||
schema_type_name(spec, name, schema)
|
||||
}
|
||||
|
@ -184,3 +187,85 @@ fn sanitize_ident(s: &str) -> String {
|
|||
}
|
||||
s
|
||||
}
|
||||
|
||||
fn schema_subtype_name(
|
||||
spec: &OpenApiV2,
|
||||
parent_name: &str,
|
||||
name: &str,
|
||||
schema: &Schema,
|
||||
ty: &mut String,
|
||||
) -> eyre::Result<bool> {
|
||||
let b = match schema {
|
||||
Schema {
|
||||
_type: Some(SchemaType::One(Primitive::Object)),
|
||||
..
|
||||
} => {
|
||||
*ty = format!("{parent_name}{}", name.to_pascal_case());
|
||||
true
|
||||
}
|
||||
Schema {
|
||||
_type: Some(SchemaType::One(Primitive::String)),
|
||||
_enum: Some(_enum),
|
||||
..
|
||||
} => {
|
||||
*ty = format!("{parent_name}{}", name.to_pascal_case());
|
||||
true
|
||||
}
|
||||
Schema {
|
||||
_type: Some(SchemaType::One(Primitive::Array)),
|
||||
items: Some(items),
|
||||
..
|
||||
} => {
|
||||
if let MaybeRef::Value { value } = &**items {
|
||||
if schema_subtype_name(spec, parent_name, name, value, ty)? {
|
||||
*ty = format!("Vec<{ty}>");
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
Ok(b)
|
||||
}
|
||||
fn schema_subtypes(
|
||||
spec: &OpenApiV2,
|
||||
parent_name: &str,
|
||||
name: &str,
|
||||
schema: &Schema,
|
||||
subtypes: &mut Vec<String>,
|
||||
) -> eyre::Result<()> {
|
||||
let b = match schema {
|
||||
Schema {
|
||||
_type: Some(SchemaType::One(Primitive::Object)),
|
||||
..
|
||||
} => {
|
||||
let name = format!("{parent_name}{}", name.to_pascal_case());
|
||||
let subtype = structs::create_struct_for_definition(spec, &name, schema)?;
|
||||
subtypes.push(subtype);
|
||||
}
|
||||
Schema {
|
||||
_type: Some(SchemaType::One(Primitive::String)),
|
||||
_enum: Some(_enum),
|
||||
..
|
||||
} => {
|
||||
let name = format!("{parent_name}{}", name.to_pascal_case());
|
||||
let subtype = structs::create_enum(&name, schema.description.as_deref(), _enum)?;
|
||||
subtypes.push(subtype);
|
||||
}
|
||||
Schema {
|
||||
_type: Some(SchemaType::One(Primitive::Array)),
|
||||
items: Some(items),
|
||||
..
|
||||
} => {
|
||||
if let MaybeRef::Value { value } = &**items {
|
||||
schema_subtypes(spec, parent_name, name, value, subtypes)?;
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{openapi::*, schema_ref_type_name};
|
||||
use eyre::{OptionExt, WrapErr};
|
||||
use heck::ToSnakeCase;
|
||||
use heck::{ToPascalCase, ToSnakeCase};
|
||||
use std::fmt::Write;
|
||||
|
||||
pub fn create_methods(spec: &OpenApiV2) -> eyre::Result<String> {
|
||||
|
@ -270,13 +270,13 @@ fn fn_return_from_op(spec: &OpenApiV2, op: &Operation) -> eyre::Result<ResponseT
|
|||
|
||||
fn response_ref_type_name(
|
||||
spec: &OpenApiV2,
|
||||
schema: &MaybeRef<Response>,
|
||||
response_ref: &MaybeRef<Response>,
|
||||
op: &Operation,
|
||||
) -> eyre::Result<ResponseType> {
|
||||
let response = schema.deref(spec)?;
|
||||
let response = response_ref.deref(spec)?;
|
||||
let mut ty = ResponseType::default();
|
||||
if response.headers.is_some() {
|
||||
let parent_name = match &schema {
|
||||
let parent_name = match &response_ref {
|
||||
MaybeRef::Ref { _ref } => _ref
|
||||
.rsplit_once("/")
|
||||
.ok_or_else(|| eyre::eyre!("invalid ref"))?
|
||||
|
@ -303,7 +303,12 @@ fn response_ref_type_name(
|
|||
(true, false, false) => {
|
||||
if let Some(schema) = &response.schema {
|
||||
ty.kind = Some(ResponseKind::Json);
|
||||
ty.body = Some(crate::schema_ref_type_name(spec, schema)?);
|
||||
let mut body = crate::schema_ref_type_name(spec, schema)?;
|
||||
if let MaybeRef::Value { value } = schema {
|
||||
let op_name = op.operation_id.as_deref().ok_or_else(|| eyre::eyre!("no operation id"))?.to_pascal_case();
|
||||
crate::schema_subtype_name(spec, &op_name, "Response", value, &mut body)?;
|
||||
}
|
||||
ty.body = Some(body);
|
||||
};
|
||||
}
|
||||
(false, _, true) => {
|
||||
|
|
|
@ -20,11 +20,17 @@ pub fn create_structs(spec: &OpenApiV2) -> eyre::Result<String> {
|
|||
let strukt = create_header_struct(name, headers)?;
|
||||
s.push_str(&strukt);
|
||||
}
|
||||
|
||||
let tys = create_response_struct(spec, name, response)?;
|
||||
s.push_str(&tys);
|
||||
}
|
||||
}
|
||||
for (_, item) in &spec.paths {
|
||||
let strukt = create_query_structs_for_path(spec, item)?;
|
||||
s.push_str(&strukt);
|
||||
|
||||
let strukt = create_response_structs(spec, item)?;
|
||||
s.push_str(&strukt);
|
||||
}
|
||||
s.push_str("\n}");
|
||||
Ok(s)
|
||||
|
@ -56,57 +62,8 @@ pub fn create_struct_for_definition(
|
|||
let field_name = crate::sanitize_ident(prop_name);
|
||||
let mut field_ty = prop_ty.clone();
|
||||
if let MaybeRef::Value { value } = &prop_schema {
|
||||
fn schema_subtypes(
|
||||
spec: &OpenApiV2,
|
||||
ty_name: &str,
|
||||
name: &str,
|
||||
schema: &Schema,
|
||||
subtypes: &mut Vec<String>,
|
||||
ty: &mut String,
|
||||
) -> eyre::Result<bool> {
|
||||
let b = match schema {
|
||||
Schema {
|
||||
_type: Some(SchemaType::One(Primitive::Object)),
|
||||
..
|
||||
} => {
|
||||
let name = format!("{ty_name}{}", name.to_pascal_case());
|
||||
let subtype = create_struct_for_definition(spec, &name, schema)?;
|
||||
subtypes.push(subtype);
|
||||
*ty = name;
|
||||
true
|
||||
}
|
||||
Schema {
|
||||
_type: Some(SchemaType::One(Primitive::String)),
|
||||
_enum: Some(_enum),
|
||||
..
|
||||
} => {
|
||||
let name = format!("{ty_name}{}", name.to_pascal_case());
|
||||
let subtype = create_enum(&name, schema.description.as_deref(), _enum)?;
|
||||
subtypes.push(subtype);
|
||||
*ty = name;
|
||||
true
|
||||
}
|
||||
Schema {
|
||||
_type: Some(SchemaType::One(Primitive::Array)),
|
||||
items: Some(items),
|
||||
..
|
||||
} => {
|
||||
if let MaybeRef::Value { value } = &**items {
|
||||
if schema_subtypes(spec, ty_name, name, value, subtypes, ty)? {
|
||||
*ty = format!("Vec<{ty}>");
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
Ok(b)
|
||||
}
|
||||
schema_subtypes(spec, name, prop_name, value, &mut subtypes, &mut field_ty)?;
|
||||
crate::schema_subtype_name(spec, name, prop_name, value, &mut field_ty)?;
|
||||
crate::schema_subtypes(spec, name, prop_name, value, &mut subtypes)?;
|
||||
}
|
||||
if field_name.ends_with("url") && field_name != "ssh_url" && field_ty == "String" {
|
||||
field_ty = "url::Url".into()
|
||||
|
@ -163,7 +120,7 @@ pub fn create_struct_for_definition(
|
|||
Ok(out)
|
||||
}
|
||||
|
||||
fn create_enum(
|
||||
pub fn create_enum(
|
||||
name: &str,
|
||||
desc: Option<&str>,
|
||||
_enum: &[serde_json::Value],
|
||||
|
@ -621,3 +578,52 @@ pub fn header_type(header: &Header) -> eyre::Result<String> {
|
|||
true,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn create_response_structs(spec: &OpenApiV2, item: &PathItem) -> eyre::Result<String> {
|
||||
let mut s = String::new();
|
||||
if let Some(op) = &item.get {
|
||||
s.push_str(&create_response_structs_for_op(spec, op).wrap_err("GET")?);
|
||||
}
|
||||
if let Some(op) = &item.put {
|
||||
s.push_str(&create_response_structs_for_op(spec, op).wrap_err("PUT")?);
|
||||
}
|
||||
if let Some(op) = &item.post {
|
||||
s.push_str(&create_response_structs_for_op(spec, op).wrap_err("POST")?);
|
||||
}
|
||||
if let Some(op) = &item.delete {
|
||||
s.push_str(&create_response_structs_for_op(spec, op).wrap_err("DELETE")?);
|
||||
}
|
||||
if let Some(op) = &item.options {
|
||||
s.push_str(&create_response_structs_for_op(spec, op).wrap_err("OPTIONS")?);
|
||||
}
|
||||
if let Some(op) = &item.head {
|
||||
s.push_str(&create_response_structs_for_op(spec, op).wrap_err("HEAD")?);
|
||||
}
|
||||
if let Some(op) = &item.patch {
|
||||
s.push_str(&create_response_structs_for_op(spec, op).wrap_err("PATCH")?);
|
||||
}
|
||||
Ok(s)
|
||||
}
|
||||
|
||||
pub fn create_response_structs_for_op(spec: &OpenApiV2, op: &Operation) -> eyre::Result<String> {
|
||||
let mut out = String::new();
|
||||
let op_name = op.operation_id.as_deref().ok_or_else(|| eyre::eyre!("no operation id"))?.to_pascal_case();
|
||||
for (_, response) in &op.responses.http_codes {
|
||||
let response = response.deref(spec)?;
|
||||
let tys = create_response_struct(spec, &op_name, response)?;
|
||||
out.push_str(&tys);
|
||||
}
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
pub fn create_response_struct(spec: &OpenApiV2, name: &str, res: &Response) -> eyre::Result<String> {
|
||||
let mut types = Vec::new();
|
||||
if let Some(MaybeRef::Value { value }) = &res.schema {
|
||||
crate::schema_subtypes(spec, name, "Response", value, &mut types)?;
|
||||
}
|
||||
let mut out = String::new();
|
||||
for ty in types {
|
||||
out.push_str(&ty);
|
||||
}
|
||||
Ok(out)
|
||||
}
|
||||
|
|
|
@ -1162,7 +1162,7 @@ impl crate::Forgejo {
|
|||
&self,
|
||||
org: &str,
|
||||
query: TeamSearchQuery,
|
||||
) -> Result<serde_json::Map<String, serde_json::Value>, ForgejoError> {
|
||||
) -> Result<TeamSearchResponse, ForgejoError> {
|
||||
let request = self
|
||||
.get(&format!("orgs/{org}/teams/search?{query}"))
|
||||
.build()?;
|
||||
|
@ -4117,7 +4117,7 @@ impl crate::Forgejo {
|
|||
&self,
|
||||
owner: &str,
|
||||
repo: &str,
|
||||
) -> Result<serde_json::Map<String, serde_json::Value>, ForgejoError> {
|
||||
) -> Result<RepoGetLanguagesResponse, ForgejoError> {
|
||||
let request = self
|
||||
.get(&format!("repos/{owner}/{repo}/languages"))
|
||||
.build()?;
|
||||
|
@ -6918,7 +6918,7 @@ impl crate::Forgejo {
|
|||
pub async fn user_search(
|
||||
&self,
|
||||
query: UserSearchQuery,
|
||||
) -> Result<serde_json::Map<String, serde_json::Value>, ForgejoError> {
|
||||
) -> Result<UserSearchResponse, ForgejoError> {
|
||||
let request = self.get(&format!("users/search?{query}")).build()?;
|
||||
let response = self.execute(request).await?;
|
||||
match response.status().as_u16() {
|
||||
|
@ -10392,6 +10392,11 @@ pub mod structs {
|
|||
})
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct LanguageStatisticsResponse {
|
||||
#[serde(flatten)]
|
||||
pub additional: std::collections::BTreeMap<String, u64>,
|
||||
}
|
||||
|
||||
pub struct ErrorHeaders {
|
||||
pub message: Option<String>,
|
||||
|
@ -11119,6 +11124,11 @@ pub mod structs {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct TeamSearchResponse {
|
||||
pub data: Option<Vec<Team>>,
|
||||
pub ok: Option<bool>,
|
||||
}
|
||||
|
||||
pub struct ListPackagesQuery {
|
||||
/// page number of results to return (1-based)
|
||||
|
@ -12363,6 +12373,11 @@ pub mod structs {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct RepoGetLanguagesResponse {
|
||||
#[serde(flatten)]
|
||||
pub additional: std::collections::BTreeMap<String, u64>,
|
||||
}
|
||||
|
||||
pub struct RepoGetRawFileOrLfsQuery {
|
||||
/// The name of the commit/branch/tag. Default the repository’s default branch (usually master)
|
||||
|
@ -13622,6 +13637,11 @@ pub mod structs {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct UserSearchResponse {
|
||||
pub data: Option<Vec<User>>,
|
||||
pub ok: Option<bool>,
|
||||
}
|
||||
|
||||
pub struct UserListActivityFeedsQuery {
|
||||
/// if true, only show actions performed by the requested user
|
||||
|
|
Loading…
Reference in a new issue