1use std::{
3 fmt::{Display, Formatter},
4 str::FromStr,
5};
6
7use alpm_types::{PackageRelation, SharedObjectName, SonameV1};
8use serde::{Deserialize, Serialize};
9use winnow::{
10 ModalResult,
11 Parser,
12 combinator::{cut_err, fail},
13 error::{StrContext, StrContextValue},
14 stream::Stream,
15 token::rest,
16};
17
18use crate::Error;
19#[cfg(doc)]
20use crate::SourceInfoV1;
21
22#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
29#[serde(untagged)]
30pub enum RelationOrSoname {
31 BasicSonameV1(SonameV1),
33 Relation(PackageRelation),
35}
36
37impl PartialEq<PackageRelation> for RelationOrSoname {
38 fn eq(&self, other: &PackageRelation) -> bool {
39 self.to_string() == other.to_string()
40 }
41}
42
43impl PartialEq<SonameV1> for RelationOrSoname {
44 fn eq(&self, other: &SonameV1) -> bool {
45 self.to_string() == other.to_string()
46 }
47}
48
49impl RelationOrSoname {
50 pub fn parser(input: &mut &str) -> ModalResult<Self> {
56 let checkpoint = input.checkpoint();
59 let shared_object_name_result = SharedObjectName::parser.parse_next(input);
60 if shared_object_name_result.is_ok() {
61 let shared_object_name = shared_object_name_result?;
62 return Ok(RelationOrSoname::BasicSonameV1(SonameV1::Basic(
63 shared_object_name,
64 )));
65 }
66
67 input.reset(&checkpoint);
68 let relation_result = rest.and_then(PackageRelation::parser).parse_next(input);
69 if relation_result.is_ok() {
70 let relation = relation_result?;
71 return Ok(RelationOrSoname::Relation(relation));
72 }
73
74 cut_err(fail)
75 .context(StrContext::Expected(StrContextValue::Description(
76 "alpm-sonamev1 or alpm-package-relation",
77 )))
78 .parse_next(input)
79 }
80}
81
82impl Display for RelationOrSoname {
83 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
84 match self {
85 RelationOrSoname::Relation(version) => write!(f, "{version}"),
86 RelationOrSoname::BasicSonameV1(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> {
128 Self::parser
129 .parse(s)
130 .map_err(|error| Error::ParseError(error.to_string()))
131 }
132}
133
134#[cfg(test)]
135mod tests {
136 use rstest::rstest;
137 use testresult::TestResult;
138
139 use super::*;
140
141 #[rstest]
142 #[case(
143 "example",
144 RelationOrSoname::Relation(PackageRelation::new("example".parse().unwrap(), None))
145 )]
146 #[case(
147 "example=1.0.0",
148 RelationOrSoname::Relation(PackageRelation::new("example".parse().unwrap(), "=1.0.0".parse().ok()))
149 )]
150 #[case(
151 "example>=1.0.0",
152 RelationOrSoname::Relation(PackageRelation::new("example".parse().unwrap(), ">=1.0.0".parse().ok()))
153 )]
154 #[case(
155 "example.so",
156 RelationOrSoname::BasicSonameV1(
157 SonameV1::new(
158 "example.so".parse().unwrap(),
159 None,
160 None,
161 ).unwrap()
162 )
163 )]
164 fn test_relation_or_soname_parser(
165 #[case] mut input: &str,
166 #[case] expected: RelationOrSoname,
167 ) -> TestResult {
168 let input_str = input.to_string();
169 let result = RelationOrSoname::parser(&mut input)?;
170 assert_eq!(result, expected);
171 assert_eq!(result.to_string(), input_str);
172 Ok(())
173 }
174}