1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4use core::{fmt, str::FromStr};
5use std::error::Error;
6
7macro_rules! 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, PnpmTextError> {
19 let trimmed = input.trim();
20 if trimmed.is_empty() {
21 Err(PnpmTextError::Empty)
22 } else {
23 Ok(Self(trimmed.to_string()))
24 }
25 }
26
27 #[must_use]
29 pub fn as_str(&self) -> &str {
30 &self.0
31 }
32 }
33 };
34}
35
36#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
38pub enum PnpmCommand {
39 Install,
40 Add,
41 Run,
42 Test,
43 Exec,
44 Publish,
45}
46
47impl FromStr for PnpmCommand {
48 type Err = PnpmTextError;
49
50 fn from_str(input: &str) -> Result<Self, Self::Err> {
51 match normalized(input)?.as_str() {
52 "install" | "i" => Ok(Self::Install),
53 "add" => Ok(Self::Add),
54 "run" => Ok(Self::Run),
55 "test" => Ok(Self::Test),
56 "exec" => Ok(Self::Exec),
57 "publish" => Ok(Self::Publish),
58 _ => Err(PnpmTextError::Unknown),
59 }
60 }
61}
62
63text_newtype!(PnpmWorkspace);
64text_newtype!(PnpmFilter);
65
66#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
68pub enum PnpmLockfile {
69 Workspace,
70}
71
72impl PnpmLockfile {
73 #[must_use]
75 pub const fn as_str(self) -> &'static str {
76 "pnpm-lock.yaml"
77 }
78}
79
80#[derive(Clone, Copy, Debug, Eq, PartialEq)]
82pub enum PnpmTextError {
83 Empty,
84 Unknown,
85}
86
87impl fmt::Display for PnpmTextError {
88 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
89 match self {
90 Self::Empty => formatter.write_str("pnpm metadata text cannot be empty"),
91 Self::Unknown => formatter.write_str("unknown pnpm command"),
92 }
93 }
94}
95
96impl Error for PnpmTextError {}
97
98fn normalized(input: &str) -> Result<String, PnpmTextError> {
99 let trimmed = input.trim();
100 if trimmed.is_empty() {
101 Err(PnpmTextError::Empty)
102 } else {
103 Ok(trimmed.to_ascii_lowercase())
104 }
105}
106
107#[cfg(test)]
108mod tests {
109 use super::{PnpmCommand, PnpmFilter, PnpmLockfile};
110
111 #[test]
112 fn models_pnpm_metadata() -> Result<(), Box<dyn std::error::Error>> {
113 assert_eq!("install".parse::<PnpmCommand>()?, PnpmCommand::Install);
114 assert_eq!(
115 PnpmFilter::new("./packages/app")?.as_str(),
116 "./packages/app"
117 );
118 assert_eq!(PnpmLockfile::Workspace.as_str(), "pnpm-lock.yaml");
119 Ok(())
120 }
121}