1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Deserialize, Serialize)]
4pub enum ProvideStrategy {
5 KeepTargetOnly,
6 KeepBoth,
7 Alias,
8}
9
10impl std::fmt::Display for ProvideStrategy {
11 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
12 let msg = match self {
13 ProvideStrategy::KeepTargetOnly => "=>",
14 ProvideStrategy::KeepBoth => "==",
15 ProvideStrategy::Alias => ":",
16 };
17 write!(f, "{msg}")
18 }
19}
20
21#[derive(Debug, Default, Deserialize, Serialize, Clone)]
22pub struct PackageProvide {
23 pub name: String,
24 pub target: Option<String>,
25 pub strategy: Option<ProvideStrategy>,
26 pub symlink_to_bin: bool,
27}
28
29impl PackageProvide {
30 pub fn bin_symlink_names(&self) -> Vec<&str> {
33 if self.symlink_to_bin {
34 return vec![&self.name];
36 }
37 match (&self.target, &self.strategy) {
38 (Some(target), Some(ProvideStrategy::KeepBoth)) => vec![&self.name, target],
39 (Some(target), Some(ProvideStrategy::KeepTargetOnly | ProvideStrategy::Alias)) => {
40 vec![target]
41 }
42 _ => vec![&self.name],
43 }
44 }
45
46 pub fn from_string(provide: &str) -> Self {
47 let (symlink_to_bin, provide) = if let Some(stripped) = provide.strip_prefix('@') {
48 (true, stripped)
49 } else {
50 (false, provide)
51 };
52
53 if let Some((name, target_name)) = provide.split_once("==") {
54 Self {
55 name: name.to_string(),
56 target: Some(target_name.to_string()),
57 strategy: Some(ProvideStrategy::KeepBoth),
58 symlink_to_bin,
59 }
60 } else if let Some((name, target_name)) = provide.split_once("=>") {
61 Self {
62 name: name.to_string(),
63 target: Some(target_name.to_string()),
64 strategy: Some(ProvideStrategy::KeepTargetOnly),
65 symlink_to_bin,
66 }
67 } else if let Some((name, target_name)) = provide.split_once(":") {
68 Self {
69 name: name.to_string(),
70 target: Some(target_name.to_string()),
71 strategy: Some(ProvideStrategy::Alias),
72 symlink_to_bin,
73 }
74 } else {
75 Self {
76 name: provide.to_string(),
77 target: None,
78 strategy: None,
79 symlink_to_bin,
80 }
81 }
82 }
83}
84
85#[cfg(test)]
86mod tests {
87 use super::*;
88
89 #[test]
90 fn test_bin_symlink_names_plain() {
91 let p = PackageProvide::from_string("clipcatd");
92 assert_eq!(p.bin_symlink_names(), vec!["clipcatd"]);
93 }
94
95 #[test]
96 fn test_bin_symlink_names_at_prefix() {
97 let p = PackageProvide::from_string("@clipcat-menu");
98 assert!(p.symlink_to_bin);
99 assert_eq!(p.bin_symlink_names(), vec!["clipcat-menu"]);
100 }
101
102 #[test]
103 fn test_bin_symlink_names_keep_both() {
104 let p = PackageProvide::from_string("clipcatd==clipcat");
105 assert_eq!(p.bin_symlink_names(), vec!["clipcatd", "clipcat"]);
106 }
107
108 #[test]
109 fn test_bin_symlink_names_keep_target_only() {
110 let p = PackageProvide::from_string("clipcatd=>clipcat");
111 assert_eq!(p.bin_symlink_names(), vec!["clipcat"]);
112 }
113
114 #[test]
115 fn test_bin_symlink_names_alias() {
116 let p = PackageProvide::from_string("clipcatd:clipcat");
117 assert_eq!(p.bin_symlink_names(), vec!["clipcat"]);
118 }
119}