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