Skip to main content

open_timeline_core/
name.rs

1// SPDX-License-Identifier: MIT
2
3//!
4//! The OpenTimeline name type
5//!
6
7use serde::{Deserialize, Deserializer, Serialize};
8use thiserror::Error;
9
10// TODO: should these <Type>Error enums have (de)serialising errors too?
11/// Errors that can arise in relation to a [`Name`]
12#[derive(Error, Debug, Clone)]
13pub enum NameError {
14    #[error("Name cannot be empty")]
15    Empty,
16}
17
18// TODO: consider impl Deref to str so can be used where &str is expected
19/// The OpenTimeline [`Name`] type.  The value can be any string apart from one
20/// which when trimmed of trailing and leading whitespace is empty.
21#[derive(derive_more::Display, Serialize, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
22#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
23#[cfg_attr(feature = "sqlx", sqlx(transparent))]
24pub struct Name(String);
25
26impl Name {
27    /// Create and initialise a new name if it will be valid
28    pub fn from<S: ToString>(name: S) -> Result<Self, NameError> {
29        let name = name.to_string();
30        if name.trim().is_empty() {
31            Err(NameError::Empty)
32        } else {
33            Ok(Name(name.trim().to_string()))
34        }
35    }
36
37    /// Get the underlying `&str`
38    pub fn as_str(&self) -> &str {
39        &self.0
40    }
41}
42
43impl<'de> Deserialize<'de> for Name {
44    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
45    where
46        D: Deserializer<'de>,
47    {
48        let string = String::deserialize(deserializer)?;
49        Name::from(string).map_err(serde::de::Error::custom)
50    }
51}
52
53#[cfg(test)]
54mod test {
55    use super::*;
56
57    #[test]
58    fn from() {
59        assert!(Name::from("").is_err());
60        assert!(Name::from("  ").is_err());
61        let ok_1 = Name::from("Pass").unwrap();
62        let ok_2 = Name::from(" Pass ").unwrap();
63        assert_eq!(ok_1, ok_2)
64    }
65}