1use crate::one_or_many::{OneOrMany, option_one_or_many_as_ref};
6use crate::types::common::*;
7use crate::{
8 AnyRef, AsClause, AsClauseContext, CapabilityClause, ContextPathClause, Error, FromClause,
9 FromClauseContext, OfferFromRef, PathClause, merge_spanned_vec,
10};
11pub use cm_types::{
12 Availability, BorrowedName, BoundedName, DeliveryType, DependencyType, HandleType, Name,
13 OnTerminate, ParseError, Path, RelativePath, StartupMode, StorageId, Url,
14};
15use cml_macro::Reference;
16use reference_doc::ReferenceDoc;
17use serde::{Deserialize, Serialize, de};
18
19use std::fmt;
20use std::path::PathBuf;
21use std::sync::Arc;
22
23#[derive(Deserialize, Debug, PartialEq, ReferenceDoc, Serialize)]
47#[serde(deny_unknown_fields)]
48#[reference_doc(fields_as = "list", top_level_doc_after_fields)]
49pub struct Environment {
50 pub name: Name,
54
55 #[serde(skip_serializing_if = "Option::is_none")]
59 pub extends: Option<EnvironmentExtends>,
60
61 #[reference_doc(recurse)]
64 #[serde(skip_serializing_if = "Option::is_none")]
65 pub runners: Option<Vec<RunnerRegistration>>,
66
67 #[reference_doc(recurse)]
70 #[serde(skip_serializing_if = "Option::is_none")]
71 pub resolvers: Option<Vec<ResolverRegistration>>,
72
73 #[reference_doc(recurse)]
76 #[serde(skip_serializing_if = "Option::is_none")]
77 pub debug: Option<Vec<DebugRegistration>>,
78
79 #[serde(rename = "__stop_timeout_ms")]
83 #[reference_doc(json_type = "number", rename = "__stop_timeout_ms")]
84 #[serde(skip_serializing_if = "Option::is_none")]
85 pub stop_timeout_ms: Option<StopTimeoutMs>,
86}
87
88impl Environment {
89 pub fn merge_from(&mut self, other: &mut Self) -> Result<(), Error> {
90 if self.extends.is_none() {
91 self.extends = other.extends.take();
92 } else if other.extends.is_some() && other.extends != self.extends {
93 return Err(Error::validate(
94 "cannot merge `environments` that declare conflicting `extends`",
95 ));
96 }
97
98 if self.stop_timeout_ms.is_none() {
99 self.stop_timeout_ms = other.stop_timeout_ms;
100 } else if other.stop_timeout_ms.is_some() && other.stop_timeout_ms != self.stop_timeout_ms {
101 return Err(Error::validate(
102 "cannot merge `environments` that declare conflicting `stop_timeout_ms`",
103 ));
104 }
105
106 match &mut self.runners {
109 Some(r) => {
110 if let Some(o) = &mut other.runners {
111 r.append(o);
112 }
113 }
114 None => self.runners = other.runners.take(),
115 }
116
117 match &mut self.resolvers {
118 Some(r) => {
119 if let Some(o) = &mut other.resolvers {
120 r.append(o);
121 }
122 }
123 None => self.resolvers = other.resolvers.take(),
124 }
125
126 match &mut self.debug {
127 Some(r) => {
128 if let Some(o) = &mut other.debug {
129 r.append(o);
130 }
131 }
132 None => self.debug = other.debug.take(),
133 }
134 Ok(())
135 }
136}
137
138#[derive(Debug, PartialEq, Eq, Hash, Clone, Reference)]
140#[reference(expected = "\"#<environment-name>\"")]
141pub enum EnvironmentRef {
142 Named(Name),
144}
145
146#[derive(Deserialize, Debug, PartialEq, Serialize)]
147#[serde(rename_all = "lowercase")]
148pub enum EnvironmentExtends {
149 Realm,
150 None,
151}
152
153#[derive(Debug, Clone, Copy, PartialEq, Serialize)]
155pub struct StopTimeoutMs(pub u32);
156
157impl<'de> de::Deserialize<'de> for StopTimeoutMs {
158 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
159 where
160 D: de::Deserializer<'de>,
161 {
162 struct Visitor;
163
164 impl<'de> de::Visitor<'de> for Visitor {
165 type Value = StopTimeoutMs;
166
167 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
168 f.write_str("an unsigned 32-bit integer")
169 }
170
171 fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
172 where
173 E: de::Error,
174 {
175 if v < 0 || v > i64::from(u32::max_value()) {
176 return Err(E::invalid_value(
177 de::Unexpected::Signed(v),
178 &"an unsigned 32-bit integer",
179 ));
180 }
181 Ok(StopTimeoutMs(v as u32))
182 }
183
184 fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
185 where
186 E: de::Error,
187 {
188 self.visit_i64(value as i64)
189 }
190 }
191
192 deserializer.deserialize_i64(Visitor)
193 }
194}
195
196#[derive(Debug, PartialEq, Eq, Hash, Clone, Reference)]
198#[reference(expected = "\"parent\", \"self\", or \"#<child-name>\"")]
199pub enum RegistrationRef {
200 Named(Name),
202 Parent,
204 Self_,
206}
207
208#[derive(Deserialize, Debug, PartialEq, ReferenceDoc, Serialize)]
209#[serde(deny_unknown_fields)]
210#[reference_doc(fields_as = "list")]
211pub struct RunnerRegistration {
212 pub runner: Name,
214
215 pub from: RegistrationRef,
221
222 #[serde(skip_serializing_if = "Option::is_none")]
225 pub r#as: Option<Name>,
226}
227
228impl Hydrate for RunnerRegistration {
229 type Output = ContextRunnerRegistration;
230
231 fn hydrate(self, file: &Arc<PathBuf>) -> Result<Self::Output, Error> {
232 let runner = hydrate_simple(self.runner, file);
233
234 let r#as = hydrate_opt_simple(self.r#as, file);
235
236 let from = hydrate_simple(self.from, file);
237
238 Ok(ContextRunnerRegistration { runner, r#as, from })
239 }
240}
241
242#[derive(Debug, PartialEq, Serialize)]
243pub struct ContextRunnerRegistration {
244 pub runner: ContextSpanned<Name>,
245 pub from: ContextSpanned<RegistrationRef>,
246 #[serde(skip_serializing_if = "Option::is_none")]
247 pub r#as: Option<ContextSpanned<Name>>,
248}
249
250impl FromClauseContext for ContextRunnerRegistration {
251 fn from_(&self) -> ContextSpanned<OneOrMany<AnyRef<'_>>> {
252 let origin = self.from.origin.clone();
253 let value = OneOrMany::One(AnyRef::from(&self.from.value));
254
255 ContextSpanned { value, origin }
256 }
257}
258
259impl FromClause for RunnerRegistration {
260 fn from_(&self) -> OneOrMany<AnyRef<'_>> {
261 OneOrMany::One(AnyRef::from(&self.from))
262 }
263}
264
265#[derive(Deserialize, Debug, PartialEq, ReferenceDoc, Serialize)]
266#[serde(deny_unknown_fields)]
267#[reference_doc(fields_as = "list")]
268pub struct ResolverRegistration {
269 pub resolver: Name,
272
273 pub from: RegistrationRef,
279
280 pub scheme: cm_types::UrlScheme,
283}
284
285impl Hydrate for ResolverRegistration {
286 type Output = ContextResolverRegistration;
287
288 fn hydrate(self, file: &Arc<PathBuf>) -> Result<Self::Output, Error> {
289 let resolver = hydrate_simple(self.resolver, file);
290
291 let from = hydrate_simple(self.from, file);
292 let scheme = hydrate_simple(self.scheme, file);
293
294 Ok(ContextResolverRegistration { resolver, from, scheme })
295 }
296}
297
298#[derive(Debug, PartialEq, Serialize)]
299pub struct ContextResolverRegistration {
300 pub resolver: ContextSpanned<Name>,
301 pub from: ContextSpanned<RegistrationRef>,
302 pub scheme: ContextSpanned<cm_types::UrlScheme>,
303}
304
305impl FromClause for ResolverRegistration {
306 fn from_(&self) -> OneOrMany<AnyRef<'_>> {
307 OneOrMany::One(AnyRef::from(&self.from))
308 }
309}
310
311impl FromClauseContext for ContextResolverRegistration {
312 fn from_(&self) -> ContextSpanned<OneOrMany<AnyRef<'_>>> {
313 let origin = self.from.origin.clone();
314 let value = OneOrMany::One(AnyRef::from(&self.from.value));
315
316 ContextSpanned { value, origin }
317 }
318}
319
320#[derive(Deserialize, Debug, Clone, PartialEq, ReferenceDoc, Serialize)]
321#[serde(deny_unknown_fields)]
322#[reference_doc(fields_as = "list")]
323pub struct DebugRegistration {
324 pub protocol: Option<OneOrMany<Name>>,
326
327 pub from: OfferFromRef,
333
334 #[serde(skip_serializing_if = "Option::is_none")]
337 pub r#as: Option<Name>,
338}
339
340impl Hydrate for DebugRegistration {
341 type Output = ContextDebugRegistration;
342
343 fn hydrate(self, file: &Arc<PathBuf>) -> Result<Self::Output, Error> {
344 let origin = file.clone();
345 let protocol = hydrate_opt_simple(self.protocol, file);
346 let from = hydrate_simple(self.from, file);
347 let r#as = hydrate_opt_simple(self.r#as, file);
348
349 Ok(ContextDebugRegistration { origin, protocol, from, r#as })
350 }
351}
352
353#[derive(Debug, Clone, PartialEq, Serialize)]
354pub struct ContextDebugRegistration {
355 #[serde(skip)]
356 pub origin: Arc<PathBuf>,
357 #[serde(skip_serializing_if = "Option::is_none")]
358 pub protocol: Option<ContextSpanned<OneOrMany<Name>>>,
359 pub from: ContextSpanned<OfferFromRef>,
360 #[serde(skip_serializing_if = "Option::is_none")]
361 pub r#as: Option<ContextSpanned<Name>>,
362}
363
364impl FromClauseContext for ContextDebugRegistration {
365 fn from_(&self) -> ContextSpanned<OneOrMany<AnyRef<'_>>> {
366 let origin = self.from.origin.clone();
367 let value = OneOrMany::One(AnyRef::from(&self.from.value));
368
369 ContextSpanned { value, origin }
370 }
371}
372
373impl AsClauseContext for ContextDebugRegistration {
374 fn r#as(&self) -> Option<ContextSpanned<&BorrowedName>> {
375 self.r#as.as_ref().map(|spanned_name| ContextSpanned {
376 value: spanned_name.value.as_ref(),
377 origin: spanned_name.origin.clone(),
378 })
379 }
380}
381
382impl ContextPathClause for ContextDebugRegistration {
383 fn path(&self) -> Option<&ContextSpanned<Path>> {
384 None
385 }
386}
387
388impl ContextCapabilityClause for ContextDebugRegistration {
389 fn service(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
390 None
391 }
392 fn protocol(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
393 option_one_or_many_as_ref_context(&self.protocol)
394 }
395 fn directory(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
396 None
397 }
398 fn storage(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
399 None
400 }
401 fn runner(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
402 None
403 }
404 fn resolver(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
405 None
406 }
407 fn event_stream(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
408 None
409 }
410 fn dictionary(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
411 None
412 }
413 fn config(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
414 None
415 }
416
417 fn decl_type(&self) -> &'static str {
418 "debug"
419 }
420 fn supported(&self) -> &[&'static str] {
421 &["service", "protocol"]
422 }
423 fn are_many_names_allowed(&self) -> bool {
424 ["protocol"].contains(&self.capability_type(None).unwrap())
425 }
426
427 fn set_service(&mut self, _o: Option<ContextSpanned<OneOrMany<Name>>>) {}
428 fn set_protocol(&mut self, o: Option<ContextSpanned<OneOrMany<Name>>>) {
429 self.protocol = o;
430 }
431 fn set_directory(&mut self, _o: Option<ContextSpanned<OneOrMany<Name>>>) {}
432 fn set_storage(&mut self, _o: Option<ContextSpanned<OneOrMany<Name>>>) {}
433 fn set_runner(&mut self, _o: Option<ContextSpanned<OneOrMany<Name>>>) {}
434 fn set_resolver(&mut self, _o: Option<ContextSpanned<OneOrMany<Name>>>) {}
435 fn set_event_stream(&mut self, _o: Option<ContextSpanned<OneOrMany<Name>>>) {}
436 fn set_dictionary(&mut self, _o: Option<ContextSpanned<OneOrMany<Name>>>) {}
437 fn set_config(&mut self, _o: Option<ContextSpanned<OneOrMany<Name>>>) {}
438
439 fn origin(&self) -> &Arc<PathBuf> {
440 &self.origin
441 }
442
443 fn availability(&self) -> Option<ContextSpanned<Availability>> {
444 None
445 }
446 fn set_availability(&mut self, _a: Option<ContextSpanned<Availability>>) {}
447}
448
449impl AsClause for DebugRegistration {
450 fn r#as(&self) -> Option<&BorrowedName> {
451 self.r#as.as_ref().map(Name::as_ref)
452 }
453}
454
455impl PathClause for DebugRegistration {
456 fn path(&self) -> Option<&Path> {
457 None
458 }
459}
460
461impl FromClause for DebugRegistration {
462 fn from_(&self) -> OneOrMany<AnyRef<'_>> {
463 OneOrMany::One(AnyRef::from(&self.from))
464 }
465}
466
467impl CapabilityClause for DebugRegistration {
468 fn service(&self) -> Option<OneOrMany<&BorrowedName>> {
469 None
470 }
471 fn protocol(&self) -> Option<OneOrMany<&BorrowedName>> {
472 option_one_or_many_as_ref(&self.protocol)
473 }
474 fn directory(&self) -> Option<OneOrMany<&BorrowedName>> {
475 None
476 }
477 fn storage(&self) -> Option<OneOrMany<&BorrowedName>> {
478 None
479 }
480 fn runner(&self) -> Option<OneOrMany<&BorrowedName>> {
481 None
482 }
483 fn resolver(&self) -> Option<OneOrMany<&BorrowedName>> {
484 None
485 }
486 fn event_stream(&self) -> Option<OneOrMany<&BorrowedName>> {
487 None
488 }
489 fn dictionary(&self) -> Option<OneOrMany<&BorrowedName>> {
490 None
491 }
492 fn config(&self) -> Option<OneOrMany<&BorrowedName>> {
493 None
494 }
495
496 fn set_service(&mut self, _o: Option<OneOrMany<Name>>) {}
497 fn set_protocol(&mut self, o: Option<OneOrMany<Name>>) {
498 self.protocol = o;
499 }
500 fn set_directory(&mut self, _o: Option<OneOrMany<Name>>) {}
501 fn set_storage(&mut self, _o: Option<OneOrMany<Name>>) {}
502 fn set_runner(&mut self, _o: Option<OneOrMany<Name>>) {}
503 fn set_resolver(&mut self, _o: Option<OneOrMany<Name>>) {}
504 fn set_event_stream(&mut self, _o: Option<OneOrMany<Name>>) {}
505 fn set_dictionary(&mut self, _o: Option<OneOrMany<Name>>) {}
506 fn set_config(&mut self, _o: Option<OneOrMany<Name>>) {}
507
508 fn availability(&self) -> Option<Availability> {
509 None
510 }
511 fn set_availability(&mut self, _a: Option<Availability>) {}
512
513 fn decl_type(&self) -> &'static str {
514 "debug"
515 }
516 fn supported(&self) -> &[&'static str] {
517 &["service", "protocol"]
518 }
519 fn are_many_names_allowed(&self) -> bool {
520 ["protocol"].contains(&self.capability_type().unwrap())
521 }
522}
523
524impl Hydrate for Environment {
525 type Output = ContextEnvironment;
526
527 fn hydrate(self, file: &Arc<PathBuf>) -> Result<Self::Output, Error> {
528 let name = hydrate_simple(self.name, file);
529
530 let extends = hydrate_opt_simple(self.extends, file);
531 let stop_timeout_ms = hydrate_opt_simple(self.stop_timeout_ms, file);
532
533 let runners = hydrate_list(self.runners, file)?;
534 let resolvers = hydrate_list(self.resolvers, file)?;
535 let debug = hydrate_list(self.debug, file)?;
536
537 Ok(ContextEnvironment { name, extends, runners, resolvers, debug, stop_timeout_ms })
538 }
539}
540
541#[derive(Debug, PartialEq, Serialize)]
542pub struct ContextEnvironment {
543 pub name: ContextSpanned<Name>,
544 #[serde(skip_serializing_if = "Option::is_none")]
545 pub extends: Option<ContextSpanned<EnvironmentExtends>>,
546 #[serde(skip_serializing_if = "Option::is_none")]
547 pub runners: Option<Vec<ContextSpanned<ContextRunnerRegistration>>>,
548 #[serde(skip_serializing_if = "Option::is_none")]
549 pub resolvers: Option<Vec<ContextSpanned<ContextResolverRegistration>>>,
550 #[serde(skip_serializing_if = "Option::is_none")]
551 pub debug: Option<Vec<ContextSpanned<ContextDebugRegistration>>>,
552 #[serde(skip_serializing_if = "Option::is_none")]
553 #[serde(rename = "__stop_timeout_ms")]
554 pub stop_timeout_ms: Option<ContextSpanned<StopTimeoutMs>>,
555}
556
557impl ContextEnvironment {
558 pub fn merge_from(&mut self, mut other: Self) -> Result<(), Error> {
559 if let Some(other_extends) = other.extends.take() {
560 if let Some(my_extends) = &self.extends {
561 if my_extends.value != other_extends.value {
562 return Err(Error::merge(
563 format!(
564 "Conflicting 'extends' field in environment '{}': found '{:?}' and '{:?}'",
565 self.name.value, my_extends.value, other_extends.value
566 ),
567 Some(other_extends.origin),
568 ));
569 }
570 } else {
571 self.extends = Some(other_extends);
572 }
573 }
574
575 if let Some(other_timeout) = other.stop_timeout_ms.take() {
576 if let Some(my_timeout) = &self.stop_timeout_ms {
577 if my_timeout.value != other_timeout.value {
578 return Err(Error::merge(
579 format!(
580 "Conflicting 'stop_timeout_ms' in environment '{}'",
581 self.name.value
582 ),
583 Some(other_timeout.origin),
584 ));
585 }
586 } else {
587 self.stop_timeout_ms = Some(other_timeout);
588 }
589 }
590
591 merge_spanned_vec!(self, other, runners);
592 merge_spanned_vec!(self, other, resolvers);
593 merge_spanned_vec!(self, other, debug);
594
595 Ok(())
596 }
597}