1use crate::{
6 common::{IntoArray, SCOPE_VALIDITY},
7 pattern::{Pattern, Similarity},
8 resource::Resource,
9};
10
11mod service;
14pub(crate) use self::service::FinalHost;
15pub use service::{ArcHostService, HostService, LeakedHostService};
16
17pub struct Host {
22 pattern: Pattern,
23 root_resource: Resource,
24}
25
26impl Host {
27 pub fn new<P>(host_pattern: P, root: Resource) -> Self
39 where
40 P: AsRef<str>,
41 {
42 let host_pattern = match parse_host_pattern(host_pattern) {
43 Ok(host_pattern) => host_pattern,
44 Err(HostPatternError::Empty) => panic!("empty host pattern"),
45 Err(HostPatternError::Wildcard) => panic!("host pattern cannot be a wildcard"),
46 };
47
48 if !root.is("/") {
49 panic!("host can only have a root resource");
50 }
51
52 Self::with_pattern(host_pattern, root)
53 }
54
55 pub(crate) fn with_pattern(host_pattern: Pattern, mut root: Resource) -> Self {
56 if root.host_pattern_ref().is_none() {
57 root.set_host_pattern(host_pattern.clone());
58 } else {
59 let resource_host_pattern = root.host_pattern_ref().expect(SCOPE_VALIDITY);
60
61 if resource_host_pattern.compare(&host_pattern) != Similarity::Same {
62 panic!(
63 "resource is intended to belong to a host {}",
64 resource_host_pattern,
65 );
66 }
67 }
68
69 Self {
70 pattern: host_pattern,
71 root_resource: root,
72 }
73 }
74
75 #[inline(always)]
79 pub fn is<P: AsRef<str>>(&self, pattern: P) -> bool {
80 let pattern = Pattern::parse(pattern.as_ref());
81
82 self.pattern.compare(&pattern) == Similarity::Same
83 }
84
85 #[inline(always)]
86 pub(crate) fn pattern_string(&self) -> String {
87 self.pattern.to_string()
88 }
89
90 #[inline(always)]
91 pub(crate) fn pattern_ref(&self) -> &Pattern {
92 &self.pattern
93 }
94
95 #[inline(always)]
96 pub(crate) fn compare_pattern(&self, other_host_pattern: &Pattern) -> Similarity {
97 self.pattern.compare(other_host_pattern)
98 }
99
100 #[inline(always)]
101 pub(crate) fn root_mut(&mut self) -> &mut Resource {
102 &mut self.root_resource
103 }
104
105 #[inline(always)]
106 pub(crate) fn root(&mut self) -> Resource {
107 std::mem::replace(
108 &mut self.root_resource,
109 Resource::with_pattern(Pattern::default()),
110 )
111 }
112
113 #[inline(always)]
114 pub(crate) fn set_root(&mut self, root: Resource) {
115 if self.root_resource.pattern_string() != "" {
116 panic!("host already has a root resource");
117 }
118
119 self.root_resource = root;
120 }
121
122 pub(crate) fn merge_or_replace_root(&mut self, mut new_root: Resource) {
123 if !new_root.has_some_effect() {
124 self.root_resource.keep_subresources(new_root);
125 } else if !self.root_resource.has_some_effect() {
126 new_root.keep_subresources(self.root());
127 self.root_resource = new_root;
128 } else {
129 panic!(
130 "conflicting root resources for a host '{}'",
131 self.pattern_string()
132 )
133 }
134 }
135
136 pub(crate) fn into_pattern_and_root(self) -> (Pattern, Resource) {
137 let Host {
138 pattern,
139 root_resource,
140 } = self;
141
142 (pattern, root_resource)
143 }
144
145 pub(crate) fn finalize(self) -> FinalHost {
146 let Host {
147 pattern,
148 root_resource,
149 } = self;
150
151 FinalHost::new(pattern, root_resource.finalize())
152 }
153
154 #[inline(always)]
156 pub fn into_service(self) -> HostService {
157 HostService::new(self.finalize())
158 }
159
160 #[inline(always)]
162 pub fn into_arc_service(self) -> ArcHostService {
163 ArcHostService::from(self.into_service())
164 }
165
166 #[inline(always)]
168 pub fn into_leaked_service(self) -> LeakedHostService {
169 LeakedHostService::from(self.into_service())
170 }
171}
172
173impl IntoArray<Host, 1> for Host {
174 fn into_array(self) -> [Host; 1] {
175 [self]
176 }
177}
178
179pub(crate) fn parse_host_pattern<P: AsRef<str>>(
182 host_pattern: P,
183) -> Result<Pattern, HostPatternError> {
184 let host_pattern_str = host_pattern.as_ref();
185
186 if host_pattern_str.is_empty() {
187 return Err(HostPatternError::Empty);
188 }
189
190 let host_pattern_str = host_pattern_str
191 .strip_prefix("https://")
192 .or_else(|| host_pattern_str.strip_prefix("http://"))
193 .unwrap_or(host_pattern_str);
194
195 let host_pattern = host_pattern_str
196 .strip_suffix('/')
197 .map_or(Pattern::parse(host_pattern_str), Pattern::parse);
198
199 if host_pattern.is_wildcard() {
200 return Err(HostPatternError::Wildcard);
201 }
202
203 Ok(host_pattern)
204}
205
206#[derive(Debug, crate::ImplError)]
207pub(crate) enum HostPatternError {
208 #[error("empty host pattern")]
209 Empty,
210 #[error("wildcard host pattern")]
211 Wildcard,
212}
213
214