Skip to main content

use_drupal/
lib.rs

1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4use core::{fmt, str::FromStr};
5use std::error::Error;
6
7macro_rules! drupal_text_newtype {
8    ($name:ident) => {
9        #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
10        pub struct $name(String);
11
12        impl $name {
13            pub fn new(input: &str) -> Result<Self, DrupalError> {
14                let trimmed = input.trim();
15                if trimmed.is_empty() {
16                    Err(DrupalError::Empty)
17                } else {
18                    Ok(Self(trimmed.to_string()))
19                }
20            }
21
22            pub fn as_str(&self) -> &str {
23                &self.0
24            }
25        }
26
27        impl fmt::Display for $name {
28            fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
29                formatter.write_str(self.as_str())
30            }
31        }
32
33        impl FromStr for $name {
34            type Err = DrupalError;
35
36            fn from_str(input: &str) -> Result<Self, Self::Err> {
37                Self::new(input)
38            }
39        }
40    };
41}
42
43drupal_text_newtype!(DrupalModuleName);
44drupal_text_newtype!(DrupalThemeName);
45drupal_text_newtype!(DrupalRouteName);
46drupal_text_newtype!(DrupalEntityTypeId);
47drupal_text_newtype!(DrupalConfigObjectName);
48drupal_text_newtype!(DrupalPermission);
49
50/// Drupal extension kind metadata.
51#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
52pub enum DrupalExtensionKind {
53    Module,
54    Theme,
55    Profile,
56}
57
58impl DrupalExtensionKind {
59    pub const fn as_str(self) -> &'static str {
60        match self {
61            Self::Module => "module",
62            Self::Theme => "theme",
63            Self::Profile => "profile",
64        }
65    }
66}
67
68/// Drupal metadata reference.
69#[derive(Clone, Debug, Eq, PartialEq)]
70pub struct DrupalMetadataReference {
71    name: String,
72    kind: DrupalExtensionKind,
73}
74
75impl DrupalMetadataReference {
76    pub fn new(name: &str, kind: DrupalExtensionKind) -> Result<Self, DrupalError> {
77        let trimmed = name.trim();
78        if trimmed.is_empty() {
79            Err(DrupalError::Empty)
80        } else {
81            Ok(Self {
82                name: trimmed.to_string(),
83                kind,
84            })
85        }
86    }
87
88    pub fn name(&self) -> &str {
89        &self.name
90    }
91
92    pub const fn kind(&self) -> DrupalExtensionKind {
93        self.kind
94    }
95}
96
97/// Error returned when Drupal metadata is invalid.
98#[derive(Clone, Copy, Debug, Eq, PartialEq)]
99pub enum DrupalError {
100    Empty,
101}
102
103impl fmt::Display for DrupalError {
104    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
105        formatter.write_str("Drupal metadata cannot be empty")
106    }
107}
108
109impl Error for DrupalError {}
110
111#[cfg(test)]
112mod tests {
113    use super::{
114        DrupalError, DrupalExtensionKind, DrupalMetadataReference, DrupalModuleName,
115        DrupalPermission, DrupalRouteName,
116    };
117
118    #[test]
119    fn builds_drupal_metadata() -> Result<(), DrupalError> {
120        let module = DrupalModuleName::new("book_tools")?;
121        let route = DrupalRouteName::new("book_tools.index")?;
122        let permission = DrupalPermission::new("administer book tools")?;
123        let reference = DrupalMetadataReference::new("book_tools", DrupalExtensionKind::Module)?;
124
125        assert_eq!(module.as_str(), "book_tools");
126        assert_eq!(route.as_str(), "book_tools.index");
127        assert_eq!(permission.as_str(), "administer book tools");
128        assert_eq!(reference.kind().as_str(), "module");
129        Ok(())
130    }
131}