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