Repository: arthurhenrique/rusti-cal (language: rust)

Issue:
Week numbers
Would be pretty neat to have a switch add week numbers. For example:

```sh
ᐅ ncal -w
    October 2022
Mo     3 10 17 24 31
Tu     4 11 18 25
We     5 12 19 26
Th     6 13 20 27
Fr     7 14 21 28
Sa  1  8 15 22 29
Su  2  9 16 23 30
   39 40 41 42 43 44
```


Discussion / hints:
this shift x to y axes would be very fun to implement! thx!

Fix patch:
diff --git a/README.md b/README.md
index 3dd2a50..1709222 100644
--- a/README.md
+++ b/README.md
@@ -30,7 +30,7 @@ $ rusti-cal <year>
 
 ![hands-on](./doc/rusti-cal.gif)
 
-## Optional Usage
+## Starting Day
 
 + Start Week With Sunday (Default)
 
@@ -38,7 +38,7 @@ $ rusti-cal <year>
 $ rusti-cal <year> --starting-day 0
 ```
 
-+ Start Week With Mondays
++ Start Week With Monday
 
 ```sh
 $ rusti-cal <year> --starting-day 1
@@ -91,3 +91,11 @@ $ rusti-cal <year> --color
 ```
 
 ![colored](./doc/colored.png)
+
+## Show Week Numbers
+
+Show week numbers with the `--week-numbers` or `-w` option.
+
+```sh
+$ rusti-cal <year> --week-numbers
+```
diff --git a/src/lib.rs b/src/lib.rs
index d07603f..9f0253f 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,7 +1,7 @@
 mod locale;
 
 use ansi_term::{
-    Color::{Black, Cyan, Red, Yellow, RGB},
+    Color::{Black, Cyan, Purple, Red, Yellow, RGB},
     Style,
 };
 use chrono::Datelike;
@@ -112,6 +112,7 @@ fn body_printable(
     months_memoized: Vec<u32>,
     year_memoized: u32,
     starting_day: u32,
+    week_numbers: bool,
 ) -> Vec<String> {
     let mut result = Vec::<String>::new();
     let mut result_days = format!("");
@@ -133,17 +134,25 @@ fn body_printable(
         .into_iter()
         .for_each(|i| result.push(i.to_string()));
 
-    // all body should have at least 6 lines
-    // plz refactor me
-    let len = result.len();
-    if len <= 6 {
-        let spaces = 21 - result[len - 1].len();
-        if result[len - 1].len() < 20 {
-            for _i in 0..spaces {
-                result[len - 1] += " "
+    (0..result.len()).for_each(|line| {
+        if result[line].is_empty() {
+            if week_numbers {
+                result[line] = "                        ".to_string()
+            } else {
+                result[line] = "                     ".to_string()
             }
+        } else {
+            let spaces = 21 - result[line].len();
+            result[line] += &" ".repeat(spaces);
+        }
+    });
+    // all bodies should have at least 7 lines
+    if result.len() < 7 {
+        if week_numbers {
+            result.push("                        ".to_string())
+        } else {
+            result.push("                     ".to_string())
         }
-        result.push("                     ".to_string())
     }
     result
 }
@@ -157,6 +166,7 @@ fn month_printable(
     starting_day: u32,
     month_names: Vec<String>,
     week_names: Vec<String>,
+    week_numbers: bool,
 ) -> Vec<String> {
     let mut result = Vec::<String>::new();
     let body = body_printable(
@@ -166,6 +176,7 @@ fn month_printable(
         months_memoized,
         year_memoized,
         starting_day,
+        week_numbers,
     );
     let month_name = &month_names[month - 1];
     result.push(format!(" {:^20}", month_name));
@@ -193,10 +204,16 @@ fn circular_week_name(week_name: Vec<String>, idx: usize) -> String {
     s.to_string()
 }
 
-pub fn calendar(year: u32, locale_str: &str, starting_day: u32) -> Vec<Vec<Vec<String>>> {
+pub fn calendar(
+    year: u32,
+    locale_str: &str,
+    starting_day: u32,
+    week_numbers: bool,
+) -> Vec<Vec<Vec<String>>> {
     let mut rows: Vec<Vec<Vec<String>>> = vec![vec![vec![String::from("")]; COLUMN]; ROWS];
     let mut row_counter = 0;
     let mut column_counter = 0;
+    let mut week_counter = 1;
     let (months_memoized, months) = get_days_accumulated_by_month(year);
     let year_memoized = days_by_year(year);
     let locale_info = locale::LocaleInfo::new(locale_str);
@@ -211,7 +228,36 @@ pub fn calendar(year: u32, locale_str: &str, starting_day: u32) -> Vec<Vec<Vec<S
             starting_day,
             locale_info.month_names(),
             locale_info.week_day_names(),
+            week_numbers,
         );
+
+        if week_numbers {
+            (0..rows[row_counter][column_counter].len()).for_each(|line| {
+                if line < 2 {
+                    rows[row_counter][column_counter][line] =
+                        "   ".to_string() + &rows[row_counter][column_counter][line]
+                } else if !&rows[row_counter][column_counter][line].trim().is_empty() {
+                    let padding = if week_counter > 9 {
+                        " ".to_string()
+                    } else {
+                        "  ".to_string()
+                    };
+                    rows[row_counter][column_counter][line] = padding
+                        + &week_counter.to_string()
+                        + &rows[row_counter][column_counter][line];
+
+                    if rows[row_counter][column_counter][line]
+                        .chars()
+                        .last()
+                        .unwrap()
+                        != ' '
+                    {
+                        week_counter += 1;
+                    }
+                }
+            });
+        }
+
         column_counter = month % COLUMN;
         if column_counter == 0 {
             row_counter += 1;
@@ -226,9 +272,10 @@ fn print_row(
     today_included: bool,
     pos_today: u32,
     monochromatic: bool,
+    week_numbers: bool,
 ) {
-    let pos_saturday = (((6 - starting_day as i32) % 7) + 7) % 7;
-    let pos_sunday = (((7 - starting_day as i32) % 7) + 7) % 7;
+    let pos_saturday = (((6 - starting_day as i32) % 7) + 7) % 7 + (week_numbers as i32);
+    let pos_sunday = (((7 - starting_day as i32) % 7) + 7) % 7 + (week_numbers as i32);
 
     let char_saturday = (1 + 3 * pos_saturday) as usize;
     let char_sunday = (1 + 3 * pos_sunday) as usize;
@@ -252,6 +299,8 @@ fn print_row(
                     Yellow.bold().paint(s)
                 } else if i == char_sunday || i == char_sunday + 1 {
                     Red.bold().paint(s)
+                } else if week_numbers && i < 3 {
+                    Purple.bold().paint(s)
                 } else {
                     ansi_term::Style::default().paint(s)
                 }
@@ -283,8 +332,14 @@ fn get_today_position(year: u32, month: u32, day: u32, starting_day: u32) -> (u3
     (row_index, col_index, x, y)
 }
 
-pub fn display(year: u32, locale_str: &str, starting_day: u32, monochromatic: bool) {
-    let rows = calendar(year, locale_str, starting_day);
+pub fn display(
+    year: u32,
+    locale_str: &str,
+    starting_day: u32,
+    monochromatic: bool,
+    week_numbers: bool,
+) {
+    let rows = calendar(year, locale_str, starting_day, week_numbers);
 
     let today = {
         let now = chrono::Local::now();
@@ -298,7 +353,11 @@ pub fn display(year: u32, locale_str: &str, starting_day: u32, monochromatic: bo
     };
 
     // print the year
-    println!("{}", Style::new().bold().paint(format!(" {:^63}", year)));
+    println!(
+        "{}{}",
+        " ".repeat(6 * week_numbers as usize),
+        Style::new().bold().paint(format!(" {:^63}", year))
+    );
 
     for (r, row) in rows.iter().enumerate() {
         for line in 0..8 {
@@ -330,6 +389,7 @@ pub fn display(year: u32, locale_str: &str, starting_day: u32, monochromatic: bo
                         today_included,
                         x,
                         monochromatic,
+                        week_numbers,
                     );
                 }
             }
diff --git a/src/main.rs b/src/main.rs
index 9b6021d..81c04e8 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -17,6 +17,10 @@ struct WithPositional {
     /// an optional flag for enabling colored output
     #[argh(switch, short = 'c')]
     color: bool,
+
+    /// an optional flag for enabling week numbers
+    #[argh(switch, short = 'w')]
+    week_numbers: bool,
 }
 
 fn default_year() -> u32 {
@@ -37,8 +41,20 @@ fn locale() -> String {
 fn main() {
     let arg = argh::from_env::<WithPositional>();
     if arg.color {
-        display(arg.year, &locale(), arg.starting_day, false);
+        display(
+            arg.year,
+            &locale(),
+            arg.starting_day,
+            false,
+            arg.week_numbers,
+        );
     } else {
-        display(arg.year, &locale(), arg.starting_day, true);
+        display(
+            arg.year,
+            &locale(),
+            arg.starting_day,
+            true,
+            arg.week_numbers,
+        );
     }
 }


Test patch: