use super::modifier::ColorModifier;
#[derive(Debug, Clone)]
pub struct ColorDescriptor {
pub iscc_nbs_number: u16,
pub modifier: ColorModifier,
pub standard_name: String,
pub extended_name: String,
pub semantic_name: Option<String>,
pub semantic_alternates: Vec<String>,
pub nearest_semantic: Option<(String, f64)>,
pub shade: String,
}
impl ColorDescriptor {
pub fn standard_descriptor(&self) -> String {
self.modifier.format(&self.standard_name)
}
pub fn extended_descriptor(&self) -> String {
self.modifier.format(&self.extended_name)
}
pub fn semantic_descriptor(&self) -> Option<String> {
self.semantic_name
.as_ref()
.map(|name| self.modifier.format(name))
}
pub fn all_semantic_descriptors(&self) -> Vec<String> {
let mut result = Vec::new();
if let Some(ref name) = self.semantic_name {
result.push(self.modifier.format(name));
}
for alt in &self.semantic_alternates {
result.push(self.modifier.format(alt));
}
result
}
pub fn has_semantic_match(&self) -> bool {
self.semantic_name.is_some()
}
pub fn nearest_semantic_descriptor(&self) -> Option<(String, f64)> {
self.nearest_semantic
.as_ref()
.map(|(name, dist)| (self.modifier.format(name), *dist))
}
pub fn semantic_match_count(&self) -> usize {
if self.semantic_name.is_some() {
1 + self.semantic_alternates.len()
} else {
0
}
}
pub fn has_alternate_name(&self) -> bool {
self.extended_name != self.standard_name
}
}
impl std::fmt::Display for ColorDescriptor {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.standard_descriptor())
}
}
#[cfg(test)]
mod tests {
use super::*;
fn sample_descriptor() -> ColorDescriptor {
ColorDescriptor {
iscc_nbs_number: 43,
modifier: ColorModifier::Moderate,
standard_name: "reddish brown".to_string(),
extended_name: "reddish brown".to_string(),
semantic_name: Some("rust".to_string()),
semantic_alternates: vec!["brown".to_string()],
nearest_semantic: Some(("rust".to_string(), 0.25)),
shade: "brown".to_string(),
}
}
#[test]
fn test_standard_descriptor() {
let desc = sample_descriptor();
assert_eq!(desc.standard_descriptor(), "moderate reddish brown");
}
#[test]
fn test_extended_descriptor_same() {
let desc = sample_descriptor();
assert_eq!(desc.extended_descriptor(), "moderate reddish brown");
}
#[test]
fn test_extended_descriptor_different() {
let mut desc = sample_descriptor();
desc.standard_name = "yellow green".to_string();
desc.extended_name = "lime".to_string();
desc.modifier = ColorModifier::Vivid;
assert_eq!(desc.standard_descriptor(), "vivid yellow green");
assert_eq!(desc.extended_descriptor(), "vivid lime");
}
#[test]
fn test_semantic_descriptor() {
let desc = sample_descriptor();
assert_eq!(desc.semantic_descriptor(), Some("moderate rust".to_string()));
}
#[test]
fn test_semantic_descriptor_none() {
let mut desc = sample_descriptor();
desc.semantic_name = None;
assert_eq!(desc.semantic_descriptor(), None);
}
#[test]
fn test_all_semantic_descriptors() {
let desc = sample_descriptor();
let all = desc.all_semantic_descriptors();
assert_eq!(all.len(), 2);
assert_eq!(all[0], "moderate rust");
assert_eq!(all[1], "moderate brown");
}
#[test]
fn test_has_semantic_match() {
let desc = sample_descriptor();
assert!(desc.has_semantic_match());
let mut no_semantic = sample_descriptor();
no_semantic.semantic_name = None;
assert!(!no_semantic.has_semantic_match());
}
#[test]
fn test_nearest_semantic_descriptor() {
let desc = sample_descriptor();
let (name, dist) = desc.nearest_semantic_descriptor().unwrap();
assert_eq!(name, "moderate rust");
assert!((dist - 0.25).abs() < 0.001);
}
#[test]
fn test_semantic_match_count() {
let desc = sample_descriptor();
assert_eq!(desc.semantic_match_count(), 2);
let mut no_semantic = sample_descriptor();
no_semantic.semantic_name = None;
no_semantic.semantic_alternates.clear();
assert_eq!(no_semantic.semantic_match_count(), 0);
}
#[test]
fn test_has_alternate_name() {
let desc = sample_descriptor();
assert!(!desc.has_alternate_name());
let mut with_alt = sample_descriptor();
with_alt.extended_name = "rust".to_string();
assert!(with_alt.has_alternate_name());
}
#[test]
fn test_display() {
let desc = sample_descriptor();
assert_eq!(format!("{}", desc), "moderate reddish brown");
}
#[test]
fn test_compound_modifier_in_descriptor() {
let desc = ColorDescriptor {
iscc_nbs_number: 22,
modifier: ColorModifier::IshGray,
standard_name: "red".to_string(),
extended_name: "red".to_string(),
semantic_name: None,
semantic_alternates: vec![],
nearest_semantic: None,
shade: "gray".to_string(),
};
assert_eq!(desc.standard_descriptor(), "reddish gray");
}
#[test]
fn test_no_modifier() {
let desc = ColorDescriptor {
iscc_nbs_number: 263,
modifier: ColorModifier::None,
standard_name: "white".to_string(),
extended_name: "white".to_string(),
semantic_name: Some("white".to_string()),
semantic_alternates: vec![],
nearest_semantic: Some(("white".to_string(), 0.0)),
shade: "white".to_string(),
};
assert_eq!(desc.standard_descriptor(), "white");
assert_eq!(desc.semantic_descriptor(), Some("white".to_string()));
}
}