pub struct ScaleOrdinal {
domain: Vec<String>,
range: Vec<String>,
}
impl ScaleOrdinal {
pub fn new(domain: Vec<String>, range: Vec<String>) -> Self {
Self { domain, range }
}
pub fn map(&self, value: &str) -> Option<&str> {
if self.range.is_empty() {
return None;
}
let index = self.domain.iter().position(|d| d == value)?;
Some(&self.range[index % self.range.len()])
}
pub fn map_index(&self, index: usize) -> Option<&str> {
if self.range.is_empty() {
return None;
}
Some(&self.range[index % self.range.len()])
}
pub fn domain(&self) -> &[String] {
&self.domain
}
pub fn range(&self) -> &[String] {
&self.range
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ordinal_basic_mapping() {
let scale = ScaleOrdinal::new(
vec!["a".into(), "b".into(), "c".into()],
vec!["red".into(), "green".into(), "blue".into()],
);
assert_eq!(scale.map("a"), Some("red"));
assert_eq!(scale.map("b"), Some("green"));
assert_eq!(scale.map("c"), Some("blue"));
}
#[test]
fn ordinal_cycling() {
let scale = ScaleOrdinal::new(
vec!["a".into(), "b".into(), "c".into(), "d".into()],
vec!["red".into(), "green".into()],
);
assert_eq!(scale.map("a"), Some("red"));
assert_eq!(scale.map("b"), Some("green"));
assert_eq!(scale.map("c"), Some("red")); assert_eq!(scale.map("d"), Some("green")); }
#[test]
fn ordinal_unknown_returns_none() {
let scale = ScaleOrdinal::new(
vec!["a".into(), "b".into()],
vec!["red".into(), "green".into()],
);
assert_eq!(scale.map("unknown"), None);
}
#[test]
fn ordinal_empty_range() {
let scale = ScaleOrdinal::new(
vec!["a".into(), "b".into()],
vec![],
);
assert_eq!(scale.map("a"), None);
}
#[test]
fn ordinal_map_index() {
let scale = ScaleOrdinal::new(
vec!["a".into(), "b".into()],
vec!["red".into(), "green".into(), "blue".into()],
);
assert_eq!(scale.map_index(0), Some("red"));
assert_eq!(scale.map_index(1), Some("green"));
assert_eq!(scale.map_index(2), Some("blue"));
assert_eq!(scale.map_index(3), Some("red")); }
}