capability_example/
partially_grown_model.rs1crate::ix!();
3
4#[derive(SaveLoad,Builder,Getters,Setters,Clone,Debug,Serialize,Deserialize)]
5#[builder(setter(into))]
6#[getset(get = "pub", set = "pub")]
7#[serde(deny_unknown_fields)] pub struct PartiallyGrownModel {
9
10 #[builder(default)]
11 grower_inputs: Option<GrowerInputs>,
12
13 #[builder(default)]
14 maybe_ungrown_justified_grower_tree_configuration: Option<JustifiedGrowerTreeConfiguration>,
15
16 #[builder(default)]
17 maybe_ungrown_justified_string_skeleton: Option<JustifiedStringSkeleton>,
18
19 #[builder(default)]
20 maybe_ungrown_stripped_string_skeleton: Option<StrippedStringSkeleton>,
21
22 #[builder(default)]
23 maybe_ungrown_core_string_skeleton: Option<CoreStringSkeleton>,
24
25 #[builder(default)]
26 maybe_ungrown_annotated_leaf_holder_expansions: Option<AnnotatedLeafHolderExpansions>,
27}
28
29impl From<GrowerInputs> for PartiallyGrownModel {
30
31 fn from(grower_inputs: GrowerInputs) -> Self {
32 Self {
33 grower_inputs: Some(grower_inputs),
34 maybe_ungrown_justified_grower_tree_configuration: None,
35 maybe_ungrown_justified_string_skeleton: None,
36 maybe_ungrown_stripped_string_skeleton: None,
37 maybe_ungrown_core_string_skeleton: None,
38 maybe_ungrown_annotated_leaf_holder_expansions: None,
39 }
40 }
41}
42
43impl PartiallyGrownModel {
44
45 pub fn empty() -> Self {
46 Self {
47 grower_inputs: None,
48 maybe_ungrown_justified_grower_tree_configuration: None,
49 maybe_ungrown_justified_string_skeleton: None,
50 maybe_ungrown_stripped_string_skeleton: None,
51 maybe_ungrown_core_string_skeleton: None,
52 maybe_ungrown_annotated_leaf_holder_expansions: None,
53 }
54 }
55
56 pub fn essentially_empty(&self) -> bool {
57 self.grower_inputs.is_some() &&
58 self.maybe_ungrown_justified_grower_tree_configuration.is_none() &&
59 self.maybe_ungrown_justified_string_skeleton.is_none() &&
60 self.maybe_ungrown_stripped_string_skeleton.is_none() &&
61 self.maybe_ungrown_core_string_skeleton.is_none() &&
62 self.maybe_ungrown_annotated_leaf_holder_expansions.is_none()
63 }
64
65 pub fn validate(&self) -> Result<(), GrowerModelGenerationInvalidPartial> {
66
67 trace!("Starting validate for PartiallyGrownModel: {:?}", self);
68
69 if self.maybe_ungrown_justified_grower_tree_configuration.is_none() {
71
72 if self.maybe_ungrown_justified_string_skeleton.is_some() {
73 error!("JustifiedStringSkeleton present without JustifiedGrowerTreeConfiguration");
74 }
75
76 if self.maybe_ungrown_stripped_string_skeleton.is_some() {
77 error!("StrippedStringSkeleton present without JustifiedGrowerTreeConfiguration");
78 }
79
80 if self.maybe_ungrown_core_string_skeleton.is_some() {
81 error!("CoreStringSkeleton present without JustifiedGrowerTreeConfiguration");
82 }
83 if self.maybe_ungrown_annotated_leaf_holder_expansions.is_some() {
84 error!("AnnotatedLeafHolderExpansions present without JustifiedGrowerTreeConfiguration");
85 }
86
87 return Err(GrowerModelGenerationInvalidPartial::MissingJustifiedGrowerTreeConfiguration);
88 }
89
90 if self.maybe_ungrown_justified_string_skeleton.is_none() {
91
92 if self.maybe_ungrown_stripped_string_skeleton.is_some() {
93 error!("StrippedStringSkeleton present without JustifiedStringSkeleton");
94 }
95
96 if self.maybe_ungrown_core_string_skeleton.is_some() {
97 error!("CoreStringSkeleton present without JustifiedStringSkeleton");
98 }
99
100 if self.maybe_ungrown_annotated_leaf_holder_expansions.is_some() {
101 error!("AnnotatedLeafHolderExpansions present without JustifiedStringSkeleton");
102 }
103
104 return Err(GrowerModelGenerationInvalidPartial::MissingJustifiedStringSkeleton);
105 }
106
107 if self.maybe_ungrown_stripped_string_skeleton.is_none() {
108
109 if self.maybe_ungrown_core_string_skeleton.is_some() {
110 error!("CoreStringSkeleton present without StrippedStringSkeleton");
111 }
112
113 if self.maybe_ungrown_annotated_leaf_holder_expansions.is_some() {
114 error!("AnnotatedLeafHolderExpansions present without StrippedStringSkeleton");
115 }
116
117 return Err(GrowerModelGenerationInvalidPartial::MissingStrippedStringSkeleton);
118 }
119
120 if self.maybe_ungrown_core_string_skeleton.is_none() {
121
122 if self.maybe_ungrown_annotated_leaf_holder_expansions.is_some() {
123 error!("AnnotatedLeafHolderExpansions present without CoreStringSkeleton");
124 }
125 return Err(GrowerModelGenerationInvalidPartial::MissingCoreStringSkeleton);
126 }
127
128 if self.maybe_ungrown_annotated_leaf_holder_expansions.is_none() {
129 return Err(GrowerModelGenerationInvalidPartial::MissingAnnotatedLeafHolderExpansions);
130 }
131
132 info!("PartiallyGrownModel validation passed");
133 Ok(())
134 }
135}
136
137impl PartiallyGrownModel {
138 #[tracing::instrument(level = "trace", skip_all)]
139 pub async fn load_from_file_fuzzy<P: AsRef<std::path::Path> + Send + Sync>(
140 path: P,
141 ) -> Result<Self, FuzzyLoadPartiallyGrownModelError> {
142 use std::fs;
143
144 let raw_contents = fs::read_to_string(&path)?;
145 debug!(
146 "Read {} bytes from '{:?}' => attempting fuzzy parse of PartiallyGrownModel.",
147 raw_contents.len(),
148 path.as_ref()
149 );
150
151 let mut root: serde_json::Value = serde_json::from_str(&raw_contents)?;
153
154 recursively_flatten_fields(&mut root);
156
157 match try_deserialize_with_path::<PartiallyGrownModel>(&root) {
159 Ok(mut pg) => {
160 trace!("Direct parse of PartiallyGrownModel succeeded => returning without further fuzz.");
161 let did_fill = pg.try_filling_next_none_field_from_clipboard();
162 if did_fill {
163 pg.save_to_file(&path).await?;
164 }
165 return Ok(pg);
166 }
167 Err(e) => {
168 warn!("Direct parse of PartiallyGrownModel failed => will attempt subfield fuzzy logic. Error string: {}", e);
169 }
170 }
171
172 let mut root_obj = match root.as_object_mut() {
174 Some(obj) => obj,
175 None => {
176 return Err(FuzzyLoadPartiallyGrownModelError::RootOfJSONIsNotAnObjectForPartiallyGrownModel);
177 }
178 };
179
180
181 let grower_inputs_val = root_obj
183 .remove("grower_inputs")
184 .unwrap_or(serde_json::Value::Null);
185
186 let grower_inputs: GrowerInputs = if grower_inputs_val.is_null() {
187 match (|| {
189 let mut ctx = ClipboardContext::new().map_err(|e| {
190 std::io::Error::new(
191 std::io::ErrorKind::Other,
192 format!("Clipboard context creation error: {e}"),
193 )
194 })?;
195
196 let contents = ctx.get_contents().map_err(|e| {
197 std::io::Error::new(
198 std::io::ErrorKind::Other,
199 format!("Clipboard get_contents error: {e}"),
200 )
201 })?;
202
203 debug!("Clipboard contents retrieved: {}", contents);
204
205 let json_val: serde_json::Value = serde_json::from_str(&contents).map_err(|e| {
206 std::io::Error::new(
207 std::io::ErrorKind::InvalidData,
208 format!("Clipboard JSON parsing error: {e}"),
209 )
210 })?;
211
212 debug!("Parsed JSON value from clipboard for GrowerInputs: {:?}", json_val);
213
214 let gi = try_deserialize_with_path::<GrowerInputs>(&json_val).map_err(|e| {
216 std::io::Error::new(
217 std::io::ErrorKind::InvalidData,
218 format!("Clipboard to GrowerInputs parse error: {e}"),
219 )
220 })?;
221
222 Ok::<GrowerInputs, std::io::Error>(gi)
224 })() {
225 Ok(v) => {
226 trace!("Clipboard parse for 'grower_inputs' succeeded => using that value.");
227 v
228 }
229 Err(e) => {
230 error!("No JSON for 'grower_inputs' in file and clipboard parse failed => cannot continue. Error: {e}");
231 return Err(FuzzyLoadPartiallyGrownModelError::NoJsonForGrowerInputsAndClipboardParseFailed);
232 }
233 }
234 } else {
235 trace!("Attempting standard parse from JSON");
236 match try_deserialize_with_path(&grower_inputs_val) {
237 Ok(g) => g,
238 Err(e) => {
239 error!("Could not parse 'grower_inputs': {e}");
240 return Err(FuzzyLoadPartiallyGrownModelError::CouldNotParseGrowerInputs);
241 }
242 }
243 };
244
245 let mgc_val = root_obj
247 .remove("maybe_ungrown_justified_grower_tree_configuration")
248 .unwrap_or(serde_json::Value::Null);
249
250 let maybe_ungrown_justified_grower_tree_configuration = if mgc_val.is_null() {
251 match fuzzy_parse_clipboard_contents::<JustifiedGrowerTreeConfiguration>(false) {
252 Ok(obj) => {
253 trace!("Clipboard parse of JustifiedGrowerTreeConfiguration succeeded => using that value.");
254 Some(obj)
255 }
256 Err(e) => {
257 warn!("Clipboard parse of JustifiedGrowerTreeConfiguration failed => returning None. Error: {:?}", e);
258 None
259 }
260 }
261 } else {
262 match JustifiedGrowerTreeConfiguration::fuzzy_from_json_value(&mgc_val) {
263 Ok(obj) => Some(obj),
264 Err(e) => {
265 warn!("Fuzzy parse of JustifiedGrowerTreeConfiguration failed => returning None. Error: {:?}", e);
266 None
267 }
268 }
269 };
270
271 let msk_val = root_obj
273 .remove("maybe_ungrown_justified_string_skeleton")
274 .unwrap_or(serde_json::Value::Null);
275
276 let maybe_ungrown_justified_string_skeleton = if msk_val.is_null() {
277 match fuzzy_parse_clipboard_contents::<JustifiedStringSkeleton>(false) {
278 Ok(obj) => {
279 trace!("Clipboard parse of JustifiedStringSkeleton succeeded => using that value.");
280 Some(obj)
281 }
282 Err(e) => {
283 warn!("Clipboard parse of JustifiedStringSkeleton failed => returning None. Error: {:?}", e);
284 None
285 }
286 }
287 } else {
288 match JustifiedStringSkeleton::fuzzy_from_json_value(&msk_val) {
289 Ok(obj) => Some(obj),
290 Err(e) => {
291 warn!("Fuzzy parse of JustifiedStringSkeleton failed => returning None. Error: {:?}", e);
292 None
293 }
294 }
295 };
296
297 let stripped_val = root_obj
299 .remove("maybe_ungrown_stripped_string_skeleton")
300 .unwrap_or(serde_json::Value::Null);
301
302 let maybe_ungrown_stripped_string_skeleton = if stripped_val.is_null() {
303 match (|| {
304 let mut ctx = ClipboardContext::new().map_err(|e| {
305 std::io::Error::new(
306 std::io::ErrorKind::Other,
307 format!("Clipboard context creation error: {e}"),
308 )
309 })?;
310 let contents = ctx.get_contents().map_err(|e| {
311 std::io::Error::new(
312 std::io::ErrorKind::Other,
313 format!("Clipboard get_contents error: {e}"),
314 )
315 })?;
316 let json_val: serde_json::Value = serde_json::from_str(&contents).map_err(|e| {
317 std::io::Error::new(
318 std::io::ErrorKind::InvalidData,
319 format!("Clipboard JSON parsing error: {e}"),
320 )
321 })?;
322 try_deserialize_with_path::<StrippedStringSkeleton>(&json_val).map_err(|e| {
323 std::io::Error::new(
324 std::io::ErrorKind::InvalidData,
325 format!("Clipboard parse error for StrippedStringSkeleton: {e}"),
326 )
327 })
328 })() {
329 Ok(s) => {
330 trace!("Clipboard parse of StrippedStringSkeleton succeeded => using that value.");
331 Some(s)
332 }
333 Err(e) => {
334 warn!("Clipboard parse of StrippedStringSkeleton failed => returning None. Error: {:?}", e);
335 None
336 }
337 }
338 } else {
339 match try_deserialize_with_path(&stripped_val) {
340 Ok(s) => Some(s),
341 Err(e) => {
342 warn!("StrippedStringSkeleton parse error => returning None: {}", e);
343 None
344 }
345 }
346 };
347
348 let core_val = root_obj
350 .remove("maybe_ungrown_core_string_skeleton")
351 .unwrap_or(serde_json::Value::Null);
352
353 let maybe_ungrown_core_string_skeleton = if core_val.is_null() {
354 match fuzzy_parse_clipboard_contents::<CoreStringSkeleton>(false) {
355 Ok(cs) => {
356 trace!("Clipboard parse of CoreStringSkeleton succeeded => using that value.");
357 Some(cs)
358 }
359 Err(e) => {
360 warn!("Clipboard parse of CoreStringSkeleton failed => returning None. Error: {:?}", e);
361 None
362 }
363 }
364 } else {
365 match CoreStringSkeleton::fuzzy_from_json_value(&core_val) {
366 Ok(cs) => Some(cs),
367 Err(e) => {
368 warn!("Fuzzy parse of CoreStringSkeleton failed => returning None. Error: {:?}", e);
369 None
370 }
371 }
372 };
373
374 let ann_val = root_obj
376 .remove("maybe_ungrown_annotated_leaf_holder_expansions")
377 .unwrap_or(serde_json::Value::Null);
378
379 let maybe_ungrown_annotated_leaf_holder_expansions = if ann_val.is_null() {
380 match fuzzy_parse_clipboard_contents::<AnnotatedLeafHolderExpansions>(false) {
381 Ok(ann) => {
382 trace!("Clipboard parse of AnnotatedLeafHolderExpansions succeeded => using that value.");
383 Some(ann)
384 }
385 Err(e) => {
386 warn!("Clipboard parse of AnnotatedLeafHolderExpansions failed => returning None. Error: {:?}", e);
387 None
388 }
389 }
390 } else {
391 match AnnotatedLeafHolderExpansions::fuzzy_from_json_value(&ann_val) {
392 Ok(ann) => Some(ann),
393 Err(e) => {
394 warn!("Fuzzy parse of AnnotatedLeafHolderExpansions failed => returning None. Error: {:?}", e);
395 None
396 }
397 }
398 };
399
400 let partial = PartiallyGrownModel {
402 grower_inputs: Some(grower_inputs),
403 maybe_ungrown_justified_grower_tree_configuration,
404 maybe_ungrown_justified_string_skeleton,
405 maybe_ungrown_stripped_string_skeleton,
406 maybe_ungrown_core_string_skeleton,
407 maybe_ungrown_annotated_leaf_holder_expansions,
408 };
409
410 Ok(partial)
411 }
412}
413
414pub fn try_deserialize_with_path<T: DeserializeOwned>(val: &serde_json::Value)
417 -> Result<T, serde_json::Error>
418{
419 match from_value_pathaware::<T>(&val) {
420 Ok(parsed) => Ok(parsed),
421 Err(path_err) => {
422 eprintln!(
423 "Deserialization failed at path {path_err}",
424 );
425 Err(path_err)
426 }
427 }
428}