Skip to main content

use_psr/
lib.rs

1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4use core::fmt;
5use std::error::Error;
6
7/// PHP-FIG PSR number metadata.
8#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
9pub struct PsrNumber(u16);
10
11impl PsrNumber {
12    pub const fn new(value: u16) -> Result<Self, PsrError> {
13        if value == 0 {
14            Err(PsrError::InvalidNumber)
15        } else {
16            Ok(Self(value))
17        }
18    }
19
20    pub const fn get(self) -> u16 {
21        self.0
22    }
23}
24
25impl fmt::Display for PsrNumber {
26    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
27        write!(formatter, "PSR-{}", self.0)
28    }
29}
30
31/// PSR title metadata.
32#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
33pub struct PsrTitle(String);
34
35impl PsrTitle {
36    pub fn new(input: &str) -> Result<Self, PsrError> {
37        let trimmed = input.trim();
38        if trimmed.is_empty() {
39            Err(PsrError::Empty)
40        } else {
41            Ok(Self(trimmed.to_string()))
42        }
43    }
44
45    pub fn as_str(&self) -> &str {
46        &self.0
47    }
48}
49
50/// PSR status metadata.
51#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
52pub enum PsrStatus {
53    Draft,
54    Accepted,
55    Deprecated,
56    Abandoned,
57    Unknown,
58}
59
60impl PsrStatus {
61    pub const fn as_str(self) -> &'static str {
62        match self {
63            Self::Draft => "draft",
64            Self::Accepted => "accepted",
65            Self::Deprecated => "deprecated",
66            Self::Abandoned => "abandoned",
67            Self::Unknown => "unknown",
68        }
69    }
70}
71
72/// Broad PSR category metadata.
73#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
74pub enum PsrCategory {
75    Autoloading,
76    CodingStyle,
77    Http,
78    Logging,
79    Cache,
80    Container,
81    Events,
82    Security,
83    Other,
84}
85
86impl PsrCategory {
87    pub const fn as_str(self) -> &'static str {
88        match self {
89            Self::Autoloading => "autoloading",
90            Self::CodingStyle => "coding-style",
91            Self::Http => "http",
92            Self::Logging => "logging",
93            Self::Cache => "cache",
94            Self::Container => "container",
95            Self::Events => "events",
96            Self::Security => "security",
97            Self::Other => "other",
98        }
99    }
100}
101
102/// PHP-FIG PSR metadata record.
103#[derive(Clone, Debug, Eq, PartialEq)]
104pub struct PsrMetadata {
105    number: PsrNumber,
106    title: PsrTitle,
107    status: PsrStatus,
108    category: PsrCategory,
109}
110
111impl PsrMetadata {
112    pub const fn new(
113        number: PsrNumber,
114        title: PsrTitle,
115        status: PsrStatus,
116        category: PsrCategory,
117    ) -> Self {
118        Self {
119            number,
120            title,
121            status,
122            category,
123        }
124    }
125
126    pub const fn number(&self) -> PsrNumber {
127        self.number
128    }
129
130    pub const fn title(&self) -> &PsrTitle {
131        &self.title
132    }
133
134    pub const fn status(&self) -> PsrStatus {
135        self.status
136    }
137
138    pub const fn category(&self) -> PsrCategory {
139        self.category
140    }
141
142    pub fn identifier(&self) -> String {
143        self.number.to_string()
144    }
145}
146
147/// Error returned when PSR metadata is invalid.
148#[derive(Clone, Copy, Debug, Eq, PartialEq)]
149pub enum PsrError {
150    Empty,
151    InvalidNumber,
152}
153
154impl fmt::Display for PsrError {
155    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
156        match self {
157            Self::Empty => formatter.write_str("PSR metadata cannot be empty"),
158            Self::InvalidNumber => formatter.write_str("PSR number must be non-zero"),
159        }
160    }
161}
162
163impl Error for PsrError {}
164
165#[cfg(test)]
166mod tests {
167    use super::{PsrCategory, PsrError, PsrMetadata, PsrNumber, PsrStatus, PsrTitle};
168
169    #[test]
170    fn builds_psr_metadata() -> Result<(), PsrError> {
171        let metadata = PsrMetadata::new(
172            PsrNumber::new(4)?,
173            PsrTitle::new("Autoloading Standard")?,
174            PsrStatus::Accepted,
175            PsrCategory::Autoloading,
176        );
177
178        assert_eq!(metadata.identifier(), "PSR-4");
179        assert_eq!(metadata.category().as_str(), "autoloading");
180        Ok(())
181    }
182}