use serde_derive::{Deserialize, Serialize};
use typed_builder::TypedBuilder;
use ulid::Ulid;
use crate::config::GeneralConfig;
#[derive(Debug, Serialize, Deserialize, TypedBuilder, Clone)]
pub struct Category {
#[builder(default, setter(strip_option))]
#[serde(skip_serializing_if = "Option::is_none")]
description: Option<String>,
#[builder(default = Some(CategoryGuid::default()), setter(strip_option))]
#[serde(rename = "id")]
guid: Option<CategoryGuid>,
name: String,
#[builder(default, setter(strip_option))]
#[serde(skip_serializing_if = "Option::is_none")]
subcategories: Option<Vec<Category>>,
}
#[must_use]
pub fn extract_categories(category_string: &str, separator: &str) -> (Category, Option<Category>) {
let parts: Vec<_> = category_string.split(separator).collect();
if parts.len() > 1 {
(
Category::builder().name(parts[0].to_string()).build(),
Some(Category::builder().name(parts[1..].join(separator)).build()),
)
} else {
(
Category::builder().name(category_string.to_owned()).build(),
None,
)
}
}
#[must_use]
pub fn split_category_by_category_separator(
category_string: &str,
separator: Option<&str>,
) -> (String, Option<String>) {
let default_separator = GeneralConfig::default()
.category_separator()
.clone()
.unwrap_or_else(|| "::".to_string());
let separator = separator.unwrap_or(default_separator.as_str());
let parts: Vec<_> = category_string.split(separator).collect();
if parts.len() > 1 {
(parts[0].to_string(), Some(parts[1..].concat()))
} else {
(parts[0].to_string(), None)
}
}
#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
pub struct CategoryGuid(Ulid);
impl Default for CategoryGuid {
fn default() -> Self {
Self(Ulid::new())
}
}
impl Default for Category {
fn default() -> Self {
Self {
guid: Some(CategoryGuid::default()),
name: "Uncategorized".to_string(),
description: Some("Uncategorized category".to_string()),
subcategories: Option::default(),
}
}
}