hydrate_pipeline/
project.rs1use hydrate_data::PathReferenceNamespaceResolver;
2use serde::{Deserialize, Serialize};
3use std::error::Error;
4use std::path::{Path, PathBuf};
5
6#[derive(Serialize, Deserialize)]
7pub struct NamePathPairJson {
8 pub name: String,
9 pub path: String,
10}
11
12#[derive(Serialize, Deserialize)]
13pub struct SchemaCodegenJobsJson {
14 name: String,
15 schema_path: String,
16 included_schema_paths: Vec<String>,
17 outfile: String,
18}
19
20#[derive(Serialize, Deserialize)]
21pub struct HydrateProjectConfigurationJson {
22 pub schema_def_paths: Vec<String>,
23 pub import_data_path: String,
24 pub build_data_path: String,
25 pub job_data_path: String,
26 pub id_based_asset_sources: Vec<NamePathPairJson>,
27 pub path_based_asset_sources: Vec<NamePathPairJson>,
28 pub source_file_locations: Vec<NamePathPairJson>,
29 pub schema_codegen_jobs: Vec<SchemaCodegenJobsJson>,
30}
31
32#[derive(Debug, Clone)]
33pub struct NamePathPair {
34 pub name: String,
35 pub path: PathBuf,
36}
37
38#[derive(Debug, Clone)]
39pub struct SchemaCodegenJobs {
40 pub name: String,
41 pub schema_path: PathBuf,
42 pub included_schema_paths: Vec<PathBuf>,
43 pub outfile: PathBuf,
44}
45
46#[derive(Debug, Clone)]
47pub struct HydrateProjectConfiguration {
48 pub schema_def_paths: Vec<PathBuf>,
50
51 pub import_data_path: PathBuf,
53
54 pub build_data_path: PathBuf,
56
57 pub job_data_path: PathBuf,
59
60 pub id_based_asset_sources: Vec<NamePathPair>,
62 pub path_based_asset_sources: Vec<NamePathPair>,
64 pub source_file_locations: Vec<NamePathPair>,
67
68 pub schema_codegen_jobs: Vec<SchemaCodegenJobs>,
69}
70
71impl PathReferenceNamespaceResolver for HydrateProjectConfiguration {
72 fn namespace_root(
73 &self,
74 namespace: &str,
75 ) -> Option<PathBuf> {
76 for src in &self.id_based_asset_sources {
77 if src.name == namespace {
78 return Some(src.path.clone());
79 }
80 }
81
82 for src in &self.path_based_asset_sources {
83 if src.name == namespace {
84 return Some(src.path.clone());
85 }
86 }
87
88 for src in &self.source_file_locations {
89 if src.name == namespace {
90 return Some(src.path.clone());
91 }
92 }
93
94 None
95 }
96
97 fn simplify_path(
98 &self,
99 path: &Path,
100 ) -> Option<(String, PathBuf)> {
101 for src in &self.id_based_asset_sources {
102 if let Ok(path) = path.strip_prefix(&src.path) {
103 return Some((src.name.clone(), path.to_path_buf()));
104 }
105 }
106
107 for src in &self.path_based_asset_sources {
108 if let Ok(path) = path.strip_prefix(&src.path) {
109 return Some((src.name.clone(), path.to_path_buf()));
110 }
111 }
112
113 for src in &self.source_file_locations {
114 if let Ok(path) = path.strip_prefix(&src.path) {
115 return Some((src.name.clone(), path.to_path_buf()));
116 }
117 }
118
119 None
120 }
121}
122
123impl HydrateProjectConfiguration {
124 pub fn unverified_absolute_path(
125 root_path: &Path,
126 json_path: &str,
127 ) -> PathBuf {
128 if Path::new(json_path).is_absolute() {
129 PathBuf::from(json_path)
130 } else {
131 root_path.join(json_path)
132 }
133 }
134
135 pub fn parse_dir_path(
138 root_path: &Path,
139 json_path: &str,
140 ) -> Result<PathBuf, Box<dyn Error>> {
141 let joined_path = Self::unverified_absolute_path(root_path, json_path);
143
144 if !joined_path.exists() {
146 std::fs::create_dir_all(&joined_path)?;
147 }
148
149 Ok(dunce::canonicalize(&joined_path).map_err(|e| e.to_string())?)
151 }
152
153 pub fn read_from_path(path: &Path) -> Result<Self, Box<dyn Error>> {
154 let root_path = dunce::canonicalize(
155 path.parent()
156 .ok_or_else(|| "Parent of project file path could not be found".to_string())?,
157 )?;
158 let file_contents = std::fs::read_to_string(path)?;
159 let project_file: HydrateProjectConfigurationJson = serde_json::from_str(&file_contents)?;
160
161 let import_data_path = Self::parse_dir_path(&root_path, &project_file.import_data_path)?;
162 let build_data_path = Self::parse_dir_path(&root_path, &project_file.build_data_path)?;
163 let job_data_path = Self::parse_dir_path(&root_path, &project_file.job_data_path)?;
164
165 let mut schema_def_paths = Vec::default();
166 for path in &project_file.schema_def_paths {
167 schema_def_paths.push(Self::parse_dir_path(&root_path, path)?)
168 }
169
170 let mut id_based_asset_sources = Vec::default();
171 for pair in project_file.id_based_asset_sources {
172 id_based_asset_sources.push(NamePathPair {
173 name: pair.name,
174 path: Self::parse_dir_path(&root_path, &pair.path)?,
175 });
176 }
177
178 let mut path_based_asset_sources = Vec::default();
179 for pair in project_file.path_based_asset_sources {
180 path_based_asset_sources.push(NamePathPair {
181 name: pair.name,
182 path: Self::parse_dir_path(&root_path, &pair.path)?,
183 });
184 }
185
186 let mut source_file_locations = Vec::default();
187 for pair in project_file.source_file_locations {
188 source_file_locations.push(NamePathPair {
189 name: pair.name,
190 path: Self::parse_dir_path(&root_path, &pair.path)?,
191 });
192 }
193
194 let mut schema_codegen_jobs = Vec::default();
196 for schema_codegen_job in project_file.schema_codegen_jobs {
197 let mut included_schema_paths = Vec::default();
198 for included_schema_path in &schema_codegen_job.included_schema_paths {
199 included_schema_paths.push(Self::unverified_absolute_path(
200 &root_path,
201 included_schema_path,
202 ));
203 }
204
205 schema_codegen_jobs.push(SchemaCodegenJobs {
206 name: schema_codegen_job.name,
207 schema_path: Self::unverified_absolute_path(
208 &root_path,
209 &schema_codegen_job.schema_path,
210 ),
211 included_schema_paths,
212 outfile: Self::unverified_absolute_path(&root_path, &schema_codegen_job.outfile),
213 })
214 }
215
216 Ok(HydrateProjectConfiguration {
217 schema_def_paths,
218 import_data_path,
219 build_data_path,
220 job_data_path,
221 id_based_asset_sources,
222 path_based_asset_sources,
223 source_file_locations,
224 schema_codegen_jobs,
225 })
226 }
227
228 pub fn locate_project_file(search_location: &Path) -> Result<Self, Box<dyn Error>> {
229 let mut path = Some(search_location.to_path_buf());
230 while let Some(p) = path {
231 let joined_path = p.join("hydrate_project.json");
232 if joined_path.exists() {
233 log::info!("Using project configuration at {:?}", joined_path);
234 return Self::read_from_path(&joined_path);
235 }
236
237 path = p.parent().map(|x| x.to_path_buf());
238 }
239
240 Err(format!(
241 "hydrate_project.json could not be located at {:?} or in any of its parent directories",
242 search_location
243 ))?
244 }
245}