1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4use core::{fmt, str::FromStr};
5use std::error::Error;
6
7macro_rules! wp_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, WordPressError> {
14 let trimmed = input.trim();
15 if trimmed.is_empty() {
16 Err(WordPressError::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 = WordPressError;
35
36 fn from_str(input: &str) -> Result<Self, Self::Err> {
37 Self::new(input)
38 }
39 }
40 };
41}
42
43wp_text_newtype!(WordPressText);
44wp_text_newtype!(WordPressPluginHeaderName);
45wp_text_newtype!(WordPressThemeFieldName);
46wp_text_newtype!(WordPressPostTypeSlug);
47wp_text_newtype!(WordPressTaxonomySlug);
48wp_text_newtype!(WordPressCapability);
49wp_text_newtype!(WordPressRestNamespace);
50wp_text_newtype!(WordPressRestRoute);
51
52#[derive(Clone, Debug, Eq, PartialEq)]
54pub struct WordPressPluginHeader {
55 name: WordPressPluginHeaderName,
56 value: WordPressText,
57}
58
59impl WordPressPluginHeader {
60 pub fn new(name: WordPressPluginHeaderName, value: WordPressText) -> Self {
61 Self { name, value }
62 }
63
64 pub const fn name(&self) -> &WordPressPluginHeaderName {
65 &self.name
66 }
67
68 pub const fn value(&self) -> &WordPressText {
69 &self.value
70 }
71}
72
73#[derive(Clone, Debug, Eq, PartialEq)]
75pub struct WordPressThemeField {
76 name: WordPressThemeFieldName,
77 value: WordPressText,
78}
79
80impl WordPressThemeField {
81 pub fn new(name: WordPressThemeFieldName, value: WordPressText) -> Self {
82 Self { name, value }
83 }
84
85 pub const fn name(&self) -> &WordPressThemeFieldName {
86 &self.name
87 }
88
89 pub const fn value(&self) -> &WordPressText {
90 &self.value
91 }
92}
93
94#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
96pub enum WordPressMetadataKind {
97 PluginHeader,
98 ThemeField,
99 PostType,
100 Taxonomy,
101 Capability,
102 RestRoute,
103}
104
105impl WordPressMetadataKind {
106 pub const fn as_str(self) -> &'static str {
107 match self {
108 Self::PluginHeader => "plugin-header",
109 Self::ThemeField => "theme-field",
110 Self::PostType => "post-type",
111 Self::Taxonomy => "taxonomy",
112 Self::Capability => "capability",
113 Self::RestRoute => "rest-route",
114 }
115 }
116}
117
118#[derive(Clone, Copy, Debug, Eq, PartialEq)]
120pub enum WordPressError {
121 Empty,
122}
123
124impl fmt::Display for WordPressError {
125 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
126 formatter.write_str("WordPress metadata cannot be empty")
127 }
128}
129
130impl Error for WordPressError {}
131
132#[cfg(test)]
133mod tests {
134 use super::{
135 WordPressCapability, WordPressError, WordPressMetadataKind, WordPressPluginHeader,
136 WordPressPluginHeaderName, WordPressPostTypeSlug, WordPressText,
137 };
138
139 #[test]
140 fn builds_wordpress_metadata() -> Result<(), WordPressError> {
141 let header = WordPressPluginHeader::new(
142 WordPressPluginHeaderName::new("Plugin Name")?,
143 WordPressText::new("Example Tools")?,
144 );
145 let post_type = WordPressPostTypeSlug::new("book")?;
146 let capability = WordPressCapability::new("edit_posts")?;
147
148 assert_eq!(header.name().as_str(), "Plugin Name");
149 assert_eq!(post_type.as_str(), "book");
150 assert_eq!(capability.as_str(), "edit_posts");
151 assert_eq!(WordPressMetadataKind::RestRoute.as_str(), "rest-route");
152 Ok(())
153 }
154}