Split code into a lib part and a binary part
This commit is contained in:
		
							
								
								
									
										167
									
								
								src/lib.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										167
									
								
								src/lib.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,167 @@ | ||||
| use okapi::openapi3::{Components, OpenApi, SchemaObject}; | ||||
| use okapi::schemars::schema::{InstanceType, Schema, SingleOrVec}; | ||||
|  | ||||
| /// Parse OpenAPI 3 schema | ||||
| pub fn parse_schema(file_content: &str) -> OpenApi { | ||||
|     let schema = serde_yaml::from_str::<OpenApi>(&file_content).expect("Failed to parse document"); | ||||
|  | ||||
|     if schema.components.is_none() { | ||||
|         log::error!("components is missing!"); | ||||
|         panic!() | ||||
|     } | ||||
|  | ||||
|     schema | ||||
| } | ||||
|  | ||||
| fn expect_single<E>(e: &SingleOrVec<E>) -> &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, | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Clone)] | ||||
| pub enum NodeType { | ||||
|     Null, | ||||
|     Boolean, | ||||
|     Array { item: Box<TreeNode> }, | ||||
|     Object { children: Vec<TreeNode> }, | ||||
|     String, | ||||
|     Number, | ||||
|     Integer, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Clone)] | ||||
| pub struct TreeNode { | ||||
|     pub name: String, | ||||
|     pub r#type: NodeType, | ||||
| } | ||||
|  | ||||
| impl TreeNode { | ||||
|     /// Merge two TreeNode | ||||
|     pub fn merge_with(&self, other: &Self) -> Self { | ||||
|         if !matches!(self.r#type, NodeType::String | NodeType::Object { .. }) { | ||||
|             panic!("Cannot merge!"); | ||||
|         } | ||||
|  | ||||
|         if !matches!(other.r#type, NodeType::String | NodeType::Object { .. }) { | ||||
|             panic!("Cannot merge other!"); | ||||
|         } | ||||
|  | ||||
|         let r#type = match (&self.r#type, &other.r#type) { | ||||
|             (NodeType::String, NodeType::String) => NodeType::String, | ||||
|             (NodeType::String, NodeType::Object { children }) | ||||
|             | (NodeType::Object { children }, NodeType::String) => NodeType::Object { | ||||
|                 children: children.clone(), | ||||
|             }, | ||||
|  | ||||
|             (NodeType::Object { children: c1 }, NodeType::Object { children: c2 }) => { | ||||
|                 let mut children = c1.clone(); | ||||
|                 children.append(&mut c2.clone()); | ||||
|                 NodeType::Object { children } | ||||
|             } | ||||
|  | ||||
|             (_, _) => unreachable!(), | ||||
|         }; | ||||
|  | ||||
|         TreeNode { | ||||
|             name: other.name.to_string(), | ||||
|             r#type, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Construct the tree of a given structure name | ||||
| pub 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) | ||||
| } | ||||
|  | ||||
| /// Build a structure tree using a schema | ||||
| 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, | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     if let Some(subschemas) = &schema.subschemas { | ||||
|         if let Some(all_of) = &subschemas.all_of { | ||||
|             assert!(!all_of.is_empty()); | ||||
|             let mut tree = | ||||
|                 build_tree_schema(expect_schema_object(&all_of[0]), struct_name, components); | ||||
|  | ||||
|             for other in all_of.iter().skip(1) { | ||||
|                 let other = build_tree_schema(expect_schema_object(other), struct_name, components); | ||||
|                 tree = tree.merge_with(&other); | ||||
|             } | ||||
|  | ||||
|             return tree; | ||||
|         } else { | ||||
|             panic!("Unsupported case!"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     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() | ||||
|                 .map(|s| s.properties.clone()) | ||||
|                 .unwrap_or_default() | ||||
|                 .iter() | ||||
|                 .map(|e| { | ||||
|                     let o = expect_schema_object(e.1); | ||||
|                     build_tree_schema(o, e.0, components) | ||||
|                 }) | ||||
|                 .collect::<Vec<_>>(); | ||||
|             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, | ||||
|     } | ||||
| } | ||||
							
								
								
									
										163
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										163
									
								
								src/main.rs
									
									
									
									
									
								
							| @@ -1,6 +1,5 @@ | ||||
| use clap::Parser; | ||||
| use okapi::openapi3::{Components, SchemaObject}; | ||||
| use okapi::schemars::schema::{InstanceType, Schema, SingleOrVec}; | ||||
| use openapi_parser::{build_tree, parse_schema}; | ||||
|  | ||||
| /// Dump the tree structure of a schema element as dot file | ||||
| #[derive(Parser, Debug)] | ||||
| @@ -18,56 +17,6 @@ struct Args { | ||||
|     struct_name: String, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Clone)] | ||||
| enum NodeType { | ||||
|     Null, | ||||
|     Boolean, | ||||
|     Array { item: Box<TreeNode> }, | ||||
|     Object { children: Vec<TreeNode> }, | ||||
|     String, | ||||
|     Number, | ||||
|     Integer, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Clone)] | ||||
| struct TreeNode { | ||||
|     name: String, | ||||
|     r#type: NodeType, | ||||
| } | ||||
|  | ||||
| impl TreeNode { | ||||
|     pub fn merge_with(&self, other: &Self) -> Self { | ||||
|         if !matches!(self.r#type, NodeType::String | NodeType::Object { .. }) { | ||||
|             panic!("Cannot merge!"); | ||||
|         } | ||||
|  | ||||
|         if !matches!(other.r#type, NodeType::String | NodeType::Object { .. }) { | ||||
|             panic!("Cannot merge other!"); | ||||
|         } | ||||
|  | ||||
|         let r#type = match (&self.r#type, &other.r#type) { | ||||
|             (NodeType::String, NodeType::String) => NodeType::String, | ||||
|             (NodeType::String, NodeType::Object { children }) | ||||
|             | (NodeType::Object { children }, NodeType::String) => NodeType::Object { | ||||
|                 children: children.clone(), | ||||
|             }, | ||||
|  | ||||
|             (NodeType::Object { children: c1 }, NodeType::Object { children: c2 }) => { | ||||
|                 let mut children = c1.clone(); | ||||
|                 children.append(&mut c2.clone()); | ||||
|                 NodeType::Object { children } | ||||
|             } | ||||
|  | ||||
|             (_, _) => unreachable!(), | ||||
|         }; | ||||
|  | ||||
|         TreeNode { | ||||
|             name: other.name.to_string(), | ||||
|             r#type, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn main() { | ||||
|     env_logger::init_from_env(env_logger::Env::new().default_filter_or("info")); | ||||
|  | ||||
| @@ -78,116 +27,8 @@ fn main() { | ||||
|         Some(path) => std::fs::read_to_string(path).expect("Unable to load schema file!"), | ||||
|     }; | ||||
|  | ||||
|     // Parse schema | ||||
|     let schema = serde_yaml::from_str::<okapi::openapi3::OpenApi>(&file_content) | ||||
|         .expect("Failed to parse document"); | ||||
|  | ||||
|     if schema.components.is_none() { | ||||
|         log::error!("components is missing!"); | ||||
|         panic!() | ||||
|     } | ||||
|  | ||||
|     let schema = parse_schema(&file_content); | ||||
|     let tree = build_tree(&args.struct_name, schema.components.as_ref().unwrap()); | ||||
|  | ||||
|     println!("{:#?}", tree); | ||||
| } | ||||
|  | ||||
| fn expect_single<E>(e: &SingleOrVec<E>) -> &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, | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     if let Some(subschemas) = &schema.subschemas { | ||||
|         if let Some(all_of) = &subschemas.all_of { | ||||
|             assert!(!all_of.is_empty()); | ||||
|             let mut tree = | ||||
|                 build_tree_schema(expect_schema_object(&all_of[0]), struct_name, components); | ||||
|  | ||||
|             for other in all_of.iter().skip(1) { | ||||
|                 let other = build_tree_schema(expect_schema_object(other), struct_name, components); | ||||
|                 tree = tree.merge_with(&other); | ||||
|             } | ||||
|  | ||||
|             return tree; | ||||
|         } else { | ||||
|             panic!("Unsupported case!"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     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() | ||||
|                 .map(|s| s.properties.clone()) | ||||
|                 .unwrap_or_default() | ||||
|                 .iter() | ||||
|                 .map(|e| { | ||||
|                     let o = expect_schema_object(e.1); | ||||
|                     build_tree_schema(o, e.0, components) | ||||
|                 }) | ||||
|                 .collect::<Vec<_>>(); | ||||
|             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, | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user