use clap::Parser; use okapi::openapi3::{Components, SchemaObject}; use okapi::schemars::schema::{InstanceType, Schema, SingleOrVec}; /// Dump the tree structure of a schema element as dot file #[derive(Parser, Debug)] #[command(author, version, about, long_about = None)] struct Args { /// The name of the file to dump /// /// If this value is unspecified, the default petstore schema /// is used instead #[arg(short, long)] file_name: Option, /// The name of the structure to dump #[arg(short, long, default_value = "Pet")] struct_name: String, } #[derive(Debug, Clone)] enum NodeType { Null, Boolean, Array { item: Box }, Object { children: Vec }, String, Number, Integer, } #[derive(Debug, Clone)] struct TreeNode { name: String, r#type: NodeType, } fn main() { env_logger::init_from_env(env_logger::Env::new().default_filter_or("info")); let args = Args::parse(); let file_content = match args.file_name { None => include_str!("../examples/petstore.yaml").to_string(), Some(path) => std::fs::read_to_string(path).expect("Unable to load schema file!"), }; // Parse schema let schema = serde_yaml::from_str::(&file_content) .expect("Failed to parse document"); if schema.components.is_none() { log::error!("components is missing!"); panic!() } let tree = build_tree(&args.struct_name, schema.components.as_ref().unwrap()); println!("{:#?}", tree); } fn expect_single(e: &SingleOrVec) -> &E { match e { SingleOrVec::Single(e) => &e, SingleOrVec::Vec(v) => &v[0], } } fn expect_schema_object(s: &Schema) -> &SchemaObject { match s { Schema::Bool(_) => { panic!("Got unexpected bool!"); } Schema::Object(o) => o, } } fn build_tree(struct_name: &str, components: &Components) -> TreeNode { let schema = components .schemas .get(struct_name) .expect(&format!("Missing {struct_name}")); build_tree_schema(schema, struct_name, components) } fn build_tree_schema( schema: &SchemaObject, struct_name: &str, components: &Components, ) -> TreeNode { if let Some(name) = &schema.reference { return build_tree( name.strip_prefix("#/components/schemas/").unwrap(), components, ); } let schema_type = schema .instance_type .as_ref() .map(expect_single) .unwrap_or(&InstanceType::String); let r#type = match schema_type { InstanceType::Null => NodeType::Null, InstanceType::Boolean => NodeType::Boolean, InstanceType::Object => { let children = schema .object .as_ref() .unwrap() .properties .iter() .map(|e| { let o = expect_schema_object(e.1); build_tree_schema(o, e.0, components) }) .collect::>(); NodeType::Object { children } } InstanceType::Array => { let item = expect_schema_object(expect_single( schema.array.as_ref().unwrap().items.as_ref().unwrap(), )); NodeType::Array { item: Box::new(build_tree_schema( item, &format!("{struct_name}[]"), components, )), } } InstanceType::Number => NodeType::Number, InstanceType::String => NodeType::String, InstanceType::Integer => NodeType::Integer, }; TreeNode { name: struct_name.to_string(), r#type, } }