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