#[cfg_attr(doc, aquamarine::aquamarine)]
use serde::{Deserialize, Serialize};
use crate::draft::blog_post::front_matter::tags;
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub(crate) struct Taxonomies {
#[allow(dead_code)]
tags: Vec<String>,
}
#[cfg(test)]
impl Taxonomies {
pub(crate) fn new(tags: Vec<String>) -> Self {
Taxonomies { tags }
}
}
impl Taxonomies {
pub(crate) fn tags(&self) -> Vec<String> {
self.tags.clone()
}
pub(crate) fn hashtags(&self) -> Vec<String> {
tags::hashtags(self.tags.clone())
}
}
#[cfg(test)]
mod tests {
use super::*;
macro_rules! test_taxonomies_hashtags {
($test_name:ident, $input:expr, $expected:expr) => {
#[test]
fn $test_name() {
let input: Vec<String> = $input.iter().map(|s| s.to_string()).collect();
let taxonomies = Taxonomies::new(input);
let result = taxonomies.hashtags();
let expected: Vec<String> = $expected.iter().map(|s| s.to_string()).collect();
assert_eq!(
result, expected,
"taxonomies.hashtags() with {:?} should return {:?}, got {:?}",
$input, expected, result
);
}
};
}
macro_rules! test_taxonomies_tags {
($test_name:ident, $input:expr) => {
#[test]
fn $test_name() {
let input: Vec<String> = $input.iter().map(|s| s.to_string()).collect();
let taxonomies = Taxonomies::new(input.clone());
let result = taxonomies.tags();
assert_eq!(
result, input,
"taxonomies.tags() should return the same tags that were input"
);
}
};
}
#[test]
fn test_deserialize_taxonomies_basic() {
let toml_str = r#"tags = ["rust", "programming"]"#;
let taxonomies: Taxonomies = toml::from_str(toml_str).unwrap();
assert_eq!(taxonomies.tags, vec!["rust", "programming"]);
}
#[test]
fn test_deserialize_taxonomies_empty() {
let toml_str = r#"tags = []"#;
let taxonomies: Taxonomies = toml::from_str(toml_str).unwrap();
assert_eq!(taxonomies.tags, Vec::<String>::new());
}
#[test]
fn test_deserialize_taxonomies_with_spaces() {
let toml_str = r#"tags = ["rust lang", "web development"]"#;
let taxonomies: Taxonomies = toml::from_str(toml_str).unwrap();
assert_eq!(taxonomies.tags, vec!["rust lang", "web development"]);
}
#[test]
fn test_deserialize_taxonomies_with_unicode() {
let toml_str = r#"tags = ["café", "москва", "日本"]"#;
let taxonomies: Taxonomies = toml::from_str(toml_str).unwrap();
assert_eq!(taxonomies.tags, vec!["café", "москва", "日本"]);
}
#[test]
fn test_deserialize_taxonomies_with_special_chars() {
let toml_str = r#"tags = ["c++", "node.js", "design-patterns"]"#;
let taxonomies: Taxonomies = toml::from_str(toml_str).unwrap();
assert_eq!(taxonomies.tags, vec!["c++", "node.js", "design-patterns"]);
}
#[test]
fn test_new_constructor() {
let tags = vec!["rust".to_string(), "programming".to_string()];
let taxonomies = Taxonomies::new(tags.clone());
assert_eq!(taxonomies.tags, tags);
}
#[test]
fn test_default_constructor() {
let taxonomies = Taxonomies::default();
assert_eq!(taxonomies.tags, Vec::<String>::new());
}
test_taxonomies_tags!(test_tags_basic, &["rust", "programming"]);
#[test]
fn test_tags_empty() {
let taxonomies = Taxonomies::new(vec![]);
let result = taxonomies.tags();
assert_eq!(result, Vec::<String>::new());
}
test_taxonomies_tags!(test_tags_with_spaces, &["rust lang", "web development"]);
test_taxonomies_tags!(test_tags_with_unicode, &["café", "москва", "日本"]);
test_taxonomies_tags!(
test_tags_with_special_chars,
&["c++", "node.js", "design-patterns"]
);
test_taxonomies_hashtags!(
test_hashtags_basic,
&["rust", "programming"],
&["#Rust", "#Programming"]
);
#[test]
fn test_hashtags_empty() {
let taxonomies = Taxonomies::new(vec![]);
let result = taxonomies.hashtags();
assert_eq!(result, Vec::<String>::new());
}
test_taxonomies_hashtags!(
test_hashtags_with_spaces,
&["rust lang", "web development"],
&["#RustLang", "#WebDevelopment"]
);
test_taxonomies_hashtags!(
test_hashtags_unicode,
&["café", "москва", "日本"],
&["#Café", "#Москва", "#日本"]
);
test_taxonomies_hashtags!(
test_hashtags_special_chars,
&["c++", "node.js", "design-patterns"],
&["#C++", "#Node.js", "#Design-patterns"]
);
test_taxonomies_hashtags!(
test_hashtags_existing_hashtags,
&["#rust", "#programming"],
&["#Rust", "#Programming"]
);
test_taxonomies_hashtags!(
test_hashtags_mixed,
&["#rust", "programming", "web development"],
&["#Rust", "#Programming", "#WebDevelopment"]
);
test_taxonomies_hashtags!(
test_hashtags_whitespace,
&[" rust ", " programming "],
&["#Rust", "#Programming"]
);
test_taxonomies_hashtags!(
test_hashtags_empty_strings,
&["", "rust", ""],
&["#", "#Rust", "#"]
);
#[test]
fn test_large_tag_vector() {
let tags: Vec<String> = (0..1000).map(|i| format!("tag{i}")).collect();
let taxonomies = Taxonomies::new(tags);
let result_tags = taxonomies.tags();
assert_eq!(result_tags.len(), 1000);
assert_eq!(result_tags[0], "tag0");
assert_eq!(result_tags[999], "tag999");
let result_hashtags = taxonomies.hashtags();
assert_eq!(result_hashtags.len(), 1000);
assert_eq!(result_hashtags[0], "#Tag0");
assert_eq!(result_hashtags[999], "#Tag999");
}
#[test]
fn test_repeated_tag_access() {
let taxonomies = Taxonomies::new(vec!["rust".to_string(), "programming".to_string()]);
let tags1 = taxonomies.tags();
let tags2 = taxonomies.tags();
let tags3 = taxonomies.tags();
assert_eq!(tags1, tags2);
assert_eq!(tags2, tags3);
assert_eq!(tags1, vec!["rust", "programming"]);
let hashtags1 = taxonomies.hashtags();
let hashtags2 = taxonomies.hashtags();
let hashtags3 = taxonomies.hashtags();
assert_eq!(hashtags1, hashtags2);
assert_eq!(hashtags2, hashtags3);
assert_eq!(hashtags1, vec!["#Rust", "#Programming"]);
}
#[test]
fn test_ownership_semantics() {
let original_tags = vec!["rust".to_string(), "programming".to_string()];
let taxonomies = Taxonomies::new(original_tags.clone());
let returned_tags = taxonomies.tags();
assert_eq!(returned_tags, original_tags);
let mut modified_tags = taxonomies.tags();
modified_tags.push("modified".to_string());
assert_eq!(taxonomies.tags(), original_tags);
assert_ne!(taxonomies.tags(), modified_tags);
}
#[test]
fn test_front_matter_integration() {
let toml_str = r#"
title = "Test Post"
description = "A test post"
[taxonomies]
tags = ["rust", "programming", "web development"]
"#;
#[derive(Deserialize)]
struct TestFrontMatter {
taxonomies: Option<Taxonomies>,
}
let front_matter: TestFrontMatter = toml::from_str(toml_str).unwrap();
assert!(front_matter.taxonomies.is_some());
let taxonomies = front_matter.taxonomies.unwrap();
assert_eq!(
taxonomies.tags(),
vec!["rust", "programming", "web development"]
);
assert_eq!(
taxonomies.hashtags(),
vec!["#Rust", "#Programming", "#WebDevelopment"]
);
}
#[test]
fn test_serialize_taxonomies() {
let taxonomies = Taxonomies::new(vec!["rust".to_string(), "programming".to_string()]);
let serialized = toml::to_string(&taxonomies).unwrap();
assert_eq!(serialized, "tags = [\"rust\", \"programming\"]\n");
}
#[test]
fn test_serialize_empty_taxonomies() {
let taxonomies = Taxonomies::default();
let serialized = toml::to_string(&taxonomies).unwrap();
assert_eq!(serialized, "tags = []\n");
}
#[test]
fn test_taxonomies_roundtrip() {
let original = Taxonomies::new(vec!["rust".to_string(), "programming".to_string()]);
let serialized = toml::to_string(&original).unwrap();
let deserialized: Taxonomies = toml::from_str(&serialized).unwrap();
assert_eq!(original.tags(), deserialized.tags());
assert_eq!(original.hashtags(), deserialized.hashtags());
}
}