meplang 0.1.8

An EVM low-level language that gives full control over the control flow of the smart contract.
Documentation
use meplang::*;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;

const HELP_MESSAGE: &str = "\
Usage: meplang <COMMAND>\n\n\
Commands:\n\
\tcompile: Compile a Meplang file into EVM bytecode.\n\
\tversion: Print version information.\n\
";

fn main() {
    tracing_subscriber::registry()
        .with(tracing_subscriber::fmt::layer())
        .with(tracing_subscriber::filter::EnvFilter::from_default_env())
        .init();

    let mut args = std::env::args();
    args.next();

    let Some(mode) = args.next() else {
        println!("Meplang - An EVM low-level language.\n\n{}", HELP_MESSAGE);
        return;
    };

    match mode.as_str() {
        "version" => println!("Meplang version: {}", env!("CARGO_PKG_VERSION")),
        "compile" => {
            let mut contract = Option::<String>::None;
            let mut input_file = Option::<String>::None;
            let mut output_file = Option::<String>::None;
            let mut settings = Option::<CompilerSettings>::None;

            while let Some(arg) = args.next() {
                match arg.as_str() {
                    "-c" | "-contract" => {
                        let Some(next) = args.next() else {
                            tracing::error!("Expected an argument after `{}`.", arg);
                            return;
                        };
                        if contract.replace(next).is_some() {
                            tracing::error!("Contract name specified multiple times.");
                            return;
                        }
                    },
                    "-i" | "-input" => {
                        let Some(next) = args.next() else {
                            tracing::error!("Expected an argument after `{}`.", arg);
                            return;
                        };
                        if input_file.replace(next).is_some() {
                            tracing::error!("Input path specified multiple times.");
                            return;
                        }
                    },
                    "-o" | "-output" => {
                        let Some(next) = args.next() else {
                            tracing::error!("Expected an argument after `{}`.", arg);
                            return;
                        };
                        if output_file.replace(next).is_some() {
                            tracing::error!("Output path specified multiple times.");
                            return;
                        }
                    },
                    "-s" | "-settings" => {
                        let Some(next) = args.next() else {
                            tracing::error!("Expected an argument after `{}`.", arg);
                            return;
                        };
                        let decoded: CompilerSettings = match serde_json::from_str(&next) {
                            Ok(decoded) => decoded,
                            Err(err) => {
                                tracing::error!("Unable to decode compiler settings: {}", err);
                                return;
                            },
                        };
                        if settings.replace(decoded).is_some() {
                            tracing::error!("Compiler settings specified multiple times.");
                            return;
                        }
                    },
                    _ => {
                        tracing::error!("Unexpected argument `{}`.", &arg);
                        return;
                    },
                }
            }

            let Some(contract) = contract else {
                tracing::error!("Expected a contract name (-contract <CONTRACT_NAME>).");
                return;
            };

            let Some(input_file) = input_file else {
                tracing::error!("Expected an input file (-input <CONTRACT_NAME>).");
                return;
            };

            match compile_file(input_file.as_str(), contract.as_str(), settings.unwrap_or_default()) {
                Ok(artifacts) => {
                    if let Some(output_file) = output_file {
                        match std::fs::write(&output_file, serde_json::to_string_pretty(&artifacts).unwrap()) {
                            Ok(()) => {
                                println!(
                                    "Contract `{}` bytecode written in the file `{}`.",
                                    contract, output_file
                                );
                                return;
                            },
                            Err(err) => {
                                tracing::error!("{}", err);
                                return;
                            },
                        }
                    } else {
                        println!(
                            "Contract `{}` bytecode: {}",
                            contract,
                            format!("0x{}", hex::encode(artifacts.main_bytecode()))
                        );
                        return;
                    }
                },
                Err(err) => {
                    tracing::error!("{}", err);
                    return;
                },
            }
        },
        _ => tracing::error!("Unexpected command `{}`", mode),
    }
}