alpm_types/relation/
composite.rs1use std::{
4 fmt::{Display, Formatter},
5 str::FromStr,
6};
7
8use alpm_parsers::traits::AlpmParser;
9use serde::{Deserialize, Serialize};
10use winnow::{
11 ModalResult,
12 Parser,
13 combinator::alt,
14 error::{StrContext, StrContextValue},
15};
16
17use crate::{Error, PackageRelation, SonameV1, SonameV2};
18
19#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
29#[serde(untagged)]
30pub enum RelationOrSoname {
31 Relation(PackageRelation),
33 SonameV1(SonameV1),
37 SonameV2(SonameV2),
41}
42
43impl PartialEq<PackageRelation> for RelationOrSoname {
44 fn eq(&self, other: &PackageRelation) -> bool {
45 self.to_string() == other.to_string()
46 }
47}
48
49impl PartialEq<SonameV1> for RelationOrSoname {
50 fn eq(&self, other: &SonameV1) -> bool {
51 self.to_string() == other.to_string()
52 }
53}
54
55impl PartialEq<SonameV2> for RelationOrSoname {
56 fn eq(&self, other: &SonameV2) -> bool {
57 self.to_string() == other.to_string()
58 }
59}
60
61impl AlpmParser for RelationOrSoname {
62 fn parser(input: &mut &str) -> ModalResult<Self> {
73 alt((
76 SonameV2::parser.map(RelationOrSoname::SonameV2),
77 SonameV1::parser.map(RelationOrSoname::SonameV1),
78 PackageRelation::parser.map(RelationOrSoname::Relation),
79 ))
80 .context(StrContext::Expected(StrContextValue::Description(
81 "alpm-sonamev2, alpm-sonamev1 or alpm-package-relation",
82 )))
83 .parse_next(input)
84 }
85}
86
87impl Display for RelationOrSoname {
88 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
89 match self {
90 RelationOrSoname::Relation(version) => write!(f, "{version}"),
91 RelationOrSoname::SonameV1(soname) => write!(f, "{soname}"),
92 RelationOrSoname::SonameV2(soname) => write!(f, "{soname}"),
93 }
94 }
95}
96
97impl FromStr for RelationOrSoname {
98 type Err = Error;
99
100 fn from_str(s: &str) -> Result<Self, Self::Err> {
139 Self::parser
140 .parse(s)
141 .map_err(|error| Error::ParseError(error.to_string()))
142 }
143}
144
145#[cfg(test)]
146mod tests {
147 use insta::assert_snapshot;
148 use rstest::rstest;
149 use testresult::TestResult;
150
151 use super::*;
152 use crate::{ElfArchitectureFormat, Soname, VersionOrSoname, configure_insta};
153
154 #[rstest]
155 #[case("libexample.so.1")]
156 #[case("lib:libexample.so-abc")]
157 #[case("lib:libexample.so.10-10")]
158 #[case("lib:libexample.so.1.0.0-64")]
159 fn invalid_sonamev2_parser(#[case] input: &str) {
160 let Err(Error::ParseError(err_msg)) = SonameV2::from_str(input) else {
161 panic!("'{input}' did not fail to parse as expected")
162 };
163
164 let (test_name, _guard) = configure_insta();
165 assert_snapshot!(test_name, err_msg.to_string());
166 }
167
168 #[rstest]
169 #[case(
170 "example",
171 RelationOrSoname::Relation(PackageRelation::new("example".parse().unwrap(), None))
172 )]
173 #[case(
174 "example=1.0.0",
175 RelationOrSoname::Relation(PackageRelation::new("example".parse().unwrap(), "=1.0.0".parse().ok()))
176 )]
177 #[case(
178 "example>=1.0.0",
179 RelationOrSoname::Relation(PackageRelation::new("example".parse().unwrap(), ">=1.0.0".parse().ok()))
180 )]
181 #[case(
182 "lib:example.so.1",
183 RelationOrSoname::SonameV2(
184 SonameV2::new(
185 "lib".parse().unwrap(),
186 Soname::from_str("example.so.1").unwrap(),
187 )
188 )
189 )]
190 #[case(
191 "lib:example.so",
192 RelationOrSoname::SonameV2(
193 SonameV2::new(
194 "lib".parse().unwrap(),
195 Soname::from_str("example.so").unwrap(),
196 )
197 )
198 )]
199 #[case(
200 "example.so",
201 RelationOrSoname::SonameV1(
202 SonameV1::new(
203 "example.so".parse().unwrap(),
204 None,
205 None,
206 ).unwrap()
207 )
208 )]
209 #[case(
210 "example.so=1.0.0-64",
211 RelationOrSoname::SonameV1(
212 SonameV1::new(
213 "example.so".parse().unwrap(),
214 Some(VersionOrSoname::Version("1.0.0".parse().unwrap())),
215 Some(ElfArchitectureFormat::Bit64),
216 ).unwrap()
217 )
218 )]
219 #[case(
220 "libexample.so=otherlibexample.so-64",
221 RelationOrSoname::SonameV1(
222 SonameV1::new(
223 "libexample.so".parse().unwrap(),
224 Some(VersionOrSoname::Soname("otherlibexample.so".parse().unwrap())),
225 Some(ElfArchitectureFormat::Bit64),
226 ).unwrap()
227 )
228 )]
229 fn test_relation_or_soname_parser(
230 #[case] mut input: &str,
231 #[case] expected: RelationOrSoname,
232 ) -> TestResult {
233 let input_str = input.to_string();
234 let result = RelationOrSoname::parser(&mut input)?;
235 assert_eq!(result, expected);
236 assert_eq!(result.to_string(), input_str);
237 Ok(())
238 }
239}