// Copyright 2022 Oxide Computer Company use std::collections::BTreeMap; use indexmap::IndexMap; use openapiv3::{ Components, Parameter, ReferenceOr, RequestBody, Response, Schema, }; use unicode_ident::{is_xid_continue, is_xid_start}; use crate::Result; pub(crate) trait ReferenceOrExt { fn item<'a>(&'a self, components: &'a Option) -> Result<&'a T>; } pub(crate) trait ComponentLookup: Sized { fn get_components( components: &Components, ) -> &IndexMap>; } impl ReferenceOrExt for openapiv3::ReferenceOr { fn item<'a>(&'a self, components: &'a Option) -> Result<&'a T> { match self { ReferenceOr::Item(item) => Ok(item), ReferenceOr::Reference { reference } => { let idx = reference.rfind('/').unwrap(); let key = &reference[idx + 1..]; let parameters = T::get_components(components.as_ref().unwrap()); parameters .get(key) .unwrap_or_else(|| panic!("key {} is missing", key)) .item(components) } } } } pub(crate) fn items<'a, T>( refs: &'a [ReferenceOr], components: &'a Option, ) -> impl Iterator> where T: ComponentLookup, { refs.iter().map(|r| r.item(components)) } pub(crate) fn parameter_map<'a>( refs: &'a [ReferenceOr], components: &'a Option, ) -> Result> { items(refs, components) .map(|res| res.map(|param| (¶m.parameter_data_ref().name, param))) .collect() } impl ComponentLookup for Parameter { fn get_components( components: &Components, ) -> &IndexMap> { &components.parameters } } impl ComponentLookup for RequestBody { fn get_components( components: &Components, ) -> &IndexMap> { &components.request_bodies } } impl ComponentLookup for Response { fn get_components( components: &Components, ) -> &IndexMap> { &components.responses } } impl ComponentLookup for Schema { fn get_components( components: &Components, ) -> &IndexMap> { &components.schemas } } pub(crate) enum Case { Pascal, Snake, } pub(crate) fn sanitize(input: &str, case: Case) -> String { use heck::{ToPascalCase, ToSnakeCase}; let to_case = match case { Case::Pascal => str::to_pascal_case, Case::Snake => str::to_snake_case, }; // If every case was special then none of them would be. let out = match input { "+1" => "plus1".to_string(), "-1" => "minus1".to_string(), _ => to_case( &input .replace('\'', "") .replace(|c: char| !is_xid_continue(c), "-"), ), }; let out = match out.chars().next() { None => to_case("x"), Some(c) if is_xid_start(c) => out, Some(_) => format!("_{}", out), }; // Make sure the string is a valid Rust identifier. if syn::parse_str::(&out).is_ok() { out } else { format!("{}_", out) } }