pub_just/
attribute.rs

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}