1use std::cmp::Ordering;
2use std::cmp::Ordering::Equal;
3use std::fmt::{Debug, Display, Formatter};
4
5use serde::{Deserialize, Serialize};
6
7use crate::helpers::{check_character_collision, remove_first_and_last_characters};
8
9pub use self::{fmri_list::FMRIList, publisher::Publisher, version::Version};
10
11pub mod fmri_list;
12mod helpers;
13pub mod publisher;
14#[cfg(test)]
15mod tests;
16pub mod version;
17
18#[derive(PartialEq, Serialize, Deserialize, Clone, Eq, Hash)]
29pub struct FMRI {
30 publisher: Option<Publisher>,
32 package_name: String,
34 version: Option<Version>,
36}
37
38impl FMRI {
39 pub fn new_from_package_name(mut package_name: String) -> Result<Self, String> {
41 if package_name.is_empty() {
42 panic!("package name can't be empty")
43 }
44
45 check_character_collision(&package_name)?;
46 package_name = remove_first_and_last_characters(&package_name, '/').to_owned();
47
48 Ok(Self {
49 publisher: None,
50 package_name,
51 version: None,
52 })
53 }
54
55 pub fn parse_raw(raw_fmri: &str) -> Result<Self, String> {
69 let mut publisher: Option<Publisher> = None;
70 let mut version: Option<Version> = None;
71 let mut package_name: String = raw_fmri.to_owned().trim_start_matches("fmri=").to_owned();
72
73 match Publisher::parse_publisher_from_raw_fmri(raw_fmri.to_owned()) {
74 Ok(None) => {
75 package_name = package_name.trim_start_matches("pkg:/").to_owned();
76 }
77 Ok(Some(p)) => {
78 publisher = Some(p);
79 let (_, end_str) = package_name
80 .trim_start_matches("pkg://")
81 .split_once('/')
82 .expect("Fmri must contain \"/package_name\"");
83 package_name = end_str.to_owned()
84 }
85 Err(e) => return Err(e),
86 }
87
88 match Version::parse_version_from_raw_fmri(raw_fmri.to_owned()) {
89 Ok(None) => {}
90 Ok(Some(v)) => {
91 version = Some(v);
92 let (start_str, _) = package_name.split_once('@').expect("error");
93 package_name = start_str.to_owned()
94 }
95 Err(e) => return Err(e),
96 }
97
98 let mut fmri = Self::new_from_package_name(package_name)?;
99 if let Some(p) = publisher {
100 fmri.change_publisher(p);
101 }
102 if let Some(v) = version {
103 fmri.change_version(v);
104 }
105 Ok(fmri)
106 }
107
108 pub fn package_name_eq(&self, comparing_to: &FMRI) -> bool {
110 self.get_package_name_as_ref_string()
111 .eq(comparing_to.get_package_name_as_ref_string())
112 }
113
114 pub fn get_package_name_as_string(self) -> String {
115 self.package_name
116 }
117
118 pub fn get_package_name_as_ref_string(&self) -> &String {
119 &self.package_name
120 }
121
122 pub fn get_package_name_as_ref_mut_string(&mut self) -> &mut String {
123 &mut self.package_name
124 }
125
126 pub fn get_publisher(self) -> Option<Publisher> {
127 self.publisher
128 }
129
130 pub fn get_publisher_ref(&self) -> &Option<Publisher> {
131 &self.publisher
132 }
133
134 pub fn get_publisher_ref_mut(&mut self) -> &mut Option<Publisher> {
135 &mut self.publisher
136 }
137
138 pub fn has_publisher(&self) -> bool {
139 self.publisher.is_some()
140 }
141
142 pub fn change_publisher(&mut self, publisher: Publisher) {
143 self.publisher = Some(publisher);
144 }
145
146 pub fn get_publisher_as_ref_string(&self) -> Option<&String> {
148 if let Some(publisher) = &self.publisher {
149 return Some(publisher.get_as_ref_string());
150 }
151 None
152 }
153
154 pub fn remove_publisher(&mut self) {
155 self.publisher = None
156 }
157
158 pub fn get_version(self) -> Option<Version> {
159 self.version
160 }
161
162 pub fn get_version_ref(&self) -> &Option<Version> {
163 &self.version
164 }
165 pub fn get_version_ref_mut(&mut self) -> &mut Option<Version> {
166 &mut self.version
167 }
168
169 pub fn has_version(&self) -> bool {
170 self.version.is_some()
171 }
172
173 pub fn change_version(&mut self, version: Version) {
174 self.version = Some(version)
175 }
176
177 pub fn get_version_as_string(&self) -> Option<String> {
179 if let Some(version) = &self.version {
180 return Some(format!("{}", version));
181 }
182 None
183 }
184
185 pub fn remove_version(&mut self) -> &mut FMRI {
186 self.version = None;
187 self
188 }
189}
190
191impl PartialOrd<Self> for FMRI {
192 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
193 Some(self.cmp(other))
194 }
195}
196
197impl Ord for FMRI {
198 fn cmp(&self, other: &Self) -> Ordering {
200 self.version
201 .as_ref()
202 .and_then(|ver| other.version.as_ref().map(|ver2| ver.cmp(ver2)))
203 .unwrap_or(Equal)
204 }
205}
206
207impl Display for FMRI {
208 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
209 let mut string: String = "".to_owned();
210
211 if let Some(publisher) = self.get_publisher_as_ref_string() {
212 string.push_str("pkg://");
213 string.push_str(publisher);
214 string.push('/');
215 } else {
216 string.push_str("pkg:/");
217 }
218
219 string.push_str(self.get_package_name_as_ref_string());
220
221 if let Some(version) = self.get_version_as_string() {
222 string.push_str(&version)
223 }
224
225 write!(f, "{}", string)
226 }
227}
228
229impl Debug for FMRI {
230 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
231 write!(f, "{}", self)
232 }
233}