diff --git a/Cargo.lock b/Cargo.lock index df075e5f..f5815c2d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1703,7 +1703,7 @@ checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" [[package]] name = "dscp-lang" -version = "0.1.1" +version = "0.2.0" dependencies = [ "clap", "dscp-runtime-types", @@ -1713,14 +1713,16 @@ dependencies = [ "pest_derive", "serde", "serde_json", + "thiserror", ] [[package]] name = "dscp-node" -version = "9.1.2" +version = "9.1.3" dependencies = [ "bs58", "clap", + "dscp-lang", "dscp-node-runtime", "frame-benchmarking", "frame-benchmarking-cli", @@ -8714,18 +8716,18 @@ checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "thiserror" -version = "1.0.40" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.40" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ "proc-macro2", "quote", diff --git a/node/Cargo.toml b/node/Cargo.toml index 5d3010a7..2e91eca8 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -6,7 +6,7 @@ edition = '2021' license = 'Apache-2.0' repository = 'https://github.com/digicatapult/dscp-node/' name = 'dscp-node' -version = '9.1.2' +version = '9.1.3' [[bin]] name = 'dscp-node' @@ -59,6 +59,7 @@ frame-benchmarking-cli = { version = "4.0.0-dev", git = "https://github.com/pari # Local Dependencies dscp-node-runtime = { path = '../runtime' } pallet-transaction-payment-free = { default-features = false, path = '../pallets/transaction-payment-free' } +dscp-lang = { path = '../tools/lang' } # CLI-specific dependencies try-runtime-cli = { version = "0.10.0-dev", optional = true, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } diff --git a/node/src/cli.rs b/node/src/cli.rs index 72d44652..599f8db5 100644 --- a/node/src/cli.rs +++ b/node/src/cli.rs @@ -1,3 +1,4 @@ +use dscp_lang::cli::Cli as LangCli; use sc_cli::RunCmd; #[derive(Debug, clap::Parser)] @@ -43,4 +44,7 @@ pub enum Subcommand { /// Db meta columns information. ChainInfo(sc_cli::ChainInfoCmd), + + /// DSCP language tool for parsing and compiling dscp files. + Lang(LangCli), } diff --git a/node/src/command.rs b/node/src/command.rs index a8333b1b..e8c20b84 100644 --- a/node/src/command.rs +++ b/node/src/command.rs @@ -181,6 +181,9 @@ pub fn run() -> sc_cli::Result<()> { let runner = cli.create_runner(cmd)?; runner.sync_run(|config| cmd.run::(&config)) } + Some(Subcommand::Lang(cmd)) => cmd + .run() + .map_err(|lang_err| sc_cli::Error::Application(Box::new(lang_err))), None => { let runner = cli.create_runner(&cli.run)?; runner diff --git a/tools/lang/Cargo.toml b/tools/lang/Cargo.toml index 975ab296..24092d90 100644 --- a/tools/lang/Cargo.toml +++ b/tools/lang/Cargo.toml @@ -1,9 +1,17 @@ [package] name = "dscp-lang" authors = ['Digital Catapult '] -version = "0.1.1" +version = "0.2.0" edition = "2021" +[lib] +name = "dscp_lang" +path = "src/lib.rs" + +[[bin]] +name = "dscp-lang" +path = "src/main.rs" + [dependencies] clap = { version = "4.4.6", features = ["derive"] } exitcode = "1.1.2" @@ -13,3 +21,4 @@ pest_derive = "2.6" dscp-runtime-types = { path = '../../runtime/types' } serde = { version = "1.0.189" } serde_json = { version = "1.0.107" } +thiserror = { version = "1.0.50" } diff --git a/tools/lang/src/cli.rs b/tools/lang/src/cli.rs index 6d893fae..193f8f57 100644 --- a/tools/lang/src/cli.rs +++ b/tools/lang/src/cli.rs @@ -5,7 +5,7 @@ use clap::{Parser, Subcommand}; use crate::{ ast::{parse_str_to_ast, types::AstRoot}, compiler::compile_ast_to_restrictions, - convert::make_pretty_processes, + convert::transform_to_json, errors::CompilationError, }; @@ -13,13 +13,13 @@ use crate::{ #[derive(Debug, Parser)] // requires `derive` feature #[command(name = "dscp-lang", version, author)] #[command(about = "Tool for checking and compiling dscp token specifications", long_about = None)] -pub(crate) struct Cli { +pub struct Cli { #[command(subcommand)] command: Commands, } #[derive(Debug, Subcommand)] -enum Commands { +pub enum Commands { #[command(arg_required_else_help = true)] Parse { #[arg(help = "Path to dscp token specification file")] @@ -52,11 +52,11 @@ enum Commands { } impl Cli { - pub(crate) fn new() -> Self { + pub fn new() -> Self { Cli::parse() } - pub(crate) fn run(&self) -> Result<(), CompilationError> { + pub fn run(&self) -> Result<(), CompilationError> { match &self.command { Commands::Parse { file_path, verbose } => { println!("Loading file {}", file_path.to_str().unwrap()); @@ -119,17 +119,19 @@ impl Cli { let ast = parse_str_to_ast(&contents)?; let programs = compile_ast_to_restrictions(ast)?; - println!("Successfully parsed the following programs:"); - if *verbose { - for program in &programs { - println!("{}", String::from_utf8(program.name.to_vec()).unwrap()); - let program_str = serde_json::to_string(program).unwrap(); - println!("JSON: {}", program_str); + println!("Successfully compiled the following programs:"); + for program in &programs { + let program_name = String::from_utf8(program.name.to_vec()).unwrap(); + if *verbose { + let program_str = transform_to_json(program, false).unwrap(); + println!("\n{}:\n{}", program_name, program_str); + } else { + println!("\t{}", program_name); } } if let Some(path) = output_file { - fs::write(path, make_pretty_processes(&programs).unwrap()).unwrap() + fs::write(path, transform_to_json(&programs, true).unwrap()).unwrap() } Ok(()) diff --git a/tools/lang/src/convert.rs b/tools/lang/src/convert.rs index f74a754d..495c22be 100644 --- a/tools/lang/src/convert.rs +++ b/tools/lang/src/convert.rs @@ -1,9 +1,8 @@ use std::error::Error; +use serde::Serialize; use serde_json::Value; -use crate::compiler::Process; - fn transform_value(val: Value) -> Value { match val { Value::Array(arr) => { @@ -23,17 +22,23 @@ fn transform_value(val: Value) -> Value { } } -pub fn make_pretty_processes(processes: &Vec) -> Result> { - let serialised = serde_json::to_value(processes)?; +pub fn transform_to_json(val: &T, pretty: bool) -> Result> +where + T: Serialize, +{ + let serialised = serde_json::to_value(val)?; let transformed = transform_value(serialised); - Ok(serde_json::to_string_pretty(&transformed)?) + Ok(match pretty { + true => serde_json::to_string_pretty(&transformed), + false => serde_json::to_string(&transformed), + }?) } #[cfg(test)] mod tests { use dscp_runtime_types::BooleanExpressionSymbol; - use super::make_pretty_processes; + use super::transform_to_json; use crate::compiler::Process; #[test] @@ -47,7 +52,7 @@ mod tests { .try_into() .unwrap(), }]; - let result = make_pretty_processes(&processes); + let result = transform_to_json(&processes, true); assert!(result.is_ok()); assert_eq!( @@ -67,6 +72,26 @@ mod tests { ); } + #[test] + fn transforms_not_pretty() { + let processes = vec![Process { + name: vec![116u8, 101u8, 115u8, 116u8].try_into().unwrap(), + version: 1u32, + program: vec![BooleanExpressionSymbol::Restriction( + dscp_runtime_types::Restriction::None, + )] + .try_into() + .unwrap(), + }]; + let result = transform_to_json(&processes, false); + + assert!(result.is_ok()); + assert_eq!( + result.unwrap(), + r#"[{"name":"test","program":[{"Restriction":"None"}],"version":1}]"#.to_owned() + ); + } + #[test] fn transforms_name_multiple_process() { let processes = vec![ @@ -89,7 +114,7 @@ mod tests { .unwrap(), }, ]; - let result = make_pretty_processes(&processes); + let result = transform_to_json(&processes, true); assert!(result.is_ok()); assert_eq!( @@ -132,7 +157,7 @@ mod tests { .try_into() .unwrap(), }]; - let result = make_pretty_processes(&processes); + let result = transform_to_json(&processes, true); assert!(result.is_ok()); assert_eq!( diff --git a/tools/lang/src/errors.rs b/tools/lang/src/errors.rs index ba4c459d..3864e693 100644 --- a/tools/lang/src/errors.rs +++ b/tools/lang/src/errors.rs @@ -28,7 +28,7 @@ impl fmt::Display for CompilationStage { } } -#[derive(PartialEq)] +#[derive(PartialEq, thiserror::Error)] pub struct CompilationError { pub(crate) stage: CompilationStage, pub(crate) exit_code: i32, diff --git a/tools/lang/src/lib.rs b/tools/lang/src/lib.rs new file mode 100644 index 00000000..1852c4de --- /dev/null +++ b/tools/lang/src/lib.rs @@ -0,0 +1,7 @@ +pub mod cli; + +mod ast; +mod compiler; +mod convert; +mod errors; +mod parser; diff --git a/tools/lang/src/main.rs b/tools/lang/src/main.rs index 31ed6709..1bff4df3 100644 --- a/tools/lang/src/main.rs +++ b/tools/lang/src/main.rs @@ -1,10 +1,9 @@ -pub mod ast; -pub mod compiler; -pub mod errors; -pub mod parser; - +mod ast; mod cli; +mod compiler; mod convert; +mod errors; +mod parser; fn main() -> ! { let result = cli::Cli::new().run();