ocpi-tariffs-cli 0.46.1

CLI application for OCPI tariff calculation
Documentation
use std::{fmt, path::PathBuf};

use console::style;
use ocpi_tariffs::{cdr, guess, json, tariff};

use crate::{load_object_file, load_object_from_stdin, print, Error, ObjectKind};

#[derive(clap::Parser)]
pub struct Command {
    #[command(flatten)]
    args: Args,
}

#[derive(clap::Args)]
pub struct Args {
    /// The type of OCPI object contained in the given JSON.
    #[arg(short = 't', long = "type")]
    kind: ObjectKind,

    /// A path to JSON file for the specified object.
    #[arg(short = 'f', long)]
    file: Option<PathBuf>,

    /// Check if all fields are allowed to be there according to the OCPI spec.
    #[arg(long)]
    validate: bool,
}

impl fmt::Display for ObjectKind {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            ObjectKind::Cdr => f.write_str("CDR"),
            ObjectKind::Tariff => f.write_str("tariff"),
        }
    }
}

enum Outcome<'buf> {
    Version {
        object_kind: ObjectKind,

        version: guess::Version<ocpi_tariffs::Version, ()>,
    },
    Report {
        object_kind: ObjectKind,

        /// A list of fields that were not expected: The schema did not define them.
        ///
        /// This list will always be empty if the guessed `Version` is `Uncertain`.
        unexpected_fields: json::UnexpectedFields<'buf>,

        version: guess::Version<ocpi_tariffs::Version, ()>,
    },
}

impl Command {
    pub fn run(self) -> Result<(), Error> {
        let Self {
            args:
                Args {
                    kind: object_kind,
                    file: path,
                    validate,
                },
        } = self;

        let json = if let Some(path) = path.as_deref() {
            load_object_file(path, object_kind)?
        } else {
            load_object_from_stdin(object_kind)?
        };

        let outcome = match object_kind {
            ObjectKind::Cdr => {
                if validate {
                    let report = cdr::parse_and_report(&json)?;
                    let guess::Report {
                        unexpected_fields,
                        version,
                    } = report;

                    Outcome::Report {
                        object_kind,
                        unexpected_fields,
                        version: version.into_version(),
                    }
                } else {
                    let version = cdr::parse(&json)?;
                    Outcome::Version {
                        object_kind,
                        version: version.into_version(),
                    }
                }
            }
            ObjectKind::Tariff => {
                if validate {
                    let report = tariff::parse_and_report(&json)?;
                    let guess::Report {
                        unexpected_fields,
                        version,
                    } = report;

                    Outcome::Report {
                        object_kind,
                        unexpected_fields,
                        version: version.into_version(),
                    }
                } else {
                    let version = tariff::parse(&json)?;
                    Outcome::Version {
                        object_kind,
                        version: version.into_version(),
                    }
                }
            }
        };

        match outcome {
            Outcome::Version {
                object_kind,
                version,
            } => print_version(object_kind, &version),
            Outcome::Report {
                object_kind,
                unexpected_fields,
                version,
            } => {
                print::unexpected_fields(object_kind, &unexpected_fields);
                print_version(object_kind, &version);
            }
        }

        Ok(())
    }
}

fn print_version(object_kind: ObjectKind, version: &guess::Version<ocpi_tariffs::Version, ()>) {
    match version {
        guess::Version::Uncertain(()) => {
            eprintln!(
                "Unable to guess the version of the given {} JSON",
                style(object_kind).green()
            );
        }
        guess::Version::Certain(version) => {
            println!("{version}");
        }
    }
}