1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
use crate::date::utils::parse_date_from_string;
use chrono::{DateTime, FixedOffset, Local};
use chrono_humanize::HumanTime;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Span, Value};
#[derive(Clone)]
pub struct SubCommand;

impl Command for SubCommand {
    fn name(&self) -> &str {
        "date humanize"
    }

    fn signature(&self) -> Signature {
        Signature::build("date humanize").category(Category::Date)
    }

    fn usage(&self) -> &str {
        "Print a 'humanized' format for the date, relative to now."
    }

    fn search_terms(&self) -> Vec<&str> {
        vec![
            "date",
            "humanize",
            "relative",
            "now",
            "today",
            "tomorrow",
            "yesterday",
            "weekday",
            "weekday_name",
            "timezone",
        ]
    }

    fn run(
        &self,
        engine_state: &EngineState,
        _stack: &mut Stack,
        call: &Call,
        input: PipelineData,
    ) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
        let head = call.head;
        input.map(move |value| helper(value, head), engine_state.ctrlc.clone())
    }

    fn examples(&self) -> Vec<Example> {
        vec![
            Example {
                description: "Print a 'humanized' format for the date, relative to now.",
                example: "date humanize",
                result: Some(Value::String {
                    val: "now".to_string(),
                    span: Span::test_data(),
                }),
            },
            Example {
                description: "Print a 'humanized' format for the date, relative to now.",
                example: r#""2021-10-22 20:00:12 +01:00" | date humanize"#,
                result: None,
            },
        ]
    }
}

fn helper(value: Value, head: Span) -> Value {
    match value {
        Value::Nothing { span: _ } => {
            let dt = Local::now();
            Value::String {
                val: humanize_date(dt.with_timezone(dt.offset())),
                span: head,
            }
        }
        Value::String {
            val,
            span: val_span,
        } => {
            let dt = parse_date_from_string(&val, val_span);
            match dt {
                Ok(x) => Value::String {
                    val: humanize_date(x),
                    span: head,
                },
                Err(e) => e,
            }
        }
        Value::Date { val, span: _ } => Value::String {
            val: humanize_date(val),
            span: head,
        },
        _ => Value::Error {
            error: ShellError::DatetimeParseError(head),
        },
    }
}

fn humanize_date(dt: DateTime<FixedOffset>) -> String {
    HumanTime::from(dt).to_string()
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_examples() {
        use crate::test_examples;

        test_examples(SubCommand {})
    }
}