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),
33 SonameV1(SonameV1),
37 SonameV2(SonameV2),
41}
42
43impl RelationOrSoname {
44 pub fn parser(input: &mut &str) -> ModalResult<Self> {
50 let checkpoint = input.checkpoint();
53 let sonamev2_result = SonameV2::parser.parse_next(input);
54 if sonamev2_result.is_ok() {
55 let sonamev2 = sonamev2_result?;
56 return Ok(RelationOrSoname::SonameV2(sonamev2));
57 }
58
59 input.reset(&checkpoint);
60 let sonamev1_result = SonameV1::parser.parse_next(input);
61 if sonamev1_result.is_ok() {
62 let sonamev1 = sonamev1_result?;
63 return Ok(RelationOrSoname::SonameV1(sonamev1));
64 }
65
66 input.reset(&checkpoint);
67 let relation_result = rest.and_then(PackageRelation::parser).parse_next(input);
68 if relation_result.is_ok() {
69 let relation = relation_result?;
70 return Ok(RelationOrSoname::Relation(relation));
71 }
72
73 cut_err(fail)
74 .context(StrContext::Expected(StrContextValue::Description(
75 "alpm-sonamev2, alpm-sonamev1 or alpm-package-relation",
76 )))
77 .parse_next(input)
78 }
79}
80
81impl Display for RelationOrSoname {
82 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
83 match self {
84 RelationOrSoname::Relation(version) => write!(f, "{version}"),
85 RelationOrSoname::SonameV1(soname) => write!(f, "{soname}"),
86 RelationOrSoname::SonameV2(soname) => write!(f, "{soname}"),
87 }
88 }
89}
90
91impl FromStr for RelationOrSoname {
92 type Err = Error;
93
94 fn from_str(s: &str) -> Result<Self, Self::Err> {
134 Self::parser
135 .parse(s)
136 .map_err(|error| Error::AlpmType(alpm_types::Error::ParseError(error.to_string())))
137 }
138}
139
140#[cfg(test)]
141mod tests {
142 use alpm_types::{ElfArchitectureFormat, VersionOrSoname};
143 use rstest::rstest;
144 use testresult::TestResult;
145
146 use super::*;
147
148 #[rstest]
149 #[case(
150 "example",
151 RelationOrSoname::Relation(PackageRelation::new("example".parse().unwrap(), None))
152 )]
153 #[case(
154 "example=1.0.0",
155 RelationOrSoname::Relation(PackageRelation::new("example".parse().unwrap(), "=1.0.0".parse().ok()))
156 )]
157 #[case(
158 "example>=1.0.0",
159 RelationOrSoname::Relation(PackageRelation::new("example".parse().unwrap(), ">=1.0.0".parse().ok()))
160 )]
161 #[case(
162 "lib:example.so.1",
163 RelationOrSoname::SonameV2(
164 SonameV2::new(
165 "lib".parse().unwrap(),
166 "example.so.1".parse().unwrap(),
167 )
168 )
169 )]
170 #[case(
171 "lib:example.so",
172 RelationOrSoname::SonameV2(
173 SonameV2::new(
174 "lib".parse().unwrap(),
175 "example.so".parse().unwrap(),
176 )
177 )
178 )]
179 #[case(
180 "example.so",
181 RelationOrSoname::SonameV1(
182 SonameV1::new(
183 "example.so".parse().unwrap(),
184 None,
185 None,
186 ).unwrap()
187 )
188 )]
189 #[case(
190 "example.so=1.0.0-64",
191 RelationOrSoname::SonameV1(
192 SonameV1::new(
193 "example.so".parse().unwrap(),
194 Some(VersionOrSoname::Version("1.0.0".parse().unwrap())),
195 Some(ElfArchitectureFormat::Bit64),
196 ).unwrap()
197 )
198 )]
199 #[case(
200 "libexample.so=otherlibexample.so-64",
201 RelationOrSoname::SonameV1(
202 SonameV1::new(
203 "libexample.so".parse().unwrap(),
204 Some(VersionOrSoname::Soname("otherlibexample.so".parse().unwrap())),
205 Some(ElfArchitectureFormat::Bit64),
206 ).unwrap()
207 )
208
209 )]
210 fn test_relation_or_soname_parser(
211 #[case] mut input: &str,
212 #[case] expected: RelationOrSoname,
213 ) -> TestResult {
214 let input_str = input.to_string();
215 let result = RelationOrSoname::parser(&mut input)?;
216 assert_eq!(result, expected);
217 assert_eq!(result.to_string(), input_str);
218 Ok(())
219 }
220}