alpm_srcinfo/source_info/
mod.rs

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