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::{fs::File, path::Path, str::FromStr};
6
7use alpm_common::MetadataFile;
8use alpm_types::{SchemaVersion, semver_version::Version};
9use fluent_i18n::t;
10use serde::{Deserialize, Serialize};
11
12use crate::{Error, SourceInfoSchema, SourceInfoV1};
13
14/// The representation of SRCINFO data.
15///
16/// Tracks all available versions of the file format.
17///
18/// [SRCINFO]: https://alpm.archlinux.page/specifications/SRCINFO.5.html
19#[derive(Clone, Debug, Deserialize, Serialize)]
20pub enum SourceInfo {
21    /// The [SRCINFO] file format.
22    ///
23    /// [SRCINFO]: https://alpm.archlinux.page/specifications/SRCINFO.5.html
24    V1(SourceInfoV1),
25}
26
27impl MetadataFile<SourceInfoSchema> for SourceInfo {
28    type Err = Error;
29
30    /// Creates a [`SourceInfo`] from `file`, optionally validated using a [`SourceInfoSchema`].
31    ///
32    /// Opens the `file` and defers to [`SourceInfo::from_reader_with_schema`].
33    ///
34    /// # Note
35    ///
36    /// To automatically derive the [`SourceInfoSchema`], use [`SourceInfo::from_file`].
37    ///
38    /// # Examples
39    ///
40    /// ```
41    /// use std::{fs::File, io::Write};
42    ///
43    /// use alpm_common::{FileFormatSchema, MetadataFile};
44    /// use alpm_srcinfo::{SourceInfo, SourceInfoSchema};
45    /// use alpm_types::{SchemaVersion, semver_version::Version};
46    ///
47    /// # fn main() -> testresult::TestResult {
48    /// // Prepare a file with SRCINFO data
49    /// let srcinfo_file = tempfile::NamedTempFile::new()?;
50    /// let (file, srcinfo_data) = {
51    ///     let srcinfo_data = r#"
52    /// pkgbase = example
53    ///     pkgdesc = An example
54    ///     arch = x86_64
55    ///     pkgver = 0.1.0
56    ///     pkgrel = 1
57    ///
58    /// pkgname = example
59    /// "#;
60    ///     let mut output = File::create(&srcinfo_file)?;
61    ///     write!(output, "{}", srcinfo_data)?;
62    ///     (srcinfo_file, srcinfo_data)
63    /// };
64    ///
65    /// let srcinfo = SourceInfo::from_file_with_schema(
66    ///     file.path().to_path_buf(),
67    ///     Some(SourceInfoSchema::V1(SchemaVersion::new(Version::new(
68    ///         1, 0, 0,
69    ///     )))),
70    /// )?;
71    /// # Ok(())
72    /// # }
73    /// ```
74    ///
75    /// # Errors
76    ///
77    /// Returns an error if
78    /// - the `file` cannot be opened for reading,
79    /// - no variant of [`SourceInfo`] can be constructed from the contents of `file`,
80    /// - or `schema` is [`Some`] and the [`SourceInfoSchema`] does not match the contents of
81    ///   `file`.
82    fn from_file_with_schema(
83        file: impl AsRef<Path>,
84        schema: Option<SourceInfoSchema>,
85    ) -> Result<Self, Error> {
86        let file = file.as_ref();
87        Self::from_reader_with_schema(
88            File::open(file).map_err(|source| Error::IoPath {
89                path: file.to_path_buf(),
90                context: t!("error-io-path-opening-file"),
91                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 {
158                context: t!("error-io-read-srcinfo-data"),
159                source,
160            })?;
161        Self::from_str_with_schema(&buf, schema)
162    }
163
164    /// Creates a [`SourceInfo`] from string slice, optionally validated using a
165    /// [`SourceInfoSchema`].
166    ///
167    /// If `schema` is [`None`] attempts to detect the [`SourceInfoSchema`] from `s`.
168    /// Attempts to create a [`SourceInfo`] variant that corresponds to the [`SourceInfoSchema`].
169    ///
170    /// # Note
171    ///
172    /// To automatically derive the [`SourceInfoSchema`], use [`SourceInfo::from_str`].
173    ///
174    /// # Examples
175    ///
176    /// ```
177    /// use std::{fs::File, io::Write};
178    ///
179    /// use alpm_common::MetadataFile;
180    /// use alpm_srcinfo::{SourceInfo, SourceInfoSchema};
181    /// use alpm_types::{SchemaVersion, semver_version::Version};
182    ///
183    /// # fn main() -> testresult::TestResult {
184    /// let srcinfo_data = r#"
185    /// pkgbase = example
186    ///     pkgdesc = An example
187    ///     arch = x86_64
188    ///     pkgver = 0.1.0
189    ///     pkgrel = 1
190    ///
191    /// pkgname = example
192    /// "#;
193    ///
194    /// let srcinfo = SourceInfo::from_str_with_schema(
195    ///     srcinfo_data,
196    ///     Some(SourceInfoSchema::V1(SchemaVersion::new(Version::new(
197    ///         1, 0, 0,
198    ///     )))),
199    /// )?;
200    /// # Ok(())
201    /// # }
202    /// ```
203    ///
204    /// # Errors
205    ///
206    /// Returns an error if
207    /// - `schema` is [`Some`] and the specified variant of [`SourceInfo`] cannot be constructed
208    ///   from `s`,
209    /// - `schema` is [`None`] and
210    ///   - a [`SourceInfoSchema`] cannot be derived from `s`,
211    ///   - or the detected variant of [`SourceInfo`] cannot be constructed from `s`.
212    fn from_str_with_schema(s: &str, schema: Option<SourceInfoSchema>) -> Result<Self, Error> {
213        // NOTE: This does not use `SourceInfoSchema::derive_from_str`,
214        // to not run the parser twice.
215        // In the future, this should run `SourceInfoContent` parser directly
216        // and delegate to `from_raw` instead of `from_string`.
217
218        let schema = match schema {
219            Some(schema) => schema,
220            None => SourceInfoSchema::V1(SchemaVersion::new(Version::new(1, 0, 0))),
221        };
222
223        match schema {
224            SourceInfoSchema::V1(_) => Ok(SourceInfo::V1(SourceInfoV1::from_string(s)?)),
225        }
226    }
227}
228
229impl FromStr for SourceInfo {
230    type Err = Error;
231
232    /// Creates a [`SourceInfo`] from string slice `s`.
233    ///
234    /// Calls [`SourceInfo::from_str_with_schema`] with `schema` set to [`None`].
235    ///
236    /// # Errors
237    ///
238    /// Returns an error if
239    /// - a [`SourceInfoSchema`] cannot be derived from `s`,
240    /// - or the detected variant of [`SourceInfo`] cannot be constructed from `s`.
241    fn from_str(s: &str) -> Result<Self, Self::Err> {
242        Self::from_str_with_schema(s, None)
243    }
244}