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}