1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4use core::{fmt, str::FromStr};
5use std::error::Error;
6
7macro_rules! laravel_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, LaravelError> {
14 let trimmed = input.trim();
15 if trimmed.is_empty() {
16 Err(LaravelError::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 = LaravelError;
35
36 fn from_str(input: &str) -> Result<Self, Self::Err> {
37 Self::new(input)
38 }
39 }
40 };
41}
42
43laravel_text_newtype!(LaravelRouteName);
44laravel_text_newtype!(LaravelMiddlewareName);
45laravel_text_newtype!(ArtisanCommandName);
46laravel_text_newtype!(LaravelMigrationName);
47laravel_text_newtype!(ServiceProviderName);
48laravel_text_newtype!(LaravelConfigKey);
49
50#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
52pub enum LaravelMetadataKind {
53 Route,
54 Middleware,
55 ArtisanCommand,
56 Migration,
57 ServiceProvider,
58 ConfigKey,
59}
60
61impl LaravelMetadataKind {
62 pub const fn as_str(self) -> &'static str {
63 match self {
64 Self::Route => "route",
65 Self::Middleware => "middleware",
66 Self::ArtisanCommand => "artisan-command",
67 Self::Migration => "migration",
68 Self::ServiceProvider => "service-provider",
69 Self::ConfigKey => "config-key",
70 }
71 }
72}
73
74#[derive(Clone, Debug, Eq, PartialEq)]
76pub struct LaravelMetadata {
77 name: String,
78 kind: LaravelMetadataKind,
79}
80
81impl LaravelMetadata {
82 pub fn new(name: &str, kind: LaravelMetadataKind) -> Result<Self, LaravelError> {
83 let trimmed = name.trim();
84 if trimmed.is_empty() {
85 Err(LaravelError::Empty)
86 } else {
87 Ok(Self {
88 name: trimmed.to_string(),
89 kind,
90 })
91 }
92 }
93
94 pub fn name(&self) -> &str {
95 &self.name
96 }
97
98 pub const fn kind(&self) -> LaravelMetadataKind {
99 self.kind
100 }
101}
102
103#[derive(Clone, Copy, Debug, Eq, PartialEq)]
105pub enum LaravelError {
106 Empty,
107}
108
109impl fmt::Display for LaravelError {
110 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
111 formatter.write_str("Laravel metadata cannot be empty")
112 }
113}
114
115impl Error for LaravelError {}
116
117#[cfg(test)]
118mod tests {
119 use super::{
120 ArtisanCommandName, LaravelConfigKey, LaravelError, LaravelMetadata, LaravelMetadataKind,
121 LaravelRouteName,
122 };
123
124 #[test]
125 fn builds_laravel_metadata() -> Result<(), LaravelError> {
126 let route = LaravelRouteName::new("books.index")?;
127 let command = ArtisanCommandName::new("books:sync")?;
128 let config = LaravelConfigKey::new("cache.default")?;
129 let metadata = LaravelMetadata::new(route.as_str(), LaravelMetadataKind::Route)?;
130
131 assert_eq!(route.as_str(), "books.index");
132 assert_eq!(command.as_str(), "books:sync");
133 assert_eq!(config.as_str(), "cache.default");
134 assert_eq!(metadata.kind().as_str(), "route");
135 Ok(())
136 }
137}