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
115
116
117
118
119
120
121
122
123
124
125
126
127
use chrono::offset::TimeZone;
use chrono::{LocalResult, NaiveDateTime};
use chrono_tz::Tz;
use clap::ValueEnum;
use clio::Input;

use super::bodyfile::{BodyfileDecoder, BodyfileSorter, BodyfileReader};
use super::cli::Cli;
use super::error::MactimeError;
use super::filter::{Sorter, RunOptions, Consumer, Provider, Joinable};
use super::output::{JsonSorter, CsvOutput, TxtOutput};
use super::stream::StreamReader;

#[derive(ValueEnum, Clone)]
pub enum InputFormat {
    BODYFILE,

    #[cfg(feature = "elastic")]
    JSON,
}

#[derive(ValueEnum, Clone)]
pub enum OutputFormat {
    CSV,
    TXT,
    JSON,

    #[cfg(feature = "elastic")]
    ELASTIC,
}

//#[derive(Builder)]
pub struct Mactime2Application {
    format: OutputFormat,
    bodyfile: Input,
    src_zone: Tz,
    dst_zone: Tz,
    strict_mode: bool,
}

impl Mactime2Application {

    fn create_sorter(&self, decoder: &mut BodyfileDecoder) -> Box<dyn Sorter<Result<(), MactimeError>>> {
        let options = RunOptions {
            strict_mode: self.strict_mode,
            src_zone: self.src_zone,
        };

        if matches!(self.format, OutputFormat::JSON) {
            Box::new(JsonSorter::with_receiver(decoder.get_receiver(), options))
        } else {
            let mut sorter = BodyfileSorter::default().with_receiver(decoder.get_receiver(), options);

            sorter = sorter.with_output(match self.format {
                OutputFormat::CSV => Box::new(CsvOutput::new(self.src_zone, self.dst_zone)),
                OutputFormat::TXT => Box::new(TxtOutput::new(self.src_zone, self.dst_zone)),
                _ => panic!("invalid execution path"),
            });
            Box::new(sorter)
        }
    }

    pub fn run(&self) -> anyhow::Result<()> {
        let options = RunOptions {
            strict_mode: self.strict_mode,
            src_zone: self.src_zone,
        };

        let mut reader = <BodyfileReader as StreamReader<String, ()>>::from(self.bodyfile.clone())?;
        let mut decoder = BodyfileDecoder::with_receiver(reader.get_receiver(), options);
        let mut sorter = self.create_sorter(&mut decoder);
        sorter.run();

        let _ = reader.join();
        let _ = decoder.join();
        sorter.join().unwrap()?;
        Ok(())
    }

    pub fn format_date(unix_ts: i64, src_zone: &Tz, dst_zone: &Tz) -> String {
        if unix_ts >= 0 {
            let src_timestamp =
                match src_zone.from_local_datetime(&NaiveDateTime::from_timestamp_opt(unix_ts, 0).unwrap()) {
                    LocalResult::None => {
                        return "INVALID DATETIME".to_owned();
                    }
                    LocalResult::Single(t) => t,
                    LocalResult::Ambiguous(t1, _t2) => t1,
                };
            let dst_timestamp = src_timestamp.with_timezone(dst_zone);
            dst_timestamp.to_rfc3339()
        } else {
            "0000-00-00T00:00:00+00:00".to_owned()
        }
    }
}

impl From<Cli> for Mactime2Application {
    fn from(cli: Cli) -> Self {
        let format = match cli.output_format {
            Some(f) => f,
            None => {
                if cli.csv_format {
                    OutputFormat::CSV
                } else if cli.json_format {
                    OutputFormat::JSON
                } else {
                    OutputFormat::TXT
                }
            }
        };

        Self {
            format,
            bodyfile: cli.input_file,
            src_zone: cli
                .src_zone
                .map(|tz| tz.parse().unwrap())
                .unwrap_or(Tz::UTC),
            dst_zone: cli
                .dst_zone
                .map(|tz| tz.parse().unwrap())
                .unwrap_or(Tz::UTC),
            strict_mode: cli.strict_mode,
        }
    }
}