use crate::conf::CmdOptConf;
use crate::util::err::BrokenPipeError;
use runnel::RunnelIoe;
use std::fmt::Write as FmtWrite;
pub fn run(sioe: &RunnelIoe, conf: &CmdOptConf) -> anyhow::Result<()> {
let r = run_0(sioe, conf);
if r.is_broken_pipe() {
return Ok(());
}
r
}
#[derive(Default)]
struct Stats {
byte_count: u64,
char_count: u64,
line_count: u64,
word_count: u64,
max_line_bytes: u64,
}
struct StatsAscii {
ascii: [u64; 128],
}
impl StatsAscii {
fn new() -> StatsAscii {
Self { ascii: [0; 128] }
}
fn count_up(&mut self, b: u8) {
if b < 128 {
self.ascii[b as usize] += 1;
}
}
fn get_count(&self, idx: usize) -> u64 {
if idx < 128 {
self.ascii[idx]
} else {
0
}
}
fn max(&self) -> u64 {
*self.ascii.iter().max().unwrap_or(&0)
}
}
impl std::default::Default for StatsAscii {
fn default() -> Self {
Self::new()
}
}
fn run_0(sioe: &RunnelIoe, conf: &CmdOptConf) -> anyhow::Result<()> {
let mut stats = Stats::default();
let mut map_ascii = StatsAscii::default();
for line in sioe.pg_in().lines() {
let line_s = line?;
let line_ss = line_s.as_str();
run_00(conf, line_ss, &mut stats, &mut map_ascii)?;
}
{
let out_s = make_out_s_from_stats(conf, &stats)?;
sioe.pg_out().write_line(out_s)?;
sioe.pg_out().flush_line()?;
}
if conf.flg_map_ascii {
if conf.is_opt_uc_x_map_ascii_rust_src() {
let out_s = make_out_s_from_map_ascii_1(&map_ascii)?;
sioe.pg_out().write_line(out_s)?;
} else {
let mut vec = make_out_s_from_map_ascii_2(&map_ascii)?;
vec.reverse();
while let Some(v) = vec.pop() {
sioe.pg_out().write_line(v)?;
}
}
sioe.pg_out().flush_line()?;
}
Ok(())
}
fn run_00(
conf: &CmdOptConf,
line_ss: &str,
stats: &mut Stats,
map_ascii: &mut StatsAscii,
) -> anyhow::Result<()> {
let line_len: usize = line_ss.len();
stats.line_count += 1;
let line_bytes = line_len as u64;
if conf.flg_bytes {
stats.byte_count += line_bytes;
}
if conf.flg_max_line_bytes {
stats.max_line_bytes = stats.max_line_bytes.max(line_bytes);
}
if conf.flg_chars || conf.flg_words {
let mut prev_c: char = ' ';
for c in line_ss.chars() {
if conf.flg_chars {
stats.char_count += 1;
}
if conf.flg_words {
if prev_c.is_ascii_whitespace() && !c.is_ascii_whitespace() {
stats.word_count += 1;
}
prev_c = c;
}
if conf.flg_map_ascii && c.is_ascii() {
map_ascii.count_up(c as u8);
}
}
} else if conf.flg_map_ascii {
for b in line_ss.as_bytes() {
map_ascii.count_up(*b);
}
}
Ok(())
}
fn make_out_s_from_stats(conf: &CmdOptConf, stats: &Stats) -> anyhow::Result<String> {
let mut vec: Vec<String> = Vec::new();
if conf.flg_lines {
vec.push(my_formatted(conf, "lines", stats.line_count)?);
}
if conf.flg_bytes {
vec.push(my_formatted(conf, "bytes", stats.byte_count)?);
}
if conf.flg_chars {
vec.push(my_formatted(conf, "chars", stats.char_count)?);
}
if conf.flg_words {
vec.push(my_formatted(conf, "words", stats.word_count)?);
}
if conf.flg_max_line_bytes {
vec.push(my_formatted(conf, "max", stats.max_line_bytes)?);
}
Ok(vec.join(", "))
}
fn my_formatted(conf: &CmdOptConf, label: &str, num: u64) -> anyhow::Result<String> {
let mut s = String::new();
s.write_fmt(format_args!(
"{}:\"{}\"",
label,
conf.opt_locale.formatted_string(num)
))?;
Ok(s)
}
fn make_out_s_from_map_ascii_1(map_ascii: &StatsAscii) -> anyhow::Result<String> {
let mut vec: Vec<String> = Vec::new();
let max_val = map_ascii.max();
for i in 0x00..0x80 {
let val = map_ascii.get_count(i);
let val = val * 255 / max_val;
vec.push(format!("{}", val as u8));
}
Ok(format!(
"const ASCII_STOCHAS: [u8;128] = [{}];",
vec.join(", ")
))
}
fn make_out_s_from_map_ascii_2(map_ascii: &StatsAscii) -> anyhow::Result<Vec<String>> {
let mut vec: Vec<String> = Vec::new();
let mut ascii_ctrl: u64 = 0;
let mut ascii_ctrl_ht: u64 = 0;
let mut ascii_ctrl_vt: u64 = 0;
for i in 0..0x1F {
let val = map_ascii.get_count(i);
match i {
0x09 => ascii_ctrl_ht = val,
0x0B => ascii_ctrl_vt = val,
_ => ascii_ctrl += val,
}
}
ascii_ctrl += map_ascii.get_count(0x7F);
vec.push(format!("ctrl: --: {ascii_ctrl}"));
vec.push(format!("ctrl: ht: {ascii_ctrl_ht}"));
vec.push(format!("ctrl: vt: {ascii_ctrl_vt}"));
vec.push(format!("0x20: SP: {}", map_ascii.get_count(0x20)));
for i in 0x21..0x7F {
vec.push(format!(
"0x{:02x}: {}: {}",
i,
i as u8 as char,
map_ascii.get_count(i)
));
}
Ok(vec)
}