1pub mod error;
12pub mod features;
13pub mod load;
14pub mod one_or_many;
15pub mod types;
16pub(crate) mod validate;
17
18#[allow(unused)] pub mod translate;
20
21use crate::error::Error;
22use cml_macro::{OneOrMany, Reference};
23use json5format::{FormatOptions, PathOption};
24use maplit::{hashmap, hashset};
25use serde::{Deserialize, Serialize, de, ser};
26use std::fmt;
27use std::hash::Hash;
28use std::num::NonZeroU32;
29use std::path::PathBuf;
30use std::str::FromStr;
31use std::sync::Arc;
32
33pub use crate::types::capability::{Capability, CapabilityFromRef, ContextCapability};
34pub use crate::types::capability_id::CapabilityId;
35pub use crate::types::child::Child;
36pub use crate::types::collection::Collection;
37use crate::types::common::{ContextCapabilityClause, ContextPathClause, ContextSpanned};
38pub use crate::types::document::{Document, DocumentContext, parse_and_hydrate};
39pub use crate::types::environment::{Environment, ResolverRegistration};
40pub use crate::types::expose::{ContextExpose, Expose};
41pub use crate::types::offer::{Offer, OfferFromRef, OfferToAllCapability, OfferToRef};
42pub use crate::types::program::Program;
43pub use crate::types::r#use::{Use, UseFromRef};
44
45pub use cm_types::{
46 AllowedOffers, Availability, BorrowedName, BoundedName, DeliveryType, DependencyType,
47 Durability, HandleType, Name, NamespacePath, OnTerminate, ParseError, Path, RelativePath,
48 StartupMode, StorageId, Url,
49};
50use error::Location;
51
52pub use crate::one_or_many::OneOrMany;
53pub use crate::translate::{CompileOptions, compile};
54pub use crate::validate::{CapabilityRequirements, MustUseRequirement};
55
56pub fn parse_one_document(buffer: &String, file: &std::path::Path) -> Result<Document, Error> {
58 serde_json5::from_str(&buffer).map_err(|e| {
59 let serde_json5::Error::Message { location, msg } = e;
60 let location = location.map(|l| Location { line: l.line, column: l.column });
61 Error::parse(msg, location, Some(file))
62 })
63}
64
65pub fn load_cml_with_context(
66 buffer: &String,
67 file: &std::path::Path,
68) -> Result<DocumentContext, Error> {
69 let file_arc = Arc::new(file.to_path_buf());
70 parse_and_hydrate(file_arc, buffer)
71}
72
73#[derive(OneOrMany, Debug, Clone)]
75#[one_or_many(
76 expected = "a name or nonempty array of names, with unique elements",
77 inner_type = "Name",
78 min_length = 1,
79 unique_items = true
80)]
81pub struct OneOrManyNames;
82
83#[derive(OneOrMany, Debug, Clone)]
85#[one_or_many(
86 expected = "a path or nonempty array of paths, with unique elements",
87 inner_type = "Path",
88 min_length = 1,
89 unique_items = true
90)]
91pub struct OneOrManyPaths;
92
93#[derive(OneOrMany, Debug, Clone)]
95#[one_or_many(
96 expected = "one or an array of \"#<collection-name>\", or \"#<child-name>\"",
97 inner_type = "EventScope",
98 min_length = 1,
99 unique_items = true
100)]
101pub struct OneOrManyEventScope;
102
103#[derive(Debug, Deserialize, PartialEq, Eq, Hash, Clone, Serialize)]
105#[serde(rename_all = "snake_case")]
106pub enum SourceAvailability {
107 Required,
108 Unknown,
109}
110
111impl Default for SourceAvailability {
112 fn default() -> Self {
113 Self::Required
114 }
115}
116
117impl<T> CanonicalizeContext for Vec<T>
118where
119 T: CanonicalizeContext + ContextCapabilityClause + ContextPathClause + Clone + PartialEq,
120{
121 fn canonicalize_context(&mut self) {
122 let mut to_merge: Vec<(T, Vec<ContextSpanned<Name>>)> = Vec::new();
123 let mut to_keep: Vec<T> = vec![];
124
125 self.iter().for_each(|c| {
126 if !c.are_many_names_allowed() || c.path().is_some() {
128 to_keep.push(c.clone());
129 return;
130 }
131
132 let mut names = c.names();
133 let mut copy: T = c.clone();
134
135 let synthetic_name = Name::from_str("a").unwrap();
136 let spanned = ContextSpanned { value: synthetic_name, origin: c.origin().clone() };
137 copy.set_names(vec![spanned]);
138
139 let r = to_merge.iter().position(|(t, _)| t == ©);
140 match r {
141 Some(i) => to_merge[i].1.append(&mut names),
142 None => to_merge.push((copy, names)),
143 };
144 });
145
146 let mut merged = to_merge
147 .into_iter()
148 .map(|(mut t, mut names)| {
149 names.sort_by(|a, b| a.value.cmp(&b.value));
150
151 t.set_names(names);
152 t
153 })
154 .collect::<Vec<_>>();
155
156 to_keep.append(&mut merged);
157 *self = to_keep;
158
159 self.iter_mut().for_each(|c| c.canonicalize_context());
160
161 self.sort_by(|a, b| {
162 let a_type = a.capability_type(None).unwrap();
164 let b_type = b.capability_type(None).unwrap();
165
166 a_type.cmp(b_type).then_with(|| {
167 let a_names = a.names();
168 let b_names = b.names();
169
170 let a_first_val = &a_names.first().unwrap().value;
172 let b_first_val = &b_names.first().unwrap().value;
173
174 a_first_val.cmp(b_first_val)
175 })
176 });
177 }
178}
179
180#[derive(Debug, PartialEq, Eq, Hash, Clone)]
188pub enum AnyRef<'a> {
189 Named(&'a BorrowedName),
191 Parent,
193 Framework,
195 Debug,
197 Self_,
199 Void,
201 Dictionary(&'a DictionaryRef),
203 OwnDictionary(&'a BorrowedName),
206}
207
208impl fmt::Display for AnyRef<'_> {
210 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
211 match self {
212 Self::Named(name) => write!(f, "#{}", name),
213 Self::Parent => write!(f, "parent"),
214 Self::Framework => write!(f, "framework"),
215 Self::Debug => write!(f, "debug"),
216 Self::Self_ => write!(f, "self"),
217 Self::Void => write!(f, "void"),
218 Self::Dictionary(d) => write!(f, "{}", d),
219 Self::OwnDictionary(name) => write!(f, "self/{}", name),
220 }
221 }
222}
223
224#[derive(Debug, PartialEq, Eq, Hash, Clone)]
226pub struct DictionaryRef {
227 pub path: RelativePath,
229 pub root: RootDictionaryRef,
230}
231
232impl<'a> From<&'a DictionaryRef> for AnyRef<'a> {
233 fn from(r: &'a DictionaryRef) -> Self {
234 Self::Dictionary(r)
235 }
236}
237
238impl<'a> From<&'a Name> for AnyRef<'a> {
239 fn from(name: &'a Name) -> Self {
240 AnyRef::Named(name.as_ref())
241 }
242}
243
244impl<'a> From<&'a BorrowedName> for AnyRef<'a> {
245 fn from(name: &'a BorrowedName) -> Self {
246 AnyRef::Named(name)
247 }
248}
249
250impl FromStr for DictionaryRef {
251 type Err = ParseError;
252
253 fn from_str(path: &str) -> Result<Self, ParseError> {
254 match path.find('/') {
255 Some(n) => {
256 let root = path[..n].parse().map_err(|_| ParseError::InvalidValue)?;
257 let path = RelativePath::new(&path[n + 1..])?;
258 Ok(Self { root, path })
259 }
260 None => Err(ParseError::InvalidValue),
261 }
262 }
263}
264
265impl fmt::Display for DictionaryRef {
266 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
267 write!(f, "{}/{}", self.root, self.path)
268 }
269}
270
271impl ser::Serialize for DictionaryRef {
272 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
273 where
274 S: serde::ser::Serializer,
275 {
276 format!("{}", self).serialize(serializer)
277 }
278}
279
280const DICTIONARY_REF_EXPECT_STR: &str = "a path to a dictionary no more \
281 than 4095 characters in length";
282
283impl<'de> de::Deserialize<'de> for DictionaryRef {
284 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
285 where
286 D: de::Deserializer<'de>,
287 {
288 struct Visitor;
289
290 impl<'de> de::Visitor<'de> for Visitor {
291 type Value = DictionaryRef;
292
293 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
294 f.write_str(DICTIONARY_REF_EXPECT_STR)
295 }
296
297 fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
298 where
299 E: de::Error,
300 {
301 s.parse().map_err(|err| match err {
302 ParseError::InvalidValue => {
303 E::invalid_value(de::Unexpected::Str(s), &DICTIONARY_REF_EXPECT_STR)
304 }
305 ParseError::TooLong | ParseError::Empty => {
306 E::invalid_length(s.len(), &DICTIONARY_REF_EXPECT_STR)
307 }
308 e => {
309 panic!("unexpected parse error: {:?}", e);
310 }
311 })
312 }
313 }
314
315 deserializer.deserialize_string(Visitor)
316 }
317}
318
319#[derive(Debug, PartialEq, Eq, Hash, Clone, Reference)]
321#[reference(expected = "\"parent\", \"self\", \"#<child-name>\"")]
322pub enum RootDictionaryRef {
323 Named(Name),
325 Parent,
327 Self_,
329}
330
331#[derive(Debug, PartialEq, Eq, Hash, Clone, Reference, Ord, PartialOrd)]
333#[reference(expected = "\"#<collection-name>\", \"#<child-name>\", or none")]
334pub enum EventScope {
335 Named(Name),
337}
338
339#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
340#[serde(rename_all = "snake_case")]
341pub enum ConfigType {
342 Bool,
343 Uint8,
344 Uint16,
345 Uint32,
346 Uint64,
347 Int8,
348 Int16,
349 Int32,
350 Int64,
351 String,
352 Vector,
353}
354
355impl From<&cm_rust::ConfigValueType> for ConfigType {
356 fn from(value: &cm_rust::ConfigValueType) -> Self {
357 match value {
358 cm_rust::ConfigValueType::Bool => ConfigType::Bool,
359 cm_rust::ConfigValueType::Uint8 => ConfigType::Uint8,
360 cm_rust::ConfigValueType::Int8 => ConfigType::Int8,
361 cm_rust::ConfigValueType::Uint16 => ConfigType::Uint16,
362 cm_rust::ConfigValueType::Int16 => ConfigType::Int16,
363 cm_rust::ConfigValueType::Uint32 => ConfigType::Uint32,
364 cm_rust::ConfigValueType::Int32 => ConfigType::Int32,
365 cm_rust::ConfigValueType::Uint64 => ConfigType::Uint64,
366 cm_rust::ConfigValueType::Int64 => ConfigType::Int64,
367 cm_rust::ConfigValueType::String { .. } => ConfigType::String,
368 cm_rust::ConfigValueType::Vector { .. } => ConfigType::Vector,
369 }
370 }
371}
372
373#[derive(Clone, Deserialize, Debug, PartialEq, Serialize)]
374#[serde(tag = "type", deny_unknown_fields, rename_all = "lowercase")]
375pub enum ConfigNestedValueType {
376 Bool {},
377 Uint8 {},
378 Uint16 {},
379 Uint32 {},
380 Uint64 {},
381 Int8 {},
382 Int16 {},
383 Int32 {},
384 Int64 {},
385 String { max_size: NonZeroU32 },
386}
387
388impl ConfigNestedValueType {
389 pub fn update_digest(&self, hasher: &mut impl sha2::Digest) {
391 let val = match self {
392 ConfigNestedValueType::Bool {} => 0u8,
393 ConfigNestedValueType::Uint8 {} => 1u8,
394 ConfigNestedValueType::Uint16 {} => 2u8,
395 ConfigNestedValueType::Uint32 {} => 3u8,
396 ConfigNestedValueType::Uint64 {} => 4u8,
397 ConfigNestedValueType::Int8 {} => 5u8,
398 ConfigNestedValueType::Int16 {} => 6u8,
399 ConfigNestedValueType::Int32 {} => 7u8,
400 ConfigNestedValueType::Int64 {} => 8u8,
401 ConfigNestedValueType::String { max_size } => {
402 hasher.update(max_size.get().to_le_bytes());
403 9u8
404 }
405 };
406 hasher.update([val])
407 }
408}
409
410impl From<ConfigNestedValueType> for cm_rust::ConfigNestedValueType {
411 fn from(value: ConfigNestedValueType) -> Self {
412 match value {
413 ConfigNestedValueType::Bool {} => cm_rust::ConfigNestedValueType::Bool,
414 ConfigNestedValueType::Uint8 {} => cm_rust::ConfigNestedValueType::Uint8,
415 ConfigNestedValueType::Uint16 {} => cm_rust::ConfigNestedValueType::Uint16,
416 ConfigNestedValueType::Uint32 {} => cm_rust::ConfigNestedValueType::Uint32,
417 ConfigNestedValueType::Uint64 {} => cm_rust::ConfigNestedValueType::Uint64,
418 ConfigNestedValueType::Int8 {} => cm_rust::ConfigNestedValueType::Int8,
419 ConfigNestedValueType::Int16 {} => cm_rust::ConfigNestedValueType::Int16,
420 ConfigNestedValueType::Int32 {} => cm_rust::ConfigNestedValueType::Int32,
421 ConfigNestedValueType::Int64 {} => cm_rust::ConfigNestedValueType::Int64,
422 ConfigNestedValueType::String { max_size } => {
423 cm_rust::ConfigNestedValueType::String { max_size: max_size.into() }
424 }
425 }
426 }
427}
428
429impl TryFrom<&cm_rust::ConfigNestedValueType> for ConfigNestedValueType {
430 type Error = ();
431 fn try_from(nested: &cm_rust::ConfigNestedValueType) -> Result<Self, ()> {
432 Ok(match nested {
433 cm_rust::ConfigNestedValueType::Bool => ConfigNestedValueType::Bool {},
434 cm_rust::ConfigNestedValueType::Uint8 => ConfigNestedValueType::Uint8 {},
435 cm_rust::ConfigNestedValueType::Int8 => ConfigNestedValueType::Int8 {},
436 cm_rust::ConfigNestedValueType::Uint16 => ConfigNestedValueType::Uint16 {},
437 cm_rust::ConfigNestedValueType::Int16 => ConfigNestedValueType::Int16 {},
438 cm_rust::ConfigNestedValueType::Uint32 => ConfigNestedValueType::Uint32 {},
439 cm_rust::ConfigNestedValueType::Int32 => ConfigNestedValueType::Int32 {},
440 cm_rust::ConfigNestedValueType::Uint64 => ConfigNestedValueType::Uint64 {},
441 cm_rust::ConfigNestedValueType::Int64 => ConfigNestedValueType::Int64 {},
442 cm_rust::ConfigNestedValueType::String { max_size } => {
443 ConfigNestedValueType::String { max_size: NonZeroU32::new(*max_size).ok_or(())? }
444 }
445 })
446 }
447}
448
449#[derive(Clone, Hash, Debug, PartialEq, PartialOrd, Eq, Ord, Serialize)]
450pub struct ConfigKey(String);
451
452impl ConfigKey {
453 pub fn as_str(&self) -> &str {
454 self.0.as_str()
455 }
456}
457
458impl std::fmt::Display for ConfigKey {
459 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
460 write!(f, "{}", self.0)
461 }
462}
463
464impl FromStr for ConfigKey {
465 type Err = ParseError;
466
467 fn from_str(s: &str) -> Result<Self, ParseError> {
468 let length = s.len();
469 if length == 0 {
470 return Err(ParseError::Empty);
471 }
472 if length > 64 {
473 return Err(ParseError::TooLong);
474 }
475
476 let first_is_letter = s.chars().next().expect("non-empty string").is_ascii_lowercase();
478 let contains_invalid_chars =
480 s.chars().any(|c| !(c.is_ascii_lowercase() || c.is_ascii_digit() || c == '_'));
481 let last_is_underscore = s.chars().next_back().expect("non-empty string") == '_';
483
484 if !first_is_letter || contains_invalid_chars || last_is_underscore {
485 return Err(ParseError::InvalidValue);
486 }
487
488 Ok(Self(s.to_string()))
489 }
490}
491
492impl<'de> de::Deserialize<'de> for ConfigKey {
493 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
494 where
495 D: de::Deserializer<'de>,
496 {
497 struct Visitor;
498
499 impl<'de> de::Visitor<'de> for Visitor {
500 type Value = ConfigKey;
501
502 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
503 f.write_str(
504 "a non-empty string no more than 64 characters in length, which must \
505 start with a letter, can contain letters, numbers, and underscores, \
506 but cannot end with an underscore",
507 )
508 }
509
510 fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
511 where
512 E: de::Error,
513 {
514 s.parse().map_err(|err| match err {
515 ParseError::InvalidValue => E::invalid_value(
516 de::Unexpected::Str(s),
517 &"a name which must start with a letter, can contain letters, \
518 numbers, and underscores, but cannot end with an underscore",
519 ),
520 ParseError::TooLong | ParseError::Empty => E::invalid_length(
521 s.len(),
522 &"a non-empty name no more than 64 characters in length",
523 ),
524 e => {
525 panic!("unexpected parse error: {:?}", e);
526 }
527 })
528 }
529 }
530 deserializer.deserialize_string(Visitor)
531 }
532}
533
534#[derive(Clone, Deserialize, Debug, PartialEq, Serialize)]
535#[serde(deny_unknown_fields, rename_all = "lowercase")]
536pub enum ConfigRuntimeSource {
537 Parent,
538}
539
540#[derive(Clone, Deserialize, Debug, PartialEq, Serialize)]
541#[serde(tag = "type", deny_unknown_fields, rename_all = "lowercase")]
542pub enum ConfigValueType {
543 Bool {
544 #[serde(skip_serializing_if = "Option::is_none")]
545 mutability: Option<Vec<ConfigRuntimeSource>>,
546 },
547 Uint8 {
548 #[serde(skip_serializing_if = "Option::is_none")]
549 mutability: Option<Vec<ConfigRuntimeSource>>,
550 },
551 Uint16 {
552 #[serde(skip_serializing_if = "Option::is_none")]
553 mutability: Option<Vec<ConfigRuntimeSource>>,
554 },
555 Uint32 {
556 #[serde(skip_serializing_if = "Option::is_none")]
557 mutability: Option<Vec<ConfigRuntimeSource>>,
558 },
559 Uint64 {
560 #[serde(skip_serializing_if = "Option::is_none")]
561 mutability: Option<Vec<ConfigRuntimeSource>>,
562 },
563 Int8 {
564 #[serde(skip_serializing_if = "Option::is_none")]
565 mutability: Option<Vec<ConfigRuntimeSource>>,
566 },
567 Int16 {
568 #[serde(skip_serializing_if = "Option::is_none")]
569 mutability: Option<Vec<ConfigRuntimeSource>>,
570 },
571 Int32 {
572 #[serde(skip_serializing_if = "Option::is_none")]
573 mutability: Option<Vec<ConfigRuntimeSource>>,
574 },
575 Int64 {
576 #[serde(skip_serializing_if = "Option::is_none")]
577 mutability: Option<Vec<ConfigRuntimeSource>>,
578 },
579 String {
580 max_size: NonZeroU32,
581 #[serde(skip_serializing_if = "Option::is_none")]
582 mutability: Option<Vec<ConfigRuntimeSource>>,
583 },
584 Vector {
585 max_count: NonZeroU32,
586 element: ConfigNestedValueType,
587 #[serde(skip_serializing_if = "Option::is_none")]
588 mutability: Option<Vec<ConfigRuntimeSource>>,
589 },
590}
591
592impl ConfigValueType {
593 pub fn update_digest(&self, hasher: &mut impl sha2::Digest) {
595 let val = match self {
596 ConfigValueType::Bool { .. } => 0u8,
597 ConfigValueType::Uint8 { .. } => 1u8,
598 ConfigValueType::Uint16 { .. } => 2u8,
599 ConfigValueType::Uint32 { .. } => 3u8,
600 ConfigValueType::Uint64 { .. } => 4u8,
601 ConfigValueType::Int8 { .. } => 5u8,
602 ConfigValueType::Int16 { .. } => 6u8,
603 ConfigValueType::Int32 { .. } => 7u8,
604 ConfigValueType::Int64 { .. } => 8u8,
605 ConfigValueType::String { max_size, .. } => {
606 hasher.update(max_size.get().to_le_bytes());
607 9u8
608 }
609 ConfigValueType::Vector { max_count, element, .. } => {
610 hasher.update(max_count.get().to_le_bytes());
611 element.update_digest(hasher);
612 10u8
613 }
614 };
615 hasher.update([val])
616 }
617}
618
619impl From<ConfigValueType> for cm_rust::ConfigValueType {
620 fn from(value: ConfigValueType) -> Self {
621 match value {
622 ConfigValueType::Bool { .. } => cm_rust::ConfigValueType::Bool,
623 ConfigValueType::Uint8 { .. } => cm_rust::ConfigValueType::Uint8,
624 ConfigValueType::Uint16 { .. } => cm_rust::ConfigValueType::Uint16,
625 ConfigValueType::Uint32 { .. } => cm_rust::ConfigValueType::Uint32,
626 ConfigValueType::Uint64 { .. } => cm_rust::ConfigValueType::Uint64,
627 ConfigValueType::Int8 { .. } => cm_rust::ConfigValueType::Int8,
628 ConfigValueType::Int16 { .. } => cm_rust::ConfigValueType::Int16,
629 ConfigValueType::Int32 { .. } => cm_rust::ConfigValueType::Int32,
630 ConfigValueType::Int64 { .. } => cm_rust::ConfigValueType::Int64,
631 ConfigValueType::String { max_size, .. } => {
632 cm_rust::ConfigValueType::String { max_size: max_size.into() }
633 }
634 ConfigValueType::Vector { max_count, element, .. } => {
635 cm_rust::ConfigValueType::Vector {
636 max_count: max_count.into(),
637 nested_type: element.into(),
638 }
639 }
640 }
641 }
642}
643
644pub trait FromClause {
645 fn from_(&self) -> OneOrMany<AnyRef<'_>>;
646}
647
648pub trait FromClauseContext {
649 fn from_(&self) -> ContextSpanned<OneOrMany<AnyRef<'_>>>;
650}
651
652pub trait CapabilityClause: Clone + PartialEq + std::fmt::Debug {
653 fn service(&self) -> Option<OneOrMany<&BorrowedName>>;
654 fn protocol(&self) -> Option<OneOrMany<&BorrowedName>>;
655 fn directory(&self) -> Option<OneOrMany<&BorrowedName>>;
656 fn storage(&self) -> Option<OneOrMany<&BorrowedName>>;
657 fn runner(&self) -> Option<OneOrMany<&BorrowedName>>;
658 fn resolver(&self) -> Option<OneOrMany<&BorrowedName>>;
659 fn event_stream(&self) -> Option<OneOrMany<&BorrowedName>>;
660 fn dictionary(&self) -> Option<OneOrMany<&BorrowedName>>;
661 fn config(&self) -> Option<OneOrMany<&BorrowedName>>;
662 fn set_service(&mut self, o: Option<OneOrMany<Name>>);
663 fn set_protocol(&mut self, o: Option<OneOrMany<Name>>);
664 fn set_directory(&mut self, o: Option<OneOrMany<Name>>);
665 fn set_storage(&mut self, o: Option<OneOrMany<Name>>);
666 fn set_runner(&mut self, o: Option<OneOrMany<Name>>);
667 fn set_resolver(&mut self, o: Option<OneOrMany<Name>>);
668 fn set_event_stream(&mut self, o: Option<OneOrMany<Name>>);
669 fn set_dictionary(&mut self, o: Option<OneOrMany<Name>>);
670 fn set_config(&mut self, o: Option<OneOrMany<Name>>);
671
672 fn availability(&self) -> Option<Availability>;
673 fn set_availability(&mut self, a: Option<Availability>);
674
675 fn capability_type(&self) -> Result<&'static str, Error> {
680 let mut types = Vec::new();
681 if self.service().is_some() {
682 types.push("service");
683 }
684 if self.protocol().is_some() {
685 types.push("protocol");
686 }
687 if self.directory().is_some() {
688 types.push("directory");
689 }
690 if self.storage().is_some() {
691 types.push("storage");
692 }
693 if self.event_stream().is_some() {
694 types.push("event_stream");
695 }
696 if self.runner().is_some() {
697 types.push("runner");
698 }
699 if self.config().is_some() {
700 types.push("config");
701 }
702 if self.resolver().is_some() {
703 types.push("resolver");
704 }
705 if self.dictionary().is_some() {
706 types.push("dictionary");
707 }
708 match types.len() {
709 0 => {
710 let supported_keywords = self
711 .supported()
712 .iter()
713 .map(|k| format!("\"{}\"", k))
714 .collect::<Vec<_>>()
715 .join(", ");
716 Err(Error::validate(format!(
717 "`{}` declaration is missing a capability keyword, one of: {}",
718 self.decl_type(),
719 supported_keywords,
720 )))
721 }
722 1 => Ok(types[0]),
723 _ => Err(Error::validate(format!(
724 "{} declaration has multiple capability types defined: {:?}",
725 self.decl_type(),
726 types
727 ))),
728 }
729 }
730
731 fn are_many_names_allowed(&self) -> bool;
733
734 fn decl_type(&self) -> &'static str;
735 fn supported(&self) -> &[&'static str];
736
737 fn names(&self) -> Vec<&BorrowedName> {
740 let res = vec![
741 self.service(),
742 self.protocol(),
743 self.directory(),
744 self.storage(),
745 self.runner(),
746 self.config(),
747 self.resolver(),
748 self.event_stream(),
749 self.dictionary(),
750 ];
751 res.into_iter()
752 .map(|o| o.map(|o| o.into_iter().collect::<Vec<&BorrowedName>>()).unwrap_or(vec![]))
753 .flatten()
754 .collect()
755 }
756
757 fn set_names(&mut self, names: Vec<Name>) {
758 let names = match names.len() {
759 0 => None,
760 1 => Some(OneOrMany::One(names.first().unwrap().clone())),
761 _ => Some(OneOrMany::Many(names)),
762 };
763
764 let cap_type = self.capability_type().unwrap();
765 if cap_type == "protocol" {
766 self.set_protocol(names);
767 } else if cap_type == "service" {
768 self.set_service(names);
769 } else if cap_type == "directory" {
770 self.set_directory(names);
771 } else if cap_type == "storage" {
772 self.set_storage(names);
773 } else if cap_type == "runner" {
774 self.set_runner(names);
775 } else if cap_type == "resolver" {
776 self.set_resolver(names);
777 } else if cap_type == "event_stream" {
778 self.set_event_stream(names);
779 } else if cap_type == "dictionary" {
780 self.set_dictionary(names);
781 } else if cap_type == "config" {
782 self.set_config(names);
783 } else {
784 panic!("Unknown capability type {}", cap_type);
785 }
786 }
787}
788
789trait CanonicalizeContext {
790 fn canonicalize_context(&mut self);
791}
792
793pub trait AsClauseContext {
794 fn r#as(&self) -> Option<ContextSpanned<&BorrowedName>>;
795}
796
797pub fn alias_or_name_context<'a>(
798 alias: Option<ContextSpanned<&'a BorrowedName>>,
799 name: &'a BorrowedName,
800 origin: Arc<PathBuf>,
801) -> ContextSpanned<&'a BorrowedName> {
802 alias.unwrap_or(ContextSpanned { value: name, origin })
803}
804
805pub fn alias_or_path<'a>(alias: Option<&'a Path>, path: &'a Path) -> &'a Path {
806 alias.unwrap_or(path)
807}
808
809pub fn format_cml(buffer: &str, file: Option<&std::path::Path>) -> Result<Vec<u8>, Error> {
810 let general_order = PathOption::PropertyNameOrder(vec![
811 "name",
812 "url",
813 "startup",
814 "environment",
815 "config",
816 "dictionary",
817 "durability",
818 "service",
819 "protocol",
820 "directory",
821 "storage",
822 "runner",
823 "resolver",
824 "event",
825 "event_stream",
826 "from",
827 "as",
828 "to",
829 "rights",
830 "path",
831 "subdir",
832 "filter",
833 "dependency",
834 "extends",
835 "runners",
836 "resolvers",
837 "debug",
838 ]);
839 let options = FormatOptions {
840 collapse_containers_of_one: true,
841 sort_array_items: true, options_by_path: hashmap! {
843 "/*" => hashset! {
844 PathOption::PropertyNameOrder(vec![
845 "include",
846 "program",
847 "children",
848 "collections",
849 "capabilities",
850 "use",
851 "offer",
852 "expose",
853 "environments",
854 "facets",
855 ])
856 },
857 "/*/program" => hashset! {
858 PathOption::CollapseContainersOfOne(false),
859 PathOption::PropertyNameOrder(vec![
860 "runner",
861 "binary",
862 "args",
863 ]),
864 },
865 "/*/program/*" => hashset! {
866 PathOption::SortArrayItems(false),
867 },
868 "/*/*/*" => hashset! {
869 general_order.clone()
870 },
871 "/*/*/*/*/*" => hashset! {
872 general_order
873 },
874 },
875 ..Default::default()
876 };
877
878 json5format::format(buffer, file.map(|f| f.to_string_lossy().to_string()), Some(options))
879 .map_err(|e| Error::json5(e, file))
880}
881
882#[cfg(test)]
883mod tests {
884 use super::*;
885 use crate::types::document;
886 use crate::types::environment::RunnerRegistration;
887 use assert_matches::assert_matches;
888 use serde_json::Value;
889 use std::path::Path;
890
891 #[test]
895 fn test_parse_named_reference() {
896 assert_matches!("#some-child".parse::<OfferFromRef>(), Ok(OfferFromRef::Named(name)) if name == "some-child");
897 assert_matches!("#A".parse::<OfferFromRef>(), Ok(OfferFromRef::Named(name)) if name == "A");
898 assert_matches!("#7".parse::<OfferFromRef>(), Ok(OfferFromRef::Named(name)) if name == "7");
899 assert_matches!("#_".parse::<OfferFromRef>(), Ok(OfferFromRef::Named(name)) if name == "_");
900
901 assert_matches!("#-".parse::<OfferFromRef>(), Err(_));
902 assert_matches!("#.".parse::<OfferFromRef>(), Err(_));
903 assert_matches!("#".parse::<OfferFromRef>(), Err(_));
904 assert_matches!("some-child".parse::<OfferFromRef>(), Err(_));
905 }
906
907 #[test]
908 fn test_parse_reference_test() {
909 assert_matches!("parent".parse::<OfferFromRef>(), Ok(OfferFromRef::Parent));
910 assert_matches!("framework".parse::<OfferFromRef>(), Ok(OfferFromRef::Framework));
911 assert_matches!("self".parse::<OfferFromRef>(), Ok(OfferFromRef::Self_));
912 assert_matches!("#child".parse::<OfferFromRef>(), Ok(OfferFromRef::Named(name)) if name == "child");
913
914 assert_matches!("invalid".parse::<OfferFromRef>(), Err(_));
915 assert_matches!("#invalid-child^".parse::<OfferFromRef>(), Err(_));
916 }
917
918 fn json_value_from_str(json: &str, filename: &Path) -> Result<Value, Error> {
919 serde_json::from_str(json).map_err(|e| {
920 Error::parse(
921 format!("Couldn't read input as JSON: {}", e),
922 Some(Location { line: e.line(), column: e.column() }),
923 Some(filename),
924 )
925 })
926 }
927
928 fn parse_as_ref(input: &str) -> Result<OfferFromRef, Error> {
929 serde_json::from_value::<OfferFromRef>(json_value_from_str(input, &Path::new("test.cml"))?)
930 .map_err(|e| Error::parse(format!("{}", e), None, None))
931 }
932
933 #[test]
934 fn test_deserialize_ref() -> Result<(), Error> {
935 assert_matches!(parse_as_ref("\"self\""), Ok(OfferFromRef::Self_));
936 assert_matches!(parse_as_ref("\"parent\""), Ok(OfferFromRef::Parent));
937 assert_matches!(parse_as_ref("\"#child\""), Ok(OfferFromRef::Named(name)) if name == "child");
938
939 assert_matches!(parse_as_ref(r#""invalid""#), Err(_));
940
941 Ok(())
942 }
943
944 #[test]
945 fn test_deny_unknown_fields() {
946 assert_matches!(serde_json5::from_str::<Document>("{ unknown: \"\" }"), Err(_));
947 assert_matches!(serde_json5::from_str::<Environment>("{ unknown: \"\" }"), Err(_));
948 assert_matches!(serde_json5::from_str::<RunnerRegistration>("{ unknown: \"\" }"), Err(_));
949 assert_matches!(serde_json5::from_str::<ResolverRegistration>("{ unknown: \"\" }"), Err(_));
950 assert_matches!(serde_json5::from_str::<Use>("{ unknown: \"\" }"), Err(_));
951 assert_matches!(serde_json5::from_str::<Expose>("{ unknown: \"\" }"), Err(_));
952 assert_matches!(serde_json5::from_str::<Offer>("{ unknown: \"\" }"), Err(_));
953 assert_matches!(serde_json5::from_str::<Capability>("{ unknown: \"\" }"), Err(_));
954 assert_matches!(serde_json5::from_str::<Child>("{ unknown: \"\" }"), Err(_));
955 assert_matches!(serde_json5::from_str::<Collection>("{ unknown: \"\" }"), Err(_));
956 }
957
958 #[test]
959 fn test_context_pipeline_denies_unknown_fields() {
960 let dummy_path = std::sync::Arc::new(std::path::PathBuf::from("test.cml"));
961 let bad_json = "{ unknown : \"\" }".to_string();
962
963 let result = document::parse_and_hydrate(dummy_path, &bad_json);
964
965 assert!(
966 result.is_err(),
967 "parse should fail because the underlying Document rejected unknown fields"
968 );
969 }
970}