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 guess::Report {
91                        unexpected_fields,
92                        version,
93                    } = report;
94
95                    Outcome::Report {
96                        object_kind,
97                        unexpected_fields,
98                        version: version.into_version(),
99                    }
100                } else {
101                    let version = cdr::parse(&json)?;
102                    Outcome::Version {
103                        object_kind,
104                        version: version.into_version(),
105                    }
106                }
107            }
108            ObjectKind::Tariff => {
109                if validate {
110                    let report = tariff::parse_and_report(&json)?;
111                    let guess::Report {
112                        unexpected_fields,
113                        version,
114                    } = report;
115
116                    Outcome::Report {
117                        object_kind,
118                        unexpected_fields,
119                        version: version.into_version(),
120                    }
121                } else {
122                    let version = tariff::parse(&json)?;
123                    Outcome::Version {
124                        object_kind,
125                        version: version.into_version(),
126                    }
127                }
128            }
129        };
130
131        match outcome {
132            Outcome::Version {
133                object_kind,
134                version,
135            } => print_version(object_kind, &version),
136            Outcome::Report {
137                object_kind,
138                unexpected_fields,
139                version,
140            } => {
141                print::unexpected_fields(object_kind, &unexpected_fields);
142                print_version(object_kind, &version);
143            }
144        }
145
146        Ok(())
147    }
148}
149
150fn print_version(object_kind: ObjectKind, version: &guess::Version<ocpi_tariffs::Version, ()>) {
151    match version {
152        guess::Version::Uncertain(()) => {
153            eprintln!(
154                "Unable to guess the version of the given {} JSON",
155                style(object_kind).green()
156            );
157        }
158        guess::Version::Certain(version) => {
159            println!("{version}");
160        }
161    }
162}