Skip to content

Commit f67be29

Browse files
committed
refactor: somewhat cleaner approach
1 parent af28d71 commit f67be29

File tree

2 files changed

+81
-53
lines changed

2 files changed

+81
-53
lines changed

progenitor-impl/src/lib.rs

+52-9
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use proc_macro2::TokenStream;
77
use quote::quote;
88
use serde::Deserialize;
99
use thiserror::Error;
10+
use typify::{TypeDetails, TypeId};
1011
use typify::{TypeSpace, TypeSpaceSettings};
1112

1213
use crate::to_schema::ToSchema;
@@ -40,6 +41,7 @@ pub type Result<T> = std::result::Result<T, Error>;
4041

4142
pub struct Generator {
4243
type_space: TypeSpace,
44+
forms: HashSet<TypeId>,
4345
settings: GenerationSettings,
4446
uses_futures: bool,
4547
uses_websockets: bool,
@@ -163,6 +165,7 @@ impl Default for Generator {
163165
type_space: TypeSpace::new(
164166
TypeSpaceSettings::default().with_type_mod("types"),
165167
),
168+
forms: Default::default(),
166169
settings: Default::default(),
167170
uses_futures: Default::default(),
168171
uses_websockets: Default::default(),
@@ -204,6 +207,7 @@ impl Generator {
204207
Self {
205208
type_space: TypeSpace::new(&type_settings),
206209
settings: settings.clone(),
210+
forms: Default::default(),
207211
uses_futures: false,
208212
uses_websockets: false,
209213
}
@@ -262,6 +266,43 @@ impl Generator {
262266

263267
let types = self.type_space.to_stream();
264268

269+
let extra_impl = TokenStream::from_iter(
270+
self.forms
271+
.iter()
272+
.map(|type_id| {
273+
let typ = self.get_type_space().get_type(type_id).unwrap();
274+
let form_name = typ.name();
275+
let td = typ.details();
276+
let TypeDetails::Struct(tstru) = td else { unreachable!() };
277+
let properties = indexmap::IndexMap::<&'_ str, _>::from_iter(
278+
tstru
279+
.properties()
280+
.filter_map(|(prop_name, prop_id)| {
281+
self.get_type_space()
282+
.get_type(&prop_id).ok()
283+
.map(|prop_typ| (prop_name, prop_typ))
284+
})
285+
);
286+
let properties = syn::punctuated::Punctuated::<_, syn::Token![,]>::from_iter(
287+
properties
288+
.into_iter()
289+
.map(|(prop_name, prop_ty)| {
290+
let ident = quote::format_ident!("{}", prop_name);
291+
quote!{ (#prop_name, &self. #ident) }
292+
}));
293+
294+
let form_name = quote::format_ident!("{}",typ.name());
295+
296+
quote! {
297+
impl #form_name {
298+
pub fn as_form<'f>(&'f self) -> impl std::iter::Iterator<Item=(&'static str, &'f [u8])> {
299+
[#properties]
300+
.into_iter()
301+
.filter_map(|(name, val)| val.as_ref().map(|val| (name, val.as_slice())))
302+
}
303+
}
304+
}
305+
}));
265306
// Generate an implementation of a `Self::as_inner` method, if an inner
266307
// type is defined.
267308
let maybe_inner = self.settings.inner_type.as_ref().map(|inner| {
@@ -290,20 +331,20 @@ impl Generator {
290331
});
291332

292333
let client_docstring = {
293-
let mut s = format!("Client for {}", spec.info.title);
334+
let mut doc = format!("Client for {}", spec.info.title);
294335

295-
if let Some(ss) = &spec.info.description {
296-
s.push_str("\n\n");
297-
s.push_str(ss);
336+
if let Some(desc) = &spec.info.description {
337+
doc.push_str("\n\n");
338+
doc.push_str(desc);
298339
}
299-
if let Some(ss) = &spec.info.terms_of_service {
300-
s.push_str("\n\n");
301-
s.push_str(ss);
340+
if let Some(tos) = &spec.info.terms_of_service {
341+
doc.push_str("\n\n");
342+
doc.push_str(tos);
302343
}
303344

304-
s.push_str(&format!("\n\nVersion: {}", &spec.info.version));
345+
doc.push_str(&format!("\n\nVersion: {}", &spec.info.version));
305346

306-
s
347+
doc
307348
};
308349

309350
let version_str = &spec.info.version;
@@ -325,6 +366,8 @@ impl Generator {
325366
use std::convert::TryFrom;
326367

327368
#types
369+
370+
#extra_impl
328371
}
329372

330373
#[derive(Clone, Debug)]

progenitor-impl/src/method.rs

+29-44
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use indexmap::{IndexMap, IndexSet};
1010
use openapiv3::{Components, Parameter, ReferenceOr, Response, StatusCode};
1111
use proc_macro2::TokenStream;
1212
use quote::{format_ident, quote, ToTokens};
13-
use typify::{TypeId, TypeSpace};
13+
use typify::{TypeId, TypeSpace, TypeSpacePatch};
1414

1515
use crate::{
1616
template::PathTemplate,
@@ -105,7 +105,7 @@ pub struct OperationParameter {
105105
#[derive(Debug, Eq, PartialEq)]
106106
pub enum OperationParameterType {
107107
Type(TypeId),
108-
Form(IndexSet<String>),
108+
Form(TypeId),
109109
RawBody,
110110
}
111111

@@ -585,27 +585,15 @@ impl Generator {
585585
.map(|param| {
586586
let name = format_ident!("{}", param.name);
587587
match &param.typ {
588-
OperationParameterType::Type(type_id) => {
588+
OperationParameterType::Type(type_id)
589+
| OperationParameterType::Form(type_id) => {
589590
let typ = self
590591
.type_space
591592
.get_type(type_id)
592593
.expect("TypeIDs are _never_ deleted. qed")
593594
.parameter_ident_with_lifetime("a");
594595
quote! { #name: #typ}
595596
}
596-
OperationParameterType::Form(keys) => {
597-
let ts = TokenStream::from_iter(
598-
itertools::Itertools::intersperse(
599-
keys.iter().map(|form_prop_name| {
600-
let form_prop_name =
601-
format_ident!("{}", form_prop_name);
602-
quote! { #form_prop_name: Vec<u8> }
603-
}),
604-
quote! {, },
605-
),
606-
);
607-
ts
608-
}
609597
OperationParameterType::RawBody => {
610598
quote! { #name: B }
611599
}
@@ -935,15 +923,11 @@ impl Generator {
935923
OperationParameterKind::Body(
936924
BodyContentType::FormData
937925
),
938-
OperationParameterType::Form(map),
926+
OperationParameterType::Form(_),
939927
) => {
940-
let form_prop_names = map.iter().cloned().map(|form_prop_name| {
941-
let ident= format_ident!("{}", form_prop_name);
942-
quote! { (#form_prop_name, #ident) }
943-
});
944928
Some(quote! {
945929
// This uses progenitor_client::RequestBuilderExt which sets up a simple form data based on bytes
946-
.form_from_raw(vec![ #(#form_prop_names),* ])?
930+
.form_from_raw(body.as_form())?
947931
})},
948932
(OperationParameterKind::Body(_), _) => {
949933
unreachable!("invalid body kind/type combination")
@@ -1408,7 +1392,8 @@ impl Generator {
14081392
.params
14091393
.iter()
14101394
.map(|param| match &param.typ {
1411-
OperationParameterType::Type(type_id) => {
1395+
OperationParameterType::Type(type_id)
1396+
| OperationParameterType::Form(type_id) => {
14121397
let ty = self.type_space.get_type(type_id)?;
14131398

14141399
// For body parameters only, if there's a builder we'll
@@ -1425,10 +1410,6 @@ impl Generator {
14251410
}
14261411
}
14271412

1428-
OperationParameterType::Form(_form) => {
1429-
todo!("Form is nit expected here")
1430-
}
1431-
14321413
OperationParameterType::RawBody => {
14331414
cloneable = false;
14341415
Ok(quote! { Result<reqwest::Body, String> })
@@ -1441,7 +1422,8 @@ impl Generator {
14411422
.params
14421423
.iter()
14431424
.map(|param| match &param.typ {
1444-
OperationParameterType::Type(type_id) => {
1425+
OperationParameterType::Type(type_id)
1426+
| OperationParameterType::Form(type_id) => {
14451427
let ty = self.type_space.get_type(type_id)?;
14461428
let details = ty.details();
14471429
let optional =
@@ -1460,9 +1442,6 @@ impl Generator {
14601442
Ok(quote! { Err(#err_msg.to_string()) })
14611443
}
14621444
}
1463-
OperationParameterType::Form(_form) => {
1464-
todo!("Form is nit expected here")
1465-
}
14661445
OperationParameterType::RawBody => {
14671446
let err_msg = format!("{} was not initialized", param.name);
14681447
Ok(quote! { Err(#err_msg.to_string()) })
@@ -1474,7 +1453,8 @@ impl Generator {
14741453
.params
14751454
.iter()
14761455
.map(|param| match &param.typ {
1477-
OperationParameterType::Type(type_id) => {
1456+
OperationParameterType::Type(type_id)
1457+
| OperationParameterType::Form(type_id) => {
14781458
let ty = self.type_space.get_type(type_id)?;
14791459
if ty.builder().is_some() {
14801460
let type_name = ty.ident();
@@ -1488,14 +1468,6 @@ impl Generator {
14881468
}
14891469
}
14901470

1491-
OperationParameterType::Form(_form) => {
1492-
todo!("Form is nit expected here")
1493-
}
1494-
1495-
OperationParameterType::Form(_form) => {
1496-
todo!("Form is nit expected here")
1497-
}
1498-
14991471
OperationParameterType::RawBody => Ok(quote! {}),
15001472
})
15011473
.collect::<Result<Vec<_>>>()?;
@@ -1508,11 +1480,12 @@ impl Generator {
15081480
.map(|param| {
15091481
let param_name = format_ident!("{}", param.name);
15101482
match &param.typ {
1511-
OperationParameterType::Type(type_id) => {
1483+
OperationParameterType::Type(type_id)
1484+
| OperationParameterType::Form(type_id) => {
15121485
let ty = self.type_space.get_type(type_id)?;
15131486
let details = ty.details();
15141487
match (&details, ty.builder()) {
1515-
// TODO right now optional body paramters are not
1488+
// TODO right now optional body parameters are not
15161489
// addressed
15171490
(typify::TypeDetails::Option(_), Some(_)) => {
15181491
unreachable!()
@@ -1596,7 +1569,7 @@ impl Generator {
15961569
}
15971570
}
15981571

1599-
OperationParameterType::Form(form_keys) => {
1572+
OperationParameterType::Form(type_id) => {
16001573
let err_msg = format!(
16011574
"conversion to `reqwest::Body` for {} failed",
16021575
param.name,
@@ -2161,7 +2134,19 @@ impl Generator {
21612134
schema
21622135
))),
21632136
}?;
2164-
OperationParameterType::Form(mapped)
2137+
2138+
let form_name = sanitize(
2139+
&format!(
2140+
"{}-form",
2141+
operation.operation_id.as_ref().unwrap(),
2142+
),
2143+
Case::Pascal,
2144+
);
2145+
let type_id = self
2146+
.type_space
2147+
.add_type_with_name(&schema.to_schema(), Some(form_name))?;
2148+
self.forms.insert(type_id.clone());
2149+
OperationParameterType::Form(type_id)
21652150
}
21662151
BodyContentType::Json | BodyContentType::FormUrlencoded => {
21672152
// TODO it would be legal to have the encoding field set for

0 commit comments

Comments
 (0)