From 334e260d3e38d502b5b961966c9b5575613d3e68 Mon Sep 17 00:00:00 2001 From: Gustav Eek Date: Sun, 5 Mar 2023 19:17:45 +0100 Subject: [PATCH] Src. Add parsing and printing of numbered item lists. Add a member *number* to *Conf* struct, which is a printing template for the case of numbered list. In *winnow*, identify number patterns, store it them as template in *conf* and remove it. In *output* print according to pattern. Also provide some basic tests. A special treatment is in printing is that number width is adopted to the length for right alignment. A special treatment in parsing is that the widest numbered list item should be used for pattern definition. Only for that you can tell about leading and trailing spaces. TODO Clean up (refactor) number pattern parsing in *winnow*. TODO Write some useful instructions in *README* from above commit message. --- src/main.rs | 88 ++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 81 insertions(+), 7 deletions(-) diff --git a/src/main.rs b/src/main.rs index d0ab599..86fd7f1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,13 +21,59 @@ fn winnow(mut list: Vec) -> (Conf, Vec ) { // Search patterns let bullet = Regex::new(r"^(\s*[\*-]\s*)").unwrap(); + let number = Regex::new(r"^(\s*)([0-9]+)([).]\s*)").unwrap(); let prio = Regex::new(r"^\s*[0-9]+\s*%\s*").unwrap(); + // Use index of the longest second group match to create conf template + if DEBUG { + eprintln!( + "Match: '{}', '{}', '{}', '{}'", + match number.captures(&list[0]){None => "", Some(x) => x.get(0).unwrap().as_str()}, + match number.captures(&list[0]){None => "", Some(x) => x.get(1).unwrap().as_str()}, + match number.captures(&list[0]){None => "", Some(x) => x.get(2).unwrap().as_str()}, + match number.captures(&list[0]){None => "", Some(x) => x.get(3).unwrap().as_str()}, + ); + } + + let lens: Vec = list.iter().map( + |y| match number.captures(y) { + None => 0, + Some(x) => x.get(2).unwrap().as_str().len(), + }).collect(); + + let maxi = lens.iter().enumerate().fold( + (0, 0), + |max, (ind, &val)| if val > max.1 {(ind, val)} else {max}); + + let tmpl = match number.captures(&list[maxi.0]) { + None => "".to_string(), + Some(x) => { + if DEBUG { + eprintln!("Test matches '{}', '{}', '{}'", + x.get(1).unwrap().as_str(), + x.get(2).unwrap().as_str(), + x.get(3).unwrap().as_str()); + } + format!( + "{}{{}}{}", + x.get(1).unwrap().as_str(), + x.get(3).unwrap().as_str()) + }, + }; + + if DEBUG { + eprint!("Lens index {}, {}: ", maxi.0, maxi.1); + for n in lens {eprint!("{}, ", n);} + eprintln!("\x08\x08."); + eprintln!("Pattern: '{}'", tmpl); + } + let conf = Conf { bullet: match bullet.captures(&list[0]) { None => "".to_string(), Some(x) => x.get(1).unwrap().as_str().to_string(), }, + number: tmpl, }; if DEBUG { @@ -37,6 +83,7 @@ fn winnow(mut list: Vec) -> (Conf, Vec ) { // Remove patterns and trim for l in &mut *list { *l = bullet.replace_all(&l, "").to_string(); + *l = number.replace_all(&l, "").to_string(); *l = prio.replace_all(&l, "").to_string(); *l = l.trim().to_owned(); } @@ -55,12 +102,19 @@ fn input() -> (Conf, Vec) { } fn output(conf: &Conf, prio: &Vec, ranked: &Vec) { - for (p, l) in prio.iter().zip(ranked.iter()) { - println!( - "{}{:2.0} %\t{}", - &conf.bullet, - p * 100.0, - l); + + let digs: usize = (prio.len() as f32).log(10.0).ceil() as usize; + + for (i, (p, l)) in prio.iter().zip(ranked).enumerate() { + + let item = if conf.number != "" { + conf.number.replace("{}", format!( // Replace template due + "{:digs$}", i + 1).as_str()) // to smt::fmt + } else { + conf.bullet.clone() + }; + + println!("{}{:2.0} %\t{}", item, 100.0 * p, l) } } @@ -138,6 +192,7 @@ fn lognormal(n: i32, std: f64) -> Vec { struct Conf { bullet: String, + number: String, } #[derive(Parser, Debug)] @@ -244,7 +299,24 @@ fn bullets() { let (conf, res) = winnow(arg); assert_eq!(exp, res); assert_eq!(conf.bullet, " * ".to_owned()); + assert_eq!(conf.number, "".to_owned()); +} + +#[test] + +fn numbers() { + let arg: Vec = + "1. Hej du\n 459. glade\n 2)ta en\n" + .split("\n").map(|x| x.to_owned()) + .collect(); + let exp: Vec = + "Hej du\nglade\nta en" + .split("\n").map(|x| x.to_owned()).collect(); + let (conf, res) = winnow(arg); + assert_eq!(exp, res); + assert_eq!(conf.bullet, "".to_owned()); + assert_eq!(conf.number, " {}. ".to_owned()); } #[test] @@ -265,8 +337,10 @@ fn prioinput() { #[test] fn bullout() { - let conf = Conf {bullet: " * ".to_owned()}; + let conf = Conf { + bullet: " * ".to_owned(), + number: "".to_owned()}; let prio = vec![0.6, 0.3, 0.1]; let ranked: Vec = "Hej du\nglade\nta en" -- 2.39.2