basyx-rs 0.1.0

A Rust library to work with Asset Administration Shells.
Documentation
use std::{
    path::{Path, PathBuf},
    str::FromStr,
};

use clap::Parser;
use color_eyre::eyre::{anyhow, Context, Result};
use colored::Colorize;
use jsonschema::JSONSchema;
use serde_json::{json, Value};
use thiserror::Error;

#[derive(Parser)]
#[clap(
    version = "0.1",
    author = "Andreas Schmidt <andreas.schmidt@iese.fraunhofer.de>"
)]
struct Opts {
    #[clap(parse(from_os_str))]
    input: PathBuf,
    #[clap(short, long)]
    mode: Mode,
}

#[allow(clippy::upper_case_acronyms)]
#[derive(Clone, Copy)]
enum Mode {
    AAS,
    Submodel,
}

impl FromStr for Mode {
    type Err = AASCheckError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s.to_lowercase().as_str() {
            "aas" => Ok(Mode::AAS),
            "submodel" => Ok(Mode::Submodel),
            _ => Err(AASCheckError::InvalidMode(s.to_string())),
        }
    }
}

#[derive(Error, Debug)]
pub enum AASCheckError {
    #[error("{0}")]
    InvalidMode(String),
}

fn main() -> Result<()> {
    let opt = Opts::parse();
    let instance = read_json(&opt.input)?;
    let schema = static_json()?;
    let compiled = JSONSchema::compile(&schema).map_err(|e| anyhow!(e.to_string()))?;
    check(&compiled, instance, opt.mode)?;
    output(&opt.input, opt.mode);
    Ok(())
}

fn check(schema: &JSONSchema, instance: Value, mode: Mode) -> Result<()> {
    let instance = match mode {
        Mode::AAS => instance,
        Mode::Submodel => {
            json!({
                "assetAdministrationShells": [],
                "submodels": [
                    instance
                ],
                "assets": [],
                "conceptDescriptions": []
            })
        }
    };
    let result = match schema.validate(&instance) {
        Ok(_) => Ok(()),
        Err(errors) => {
            let text = errors
                .into_iter()
                .map(|e| format!("{:#?}", e))
                .collect::<Vec<String>>()
                .join("\n");
            Err(color_eyre::eyre::anyhow!(text).wrap_err("Validation failed!"))
        }
    };
    result
}

fn read_json(path: &Path) -> Result<serde_json::Value> {
    let content =
        std::fs::read_to_string(path).wrap_err(format!("Opening file: {}", path.display()))?;
    Ok(serde_json::from_str(&content)?)
}

fn static_json() -> Result<serde_json::Value> {
    let content = include_str!("../../schema/aas.json");
    Ok(serde_json::from_str(content)?)
}

fn output(path: &Path, mode: Mode) {
    let t = match mode {
        Mode::AAS => "Asset Administration Shell",
        Mode::Submodel => "Submodel of an Asset Administration Shell",
    }
    .bold();
    let path = format!("{}", path.display()).bold();
    let text = format!("{} is a valid {}", path, t).green();
    println!("{}", text);
}