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}