alpm_srcinfo/source_info/
mod.rs

1//! Data representations and integrations for reading of SRCINFO data.
2pub mod parser;
3pub mod v1;
4
5use std::{
6    fs::File,
7    path::{Path, PathBuf},
8    str::FromStr,
9};
10
11use alpm_common::{FileFormatSchema, MetadataFile};
12use serde::{Deserialize, Serialize};
13
14use crate::{Error, SourceInfoSchema, SourceInfoV1};
15
16/// The representation of SRCINFO data.
17///
18/// Tracks all available versions of the file format.
19///
20/// [SRCINFO]: https://alpm.archlinux.page/specifications/SRCINFO.5.html
21#[derive(Clone, Debug, Deserialize, Serialize)]
22pub enum SourceInfo {
23    /// The [SRCINFO] file format.
24    ///
25    /// [SRCINFO]: https://alpm.archlinux.page/specifications/SRCINFO.5.html
26    V1(SourceInfoV1),
27}
28
29impl MetadataFile<SourceInfoSchema> for SourceInfo {
30    type Err = Error;
31
32    /// Creates a [`SourceInfo`] from `file`, optionally validated using a [`SourceInfoSchema`].
33    ///
34    /// Opens the `file` and defers to [`SourceInfo::from_reader_with_schema`].
35    ///
36    /// # Note
37    ///
38    /// To automatically derive the [`SourceInfoSchema`], use [`SourceInfo::from_file`].
39    ///
40    /// # Examples
41    ///
42    /// ```
43    /// use std::{fs::File, io::Write};
44    ///
45    /// use alpm_common::{FileFormatSchema, MetadataFile};
46    /// use alpm_srcinfo::{SourceInfo, SourceInfoSchema};
47    /// use alpm_types::{SchemaVersion, semver_version::Version};
48    ///
49    /// # fn main() -> testresult::TestResult {
50    /// // Prepare a file with SRCINFO data
51    /// let srcinfo_file = tempfile::NamedTempFile::new()?;
52    /// let (file, srcinfo_data) = {
53    ///     let srcinfo_data = r#"
54    /// pkgbase = example
55    ///     pkgdesc = An example
56    ///     arch = x86_64
57    ///     pkgver = 0.1.0
58    ///     pkgrel = 1
59    ///
60    /// pkgname = example
61    /// "#;
62    ///     let mut output = File::create(&srcinfo_file)?;
63    ///     write!(output, "{}", srcinfo_data)?;
64    ///     (srcinfo_file, srcinfo_data)
65    /// };
66    ///
67    /// let srcinfo = SourceInfo::from_file_with_schema(
68    ///     file.path().to_path_buf(),
69    ///     Some(SourceInfoSchema::V1(SchemaVersion::new(Version::new(
70    ///         1, 0, 0,
71    ///     )))),
72    /// )?;
73    /// # Ok(())
74    /// # }
75    /// ```
76    ///
77    /// # Errors
78    ///
79    /// Returns an error if
80    /// - the `file` cannot be opened for reading,
81    /// - no variant of [`SourceInfo`] can be constructed from the contents of `file`,
82    /// - or `schema` is [`Some`] and the [`SourceInfoSchema`] does not match the contents of
83    ///   `file`.
84    fn from_file_with_schema(
85        file: impl AsRef<Path>,
86        schema: Option<SourceInfoSchema>,
87    ) -> Result<Self, Error> {
88        let file = file.as_ref();
89        Self::from_reader_with_schema(
90            File::open(file).map_err(|source| {
91                Error::IoPath(PathBuf::from(file), "opening the file for reading", source)
92            })?,
93            schema,
94        )
95    }
96
97    /// Creates a [`SourceInfo`] from a `reader`, optionally validated using a
98    /// [`SourceInfoSchema`].
99    ///
100    /// Reads the `reader` to string and defers to [`SourceInfo::from_str_with_schema`].
101    ///
102    /// # Note
103    ///
104    /// To automatically derive the [`SourceInfoSchema`], use [`SourceInfo::from_reader`].
105    ///
106    /// # Examples
107    ///
108    /// ```
109    /// use std::{fs::File, io::Write};
110    ///
111    /// use alpm_common::MetadataFile;
112    /// use alpm_srcinfo::{SourceInfo, SourceInfoSchema};
113    /// use alpm_types::{SchemaVersion, semver_version::Version};
114    ///
115    /// # fn main() -> testresult::TestResult {
116    /// let srcinfo_file = tempfile::NamedTempFile::new()?;
117    /// // Prepare a reader with SRCINFO data
118    /// let (reader, srcinfo_data) = {
119    ///     let srcinfo_data = r#"
120    /// pkgbase = example
121    ///     pkgdesc = An example
122    ///     arch = x86_64
123    ///     pkgver = 0.1.0
124    ///     pkgrel = 1
125    ///
126    /// pkgname = example
127    /// "#;
128    ///     let mut output = File::create(&srcinfo_file)?;
129    ///     write!(output, "{}", srcinfo_data)?;
130    ///     (File::open(&srcinfo_file.path())?, srcinfo_data)
131    /// };
132    ///
133    /// let srcinfo = SourceInfo::from_reader_with_schema(
134    ///     reader,
135    ///     Some(SourceInfoSchema::V1(SchemaVersion::new(Version::new(
136    ///         1, 0, 0,
137    ///     )))),
138    /// )?;
139    /// # Ok(())
140    /// # }
141    /// ```
142    ///
143    /// # Errors
144    ///
145    /// Returns an error if
146    /// - the `reader` cannot be read to string,
147    /// - no variant of [`SourceInfo`] can be constructed from the contents of the `reader`,
148    /// - or `schema` is [`Some`] and the [`SourceInfoSchema`] does not match the contents of the
149    ///   `reader`.
150    fn from_reader_with_schema(
151        mut reader: impl std::io::Read,
152        schema: Option<SourceInfoSchema>,
153    ) -> Result<Self, Error> {
154        let mut buf = String::new();
155        reader
156            .read_to_string(&mut buf)
157            .map_err(|source| Error::Io("reading SRCINFO data", source))?;
158        Self::from_str_with_schema(&buf, schema)
159    }
160
161    /// Creates a [`SourceInfo`] from string slice, optionally validated using a
162    /// [`SourceInfoSchema`].
163    ///
164    /// If `schema` is [`None`] attempts to detect the [`SourceInfoSchema`] from `s`.
165    /// Attempts to create a [`SourceInfo`] variant that corresponds to the [`SourceInfoSchema`].
166    ///
167    /// # Note
168    ///
169    /// To automatically derive the [`SourceInfoSchema`], use [`SourceInfo::from_str`].
170    ///
171    /// # Examples
172    ///
173    /// ```
174    /// use std::{fs::File, io::Write};
175    ///
176    /// use alpm_common::MetadataFile;
177    /// use alpm_srcinfo::{SourceInfo, SourceInfoSchema};
178    /// use alpm_types::{SchemaVersion, semver_version::Version};
179    ///
180    /// # fn main() -> testresult::TestResult {
181    /// let srcinfo_data = r#"
182    /// pkgbase = example
183    ///     pkgdesc = An example
184    ///     arch = x86_64
185    ///     pkgver = 0.1.0
186    ///     pkgrel = 1
187    ///
188    /// pkgname = example
189    /// "#;
190    ///
191    /// let srcinfo = SourceInfo::from_str_with_schema(
192    ///     srcinfo_data,
193    ///     Some(SourceInfoSchema::V1(SchemaVersion::new(Version::new(
194    ///         1, 0, 0,
195    ///     )))),
196    /// )?;
197    /// # Ok(())
198    /// # }
199    /// ```
200    ///
201    /// # Errors
202    ///
203    /// Returns an error if
204    /// - `schema` is [`Some`] and the specified variant of [`SourceInfo`] cannot be constructed
205    ///   from `s`,
206    /// - `schema` is [`None`] and
207    ///   - a [`SourceInfoSchema`] cannot be derived from `s`,
208    ///   - or the detected variant of [`SourceInfo`] cannot be constructed from `s`.
209    fn from_str_with_schema(s: &str, schema: Option<SourceInfoSchema>) -> Result<Self, Error> {
210        let schema = match schema {
211            Some(schema) => schema,
212            None => SourceInfoSchema::derive_from_str(s)?,
213        };
214
215        match schema {
216            SourceInfoSchema::V1(_) => Ok(SourceInfo::V1(SourceInfoV1::from_string(s)?)),
217        }
218    }
219}
220
221impl FromStr for SourceInfo {
222    type Err = Error;
223
224    /// Creates a [`SourceInfo`] from string slice `s`.
225    ///
226    /// Calls [`SourceInfo::from_str_with_schema`] with `schema` set to [`None`].
227    ///
228    /// # Errors
229    ///
230    /// Returns an error if
231    /// - a [`SourceInfoSchema`] cannot be derived from `s`,
232    /// - or the detected variant of [`SourceInfo`] cannot be constructed from `s`.
233    fn from_str(s: &str) -> Result<Self, Self::Err> {
234        Self::from_str_with_schema(s, None)
235    }
236}