1#![cfg_attr(
28 any(
29 not(feature = "repr-dump"),
30 all(feature = "repr-dump", feature = "repr-color")
31 ),
32 doc = "```ignore"
33)]
34#![cfg_attr(
35 all(feature = "repr-dump", not(feature = "repr-color")),
36 doc = "```rust"
37)]
38use crate::{
63 Binary,
64 error::Error,
65 repr::{BinaryFormatOptions, ByteKind, RadixFormat, ReprComponentKind},
66};
67use alloc::{
68 format,
69 string::{String, ToString},
70};
71use core::{
72 assert,
73 clone::Clone,
74 cmp::{Eq, PartialEq},
75 default::Default,
76 fmt::Debug,
77 iter::Iterator,
78 marker::Copy,
79 option::Option::{self, None, Some},
80 result::Result,
81 todo, unreachable,
82};
83
84#[derive(Clone, Debug, PartialEq, Eq)]
89pub struct DumpFormatOptions {
90 radix_format: RadixFormat,
91 compact: bool,
92 index_header_line: bool,
93 index_line_numbers: bool,
94 index_radix_format: RadixFormat,
95 column_width: DumpColumnWidth,
96 two_columns: bool,
97 show_ascii: bool,
98 show_extended_ascii: bool,
99 line_index_spacing: String,
100 value_spacing: String,
101 column_separator: char,
102 column_index_underline: Option<char>,
103 colored: bool,
104}
105
106#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
107#[repr(usize)]
108pub enum DumpColumnWidth {
109 #[default]
110 Eight = 8,
111 Sixteen = 16,
112 ThirtyTwo = 32,
113}
114
115pub fn dump_representation(value: &Binary<'_>, options: &DumpFormatOptions) -> String {
120 assert!(options.index_radix_format != RadixFormat::Binary);
124
125 let (mid, end) = options.byte_counts();
126 let mut buffer = String::default();
127
128 if options.index_header_line {
132 buffer.push_str(&format!(
133 "{:1$}{2:3$}",
134 options.radix_format.prefix_str(),
135 options.line_index_width(),
136 "",
137 options.line_index_spacing.len(),
138 ));
139 for i in 0..end {
140 buffer.push_str(&options.format_column_index(i));
141 if (i + 1) % end != 0 && options.two_columns && (i + 1) % mid == 0 {
142 buffer.push_str(&options.format_column_separator());
143 }
144 }
145 buffer.push('\n');
146 if let Some(underline) = options.format_header_underline() {
147 buffer.push_str(&format!(
148 "{:1$}",
149 "",
150 options.line_index_width() + options.line_index_spacing.len()
151 ));
152 buffer.push_str(&underline);
153 }
154 }
155
156 for (index, byte) in value.iter().enumerate() {
160 let one_index = index + 1;
161
162 if options.index_line_numbers && index == 0 || index % end == 0 {
163 buffer.push_str(&options.format_line_index(index));
164 }
165
166 if options.show_ascii {
167 buffer.push_str(&options.format_ascii_char(byte));
168 } else {
169 buffer.push_str(&options.format_data_value(*byte));
170 }
171
172 if one_index % end == 0 {
173 buffer.push('\n');
174 } else if options.two_columns && one_index > 0 && one_index % mid == 0 {
175 buffer.push_str(&options.format_column_separator());
176 }
177 }
178 buffer
179}
180
181pub fn parse_dump_representation(_s: &str) -> Result<Binary<'_>, Error> {
182 todo!()
183}
184
185impl From<DumpFormatOptions> for BinaryFormatOptions {
190 fn from(value: DumpFormatOptions) -> Self {
191 Self::Dump(value)
192 }
193}
194
195impl Default for DumpFormatOptions {
196 fn default() -> Self {
197 Self {
198 radix_format: RadixFormat::default(),
199 compact: false,
200 index_header_line: true,
201 index_line_numbers: true,
202 index_radix_format: RadixFormat::default(),
203 column_width: DumpColumnWidth::default(),
204 two_columns: true,
205 show_ascii: false,
206 show_extended_ascii: false,
207 line_index_spacing: ": ".to_string(),
208 value_spacing: " ".to_string(),
209 column_separator: '│',
210 column_index_underline: Some('─'),
211 colored: cfg!(feature = "repr-color"),
212 }
213 }
214}
215
216impl DumpFormatOptions {
217 pub fn classic_hex_dump() -> Self {
218 Self::default()
219 .with_upper_hex_bytes()
220 .with_upper_hex_indices()
221 .compact(false)
222 .two_columns_of(DumpColumnWidth::Eight)
223 .no_column_index_underline()
224 .separate_columns_with('-')
225 .show_ascii(false)
226 }
227
228 pub fn ascii_hex_dump() -> Self {
229 Self::default()
230 .with_upper_hex_bytes()
231 .with_upper_hex_indices()
232 .compact(false)
233 .two_columns_of(DumpColumnWidth::Eight)
234 .no_column_index_underline()
235 .separate_columns_with('-')
236 .show_extended_ascii(true)
237 }
238
239 pub fn hex_dump() -> Self {
240 Self::default()
241 .with_upper_hex_bytes()
242 .with_upper_hex_indices()
243 .compact(false)
244 .two_columns_of(DumpColumnWidth::Eight)
245 .show_ascii(false)
246 }
247
248 pub fn octal_dump() -> Self {
249 Self::default()
250 .with_octal_bytes()
251 .with_octal_indices()
252 .compact(false)
253 .two_columns_of(DumpColumnWidth::Eight)
254 .show_ascii(false)
255 }
256
257 pub fn binary_dump() -> Self {
258 Self::default()
259 .with_binary_bytes()
260 .with_upper_hex_indices()
261 .compact(false)
262 .two_columns_of(DumpColumnWidth::Eight)
263 .show_ascii(false)
264 }
265
266 pub fn decimal_dump() -> Self {
267 Self::default()
268 .with_decimal_bytes()
269 .with_decimal_indices()
270 .compact(false)
271 .two_columns_of(DumpColumnWidth::Eight)
272 .show_ascii(false)
273 }
274
275 pub fn lower_hex_dump() -> Self {
276 Self::default()
277 .with_lower_hex_bytes()
278 .with_lower_hex_indices()
279 .compact(false)
280 .two_columns_of(DumpColumnWidth::Eight)
281 .show_ascii(false)
282 }
283
284 pub fn with_byte_radix_format(mut self, radix_format: RadixFormat) -> Self {
287 self.radix_format = radix_format;
288 self
289 }
290 pub fn with_binary_bytes(self) -> Self {
292 Self::with_byte_radix_format(self, RadixFormat::Binary)
293 }
294 pub fn with_decimal_bytes(self) -> Self {
296 Self::with_byte_radix_format(self, RadixFormat::Decimal)
297 }
298 pub fn with_lower_hex_bytes(self) -> Self {
300 Self::with_byte_radix_format(self, RadixFormat::LowerHex)
301 }
302 pub fn with_octal_bytes(self) -> Self {
304 Self::with_byte_radix_format(self, RadixFormat::Octal)
305 }
306 pub fn with_upper_hex_bytes(self) -> Self {
308 Self::with_byte_radix_format(self, RadixFormat::UpperHex)
309 }
310
311 pub fn has_index_header_line(mut self, header_line: bool) -> Self {
312 self.index_header_line = header_line;
313 self
314 }
315
316 pub fn underline_column_index_with(mut self, underline: char) -> Self {
317 self.column_index_underline = Some(underline);
318 self
319 }
320
321 pub fn no_column_index_underline(mut self) -> Self {
322 self.column_index_underline = None;
323 self
324 }
325
326 pub fn separate_columns_with(mut self, column_separator: char) -> Self {
327 self.column_separator = column_separator;
328 self
329 }
330
331 pub fn has_index_line_numbers(mut self, line_numbers: bool) -> Self {
332 self.index_line_numbers = line_numbers;
333 self
334 }
335
336 pub fn with_index_radix_format(mut self, index_radix_format: RadixFormat) -> Self {
339 self.index_radix_format = index_radix_format;
340 self
341 }
342 pub fn with_decimal_indices(self) -> Self {
344 Self::with_index_radix_format(self, RadixFormat::Decimal)
345 }
346 pub fn with_lower_hex_indices(self) -> Self {
348 Self::with_index_radix_format(self, RadixFormat::LowerHex)
349 }
350 pub fn with_octal_indices(self) -> Self {
352 Self::with_index_radix_format(self, RadixFormat::Octal)
353 }
354 pub fn with_upper_hex_indices(self) -> Self {
356 Self::with_index_radix_format(self, RadixFormat::UpperHex)
357 }
358
359 pub fn has_two_columns(mut self, two_columns: bool) -> Self {
360 self.two_columns = two_columns;
361 self
362 }
363
364 pub fn with_column_width(mut self, column_width: DumpColumnWidth) -> Self {
365 self.column_width = column_width;
366 self
367 }
368
369 pub fn one_column_of(mut self, column_width: DumpColumnWidth) -> Self {
370 self.two_columns = false;
371 self.column_width = column_width;
372 self
373 }
374
375 pub fn two_columns_of(mut self, column_width: DumpColumnWidth) -> Self {
376 self.two_columns = true;
377 self.column_width = column_width;
378 self
379 }
380
381 pub fn compact(mut self, compact: bool) -> Self {
382 self.compact = compact;
383 self
384 }
385
386 pub fn show_ascii(mut self, show_ascii: bool) -> Self {
387 self = self.with_upper_hex_bytes();
388 self.show_ascii = show_ascii;
389 self
390 }
391
392 pub fn show_extended_ascii(mut self, show_extended_ascii: bool) -> Self {
393 self = self.show_ascii(true);
394 self.show_extended_ascii = show_extended_ascii;
395 self
396 }
397
398 #[cfg(feature = "repr-color")]
401 pub fn use_color(mut self, colored: bool) -> Self {
402 self.colored = colored;
403 self
404 }
405
406 const fn byte_counts(&self) -> (usize, usize) {
407 match (self.two_columns, self.column_width) {
408 (false, w @ DumpColumnWidth::Eight) => (0, w.byte_count()),
409 (true, w @ DumpColumnWidth::Eight) => (w.byte_count(), w.two_column_byte_count()),
410 (false, w @ DumpColumnWidth::Sixteen) => (0, w.byte_count()),
411 (true, w @ DumpColumnWidth::Sixteen) => (w.byte_count(), w.two_column_byte_count()),
412 (false, w @ DumpColumnWidth::ThirtyTwo) => (0, w.byte_count()),
413 (true, w @ DumpColumnWidth::ThirtyTwo) => (w.byte_count(), w.two_column_byte_count()),
414 }
415 }
416
417 fn format_column_index(&self, index: usize) -> String {
418 let style = ReprComponentKind::Index.display_style(self.colored);
419 match self.radix_format {
420 RadixFormat::Binary => {
421 format!(
422 "{style}{index:00$b}{style:#}{spacing}",
423 self.data_value_width(),
424 spacing = self.value_spacing
425 )
426 }
427 RadixFormat::Decimal => {
428 format!(
429 "{style}{index:00$}{style:#}{spacing}",
430 self.data_value_width(),
431 spacing = self.value_spacing
432 )
433 }
434 RadixFormat::Octal => {
435 format!(
436 "{style}{index:00$o}{style:#}{spacing}",
437 self.data_value_width(),
438 spacing = self.value_spacing
439 )
440 }
441 RadixFormat::LowerHex => {
442 format!(
443 "{style}{index:00$x}{style:#}{spacing}",
444 self.data_value_width(),
445 spacing = self.value_spacing
446 )
447 }
448 RadixFormat::UpperHex => {
449 format!(
450 "{style}{index:00$X}{style:#}{spacing}",
451 self.data_value_width(),
452 spacing = self.value_spacing
453 )
454 }
455 }
456 }
457
458 fn format_header_underline(&self) -> Option<String> {
459 if let Some(underline) = self.column_index_underline {
460 let width =
461 (self.data_value_width() + self.value_spacing.len()) * self.column_width as usize;
462 let style = ReprComponentKind::Separator.display_style(self.colored);
463 let underline = format!("{style}{}{style:#}", underline.to_string().repeat(width));
464 let mut buffer = String::default();
465 buffer.push_str(&underline);
466 if self.two_columns {
467 buffer.push_str(&self.format_column_separator());
468 buffer.push_str(&underline);
469 }
470 buffer.push('\n');
471 Some(buffer)
472 } else {
473 None
474 }
475 }
476
477 fn format_column_separator(&self) -> String {
478 let style = ReprComponentKind::Separator.display_style(self.colored);
479 format!(
480 "{style}{}{}{style:#}",
481 self.column_separator, self.value_spacing
482 )
483 }
484
485 const fn line_index_width(&self) -> usize {
486 match self.index_radix_format {
487 RadixFormat::Decimal | RadixFormat::Octal => 8,
488 RadixFormat::LowerHex | RadixFormat::UpperHex => 6,
489 _ => unreachable!(),
490 }
491 }
492
493 fn format_line_index(&self, index: usize) -> String {
494 let style = ReprComponentKind::Index.display_style(self.colored);
495 match self.index_radix_format {
496 RadixFormat::Decimal => format!(
497 "{style}{index:0width$}{spacer}{style:#}",
498 width = self.line_index_width(),
499 spacer = self.line_index_spacing
500 ),
501 RadixFormat::Octal => format!(
502 "{style}{index:0width$o}{spacer}{style:#}",
503 width = self.line_index_width(),
504 spacer = self.line_index_spacing
505 ),
506 RadixFormat::LowerHex => format!(
507 "{style}{index:0width$x}{spacer}{style:#}",
508 width = self.line_index_width(),
509 spacer = self.line_index_spacing
510 ),
511 RadixFormat::UpperHex => format!(
512 "{style}{index:0width$X}{spacer}{style:#}",
513 width = self.line_index_width(),
514 spacer = self.line_index_spacing
515 ),
516 _ => unreachable!(),
517 }
518 }
519
520 const fn data_value_width(&self) -> usize {
521 match self.radix_format {
522 RadixFormat::Binary => 8,
523 RadixFormat::Decimal | RadixFormat::Octal => 3,
524 RadixFormat::LowerHex | RadixFormat::UpperHex => 2,
525 }
526 }
527
528 fn format_data_value(&self, byte: u8) -> String {
529 let style = ByteKind::ascii_char_display_style(&byte, self.colored);
530 match self.radix_format {
531 RadixFormat::Binary => {
532 format!(
533 "{style}{byte:00$b}{style:#}{spacing}",
534 self.data_value_width(),
535 spacing = self.value_spacing
536 )
537 }
538 RadixFormat::Decimal => {
539 format!(
540 "{style}{byte:00$}{style:#}{spacing}",
541 self.data_value_width(),
542 spacing = self.value_spacing
543 )
544 }
545 RadixFormat::Octal => {
546 format!(
547 "{style}{byte:00$o}{style:#}{spacing}",
548 self.data_value_width(),
549 spacing = self.value_spacing
550 )
551 }
552 RadixFormat::LowerHex => {
553 format!(
554 "{style}{byte:00$x}{style:#}{spacing}",
555 self.data_value_width(),
556 spacing = self.value_spacing
557 )
558 }
559 RadixFormat::UpperHex => {
560 format!(
561 "{style}{byte:00$X}{style:#}{spacing}",
562 self.data_value_width(),
563 spacing = self.value_spacing
564 )
565 }
566 }
567 }
568
569 fn format_ascii_char(&self, byte: &u8) -> String {
570 let decoded_char = match byte {
572 0x00 if self.show_extended_ascii => Some('␀'),
574 0x01 if self.show_extended_ascii => Some('␁'),
575 0x02 if self.show_extended_ascii => Some('␂'),
576 0x03 if self.show_extended_ascii => Some('␃'),
577 0x04 if self.show_extended_ascii => Some('␄'),
578 0x05 if self.show_extended_ascii => Some('␅'),
579 0x06 if self.show_extended_ascii => Some('␆'),
580 0x07 if self.show_extended_ascii => Some('␇'),
581 0x08 if self.show_extended_ascii => Some('␈'),
582 0x09 if self.show_extended_ascii => Some('␉'),
583 0x0A if self.show_extended_ascii => Some('␊'),
584 0x0B if self.show_extended_ascii => Some('␋'),
585 0x0C if self.show_extended_ascii => Some('␌'),
586 0x0D if self.show_extended_ascii => Some('␍'),
587 0x0E if self.show_extended_ascii => Some('␎'),
588 0x0F if self.show_extended_ascii => Some('␏'),
589 0x10 if self.show_extended_ascii => Some('␐'),
590 0x11 if self.show_extended_ascii => Some('␑'),
591 0x12 if self.show_extended_ascii => Some('␒'),
592 0x13 if self.show_extended_ascii => Some('␓'),
593 0x14 if self.show_extended_ascii => Some('␔'),
594 0x15 if self.show_extended_ascii => Some('␕'),
595 0x16 if self.show_extended_ascii => Some('␖'),
596 0x17 if self.show_extended_ascii => Some('␗'),
597 0x18 if self.show_extended_ascii => Some('␘'),
598 0x19 if self.show_extended_ascii => Some('␙'),
599 0x1A if self.show_extended_ascii => Some('␚'),
600 0x1B if self.show_extended_ascii => Some('␛'),
601 0x1C if self.show_extended_ascii => Some('␜'),
602 0x1D if self.show_extended_ascii => Some('␝'),
603 0x1E if self.show_extended_ascii => Some('␞'),
604 0x1F if self.show_extended_ascii => Some('␟'),
605 0x20 if self.show_extended_ascii => Some('␠'),
606 0x21..=0x7E => Some(*byte as char),
608 0x7F if self.show_extended_ascii => Some('␡'),
610 0xA0 if self.show_extended_ascii => Some('⍽'), 0xA1..=0xAC | 0xAE..=0xFF => Some(*byte as char),
616 _ => None, };
618 let style = ByteKind::ascii_char_display_style(byte, self.colored);
619 if let Some(c) = decoded_char {
620 format!(
621 "{style}{c:0$}{style:#}{spacing}",
622 self.data_value_width(),
623 spacing = self.value_spacing
624 )
625 } else {
626 format!(
627 "{style}{byte:00$X}{style:#}{spacing}",
628 self.data_value_width(),
629 spacing = self.value_spacing
630 )
631 }
632 }
633}
634
635impl DumpColumnWidth {
640 #[inline(always)]
641 pub const fn byte_count(&self) -> usize {
642 *self as usize
643 }
644
645 #[inline(always)]
646 pub const fn two_column_byte_count(&self) -> usize {
647 self.byte_count() * 2
648 }
649}