1use std::{cmp::Ordering, ffi::OsStr, fmt::Display};
2
3use semver::Version;
4use serde::{Deserialize, Serialize};
5
6use crate::{utils::BundleFromError, Depend, Info, Plugin};
7
8#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Hash)]
9pub struct Bundle {
10 pub id: String,
11 pub version: Version,
12 pub format: String,
13}
14
15impl Bundle {
16 pub fn from_filename<S>(filename: &S) -> Result<Self, BundleFromError>
17 where
18 S: AsRef<OsStr> + ?Sized,
19 {
20 let mut path = filename
21 .as_ref()
22 .to_str()
23 .ok_or(BundleFromError::OsStrToStrFailed)?
24 .to_string();
25
26 let format = path
27 .drain(path.rfind('.').ok_or(BundleFromError::FormatFailed)? + 1..)
28 .collect::<String>();
29 let version = path
30 .drain(path.rfind("-v").ok_or(BundleFromError::VersionFailed)? + 2..path.len() - 1)
31 .collect::<String>();
32 let id = path
33 .drain(..path.rfind("-v").ok_or(BundleFromError::IDFailed)?)
34 .collect::<String>();
35
36 if format.is_empty() {
37 return Err(BundleFromError::FormatFailed);
38 }
39 if version.is_empty() {
40 return Err(BundleFromError::VersionFailed);
41 }
42 if id.is_empty() {
43 return Err(BundleFromError::IDFailed);
44 }
45
46 Ok(Self {
47 id,
48 version: Version::parse(version.as_str())?,
49 format,
50 })
51 }
52}
53
54impl<ID: AsRef<str>> PartialEq<(ID, &Version)> for Bundle {
55 fn eq(&self, (id, version): &(ID, &Version)) -> bool {
56 self.id == *id.as_ref() && self.version == **version
57 }
58}
59
60impl<O: Send + Sync, I: Info> PartialEq<Plugin<'_, O, I>> for Bundle {
61 fn eq(&self, other: &Plugin<'_, O, I>) -> bool {
62 self.id == other.info.bundle.id && self.version == other.info.bundle.version
63 }
64}
65
66impl PartialEq<Depend> for Bundle {
67 fn eq(&self, Depend { id: name, version }: &Depend) -> bool {
68 self.id == *name && version.matches(&self.version)
69 }
70}
71
72impl<ID: AsRef<str>> PartialOrd<(ID, &Version)> for Bundle {
73 fn partial_cmp(&self, (id, version): &(ID, &Version)) -> Option<Ordering> {
74 match self.id == *id.as_ref() {
75 true => self.version.partial_cmp(*version),
76 false => None,
77 }
78 }
79}
80
81impl<O: Send + Sync, I: Info> PartialOrd<Plugin<'_, O, I>> for Bundle {
82 fn partial_cmp(&self, other: &Plugin<'_, O, I>) -> Option<Ordering> {
83 match self.id == other.info.bundle.id {
84 true => self.version.partial_cmp(&other.info.bundle.version),
85 false => None,
86 }
87 }
88}
89
90impl Display for Bundle {
91 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
92 write!(f, "{}-v{}.{}", self.id, self.version, self.format)
93 }
94}