Change-Id: Iab7e00cc26a4f9727d3ab98691ef379921a33052 Reviewed-on: https://cl.tvl.fyi/c/depot/+/5240 Tested-by: BuildkiteCI Reviewed-by: kanepyork <rikingcoding@gmail.com> Reviewed-by: Profpatsch <mail@profpatsch.de> Reviewed-by: grfn <grfn@gws.fyi> Reviewed-by: tazjin <tazjin@tvl.su>
		
			
				
	
	
		
			163 lines
		
	
	
	
		
			5.1 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			163 lines
		
	
	
	
		
			5.1 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| extern crate netencode;
 | ||
| 
 | ||
| use netencode::{Tag, T, U};
 | ||
| 
 | ||
| pub enum Pretty {
 | ||
|     Single {
 | ||
|         r#type: char,
 | ||
|         length: String,
 | ||
|         val: String,
 | ||
|         trailer: char,
 | ||
|     },
 | ||
|     Tag {
 | ||
|         r#type: char,
 | ||
|         length: String,
 | ||
|         key: String,
 | ||
|         inner: char,
 | ||
|         val: Box<Pretty>,
 | ||
|     },
 | ||
|     Multi {
 | ||
|         r#type: char,
 | ||
|         length: String,
 | ||
|         vals: Vec<Pretty>,
 | ||
|         trailer: char,
 | ||
|     },
 | ||
| }
 | ||
| 
 | ||
