even more strongly typed returns
This commit is contained in:
		
							parent
							
								
									8cf3213267
								
							
						
					
					
						commit
						3f1458e1be
					
				
					 4 changed files with 177 additions and 61 deletions
				
			
		| 
						 | 
				
			
			@ -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…
	
	Add table
		Add a link
		
	
		Reference in a new issue