use crate::pyjson::Shape;
use crate::{ProjectError, PyJson};
pub use secs_types::target::TargetCurve;
pub(crate) static TARGET_SHAPE: Shape = Shape::Record {
optional: &[],
nested: &[],
};
pub trait TargetJson: Sized {
fn to_json(&self) -> PyJson;
fn from_json(value: PyJson) -> Result<Self, ProjectError>;
}
impl TargetJson for TargetCurve {
fn to_json(&self) -> PyJson {
PyJson::Object(vec![(
"points".into(),
PyJson::Array(
self.points
.iter()
.map(|(frequency, level)| {
PyJson::Array(vec![PyJson::Float(*frequency), PyJson::Float(*level)])
})
.collect(),
),
)])
}
fn from_json(value: PyJson) -> Result<Self, ProjectError> {
let points = match &value {
PyJson::Null => &[][..],
PyJson::Array(points) => points.as_slice(),
PyJson::Object(_) => value
.get("points")
.map_or(&[][..], |value| value.array().unwrap_or(&[])),
_ => {
return Err(ProjectError::Invalid(
"target must be an object or array".into(),
));
}
};
Ok(Self::new(
points
.iter()
.map(|point| {
let pair = point.array().ok_or_else(|| {
ProjectError::Invalid("target point must be a pair".into())
})?;
if pair.len() != 2 {
return Err(ProjectError::Invalid(
"target point must contain two numbers".into(),
));
}
Ok((
crate::project::float(&pair[0])?,
crate::project::float(&pair[1])?,
))
})
.collect::<Result<Vec<_>, ProjectError>>()?,
))
}
}