assorted_debian_utils/
package.rs1use std::fmt::Display;
9
10use serde::Deserialize;
11use thiserror::Error;
12
13use crate::{utils::TryFromStrVisitor, version::PackageVersion};
14
15fn check_package_name(package: &str) -> Result<(), PackageError> {
16 if package.len() < 2 {
18 return Err(PackageError::InvalidNameLength);
19 }
20
21 if !package
22 .chars()
23 .enumerate()
24 .all(|(i, c)| c.is_ascii_lowercase() || c.is_ascii_digit() || (i > 0 && ".+-".contains(c)))
25 {
26 Err(PackageError::InvalidName)
27 } else {
28 Ok(())
29 }
30}
31
32#[derive(Clone, Copy, Debug, Error)]
34pub enum PackageError {
35 #[error("package name too short")]
36 InvalidNameLength,
38 #[error("package name contains invalid character")]
39 InvalidName,
41}
42
43#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
45pub struct PackageName(String);
46
47impl TryFrom<&str> for PackageName {
48 type Error = PackageError;
49
50 fn try_from(package: &str) -> Result<Self, Self::Error> {
51 check_package_name(package).map(|_| Self(package.to_owned()))
52 }
53}
54
55impl TryFrom<String> for PackageName {
56 type Error = PackageError;
57
58 fn try_from(package: String) -> Result<Self, Self::Error> {
59 check_package_name(&package).map(|_| Self(package))
60 }
61}
62
63impl AsRef<str> for PackageName {
64 fn as_ref(&self) -> &str {
65 self.0.as_str()
66 }
67}
68
69impl PartialEq<&str> for PackageName {
70 fn eq(&self, other: &&str) -> bool {
71 self.0.eq(other)
72 }
73}
74
75impl PartialEq<String> for PackageName {
76 fn eq(&self, other: &String) -> bool {
77 self.0.eq(other)
78 }
79}
80
81impl Display for PackageName {
82 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83 write!(f, "{}", self.0)
84 }
85}
86
87impl<'de> Deserialize<'de> for PackageName {
88 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
89 where
90 D: serde::Deserializer<'de>,
91 {
92 deserializer.deserialize_str(TryFromStrVisitor::new("a package name"))
93 }
94}
95
96#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
98pub struct VersionedPackage {
99 pub package: PackageName,
101 pub version: PackageVersion,
103}
104
105impl AsRef<PackageName> for VersionedPackage {
106 fn as_ref(&self) -> &PackageName {
107 &self.package
108 }
109}
110
111impl AsRef<PackageVersion> for VersionedPackage {
112 fn as_ref(&self) -> &PackageVersion {
113 &self.version
114 }
115}
116
117#[cfg(test)]
118mod test {
119 use super::*;
120
121 #[test]
122 fn valid_package_names() {
123 assert!(PackageName::try_from("zathura").is_ok());
124 assert!(PackageName::try_from("0ad").is_ok());
125 assert!(PackageName::try_from("zathura-pdf").is_ok());
126 }
127
128 #[test]
129 fn invalid_package_names() {
130 assert!(PackageName::try_from("z").is_err());
131 assert!(PackageName::try_from("-ad").is_err());
132 }
133}