use std::collections::HashSet;
use serde::{Deserialize, Serialize};
use super::param_error::ParamError;
pub fn option_validate(
primary_items: &Vec<&str>,
secondary_items: &Vec<(&str, &str)>,
) -> Result<(), ParamError> {
let mut unique_items: HashSet<&str> = HashSet::new();
for item in primary_items {
if !unique_items.insert(item) {
let message = format!("Duplicate primary item {}", &item);
return Err(ParamError::new(message));
}
}
unique_items.clear();
for (item, _) in secondary_items {
if !unique_items.insert(item) {
let message = format!("Duplicate secondary item {}", &item);
return Err(ParamError::new(message));
}
if primary_items.contains(&item) {
let message = format!("Item {} both primary and secondary", &item);
return Err(ParamError::new(message));
}
}
Ok(())
}
pub fn new_primary_validate(items: &Vec<&str>, new: &&str) -> Result<(), ParamError> {
if items.contains(new) {
let message = format!("Item {} overlap with existing primary item", new);
return Err(ParamError::new(message));
}
Ok(())
}
pub fn new_secondary_validate(
items: &Vec<(&str, &str)>,
new: &(&str, &str),
) -> Result<(), ParamError> {
if items
.iter()
.map(|(i, _)| *i)
.collect::<Vec<&str>>()
.contains(&&new.0)
{
let message = format!("Item {} overlap with existing secondary item", &new.0);
return Err(ParamError::new(message));
}
Ok(())
}
#[derive(Debug, Eq, PartialEq, Clone, PartialOrd, Ord, Hash, Default, Serialize, Deserialize)]
pub struct Option {
pub label: String,
pub primary_items: Vec<String>,
pub secondary_items: Vec<(String, String)>,
}
impl Option {
pub fn new(
label: &str,
primary_items: &Vec<&str>,
secondary_items: &Vec<(&str, &str)>,
) -> Result<Self, ParamError> {
option_validate(&primary_items, &secondary_items)?;
Ok(Self {
label: label.to_string(),
primary_items: primary_items.into_iter().map(|s| s.to_string()).collect(),
secondary_items: secondary_items
.iter()
.map(|s| (s.0.to_string(), s.1.to_string()))
.collect(),
})
}
pub fn new_validated(
label: &str,
primary_items: &Vec<&str>,
secondary_items: &Vec<(&str, &str)>,
) -> Self {
Self {
label: label.to_string(),
primary_items: primary_items.into_iter().map(|s| s.to_string()).collect(),
secondary_items: secondary_items
.iter()
.map(|s| (s.0.to_string(), s.1.to_string()))
.collect(),
}
}
pub fn add_secondary_item(&mut self, item: (&str, &str)) -> Result<(), ParamError> {
new_secondary_validate(
&self
.secondary_items
.iter()
.map(|s| (s.0.as_str(), s.1.as_str()))
.collect(),
&item,
)?;
self.secondary_items
.push((item.0.to_string(), item.1.to_string()));
Ok(())
}
pub fn add_primary_item(&mut self, item: &str) -> Result<(), ParamError> {
new_primary_validate(
&self.primary_items.iter().map(|s| s.as_str()).collect(),
&item,
)?;
self.primary_items.push(item.to_string());
Ok(())
}
pub fn add_secondary_valid(&mut self, item: (&str, &str)) {
self.secondary_items
.push((item.0.to_string(), item.1.to_string()));
}
pub fn add_primary_valid(&mut self, item: &str) {
self.primary_items.push(item.to_string());
}
}
#[cfg(test)]
mod tests {
use super::*;
fn s(lit: &str) -> String {
lit.to_string()
}
#[test]
fn test_new_option() {
let result = Option::new("o1", &vec!["p1", "p2"], &vec![("s1", "")]);
assert!(result.is_ok());
let mut o1 = result.unwrap();
assert_eq!(o1.label, "o1");
assert_eq!(o1.primary_items, ["p1", "p2"]);
assert_eq!(o1.secondary_items, [(s("s1"), s(""))]);
o1.add_primary_item("p3").unwrap();
assert_eq!(o1.primary_items, ["p1", "p2", "p3"]);
o1.add_secondary_item(("s2", "c1")).unwrap();
assert_eq!(o1.secondary_items, [(s("s1"), s("")), (s("s2"), s("c1"))]);
let result = serde_json::to_string(&o1);
assert!(result.is_ok());
let json = result.unwrap();
assert_eq!(
json,
r#"{"label":"o1","primary_items":["p1","p2","p3"],"secondary_items":[["s1",""],["s2","c1"]]}"#
);
}
#[test]
fn test_new_option_from_json() {
let valid_json = r#"{"label":"o1","primary_items":["p1","p2","p3"],"secondary_items":[["s1",""],["s2","c1"]]}"#;
let result = serde_json::from_str::<Option>(valid_json);
assert!(result.is_ok());
let option = result.unwrap();
assert_eq!(option.primary_items, ["p1", "p2", "p3"]);
let invalid_json = r#"{"label":"o1","unknown_items":["p1","p2","p3"],"secondary_items":[["s1",""],["s2","c1"]]}"#;
let result = serde_json::from_str::<Option>(invalid_json);
assert!(result.is_err());
}
#[test]
fn test_new_option_overlap() {
let result = Option::new("o1", &vec!["p1", "p2"], &vec![("p1", "")]);
assert!(result.is_err());
let error = result.unwrap_err();
assert_eq!(
error,
ParamError::new("Item p1 both primary and secondary".to_string())
);
}
#[test]
fn test_new_option_duplicate_primary() {
let result = Option::new("o1", &vec!["p1", "p1"], &vec![("s1", "")]);
assert!(result.is_err());
let error = result.unwrap_err();
assert_eq!(
error,
ParamError::new("Duplicate primary item p1".to_string())
);
}
#[test]
fn test_new_option_duplicate_secondary() {
let result = Option::new("o1", &vec!["p1", "p2"], &vec![("s1", "c1"), ("s1", "c2")]);
assert!(result.is_err());
let error = result.unwrap_err();
assert_eq!(
error,
ParamError::new("Duplicate secondary item s1".to_string())
);
}
#[test]
fn test_add_primary_overlap() {
let result = Option::new("o1", &vec!["p1", "p2"], &vec![("s1", "")]);
assert!(result.is_ok());
let mut option = result.unwrap();
let result = option.add_primary_item("p1");
assert!(result.is_err());
let error = result.unwrap_err();
assert_eq!(
error,
ParamError::new("Item p1 overlap with existing primary item".to_string())
);
}
#[test]
fn test_add_secondary_overlap() {
let mut option = Option::new("o1", &vec!["p1", "p2"], &vec![("s1", "")]).unwrap();
let result = option.add_secondary_item(("s1", ""));
assert!(result.is_err());
let error = result.unwrap_err();
assert_eq!(
error,
ParamError::new("Item s1 overlap with existing secondary item".to_string())
);
}
}