1use super::*;
2
3#[derive(
4 EnumDiscriminants, PartialEq, Debug, Clone, Serialize, Ord, PartialOrd, Eq, IntoStaticStr,
5)]
6#[strum(serialize_all = "kebab-case")]
7#[serde(rename_all = "kebab-case")]
8#[strum_discriminants(name(AttributeDiscriminant))]
9#[strum_discriminants(derive(EnumString))]
10#[strum_discriminants(strum(serialize_all = "kebab-case"))]
11pub enum Attribute<'src> {
12 Confirm(Option<StringLiteral<'src>>),
13 Doc(Option<StringLiteral<'src>>),
14 Extension(StringLiteral<'src>),
15 Group(StringLiteral<'src>),
16 Linux,
17 Macos,
18 NoCd,
19 NoExitMessage,
20 NoQuiet,
21 PositionalArguments,
22 Private,
23 Script(Option<Interpreter<'src>>),
24 Unix,
25 Windows,
26}
27
28impl AttributeDiscriminant {
29 fn argument_range(self) -> RangeInclusive<usize> {
30 match self {
31 Self::Confirm | Self::Doc => 0..=1,
32 Self::Group | Self::Extension => 1..=1,
33 Self::Linux
34 | Self::Macos
35 | Self::NoCd
36 | Self::NoExitMessage
37 | Self::NoQuiet
38 | Self::PositionalArguments
39 | Self::Private
40 | Self::Unix
41 | Self::Windows => 0..=0,
42 Self::Script => 0..=usize::MAX,
43 }
44 }
45}
46
47impl<'src> Attribute<'src> {
48 pub fn new(
49 name: Name<'src>,
50 arguments: Vec<StringLiteral<'src>>,
51 ) -> CompileResult<'src, Self> {
52 let discriminant = name
53 .lexeme()
54 .parse::<AttributeDiscriminant>()
55 .ok()
56 .ok_or_else(|| {
57 name.error(CompileErrorKind::UnknownAttribute {
58 attribute: name.lexeme(),
59 })
60 })?;
61
62 let found = arguments.len();
63 let range = discriminant.argument_range();
64 if !range.contains(&found) {
65 return Err(
66 name.error(CompileErrorKind::AttributeArgumentCountMismatch {
67 attribute: name.lexeme(),
68 found,
69 min: *range.start(),
70 max: *range.end(),
71 }),
72 );
73 }
74
75 Ok(match discriminant {
76 AttributeDiscriminant::Confirm => Self::Confirm(arguments.into_iter().next()),
77 AttributeDiscriminant::Doc => Self::Doc(arguments.into_iter().next()),
78 AttributeDiscriminant::Extension => Self::Extension(arguments.into_iter().next().unwrap()),
79 AttributeDiscriminant::Group => Self::Group(arguments.into_iter().next().unwrap()),
80 AttributeDiscriminant::Linux => Self::Linux,
81 AttributeDiscriminant::Macos => Self::Macos,
82 AttributeDiscriminant::NoCd => Self::NoCd,
83 AttributeDiscriminant::NoExitMessage => Self::NoExitMessage,
84 AttributeDiscriminant::NoQuiet => Self::NoQuiet,
85 AttributeDiscriminant::PositionalArguments => Self::PositionalArguments,
86 AttributeDiscriminant::Private => Self::Private,
87 AttributeDiscriminant::Script => Self::Script({
88 let mut arguments = arguments.into_iter();
89 arguments.next().map(|command| Interpreter {
90 command,
91 arguments: arguments.collect(),
92 })
93 }),
94 AttributeDiscriminant::Unix => Self::Unix,
95 AttributeDiscriminant::Windows => Self::Windows,
96 })
97 }
98
99 pub fn name(&self) -> &'static str {
100 self.into()
101 }
102}
103
104impl<'src> Display for Attribute<'src> {
105 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
106 write!(f, "{}", self.name())?;
107
108 match self {
109 Self::Confirm(Some(argument))
110 | Self::Doc(Some(argument))
111 | Self::Extension(argument)
112 | Self::Group(argument) => write!(f, "({argument})")?,
113 Self::Script(Some(shell)) => write!(f, "({shell})")?,
114 Self::Confirm(None)
115 | Self::Doc(None)
116 | Self::Linux
117 | Self::Macos
118 | Self::NoCd
119 | Self::NoExitMessage
120 | Self::NoQuiet
121 | Self::PositionalArguments
122 | Self::Private
123 | Self::Script(None)
124 | Self::Unix
125 | Self::Windows => {}
126 }
127
128 Ok(())
129 }
130}
131
132#[cfg(test)]
133mod tests {
134 use super::*;
135
136 #[test]
137 fn name() {
138 assert_eq!(Attribute::NoExitMessage.name(), "no-exit-message");
139 }
140}