ocpi_tariffs_cli/
guess_version.rs

1use std::{
2    fmt, fs,
3    io::{self, Read as _},
4    path::PathBuf,
5};
6
7use console::style;
8use ocpi_tariffs::{cdr, guess, json, tariff};
9use tracing::debug;
10
11use crate::{opts::DEFAULT_STDIO_BUF_SIZE, print, Error, ObjectKind};
12
13#[derive(clap::Parser)]
14pub struct Command {
15    #[command(flatten)]
16    args: Args,
17}
18
19#[derive(clap::Args)]
20pub struct Args {
21    /// The type of OCPI object contained in the given JSON.
22    #[arg(short = 't', long = "type")]
23    kind: ObjectKind,
24
25    /// A path to json file for the specified object.
26    #[arg(short = 'f', long)]
27    file: Option<PathBuf>,
28
29    /// Check if all fields are allowed to be there according to the OCPI spec.
30    #[arg(long)]
31    validate: bool,
32}
33
34impl fmt::Display for ObjectKind {
35    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36        match self {
37            ObjectKind::Cdr => f.write_str("CDR"),
38            ObjectKind::Tariff => f.write_str("tariff"),
39        }
40    }
41}
42
43enum Outcome<'buf> {
44    Version {
45        object_kind: ObjectKind,
46
47        version: guess::Version<ocpi_tariffs::Version, ()>,
48    },
49    Report {
50        object_kind: ObjectKind,
51
52        /// A list of fields that were not expected: The schema did not define them.
53        ///
54        /// This list will always be empty if the guessed `Version` is `Uncertain`.
55        unexpected_fields: json::UnexpectedFields<'buf>,
56
57        version: guess::Version<ocpi_tariffs::Version, ()>,
58    },
59}
60
61impl Command {
62    pub fn run(self) -> Result<(), Error> {
63        let Self {
64            args:
65                Args {
66                    kind: object_kind,
67                    file: path,
68                    validate,
69                },
70        } = self;
71
72        let json = if let Some(path) = path {
73            debug!("Loading {object_kind} from file");
74            let json = fs::read_to_string(&*path).map_err(|e| Error::file(path, e))?;
75            debug!(bytes_read = json.len(), "{object_kind} read from file");
76            json
77        } else {
78            debug!("Loading {object_kind} from stdin");
79            let mut stdin = io::stdin().lock();
80            let mut json = String::with_capacity(DEFAULT_STDIO_BUF_SIZE);
81            let bytes_read = stdin.read_to_string(&mut json).map_err(Error::stdin)?;
82            debug!(bytes_read, "{object_kind} read from stdin");
83            json
84        };
85
86        let outcome = match object_kind {
87            ObjectKind::Cdr => {
88                if validate {
89                    let report = cdr::parse_and_report(&json)?;
90                    let (version, unexpected_fields) = report.into_parts();
91
92                    Outcome::Report {
93                        object_kind,
94                        unexpected_fields,
95                        version: version.into_version(),
96                    }
97                } else {
98                    let version = cdr::parse(&json)?;
99                    Outcome::Version {
100                        object_kind,
101                        version: version.into_version(),
102                    }
103                }
104            }
105            ObjectKind::Tariff => {
106                if validate {
107                    let report = tariff::parse_and_report(&json)?;
108                    let (version, unexpected_fields) = report.into_parts();
109
110                    Outcome::Report {
111                        object_kind,
112                        unexpected_fields,
113                        version: version.into_version(),
114                    }
115                } else {
116                    let version = tariff::parse(&json)?;
117                    Outcome::Version {
118                        object_kind,
119                        version: version.into_version(),
120                    }
121                }
122            }
123        };
124
125        match outcome {
126            Outcome::Version {
127                object_kind,
128                version,
129            } => print_version(object_kind, &version),
130            Outcome::Report {
131                object_kind,
132                unexpected_fields,
133                version,
134            } => {
135                print::unexpected_fields(object_kind, &unexpected_fields);
136                print_version(object_kind, &version);
137            }
138        }
139
140        Ok(())
141    }
142}
143
144fn print_version(object_kind: ObjectKind, version: &guess::Version<ocpi_tariffs::Version, ()>) {
145    match version {
146        guess::Version::Uncertain(()) => {
147            eprintln!(
148                "Unable to guess the version of the given {} JSON",
149                style(object_kind).green()
150            );
151        }
152        guess::Version::Certain(version) => {
153            println!("{version}");
154        }
155    }
156}