alpm_types/
pkg.rs

1use std::{fmt::Display, str::FromStr};
2
3use serde::Serialize;
4use strum::{Display, EnumString};
5
6use crate::{Error, Name};
7
8/// The type of a package
9///
10/// ## Examples
11/// ```
12/// use std::str::FromStr;
13///
14/// use alpm_types::PackageType;
15///
16/// // create PackageType from str
17/// assert_eq!(PackageType::from_str("pkg"), Ok(PackageType::Package));
18///
19/// // format as String
20/// assert_eq!("debug", format!("{}", PackageType::Debug));
21/// assert_eq!("pkg", format!("{}", PackageType::Package));
22/// assert_eq!("src", format!("{}", PackageType::Source));
23/// assert_eq!("split", format!("{}", PackageType::Split));
24/// ```
25#[derive(Copy, Clone, Debug, Display, EnumString, Eq, PartialEq, Serialize)]
26#[non_exhaustive]
27pub enum PackageType {
28    /// a debug package
29    #[strum(to_string = "debug")]
30    Debug,
31    /// a single (non-split) package
32    #[strum(to_string = "pkg")]
33    Package,
34    /// a source-only package
35    #[strum(to_string = "src")]
36    Source,
37    /// one split package out of a set of several
38    #[strum(to_string = "split")]
39    Split,
40}
41
42/// Description of a package
43///
44/// This is a type alias for [`String`].
45///
46/// ## Examples
47/// ```
48/// use alpm_types::{Error, PackageDescription};
49///
50/// // Create a PackageDescription
51/// let desc: PackageDescription = "A simple package".to_string();
52/// ```
53pub type PackageDescription = String;
54
55/// Name of the base package information that one or more packages are built from.
56///
57/// This is a type alias for [`Name`].
58///
59/// ## Examples
60/// ```
61/// use std::str::FromStr;
62///
63/// use alpm_types::{Error, Name};
64///
65/// # fn main() -> Result<(), alpm_types::Error> {
66/// // create PackageBaseName from &str
67/// let pkgbase = Name::from_str("test-123@.foo_+")?;
68///
69/// // format as String
70/// let pkgbase = Name::from_str("foo")?;
71/// assert_eq!("foo", pkgbase.to_string());
72/// # Ok(())
73/// # }
74/// ```
75pub type PackageBaseName = Name;
76
77/// Extra data associated with a package
78///
79/// This type wraps a key-value pair of data as String, which is separated by an equal sign (`=`).
80#[derive(Debug, Clone, PartialEq, Serialize)]
81pub struct ExtraData {
82    key: String,
83    value: String,
84}
85
86impl ExtraData {
87    /// Create a new extra_data
88    pub fn new(key: String, value: String) -> Self {
89        Self { key, value }
90    }
91
92    /// Return the key of the extra_data
93    pub fn key(&self) -> &str {
94        &self.key
95    }
96
97    /// Return the value of the extra_data
98    pub fn value(&self) -> &str {
99        &self.value
100    }
101}
102
103impl FromStr for ExtraData {
104    type Err = Error;
105
106    /// Parses an `extra_data` from string.
107    ///
108    /// The string is expected to be in the format `key=value`.
109    ///
110    /// ## Errors
111    ///
112    /// This function returns an error if the string is missing the key or value component.
113    ///
114    /// ## Examples
115    ///
116    /// ```
117    /// use std::str::FromStr;
118    ///
119    /// use alpm_types::{Error, ExtraData, PackageType};
120    ///
121    /// # fn main() -> Result<(), alpm_types::Error> {
122    /// // create ExtraData from str
123    /// let extra_data: ExtraData = ExtraData::from_str("pkgtype=debug")?;
124    /// assert_eq!(extra_data.key(), "pkgtype");
125    /// assert_eq!(extra_data.value(), "debug");
126    /// # Ok(())
127    /// # }
128    /// ```
129    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}