1use std::{fmt::Display, str::FromStr};
2
3use serde::Serialize;
4use strum::{Display, EnumString};
5
6use crate::{Error, Name};
7
8#[derive(Copy, Clone, Debug, Display, EnumString, Eq, PartialEq, Serialize)]
26#[non_exhaustive]
27pub enum PackageType {
28 #[strum(to_string = "debug")]
30 Debug,
31 #[strum(to_string = "pkg")]
33 Package,
34 #[strum(to_string = "src")]
36 Source,
37 #[strum(to_string = "split")]
39 Split,
40}
41
42pub type PackageDescription = String;
54
55pub type PackageBaseName = Name;
76
77#[derive(Debug, Clone, PartialEq, Serialize)]
81pub struct ExtraData {
82 key: String,
83 value: String,
84}
85
86impl ExtraData {
87 pub fn new(key: String, value: String) -> Self {
89 Self { key, value }
90 }
91
92 pub fn key(&self) -> &str {
94 &self.key
95 }
96
97 pub fn value(&self) -> &str {
99 &self.value
100 }
101}
102
103impl FromStr for ExtraData {
104 type Err = Error;
105
106 fn from_str(s: &str) -> Result<Self, Self::Err> {
130 const DELIMITER: char = '=';
131 let mut parts = s.splitn(2, DELIMITER);
132 let key = parts
133 .next()
134 .map(|v| v.trim())
135 .filter(|v| !v.is_empty())
136 .ok_or(Error::MissingComponent { component: "key" })?;
137 let value = parts
138 .next()
139 .map(|v| v.trim())
140 .filter(|v| !v.is_empty())
141 .ok_or(Error::MissingComponent { component: "value" })?;
142 Ok(Self::new(key.to_string(), value.to_string()))
143 }
144}
145
146impl Display for ExtraData {
147 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
148 write!(f, "{}={}", self.key, self.value)
149 }
150}
151
152#[cfg(test)]
153mod tests {
154 use std::str::FromStr;
155
156 use rstest::rstest;
157
158 use super::*;
159
160 #[rstest]
161 #[case("debug", Ok(PackageType::Debug))]
162 #[case("pkg", Ok(PackageType::Package))]
163 #[case("src", Ok(PackageType::Source))]
164 #[case("split", Ok(PackageType::Split))]
165 #[case("foo", Err(strum::ParseError::VariantNotFound))]
166 fn pkgtype_from_string(
167 #[case] from_str: &str,
168 #[case] result: Result<PackageType, strum::ParseError>,
169 ) {
170 assert_eq!(PackageType::from_str(from_str), result);
171 }
172
173 #[rstest]
174 #[case(PackageType::Debug, "debug")]
175 #[case(PackageType::Package, "pkg")]
176 #[case(PackageType::Source, "src")]
177 #[case(PackageType::Split, "split")]
178 fn pkgtype_format_string(#[case] pkgtype: PackageType, #[case] pkgtype_str: &str) {
179 assert_eq!(pkgtype_str, format!("{}", pkgtype));
180 }
181
182 #[rstest]
183 #[case("key=value", "key", "value")]
184 #[case("pkgtype=debug", "pkgtype", "debug")]
185 #[case("test-123@.foo_+=1000", "test-123@.foo_+", "1000")]
186 fn extra_data_from_str(
187 #[case] data: &str,
188 #[case] key: &str,
189 #[case] value: &str,
190 ) -> testresult::TestResult<()> {
191 let extra_data: ExtraData = ExtraData::from_str(data)?;
192 assert_eq!(extra_data.key(), key);
193 assert_eq!(extra_data.value(), value);
194 assert_eq!(extra_data.to_string(), data);
195 Ok(())
196 }
197
198 #[rstest]
199 #[case("key", Err(Error::MissingComponent { component: "value" }))]
200 #[case("key=", Err(Error::MissingComponent { component: "value" }))]
201 #[case("=value", Err(Error::MissingComponent { component: "key" }))]
202 fn extra_data_from_str_error(
203 #[case] extra_data: &str,
204 #[case] result: Result<ExtraData, Error>,
205 ) {
206 assert_eq!(ExtraData::from_str(extra_data), result);
207 }
208}