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