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}