1pub mod client;
2
3pub use client::client;
4use heck::{CamelCase, SnakeCase};
5use lazy_static::lazy_static;
6use openapiv3::ParameterData;
7use openapiv3::RequestBody;
8use openapiv3::Schema as SchemaV3;
9use openapiv3::SchemaVariant;
10use openapiv3::{IntegerFormat, NumberFormat, ReferenceOr, StringFormat, VariantOrUnknownOrEmpty};
11use regex::Regex;
12use serde_derive::Serialize;
13use std::borrow::Borrow;
14use std::fmt;
15
16lazy_static! {
17 static ref STRICT_KEYWORDS: [&'static str; 36] = [
18 "as", "break", "const", "continue", "crate", "dyn", "else", "enum", "extern", "false",
19 "fn", "for", "if", "impl", "in", "let", "loop", "match", "mod", "move", "mut", "pub",
20 "ref", "return", "Self", "self", "static", "struct", "super", "trait", "true", "type",
21 "unsafe", "use", "where", "while",
22 ];
23}
24
25lazy_static! {
26 static ref RESERVED_KEYWORDS: [&'static str; 15] = [
27 "abstract", "async", "await", "become", "box", "do", "final", "macro", "override", "priv",
28 "try", "typeof", "unsized", "virtual", "yield",
29 ];
30}
31
32lazy_static! {
33 static ref RAW_INCOMPATIBLE_KEYWORDS: [&'static str; 5] =
34 ["crate", "extern", "self", "Self", "super"];
35}
36
37lazy_static! {
38 static ref INVALID_PATTERNS: Regex = Regex::new(r"[^a-zA-Z0-9_]").unwrap();
39}
40
41#[derive(Debug, Serialize)]
43pub struct RustSnakeIdentifier(String);
44
45impl From<String> for RustSnakeIdentifier {
46 fn from(s: String) -> Self {
47 let identifier = INVALID_PATTERNS.replace_all(&s, " ").to_snake_case();
48
49 if RAW_INCOMPATIBLE_KEYWORDS.contains(&identifier.borrow()) {
50 RustSnakeIdentifier(format!("{}_", identifier))
51 } else {
52 RustSnakeIdentifier(identifier)
53 }
54 }
55}
56
57impl fmt::Display for RustSnakeIdentifier {
58 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
59 write!(f, "{}", self.0)
60 }
61}
62
63#[derive(Debug, Serialize)]
65pub struct RustPascalIdentifier(String);
66
67impl From<String> for RustPascalIdentifier {
68 fn from(s: String) -> Self {
69 let identifier = INVALID_PATTERNS.replace_all(&s, "_").to_camel_case();
70
71 if RAW_INCOMPATIBLE_KEYWORDS.contains(&identifier.borrow()) {
72 RustPascalIdentifier(format!("{}_", identifier))
73 } else {
74 RustPascalIdentifier(identifier)
75 }
76 }
77}
78
79impl fmt::Display for RustPascalIdentifier {
80 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
81 write!(f, "{}", self.0)
82 }
83}
84
85#[derive(Debug, Serialize)]
86pub struct RustType(String);
87
88impl From<&SchemaVariant> for RustType {
89 fn from(schema_variant: &SchemaVariant) -> RustType {
90 RustType(match schema_variant {
92 SchemaVariant::String { format, .. } => match format {
93 VariantOrUnknownOrEmpty::Item(StringFormat::Date) => "String",
94 VariantOrUnknownOrEmpty::Item(StringFormat::DateTime) => "String",
95 VariantOrUnknownOrEmpty::Item(StringFormat::Password) => "String",
96 VariantOrUnknownOrEmpty::Item(StringFormat::Byte) => "String",
97 VariantOrUnknownOrEmpty::Item(StringFormat::Binary) => "String",
98 VariantOrUnknownOrEmpty::Unknown(_) => "String",
99 VariantOrUnknownOrEmpty::Empty => "String",
100 }
101 .into(),
102 SchemaVariant::Number { format, .. } => match format {
103 VariantOrUnknownOrEmpty::Item(NumberFormat::Float) => "f32",
104 VariantOrUnknownOrEmpty::Item(NumberFormat::Double) => "f64",
105 VariantOrUnknownOrEmpty::Unknown(_) => "f32",
106 VariantOrUnknownOrEmpty::Empty => "f32",
107 }
108 .into(),
109 SchemaVariant::Integer { format, .. } => match format {
110 VariantOrUnknownOrEmpty::Item(IntegerFormat::Int32) => "i32",
111 VariantOrUnknownOrEmpty::Item(IntegerFormat::Int64) => "i64",
112 VariantOrUnknownOrEmpty::Unknown(_) => "i32",
113 VariantOrUnknownOrEmpty::Empty => "i32",
114 }
115 .into(),
116 SchemaVariant::Object { .. } => "Value".into(),
117 SchemaVariant::Array { items, .. } => format!("Vec<{}>", RustType::from(items)),
118 SchemaVariant::Boolean { .. } => "bool".into(),
119 })
120 }
121}
122
123impl From<&ReferenceOr<Box<SchemaV3>>> for RustType {
124 fn from(reference_or_schema: &ReferenceOr<Box<SchemaV3>>) -> RustType {
125 match reference_or_schema {
126 ReferenceOr::Reference { reference } => {
127 let type_name = reference
128 .trim_start_matches("#/components/schemas/")
129 .trim_start_matches("#/components/requestBodies/")
130 .to_camel_case();
131 RustType(format!("{}", type_name))
132 }
133 ReferenceOr::Item(schema) => match schema.borrow() {
134 SchemaV3::Schema(schema_variant) => {
135 (schema_variant.borrow() as &SchemaVariant).into()
136 }
137 SchemaV3::Any(any_schema) => {
138 dbg!(any_schema);
140 unimplemented!()
141 }
142 SchemaV3::OneOf { one_of } => {
143 if one_of.is_empty() {
144 RustType("Value".into())
145 } else {
146 unimplemented!()
147 }
148 }
149 _ => unimplemented!(),
150 },
151 }
152 }
153}
154
155impl From<&ReferenceOr<SchemaV3>> for RustType {
156 fn from(reference_or_schema: &ReferenceOr<SchemaV3>) -> RustType {
157 match reference_or_schema {
158 ReferenceOr::Reference { reference } => {
159 let type_name = reference
160 .trim_start_matches("#/components/schemas/")
161 .trim_start_matches("#/components/requestBodies/")
162 .to_camel_case();
163 RustType(format!("{}", type_name))
164 }
165 ReferenceOr::Item(schema) => match schema {
166 SchemaV3::Schema(schema_variant) => {
167 (schema_variant.borrow() as &SchemaVariant).into()
168 }
169 SchemaV3::Any(any_schema) => {
170 dbg!(any_schema);
172 unimplemented!()
173 }
174 SchemaV3::OneOf { one_of } => {
175 if one_of.is_empty() {
176 RustType("Value".into())
177 } else {
178 unimplemented!()
179 }
180 }
181 _ => unimplemented!(),
182 },
183 }
184 }
185}
186
187impl From<&ParameterData> for RustType {
188 fn from(parameter_data: &ParameterData) -> RustType {
189 match ¶meter_data.format {
190 openapiv3::ParameterSchemaOrContent::Content(_) => unimplemented!(),
191 openapiv3::ParameterSchemaOrContent::Schema(ref reference_or_schema) => {
192 reference_or_schema.into()
193 }
194 }
195 }
196}
197
198impl From<&ReferenceOr<RequestBody>> for RustType {
199 fn from(reference_or_requestbody: &ReferenceOr<RequestBody>) -> RustType {
200 match reference_or_requestbody {
201 ReferenceOr::Reference { reference } => {
202 let type_name = reference
203 .trim_start_matches("#/components/schemas/")
204 .trim_start_matches("#/components/requestBodies/")
205 .to_camel_case();
206 RustType(format!("{}", type_name))
207 }
208 ReferenceOr::Item(requestbody) => match requestbody.content.get("application/json") {
209 Some(mediatype) => match mediatype.schema {
210 Some(ref reference_or_schema) => reference_or_schema.into(),
211 None => unimplemented!(),
212 },
213 None => {
214 dbg!(requestbody);
215 unimplemented!()
216 }
217 },
218 }
219 }
220}
221
222impl RustType {
223 pub fn borrowed(&self) -> RustType {
224 RustType(match self.0.as_str() {
225 "String" => "&str".into(),
226 x => format!("&{}", x),
227 })
228 }
229}
230
231impl fmt::Display for RustType {
232 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
233 write!(f, "{}", self.0)
234 }
235}