1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
use super::*;

#[derive(Debug, Deserialize)]
pub struct MergeConfig {
    pub inputs: Vec<Input>,
    pub output: PathBuf,
    #[serde(skip)]
    pub source: PathBuf,
    #[serde(skip)]
    pub load_time: time::Duration,
}

impl MergeConfig {
    pub fn from_path(path: impl AsRef<Path>) -> io::Result<Self> {
        let now = time::Instant::now();
        let source = path.as_ref().to_path_buf();
        let merge = load_json_file(&source)?;
        let load_time = now.elapsed();

        Ok(Self {
            source,
            load_time,
            ..merge
        })
        // load_json_file::<Self>(path).inspect(|oam| {
        //     println!(
        //         "## Loaded the configuration: {} inputs ({:?})",
        //         oam.inputs.len(),
        //         now.elapsed()
        //     )
        // })
    }

    pub fn load_inputs(self) -> io::Result<Self> {
        let base = self.source.parent().unwrap_or(Path::new("."));

        self.inputs
            .into_iter()
            .map(|input| input.load(base))
            .collect::<io::Result<Vec<_>>>()
            .map(|inputs| Self { inputs, ..self })
    }

    pub fn merge(self) -> io::Result<()> {
        // Use first element is a base for merging
        let now = time::Instant::now();
        let mut inputs = self.inputs.into_iter();
        let base = inputs
            .next()
            .ok_or(io::Error::other("At least one input required"))?;
        let merged = inputs.fold(base, merge_into_base);
        println!(
            "## Inputs merged, writing the results out to '{}' ({:?})",
            self.output.display(),
            now.elapsed()
        );

        let now = time::Instant::now();
        save_json_file(&self.output, &merged.openapi)?;
        println!(
            "## Finished writing to '{}' ({:?})",
            self.output.display(),
            now.elapsed()
        );
        Ok(())
    }
}

fn merge_into_base(base: Input, mut other: Input) -> Input {
    tracing::info!(other = %other.source, "Processing");

    let mut openapi = base.openapi;
    openapi.merge_components(other.components());
    openapi.merge_security(other.security());
    openapi.merge_tags(other.tags());
    openapi.merge_extensions(other.extensions());

    for (path, method, operation) in other.operations() {
        openapi.merge_operation(path, method, operation);
    }

    Input { openapi, ..base }
}