openapi_tools 0.5.1

Tools for working with OpenAPI specs
Documentation
use std::path::PathBuf;

use openapiv3::{OpenAPI, PathItem, RefOr};

use crate::MergeArgs;

use super::SourceMap;

pub fn merge_impl(args: &MergeArgs, map: SourceMap) -> OpenAPI {
    let mut result = OpenAPI::default();
    merge_source_map(&mut result, &map);
    let sources = &map.sources;

    for (source, overrides) in sources {
        let spec = get_spec(&args, source, overrides);
        result = result.merge(spec).unwrap();
    }

    result
}

pub fn get_spec(args: &MergeArgs, source: &PathBuf, overrides: &Option<PathItem>) -> OpenAPI {
    let mut spec = get_source_spec(args, &source);

    if let Some(overrides) = overrides {
        for (_, item) in spec.paths.iter_mut() {
            if let RefOr::Item(item) = item {
                merge_path_item(item, &overrides);
            }
        }
    }

    spec
}

pub fn get_source_spec(args: &MergeArgs, source: &PathBuf) -> OpenAPI {
    let path = args.relative_path(&source);

    if args.verbose {
        eprintln!("getting source spec at path: {:?}", path);
    }

    let source_content = std::fs::read_to_string(&path).unwrap();
    let spec: OpenAPI = match source.extension().and_then(|ext| ext.to_str()) {
        Some("json") => serde_json::from_str(&source_content).unwrap(),
        Some("yml") => serde_yaml::from_str(&source_content).unwrap(),
        Some("yaml") => serde_yaml::from_str(&source_content).unwrap(),
        _ => {
            if let Ok(spec) = serde_json::from_str(&source_content) {
                spec
            } else {
                serde_yaml::from_str(&source_content).unwrap()
            }
        }
    };
    spec
}

pub fn merge_source_map(spec: &mut OpenAPI, map: &SourceMap) {
    if let Some(version) = &map.openapi {
        spec.openapi = version.clone();
    }
    if let Some(info) = &map.info {
        spec.info = info.clone();
    }
    if !map.servers.is_empty() {
        spec.servers = map.servers.clone();
    }
    if !map.security.is_empty() {
        spec.security = map.security.clone();
    }
    if !map.tags.is_empty() {
        spec.tags = map.tags.clone();
    }
    if let Some(external_docs) = &map.external_docs {
        spec.external_docs = Some(external_docs.clone());
    }
    if !map.extensions.is_empty() {
        spec.extensions = map.extensions.clone();
    }
    spec.paths = map.paths.clone();
}

pub fn merge_path_item(item: &mut PathItem, overrides: &PathItem) {
    if let Some(summary) = &overrides.summary {
        item.summary = Some(summary.clone());
    }
    if let Some(description) = &overrides.description {
        item.description = Some(description.clone());
    }
    if let Some(get) = &overrides.get {
        let get = get.clone();
        item.get = Some(get);
    }
    if let Some(put) = &overrides.put {
        let put = put.clone();
        item.put = Some(put);
    }
    if let Some(post) = &overrides.post {
        let post = post.clone();
        item.post = Some(post);
    }
    if let Some(delete) = &overrides.delete {
        let delete = delete.clone();
        item.delete = Some(delete);
    }
    if let Some(options) = &overrides.options {
        let options = options.clone();
        item.options = Some(options);
    }
    if let Some(head) = &overrides.head {
        let head = head.clone();
        item.head = Some(head);
    }
    if let Some(patch) = &overrides.patch {
        let patch = patch.clone();
        item.patch = Some(patch);
    }
    if let Some(trace) = &overrides.trace {
        let trace = trace.clone();
        item.trace = Some(trace);
    }
    if !overrides.servers.is_empty() {
        item.servers = overrides.servers.clone();
    }
    if !overrides.parameters.is_empty() {
        item.parameters = overrides.parameters.clone();
    }

    if !overrides.extensions.is_empty() {
        for (_, operation) in item.iter_mut() {
            operation.extensions = overrides.extensions.clone();
        }
    }
}