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