Add cal command ()

* Add cal command

* Fix docmentation to show commands on two lines

* Use bullet points on flag documentation for cal

* Dereference day before calling string method

* Silence Clippy warning regarding a function with too many arguments

* Update cal flag descriptions and documentation

* Add some tests for the cal command
This commit is contained in:
Joseph T. Lyons
2020-05-09 19:05:48 -04:00
committed by GitHub
parent 0f0847e45b
commit f5e03aaf1c
6 changed files with 492 additions and 0 deletions
crates/nu-cli
src
tests
commands
docs/commands

@ -251,6 +251,7 @@ pub fn create_default_context(
whole_stream_command(Touch), whole_stream_command(Touch),
whole_stream_command(Cpy), whole_stream_command(Cpy),
whole_stream_command(Date), whole_stream_command(Date),
whole_stream_command(Cal),
whole_stream_command(Calc), whole_stream_command(Calc),
whole_stream_command(Mkdir), whole_stream_command(Mkdir),
whole_stream_command(Move), whole_stream_command(Move),

@ -8,6 +8,7 @@ pub(crate) mod alias;
pub(crate) mod append; pub(crate) mod append;
pub(crate) mod args; pub(crate) mod args;
pub(crate) mod autoview; pub(crate) mod autoview;
pub(crate) mod cal;
pub(crate) mod calc; pub(crate) mod calc;
pub(crate) mod cd; pub(crate) mod cd;
pub(crate) mod classified; pub(crate) mod classified;
@ -128,6 +129,7 @@ pub(crate) use command::{whole_stream_command, Command, UnevaluatedCallInfo, Who
pub(crate) use alias::Alias; pub(crate) use alias::Alias;
pub(crate) use append::Append; pub(crate) use append::Append;
pub(crate) use cal::Cal;
pub(crate) use calc::Calc; pub(crate) use calc::Calc;
pub(crate) use compact::Compact; pub(crate) use compact::Compact;
pub(crate) use config::Config; pub(crate) use config::Config;

@ -0,0 +1,261 @@
use crate::prelude::*;
use chrono::{DateTime, Datelike, Local, NaiveDate};
use nu_errors::ShellError;
use nu_protocol::Dictionary;
use crate::commands::WholeStreamCommand;
use indexmap::IndexMap;
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
pub struct Cal;
impl WholeStreamCommand for Cal {
fn name(&self) -> &str {
"cal"
}
fn signature(&self) -> Signature {
Signature::build("cal")
.switch(
"month-names",
"Display the month names instead of integers",
Some('m'),
)
.named(
"year",
SyntaxShape::Int,
"Display a year-long calendar for the specified year",
Some('y'),
)
}
fn usage(&self) -> &str {
"Display a calendar."
}
fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
cal(args, registry)
}
}
pub fn cal(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once(registry)?;
let mut calendar_vec_deque = VecDeque::new();
let tag = args.call_info.name_tag.clone();
let (current_year, current_month, current_day) = get_current_date();
let should_show_month_names = args.has("month-names");
if args.has("year") {
let mut day_value: Option<u32> = Some(current_day);
let year_option = args.get("year");
let mut year_value = current_year as u64;
if let Some(year) = year_option {
if let Ok(year_u64) = year.as_u64() {
year_value = year_u64;
}
if year_value != current_year as u64 {
day_value = None
}
}
add_year_to_table(
&mut calendar_vec_deque,
&tag,
year_value as i32,
current_year,
current_month,
day_value,
should_show_month_names,
);
} else {
let (day_start_offset, number_of_days_in_month, _) =
get_month_information(current_year, current_month, current_year);
add_month_to_table(
&mut calendar_vec_deque,
&tag,
current_year,
current_month,
Some(current_day),
day_start_offset,
number_of_days_in_month as usize,
should_show_month_names,
);
}
Ok(futures::stream::iter(calendar_vec_deque).to_output_stream())
}
fn get_current_date() -> (i32, u32, u32) {
let local_now: DateTime<Local> = Local::now();
let current_year: i32 = local_now.date().year();
let current_month: u32 = local_now.date().month();
let current_day: u32 = local_now.date().day();
(current_year, current_month, current_day)
}
fn add_year_to_table(
mut calendar_vec_deque: &mut VecDeque<Value>,
tag: &Tag,
mut selected_year: i32,
current_year: i32,
current_month: u32,
current_day_option: Option<u32>,
should_show_month_names: bool,
) {
for month_number in 1..=12 {
let (day_start_offset, number_of_days_in_month, chosen_date_is_valid) =
get_month_information(selected_year, month_number, current_year);
if !chosen_date_is_valid {
selected_year = current_year;
}
let mut new_current_day_option: Option<u32> = None;
if let Some(current_day) = current_day_option {
if month_number == current_month {
new_current_day_option = Some(current_day)
}
}
add_month_to_table(
&mut calendar_vec_deque,
&tag,
selected_year,
month_number,
new_current_day_option,
day_start_offset,
number_of_days_in_month,
should_show_month_names,
);
}
}
#[allow(clippy::too_many_arguments)]
fn add_month_to_table(
calendar_vec_deque: &mut VecDeque<Value>,
tag: &Tag,
year: i32,
month: u32,
_current_day_option: Option<u32>, // Can be used in the future to display current day
day_start_offset: usize,
number_of_days_in_month: usize,
should_show_month_names: bool,
) {
let day_limit = number_of_days_in_month + day_start_offset;
let mut day_count: usize = 1;
let days_of_the_week = [
"sunday",
"monday",
"tuesday",
"wednesday",
"thurday",
"friday",
"saturday",
];
while day_count <= day_limit {
let mut indexmap = IndexMap::new();
indexmap.insert("year".to_string(), UntaggedValue::int(year).into_value(tag));
let month_value = if should_show_month_names {
UntaggedValue::string(get_month_name(month)).into_value(tag)
} else {
UntaggedValue::int(month).into_value(tag)
};
indexmap.insert("month".to_string(), month_value);
for day in &days_of_the_week {
let value = if (day_count <= day_limit) && (day_count > day_start_offset) {
UntaggedValue::int(day_count - day_start_offset).into_value(tag)
} else {
UntaggedValue::nothing().into_value(tag)
};
indexmap.insert((*day).to_string(), value);
day_count += 1;
}
calendar_vec_deque
.push_back(UntaggedValue::Row(Dictionary::from(indexmap)).into_value(tag));
}
}
fn get_month_name(month_number: u32) -> String {
let month_name = match month_number {
1 => "january",
2 => "february",
3 => "march",
4 => "april",
5 => "may",
6 => "june",
7 => "july",
8 => "august",
9 => "september",
10 => "october",
11 => "november",
_ => "december",
};
month_name.to_string()
}
fn get_month_information(
selected_year: i32,
month: u32,
current_year: i32,
) -> (usize, usize, bool) {
let (naive_date, chosen_date_is_valid_one) =
get_safe_naive_date(selected_year, month, current_year);
let weekday = naive_date.weekday();
let (days_in_month, chosen_date_is_valid_two) =
get_days_in_month(selected_year, month, current_year);
(
weekday.num_days_from_sunday() as usize,
days_in_month,
chosen_date_is_valid_one && chosen_date_is_valid_two,
)
}
fn get_days_in_month(selected_year: i32, month: u32, current_year: i32) -> (usize, bool) {
// Chrono does not provide a method to output the amount of days in a month
// This is a workaround taken from the example code from the Chrono docs here:
// https://docs.rs/chrono/0.3.0/chrono/naive/date/struct.NaiveDate.html#example-30
let (adjusted_year, adjusted_month) = if month == 12 {
(selected_year + 1, 1)
} else {
(selected_year, month + 1)
};
let (naive_date, chosen_date_is_valid) =
get_safe_naive_date(adjusted_year, adjusted_month, current_year);
(naive_date.pred().day() as usize, chosen_date_is_valid)
}
fn get_safe_naive_date(
selected_year: i32,
selected_month: u32,
current_year: i32,
) -> (NaiveDate, bool) {
if let Some(naive_date) = NaiveDate::from_ymd_opt(selected_year, selected_month, 1) {
return (naive_date, true);
}
(NaiveDate::from_ymd(current_year, selected_month, 1), false)
}

@ -0,0 +1,39 @@
use nu_test_support::{nu, pipeline};
#[test]
fn cal_february_2020_leap_year() {
let actual = nu!(
cwd: ".", pipeline(
r#"
cal -my 2020 | where month == "february" | to json
"#
));
let cal_february_json = r#"[{"year":2020,"month":"february","sunday":null,"monday":null,"tuesday":null,"wednesday":null,"thurday":null,"friday":null,"saturday":1},{"year":2020,"month":"february","sunday":2,"monday":3,"tuesday":4,"wednesday":5,"thurday":6,"friday":7,"saturday":8},{"year":2020,"month":"february","sunday":9,"monday":10,"tuesday":11,"wednesday":12,"thurday":13,"friday":14,"saturday":15},{"year":2020,"month":"february","sunday":16,"monday":17,"tuesday":18,"wednesday":19,"thurday":20,"friday":21,"saturday":22},{"year":2020,"month":"february","sunday":23,"monday":24,"tuesday":25,"wednesday":26,"thurday":27,"friday":28,"saturday":29}]"#;
assert_eq!(actual.out, cal_february_json);
}
#[test]
fn cal_friday_the_thirteenths_in_2015() {
let actual = nu!(
cwd: ".", pipeline(
r#"
cal -ym 2015 | where friday == 13 | count
"#
));
assert!(actual.out.contains("3"));
}
#[test]
fn cal_rows_in_2020() {
let actual = nu!(
cwd: ".", pipeline(
r#"
cal -y 2020 | count
"#
));
assert!(actual.out.contains("62"));
}

@ -1,5 +1,6 @@
mod alias; mod alias;
mod append; mod append;
mod cal;
mod calc; mod calc;
mod cd; mod cd;
mod compact; mod compact;

188
docs/commands/cal.md Normal file

@ -0,0 +1,188 @@
# cal
Use `cal` to display a calendar.
## Flags
* `-m`, `--month-names`: Display the month names instead of integers
* `-y`, `--year`: Display a year-long calendar for the specified year
## Examples
```shell
> cal
───┬──────┬───────┬────────┬────────┬─────────┬───────────┬─────────┬────────┬──────────
# │ year │ month │ sunday │ monday │ tuesday │ wednesday │ thurday │ friday │ saturday
───┼──────┼───────┼────────┼────────┼─────────┼───────────┼─────────┼────────┼──────────
020205 │ │ │ │ │ │ 12
1202053456789
22020510111213141516
32020517181920212223
42020524252627282930
52020531 │ │ │ │ │ │
───┴──────┴───────┴────────┴────────┴─────────┴───────────┴─────────┴────────┴──────────
```
```shell
> cal -y 2020
────┬──────┬───────┬────────┬────────┬─────────┬───────────┬─────────┬────────┬──────────
# │ year │ month │ sunday │ monday │ tuesday │ wednesday │ thurday │ friday │ saturday
────┼──────┼───────┼────────┼────────┼─────────┼───────────┼─────────┼────────┼──────────
020201 │ │ │ │ 1234
120201567891011
22020112131415161718
32020119202122232425
420201262728293031
520202 │ │ │ │ │ │ │ 1
6202022345678
7202029101112131415
82020216171819202122
92020223242526272829
10202031234567
1120203891011121314
122020315161718192021
132020322232425262728
1420203293031 │ │ │ │
1520204 │ │ │ │ 1234
1620204567891011
172020412131415161718
182020419202122232425
19202042627282930 │ │
2020205 │ │ │ │ │ │ 12
21202053456789
222020510111213141516
232020517181920212223
242020524252627282930
252020531 │ │ │ │ │ │
2620206 │ │ 123456
272020678910111213
282020614151617181920
292020621222324252627
3020206282930 │ │ │ │
3120207 │ │ │ │ 1234
3220207567891011
332020712131415161718
342020719202122232425
3520207262728293031
3620208 │ │ │ │ │ │ │ 1
37202082345678
38202089101112131415
392020816171819202122
402020823242526272829
41202083031 │ │ │ │ │
4220209 │ │ │ 12345
43202096789101112
442020913141516171819
452020920212223242526
462020927282930 │ │ │
47202010 │ │ │ │ │ 123
4820201045678910
4920201011121314151617
5020201018192021222324
5120201025262728293031
522020111234567
53202011891011121314
5420201115161718192021
5520201122232425262728
562020112930 │ │ │ │ │
57202012 │ │ │ 12345
582020126789101112
5920201213141516171819
6020201220212223242526
612020122728293031 │ │
────┴──────┴───────┴────────┴────────┴─────────┴───────────┴─────────┴────────┴──────────
```
```shell
> cal -my 2020
────┬──────┬───────────┬────────┬────────┬─────────┬───────────┬─────────┬────────┬──────────
# │ year │ month │ sunday │ monday │ tuesday │ wednesday │ thurday │ friday │ saturday
────┼──────┼───────────┼────────┼────────┼─────────┼───────────┼─────────┼────────┼──────────
02020 │ january │ │ │ │ 1234
12020 │ january │ 567891011
22020 │ january │ 12131415161718
32020 │ january │ 19202122232425
42020 │ january │ 262728293031
52020 │ february │ │ │ │ │ │ │ 1
62020 │ february │ 2345678
72020 │ february │ 9101112131415
82020 │ february │ 16171819202122
92020 │ february │ 23242526272829
102020 │ march │ 1234567
112020 │ march │ 891011121314
122020 │ march │ 15161718192021
132020 │ march │ 22232425262728
142020 │ march │ 293031 │ │ │ │
152020 │ april │ │ │ │ 1234
162020 │ april │ 567891011
172020 │ april │ 12131415161718
182020 │ april │ 19202122232425
192020 │ april │ 2627282930 │ │
202020 │ may │ │ │ │ │ │ 12
212020 │ may │ 3456789
222020 │ may │ 10111213141516
232020 │ may │ 17181920212223
242020 │ may │ 24252627282930
252020 │ may │ 31 │ │ │ │ │ │
262020 │ june │ │ 123456
272020 │ june │ 78910111213
282020 │ june │ 14151617181920
292020 │ june │ 21222324252627
302020 │ june │ 282930 │ │ │ │
312020 │ july │ │ │ │ 1234
322020 │ july │ 567891011
332020 │ july │ 12131415161718
342020 │ july │ 19202122232425
352020 │ july │ 262728293031
362020 │ august │ │ │ │ │ │ │ 1
372020 │ august │ 2345678
382020 │ august │ 9101112131415
392020 │ august │ 16171819202122
402020 │ august │ 23242526272829
412020 │ august │ 3031 │ │ │ │ │
422020 │ september │ │ │ 12345
432020 │ september │ 6789101112
442020 │ september │ 13141516171819
452020 │ september │ 20212223242526
462020 │ september │ 27282930 │ │ │
472020 │ october │ │ │ │ │ 123
482020 │ october │ 45678910
492020 │ october │ 11121314151617
502020 │ october │ 18192021222324
512020 │ october │ 25262728293031
522020 │ november │ 1234567
532020 │ november │ 891011121314
542020 │ november │ 15161718192021
552020 │ november │ 22232425262728
562020 │ november │ 2930 │ │ │ │ │
572020 │ december │ │ │ 12345
582020 │ december │ 6789101112
592020 │ december │ 13141516171819
602020 │ december │ 20212223242526
612020 │ december │ 2728293031 │ │
────┴──────┴───────────┴────────┴────────┴─────────┴───────────┴─────────┴────────┴──────────
```
```shell
> cal -my 2303 | where month == "june"
───┬──────┬───────┬────────┬────────┬─────────┬───────────┬─────────┬────────┬──────────
# │ year │ month │ sunday │ monday │ tuesday │ wednesday │ thurday │ friday │ saturday
───┼──────┼───────┼────────┼────────┼─────────┼───────────┼─────────┼────────┼──────────
02303 │ june │ │ 123456
12303 │ june │ 78910111213
22303 │ june │ 14151617181920
32303 │ june │ 21222324252627
42303 │ june │ 282930 │ │ │ │
───┴──────┴───────┴────────┴────────┴─────────┴───────────┴─────────┴────────┴──────────
```
```shell
> cal -my 2020 | where friday == 13
───┬──────┬──────────┬────────┬────────┬─────────┬───────────┬─────────┬────────┬──────────
# │ year │ month │ sunday │ monday │ tuesday │ wednesday │ thurday │ friday │ saturday
───┼──────┼──────────┼────────┼────────┼─────────┼───────────┼─────────┼────────┼──────────
02020 │ march │ 891011121314
12020 │ november │ 891011121314
───┴──────┴──────────┴────────┴────────┴─────────┴───────────┴─────────┴────────┴──────────
```