use clap::{Parser, Subcommand}; use openapi_parser::{build_tree, parse_schema, NodeType, ObjectChild, TreeNode}; use std::fmt::Write; /// 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, /// The action to perform #[clap(subcommand)] action: Action, } #[derive(Subcommand, Debug)] enum Action { /// Dump as JSON Json, /// Dump as Graphviz graph Graph, /// Dump as Tex list Tex, } 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!"), }; let schema = parse_schema(&file_content); let tree = build_tree(&args.struct_name, schema.components.as_ref().unwrap()); match args.action { Action::Json => println!("{}", serde_json::to_string(&tree).unwrap()), Action::Graph => println!("{}", graphviz_export(&tree)), Action::Tex => println!("{}", tex_export(&tree)), } } fn recurse_export(node: &TreeNode, parent_name: &str, out: &mut String) { if !parent_name.is_empty() && matches!(node.r#type, NodeType::Object { .. }) { writeln!(out, "\"{}\" -> \"{}\";", parent_name, node.name).unwrap(); } match &node.r#type { NodeType::Array { item } => { let mut item = item.clone(); item.name.push_str(" []"); recurse_export(&item, parent_name, out); } NodeType::Object { children, .. } => { for child in children { recurse_export(&child.node, &node.name, out); } } _ => {} } } fn graphviz_export(tree: &TreeNode) -> String { let mut out = "digraph G {\n".to_string(); recurse_export(tree, "", &mut out); out.push_str("}\n"); out } fn tex_export_inner(tree: &ObjectChild, out: &mut String, required: bool) { let type_str = match &tree.node.r#type { NodeType::Null => "NULL".to_string(), NodeType::Boolean => "bool".to_string(), NodeType::Array { item } => format!("{}[]", item.name), NodeType::Object { .. } => tree.node.name.to_string(), NodeType::String => "string".to_string(), NodeType::Number => "number".to_string(), NodeType::Integer => "integer".to_string(), }; write!(out, "\\textbf{{{}}}", tree.name).unwrap(); if required { out.push_str("\\textcolor{red}{*}"); } out.push_str("\n\\newline"); write!(out, "\\textit{{\\textcolor{{gray}}{{{type_str}}}}}").unwrap(); if let Some(description) = &tree.node.description { write!(out, "\\newline\n\textcolor{{gray}}{{{}}}", description).unwrap(); } if let Some(example) = &tree.node.examples.get(0) { write!( out, "\\newline\n\textcolor{{gray}}{{Exemple : {}}}", example ) .unwrap(); } } fn tex_export(tree: &TreeNode) -> String { let mut out = String::new(); writeln!(out, "% START OF EXPORT OF STRUCTURE {}", tree.name).unwrap(); match &tree.r#type { NodeType::Object { children, required } => { for child in children { tex_export_inner( child, &mut out, required .as_ref() .map(|r| r.contains(&child.name)) .unwrap_or(false), ); out.push_str("\n\n"); } } _ => tex_export_inner( &ObjectChild { name: tree.name.to_string(), node: tree.clone(), }, &mut out, false, ), } writeln!(out, "% END OF EXPORT OF STRUCTURE {}", tree.name).unwrap(); out }