1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
//! Defines the [`Tag`] type, which represents a [`crate::post::Post`] tag. use crate::url::*; use serde::de::{Deserialize, Deserializer}; use std::hash::{Hash, Hasher}; /// Represents a [`crate::post::Post`] tag. On parsing a post from YAML, only the /// `name` field is parsed while the `url` field is left empty. The URL field /// must be filled in later based on the `index_base_url` and the tag name. #[derive(Clone, Debug)] pub struct Tag { /// The tag's name. This should be slugified so e.g., `macOS` and `MacOS` /// resolve to the same value, and also so the field can be dropped into a /// [`crate::url::UrlBuf`]. pub name: String, /// The URL for the tag's first index page. Given an `index_base_url`, this /// should look something like `{index_base_url}/{tag_name}/index.html`. pub url: UrlBuf, } impl<'de> Deserialize<'de> for Tag { /// Implements [`serde::de::Deserialize`] for [`Tag`]. This expects a string /// and will deserialize it into a [`Tag`] whose `name` is the sluggified /// input string and whose `url` field is left empty. fn deserialize<D>(deserializer: D) -> std::result::Result<Tag, D::Error> where D: Deserializer<'de>, { Ok(Tag { name: slug::slugify(&String::deserialize(deserializer)?), url: UrlBuf::new(), }) } } impl Hash for Tag { /// Implements [`Hash`] for [`Tag`] by delegating directly to the `name` /// field. fn hash<H: Hasher>(&self, state: &mut H) { self.name.hash(state) } } impl PartialEq for Tag { /// Implements [`PartialEq`] and [`Eq`] for [`Tag`] by delegating directly to /// the `name` field. fn eq(&self, other: &Self) -> bool { self.name == other.name } } impl Eq for Tag {}