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