alpm_srcinfo/source_info/mod.rs
1//! Data representations and integrations for reading of SRCINFO data.
2mod helper;
3pub mod parser;
4pub mod v1;
5
6use std::{
7 fs::File,
8 path::{Path, PathBuf},
9 str::FromStr,
10};
11
12use alpm_common::{FileFormatSchema, MetadataFile};
13use serde::{Deserialize, Serialize};
14
15use crate::{Error, SourceInfoSchema, SourceInfoV1};
16
17/// The representation of SRCINFO data.
18///
19/// Tracks all available versions of the file format.
20///
21/// [SRCINFO]: https://alpm.archlinux.page/specifications/SRCINFO.5.html
22#[derive(Clone, Debug, Deserialize, Serialize)]
23pub enum SourceInfo {
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 /// 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| {
88 Error::IoPath(PathBuf::from(file), "opening the file for reading", source)
89 })?,
90 schema,
91 )
92 }
93
94 /// Creates a [`SourceInfo`] from a `reader`, optionally validated using a
95 /// [`SourceInfoSchema`].
96 ///
97 /// Reads the `reader` to string and defers to [`SourceInfo::from_str_with_schema`].
98 ///
99 /// # Note
100 ///
101 /// To automatically derive the [`SourceInfoSchema`], use [`SourceInfo::from_reader`].
102 ///
103 /// # Examples
104 ///
105 /// ```
106 /// use std::{fs::File, io::Write};
107 ///
108 /// use alpm_common::MetadataFile;
109 /// use alpm_srcinfo::{SourceInfo, SourceInfoSchema};
110 /// use alpm_types::{SchemaVersion, semver_version::Version};
111 ///
112 /// # fn main() -> testresult::TestResult {
113 /// let srcinfo_file = tempfile::NamedTempFile::new()?;
114 /// // Prepare a reader with SRCINFO data
115 /// let (reader, srcinfo_data) = {
116 /// let srcinfo_data = r#"
117 /// pkgbase = example
118 /// pkgdesc = An example
119 /// pkgver = 0.1.0
120 /// pkgrel = 1
121 ///
122 /// pkgname = example
123 /// "#;
124 /// let mut output = File::create(&srcinfo_file)?;
125 /// write!(output, "{}", srcinfo_data)?;
126 /// (File::open(&srcinfo_file.path())?, srcinfo_data)
127 /// };
128 ///
129 /// let srcinfo = SourceInfo::from_reader_with_schema(
130 /// reader,
131 /// Some(SourceInfoSchema::V1(SchemaVersion::new(Version::new(
132 /// 1, 0, 0,
133 /// )))),
134 /// )?;
135 /// # Ok(())
136 /// # }
137 /// ```
138 ///
139 /// # Errors
140 ///
141 /// Returns an error if
142 /// - the `reader` cannot be read to string,
143 /// - no variant of [`SourceInfo`] can be constructed from the contents of the `reader`,
144 /// - or `schema` is [`Some`] and the [`SourceInfoSchema`] does not match the contents of the
145 /// `reader`.
146 fn from_reader_with_schema(
147 mut reader: impl std::io::Read,
148 schema: Option<SourceInfoSchema>,
149 ) -> Result<Self, Error> {
150 let mut buf = String::new();
151 reader
152 .read_to_string(&mut buf)
153 .map_err(|source| Error::Io("reading SRCINFO data", source))?;
154 Self::from_str_with_schema(&buf, schema)
155 }
156
157 /// Creates a [`SourceInfo`] from string slice, optionally validated using a
158 /// [`SourceInfoSchema`].
159 ///
160 /// If `schema` is [`None`] attempts to detect the [`SourceInfoSchema`] from `s`.
161 /// Attempts to create a [`SourceInfo`] variant that corresponds to the [`SourceInfoSchema`].
162 ///
163 /// # Note
164 ///
165 /// To automatically derive the [`SourceInfoSchema`], use [`SourceInfo::from_str`].
166 ///
167 /// # Examples
168 ///
169 /// ```
170 /// use std::{fs::File, io::Write};
171 ///
172 /// use alpm_common::MetadataFile;
173 /// use alpm_srcinfo::{SourceInfo, SourceInfoSchema};
174 /// use alpm_types::{SchemaVersion, semver_version::Version};
175 ///
176 /// # fn main() -> testresult::TestResult {
177 /// let srcinfo_data = r#"
178 /// pkgbase = example
179 /// pkgdesc = An example
180 /// pkgver = 0.1.0
181 /// pkgrel = 1
182 ///
183 /// pkgname = example
184 /// "#;
185 ///
186 /// let srcinfo = SourceInfo::from_str_with_schema(
187 /// srcinfo_data,
188 /// Some(SourceInfoSchema::V1(SchemaVersion::new(Version::new(
189 /// 1, 0, 0,
190 /// )))),
191 /// )?;
192 /// # Ok(())
193 /// # }
194 /// ```
195 ///
196 /// # Errors
197 ///
198 /// Returns an error if
199 /// - `schema` is [`Some`] and the specified variant of [`SourceInfo`] cannot be constructed
200 /// from `s`,
201 /// - `schema` is [`None`] and
202 /// - a [`SourceInfoSchema`] cannot be derived from `s`,
203 /// - or the detected variant of [`SourceInfo`] cannot be constructed from `s`.
204 fn from_str_with_schema(s: &str, schema: Option<SourceInfoSchema>) -> Result<Self, Error> {
205 let schema = match schema {
206 Some(schema) => schema,
207 None => SourceInfoSchema::derive_from_str(s)?,
208 };
209
210 match schema {
211 SourceInfoSchema::V1(_) => {
212 Ok(SourceInfo::V1(SourceInfoV1::from_string(s)?.source_info()?))
213 }
214 }
215 }
216}
217
218impl FromStr for SourceInfo {
219 type Err = Error;
220
221 /// Creates a [`SourceInfo`] from string slice `s`.
222 ///
223 /// Calls [`SourceInfo::from_str_with_schema`] with `schema` set to [`None`].
224 ///
225 /// # Errors
226 ///
227 /// Returns an error if
228 /// - a [`SourceInfoSchema`] cannot be derived from `s`,
229 /// - or the detected variant of [`SourceInfo`] cannot be constructed from `s`.
230 fn from_str(s: &str) -> Result<Self, Self::Err> {
231 Self::from_str_with_schema(s, None)
232 }
233}