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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
use soulog::*;
use crate::cli::VERBOSE;

pub struct DynamicLogger {
    verbose: Option<Verbose>,
    quiet: Option<Quiet>,
}

macro_rules! one_or_other {
    ($name:ident($one:expr, $two:expr) $code:block) => {'code: {
        if let Some($name) = &mut $one {
            break 'code ($code);
        }
        if let Some($name) = &mut $two {
            break 'code ($code);
        }

        panic!("DynamicLogger of no logger type (should not happen)");
    }}
}

impl Logger for DynamicLogger {
    fn new() -> Self {
        unsafe { if VERBOSE {
            Self {
                verbose: Some(Verbose::new()),
                quiet: None,
            }
        } else {
            Self {
                verbose: None,
                quiet: Some(Quiet::new()),
            }
        }}
    }

    fn hollow(&self) -> Self { Self::new() }

    fn crash<T>(&mut self) -> T {one_or_other!(x(self.verbose, self.quiet) {
        x.crash()
    })}

    fn verbose(&mut self, log: Log) {one_or_other!(x(self.verbose, self.quiet) {
        x.verbose(log)
    })}

    fn vital(&mut self, log: Log) {one_or_other!(x(self.verbose, self.quiet) {
        x.vital(log)
    })}

    fn error(&mut self, log: Log) -> ErrorResponse {one_or_other!(x(self.verbose, self.quiet) {
        x.error(log)
    })}
}

pub struct Verbose {
    retry_count: u8,
}

impl Logger for Verbose {
    fn new() -> Self { Self { retry_count: 2 } }
    fn hollow(&self) -> Self { Self::new() }

    fn crash<T>(&mut self) -> T {
        let mut logger = Self::new();
        log!((logger.vital) Diary("if the fatal error occurred during any writing to the archive, the archive may be corrupted! If so, then use `diary-cli rollback` to roll-back to the latest backup (that was made before any modification of the archive") as Warning);
        std::process::exit(1)
    }

    fn verbose(&mut self, log: Log) {
        self.retry_count = 2;
        println!("{}", colour_format!(blue("["), cyan(log.origin), blue("] "), none(log.message)));
    }

    fn error(&mut self, log: Log) -> ErrorResponse {
        let message = match log.log_type {
            LogType::Failure => colour_format![blue("["), red(log.origin), blue("] "), red("Failure"), blue(": "), none(log.message)],
            LogType::Fatal => colour_format![blue("["), red(log.origin), blue("] "), red("Fatal"), blue(": "), none(log.message)],
            _ => panic!("meta error: invalid error log type '{:?}'", log.log_type),
        }; println!("{message}");

        if ErrorResponse::AskUser.allowed_in(&log) { return ErrorResponse::AskUser };
        if ErrorResponse::Retry.allowed_in(&log) && self.retry_count > 0 {
            self.retry_count -= 1;
            // wait for a bit
            std::thread::sleep(std::time::Duration::from_millis(800));
            return ErrorResponse::Retry;
        };

        ErrorResponse::Crash
    }

    fn vital(&mut self, log: Log) {
        let message = match log.log_type {
            LogType::Inconvenience => colour_format![blue("["), yellow(log.origin), blue("] "), yellow("Inconvenience"), blue(": "), none(log.message)],
            LogType::Warning => colour_format![blue("["), yellow(log.origin), blue("] "), yellow("Warning"), blue(": "), none(log.message)],
            LogType::Result => colour_format![blue("["), green("Result"), blue("] "), green(log.origin), blue(": "), none(log.message)],
            LogType::Log => colour_format!(blue("["), cyan(log.origin), blue("] "), none(log.message)),
            _ => panic!("meta error: invalid error log type '{:?}'", log.log_type),
        }; println!("{message}");
    }
}

pub struct Quiet {
    retry_count: u8,
}

impl Logger for Quiet {
    fn new() -> Self { Self { retry_count: 2 } }
    fn hollow(&self) -> Self { Self::new() }

    fn crash<T>(&mut self) -> T {
        let mut logger = Self::new();
        log!((logger.vital) Diary("The archive may now be corrupted! Use `diary-cli rollback` to roll-back to the latest backup (that was made before any modification of the archive") as Warning);
        std::process::exit(1)
    }

    fn verbose(&mut self, _: Log) {
        self.retry_count = 2;
    }

    fn error(&mut self, log: Log) -> ErrorResponse {
        let message = match log.log_type {
            LogType::Failure => colour_format![blue("["), red(log.origin), blue("] "), red("Failure"), blue(": "), none(log.message)],
            LogType::Fatal => colour_format![blue("["), red(log.origin), blue("] "), red("Fatal"), blue(": "), none(log.message)],
            _ => panic!("meta error: invalid error log type '{:?}'", log.log_type),
        }; println!("{message}");

        if ErrorResponse::AskUser.allowed_in(&log) { return ErrorResponse::AskUser };
        if ErrorResponse::Retry.allowed_in(&log) && self.retry_count > 0 {
            self.retry_count -= 1;
            // wait for a bit
            std::thread::sleep(std::time::Duration::from_millis(800));
            return ErrorResponse::Retry;
        };

        ErrorResponse::Crash
    }

    fn vital(&mut self, log: Log) {
        let message = match log.log_type {
            LogType::Inconvenience => colour_format![blue("["), yellow(log.origin), blue("] "), yellow("Inconvenience"), blue(": "), none(log.message)],
            LogType::Warning => colour_format![blue("["), yellow(log.origin), blue("] "), yellow("Warning"), blue(": "), none(log.message)],
            LogType::Result => colour_format![blue("["), green("Result"), blue("] "), green(log.origin), blue(": "), none(log.message)],
            LogType::Log => colour_format!(blue("["), cyan(log.origin), blue("] "), none(log.message)),
            _ => panic!("meta error: invalid error log type '{:?}'", log.log_type),
        }; println!("{message}");
    }
}