ocpi_tariffs_cli/
lib.rs

1#![doc = include_str!("../README.md")]
2
3mod analyzse;
4mod opts;
5mod print;
6mod validate;
7
8use std::{
9    env, fmt,
10    io::{self, IsTerminal as _},
11    path::PathBuf,
12};
13
14use clap::Parser;
15
16#[doc(hidden)]
17#[derive(Parser)]
18pub struct Opts {
19    #[clap(subcommand)]
20    command: opts::Command,
21}
22
23impl Opts {
24    pub fn run(self) -> Result<(), Error> {
25        self.command.run()
26    }
27}
28
29#[doc(hidden)]
30#[derive(Debug)]
31pub enum Error {
32    File {
33        path: PathBuf,
34        error: io::Error,
35    },
36    StdIn {
37        error: io::Error,
38    },
39    Internal(ocpi_tariffs::Error),
40    /// The calculated totals deviate from the totals in the CDR.
41    TotalsDoNotMatch,
42
43    /// When the process is a TTY `--cdr` is required.
44    CdrRequired,
45}
46impl std::error::Error for Error {}
47
48impl fmt::Display for Error {
49    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50        match self {
51            Self::File { path, error } => {
52                write!(f, "File error `{}`: {}", path.display(), error)
53            }
54            Self::StdIn { error } => {
55                write!(f, "Stdin error {error}")
56            }
57            Self::Internal(e) => write!(f, "{e}"),
58            Self::TotalsDoNotMatch => {
59                f.write_str("Calculation does not match all totals in the CDR")
60            }
61            Self::CdrRequired => f.write_str("`--cdr` is required when the process is a TTY"),
62        }
63    }
64}
65
66impl From<ocpi_tariffs::Error> for Error {
67    fn from(err: ocpi_tariffs::Error) -> Self {
68        Self::Internal(err)
69    }
70}
71
72impl Error {
73    pub fn file(path: PathBuf, error: io::Error) -> Self {
74        Self::File { path, error }
75    }
76
77    pub fn stdin(error: io::Error) -> Self {
78        Self::StdIn { error }
79    }
80}
81
82#[doc(hidden)]
83pub fn setup_logging() -> Result<(), &'static str> {
84    let stderr = io::stderr();
85    let builder = tracing_subscriber::fmt()
86        .without_time()
87        .with_writer(io::stderr)
88        .with_ansi(stderr.is_terminal());
89
90    let level = match env::var("RUST_LOG") {
91        Ok(s) => s.parse().unwrap_or(tracing::Level::INFO),
92        Err(err) => match err {
93            env::VarError::NotPresent => tracing::Level::INFO,
94            env::VarError::NotUnicode(_) => {
95                return Err("RUST_LOG is not unicode");
96            }
97        },
98    };
99
100    builder.with_max_level(level).init();
101
102    Ok(())
103}