openapi_nexus/generators/java/okhttp/
util.rs1use std::collections::HashSet;
2
3use crate::ir::types::{IrPrimitive, IrTypeExpr};
4use heck::{ToLowerCamelCase, ToPascalCase};
5#[allow(unused_imports)]
6use sigil_stitch::lang::java::Java;
7use sigil_stitch::prelude::*;
8
9pub fn java_type_str(expr: &IrTypeExpr) -> String {
10 match expr {
11 IrTypeExpr::Named(name) => name.to_pascal_case(),
12 IrTypeExpr::Primitive(p) => java_primitive(p).to_string(),
13 IrTypeExpr::Array(inner) => format!("List<{}>", java_boxed_type_str(inner)),
14 IrTypeExpr::Map(inner) => format!("Map<String, {}>", java_boxed_type_str(inner)),
15 IrTypeExpr::Nullable(inner) => java_boxed_type_str(inner),
16 IrTypeExpr::StringLiteral(_) | IrTypeExpr::StringEnum(_) => "String".to_string(),
17 IrTypeExpr::Union(_) | IrTypeExpr::Any => "Object".to_string(),
18 }
19}
20
21pub fn java_boxed_type_str(expr: &IrTypeExpr) -> String {
22 match expr {
23 IrTypeExpr::Primitive(p) => java_primitive_boxed(p).to_string(),
24 _ => java_type_str(expr),
25 }
26}
27
28pub fn java_primitive(p: &IrPrimitive) -> &'static str {
29 match p {
30 IrPrimitive::String | IrPrimitive::StringWithFormat(_) => "String",
31 IrPrimitive::Date | IrPrimitive::DateTime => "String",
32 IrPrimitive::Uuid => "String",
33 IrPrimitive::Binary => "byte[]",
34 IrPrimitive::Integer => "int",
35 IrPrimitive::IntegerWithFormat(format) => match format.as_str() {
36 "int64" => "long",
37 _ => "int",
38 },
39 IrPrimitive::Number => "double",
40 IrPrimitive::NumberWithFormat(format) => match format.as_str() {
41 "float" => "float",
42 _ => "double",
43 },
44 IrPrimitive::Boolean => "boolean",
45 }
46}
47
48pub fn java_primitive_boxed(p: &IrPrimitive) -> &'static str {
49 match p {
50 IrPrimitive::String | IrPrimitive::StringWithFormat(_) => "String",
51 IrPrimitive::Date | IrPrimitive::DateTime => "String",
52 IrPrimitive::Uuid => "String",
53 IrPrimitive::Binary => "byte[]",
54 IrPrimitive::Integer => "Integer",
55 IrPrimitive::IntegerWithFormat(format) => match format.as_str() {
56 "int64" => "Long",
57 _ => "Integer",
58 },
59 IrPrimitive::Number => "Double",
60 IrPrimitive::NumberWithFormat(format) => match format.as_str() {
61 "float" => "Float",
62 _ => "Double",
63 },
64 IrPrimitive::Boolean => "Boolean",
65 }
66}
67
68pub fn java_field_name(name: &str) -> String {
69 let camel = name.to_lower_camel_case();
70 if camel.is_empty() {
71 return "value".to_string();
72 }
73 if is_java_reserved(&camel) {
74 format!("{camel}_")
75 } else {
76 camel
77 }
78}
79
80pub fn java_ident(name: &str) -> String {
81 let camel = name.to_lower_camel_case();
82 if camel.is_empty() {
83 return "arg".to_string();
84 }
85 if is_java_reserved(&camel) {
86 format!("{camel}_")
87 } else {
88 camel
89 }
90}
91
92pub fn java_getter_name(name: &str) -> String {
93 let getter = format!("get{}", name.to_pascal_case());
94 if getter == "getClass" {
95 "getClass_".to_string()
96 } else {
97 getter
98 }
99}
100
101pub fn is_java_reserved(name: &str) -> bool {
102 matches!(
103 name,
104 "abstract"
105 | "assert"
106 | "boolean"
107 | "break"
108 | "byte"
109 | "case"
110 | "catch"
111 | "char"
112 | "class"
113 | "const"
114 | "continue"
115 | "default"
116 | "do"
117 | "double"
118 | "else"
119 | "enum"
120 | "extends"
121 | "final"
122 | "finally"
123 | "float"
124 | "for"
125 | "goto"
126 | "if"
127 | "implements"
128 | "import"
129 | "instanceof"
130 | "int"
131 | "interface"
132 | "long"
133 | "native"
134 | "new"
135 | "package"
136 | "private"
137 | "protected"
138 | "public"
139 | "return"
140 | "short"
141 | "static"
142 | "strictfp"
143 | "super"
144 | "switch"
145 | "synchronized"
146 | "this"
147 | "throw"
148 | "throws"
149 | "transient"
150 | "try"
151 | "void"
152 | "volatile"
153 | "while"
154 )
155}
156
157pub fn escape_java_string(s: &str) -> String {
158 s.replace('\\', "\\\\")
159 .replace('"', "\\\"")
160 .replace('\n', "\\n")
161 .replace('\r', "\\r")
162 .replace('\t', "\\t")
163}
164
165pub fn unique_name(desired: &str, used: &mut HashSet<String>) -> String {
166 if used.insert(desired.to_string()) {
167 return desired.to_string();
168 }
169 for i in 2..=u32::MAX {
170 let candidate = format!("{desired}{i}");
171 if used.insert(candidate.clone()) {
172 return candidate;
173 }
174 }
175 unreachable!()
176}
177
178pub fn sanitize_operation_id(op_id: &str, method: &str, path: &str) -> String {
179 if !op_id.is_empty() {
180 return op_id.to_string();
181 }
182 let path_part: String = path
183 .chars()
184 .map(|c| if c.is_alphanumeric() { c } else { '_' })
185 .collect();
186 format!("{method}_{path_part}")
187}
188
189pub fn render_value_as_string(value_expr: &str, t: &IrTypeExpr) -> String {
190 match t {
191 IrTypeExpr::Primitive(
192 IrPrimitive::String
193 | IrPrimitive::Date
194 | IrPrimitive::DateTime
195 | IrPrimitive::Uuid
196 | IrPrimitive::StringWithFormat(_),
197 )
198 | IrTypeExpr::StringLiteral(_)
199 | IrTypeExpr::StringEnum(_) => value_expr.to_string(),
200 IrTypeExpr::Primitive(IrPrimitive::Boolean)
201 | IrTypeExpr::Primitive(IrPrimitive::Integer)
202 | IrTypeExpr::Primitive(IrPrimitive::IntegerWithFormat(_))
203 | IrTypeExpr::Primitive(IrPrimitive::Number)
204 | IrTypeExpr::Primitive(IrPrimitive::NumberWithFormat(_)) => {
205 format!("String.valueOf({value_expr})")
206 }
207 IrTypeExpr::Nullable(inner) => render_value_as_string(value_expr, inner),
208 IrTypeExpr::Array(inner) => {
209 if matches!(
210 inner.as_ref(),
211 IrTypeExpr::Primitive(
212 IrPrimitive::String
213 | IrPrimitive::Date
214 | IrPrimitive::DateTime
215 | IrPrimitive::Uuid
216 | IrPrimitive::StringWithFormat(_)
217 ) | IrTypeExpr::StringLiteral(_)
218 | IrTypeExpr::StringEnum(_)
219 ) {
220 format!("String.join(\",\", {value_expr})")
221 } else {
222 format!(
223 "{value_expr}.stream().map(Object::toString).collect(java.util.stream.Collectors.joining(\",\"))"
224 )
225 }
226 }
227 IrTypeExpr::Named(_) => format!("String.valueOf({value_expr})"),
228 _ => format!("String.valueOf({value_expr})"),
229 }
230}
231
232pub fn build_java_getter(getter_name: &str, type_str: &str, field_name: &str) -> FunSpec {
233 let mut getter = FunSpec::builder(getter_name);
234 getter = getter.visibility(Visibility::Public);
235 getter = getter.returns(TypeName::primitive(type_str));
236 let body = sigil_quote!(Java {
237 return this.$L(field_name);
238 })
239 .expect("getter body");
240 getter = getter.body(body);
241 getter.build().expect("getter")
242}
243
244pub fn type_uses_list(expr: &IrTypeExpr) -> bool {
245 match expr {
246 IrTypeExpr::Array(_) => true,
247 IrTypeExpr::Nullable(inner) => type_uses_list(inner),
248 IrTypeExpr::Map(inner) => type_uses_list(inner),
249 _ => false,
250 }
251}
252
253pub fn type_uses_map(expr: &IrTypeExpr) -> bool {
254 match expr {
255 IrTypeExpr::Map(_) => true,
256 IrTypeExpr::Nullable(inner) => type_uses_map(inner),
257 IrTypeExpr::Array(inner) => type_uses_map(inner),
258 _ => false,
259 }
260}