path parameters first, by position; then query parameters, by name

This commit is contained in:
Joshua M. Clulow 2021-09-17 21:17:21 -07:00
parent aab5061b56
commit 2a319675b1
2 changed files with 107 additions and 45 deletions

View File

@ -1171,7 +1171,15 @@ fn gen(api: &OpenAPI, ts: &mut TypeSpace) -> Result<String> {
} }
a(" &self,"); a(" &self,");
for par in o.parameters.iter() { /*
* The order of parameters in the specification is effectively
* arbitrary, and both path and query style parameters end up
* mingled in the same list.
*/
let mut parms = o
.parameters
.iter()
.map(|par| {
match par.item()? { match par.item()? {
openapiv3::Parameter::Path { openapiv3::Parameter::Path {
parameter_data, parameter_data,
@ -1183,10 +1191,11 @@ fn gen(api: &OpenAPI, ts: &mut TypeSpace) -> Result<String> {
assert!(parameter_data.required); assert!(parameter_data.required);
let nam = &parameter_data.name; let nam = &parameter_data.name;
let tid = ts.select(None, parameter_data.schema()?)?; let tid =
ts.select(None, parameter_data.schema()?)?;
let typ = let typ =
ts.render_type(&tid, UseContext::Parameter)?; ts.render_type(&tid, UseContext::Parameter)?;
a(&format!(" {}: {},", nam, typ)); return Ok((true, nam, typ));
} }
openapiv3::Parameter::Query { openapiv3::Parameter::Query {
parameter_data, parameter_data,
@ -1201,7 +1210,8 @@ fn gen(api: &OpenAPI, ts: &mut TypeSpace) -> Result<String> {
} }
let nam = &parameter_data.name; let nam = &parameter_data.name;
let tid = ts.select(None, parameter_data.schema()?)?; let tid =
ts.select(None, parameter_data.schema()?)?;
let tid = if parameter_data.required { let tid = if parameter_data.required {
tid tid
} else { } else {
@ -1213,14 +1223,38 @@ fn gen(api: &OpenAPI, ts: &mut TypeSpace) -> Result<String> {
}; };
let typ = let typ =
ts.render_type(&tid, UseContext::Parameter)?; ts.render_type(&tid, UseContext::Parameter)?;
a(&format!(" {}: {},", nam, typ)); query.push((
nam.to_string(),
query.push((nam.to_string(), !parameter_data.required)); !parameter_data.required,
));
return Ok((false, nam, typ));
} }
x => bail!("unhandled parameter type: {:#?}", x), x => bail!("unhandled parameter type: {:#?}", x),
} }
})
.collect::<Result<Vec<_>>>()?;
/*
* Deal first with path parameters, ordered by their position in the
* path template string.
*/
let tmp = template::parse(p)?;
for pn in tmp.names() {
let par = parms.iter().find(|p| p.0 && p.1 == &pn).unwrap();
a(&format!(" {}: {},", par.1, par.2));
} }
/*
* Second, include query parameters, ordered by parameter name.
*/
parms.sort_by(|a, b| a.1.cmp(&b.1));
for par in parms.iter().filter(|p| !p.0) {
a(&format!(" {}: {},", par.1, par.2));
}
/*
* Include the body parameter, if there is one, last in the list:
*/
if let Some(bp) = &body_param { if let Some(bp) = &body_param {
a(&format!(" body: {},", bp)); a(&format!(" body: {},", bp));
} }
@ -1289,7 +1323,6 @@ fn gen(api: &OpenAPI, ts: &mut TypeSpace) -> Result<String> {
/* /*
* Generate the URL for the request. * Generate the URL for the request.
*/ */
let tmp = template::parse(p)?;
a(&tmp.compile()); a(&tmp.compile());
/* /*

View File

@ -35,6 +35,16 @@ impl Template {
out.push_str(" );\n"); out.push_str(" );\n");
out out
} }
pub fn names(&self) -> Vec<String> {
self.components
.iter()
.filter_map(|c| match c {
Component::Parameter(name) => Some(name.to_string()),
Component::Constant(_) => None,
})
.collect()
}
} }
pub fn parse(t: &str) -> Result<Template> { pub fn parse(t: &str) -> Result<Template> {
@ -159,6 +169,25 @@ mod test {
Ok(()) Ok(())
} }
#[test]
fn names() -> Result<()> {
let trials = vec![
("/info", vec![]),
("/measure/{number}", vec!["number".to_string()]),
(
"/measure/{one}/{two}/and/{three}/yeah",
vec!["one".to_string(), "two".to_string(), "three".to_string()],
),
];
for (path, want) in trials.iter() {
let t = parse(path).with_context(|| anyhow!("path {}", path))?;
assert_eq!(&t.names(), want);
}
Ok(())
}
#[test] #[test]
fn compile() -> Result<()> { fn compile() -> Result<()> {
let t = parse("/measure/{number}")?; let t = parse("/measure/{number}")?;