Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 49 additions & 36 deletions src/analyzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ pub struct Config {
///
/// All found output structs will have its names prefixed by the kind it is for
pub fn analyze(schema: JSONSchemaProps, kind: &str, cfg: Config) -> Result<Output> {
let mut res = vec![];
let mut res = Output::default();

analyze_(&schema, "", kind, 0, &mut res, &cfg)?;
Ok(Output(res))
Ok(res)
}

/// Scan a schema for structs and members, and recurse to find all structs
Expand All @@ -39,7 +39,7 @@ fn analyze_(
current: &str,
stack: &str,
level: u8,
results: &mut Vec<Container>,
results: &mut Output,
cfg: &Config,
) -> Result<()> {
let props = schema.properties.clone().unwrap_or_default();
Expand Down Expand Up @@ -69,7 +69,7 @@ fn analyze_(
schema,
cfg,
)?;
results.push(c);
results.insert(c); // deduplicated insert
} else if dict_type == "object" {
// recurse to see if we eventually find properties
debug!(
Expand Down Expand Up @@ -97,7 +97,7 @@ fn analyze_(
schema,
cfg,
)?;
results.push(c);
results.insert(c); // deduplicated insert
}
}
//trace!("full schema here: {}", serde_yaml::to_string(&schema).unwrap());
Expand Down Expand Up @@ -146,9 +146,9 @@ fn find_containers(
level: u8,
schema: &JSONSchemaProps,
cfg: &Config,
) -> Result<Vec<Container>> {
) -> Result<Output> {
//trace!("finding containers in: {}", serde_yaml::to_string(&props)?);
let mut results = vec![];
let mut results = Output::default();
for (key, value) in props {
if level == 0 && IGNORED_KEYS.contains(&(key.as_ref())) {
debug!("not recursing into ignored {}", key); // handled elsewhere
Expand Down Expand Up @@ -217,7 +217,7 @@ fn find_containers(
// ....although this makes it impossible for us to handle enums at the top level
// TODO: move this to the top level
let new_result = analyze_enum_properties(en, &next_stack, level, schema)?;
results.push(new_result);
results.insert(new_result); // deduplicated insert
} else {
debug!("..not recursing into {} ('{}' is not a container)", key, x)
}
Expand Down Expand Up @@ -644,7 +644,7 @@ mod test {
let schema: JSONSchemaProps = serde_yaml::from_str(schema_str).unwrap();
//println!("schema: {}", serde_json::to_string_pretty(&schema).unwrap());

let structs = analyze(schema, "Agent", Cfg::default()).unwrap().0;
let structs = analyze(schema, "Agent", Cfg::default()).unwrap().output();
//println!("{:?}", structs);
let root = &structs[0];
assert_eq!(root.name, "Agent");
Expand Down Expand Up @@ -688,7 +688,7 @@ mod test {

let structs = analyze(schema, "ClusterStatusTopology", Cfg::default())
.unwrap()
.0;
.output();
//println!("{:?}", structs);
let root = &structs[0];
assert_eq!(root.name, "ClusterStatusTopology");
Expand All @@ -714,8 +714,9 @@ mod test {
"#;
let schema: JSONSchemaProps = serde_yaml::from_str(schema_str).unwrap();

let structs = analyze(schema, "AliasRoutingConfig", Cfg::default()).unwrap().0;
//println!("{:?}", structs);
let structs = analyze(schema, "AliasRoutingConfig", Cfg::default())
.unwrap()
.output(); //println!("{:?}", structs);
let root = &structs[0];
assert_eq!(root.name, "AliasRoutingConfig");
assert_eq!(root.level, 0);
Expand Down Expand Up @@ -748,7 +749,7 @@ type: object
"#;
let schema: JSONSchemaProps = serde_yaml::from_str(schema_str).unwrap();
//println!("schema: {}", serde_json::to_string_pretty(&schema).unwrap());
let structs = analyze(schema, "Server", Cfg::default()).unwrap().0;
let structs = analyze(schema, "Server", Cfg::default()).unwrap().output();
//println!("{:#?}", structs);

let root = &structs[0];
Expand Down Expand Up @@ -780,7 +781,7 @@ type: object
let schema: JSONSchemaProps = serde_yaml::from_str(schema_str).unwrap();
println!("got {schema:?}");

let structs = analyze(schema, "Spec", Cfg::default()).unwrap().0;
let structs = analyze(schema, "Spec", Cfg::default()).unwrap().output();
println!("got: {structs:?}");
let root = &structs[0];
assert_eq!(root.name, "Spec");
Expand Down Expand Up @@ -824,7 +825,7 @@ type: object
"#;
let schema: JSONSchemaProps = serde_yaml::from_str(schema_str).unwrap();
// println!("schema: {}", serde_json::to_string_pretty(&schema).unwrap());
let structs = analyze(schema, "Variables", Cfg::default()).unwrap().0;
let structs = analyze(schema, "Variables", Cfg::default()).unwrap().output();
// println!("{:#?}", structs);

let root = &structs[0];
Expand Down Expand Up @@ -858,7 +859,7 @@ type: object
"#;
let schema: JSONSchemaProps = serde_yaml::from_str(schema_str).unwrap();

let structs = analyze(schema, "Server", Cfg::default()).unwrap().0;
let structs = analyze(schema, "Server", Cfg::default()).unwrap().output();
let root = &structs[0];
assert_eq!(root.name, "Server");
// should have an IntOrString member:
Expand Down Expand Up @@ -892,7 +893,7 @@ type: object
"#;
let schema: JSONSchemaProps = serde_yaml::from_str(schema_str).unwrap();

let structs = analyze(schema, "Host", Cfg::default()).unwrap().0;
let structs = analyze(schema, "Host", Cfg::default()).unwrap().output();

let root = &structs[0];
assert_eq!(root.name, "Host");
Expand Down Expand Up @@ -921,7 +922,7 @@ type: object
type: object
"#;
let schema: JSONSchemaProps = serde_yaml::from_str(schema_str).unwrap();
let structs = analyze(schema, "Options", Cfg::default()).unwrap().0;
let structs = analyze(schema, "Options", Cfg::default()).unwrap().output();
println!("got {:?}", structs);
let root = &structs[0];
assert_eq!(root.name, "Options");
Expand All @@ -948,7 +949,9 @@ type: object
"#;

let schema: JSONSchemaProps = serde_yaml::from_str(schema_str).unwrap();
let structs = analyze(schema, "MatchExpressions", Cfg::default()).unwrap().0;
let structs = analyze(schema, "MatchExpressions", Cfg::default())
.unwrap()
.output();
println!("got {:?}", structs);
let root = &structs[0];
assert_eq!(root.name, "MatchExpressions");
Expand Down Expand Up @@ -1001,7 +1004,7 @@ type: object
"#;

let schema: JSONSchemaProps = serde_yaml::from_str(schema_str).unwrap();
let structs = analyze(schema, "Endpoint", Cfg::default()).unwrap().0;
let structs = analyze(schema, "Endpoint", Cfg::default()).unwrap().output();
println!("got {:?}", structs);
let root = &structs[0];
assert_eq!(root.name, "Endpoint");
Expand Down Expand Up @@ -1085,7 +1088,7 @@ type: object
type: object"#;

let schema: JSONSchemaProps = serde_yaml::from_str(schema_str).unwrap();
let structs = analyze(schema, "ServerSpec", Cfg::default()).unwrap().0;
let structs = analyze(schema, "ServerSpec", Cfg::default()).unwrap().output();
println!("got {:?}", structs);
let root = &structs[0];
assert_eq!(root.name, "ServerSpec");
Expand Down Expand Up @@ -1156,7 +1159,9 @@ type: object
type: object
"#;
let schema: JSONSchemaProps = serde_yaml::from_str(schema_str).unwrap();
let structs = analyze(schema, "ServiceMonitor", Cfg::default()).unwrap().0;
let structs = analyze(schema, "ServiceMonitor", Cfg::default())
.unwrap()
.output();
println!("got {:?}", structs);
let root = &structs[0];
assert_eq!(root.name, "ServiceMonitor");
Expand Down Expand Up @@ -1219,7 +1224,9 @@ type: object
let schema: JSONSchemaProps = serde_yaml::from_str(schema_str).unwrap();

//println!("schema: {}", serde_json::to_string_pretty(&schema).unwrap());
let structs = analyze(schema, "DestinationRule", Cfg::default()).unwrap().0;
let structs = analyze(schema, "DestinationRule", Cfg::default())
.unwrap()
.output();
//println!("{:#?}", structs);

// this should produce the root struct struct
Expand Down Expand Up @@ -1254,7 +1261,7 @@ type: object
"#;
let schema: JSONSchemaProps = serde_yaml::from_str(schema_str).unwrap();
println!("got schema {}", serde_yaml::to_string(&schema).unwrap());
let structs = analyze(schema, "StatusCode", Cfg::default()).unwrap().0;
let structs = analyze(schema, "StatusCode", Cfg::default()).unwrap().output();
println!("got {:?}", structs);
let root = &structs[0];
assert_eq!(root.name, "StatusCode");
Expand All @@ -1280,7 +1287,9 @@ type: object
"#;

let schema: JSONSchemaProps = serde_yaml::from_str(schema_str).unwrap();
let structs = analyze(schema, "KustomizationSpec", Cfg::default()).unwrap().0;
let structs = analyze(schema, "KustomizationSpec", Cfg::default())
.unwrap()
.output();
println!("got {:?}", structs);
let root = &structs[0];
assert_eq!(root.name, "KustomizationSpec");
Expand All @@ -1304,7 +1313,7 @@ type: object
"#;

let schema: JSONSchemaProps = serde_yaml::from_str(schema_str).unwrap();
let structs = analyze(schema, "Schema", Cfg::default()).unwrap().0;
let structs = analyze(schema, "Schema", Cfg::default()).unwrap().output();
println!("got {:?}", structs);
let root = &structs[0];
assert_eq!(root.name, "Schema");
Expand Down Expand Up @@ -1343,7 +1352,9 @@ type: object
type: object
type: object"#;
let schema: JSONSchemaProps = serde_yaml::from_str(schema_str).unwrap();
let structs = analyze(schema, "AppProjectStatus", Cfg::default()).unwrap().0;
let structs = analyze(schema, "AppProjectStatus", Cfg::default())
.unwrap()
.output();
println!("got {:?}", structs);
let root = &structs[0];
assert_eq!(root.name, "AppProjectStatus");
Expand Down Expand Up @@ -1377,7 +1388,7 @@ type: object
"#;
let schema: JSONSchemaProps = serde_yaml::from_str(schema_str).unwrap();

let structs = analyze(schema, "Agent", Cfg::default()).unwrap().0;
let structs = analyze(schema, "Agent", Cfg::default()).unwrap().output();

let root = &structs[0];
assert_eq!(root.name, "Agent");
Expand Down Expand Up @@ -1405,7 +1416,7 @@ type: object
"#;
let schema: JSONSchemaProps = serde_yaml::from_str(schema_str).unwrap();

let structs = analyze(schema, "ArgoCDExport", Cfg::default()).unwrap().0;
let structs = analyze(schema, "ArgoCDExport", Cfg::default()).unwrap().output();

let root = &structs[0];
assert_eq!(root.name, "ArgoCdExport");
Expand Down Expand Up @@ -1435,7 +1446,7 @@ type: object
"#;
let schema: JSONSchemaProps = serde_yaml::from_str(schema_str).unwrap();

let structs = analyze(schema, "Geoip", Cfg::default()).unwrap().0;
let structs = analyze(schema, "Geoip", Cfg::default()).unwrap().output();

assert_eq!(structs.len(), 1);
assert_eq!(structs[0].members.len(), 1);
Expand Down Expand Up @@ -1473,7 +1484,7 @@ type: object

let schema: JSONSchemaProps = serde_yaml::from_str(schema_str).unwrap();

let structs = analyze(schema, "Gateway", Cfg::default()).unwrap().0;
let structs = analyze(schema, "Gateway", Cfg::default()).unwrap().output();
assert_eq!(structs.len(), 1);
assert_eq!(structs[0].members.len(), 1);
assert_eq!(structs[0].members[0].type_, "Option<Vec<Condition>>");
Expand Down Expand Up @@ -1506,7 +1517,7 @@ type: object

let schema: JSONSchemaProps = serde_yaml::from_str(schema_str).unwrap();

let structs = analyze(schema, "Reference", Cfg::default()).unwrap().0;
let structs = analyze(schema, "Reference", Cfg::default()).unwrap().output();
assert_eq!(structs[0].members[0].type_, "Option<ObjectReference>");
}

Expand Down Expand Up @@ -1540,7 +1551,7 @@ type: object

let schema: JSONSchemaProps = serde_yaml::from_str(schema_str).unwrap();

let structs = analyze(schema, "Reference", Cfg::default()).unwrap().0;
let structs = analyze(schema, "Reference", Cfg::default()).unwrap().output();
assert_eq!(structs[0].members[0].type_, "Option<Vec<ObjectReference>>");
}

Expand All @@ -1559,7 +1570,7 @@ type: object

let schema: JSONSchemaProps = serde_yaml::from_str(schema_str).unwrap();

let structs = analyze(schema, "postgresql", Cfg::default()).unwrap().0;
let structs = analyze(schema, "postgresql", Cfg::default()).unwrap().output();
assert_eq!(structs[0].members[0].type_, "PostgresqlProp");
}

Expand Down Expand Up @@ -1598,7 +1609,9 @@ type: object

let schema: JSONSchemaProps = serde_yaml::from_str(schema_str).unwrap();

let structs = analyze(schema, "CephClusterSpec", Cfg::default()).unwrap().0;
let structs = analyze(schema, "CephClusterSpec", Cfg::default())
.unwrap()
.output();

// debug!("got: {:#?}", structs);

Expand Down Expand Up @@ -1628,7 +1641,7 @@ type: object

let schema: JSONSchemaProps = serde_yaml::from_str(schema_str).unwrap();

let structs = analyze(schema, "KubeadmConfig", Cfg::default()).unwrap().0;
let structs = analyze(schema, "KubeadmConfig", Cfg::default()).unwrap().output();

let root = &structs[0];
assert_eq!(root.name, "KubeadmConfig");
Expand Down
2 changes: 1 addition & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ impl Kopium {
let structs = analyze(schema, kind, cfg)?
.rename()
.builder_fields(self.builders)
.0;
.output();

if !self.hide_prelude {
self.print_prelude(&structs);
Expand Down
54 changes: 53 additions & 1 deletion src/output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,59 @@ use heck::{ToPascalCase, ToSnakeCase};
use regex::{Regex, RegexBuilder};

/// All found containers
pub struct Output(pub Vec<Container>);
#[derive(Default, Debug)]
pub struct Output(Vec<Container>);

impl Output {
/// Safe container inserter
///
/// Using this to insert, and encapulating the underlying vector ensures that we;
///
/// 1. repeat struct with pathological names are distinguished https://github.com/kube-rs/kopium/issues/66
/// 2. identical structs with different names are deduplicated https://github.com/kube-rs/kopium/issues/298
pub fn insert(&mut self, mut value: Container) -> bool {
let mut name_clashes = 0;
for c in &self.0 {
if c == &value {
return false; // no new value inserted
}
if c.name == value.name {
// there is a struct with the same name
name_clashes += 1;
}
}
// keep adding X suffixes to the struct/enum name until we are past the number of clashes
// this setup is not foolproof, but should be good enough for a POC
while name_clashes > 0 {
value.name = format!("{}X", value.name);
name_clashes -= 1;
}
// push the struct/enum (possibly with a new unique name)
self.0.push(value);
true // new value inserted
}

/// Consume self and return the final container vec
pub fn output(self) -> Vec<Container> {
self.0
}

/// Extend the inner vector with another Output instance
pub fn extend(&mut self, extras: Output) {
self.0.extend(extras.0);
}
}

impl PartialEq for Container {
fn eq(&self, other: &Self) -> bool {
(&self.name, &self.members, &self.is_enum) == (&other.name, &other.members, &other.is_enum)
}
}
impl PartialEq for Member {
fn eq(&self, other: &Self) -> bool {
(&self.name, &self.type_, &self.serde_annot) == (&other.name, &other.type_, &other.serde_annot)
}
}

/// Output container found by analyzer
#[derive(Default, Debug)]
Expand Down