runmat_package/identity/
package.rs1use super::RegistryId;
2use crate::policy::{validate_canonical_segment, PACKAGE_ALIAS_MAX_LEN, PACKAGE_SEGMENT_MAX_LEN};
3use crate::IdentityError;
4use serde::{Deserialize, Serialize};
5use std::fmt::{Display, Formatter};
6use std::str::FromStr;
7
8#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
9#[serde(try_from = "String", into = "String")]
10pub struct CanonicalPackageId {
11 registry: RegistryId,
12 organization: String,
13 name: String,
14}
15
16impl CanonicalPackageId {
17 pub fn new(
18 registry: RegistryId,
19 organization: impl Into<String>,
20 name: impl Into<String>,
21 ) -> Result<Self, IdentityError> {
22 let organization = organization.into();
23 let name = name.into();
24 validate_canonical_segment(
25 &organization,
26 "package organization",
27 PACKAGE_SEGMENT_MAX_LEN,
28 )?;
29 validate_canonical_segment(&name, "package name", PACKAGE_SEGMENT_MAX_LEN)?;
30 Ok(Self {
31 registry,
32 organization,
33 name,
34 })
35 }
36
37 pub fn registry(&self) -> &RegistryId {
38 &self.registry
39 }
40
41 pub fn organization(&self) -> &str {
42 &self.organization
43 }
44
45 pub fn name(&self) -> &str {
46 &self.name
47 }
48}
49
50impl Display for CanonicalPackageId {
51 fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
52 write!(
53 formatter,
54 "{}:{}/{}",
55 self.registry, self.organization, self.name
56 )
57 }
58}
59
60impl FromStr for CanonicalPackageId {
61 type Err = IdentityError;
62
63 fn from_str(value: &str) -> Result<Self, Self::Err> {
64 let Some((registry, package)) = value.split_once(':') else {
65 return Err(invalid_package(value, "missing registry prefix"));
66 };
67 let Some((organization, name)) = package.split_once('/') else {
68 return Err(invalid_package(value, "missing organization namespace"));
69 };
70 if name.contains('/') {
71 return Err(invalid_package(
72 value,
73 "contains more than two package segments",
74 ));
75 }
76 Self::new(registry.parse()?, organization, name)
77 }
78}
79
80impl TryFrom<String> for CanonicalPackageId {
81 type Error = IdentityError;
82
83 fn try_from(value: String) -> Result<Self, Self::Error> {
84 value.parse()
85 }
86}
87
88impl From<CanonicalPackageId> for String {
89 fn from(value: CanonicalPackageId) -> Self {
90 value.to_string()
91 }
92}
93
94#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
95#[serde(try_from = "String", into = "String")]
96pub struct PackageAlias(String);
97
98impl PackageAlias {
99 pub fn new(value: impl Into<String>) -> Result<Self, IdentityError> {
100 let value = value.into();
101 validate_canonical_segment(&value, "dependency alias", PACKAGE_ALIAS_MAX_LEN)?;
102 Ok(Self(value))
103 }
104
105 pub fn as_str(&self) -> &str {
106 &self.0
107 }
108}
109
110impl Display for PackageAlias {
111 fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
112 formatter.write_str(&self.0)
113 }
114}
115
116impl FromStr for PackageAlias {
117 type Err = IdentityError;
118
119 fn from_str(value: &str) -> Result<Self, Self::Err> {
120 Self::new(value)
121 }
122}
123
124impl TryFrom<String> for PackageAlias {
125 type Error = IdentityError;
126
127 fn try_from(value: String) -> Result<Self, Self::Error> {
128 Self::new(value)
129 }
130}
131
132impl From<PackageAlias> for String {
133 fn from(value: PackageAlias) -> Self {
134 value.0
135 }
136}
137
138fn invalid_package(value: &str, reason: &'static str) -> IdentityError {
139 IdentityError::InvalidName {
140 kind: "canonical package ID",
141 value: value.to_string(),
142 reason,
143 }
144}