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