pro_core/resolver/
package.rs1use serde::{Deserialize, Serialize};
4use std::cmp::Ordering;
5
6#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
8pub struct Package {
9 pub name: String,
11}
12
13impl PartialOrd for Package {
14 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
15 Some(self.cmp(other))
16 }
17}
18
19impl Ord for Package {
20 fn cmp(&self, other: &Self) -> Ordering {
21 self.name.cmp(&other.name)
22 }
23}
24
25impl Package {
26 pub fn new(name: impl Into<String>) -> Self {
28 Self {
29 name: Self::normalize(name.into()),
30 }
31 }
32
33 fn normalize(name: String) -> String {
37 let mut result = String::with_capacity(name.len());
38 let mut prev_was_separator = false;
39
40 for c in name.chars() {
41 match c {
42 '-' | '_' | '.' => {
43 if !prev_was_separator && !result.is_empty() {
44 result.push('-');
45 prev_was_separator = true;
46 }
47 }
48 c => {
49 result.push(c.to_ascii_lowercase());
50 prev_was_separator = false;
51 }
52 }
53 }
54
55 if result.ends_with('-') {
57 result.pop();
58 }
59
60 result
61 }
62}
63
64impl std::fmt::Display for Package {
65 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66 write!(f, "{}", self.name)
67 }
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73
74 #[test]
75 fn test_normalize_simple() {
76 assert_eq!(Package::new("requests").name, "requests");
77 }
78
79 #[test]
80 fn test_normalize_uppercase() {
81 assert_eq!(Package::new("Requests").name, "requests");
82 }
83
84 #[test]
85 fn test_normalize_underscore() {
86 assert_eq!(Package::new("my_package").name, "my-package");
87 }
88
89 #[test]
90 fn test_normalize_dots() {
91 assert_eq!(Package::new("zope.interface").name, "zope-interface");
92 }
93
94 #[test]
95 fn test_normalize_mixed() {
96 assert_eq!(Package::new("My__Package..Name").name, "my-package-name");
97 }
98}