]> git.g-eek.se Git - ranknauto.git/commitdiff
Src. Add parsing and printing of numbered item lists.
authorGustav Eek <gustav.eek@fripost.org>
Sun, 5 Mar 2023 18:17:45 +0000 (19:17 +0100)
committerGustav Eek <gustav.eek@fripost.org>
Mon, 6 Mar 2023 05:51:33 +0000 (06:51 +0100)
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

index d0ab599c84b0afa5dfd9cb7295770f23ecac0d9b..86fd7f108b9213624c2d5c6299bacf3122bb56ee 100644 (file)
@@ -21,13 +21,59 @@ fn winnow(mut list: Vec<String>) -> (Conf, Vec<String> ) {
 
     // 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<usize> = 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<String>) -> (Conf, Vec<String> ) {
     // 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<String>) {
 }
 
 fn output(conf: &Conf, prio: &Vec<f64>, ranked: &Vec<String>) {
-    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<f64> {
 
 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<String> =
+        "1.  Hej du\n 459. glade\n  2)ta en\n"
+        .split("\n").map(|x| x.to_owned())
+        .collect();
+    let exp: Vec<String> =
+        "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<String> =
         "Hej du\nglade\nta en"