/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ //! Modules for simple representations of generic OpenAPI data from the yaml //! source, primarily for initial parsing. use std::collections::BTreeMap; use yaml_rust2::{Yaml, YamlLoader, yaml::Hash as YamlHash}; pub mod path; pub mod schema; use path::{OaPath, parse_path}; use schema::{OaSchema, parse_request_body, parse_schema}; /// A parsed OpenAPI yaml file, not yet interpreted. pub struct LoadedYaml { pub paths: BTreeMap, pub schemas: BTreeMap, } /// Parse the given yaml text as an OpenAPI specification. pub fn load_yaml(yaml_str: &str) -> Result> { let docs = YamlLoader::load_from_str(yaml_str)?; println!("yaml loaded"); let doc = docs.into_iter().next().ok_or("Empty YAML document")?; let paths = get_map_key(&doc, "paths").ok_or("Missing 'paths'")?; let components = get_map_key(&doc, "components").ok_or("Missing 'components'")?; let schemas = get_map_key(components, "schemas").ok_or("Missing 'components.schemas'")?; let request_bodies = get_map_key(components, "requestBodies").ok_or("Missing 'components.requestBodies'")?; println!("loaded roots"); let paths = paths .as_hash() .expect("paths should be a compound YAML ojbect") .into_iter() .filter_map(|(k, v)| k.as_str().map(|name| (name.to_string(), parse_path(v)))) .collect(); let mut schemas: BTreeMap = schemas .as_hash() .expect("schemas should be a compound YAML object") .into_iter() .filter_map(|(k, v)| k.as_str().map(|name| (name.to_string(), parse_schema(v)))) .collect(); let mut request_bodies: BTreeMap = request_bodies .as_hash() .expect("requestBodies should be a compound YAML object") .into_iter() .filter_map(|(k, v)| { k.as_str() .map(|name| (name.to_string(), parse_request_body(v))) }) .collect(); // Bundle the schemas from request bodies together with the rest of the // schemas. This *should* not cause any conflict (despite schemas and // request bodies being defined in separate sections of the spec file), // because the name of request bodies all seem to end with "RequestBody" // (e.g. "sendMailRequestBody"). schemas.append(&mut request_bodies); Ok(LoadedYaml { paths, schemas }) } fn get_map_key<'a>(y: &'a Yaml, key: &str) -> Option<&'a Yaml> { if let Some(h) = y.as_hash() { h.get(&Yaml::from_str(key)) } else { None } } fn get_str_in(h: &YamlHash, key: &str) -> Option { h.get(&Yaml::from_str(key))?.as_str().map(str::to_string) } fn get_bool_in(h: &YamlHash, key: &str) -> Option { match h.get(&Yaml::from_str(key)) { Some(Yaml::Boolean(b)) => Some(*b), _ => None, } } fn get_map_in<'a>(h: &'a YamlHash, key: &str) -> Option<&'a YamlHash> { match h.get(&Yaml::from_str(key)) { Some(Yaml::Hash(m)) => Some(m), _ => None, } } fn get_node_in<'a>(h: &'a YamlHash, key: &str) -> Option<&'a Yaml> { h.get(&Yaml::from_str(key)) } fn get_seq_in<'a>(h: &'a YamlHash, key: &str) -> Option<&'a Vec> { match h.get(&Yaml::from_str(key)) { Some(Yaml::Array(a)) => Some(a), _ => None, } }