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    ///     pkgver = 0.1.0
57    ///     pkgrel = 1
58    ///
59    /// pkgname = example
60    /// "#;
61    ///     let mut output = File::create(&srcinfo_file)?;
62    ///     write!(output, "{}", srcinfo_data)?;
63    ///     (srcinfo_file, srcinfo_data)
64    /// };
65    ///
66    /// let srcinfo = SourceInfo::from_file_with_schema(
67    ///     file.path().to_path_buf(),
68    ///     Some(SourceInfoSchema::V1(SchemaVersion::new(Version::new(
69    ///         1, 0, 0,
70    ///     )))),
71    /// )?;
72    /// # Ok(())
73    /// # }
74    /// ```
75    ///
76    /// # Errors
77    ///
78    /// Returns an error if
79    /// - the `file` cannot be opened for reading,
80    /// - no variant of [`SourceInfo`] can be constructed from the contents of `file`,
81    /// - or `schema` is [`Some`] and the [`SourceInfoSchema`] does not match the contents of
82    ///   `file`.
83    fn from_file_with_schema(
84        file: impl AsRef<Path>,
85        schema: Option<SourceInfoSchema>,
86    ) -> Result<Self, Error> {
87        let file = file.as_ref();
88        Self::from_reader_with_schema(
89            File::open(file).map_err(|source| {
90                Error::IoPath(PathBuf::from(file), "opening the file for reading", 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    ///     pkgver = 0.1.0
122    ///     pkgrel = 1
123    ///
124    /// pkgname = example
125    /// "#;
126    ///     let mut output = File::create(&srcinfo_file)?;
127    ///     write!(output, "{}", srcinfo_data)?;
128    ///     (File::open(&srcinfo_file.path())?, srcinfo_data)
129    /// };
130    ///
131    /// let srcinfo = SourceInfo::from_reader_with_schema(
132    ///     reader,
133    ///     Some(SourceInfoSchema::V1(SchemaVersion::new(Version::new(
134    ///         1, 0, 0,
135    ///     )))),
136    /// )?;
137    /// # Ok(())
138    /// # }
139    /// ```
140    ///
141    /// # Errors
142    ///
143    /// Returns an error if
144    /// - the `reader` cannot be read to string,
145    /// - no variant of [`SourceInfo`] can be constructed from the contents of the `reader`,
146    /// - or `schema` is [`Some`] and the [`SourceInfoSchema`] does not match the contents of the
147    ///   `reader`.
148    fn from_reader_with_schema(
149        mut reader: impl std::io::Read,
150        schema: Option<SourceInfoSchema>,
151    ) -> Result<Self, Error> {
152        let mut buf = String::new();
153        reader
154            .read_to_string(&mut buf)
155            .map_err(|source| Error::Io("reading SRCINFO data", source))?;
156        Self::from_str_with_schema(&buf, schema)
157    }
158
159    /// Creates a [`SourceInfo`] from string slice, optionally validated using a
160    /// [`SourceInfoSchema`].
161    ///
162    /// If `schema` is [`None`] attempts to detect the [`SourceInfoSchema`] from `s`.
163    /// Attempts to create a [`SourceInfo`] variant that corresponds to the [`SourceInfoSchema`].
164    ///
165    /// # Note
166    ///
167    /// To automatically derive the [`SourceInfoSchema`], use [`SourceInfo::from_str`].
168    ///
169    /// # Examples
170    ///
171    /// ```
172    /// use std::{fs::File, io::Write};
173    ///
174    /// use alpm_common::MetadataFile;
175    /// use alpm_srcinfo::{SourceInfo, SourceInfoSchema};
176    /// use alpm_types::{SchemaVersion, semver_version::Version};
177    ///
178    /// # fn main() -> testresult::TestResult {
179    /// let srcinfo_data = r#"
180    /// pkgbase = example
181    ///     pkgdesc = An example
182    ///     pkgver = 0.1.0
183    ///     pkgrel = 1
184    ///
185    /// pkgname = example
186    /// "#;
187    ///
188    /// let srcinfo = SourceInfo::from_str_with_schema(
189    ///     srcinfo_data,
190    ///     Some(SourceInfoSchema::V1(SchemaVersion::new(Version::new(
191    ///         1, 0, 0,
192    ///     )))),
193    /// )?;
194    /// # Ok(())
195    /// # }
196    /// ```
197    ///
198    /// # Errors
199    ///
200    /// Returns an error if
201    /// - `schema` is [`Some`] and the specified variant of [`SourceInfo`] cannot be constructed
202    ///   from `s`,
203    /// - `schema` is [`None`] and
204    ///   - a [`SourceInfoSchema`] cannot be derived from `s`,
205    ///   - or the detected variant of [`SourceInfo`] cannot be constructed from `s`.
206    fn from_str_with_schema(s: &str, schema: Option<SourceInfoSchema>) -> Result<Self, Error> {
207        let schema = match schema {
208            Some(schema) => schema,
209            None => SourceInfoSchema::derive_from_str(s)?,
210        };
211
212        match schema {
213            SourceInfoSchema::V1(_) => {
214                Ok(SourceInfo::V1(SourceInfoV1::from_string(s)?.source_info()?))
215            }
216        }
217    }
218}
219
220impl FromStr for SourceInfo {
221    type Err = Error;
222
223    /// Creates a [`SourceInfo`] from string slice `s`.
224    ///
225    /// Calls [`SourceInfo::from_str_with_schema`] with `schema` set to [`None`].
226    ///
227    /// # Errors
228    ///
229    /// Returns an error if
230    /// - a [`SourceInfoSchema`] cannot be derived from `s`,
231    /// - or the detected variant of [`SourceInfo`] cannot be constructed from `s`.
232    fn from_str(s: &str) -> Result<Self, Self::Err> {
233        Self::from_str_with_schema(s, None)
234    }
235}