1use std::{
3 fmt::{Display, Formatter},
4 str::FromStr,
5};
6
7use alpm_types::{PackageRelation, SonameV1, SonameV2};
8use winnow::{
9 ModalResult,
10 Parser,
11 combinator::{cut_err, fail},
12 error::{StrContext, StrContextValue},
13 stream::Stream,
14 token::rest,
15};
16
17use crate::Error;
18#[cfg(doc)]
19use crate::{PackageInfoV1, PackageInfoV2};
20
21#[derive(Clone, Debug, Eq, PartialEq)]
28pub enum RelationOrSoname {
29 Relation(PackageRelation),
30 SonameV1(SonameV1),
31 SonameV2(SonameV2),
32}
33
34impl RelationOrSoname {
35 pub fn parser(input: &mut &str) -> ModalResult<Self> {
41 let checkpoint = input.checkpoint();
44 let sonamev2_result = SonameV2::parser.parse_next(input);
45 if sonamev2_result.is_ok() {
46 let sonamev2 = sonamev2_result?;
47 return Ok(RelationOrSoname::SonameV2(sonamev2));
48 }
49
50 input.reset(&checkpoint);
51 let sonamev1_result = SonameV1::parser.parse_next(input);
52 if sonamev1_result.is_ok() {
53 let sonamev1 = sonamev1_result?;
54 return Ok(RelationOrSoname::SonameV1(sonamev1));
55 }
56
57 input.reset(&checkpoint);
58 let relation_result = rest.and_then(PackageRelation::parser).parse_next(input);
59 if relation_result.is_ok() {
60 let relation = relation_result?;
61 return Ok(RelationOrSoname::Relation(relation));
62 }
63
64 cut_err(fail)
65 .context(StrContext::Expected(StrContextValue::Description(
66 "alpm-sonamev2, alpm-sonamev1 or alpm-package-relation",
67 )))
68 .parse_next(input)
69 }
70}
71
72impl Display for RelationOrSoname {
73 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
74 match self {
75 RelationOrSoname::Relation(version) => write!(f, "{version}"),
76 RelationOrSoname::SonameV1(soname) => write!(f, "{soname}"),
77 RelationOrSoname::SonameV2(soname) => write!(f, "{soname}"),
78 }
79 }
80}
81
82impl FromStr for RelationOrSoname {
83 type Err = Error;
84
85 fn from_str(s: &str) -> Result<Self, Self::Err> {
125 Self::parser
126 .parse(s)
127 .map_err(|error| Error::AlpmType(alpm_types::Error::ParseError(error.to_string())))
128 }
129}
130
131#[cfg(test)]
132mod tests {
133 use alpm_types::{ElfArchitectureFormat, VersionOrSoname};
134 use rstest::rstest;
135 use testresult::TestResult;
136
137 use super::*;
138
139 #[rstest]
140 #[case(
141 "example",
142 RelationOrSoname::Relation(PackageRelation::new("example".parse().unwrap(), None))
143 )]
144 #[case(
145 "example=1.0.0",
146 RelationOrSoname::Relation(PackageRelation::new("example".parse().unwrap(), "=1.0.0".parse().ok()))
147 )]
148 #[case(
149 "example>=1.0.0",
150 RelationOrSoname::Relation(PackageRelation::new("example".parse().unwrap(), ">=1.0.0".parse().ok()))
151 )]
152 #[case(
153 "lib:example.so.1",
154 RelationOrSoname::SonameV2(
155 SonameV2::new(
156 "lib".parse().unwrap(),
157 "example.so.1".parse().unwrap(),
158 )
159 )
160 )]
161 #[case(
162 "lib:example.so",
163 RelationOrSoname::SonameV2(
164 SonameV2::new(
165 "lib".parse().unwrap(),
166 "example.so".parse().unwrap(),
167 )
168 )
169 )]
170 #[case(
171 "example.so",
172 RelationOrSoname::SonameV1(
173 SonameV1::new(
174 "example.so".parse().unwrap(),
175 None,
176 None,
177 ).unwrap()
178 )
179 )]
180 #[case(
181 "example.so=1.0.0-64",
182 RelationOrSoname::SonameV1(
183 SonameV1::new(
184 "example.so".parse().unwrap(),
185 Some(VersionOrSoname::Version("1.0.0".parse().unwrap())),
186 Some(ElfArchitectureFormat::Bit64),
187 ).unwrap()
188 )
189 )]
190 #[case(
191 "libexample.so=otherlibexample.so-64",
192 RelationOrSoname::SonameV1(
193 SonameV1::new(
194 "libexample.so".parse().unwrap(),
195 Some(VersionOrSoname::Soname("otherlibexample.so".parse().unwrap())),
196 Some(ElfArchitectureFormat::Bit64),
197 ).unwrap()
198 )
199
200 )]
201 fn test_relation_or_soname_parser(
202 #[case] mut input: &str,
203 #[case] expected: RelationOrSoname,
204 ) -> TestResult {
205 let input_str = input.to_string();
206 let result = RelationOrSoname::parser(&mut input)?;
207 assert_eq!(result, expected);
208 assert_eq!(result.to_string(), input_str);
209 Ok(())
210 }
211}