crate::ix!();
#[derive(SaveLoad,Builder,Getters,Setters,Clone,Debug,Serialize,Deserialize)]
#[builder(setter(into))]
#[getset(get = "pub", set = "pub")]
#[serde(deny_unknown_fields)] pub struct PartiallyGrownModel {
#[builder(default)]
grower_inputs: Option<GrowerInputs>,
#[builder(default)]
maybe_ungrown_justified_grower_tree_configuration: Option<JustifiedGrowerTreeConfiguration>,
#[builder(default)]
maybe_ungrown_justified_string_skeleton: Option<JustifiedStringSkeleton>,
#[builder(default)]
maybe_ungrown_stripped_string_skeleton: Option<StrippedStringSkeleton>,
#[builder(default)]
maybe_ungrown_core_string_skeleton: Option<CoreStringSkeleton>,
#[builder(default)]
maybe_ungrown_annotated_leaf_holder_expansions: Option<AnnotatedLeafHolderExpansions>,
}
impl From<GrowerInputs> for PartiallyGrownModel {
fn from(grower_inputs: GrowerInputs) -> Self {
Self {
grower_inputs: Some(grower_inputs),
maybe_ungrown_justified_grower_tree_configuration: None,
maybe_ungrown_justified_string_skeleton: None,
maybe_ungrown_stripped_string_skeleton: None,
maybe_ungrown_core_string_skeleton: None,
maybe_ungrown_annotated_leaf_holder_expansions: None,
}
}
}
impl PartiallyGrownModel {
pub fn empty() -> Self {
Self {
grower_inputs: None,
maybe_ungrown_justified_grower_tree_configuration: None,
maybe_ungrown_justified_string_skeleton: None,
maybe_ungrown_stripped_string_skeleton: None,
maybe_ungrown_core_string_skeleton: None,
maybe_ungrown_annotated_leaf_holder_expansions: None,
}
}
pub fn essentially_empty(&self) -> bool {
self.grower_inputs.is_some() &&
self.maybe_ungrown_justified_grower_tree_configuration.is_none() &&
self.maybe_ungrown_justified_string_skeleton.is_none() &&
self.maybe_ungrown_stripped_string_skeleton.is_none() &&
self.maybe_ungrown_core_string_skeleton.is_none() &&
self.maybe_ungrown_annotated_leaf_holder_expansions.is_none()
}
pub fn validate(&self) -> Result<(), GrowerModelGenerationInvalidPartial> {
trace!("Starting validate for PartiallyGrownModel: {:?}", self);
if self.maybe_ungrown_justified_grower_tree_configuration.is_none() {
if self.maybe_ungrown_justified_string_skeleton.is_some() {
error!("JustifiedStringSkeleton present without JustifiedGrowerTreeConfiguration");
}
if self.maybe_ungrown_stripped_string_skeleton.is_some() {
error!("StrippedStringSkeleton present without JustifiedGrowerTreeConfiguration");
}
if self.maybe_ungrown_core_string_skeleton.is_some() {
error!("CoreStringSkeleton present without JustifiedGrowerTreeConfiguration");
}
if self.maybe_ungrown_annotated_leaf_holder_expansions.is_some() {
error!("AnnotatedLeafHolderExpansions present without JustifiedGrowerTreeConfiguration");
}
return Err(GrowerModelGenerationInvalidPartial::MissingJustifiedGrowerTreeConfiguration);
}
if self.maybe_ungrown_justified_string_skeleton.is_none() {
if self.maybe_ungrown_stripped_string_skeleton.is_some() {
error!("StrippedStringSkeleton present without JustifiedStringSkeleton");
}
if self.maybe_ungrown_core_string_skeleton.is_some() {
error!("CoreStringSkeleton present without JustifiedStringSkeleton");
}
if self.maybe_ungrown_annotated_leaf_holder_expansions.is_some() {
error!("AnnotatedLeafHolderExpansions present without JustifiedStringSkeleton");
}
return Err(GrowerModelGenerationInvalidPartial::MissingJustifiedStringSkeleton);
}
if self.maybe_ungrown_stripped_string_skeleton.is_none() {
if self.maybe_ungrown_core_string_skeleton.is_some() {
error!("CoreStringSkeleton present without StrippedStringSkeleton");
}
if self.maybe_ungrown_annotated_leaf_holder_expansions.is_some() {
error!("AnnotatedLeafHolderExpansions present without StrippedStringSkeleton");
}
return Err(GrowerModelGenerationInvalidPartial::MissingStrippedStringSkeleton);
}
if self.maybe_ungrown_core_string_skeleton.is_none() {
if self.maybe_ungrown_annotated_leaf_holder_expansions.is_some() {
error!("AnnotatedLeafHolderExpansions present without CoreStringSkeleton");
}
return Err(GrowerModelGenerationInvalidPartial::MissingCoreStringSkeleton);
}
if self.maybe_ungrown_annotated_leaf_holder_expansions.is_none() {
return Err(GrowerModelGenerationInvalidPartial::MissingAnnotatedLeafHolderExpansions);
}
info!("PartiallyGrownModel validation passed");
Ok(())
}
}
impl PartiallyGrownModel {
#[tracing::instrument(level = "trace", skip_all)]
pub async fn load_from_file_fuzzy<P: AsRef<std::path::Path> + Send + Sync>(
path: P,
) -> Result<Self, FuzzyLoadPartiallyGrownModelError> {
use std::fs;
let raw_contents = fs::read_to_string(&path)?;
debug!(
"Read {} bytes from '{:?}' => attempting fuzzy parse of PartiallyGrownModel.",
raw_contents.len(),
path.as_ref()
);
let mut root: serde_json::Value = serde_json::from_str(&raw_contents)?;
recursively_flatten_fields(&mut root);
match try_deserialize_with_path::<PartiallyGrownModel>(&root) {
Ok(mut pg) => {
trace!("Direct parse of PartiallyGrownModel succeeded => returning without further fuzz.");
let did_fill = pg.try_filling_next_none_field_from_clipboard();
if did_fill {
pg.save_to_file(&path).await?;
}
return Ok(pg);
}
Err(e) => {
warn!("Direct parse of PartiallyGrownModel failed => will attempt subfield fuzzy logic. Error string: {}", e);
}
}
let mut root_obj = match root.as_object_mut() {
Some(obj) => obj,
None => {
return Err(FuzzyLoadPartiallyGrownModelError::RootOfJSONIsNotAnObjectForPartiallyGrownModel);
}
};
let grower_inputs_val = root_obj
.remove("grower_inputs")
.unwrap_or(serde_json::Value::Null);
let grower_inputs: GrowerInputs = if grower_inputs_val.is_null() {
match (|| {
let mut ctx = ClipboardContext::new().map_err(|e| {
std::io::Error::new(
std::io::ErrorKind::Other,
format!("Clipboard context creation error: {e}"),
)
})?;
let contents = ctx.get_contents().map_err(|e| {
std::io::Error::new(
std::io::ErrorKind::Other,
format!("Clipboard get_contents error: {e}"),
)
})?;
debug!("Clipboard contents retrieved: {}", contents);
let json_val: serde_json::Value = serde_json::from_str(&contents).map_err(|e| {
std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("Clipboard JSON parsing error: {e}"),
)
})?;
debug!("Parsed JSON value from clipboard for GrowerInputs: {:?}", json_val);
let gi = try_deserialize_with_path::<GrowerInputs>(&json_val).map_err(|e| {
std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("Clipboard to GrowerInputs parse error: {e}"),
)
})?;
Ok::<GrowerInputs, std::io::Error>(gi)
})() {
Ok(v) => {
trace!("Clipboard parse for 'grower_inputs' succeeded => using that value.");
v
}
Err(e) => {
error!("No JSON for 'grower_inputs' in file and clipboard parse failed => cannot continue. Error: {e}");
return Err(FuzzyLoadPartiallyGrownModelError::NoJsonForGrowerInputsAndClipboardParseFailed);
}
}
} else {
trace!("Attempting standard parse from JSON");
match try_deserialize_with_path(&grower_inputs_val) {
Ok(g) => g,
Err(e) => {
error!("Could not parse 'grower_inputs': {e}");
return Err(FuzzyLoadPartiallyGrownModelError::CouldNotParseGrowerInputs);
}
}
};
let mgc_val = root_obj
.remove("maybe_ungrown_justified_grower_tree_configuration")
.unwrap_or(serde_json::Value::Null);
let maybe_ungrown_justified_grower_tree_configuration = if mgc_val.is_null() {
match fuzzy_parse_clipboard_contents::<JustifiedGrowerTreeConfiguration>(false) {
Ok(obj) => {
trace!("Clipboard parse of JustifiedGrowerTreeConfiguration succeeded => using that value.");
Some(obj)
}
Err(e) => {
warn!("Clipboard parse of JustifiedGrowerTreeConfiguration failed => returning None. Error: {:?}", e);
None
}
}
} else {
match JustifiedGrowerTreeConfiguration::fuzzy_from_json_value(&mgc_val) {
Ok(obj) => Some(obj),
Err(e) => {
warn!("Fuzzy parse of JustifiedGrowerTreeConfiguration failed => returning None. Error: {:?}", e);
None
}
}
};
let msk_val = root_obj
.remove("maybe_ungrown_justified_string_skeleton")
.unwrap_or(serde_json::Value::Null);
let maybe_ungrown_justified_string_skeleton = if msk_val.is_null() {
match fuzzy_parse_clipboard_contents::<JustifiedStringSkeleton>(false) {
Ok(obj) => {
trace!("Clipboard parse of JustifiedStringSkeleton succeeded => using that value.");
Some(obj)
}
Err(e) => {
warn!("Clipboard parse of JustifiedStringSkeleton failed => returning None. Error: {:?}", e);
None
}
}
} else {
match JustifiedStringSkeleton::fuzzy_from_json_value(&msk_val) {
Ok(obj) => Some(obj),
Err(e) => {
warn!("Fuzzy parse of JustifiedStringSkeleton failed => returning None. Error: {:?}", e);
None
}
}
};
let stripped_val = root_obj
.remove("maybe_ungrown_stripped_string_skeleton")
.unwrap_or(serde_json::Value::Null);
let maybe_ungrown_stripped_string_skeleton = if stripped_val.is_null() {
match (|| {
let mut ctx = ClipboardContext::new().map_err(|e| {
std::io::Error::new(
std::io::ErrorKind::Other,
format!("Clipboard context creation error: {e}"),
)
})?;
let contents = ctx.get_contents().map_err(|e| {
std::io::Error::new(
std::io::ErrorKind::Other,
format!("Clipboard get_contents error: {e}"),
)
})?;
let json_val: serde_json::Value = serde_json::from_str(&contents).map_err(|e| {
std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("Clipboard JSON parsing error: {e}"),
)
})?;
try_deserialize_with_path::<StrippedStringSkeleton>(&json_val).map_err(|e| {
std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("Clipboard parse error for StrippedStringSkeleton: {e}"),
)
})
})() {
Ok(s) => {
trace!("Clipboard parse of StrippedStringSkeleton succeeded => using that value.");
Some(s)
}
Err(e) => {
warn!("Clipboard parse of StrippedStringSkeleton failed => returning None. Error: {:?}", e);
None
}
}
} else {
match try_deserialize_with_path(&stripped_val) {
Ok(s) => Some(s),
Err(e) => {
warn!("StrippedStringSkeleton parse error => returning None: {}", e);
None
}
}
};
let core_val = root_obj
.remove("maybe_ungrown_core_string_skeleton")
.unwrap_or(serde_json::Value::Null);
let maybe_ungrown_core_string_skeleton = if core_val.is_null() {
match fuzzy_parse_clipboard_contents::<CoreStringSkeleton>(false) {
Ok(cs) => {
trace!("Clipboard parse of CoreStringSkeleton succeeded => using that value.");
Some(cs)
}
Err(e) => {
warn!("Clipboard parse of CoreStringSkeleton failed => returning None. Error: {:?}", e);
None
}
}
} else {
match CoreStringSkeleton::fuzzy_from_json_value(&core_val) {
Ok(cs) => Some(cs),
Err(e) => {
warn!("Fuzzy parse of CoreStringSkeleton failed => returning None. Error: {:?}", e);
None
}
}
};
let ann_val = root_obj
.remove("maybe_ungrown_annotated_leaf_holder_expansions")
.unwrap_or(serde_json::Value::Null);
let maybe_ungrown_annotated_leaf_holder_expansions = if ann_val.is_null() {
match fuzzy_parse_clipboard_contents::<AnnotatedLeafHolderExpansions>(false) {
Ok(ann) => {
trace!("Clipboard parse of AnnotatedLeafHolderExpansions succeeded => using that value.");
Some(ann)
}
Err(e) => {
warn!("Clipboard parse of AnnotatedLeafHolderExpansions failed => returning None. Error: {:?}", e);
None
}
}
} else {
match AnnotatedLeafHolderExpansions::fuzzy_from_json_value(&ann_val) {
Ok(ann) => Some(ann),
Err(e) => {
warn!("Fuzzy parse of AnnotatedLeafHolderExpansions failed => returning None. Error: {:?}", e);
None
}
}
};
let partial = PartiallyGrownModel {
grower_inputs: Some(grower_inputs),
maybe_ungrown_justified_grower_tree_configuration,
maybe_ungrown_justified_string_skeleton,
maybe_ungrown_stripped_string_skeleton,
maybe_ungrown_core_string_skeleton,
maybe_ungrown_annotated_leaf_holder_expansions,
};
Ok(partial)
}
}
pub fn try_deserialize_with_path<T: DeserializeOwned>(val: &serde_json::Value)
-> Result<T, serde_json::Error>
{
match from_value_pathaware::<T>(&val) {
Ok(parsed) => Ok(parsed),
Err(path_err) => {
eprintln!(
"Deserialization failed at path {path_err}",
);
Err(path_err)
}
}
}