Skip to main content

openapi_nexus/generators/java/okhttp/
util.rs

1use 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 is_java_reserved(name: &str) -> bool {
93    matches!(
94        name,
95        "abstract"
96            | "assert"
97            | "boolean"
98            | "break"
99            | "byte"
100            | "case"
101            | "catch"
102            | "char"
103            | "class"
104            | "const"
105            | "continue"
106            | "default"
107            | "do"
108            | "double"
109            | "else"
110            | "enum"
111            | "extends"
112            | "final"
113            | "finally"
114            | "float"
115            | "for"
116            | "goto"
117            | "if"
118            | "implements"
119            | "import"
120            | "instanceof"
121            | "int"
122            | "interface"
123            | "long"
124            | "native"
125            | "new"
126            | "package"
127            | "private"
128            | "protected"
129            | "public"
130            | "return"
131            | "short"
132            | "static"
133            | "strictfp"
134            | "super"
135            | "switch"
136            | "synchronized"
137            | "this"
138            | "throw"
139            | "throws"
140            | "transient"
141            | "try"
142            | "void"
143            | "volatile"
144            | "while"
145    )
146}
147
148pub fn escape_java_string(s: &str) -> String {
149    s.replace('\\', "\\\\")
150        .replace('"', "\\\"")
151        .replace('\n', "\\n")
152        .replace('\r', "\\r")
153        .replace('\t', "\\t")
154}
155
156pub fn unique_name(desired: &str, used: &mut HashSet<String>) -> String {
157    if used.insert(desired.to_string()) {
158        return desired.to_string();
159    }
160    for i in 2..=u32::MAX {
161        let candidate = format!("{desired}{i}");
162        if used.insert(candidate.clone()) {
163            return candidate;
164        }
165    }
166    unreachable!()
167}
168
169pub fn sanitize_operation_id(op_id: &str, method: &str, path: &str) -> String {
170    if !op_id.is_empty() {
171        return op_id.to_string();
172    }
173    let path_part: String = path
174        .chars()
175        .map(|c| if c.is_alphanumeric() { c } else { '_' })
176        .collect();
177    format!("{method}_{path_part}")
178}
179
180pub fn render_value_as_string(value_expr: &str, t: &IrTypeExpr) -> String {
181    match t {
182        IrTypeExpr::Primitive(
183            IrPrimitive::String
184            | IrPrimitive::Date
185            | IrPrimitive::DateTime
186            | IrPrimitive::Uuid
187            | IrPrimitive::StringWithFormat(_),
188        )
189        | IrTypeExpr::StringLiteral(_)
190        | IrTypeExpr::StringEnum(_) => value_expr.to_string(),
191        IrTypeExpr::Primitive(IrPrimitive::Boolean)
192        | IrTypeExpr::Primitive(IrPrimitive::Integer)
193        | IrTypeExpr::Primitive(IrPrimitive::IntegerWithFormat(_))
194        | IrTypeExpr::Primitive(IrPrimitive::Number)
195        | IrTypeExpr::Primitive(IrPrimitive::NumberWithFormat(_)) => {
196            format!("String.valueOf({value_expr})")
197        }
198        IrTypeExpr::Nullable(inner) => render_value_as_string(value_expr, inner),
199        IrTypeExpr::Array(inner) => {
200            if matches!(
201                inner.as_ref(),
202                IrTypeExpr::Primitive(
203                    IrPrimitive::String
204                        | IrPrimitive::Date
205                        | IrPrimitive::DateTime
206                        | IrPrimitive::Uuid
207                        | IrPrimitive::StringWithFormat(_)
208                ) | IrTypeExpr::StringLiteral(_)
209                    | IrTypeExpr::StringEnum(_)
210            ) {
211                format!("String.join(\",\", {value_expr})")
212            } else {
213                format!(
214                    "{value_expr}.stream().map(Object::toString).collect(java.util.stream.Collectors.joining(\",\"))"
215                )
216            }
217        }
218        IrTypeExpr::Named(_) => format!("String.valueOf({value_expr})"),
219        _ => format!("String.valueOf({value_expr})"),
220    }
221}
222
223pub fn build_java_getter(getter_name: &str, type_str: &str, field_name: &str) -> FunSpec {
224    let mut getter = FunSpec::builder(getter_name);
225    getter = getter.visibility(Visibility::Public);
226    getter = getter.returns(TypeName::primitive(type_str));
227    let body = sigil_quote!(Java {
228        return this.$L(field_name);
229    })
230    .expect("getter body");
231    getter = getter.body(body);
232    getter.build().expect("getter")
233}
234
235pub fn type_uses_list(expr: &IrTypeExpr) -> bool {
236    match expr {
237        IrTypeExpr::Array(_) => true,
238        IrTypeExpr::Nullable(inner) => type_uses_list(inner),
239        IrTypeExpr::Map(inner) => type_uses_list(inner),
240        _ => false,
241    }
242}
243
244pub fn type_uses_map(expr: &IrTypeExpr) -> bool {
245    match expr {
246        IrTypeExpr::Map(_) => true,
247        IrTypeExpr::Nullable(inner) => type_uses_map(inner),
248        IrTypeExpr::Array(inner) => type_uses_map(inner),
249        _ => false,
250    }
251}