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