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 TotalsDoNotMatch,
42}
43impl std::error::Error for Error {}
44
45impl fmt::Display for Error {
46 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47 match self {
48 Self::File { path, error } => {
49 write!(f, "File error `{}`: {}", path.display(), error)
50 }
51 Self::StdIn { error } => {
52 write!(f, "Stdin error {error}")
53 }
54 Self::Internal(e) => write!(f, "{e}"),
55 Self::TotalsDoNotMatch => {
56 f.write_str("Calculation does not match all totals in the CDR")
57 }
58 }
59 }
60}
61
62impl From<ocpi_tariffs::Error> for Error {
63 fn from(err: ocpi_tariffs::Error) -> Self {
64 Self::Internal(err)
65 }
66}
67
68impl Error {
69 pub fn file(path: PathBuf, error: io::Error) -> Self {
70 Self::File { path, error }
71 }
72
73 pub fn stdin(error: io::Error) -> Self {
74 Self::StdIn { error }
75 }
76}
77
78#[doc(hidden)]
79pub fn setup_logging() -> Result<(), &'static str> {
80 let stderr = io::stderr();
81 let builder = tracing_subscriber::fmt()
82 .without_time()
83 .with_writer(io::stderr)
84 .with_ansi(stderr.is_terminal());
85
86 let level = match env::var("RUST_LOG") {
87 Ok(s) => s.parse().unwrap_or(tracing::Level::INFO),
88 Err(err) => match err {
89 env::VarError::NotPresent => tracing::Level::INFO,
90 env::VarError::NotUnicode(_) => {
91 return Err("RUST_LOG is not unicode");
92 }
93 },
94 };
95
96 builder.with_max_level(level).init();
97
98 Ok(())
99}