| impl Pretty {
 | ||
|     pub fn from_u<'a>(u: U<'a>) -> Pretty {
 | ||
|         match u {
 | ||
|             U::Unit => Self::scalar('u', "", ""),
 | ||
|             U::N1(b) => Self::scalar('n', "1:", if b { "1" } else { "0" }),
 | ||
|             U::N3(n) => Self::scalar('n', "3:", n),
 | ||
|             U::N6(n) => Self::scalar('n', "6:", n),
 | ||
|             U::N7(n) => Self::scalar('n', "7:", n),
 | ||
|             U::I3(i) => Self::scalar('i', "3:", i),
 | ||
|             U::I6(i) => Self::scalar('i', "6:", i),
 | ||
|             U::I7(i) => Self::scalar('i', "7:", i),
 | ||
|             U::Text(s) => Pretty::Single {
 | ||
|                 r#type: 't',
 | ||
|                 length: format!("{}:", s.len()),
 | ||
|                 val: s.to_string(),
 | ||
|                 trailer: ',',
 | ||
|             },
 | ||
|             U::Binary(s) => Pretty::Single {
 | ||
|                 r#type: 'b',
 | ||
|                 length: format!("{}:", s.len()),
 | ||
|                 // For pretty printing we want the string to be visible obviously.
 | ||
|                 // Instead of not supporting binary, let’s use lossy conversion.
 | ||
|                 val: String::from_utf8_lossy(s).into_owned(),
 | ||
|                 trailer: ',',
 | ||
|             },
 | ||
|             U::Sum(Tag { tag, val }) => Self::pretty_tag(tag, Self::from_u(*val)),
 | ||
|             U::Record(m) => Pretty::Multi {
 | ||
|                 r#type: '{',
 | ||
|                 // TODO: we are losing the size here, should we recompute it? Keep it?
 | ||
|                 length: String::from(""),
 | ||
|                 vals: m
 | ||
|                     .into_iter()
 | ||
|                     .map(|(k, v)| Self::pretty_tag(k, Self::from_u(v)))
 | ||
|                     .collect(),
 | ||
|                 trailer: '}',
 | ||
|             },
 | ||
|             U::List(l) => Pretty::Multi {
 | ||
|                 r#type: '[',
 | ||
|                 // TODO: we are losing the size here, should we recompute it? Keep it?
 | ||
|                 length: String::from(""),
 | ||
|                 vals: l.into_iter().map(|v| Self::from_u(v)).collect(),
 | ||
|                 trailer: ']',
 | ||
|             },
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     fn scalar<D>(r#type: char, length: &str, d: D) -> Pretty
 | ||
|     where
 | ||
|         D: std::fmt::Display,
 | ||
|     {
 | ||
|         Pretty::Single {
 | ||
|             r#type,
 | ||
|             length: length.to_string(),
 | ||
|             val: format!("{}", d),
 | ||
|             trailer: ',',
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     fn pretty_tag(tag: &str, val: Pretty) -> Pretty {
 | ||
|         Pretty::Tag {
 | ||
|             r#type: '<',
 | ||
|             length: format!("{}:", tag.len()),
 | ||
|             key: tag.to_string(),
 | ||
|             inner: '|',
 | ||
|             val: Box::new(val),
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     pub fn print_multiline<W>(&self, mut w: &mut W) -> std::io::Result<()>
 | ||
|     where
 | ||
|         W: std::io::Write,
 | ||
|     {
 | ||
|         Self::go(&mut w, self, 0, true);
 | ||
|         write!(w, "\n")
 | ||
|     }
 | ||
| 
 | ||
|     fn go<W>(mut w: &mut W, p: &Pretty, depth: usize, is_newline: bool) -> std::io::Result<()>
 | ||
|     where
 | ||
|         W: std::io::Write,
 | ||
|     {
 | ||
|         const full: usize = 4;
 | ||
|         const half: usize = 2;
 | ||
|         let i = &vec![b' '; depth * full];
 | ||
|         let iandhalf = &vec![b' '; depth * full + half];
 | ||
|         let (i, iandhalf) = unsafe {
 | ||
|             (
 | ||
|                 std::str::from_utf8_unchecked(i),
 | ||
|                 std::str::from_utf8_unchecked(iandhalf),
 | ||
|             )
 | ||
|         };
 | ||
|         if is_newline {
 | ||
|             write!(&mut w, "{}", i);
 | ||
|         }
 | ||
|         match p {
 | ||
|             Pretty::Single {
 | ||
|                 r#type,
 | ||
|                 length,
 | ||
|                 val,
 | ||
|                 trailer,
 | ||
|             } => write!(&mut w, "{} {}{}", r#type, val, trailer),
 | ||
|             Pretty::Tag {
 | ||
|                 r#type,
 | ||
|                 length,
 | ||
|                 key,
 | ||
|                 inner,
 | ||
|                 val,
 | ||
|             } => {
 | ||
|                 write!(&mut w, "{} {} {}", r#type, key, inner)?;
 | ||
|                 Self::go::<W>(&mut w, val, depth, false)
 | ||
|             }
 | ||
|             // if the length is 0 or 1, we print on one line,
 | ||
|             // only if there’s more than one element we split the resulting value.
 | ||
|             // we never break lines on arbitrary column sizes, since that is just silly.
 | ||
|             Pretty::Multi {
 | ||
|                 r#type,
 | ||
|                 length,
 | ||
|                 vals,
 | ||
|                 trailer,
 | ||
|             } => match vals.len() {
 | ||
|                 0 => write!(&mut w, "{} {}", r#type, trailer),
 | ||
|                 1 => {
 | ||
|                     write!(&mut w, "{} ", r#type);
 | ||
|                     Self::go::<W>(&mut w, &vals[0], depth, false)?;
 | ||
|                     write!(&mut w, "{}", trailer)
 | ||
|                 }
 | ||
|                 more => {
 | ||
|                     write!(&mut w, "\n{}{} \n", iandhalf, r#type)?;
 | ||
|                     for v in vals {
 | ||
|                         Self::go::<W>(&mut w, v, depth + 1, true)?;
 | ||
|                         write!(&mut w, "\n")?;
 | ||
|                     }
 | ||
|                     write!(&mut w, "{}{}", iandhalf, trailer)
 | ||
|                 }
 | ||
|             },
 | ||
|         }
 | ||
|     }
 | ||
| }
 |