nix_compat/derived_path/
output_spec.rs1use std::collections::BTreeSet;
2use std::fmt;
3use std::str::FromStr;
4
5use crate::store_path::{ValidateNameError, validate_name};
6
7#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
12#[cfg_attr(
13 feature = "serde",
14 derive(serde_with::DeserializeFromStr, serde_with::SerializeDisplay)
15)]
16pub struct OutputName(pub(crate) String);
17
18impl OutputName {
19 pub fn is_default(&self) -> bool {
21 self.0 == "out"
22 }
23}
24
25impl fmt::Display for OutputName {
26 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27 f.write_str(&self.0)
28 }
29}
30
31impl AsRef<str> for OutputName {
32 fn as_ref(&self) -> &str {
33 &self.0
34 }
35}
36
37impl Default for OutputName {
38 fn default() -> Self {
39 OutputName("out".into())
40 }
41}
42
43impl FromStr for OutputName {
44 type Err = ValidateNameError;
45
46 fn from_str(s: &str) -> Result<Self, Self::Err> {
47 let name = validate_name(&s)?.to_string();
48 Ok(OutputName(name))
49 }
50}
51
52#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
61#[cfg_attr(
62 feature = "serde",
63 derive(serde_with::DeserializeFromStr, serde_with::SerializeDisplay)
64)]
65pub enum OutputSpec {
66 All,
67 Named(BTreeSet<OutputName>),
68}
69
70impl OutputSpec {
71 pub fn single(output_name: OutputName) -> Self {
72 let mut set = BTreeSet::new();
73 set.insert(output_name);
74 Self::Named(set)
75 }
76}
77
78impl fmt::Display for OutputSpec {
79 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80 match self {
81 OutputSpec::All => f.write_str("*")?,
82 OutputSpec::Named(outputs) => {
83 let mut it = outputs.iter();
84 if let Some(output) = it.next() {
85 write!(f, "{output}")?;
86 for output in it {
87 write!(f, ",{output}")?;
88 }
89 }
90 }
91 }
92 Ok(())
93 }
94}
95
96impl FromStr for OutputSpec {
97 type Err = ValidateNameError;
98
99 fn from_str(s: &str) -> Result<Self, Self::Err> {
100 if s == "*" {
101 Ok(OutputSpec::All)
102 } else {
103 let mut outputs = BTreeSet::new();
104 for name in s.split(",") {
105 let output = name.parse()?;
106 outputs.insert(output);
107 }
108 Ok(OutputSpec::Named(outputs))
109 }
110 }
111}
112
113impl From<OutputName> for OutputSpec {
114 fn from(output_name: OutputName) -> Self {
115 Self::single(output_name)
116 }
117}
118
119#[cfg(test)]
120mod unittests {
121 use rstest::rstest;
122
123 use crate::derived_path::OutputSpec;
124
125 #[macro_export]
126 macro_rules! set {
127 () => { BTreeSet::new() };
128 ($($x:expr),+ $(,)?) => {{
129 let mut ret = std::collections::BTreeSet::new();
130 $(
131 ret.insert($x.parse().unwrap());
132 )+
133 ret
134 }};
135 }
136
137 #[rstest]
138 #[case("*", OutputSpec::All)]
139 #[case("out", OutputSpec::Named(set!("out")))]
140 #[case("bin,dev,out", OutputSpec::Named(set!("bin", "dev", "out")))]
141 fn parse(#[case] value: &str, #[case] expected: OutputSpec) {
142 let actual = value.parse::<OutputSpec>().unwrap();
143 assert_eq!(actual, expected);
144 }
145
146 #[rstest]
147 #[should_panic(expected = "Invalid name")]
148 #[case("bin{n")]
149 #[should_panic(expected = "Invalid name")]
150 #[case("out,bin{n")]
151 #[should_panic(expected = "Invalid name")]
152 #[case(" bin{n")]
153 #[should_panic(expected = "Invalid length")]
154 #[case("out,")]
155 #[should_panic(expected = "Invalid length")]
156 #[case("")]
157 #[should_panic(expected = "Invalid length")]
158 #[case(",out")]
159 #[should_panic(expected = "Invalid length")]
160 #[case::too_long(
161 "test-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
162 )]
163 fn parse_failure(#[case] value: &str) {
164 let actual = value.parse::<OutputSpec>().unwrap_err();
165 panic!("{actual}");
166 }
167
168 #[rstest]
169 #[case(OutputSpec::All, "*")]
170 #[case(OutputSpec::Named(set!("out")), "out")]
171 #[case(OutputSpec::Named(set!("bin", "dev", "out")), "bin,dev,out")]
172 fn display(#[case] value: OutputSpec, #[case] expected: &str) {
173 assert_eq!(value.to_string(), expected);
174 }
175}