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 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}