winget_types/shared/
package_version.rs1use std::str::FromStr;
2
3use derive_more::{Deref, Display, Into};
4use serde_with::{DeserializeFromStr, SerializeDisplay};
5use thiserror::Error;
6
7use crate::shared::{DISALLOWED_CHARACTERS, version::Version};
8
9#[derive(
10 Clone,
11 Debug,
12 Default,
13 Deref,
14 Display,
15 Eq,
16 PartialEq,
17 Ord,
18 PartialOrd,
19 Hash,
20 Into,
21 SerializeDisplay,
22 DeserializeFromStr,
23)]
24#[into(ref)]
25pub struct PackageVersion(Version);
26
27#[derive(Error, Debug, Eq, PartialEq)]
28pub enum PackageVersionError {
29 #[error("Package version may not contain any control characters")]
30 ContainsControlChars,
31 #[error(
32 "Package version may not contain any of the following characters: {:?}",
33 DISALLOWED_CHARACTERS
34 )]
35 DisallowedCharacters,
36 #[error(
37 "Package version cannot be more than {} characters long",
38 PackageVersion::MAX_LENGTH
39 )]
40 TooLong,
41}
42
43impl PackageVersion {
44 const MAX_LENGTH: usize = 1 << 7;
45
46 pub fn new<S: AsRef<str>>(input: S) -> Result<Self, PackageVersionError> {
47 let input = input.as_ref();
48
49 if input.contains(DISALLOWED_CHARACTERS) {
50 Err(PackageVersionError::DisallowedCharacters)
51 } else if input.contains(char::is_control) {
52 Err(PackageVersionError::ContainsControlChars)
53 } else if input.chars().count() > Self::MAX_LENGTH {
54 Err(PackageVersionError::TooLong)
55 } else {
56 Ok(Self(Version::new(input)))
57 }
58 }
59}
60
61impl FromStr for PackageVersion {
62 type Err = PackageVersionError;
63
64 fn from_str(s: &str) -> Result<Self, Self::Err> {
65 Self::new(s)
66 }
67}
68
69#[cfg(test)]
70mod tests {
71 use crate::shared::{
72 DISALLOWED_CHARACTERS,
73 package_version::{PackageVersion, PackageVersionError},
74 };
75
76 #[test]
77 fn package_version_disallowed_characters() {
78 for char in DISALLOWED_CHARACTERS {
79 assert_eq!(
80 PackageVersion::new(format!("1.2{char}3")).err().unwrap(),
81 PackageVersionError::DisallowedCharacters
82 )
83 }
84 }
85
86 #[test]
87 fn package_version_contains_control_chars() {
88 assert_eq!(
89 PackageVersion::new("1.2\03").err().unwrap(),
90 PackageVersionError::ContainsControlChars
91 );
92 }
93
94 #[test]
95 fn unicode_package_version_max_length() {
96 let version = "🦀".repeat(PackageVersion::MAX_LENGTH);
97
98 assert!(version.len() > PackageVersion::MAX_LENGTH);
100 assert!(version.encode_utf16().count() > PackageVersion::MAX_LENGTH);
101 assert_eq!(version.chars().count(), PackageVersion::MAX_LENGTH);
102 assert!(PackageVersion::new(version).is_ok());
103 }
104
105 #[test]
106 fn package_version_too_long() {
107 let version = "🦀".repeat(PackageVersion::MAX_LENGTH + 1);
108
109 assert_eq!(
110 PackageVersion::new(version).err().unwrap(),
111 PackageVersionError::TooLong
112 );
113 }
114}