use std::env; use tabled::{ builder::Builder, settings::{Modify, object::Rows, Alignment, Style} }; struct StringDetail { characters: Vec, len: usize, } struct CharacterDetail { byte_index: usize, character: Option, byte: u8, } impl StringDetail{ fn parse_utf8(query: &String) -> Self { let mut details:StringDetail = StringDetail::default(); for i in query.chars() { let mut bytes = [0; 4]; i.encode_utf8(&mut bytes); details.push(Some(i), bytes[0]); for b in 1..i.len_utf8() { details.push(None, bytes[b]); } } details } fn default() -> Self { Self { characters: Vec::new(), len: 0 } } fn push(&mut self, character:Option, byte:u8){ self.characters .push(CharacterDetail { byte_index: self.len, character, byte, }); self.len += 1; } fn print_table(&self) { let mut table_builder = Builder::default(); table_builder.set_header(StringDetail::table_header()); for i in self.table_rows() { table_builder.push_record(i); } let table = table_builder.build() .with(Style::sharp()) .with(Modify::new(Rows::new(1..)).with(Alignment::left())) .to_string(); print!("{}", table); } fn table_rows(&self) -> Vec> { self.characters.iter() .map(StringDetail::to_table_row) .collect::>>() } fn to_table_row(char_detail: &CharacterDetail) -> Vec { let character: String = match char_detail.character { Some(x) => String::from(format!("{}", x)), None => String::from("<..>"), }; vec![ character, format!("{}", char_detail.byte_index), format!("{:02x}", char_detail.byte), format!("{}", char_detail.byte), format!("{:08b}", char_detail.byte)] } fn table_header() -> Vec { vec![ String::from("character"), String::from("byte"), String::from("hex"), String::from("dec"), String::from("bin"), ] } } fn main() { let args: Vec = env::args().collect(); if args.len() < 2 { print!("Usage: enc "); return; } let utf8 = StringDetail::parse_utf8(&args[1]); utf8.print_table(); }