use crate::annotations::encode_geometric::{SCOPES, SPACES};
use crate::annotations::header::{is_geometric_kind, kinds, RESERVED_KINDS, TASKS};
use crate::geometry::grid::AXIS_KINDS;
use crate::h5::attrs;
use crate::json::repr_str;
use crate::labels::CLOSURES;
use crate::transforms::model::{TRANSFORM_KINDS, VECTOR_SPACES};
use crate::version::LATEST_VERSION;
use crate::{Error, Result};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Met {
First,
Reading(&'static str),
InUse,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Later {
pub what: &'static str,
pub value: String,
pub met: Met,
}
fn later(what: &'static str, value: &str, known: &[&str], met: Met) -> Option<Later> {
(!known.contains(&value)).then(|| Later { what, value: value.to_string(), met })
}
pub fn grid_value(group: &hdf5::Group) -> Result<Option<Later>> {
let kinds = attrs::get_strs(group, "axis_kinds")?.unwrap_or_default();
Ok(kinds.iter().find_map(|k| later("axis kind", k, &AXIS_KINDS, Met::Reading("E110"))))
}
pub fn annotation_value(group: &hdf5::Group) -> Result<Option<Later>> {
let text = |name: &str| attrs::get_str(group, name);
let Some(kind) = text("kind")? else { return Ok(None) };
if RESERVED_KINDS.contains(&kind.as_str()) {
return Ok(Some(Later { what: "kind", value: kind, met: Met::First }));
}
let known = kinds(crate::version::implemented()).unwrap_or_default();
if let Some(found) = later("kind", &kind, known, Met::First) {
return Ok(Some(found));
}
let mut checked: Vec<(&'static str, &[&str], Met)> =
vec![("task", &TASKS, Met::Reading("E412")), ("closure", &CLOSURES, Met::Reading("E412"))];
if is_geometric_kind(&kind) {
checked.push(("space", &SPACES, Met::InUse));
}
if kind == "classification" {
checked.push(("scope", &SCOPES, Met::InUse));
}
for (name, known, met) in checked {
if let Some(found) = text(name)?.and_then(|value| later(name, &value, known, met)) {
return Ok(Some(found));
}
}
Ok(None)
}
pub fn transform_value(group: &hdf5::Group) -> Result<Option<Later>> {
let Some(kind) = attrs::get_str(group, "kind")? else { return Ok(None) };
if let Some(found) = later("kind", &kind, &TRANSFORM_KINDS, Met::First) {
return Ok(Some(found));
}
if matches!(kind.as_str(), "displacement" | "bspline") {
let space = attrs::get_str(group, "vector_space")?.unwrap_or_else(|| "world".into());
return Ok(later("vector space", &space, &VECTOR_SPACES, Met::InUse));
}
Ok(None)
}
pub fn read_up_to<T>(value: &Later, read: impl FnOnce() -> Result<T>) -> Result<()> {
match value.met {
Met::First => Ok(()),
Met::Reading(code) => match read() {
Err(e) if e.code() != Some(code) => Err(e),
_ => Ok(()),
},
Met::InUse => read().map(|_| ()),
}
}
pub fn holds(what: &str, id: &str, value: &Later, version: &str) -> Error {
Error::coded(
"W913",
format!(
"{what} {} holds {} {}, which MEDH5 {LATEST_VERSION} does not define: this engine reads MEDH5 {version} \
as its {LATEST_VERSION} projection, which leaves the {what} out (1.1 §2.2)",
repr_str(id),
value.what,
repr_str(&value.value)
),
)
}
pub fn depends(what: &str, id: &str, relation: &str, other: &str, other_id: &str, cause: &Error) -> Error {
Error::coded(
"W913",
format!(
"{what} {} {relation} {other} {}, which the projection leaves out: {}",
repr_str(id),
repr_str(other_id),
cause.message()
),
)
}