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