cargo_atelier 0.2.0

Cargo integration for the AWS Smithy IDL.
Documentation
use crate::{FileCommand, FileFormat, TransformCommand};
use atelier_lib::action::{standard_model_lint, standard_model_validation};
use atelier_lib::core::action::ActionIssue;
use atelier_lib::core::error::{Error as ModelError, ErrorKind, Result as ModelResult};
use atelier_lib::core::io::read_model_from_string;
use atelier_lib::core::io::ModelWriter;
use atelier_lib::core::model::{Model, NamespaceID};
use atelier_lib::format::json::{JsonReader, JsonWriter};
use atelier_lib::format::plant_uml::PlantUmlWriter;
use atelier_lib::format::smithy::{SmithyReader, SmithyWriter};
use std::error::Error;
use std::fs::File;
use std::io::{Read, Write};
use std::str::FromStr;

// ------------------------------------------------------------------------------------------------
// Public Functions
// ------------------------------------------------------------------------------------------------

pub fn lint_file(cmd: FileCommand) -> ModelResult<Vec<ActionIssue>> {
    check_file(cmd, &standard_model_lint)
}

pub fn validate_file(cmd: FileCommand) -> ModelResult<Vec<ActionIssue>> {
    check_file(cmd, &standard_model_validation)
}

pub fn convert_file_format(cmd: TransformCommand) -> Result<(), Box<dyn Error>> {
    transform_file(cmd, None)
}

// ------------------------------------------------------------------------------------------------
// Implementations
// ------------------------------------------------------------------------------------------------

pub fn check_file(
    cmd: FileCommand,
    check_fn: &dyn Fn(&Model, bool) -> ModelResult<Vec<ActionIssue>>,
) -> ModelResult<Vec<ActionIssue>> {
    fn read_json(content: Vec<u8>) -> Result<Model, ModelError> {
        read_model_from_string(&mut JsonReader::default(), content)
    }
    fn read_smithy(content: Vec<u8>) -> Result<Model, ModelError> {
        read_model_from_string(&mut SmithyReader::default(), content)
    }
    let err: ModelError = ErrorKind::InvalidRepresentation("read".to_string()).into();

    let reader = match cmd.input_file.format {
        FileFormat::Json => read_json,
        FileFormat::Smithy => read_smithy,
        _ => {
            return Err(err);
        }
    };
    let mut file: Box<dyn Read> = match cmd.input_file.file_name {
        None => Box::new(std::io::stdin()),
        Some(file_name) => Box::new(File::open(file_name)?),
    };
    let mut content: Vec<u8> = Vec::default();
    let _ = file.read_to_end(&mut content).unwrap();

    check_fn(&reader(content)?, false)
}

pub fn transform_file(
    cmd: TransformCommand,
    transform_fn: Option<&dyn Fn(Model) -> Result<Model, Box<dyn Error>>>,
) -> Result<(), Box<dyn Error>> {
    fn read_json(content: Vec<u8>) -> Result<Model, ModelError> {
        read_model_from_string(&mut JsonReader::default(), content)
    }
    fn read_smithy(content: Vec<u8>) -> Result<Model, ModelError> {
        read_model_from_string(&mut SmithyReader::default(), content)
    }
    fn write_json(w: &mut impl Write, model: Model) -> Result<(), Box<dyn Error>> {
        let mut writer = JsonWriter::new(true);
        writer.write(w, &model)?;
        Ok(())
    }
    fn write_smithy(
        w: &mut impl Write,
        model: Model,
        namespace: NamespaceID,
    ) -> Result<(), Box<dyn Error>> {
        let mut writer = SmithyWriter::new(namespace);
        writer.write(w, &model)?;
        Ok(())
    }
    fn write_uml(w: &mut impl Write, model: Model) -> Result<(), Box<dyn Error>> {
        let mut writer = PlantUmlWriter::default();
        writer.write(w, &model)?;
        Ok(())
    }
    let err: ModelError = ErrorKind::InvalidRepresentation("read".to_string()).into();

    let reader = match cmd.input_file.format {
        FileFormat::Json => read_json,
        FileFormat::Smithy => read_smithy,
        _ => {
            return Err(Box::new(err));
        }
    };
    let mut file: Box<dyn Read> = match cmd.input_file.file_name {
        None => Box::new(std::io::stdin()),
        Some(file_name) => Box::new(File::open(file_name)?),
    };
    let mut content: Vec<u8> = Vec::default();
    let _ = file.read_to_end(&mut content).unwrap();

    let model = reader(content)?;
    let model = if let Some(transform_fn) = transform_fn {
        transform_fn(model)?
    } else {
        model
    };

    let mut file: Box<dyn Write> = match cmd.output_file.file_name {
        None => Box::new(std::io::stdout()),
        Some(file_name) => Box::new(File::open(file_name)?),
    };

    match cmd.output_file.format {
        FileFormat::Json => write_json(&mut file, model),
        FileFormat::Smithy => write_smithy(
            &mut file,
            model,
            NamespaceID::from_str(&cmd.namespace.unwrap()).unwrap(),
        ),
        FileFormat::Uml => write_uml(&mut file, model),
    }
}