Skip to content

Commit f4cff40

Browse files
authored
handling of consecutive upper case letters (#370)
Signed-off-by: Sebastian Hoß <[email protected]>
1 parent 4002c38 commit f4cff40

File tree

9 files changed

+609
-541
lines changed

9 files changed

+609
-541
lines changed

src/analyzer.rs

Lines changed: 70 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ pub struct Config {
2323
pub fn analyze(schema: JSONSchemaProps, kind: &str, cfg: Config) -> Result<Output> {
2424
let mut res = vec![];
2525

26-
analyze_(&schema, "", &kind.to_upper_camel_case(), 0, &mut res, &cfg)?;
26+
analyze_(&schema, "", kind, 0, &mut res, &cfg)?;
2727
Ok(Output(res))
2828
}
2929

@@ -45,6 +45,8 @@ fn analyze_(
4545
let props = schema.properties.clone().unwrap_or_default();
4646
let mut array_recurse_level: HashMap<String, u8> = Default::default();
4747

48+
let camel_cased_stack = &stack.to_upper_camel_case();
49+
4850
// create a Container if we have a container type:
4951
//trace!("analyze_ with {} + {}", current, stack);
5052
if schema.type_.clone().unwrap_or_default() == "object" {
@@ -55,29 +57,46 @@ fn analyze_(
5557
// object with additionalProperties == map
5658
if let Some(extra_props) = &s.properties {
5759
// map values is an object with properties
58-
debug!("Generating map struct for {} (under {})", current, stack);
59-
let c = extract_container(extra_props, stack, &mut array_recurse_level, level, schema, cfg)?;
60+
debug!(
61+
"Generating map struct for {} (under {})",
62+
current, camel_cased_stack
63+
);
64+
let c = extract_container(
65+
extra_props,
66+
camel_cased_stack,
67+
&mut array_recurse_level,
68+
level,
69+
schema,
70+
cfg,
71+
)?;
6072
results.push(c);
6173
} else if dict_type == "object" {
6274
// recurse to see if we eventually find properties
6375
debug!(
6476
"Recursing into nested additional properties for {} (under {})",
65-
current, stack
77+
current, camel_cased_stack
6678
);
67-
analyze_(s, current, stack, level, results, cfg)?;
79+
analyze_(s, current, camel_cased_stack, level, results, cfg)?;
6880
} else if !dict_type.is_empty() {
6981
warn!("not generating type {} - using {} map", current, dict_type);
7082
return Ok(()); // no members here - it'll be inlined
7183
}
7284
} else {
7385
// else, regular properties only
74-
debug!("Generating struct for {} (under {})", current, stack);
86+
debug!("Generating struct for {} (under {})", current, camel_cased_stack);
7587
// initial analysis of properties (we do not recurse here, we need to find members first)
7688
if props.is_empty() && schema.x_kubernetes_preserve_unknown_fields.unwrap_or(false) {
7789
warn!("not generating type {} - using map", current);
7890
return Ok(());
7991
}
80-
let c = extract_container(&props, stack, &mut array_recurse_level, level, schema, cfg)?;
92+
let c = extract_container(
93+
&props,
94+
camel_cased_stack,
95+
&mut array_recurse_level,
96+
level,
97+
schema,
98+
cfg,
99+
)?;
81100
results.push(c);
82101
}
83102
}
@@ -91,10 +110,24 @@ fn analyze_(
91110
// again; additionalProperties XOR properties
92111
let extras = if let Some(JSONSchemaPropsOrBool::Schema(s)) = schema.additional_properties.as_ref() {
93112
let extra_props = s.properties.clone().unwrap_or_default();
94-
find_containers(&extra_props, stack, &mut array_recurse_level, level, schema, cfg)?
113+
find_containers(
114+
&extra_props,
115+
camel_cased_stack,
116+
&mut array_recurse_level,
117+
level,
118+
schema,
119+
cfg,
120+
)?
95121
} else {
96122
// regular properties only
97-
find_containers(&props, stack, &mut array_recurse_level, level, schema, cfg)?
123+
find_containers(
124+
&props,
125+
camel_cased_stack,
126+
&mut array_recurse_level,
127+
level,
128+
schema,
129+
cfg,
130+
)?
98131
};
99132
results.extend(extras);
100133

@@ -1317,6 +1350,34 @@ type: object
13171350
assert_eq!(other.level, 1);
13181351
}
13191352

1353+
#[test]
1354+
fn camel_case_of_kinds_with_consecutive_upper_case_letters() {
1355+
init();
1356+
let schema_str = r#"
1357+
properties:
1358+
spec:
1359+
type: object
1360+
status:
1361+
type: object
1362+
type: object
1363+
"#;
1364+
let schema: JSONSchemaProps = serde_yaml::from_str(schema_str).unwrap();
1365+
1366+
let structs = analyze(schema, "ArgoCDExport", Cfg::default()).unwrap().0;
1367+
1368+
let root = &structs[0];
1369+
assert_eq!(root.name, "ArgoCdExport");
1370+
assert_eq!(root.level, 0);
1371+
1372+
let spec = &structs[1];
1373+
assert_eq!(spec.name, "ArgoCdExportSpec");
1374+
assert_eq!(spec.level, 1);
1375+
1376+
let status = &structs[2];
1377+
assert_eq!(status.name, "ArgoCdExportStatus");
1378+
assert_eq!(status.level, 1);
1379+
}
1380+
13201381
#[test]
13211382
fn skipped_type_as_map_nested_in_array() {
13221383
init();

src/main.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use std::{path::PathBuf, str::FromStr};
22
#[macro_use] extern crate log;
33
use anyhow::{anyhow, Context, Result};
44
use clap::{CommandFactory, Parser, Subcommand};
5+
use heck::ToUpperCamelCase;
56
use k8s_openapi::apiextensions_apiserver::pkg::apis::apiextensions::v1::{
67
CustomResourceDefinition, CustomResourceDefinitionVersion,
78
};
@@ -281,7 +282,7 @@ impl Kopium {
281282
.is_some_and(|c| c.contains_key("status")))
282283
&& self.has_status_resource(&structs)
283284
{
284-
println!(r#"#[kube(status = "{}Status")]"#, kind);
285+
println!(r#"#[kube(status = "{}Status")]"#, kind.to_upper_camel_case());
285286
}
286287
if self.schema != "derived" {
287288
println!(r#"#[kube(schema = "{}")]"#, self.schema);
@@ -306,7 +307,10 @@ impl Kopium {
306307
}
307308
} else {
308309
self.print_derives(s, &structs);
309-
let spec_trimmed_name = s.name.as_str().replace(&format!("{}Spec", kind), kind);
310+
let spec_trimmed_name = s.name.as_str().replace(
311+
&format!("{}Spec", kind.to_upper_camel_case()),
312+
&kind.to_upper_camel_case(),
313+
);
310314
if s.is_enum {
311315
println!("pub enum {} {{", spec_trimmed_name);
312316
} else {
@@ -322,7 +326,10 @@ impl Kopium {
322326
for annot in &m.extra_annot {
323327
println!(" {}", annot);
324328
}
325-
let spec_trimmed_type = m.type_.as_str().replace(&format!("{}Spec", kind), kind);
329+
let spec_trimmed_type = m.type_.as_str().replace(
330+
&format!("{}Spec", kind.to_upper_camel_case()),
331+
&kind.to_upper_camel_case(),
332+
);
326333
if s.is_enum {
327334
// NB: only supporting plain enumerations atm, not oneOf
328335
println!(" {},", name);

src/output.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ impl Container {
212212
}
213213

214214
impl Output {
215-
/// Rename all structs and all all their members to rust conventions
215+
/// Rename all structs and all their members to rust conventions
216216
///
217217
/// Converts [*].members[*].name to snake_case for structs, PascalCase for enums,
218218
/// and adds a serde(rename = "orig_name") annotation to `serde_annot`.

0 commit comments

Comments
 (0)