From 37ce9dafb3e83997806967c92a5accf9fd5e5b1e Mon Sep 17 00:00:00 2001 From: vms Date: Thu, 9 Jul 2020 20:27:50 +0300 Subject: [PATCH] improve error handling --- Cargo.lock | 38 ++++++++-- crates/wit-generator/src/errors.rs | 48 +++++++++++++ .../wit-generator/src/interface_generator.rs | 22 +++--- crates/wit-generator/src/lib.rs | 4 ++ examples/simple_greeting/Cargo.toml | 7 +- examples/simple_greeting/src/lib.rs | 31 ++------ examples/simple_greeting/src/mem.rs | 32 --------- examples/simple_greeting/src/result.rs | 30 -------- examples/simple_greeting/wit | 51 ------------- tools/cli/src/args.rs | 26 ++++--- tools/cli/src/build.rs | 50 +++++++++---- tools/cli/src/errors.rs | 71 +++++++++++++++++++ tools/cli/src/main.rs | 18 ++--- 13 files changed, 237 insertions(+), 191 deletions(-) create mode 100644 crates/wit-generator/src/errors.rs delete mode 100644 examples/simple_greeting/src/mem.rs delete mode 100644 examples/simple_greeting/src/result.rs delete mode 100644 examples/simple_greeting/wit create mode 100644 tools/cli/src/errors.rs diff --git a/Cargo.lock b/Cargo.lock index dd312989..faad3094 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -340,10 +340,6 @@ dependencies = [ "failure", ] -[[package]] -name = "export_test" -version = "0.1.0" - [[package]] name = "failure" version = "0.1.8" @@ -404,6 +400,15 @@ dependencies = [ "wit-parser", ] +[[package]] +name = "fluence" +version = "0.2.0" +source = "git+https://github.com/fluencelabs/rust-sdk#cc2b8249fc43d9667a4ec82a2e767f27df100c55" +dependencies = [ + "fluence-sdk-macro", + "fluence-sdk-main", +] + [[package]] name = "fluence-faas" version = "0.1.0" @@ -420,10 +425,26 @@ dependencies = [ "wasmer-wasi", ] +[[package]] +name = "fluence-sdk-macro" +version = "0.2.0" +source = "git+https://github.com/fluencelabs/rust-sdk#cc2b8249fc43d9667a4ec82a2e767f27df100c55" +dependencies = [ + "fluence-sdk-wit", +] + +[[package]] +name = "fluence-sdk-main" +version = "0.2.0" +source = "git+https://github.com/fluencelabs/rust-sdk#cc2b8249fc43d9667a4ec82a2e767f27df100c55" +dependencies = [ + "log", +] + [[package]] name = "fluence-sdk-wit" version = "0.2.0" -source = "git+https://github.com/fluencelabs/rust-sdk#d5d1207a3db623876bdd6697ca9b1171b46f0813" +source = "git+https://github.com/fluencelabs/rust-sdk#cc2b8249fc43d9667a4ec82a2e767f27df100c55" dependencies = [ "proc-macro2", "quote", @@ -496,6 +517,13 @@ version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724" +[[package]] +name = "greeting" +version = "0.1.0" +dependencies = [ + "fluence", +] + [[package]] name = "heck" version = "0.3.1" diff --git a/crates/wit-generator/src/errors.rs b/crates/wit-generator/src/errors.rs new file mode 100644 index 00000000..f8e9bcba --- /dev/null +++ b/crates/wit-generator/src/errors.rs @@ -0,0 +1,48 @@ +/* + * Copyright 2020 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use std::error::Error; +use serde_json::Error as SerdeDeserializationError; + +#[derive(Debug)] +pub enum WITGeneratorError { + /// An error related to serde deserialization. + DeserializationError(SerdeDeserializationError), + + /// Various errors occurred during the parsing/emitting a Wasm file. + IOError(String), +} + +impl Error for WITGeneratorError {} + +impl std::fmt::Display for WITGeneratorError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + match self { + WITGeneratorError::DeserializationError(err) => write!( + f, + "Embedded by rust-sdk metadata could't be parsed by serde: {:?}", + err + ), + WITGeneratorError::IOError(err) => write!(f, "I/O error occurred: {:?}", err), + } + } +} + +impl From for WITGeneratorError { + fn from(err: SerdeDeserializationError) -> Self { + WITGeneratorError::DeserializationError(err) + } +} diff --git a/crates/wit-generator/src/interface_generator.rs b/crates/wit-generator/src/interface_generator.rs index 1d4587c5..38f62fbe 100644 --- a/crates/wit-generator/src/interface_generator.rs +++ b/crates/wit-generator/src/interface_generator.rs @@ -14,27 +14,31 @@ * limitations under the License. */ -use crate::instructions_generator::WITGenerator; use crate::default_export_api_config::*; +use crate::errors::WITGeneratorError; +use crate::instructions_generator::WITGenerator; +use crate::Result; pub use fluence_sdk_wit::FCEAst; use wasmer_wit::ast::Interfaces; -// TODO: add error handling /// Parse generated by rust-sdk AST types, generate instructions and embed them to Wasm file. -pub fn embed_wit(path: std::path::PathBuf) { +pub fn embed_wit(path: std::path::PathBuf) -> Result<()> { let wasm_module = walrus::ModuleConfig::new() .parse_file(path.clone()) - .unwrap(); - let ast_set = wasm_ast_extractor(&wasm_module); + .map_err(|e| WITGeneratorError::IOError(format!("{:?} can't be parsed: {:?}", path, e)))?; + + let ast_set = wasm_ast_extractor(&wasm_module)?; let interfaces = generate_interfaces(&ast_set); let mut wasm_module = wit_parser::embed_wit(wasm_module, &interfaces); - wasm_module.emit_wasm_file(path).unwrap(); + wasm_module.emit_wasm_file(path).map_err(|e| { + WITGeneratorError::IOError(format!("resulted Wasm fule can't be emitted: {:?}", e)) + }) } /// Extract all custom AST types previously embedded by rust-sdk from compiled binary. -fn wasm_ast_extractor(wasm_module: &walrus::Module) -> Vec { +fn wasm_ast_extractor(wasm_module: &walrus::Module) -> Result> { let mut extracted_ast = Vec::new(); // consider only sections name of that starts with GENERATED_SECTION_PREFIX @@ -45,11 +49,11 @@ fn wasm_ast_extractor(wasm_module: &walrus::Module) -> Vec Interfaces<'_> { diff --git a/crates/wit-generator/src/lib.rs b/crates/wit-generator/src/lib.rs index 2cfae658..31b6df52 100644 --- a/crates/wit-generator/src/lib.rs +++ b/crates/wit-generator/src/lib.rs @@ -15,7 +15,11 @@ */ mod default_export_api_config; +mod errors; mod instructions_generator; mod interface_generator; pub use interface_generator::embed_wit; +pub use errors::WITGeneratorError; + +pub(crate) type Result = std::result::Result; diff --git a/examples/simple_greeting/Cargo.toml b/examples/simple_greeting/Cargo.toml index c813af87..92cf1600 100644 --- a/examples/simple_greeting/Cargo.toml +++ b/examples/simple_greeting/Cargo.toml @@ -1,10 +1,13 @@ [package] -name = "export_test" +name = "greeting" version = "0.1.0" authors = ["Fluence Labs"] edition = "2018" [lib] -name = "export_test" +name = "greeting" path = "src/lib.rs" crate-type = ["cdylib"] + +[dependencies] +fluence = { git = "https://github.com/fluencelabs/rust-sdk" } diff --git a/examples/simple_greeting/src/lib.rs b/examples/simple_greeting/src/lib.rs index 9646e714..571c1b27 100644 --- a/examples/simple_greeting/src/lib.rs +++ b/examples/simple_greeting/src/lib.rs @@ -13,33 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#![allow(clippy::missing_safety_doc)] -mod mem; -mod result; +use fluence::fce; -use crate::result::{RESULT_PTR, RESULT_SIZE}; - -#[no_mangle] -pub unsafe fn strlen(str_ptr: *mut u8, str_len: usize) -> usize { - let user_name = String::from_raw_parts(str_ptr, str_len, str_len); - user_name.len() -} - -#[no_mangle] -pub unsafe fn greeting(user_name_ptr: *mut u8, user_name_size: usize) { - let user_name = String::from_raw_parts(user_name_ptr, user_name_size, user_name_size); - let user_name = format!("Hi, {}\n", user_name); - - log_utf8_string(user_name.as_ptr() as i32, user_name.len() as i32); - - *RESULT_PTR.get_mut() = user_name.as_ptr() as _; - *RESULT_SIZE.get_mut() = user_name.len(); - std::mem::forget(user_name); -} - -#[link(wasm_import_module = "logger")] -extern "C" { - // Writes a byte string of size bytes that starts from ptr to a logger - fn log_utf8_string(ptr: i32, size: i32); +#[fce] +pub fn greeting(name: String) -> String { + format!("Hi, {}\n", name) } diff --git a/examples/simple_greeting/src/mem.rs b/examples/simple_greeting/src/mem.rs deleted file mode 100644 index 4dd7abd8..00000000 --- a/examples/simple_greeting/src/mem.rs +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2020 Fluence Labs Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -use std::alloc::{alloc as global_alloc, dealloc as global_dealloc, Layout}; -use std::ptr::NonNull; - -/// Allocates memory area of specified size and returns its address. -#[no_mangle] -pub unsafe fn allocate(size: usize) -> NonNull { - let layout: Layout = Layout::from_size_align(size, std::mem::align_of::()).unwrap(); - NonNull::new_unchecked(global_alloc(layout)) -} - -/// Deallocates memory area for provided memory pointer and size. -#[no_mangle] -pub unsafe fn deallocate(ptr: NonNull, size: usize) { - let layout = Layout::from_size_align(size, std::mem::align_of::()).unwrap(); - global_dealloc(ptr.as_ptr(), layout); -} diff --git a/examples/simple_greeting/src/result.rs b/examples/simple_greeting/src/result.rs deleted file mode 100644 index 57ba678c..00000000 --- a/examples/simple_greeting/src/result.rs +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2020 Fluence Labs Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -use std::sync::atomic::AtomicUsize; - -pub static mut RESULT_PTR: AtomicUsize = AtomicUsize::new(0); -pub static mut RESULT_SIZE: AtomicUsize = AtomicUsize::new(0); - -#[no_mangle] -pub unsafe fn get_result_ptr() -> usize { - *RESULT_PTR.get_mut() -} - -#[no_mangle] -pub unsafe fn get_result_size() -> usize { - *RESULT_SIZE.get_mut() -} diff --git a/examples/simple_greeting/wit b/examples/simple_greeting/wit deleted file mode 100644 index daf26514..00000000 --- a/examples/simple_greeting/wit +++ /dev/null @@ -1,51 +0,0 @@ -;; allocate function -(@interface type (func (param i32) (result i32))) - -;; deallocate function -(@interface type (func (param i32 i32))) - -;; greeting function -(@interface type (func (param string) (result string))) - -;; strlen function -(@interface type (func (param string) (result i32))) - -;; result extractor functions -(@interface type (func (result i32))) - -(@interface export "allocate" (func 0)) -(@interface export "deallocate" (func 1)) -(@interface export "greeting" (func 2)) -(@interface export "strlen" (func 3)) -(@interface export "get_result_size" (func 4)) -(@interface export "get_result_ptr" (func 4)) - -;; greeting export adapter -(@interface func (type 2) - arg.get 0 - string.size - call-core 0 ;; call allocate - arg.get 0 - string.lower_memory - call-core 2 ;; call greeting - call-core 5 ;; call get_result_ptr - call-core 4 ;; call get_result_size - string.lift_memory - call-core 5 ;; call get_result_ptr - call-core 4 ;; call get_result_size - call-core 1 ;; call deallocate -) - -;; strlen export adapter -(@interface func (type 3) - arg.get 0 - string.size - call-core 0 ;; call allocate - arg.get 0 - string.lower_memory - call-core 3 ;; call strlen -) - -;; Implementations -(@interface implement (func 2) (func 2)) -(@interface implement (func 3) (func 3)) diff --git a/tools/cli/src/args.rs b/tools/cli/src/args.rs index ef11cf77..36cca3aa 100644 --- a/tools/cli/src/args.rs +++ b/tools/cli/src/args.rs @@ -1,3 +1,19 @@ +/* + * Copyright 2020 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + use clap::{App, Arg, SubCommand}; pub const VERSION: &str = env!("CARGO_PKG_VERSION"); @@ -24,13 +40,3 @@ pub fn show_wit<'a, 'b>() -> App<'a, 'b> { .short("i") .help("path to the Wasm file")]) } - -pub fn validate<'a, 'b>() -> App<'a, 'b> { - SubCommand::with_name("validate") - .about("validates provided Wasm file to suite Fluence network requirements") - .args(&[Arg::with_name(IN_WASM_PATH) - .required(true) - .takes_value(true) - .short("i") - .help("path to the wasm file")]) -} diff --git a/tools/cli/src/build.rs b/tools/cli/src/build.rs index 7c99e278..7300d4ba 100644 --- a/tools/cli/src/build.rs +++ b/tools/cli/src/build.rs @@ -14,6 +14,9 @@ * limitations under the License. */ +use crate::Result; +use crate::errors::CLIError; + use std::path::PathBuf; use std::process::Command; @@ -22,12 +25,11 @@ use std::process::Command; enum DiagnosticMessage { BuildScriptExecuted, BuildFinished, - CompilerArtifact { executable: String }, + CompilerArtifact { filenames: Vec }, RunWithArgs, } -// TODO: add error handling -pub(crate) fn build(manifest_path: Option) { +pub(crate) fn build(manifest_path: Option) -> Result<()> { use std::io::Read; let mut cargo = Command::new("cargo"); @@ -37,25 +39,45 @@ pub(crate) fn build(manifest_path: Option) { cargo.arg("--manifest-path").arg(wasm_path); } - let mut process = cargo.stdout(std::process::Stdio::piped()).spawn().unwrap(); + let mut process = cargo.stdout(std::process::Stdio::piped()).spawn()?; let mut output = String::new(); - process .stdout .take() - .unwrap() - .read_to_string(&mut output) - .unwrap(); - let _status = process.wait().unwrap(); + .ok_or_else(|| CLIError::WasmCompilationError("Compilation failed: no output".to_string()))? + .read_to_string(&mut output)?; - let mut wasms = Vec::new(); + let status = process.wait()?; + if !status.success() { + return Err(CLIError::WasmCompilationError(format!( + "Compilation failed with status {}", + status + ))); + } + + let mut wasms: Vec = Vec::new(); for line in output.lines() { - if let Ok(DiagnosticMessage::CompilerArtifact { executable }) = serde_json::from_str(line) { - wasms.push(executable) + if let Ok(DiagnosticMessage::CompilerArtifact { filenames }) = serde_json::from_str(line) { + wasms.extend( + filenames + .into_iter() + .filter(|name| name.ends_with(".wasm")) + .collect::>(), + ) } } - let wasm_path = std::path::PathBuf::from(wasms.first().unwrap()); - wit_generator::embed_wit(wasm_path); + if wasms.is_empty() { + return Err(CLIError::WasmCompilationError( + "Compilation failed: no .wasm files was generated".to_string(), + )); + } + + for wasm in wasms { + let wasm_path = std::path::PathBuf::from(wasm); + wit_generator::embed_wit(wasm_path)?; + } + + Ok(()) } diff --git a/tools/cli/src/errors.rs b/tools/cli/src/errors.rs new file mode 100644 index 00000000..8eb9c6b0 --- /dev/null +++ b/tools/cli/src/errors.rs @@ -0,0 +1,71 @@ +/* + * Copyright 2020 Fluence Labs Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use wit_generator::WITGeneratorError; +use wit_parser::WITParserError; + +use std::io::Error as StdIOError; +use std::error::Error; + +#[derive(Debug)] +pub enum CLIError { + /// Unknown command was entered by user. + NoSuchCommand(String), + + /// An error occurred while generating interface types. + WITGeneratorError(WITGeneratorError), + + /// An error occurred while parsing interface types. + WITParserError(WITParserError), + + /// An error occurred when no Wasm file was compiled. + WasmCompilationError(String), + + /// Various errors related to I/O operations. + IOError(StdIOError), +} + +impl Error for CLIError {} + +impl std::fmt::Display for CLIError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + match self { + CLIError::NoSuchCommand(cmd) => write!(f, "{} is unknown command", cmd), + CLIError::WITGeneratorError(err) => write!(f, "{}", err), + CLIError::WITParserError(err) => write!(f, "{}", err), + CLIError::WasmCompilationError(err) => write!(f, "{}", err), + CLIError::IOError(err) => write!(f, "{:?}", err), + } + } +} + +impl From for CLIError { + fn from(err: WITGeneratorError) -> Self { + CLIError::WITGeneratorError(err) + } +} + +impl From for CLIError { + fn from(err: WITParserError) -> Self { + CLIError::WITParserError(err) + } +} + +impl From for CLIError { + fn from(err: StdIOError) -> Self { + CLIError::IOError(err) + } +} diff --git a/tools/cli/src/main.rs b/tools/cli/src/main.rs index eb0de8c0..e77dc56b 100644 --- a/tools/cli/src/main.rs +++ b/tools/cli/src/main.rs @@ -27,28 +27,29 @@ mod args; mod build; +mod errors; use clap::App; use clap::AppSettings; -pub fn main() -> Result<(), exitfailure::ExitFailure> { +pub(crate) type Result = std::result::Result; + +pub fn main() -> Result<()> { let app = App::new("CLI tool for embedding WIT to provided Wasm file") .version(args::VERSION) .author(args::AUTHORS) .about(args::DESCRIPTION) .setting(AppSettings::ArgRequiredElseHelp) .subcommand(args::build()) - .subcommand(args::show_wit()) - .subcommand(args::validate()); + .subcommand(args::show_wit()); match app.get_matches().subcommand() { ("build", Some(arg)) => { let manifest_path = arg .value_of(args::IN_WASM_PATH) .map(std::path::PathBuf::from); - crate::build::build(manifest_path); - Ok(()) + crate::build::build(manifest_path) } ("show", Some(arg)) => { let wasm_path = arg.value_of(args::IN_WASM_PATH).unwrap(); @@ -59,11 +60,6 @@ pub fn main() -> Result<(), exitfailure::ExitFailure> { Ok(()) } - ("validate", Some(arg)) => { - let _wasm_path = arg.value_of(args::IN_WASM_PATH).unwrap(); - - Ok(()) - } - c => Err(failure::err_msg(format!("Unexpected command: {}", c.0)).into()), + c => Err(crate::errors::CLIError::NoSuchCommand(c.0.to_string())), } }