1
0
Fork 0

even more strongly typed returns

This commit is contained in:
Cyborus 2024-02-09 22:39:32 -05:00
parent 8cf3213267
commit 3f1458e1be
No known key found for this signature in database
4 changed files with 177 additions and 61 deletions

View file

@ -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(())
}

View file

@ -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) => {

View file

@ -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)
}

View file

@ -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 repositorys 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