mithril_build_script/
open_api.rs1use semver::Version;
2use std::collections::BTreeMap;
3use std::fs;
4use std::path::{Path, PathBuf};
5
6type OpenAPIFileName = String;
7type OpenAPIVersionRaw = String;
8
9const TYPE_ALIAS: &str = r"/// Open API file name
10pub type OpenAPIFileName = String;
11";
12
13pub fn list_all_open_api_spec_files(paths: &[&Path]) -> Vec<PathBuf> {
15 let mut open_api_spec_files = Vec::new();
16
17 for path in paths {
18 for entry in crate::list_files_in_folder(path).filter(|e| {
19 let os_filename = e.file_name();
20 let filename = os_filename.to_string_lossy();
21 filename.starts_with("openapi") && filename.ends_with(".yaml")
22 }) {
23 open_api_spec_files.push(entry.path())
24 }
25 }
26
27 open_api_spec_files
28}
29
30fn read_version_from_open_api_spec_file<P: AsRef<Path>>(spec_file_path: P) -> OpenAPIVersionRaw {
31 let yaml_spec = fs::read_to_string(spec_file_path).unwrap();
32 let open_api: serde_yaml::Value = serde_yaml::from_str(&yaml_spec).unwrap();
33 open_api["info"]["version"].as_str().unwrap().to_owned()
34}
35
36pub fn generate_open_api_versions_mapping(open_api_spec_files: &[PathBuf]) -> String {
38 let open_api_versions: BTreeMap<OpenAPIFileName, Version> = open_api_spec_files
40 .iter()
41 .map(|path| (path.clone(), read_version_from_open_api_spec_file(path)))
42 .map(|(path, version_raw)| {
43 (
44 path.file_name().unwrap().to_string_lossy().to_string(),
45 Version::parse(&version_raw).unwrap(),
46 )
47 })
48 .collect();
49
50 let mut open_api_versions_hashmap = String::new();
51 for (filename, version) in open_api_versions {
52 open_api_versions_hashmap.push_str(&format!(
53 r#"("{filename}".to_string(), semver::Version::new({}, {}, {})), "#,
54 version.major, version.minor, version.patch
55 ));
56 }
57
58 format!(
59 r#"{TYPE_ALIAS}
60/// Build Open API versions mapping
61pub fn get_open_api_versions_mapping() -> HashMap<OpenAPIFileName, semver::Version> {{
62 HashMap::from([
63 {open_api_versions_hashmap}
64 ])
65}}
66 "#
67 )
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73 use crate::get_temp_dir;
74 use std::path::Path;
75
76 fn write_minimal_open_api_file(version: &str, path: &Path) {
77 fs::write(
78 path,
79 format!(
80 r#"openapi: "3.0.0"
81info:
82 version: {version}
83 title: Minimal Open Api File
84"#
85 ),
86 )
87 .unwrap()
88 }
89
90 fn assert_open_api_content_contains(expected_content: &str, generated_code: &str) {
91 assert!(
92 generated_code.contains(expected_content),
93 "generated code did not include expected openapi files entries:\
94 \n---- Code that was expected to be included:\n{expected_content}\
95 \n---- Actual generated code:{}",
96 generated_code.trim_start_matches(TYPE_ALIAS)
98 );
99 }
100
101 #[test]
102 fn generated_code_include_type_aliases() {
103 let open_api_spec_files = list_all_open_api_spec_files(&[Path::new("./")]);
104 let generated_code = generate_open_api_versions_mapping(&open_api_spec_files);
105
106 assert!(generated_code.contains(TYPE_ALIAS));
107 }
108
109 #[test]
110 fn generated_function_returns_an_hashmap_of_open_api_file_name_and_semver_version() {
111 let open_api_spec_files = list_all_open_api_spec_files(&[Path::new("./")]);
112 let generated_code = generate_open_api_versions_mapping(&open_api_spec_files);
113
114 assert!(generated_code.contains("-> HashMap<OpenAPIFileName, semver::Version>"));
115 }
116
117 #[test]
118 fn generate_code_from_a_simple_open_api_file() {
119 let dir = get_temp_dir("generate_code_from_a_simple_open_api_file");
120 write_minimal_open_api_file("1.0.0", &dir.join("openapi.yaml"));
121
122 let expected = r#"("openapi.yaml".to_string(), semver::Version::new(1, 0, 0))"#;
123 let open_api_spec_files = list_all_open_api_spec_files(&[&dir]);
124 let generated_code = generate_open_api_versions_mapping(&open_api_spec_files);
125
126 assert_open_api_content_contains(expected, &generated_code);
127 }
128
129 #[test]
130 fn only_read_yaml_files() {
131 let dir = get_temp_dir("only_read_yaml_files");
132 write_minimal_open_api_file("1.0.0", &dir.join("openapi.yaml"));
133 fs::write(dir.join("openapi.json"), "{}").unwrap();
134
135 let included_files = list_all_open_api_spec_files(&[&dir]);
136
137 assert_eq!(vec![dir.join("openapi.yaml")], included_files);
138 }
139
140 #[test]
141 fn generate_code_from_two_open_api_files_in_different_folders() {
142 let sub_folder =
143 get_temp_dir("generate_code_from_two_open_api_files_in_different_folders/subfolder");
144 let parent_folder = sub_folder.parent().unwrap();
145 write_minimal_open_api_file("1.0.0", &parent_folder.join("openapi.yaml"));
146 write_minimal_open_api_file("2.0.0", &sub_folder.join("openapi-thales.yaml"));
147
148 let expected = r#"("openapi-thales.yaml".to_string(), semver::Version::new(2, 0, 0)), ("openapi.yaml".to_string(), semver::Version::new(1, 0, 0))"#;
149 let open_api_spec_files = list_all_open_api_spec_files(&[parent_folder, &sub_folder]);
150 let generated_code = generate_open_api_versions_mapping(&open_api_spec_files);
151
152 assert_open_api_content_contains(expected, &generated_code);
153 }
154
155 #[test]
156 fn when_colliding_filenames_version_is_read_from_latest_given_folder() {
157 let sub_folder = get_temp_dir(
158 "when_colliding_filenames_version_read_is_from_latest_given_folder/subfolder",
159 );
160 let parent_folder = sub_folder.parent().unwrap();
161 write_minimal_open_api_file("1.0.0", &parent_folder.join("openapi.yaml"));
162 write_minimal_open_api_file("2.0.0", &sub_folder.join("openapi.yaml"));
163
164 let expected = r#"HashMap::from([
165 ("openapi.yaml".to_string(), semver::Version::new(2, 0, 0)),
166 ])"#;
167 let open_api_spec_files = list_all_open_api_spec_files(&[parent_folder, &sub_folder]);
168 let generated_code = generate_open_api_versions_mapping(&open_api_spec_files);
169
170 assert_open_api_content_contains(expected, &generated_code);
171 }
172}