sim_lib_namespace/
specifier.rs1use std::fmt;
4
5use super::ModuleIdentity;
6
7pub const MAX_SPECIFIER_CANDIDATES: usize = 16;
9
10pub const MAX_SPECIFIER_BYTES: usize = 4_096;
12
13#[derive(Clone, Debug, PartialEq, Eq)]
19pub struct SpecifierPolicyRequest {
20 importer: Option<ModuleIdentity>,
21 candidates: Vec<String>,
22}
23
24impl SpecifierPolicyRequest {
25 pub fn new(
28 importer: Option<ModuleIdentity>,
29 candidates: Vec<String>,
30 ) -> std::result::Result<Self, SpecifierRefusal> {
31 if candidates.is_empty() {
32 return Err(SpecifierRefusal::new(
33 SpecifierRefusalCode::NoCandidates,
34 "module specifier policy requires at least one candidate",
35 ));
36 }
37 if candidates.len() > MAX_SPECIFIER_CANDIDATES {
38 return Err(SpecifierRefusal::new(
39 SpecifierRefusalCode::TooManyCandidates,
40 format!(
41 "module specifier policy candidate count {} exceeds {}",
42 candidates.len(),
43 MAX_SPECIFIER_CANDIDATES
44 ),
45 ));
46 }
47 if let Some(candidate) = candidates
48 .iter()
49 .find(|candidate| candidate.len() > MAX_SPECIFIER_BYTES)
50 {
51 return Err(SpecifierRefusal::new(
52 SpecifierRefusalCode::CandidateTooLong,
53 format!(
54 "module specifier policy candidate length {} exceeds {} bytes",
55 candidate.len(),
56 MAX_SPECIFIER_BYTES
57 ),
58 ));
59 }
60 Ok(Self {
61 importer,
62 candidates,
63 })
64 }
65
66 pub fn importer(&self) -> Option<&ModuleIdentity> {
68 self.importer.as_ref()
69 }
70
71 pub fn candidates(&self) -> &[String] {
73 &self.candidates
74 }
75}
76
77#[derive(Clone, Debug, PartialEq, Eq)]
79pub struct NormalizedSpecifier(String);
80
81impl NormalizedSpecifier {
82 pub fn new(text: String) -> std::result::Result<Self, SpecifierRefusal> {
84 if text.len() > MAX_SPECIFIER_BYTES {
85 return Err(SpecifierRefusal::new(
86 SpecifierRefusalCode::CandidateTooLong,
87 format!(
88 "normalized module specifier length {} exceeds {} bytes",
89 text.len(),
90 MAX_SPECIFIER_BYTES
91 ),
92 ));
93 }
94 Ok(Self(text))
95 }
96
97 pub fn as_str(&self) -> &str {
99 &self.0
100 }
101}
102
103#[derive(Clone, Copy, Debug, PartialEq, Eq)]
105pub enum SpecifierRefusalCode {
106 NoCandidates,
108 TooManyCandidates,
110 CandidateTooLong,
112 IdentityRequiresOneCandidate,
114 PolicyRefused,
116}
117
118#[derive(Clone, Debug, PartialEq, Eq)]
120pub struct SpecifierRefusal {
121 code: SpecifierRefusalCode,
122 detail: String,
123}
124
125impl SpecifierRefusal {
126 pub fn new(code: SpecifierRefusalCode, detail: impl Into<String>) -> Self {
128 Self {
129 code,
130 detail: detail.into(),
131 }
132 }
133
134 pub fn code(&self) -> SpecifierRefusalCode {
136 self.code
137 }
138
139 pub fn detail(&self) -> &str {
141 &self.detail
142 }
143}
144
145impl fmt::Display for SpecifierRefusal {
146 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
147 formatter.write_str(&self.detail)
148 }
149}
150
151pub trait ModuleSpecifierPolicy: Send + Sync {
153 fn resolve(
155 &self,
156 request: &SpecifierPolicyRequest,
157 ) -> std::result::Result<NormalizedSpecifier, SpecifierRefusal>;
158}
159
160#[derive(Clone, Copy, Debug, Default)]
162pub struct IdentitySpecifierPolicy;
163
164impl ModuleSpecifierPolicy for IdentitySpecifierPolicy {
165 fn resolve(
166 &self,
167 request: &SpecifierPolicyRequest,
168 ) -> std::result::Result<NormalizedSpecifier, SpecifierRefusal> {
169 if request.candidates.len() != 1 {
170 return Err(SpecifierRefusal::new(
171 SpecifierRefusalCode::IdentityRequiresOneCandidate,
172 format!(
173 "identity module specifier policy requires exactly one candidate, got {}",
174 request.candidates.len()
175 ),
176 ));
177 }
178 NormalizedSpecifier::new(request.candidates[0].clone())
179 }
180}