Skip to main content

kcode_k1_web_package/
lib.rs

1use kcode_k1_transaction_id::TxId;
2use semver::Version;
3use std::fmt::{Display, Formatter};
4
5mod source;
6pub use source::{SourceFile, SourcePackage, WebDependency};
7
8#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
9pub struct AuthorityId(TxId);
10
11impl AuthorityId {
12    pub const fn new(transaction_id: TxId) -> Self {
13        Self(transaction_id)
14    }
15
16    pub const fn transaction_id(&self) -> &TxId {
17        &self.0
18    }
19}
20
21impl Display for AuthorityId {
22    fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
23        Display::fmt(&self.0, formatter)
24    }
25}
26
27#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
28pub struct WebFamily(AuthorityId, String);
29
30impl WebFamily {
31    pub fn new(
32        authority: AuthorityId,
33        logical_name: impl Into<String>,
34    ) -> Result<Self, PackageError> {
35        let logical_name = logical_name.into();
36        validate_logical_name(&logical_name)?;
37        Ok(Self(authority, logical_name))
38    }
39
40    pub const fn authority(&self) -> AuthorityId {
41        self.0
42    }
43
44    pub fn logical_name(&self) -> &str {
45        &self.1
46    }
47}
48
49#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
50pub struct WebId(WebFamily, Version);
51
52impl WebId {
53    pub fn new(family: WebFamily, version: Version) -> Result<Self, PackageError> {
54        validate_stable(&version)?;
55        Ok(Self(family, version))
56    }
57
58    pub fn family(&self) -> &WebFamily {
59        &self.0
60    }
61
62    pub fn version(&self) -> &Version {
63        &self.1
64    }
65}
66
67#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
68enum SelectorKind {
69    Any,
70    Major(u64),
71    MajorMinor(u64, u64),
72    Exact(u64, u64, u64),
73}
74
75#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
76pub struct DependencySelector(SelectorKind);
77
78impl DependencySelector {
79    pub fn parse(text: &str) -> Result<Self, PackageError> {
80        if text == "*" {
81            return Ok(Self(SelectorKind::Any));
82        }
83        let numbers = text
84            .split('.')
85            .map(parse_selector_number)
86            .collect::<Result<Vec<_>, _>>()?;
87        let kind = match numbers.as_slice() {
88            [major] => SelectorKind::Major(*major),
89            [major, minor] => SelectorKind::MajorMinor(*major, *minor),
90            [major, minor, patch] => SelectorKind::Exact(*major, *minor, *patch),
91            _ => return fail("dependency selector must contain one to three numbers or *"),
92        };
93        Ok(Self(kind))
94    }
95
96    pub fn matches(&self, version: &Version) -> bool {
97        if !version.pre.is_empty() || !version.build.is_empty() {
98            return false;
99        }
100        match self.0 {
101            SelectorKind::Any => true,
102            SelectorKind::Major(major) => version.major == major,
103            SelectorKind::MajorMinor(major, minor) => {
104                version.major == major && version.minor == minor
105            }
106            SelectorKind::Exact(major, minor, patch) => {
107                version.major == major && version.minor == minor && version.patch == patch
108            }
109        }
110    }
111}
112
113impl Display for DependencySelector {
114    fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
115        match self.0 {
116            SelectorKind::Any => formatter.write_str("*"),
117            SelectorKind::Major(major) => write!(formatter, "{major}"),
118            SelectorKind::MajorMinor(major, minor) => write!(formatter, "{major}.{minor}"),
119            SelectorKind::Exact(major, minor, patch) => {
120                write!(formatter, "{major}.{minor}.{patch}")
121            }
122        }
123    }
124}
125
126#[derive(Clone, Debug, Eq, PartialEq)]
127pub struct PackageError(String);
128
129impl PackageError {
130    pub fn message(&self) -> &str {
131        &self.0
132    }
133}
134
135impl Display for PackageError {
136    fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
137        formatter.write_str(&self.0)
138    }
139}
140
141impl std::error::Error for PackageError {}
142
143pub(crate) fn fail<T>(message: impl Into<String>) -> Result<T, PackageError> {
144    Err(PackageError(message.into()))
145}
146
147pub(crate) fn validate_logical_name(name: &str) -> Result<(), PackageError> {
148    let valid = !name.is_empty()
149        && name.len() <= 36
150        && name.split('-').all(|part| {
151            !part.is_empty()
152                && part
153                    .bytes()
154                    .all(|byte| byte.is_ascii_lowercase() || byte.is_ascii_digit())
155        });
156    if !valid {
157        return fail("logical name must be 1-36 bytes of lowercase kebab case");
158    }
159    Ok(())
160}
161
162pub(crate) fn validate_stable(version: &Version) -> Result<(), PackageError> {
163    if !version.pre.is_empty() || !version.build.is_empty() {
164        return fail("version must be stable SemVer without prerelease or build metadata");
165    }
166    Ok(())
167}
168
169fn parse_selector_number(text: &str) -> Result<u64, PackageError> {
170    let canonical = !text.is_empty()
171        && !(text.len() > 1 && text.starts_with('0'))
172        && text.bytes().all(|byte| byte.is_ascii_digit());
173    if !canonical {
174        return fail("dependency selector numbers must use canonical decimal spelling");
175    }
176    text.parse()
177        .map_err(|_| PackageError("dependency selector number is too large".into()))
178}