1use bc_components::XID;
41use dcbor::prelude::*;
42
43use super::EnvelopeSummary;
44use crate::{
45 Assertion, Envelope, FormatContextOpt,
46 base::envelope::EnvelopeCase, string_utils::StringUtils,
47};
48#[cfg(feature = "known_value")]
49use crate::{KnownValue, known_values};
50
51#[derive(Clone, Default)]
52pub struct EnvelopeFormatOpts<'a> {
53 flat: bool,
54 context: FormatContextOpt<'a>,
55}
56
57impl<'a> EnvelopeFormatOpts<'a> {
58 pub fn flat(mut self, flat: bool) -> Self {
60 self.flat = flat;
61 self
62 }
63
64 pub fn context(mut self, context: FormatContextOpt<'a>) -> Self {
66 self.context = context;
67 self
68 }
69}
70
71impl Envelope {
73 pub fn format_opt<'a>(&self, opts: &EnvelopeFormatOpts<'a>) -> String {
75 self.format_item(opts)
76 .format(opts)
77 .trim()
78 .to_string()
79 }
80
81 pub fn format(&self) -> String {
85 self.format_opt(&EnvelopeFormatOpts::default())
86 }
87
88 pub fn format_flat(&self) -> String {
92 self.format_opt(&EnvelopeFormatOpts::default().flat(true))
93 }
94}
95
96pub trait EnvelopeFormat {
99 fn format_item<'a>(
100 &self,
101 opts: &EnvelopeFormatOpts<'a>,
102 ) -> EnvelopeFormatItem;
103}
104
105#[derive(Debug, Clone, Eq)]
107pub enum EnvelopeFormatItem {
108 Begin(String),
109 End(String),
110 Item(String),
111 Separator,
112 List(Vec<EnvelopeFormatItem>),
113}
114
115impl EnvelopeFormatItem {
116 fn flatten(&self) -> Vec<EnvelopeFormatItem> {
117 match self {
118 EnvelopeFormatItem::List(items) => {
119 items.iter().flat_map(|i| i.flatten()).collect()
120 }
121 _ => vec![self.clone()],
122 }
123 }
124
125 fn nicen(items: &[EnvelopeFormatItem]) -> Vec<EnvelopeFormatItem> {
126 let mut input = items.to_vec();
127 let mut result: Vec<EnvelopeFormatItem> = vec![];
128
129 while !input.is_empty() {
130 let current = input.remove(0);
131 if input.is_empty() {
132 result.push(current);
133 break;
134 }
135 if let EnvelopeFormatItem::End(end_string) = current.clone() {
136 if let EnvelopeFormatItem::Begin(begin_string) =
137 input[0].clone()
138 {
139 result.push(EnvelopeFormatItem::End(format!(
140 "{} {}",
141 end_string, begin_string
142 )));
143 result.push(EnvelopeFormatItem::Begin("".to_string()));
144 input.remove(0);
145 } else {
146 result.push(current);
147 }
148 } else {
149 result.push(current);
150 }
151 }
152
153 result
154 }
155
156 fn indent(level: usize) -> String { " ".repeat(level * 4) }
157
158 fn add_space_at_end_if_needed(s: &str) -> String {
159 if s.is_empty() {
160 " ".to_string()
161 } else if s.ends_with(' ') {
162 s.to_string()
163 } else {
164 s.to_string() + " "
165 }
166 }
167
168 fn format(&self, opts: &EnvelopeFormatOpts<'_>) -> String {
169 if opts.flat {
170 return self.format_flat();
171 }
172 self.format_hierarchical()
173 }
174
175 fn format_flat(&self) -> String {
176 let mut line: String = "".to_string();
177 let items = self.flatten();
178 for item in items {
179 match item {
180 EnvelopeFormatItem::Begin(s) => {
181 if !line.ends_with(' ') {
182 line += " ";
183 }
184 line += &s;
185 line += " ";
186 }
187 EnvelopeFormatItem::End(s) => {
188 if !line.ends_with(' ') {
189 line += " ";
190 }
191 line += &s;
192 line += " ";
193 }
194 EnvelopeFormatItem::Item(s) => line += &s,
195 EnvelopeFormatItem::Separator => {
196 line = line.trim_end().to_string() + ", ";
197 }
198 EnvelopeFormatItem::List(items) => {
199 for item in items {
200 line += &item.format_flat();
201 }
202 }
203 }
204 }
205 line
206 }
207
208 fn format_hierarchical(&self) -> String {
209 let mut lines: Vec<String> = vec![];
210 let mut level = 0;
211 let mut current_line = "".to_string();
212 let items = Self::nicen(&self.flatten());
213 for item in items {
214 match item {
215 EnvelopeFormatItem::Begin(delimiter) => {
216 if !delimiter.is_empty() {
217 let c = if current_line.is_empty() {
218 delimiter
219 } else {
220 Self::add_space_at_end_if_needed(¤t_line)
221 + &delimiter
222 };
223 lines.push(Self::indent(level) + &c + "\n");
224 }
225 level += 1;
226 current_line = "".to_string();
227 }
228 EnvelopeFormatItem::End(delimiter) => {
229 if !current_line.is_empty() {
230 lines.push(Self::indent(level) + ¤t_line + "\n");
231 current_line = "".to_string();
232 }
233 level -= 1;
234 lines.push(Self::indent(level) + &delimiter + "\n");
235 }
236 EnvelopeFormatItem::Item(string) => {
237 current_line += &string;
238 }
239 EnvelopeFormatItem::Separator => {
240 if !current_line.is_empty() {
241 lines.push(Self::indent(level) + ¤t_line + "\n");
242 current_line = "".to_string();
243 }
244 }
245 EnvelopeFormatItem::List(_) => {
246 lines.push("<list>".to_string());
247 }
248 }
249 }
250 if !current_line.is_empty() {
251 lines.push(current_line);
252 }
253 lines.join("")
254 }
255}
256
257impl From<&str> for EnvelopeFormatItem {
259 fn from(s: &str) -> Self { Self::Item(s.to_string()) }
260}
261
262impl std::fmt::Display for EnvelopeFormatItem {
264 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
265 match self {
266 EnvelopeFormatItem::Begin(s) => write!(f, ".begin({})", s),
267 EnvelopeFormatItem::End(s) => write!(f, ".end({})", s),
268 EnvelopeFormatItem::Item(s) => write!(f, ".item({})", s),
269 EnvelopeFormatItem::Separator => write!(f, ".separator"),
270 EnvelopeFormatItem::List(items) => write!(f, ".list({:?})", items),
271 }
272 }
273}
274
275impl PartialEq for EnvelopeFormatItem {
277 fn eq(&self, other: &Self) -> bool {
278 match (self, other) {
279 (EnvelopeFormatItem::Begin(s1), EnvelopeFormatItem::Begin(s2)) => {
280 s1 == s2
281 }
282 (EnvelopeFormatItem::End(s1), EnvelopeFormatItem::End(s2)) => {
283 s1 == s2
284 }
285 (EnvelopeFormatItem::Item(s1), EnvelopeFormatItem::Item(s2)) => {
286 s1 == s2
287 }
288 (EnvelopeFormatItem::Separator, EnvelopeFormatItem::Separator) => {
289 true
290 }
291 (
292 EnvelopeFormatItem::List(items1),
293 EnvelopeFormatItem::List(items2),
294 ) => items1 == items2,
295 _ => false,
296 }
297 }
298}
299
300impl EnvelopeFormatItem {
301 fn index(&self) -> u32 {
302 match self {
303 EnvelopeFormatItem::Begin(_) => 1,
304 EnvelopeFormatItem::End(_) => 2,
305 EnvelopeFormatItem::Item(_) => 3,
306 EnvelopeFormatItem::Separator => 4,
307 EnvelopeFormatItem::List(_) => 5,
308 }
309 }
310}
311
312impl PartialOrd for EnvelopeFormatItem {
314 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
315 Some(self.cmp(other))
316 }
317}
318
319impl Ord for EnvelopeFormatItem {
321 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
322 let l_index = self.index();
323 let r_index = other.index();
324 match l_index.cmp(&r_index) {
325 std::cmp::Ordering::Less => return std::cmp::Ordering::Less,
326 std::cmp::Ordering::Greater => return std::cmp::Ordering::Greater,
327 _ => {}
328 }
329 match (self, other) {
330 (EnvelopeFormatItem::Begin(l), EnvelopeFormatItem::Begin(r)) => {
331 l.cmp(r)
332 }
333 (EnvelopeFormatItem::End(l), EnvelopeFormatItem::End(r)) => {
334 l.cmp(r)
335 }
336 (EnvelopeFormatItem::Item(l), EnvelopeFormatItem::Item(r)) => {
337 l.cmp(r)
338 }
339 (EnvelopeFormatItem::Separator, EnvelopeFormatItem::Separator) => {
340 std::cmp::Ordering::Equal
341 }
342 (EnvelopeFormatItem::List(l), EnvelopeFormatItem::List(r)) => {
343 l.cmp(r)
344 }
345 _ => std::cmp::Ordering::Equal,
346 }
347 }
348}
349
350impl EnvelopeFormat for CBOR {
352 fn format_item<'a>(
353 &self,
354 opts: &EnvelopeFormatOpts<'a>,
355 ) -> EnvelopeFormatItem {
356 match self.as_case() {
357 CBORCase::Tagged(tag, cbor) if tag == &Envelope::cbor_tags()[0] => {
358 Envelope::from_untagged_cbor(cbor.clone())
359 .map(|envelope| envelope.format_item(opts))
360 .unwrap_or_else(|_| "<error>".into())
361 }
362 _ => EnvelopeFormatItem::Item(
363 self.envelope_summary(usize::MAX, &opts.context)
364 .unwrap_or_else(|_| "<error>".into()),
365 ),
366 }
367 }
368}
369
370impl EnvelopeFormat for Envelope {
372 fn format_item<'a>(
373 &self,
374 opts: &EnvelopeFormatOpts<'a>,
375 ) -> EnvelopeFormatItem {
376 match self.case() {
377 EnvelopeCase::Leaf { cbor, .. } => cbor.format_item(opts),
378 EnvelopeCase::Wrapped { envelope, .. } => {
379 EnvelopeFormatItem::List(vec![
380 EnvelopeFormatItem::Begin("{".to_string()),
381 envelope.format_item(opts),
382 EnvelopeFormatItem::End("}".to_string()),
383 ])
384 }
385 EnvelopeCase::Assertion(assertion) => {
386 assertion.format_item(opts)
387 }
388 #[cfg(feature = "known_value")]
389 EnvelopeCase::KnownValue { value, .. } => {
390 value.format_item(opts)
391 }
392 #[cfg(feature = "encrypt")]
393 EnvelopeCase::Encrypted(_) => {
394 EnvelopeFormatItem::Item("ENCRYPTED".to_string())
395 }
396 #[cfg(feature = "compress")]
397 EnvelopeCase::Compressed(_) => {
398 EnvelopeFormatItem::Item("COMPRESSED".to_string())
399 }
400 EnvelopeCase::Node { subject, assertions, .. } => {
401 let mut items: Vec<EnvelopeFormatItem> = Vec::new();
402
403 let subject_item = subject.format_item(opts);
404 let mut elided_count = 0;
405 #[cfg(feature = "encrypt")]
406 let mut encrypted_count = 0;
407 #[cfg(feature = "compress")]
408 let mut compressed_count = 0;
409 #[cfg(feature = "known_value")]
410 let mut type_assertion_items: Vec<
411 Vec<EnvelopeFormatItem>,
412 > = Vec::new();
413 let mut assertion_items: Vec<Vec<EnvelopeFormatItem>> =
414 Vec::new();
415
416 for assertion in assertions {
417 match assertion.case() {
418 EnvelopeCase::Elided(_) => {
419 elided_count += 1;
420 }
421 #[cfg(feature = "encrypt")]
422 EnvelopeCase::Encrypted(_) => {
423 encrypted_count += 1;
424 }
425 #[cfg(feature = "compress")]
426 EnvelopeCase::Compressed(_) => {
427 compressed_count += 1;
428 }
429 _ => {
430 let item =
431 vec![assertion.format_item(opts)];
432 #[cfg(feature = "known_value")]
433 {
434 let mut is_type_assertion = false;
435 if let Some(predicate) =
436 assertion.as_predicate()
437 {
438 if let Some(known_value) =
439 predicate.subject().as_known_value()
440 {
441 if *known_value == known_values::IS_A {
442 is_type_assertion = true;
443 }
444 }
445 }
446 if is_type_assertion {
447 type_assertion_items.push(item);
448 } else {
449 assertion_items.push(item);
450 }
451 }
452 #[cfg(not(feature = "known_value"))]
453 assertion_items.push(item);
454 }
455 }
456 }
457 #[cfg(feature = "known_value")]
458 type_assertion_items.sort();
459 assertion_items.sort();
460 #[cfg(feature = "known_value")]
461 assertion_items.splice(0..0, type_assertion_items);
462 #[cfg(feature = "compress")]
463 if compressed_count > 1 {
464 assertion_items.push(vec![EnvelopeFormatItem::Item(
465 format!("COMPRESSED ({})", compressed_count),
466 )]);
467 } else if compressed_count > 0 {
468 assertion_items.push(vec![EnvelopeFormatItem::Item(
469 "COMPRESSED".to_string(),
470 )]);
471 }
472 if elided_count > 1 {
473 assertion_items.push(vec![EnvelopeFormatItem::Item(
474 format!("ELIDED ({})", elided_count),
475 )]);
476 } else if elided_count > 0 {
477 assertion_items.push(vec![EnvelopeFormatItem::Item(
478 "ELIDED".to_string(),
479 )]);
480 }
481 #[cfg(feature = "encrypt")]
482 if encrypted_count > 1 {
483 assertion_items.push(vec![EnvelopeFormatItem::Item(
484 format!("ENCRYPTED ({})", encrypted_count),
485 )]);
486 } else if encrypted_count > 0 {
487 assertion_items.push(vec![EnvelopeFormatItem::Item(
488 "ENCRYPTED".to_string(),
489 )]);
490 }
491 let joined_assertions_items: Vec<Vec<EnvelopeFormatItem>> =
492 itertools::intersperse_with(assertion_items, || {
493 vec![EnvelopeFormatItem::Separator]
494 })
495 .collect();
496
497 let needs_braces = subject.is_subject_assertion();
498
499 if needs_braces {
500 items.push(EnvelopeFormatItem::Begin("{".to_string()));
501 }
502 items.push(subject_item);
503 if needs_braces {
504 items.push(EnvelopeFormatItem::End("}".to_string()));
505 }
506 items.push(EnvelopeFormatItem::Begin("[".to_string()));
507 items.extend(joined_assertions_items.into_iter().flatten());
508 items.push(EnvelopeFormatItem::End("]".to_string()));
509 EnvelopeFormatItem::List(items)
510 }
511 EnvelopeCase::Elided(_) => {
512 EnvelopeFormatItem::Item("ELIDED".to_string())
513 }
514 }
515 }
516}
517
518impl EnvelopeFormat for Assertion {
520 fn format_item<'a>(
521 &self,
522 opts: &EnvelopeFormatOpts<'a>,
523 ) -> EnvelopeFormatItem {
524 EnvelopeFormatItem::List(vec![
525 self.predicate().format_item(opts),
526 EnvelopeFormatItem::Item(": ".to_string()),
527 self.object().format_item(opts),
528 ])
529 }
530}
531
532#[cfg(feature = "known_value")]
533impl EnvelopeFormat for KnownValue {
535 fn format_item<'a>(
536 &self,
537 opts: &EnvelopeFormatOpts<'a>,
538 ) -> EnvelopeFormatItem {
539 let name = match opts.context {
540 FormatContextOpt::None => {
541 self.name().to_string().flanked_by("'", "'")
542 }
543 FormatContextOpt::Global => {
544 crate::with_format_context!(|context: &crate::FormatContext| {
545 context
546 .known_values()
547 .assigned_name(self)
548 .map(|s| s.to_string())
549 .unwrap_or_else(|| self.name().to_string())
550 })
551 }
552 FormatContextOpt::Custom(format_context) => {
553 format_context
554 .known_values()
555 .assigned_name(self)
556 .map(|s| s.to_string())
557 .unwrap_or_else(|| self.name().to_string())
558 }
559 };
560 EnvelopeFormatItem::Item(name.flanked_by("'", "'"))
561 }
562}
563
564impl EnvelopeFormat for XID {
566 fn format_item<'a>(
567 &self,
568 _opts: &EnvelopeFormatOpts<'a>,
569 ) -> EnvelopeFormatItem {
570 EnvelopeFormatItem::Item(hex::encode(self.data()))
571 }
572}
573
574impl Envelope {
575 fn description<'a>(&self, opts: &EnvelopeFormatOpts<'a>) -> String {
576 match self.case() {
577 EnvelopeCase::Node { subject, assertions, .. } => {
578 let assertions = assertions
579 .iter()
580 .map(|a| a.to_string())
581 .collect::<Vec<String>>()
582 .join(", ")
583 .flanked_by("[", "]");
584 format!(".node({}, {})", subject, assertions)
585 }
586 EnvelopeCase::Leaf { cbor, .. } => {
587 format!(
588 ".cbor({})",
589 cbor.format_item(opts)
590 )
591 },
592 EnvelopeCase::Wrapped { envelope, .. } => {
593 format!(".wrapped({})", envelope)
594 }
595 EnvelopeCase::Assertion(assertion) => format!(
596 ".assertion({}, {})",
597 assertion.predicate(),
598 assertion.object()
599 ),
600 EnvelopeCase::Elided(_) => ".elided".to_string(),
601 #[cfg(feature = "known_value")]
602 EnvelopeCase::KnownValue { value, .. } => {
603 format!(".knownValue({})", value)
604 }
605 #[cfg(feature = "encrypt")]
606 EnvelopeCase::Encrypted(_) => ".encrypted".to_string(),
607 #[cfg(feature = "compress")]
608 EnvelopeCase::Compressed(_) => ".compressed".to_string(),
609 }
610 }
611}
612
613impl std::fmt::Display for Envelope {
615 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
616 f.write_str(&self.description(&EnvelopeFormatOpts::default()))
617 }
618}