use std::{collections::HashSet, path::PathBuf};
use serde::{Deserialize, Serialize};
use validatrix::{Accumulator, Validate};
pub type Labels = Vec<String>;
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(deny_unknown_fields)]
pub struct ImageLabel {
pub version: super::ConstrainedVersion,
pub colors: Option<Vec<ImageLabelColor>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub properties: Option<Vec<ImageLabelProperties>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub source: Option<ImageLabelSource>,
}
impl Validate for ImageLabel {
fn validate_inner(&self, accum: &mut validatrix::Accumulator) {
if let Some(c) = self.colors.as_ref() {
accum.with_key("colors", |a| {
if c.is_empty() {
a.add_failure("empty");
}
validate_unique_labels(a, c.iter())
});
}
if let Some(p) = self.properties.as_ref() {
accum.with_key("properties", |a| {
if p.is_empty() {
a.add_failure("empty");
}
validate_unique_labels(a, p.iter());
});
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(deny_unknown_fields)]
pub struct ImageLabelColor {
#[serde(rename = "label-value")]
pub label_value: u64,
pub rgba: [u8; 4],
}
pub(crate) fn validate_unique_labels<'a, T: HasLabelValue + 'a>(
accum: &mut Accumulator,
it: impl IntoIterator<Item = &'a T>,
) {
let mut set: HashSet<u64> = HashSet::default();
for (idx, lbl) in it.into_iter().map(HasLabelValue::get_label).enumerate() {
if !set.insert(lbl) {
accum.add_failure_at(idx, format!("repeated label {lbl}"));
}
}
}
pub(crate) trait HasLabelValue {
fn get_label(&self) -> u64;
}
impl HasLabelValue for ImageLabelColor {
fn get_label(&self) -> u64 {
self.label_value
}
}
impl HasLabelValue for ImageLabelProperties {
fn get_label(&self) -> u64 {
self.label_value
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ImageLabelProperties {
#[serde(rename = "label-value")]
pub label_value: u64,
#[serde(flatten)]
pub properties: serde_json::Map<String, serde_json::Value>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ImageLabelSource {
pub image: Option<PathBuf>,
}
#[cfg(test)]
mod tests {
use crate::v0_4::OmeNgffGroupAttributes;
use super::*;
#[test]
fn labels_color_properties() {
let json = include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/ome-zarr/specifications/0.4/examples/label_strict/colors_properties.json"
));
let ome_metadata: OmeNgffGroupAttributes = serde_json::from_str(json).unwrap();
let _image_label: ImageLabel = ome_metadata.image_label.unwrap();
}
}