alpm_srcinfo/schema.rs
1//! Schemas for SRCINFO data.
2
3use std::{
4 fmt::{Display, Formatter},
5 fs::File,
6 path::{Path, PathBuf},
7 str::FromStr,
8};
9
10use alpm_common::FileFormatSchema;
11use alpm_types::{SchemaVersion, semver_version::Version};
12use winnow::Parser;
13
14use crate::{Error, source_info::parser::SourceInfoContent};
15
16/// An enum tracking all available [SRCINFO] schemas.
17///
18/// The schema of a SRCINFO refers to the minimum required sections and keywords, as well as the
19/// complete set of available keywords in a specific version.
20///
21/// [SRCINFO]: https://alpm.archlinux.page/specifications/SRCINFO.5.html
22#[derive(Clone, Debug, Eq, PartialEq)]
23pub enum SourceInfoSchema {
24 /// Schema for the [SRCINFO] file format.
25 ///
26 /// [SRCINFO]: https://alpm.archlinux.page/specifications/SRCINFO.5.html
27 V1(SchemaVersion),
28}
29
30impl FileFormatSchema for SourceInfoSchema {
31 type Err = Error;
32
33 /// Returns a reference to the inner [`SchemaVersion`].
34 fn inner(&self) -> &SchemaVersion {
35 match self {
36 SourceInfoSchema::V1(v) => v,
37 }
38 }
39
40 /// Derives a [`SourceInfoSchema`] from a SRCINFO file.
41 ///
42 /// Opens the `file` and defers to [`SourceInfoSchema::derive_from_reader`].
43 ///
44 /// # Errors
45 ///
46 /// Returns an error if
47 /// - opening `file` for reading fails
48 /// - or deriving a [`SourceInfoSchema`] from the contents of `file` fails.
49 fn derive_from_file(file: impl AsRef<Path>) -> Result<Self, Error>
50 where
51 Self: Sized,
52 {
53 let file = file.as_ref();
54 Self::derive_from_reader(File::open(file).map_err(|source| {
55 Error::IoPath(
56 PathBuf::from(file),
57 "deriving schema version from SRCINFO file",
58 source,
59 )
60 })?)
61 }
62
63 /// Derives a [`SourceInfoSchema`] from SRCINFO data in a `reader`.
64 ///
65 /// Reads the `reader` to string and defers to [`SourceInfoSchema::derive_from_str`].
66 ///
67 /// # Errors
68 ///
69 /// Returns an error if
70 /// - reading a [`String`] from `reader` fails
71 /// - or deriving a [`SourceInfoSchema`] from the contents of `reader` fails.
72 fn derive_from_reader(reader: impl std::io::Read) -> Result<Self, Error>
73 where
74 Self: Sized,
75 {
76 let mut buf = String::new();
77 let mut reader = reader;
78 reader
79 .read_to_string(&mut buf)
80 .map_err(|source| Error::Io("deriving schema version from SRCINFO data", source))?;
81 Self::derive_from_str(&buf)
82 }
83
84 /// Derives a [`SourceInfoSchema`] from a string slice containing SRCINFO data.
85 ///
86 /// Since the SRCINFO format is only covered by a single version and it not carrying any
87 /// version information, this function checks whether `s` contains at least the sections
88 /// `pkgbase` and `pkgname` and the keywords `pkgver` and `pkgrel`.
89 ///
90 /// # Examples
91 ///
92 /// ```
93 /// use alpm_common::FileFormatSchema;
94 /// use alpm_srcinfo::SourceInfoSchema;
95 /// use alpm_types::{SchemaVersion, semver_version::Version};
96 ///
97 /// # fn main() -> Result<(), alpm_srcinfo::Error> {
98 /// let srcinfo_data = r#"
99 /// pkgbase = example
100 /// pkgdesc = An example
101 /// pkgver = 0.1.0
102 /// pkgrel = 1
103 ///
104 /// pkgname = example
105 /// "#;
106 /// # Ok(())
107 /// # }
108 /// ```
109 ///
110 /// # Errors
111 ///
112 /// Returns an error if `s` cannot be parsed.
113 fn derive_from_str(s: &str) -> Result<SourceInfoSchema, Error> {
114 let _parsed = SourceInfoContent::parser
115 .parse(s)
116 .map_err(|err| Error::ParseError(format!("{err}")))?;
117
118 Ok(SourceInfoSchema::V1(SchemaVersion::new(Version::new(
119 1, 0, 0,
120 ))))
121 }
122}
123
124impl Default for SourceInfoSchema {
125 /// Returns the default [`SourceInfoSchema`] variant ([`SourceInfoSchema::V1`]).
126 fn default() -> Self {
127 Self::V1(SchemaVersion::new(Version::new(1, 0, 0)))
128 }
129}
130
131impl FromStr for SourceInfoSchema {
132 type Err = Error;
133
134 /// Creates a [`SourceInfoSchema`] from string slice `s`.
135 ///
136 /// Relies on [`SchemaVersion::from_str`] to create a corresponding [`SourceInfoSchema`] from
137 /// `s`.
138 ///
139 /// # Errors
140 ///
141 /// Returns an error if
142 /// - no [`SchemaVersion`] can be created from `s`,
143 /// - or the conversion from [`SchemaVersion`] to [`SourceInfoSchema`] fails.
144 fn from_str(s: &str) -> Result<SourceInfoSchema, Self::Err> {
145 match SchemaVersion::from_str(s) {
146 Ok(version) => Self::try_from(version),
147 Err(_) => Err(Error::UnsupportedSchemaVersion(s.to_string())),
148 }
149 }
150}
151
152impl TryFrom<SchemaVersion> for SourceInfoSchema {
153 type Error = Error;
154
155 /// Converts a [`SchemaVersion`] to a [`SourceInfoSchema`].
156 ///
157 /// # Errors
158 ///
159 /// Returns an error if the [`SchemaVersion`]'s inner [`Version`] does not provide a major
160 /// version that corresponds to a [`SourceInfoSchema`] variant.
161 fn try_from(value: SchemaVersion) -> Result<Self, Self::Error> {
162 match value.inner().major {
163 1 => Ok(SourceInfoSchema::V1(value)),
164 _ => Err(Error::UnsupportedSchemaVersion(value.to_string())),
165 }
166 }
167}
168
169impl Display for SourceInfoSchema {
170 fn fmt(&self, fmt: &mut Formatter) -> std::fmt::Result {
171 write!(
172 fmt,
173 "{}",
174 match self {
175 SourceInfoSchema::V1(version) => version.inner().major,
176 }
177 )
178 }
179}