paperclip_ng/v3/
parameter.rs

1use heck::ToSnakeCase;
2use ramhorns_derive::Content;
3use std::collections::HashMap;
4
5use super::{property::Property, OpenApiV3};
6
7#[derive(Default, Content, Clone, Debug)]
8#[ramhorns(rename_all = "camelCase")]
9pub(crate) struct Parameter {
10    param_name: String,
11    base_name: String,
12    example: Option<String>,
13    description: Option<String>,
14    examples: Vec<String>,
15    required: bool,
16    deprecated: Option<bool>,
17    is_nullable: bool,
18    is_string: bool,
19    is_array: bool,
20    is_uuid: bool,
21    is_uri: bool,
22    is_primitive_type: bool,
23    is_container: bool,
24    data_type: String,
25    data_format: String,
26    vendor_extensions: HashMap<String, String>,
27    items: Option<Box<super::Property>>,
28}
29
30impl Parameter {
31    /// Create a new Parameter from a request body.
32    pub(super) fn from_body(api: &OpenApiV3, body: &openapiv3::RequestBody) -> Option<Self> {
33        let (_, media) = body.content.first()?;
34
35        let schema_back;
36        let mut body_type = None;
37        let schema = match media.schema.as_ref()? {
38            openapiv3::ReferenceOr::Reference { reference } => {
39                let schema_ref = reference.replace("#/components/schemas/", "");
40                let schema = api
41                    .api
42                    .components
43                    .as_ref()
44                    .and_then(|c| c.schemas.get(&schema_ref));
45                body_type = Some(schema_ref);
46                match schema {
47                    None => {
48                        api.missing_schema_ref(reference);
49                        schema_back = openapiv3::Schema {
50                            schema_data: Default::default(),
51                            schema_kind: openapiv3::SchemaKind::Any(openapiv3::AnySchema::default()),
52                        };
53                        &schema_back
54                    }
55                    Some(ref_or) => match ref_or {
56                        openapiv3::ReferenceOr::Reference { .. } => {
57                            panic!("double reference not supported");
58                        }
59                        openapiv3::ReferenceOr::Item(schema) => schema,
60                    },
61                }
62            }
63            openapiv3::ReferenceOr::Item(schema) => schema,
64        };
65        let property = Property::from_schema(api, None, schema, None, body_type.as_deref());
66        if property.is_any_type() {
67            body_type = Some("body".to_string());
68        }
69        let body_type = body_type.as_deref().unwrap_or("unknown");
70        let property = super::OpenApiV3::post_process(property);
71        Some(Self {
72            // todo: should have snake case param
73            param_name: body_type.to_snake_case(),
74            base_name: body_type.to_snake_case(),
75            example: media.example.as_ref().map(|v| v.to_string()),
76            examples: vec![],
77            description: body.description.as_ref().map(|s| s.replace('\n', " ")),
78            required: body.required,
79            deprecated: None,
80            is_nullable: schema.schema_data.nullable,
81            is_string: property.is_string(),
82            is_array: property.is_array(),
83            is_uuid: property.is_uuid(),
84            is_uri: property.is_uri(),
85            is_primitive_type: property.is_primitive_type(),
86            is_container: property.is_container(),
87            items: property.items().clone(),
88            data_type: property.data_type(),
89            data_format: property.data_format(),
90            vendor_extensions: body
91                .extensions
92                .iter()
93                .map(|(k, v)| (k.clone(), v.to_string()))
94                .collect(),
95        })
96    }
97    /// Create a new Parameter based on the deserialized parameter data.
98    pub(super) fn new(api: &OpenApiV3, param: &openapiv3::ParameterData) -> Self {
99        let schema_back;
100        let mut schema_type = None;
101        let schema = match &param.format {
102            openapiv3::ParameterSchemaOrContent::Schema(ref_s) => match ref_s {
103                openapiv3::ReferenceOr::Reference { reference } => {
104                    let schema_ref = reference.replace("#/components/schemas/", "");
105                    let schema = api
106                        .api
107                        .components
108                        .as_ref()
109                        .and_then(|c| c.schemas.get(&schema_ref));
110                    schema_type = Some(schema_ref);
111                    match schema {
112                        None => {
113                            api.missing_schema_ref(reference);
114                            schema_back = openapiv3::Schema {
115                                schema_data: Default::default(),
116                                schema_kind: openapiv3::SchemaKind::Any(
117                                    openapiv3::AnySchema::default(),
118                                ),
119                            };
120                            &schema_back
121                        }
122                        Some(ref_or) => match ref_or {
123                            openapiv3::ReferenceOr::Reference { .. } => {
124                                panic!("double reference not supported");
125                            }
126                            openapiv3::ReferenceOr::Item(schema) => schema,
127                        },
128                    }
129                }
130                openapiv3::ReferenceOr::Item(schema) => schema,
131            },
132            openapiv3::ParameterSchemaOrContent::Content(_) => {
133                todo!()
134            }
135        };
136        let property =
137            Property::from_schema(api, None, schema, Some(&param.name), schema_type.as_deref());
138        let property = super::OpenApiV3::post_process(property);
139        Self {
140            // todo: should have snake case param
141            param_name: param.name.to_snake_case(),
142            base_name: param.name.clone(),
143            example: param.example.as_ref().map(|v| v.to_string()),
144            examples: vec![],
145            description: param.description.as_ref().map(|s| s.replace('\n', " ")),
146            required: param.required,
147            deprecated: param.deprecated,
148            is_nullable: schema.schema_data.nullable,
149            is_string: property.is_string(),
150            is_array: property.is_array(),
151            is_uuid: property.is_uuid(),
152            is_uri: property.is_uri(),
153            is_primitive_type: property.is_primitive_type(),
154            is_container: property.is_container(),
155            items: property.items().clone(),
156            data_type: property.data_type(),
157            data_format: property.data_format(),
158            vendor_extensions: param
159                .extensions
160                .iter()
161                .map(|(k, v)| (k.clone(), v.to_string()))
162                .collect(),
163        }
164    }
165    /// Get a reference to the parameter data type format.
166    pub fn data_format(&self) -> &str {
167        &self.data_format
168    }
169    /// Get a reference to the parameter base name (no case modifications).
170    pub fn base_name(&self) -> &str {
171        &self.base_name
172    }
173    /// Get a reference to the parameter name.
174    pub fn name(&self) -> &str {
175        &self.param_name
176    }
177    /// Is the parameter required or optional.
178    pub fn required(&self) -> bool {
179        self.required
180    }
181}