From 0c341fef817c5c16536dcc39fc1dd010480e51a5 Mon Sep 17 00:00:00 2001 From: Jeremy HERGAULT Date: Mon, 30 Mar 2026 16:31:08 +0200 Subject: [PATCH] fix: ban all unwrap to improve error handling Signed-off-by: Jeremy HERGAULT --- Cargo.toml | 4 +- cargo-prosa/Cargo.toml | 5 +- cargo-prosa/assets/build.rs.j2 | 5 +- cargo-prosa/assets/main.rs.j2 | 2 +- cargo-prosa/src/builder.rs | 11 +- cargo-prosa/src/cargo.rs | 6 +- cargo-prosa/src/main.rs | 12 +- cargo-prosa/src/package/container.rs | 18 +-- cargo-prosa/src/package/install.rs | 4 +- cargo-prosa/tests/cargo-prosa.rs | 15 ++- prosa/Cargo.toml | 5 +- prosa/examples/proc.rs | 13 +- prosa/src/core/msg.rs | 8 +- prosa/src/core/proc.rs | 23 ++-- prosa/src/event/pending.rs | 10 +- prosa/src/event/queue/mpsc.rs | 12 +- prosa/src/event/queue/timed.rs | 2 +- prosa/src/inj/proc.rs | 24 ++-- prosa/src/io.rs | 160 +++++++++++------------ prosa/src/io/stream.rs | 23 ++-- prosa/src/lib.rs | 12 +- prosa_macros/Cargo.toml | 5 +- prosa_macros/src/io.rs | 27 ++-- prosa_macros/src/proc.rs | 26 ++-- prosa_macros/src/settings.rs | 89 +++++-------- prosa_macros/tests/tvf.rs | 44 ++++--- prosa_utils/Cargo.toml | 5 +- prosa_utils/build.rs | 2 +- prosa_utils/src/config.rs | 20 ++- prosa_utils/src/config/observability.rs | 2 +- prosa_utils/src/config/ssl.rs | 10 +- prosa_utils/src/config/ssl/openssl.rs | 32 +++-- prosa_utils/src/config/tracing.rs | 7 +- prosa_utils/src/msg/simple_string_tvf.rs | 55 ++++---- 34 files changed, 372 insertions(+), 326 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e91ea27..e2b72d0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,8 +22,8 @@ include = [ ] [workspace.dependencies] -prosa-utils = { version = "0.4.2", path = "prosa_utils" } -prosa-macros = { version = "0.4.1", path = "prosa_macros" } +prosa-utils = { version = "0.4.3", path = "prosa_utils" } +prosa-macros = { version = "0.4.2", path = "prosa_macros" } thiserror = "2" aquamarine = "0.6" bytes = "1" diff --git a/cargo-prosa/Cargo.toml b/cargo-prosa/Cargo.toml index 3d962e9..faa198a 100644 --- a/cargo-prosa/Cargo.toml +++ b/cargo-prosa/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cargo-prosa" -version = "0.4.1" +version = "0.4.2" authors.workspace = true description = "ProSA utility to package and deliver a builded ProSA" homepage.workspace = true @@ -13,6 +13,9 @@ include.workspace = true name = "cargo-prosa" path = "src/main.rs" +[lints.clippy] +unwrap_used = "deny" + [dependencies] aquamarine.workspace = true clap = "4" diff --git a/cargo-prosa/assets/build.rs.j2 b/cargo-prosa/assets/build.rs.j2 index 131ba42..b445cde 100644 --- a/cargo-prosa/assets/build.rs.j2 +++ b/cargo-prosa/assets/build.rs.j2 @@ -117,7 +117,7 @@ fn write_run_rs(out_dir: &OsString, desc: &Desc, metadata: &HashMap<&str, Metada writeln!(f, "{{ '}}' }}")?; writeln!(f, "\n/// Method to run all configured processors, return the number of processors runned")?; - writeln!(f, "fn run_processors(bus: prosa::core::main::Main<{{ '{}' }}>, settings: &RunSettings) {{ '{{' }}", desc.prosa.tvf)?; + writeln!(f, "fn run_processors(bus: prosa::core::main::Main<{{ '{}' }}>, settings: &RunSettings) -> Result<(), std::io::Error> {{ '{{' }}", desc.prosa.tvf)?; let mut proc_id = 0u32; if let Some(processors) = &desc.proc {{ '{' }} @@ -131,10 +131,11 @@ fn write_run_rs(out_dir: &OsString, desc: &Desc, metadata: &HashMap<&str, Metada writeln!(f, " let proc = {{ '{}' }}::<{{ '{}' }}>::create_raw({{ '{}' }}, \"{{ '{}' }}\".to_string(), bus.clone());", processor.proc, desc.prosa.tvf, proc_id, processor.get_name())?; {{ '}' }} - writeln!(f, " prosa::core::proc::Proc::<{{ '{}' }}>::run(proc);", processor.adaptor)?; + writeln!(f, " prosa::core::proc::Proc::<{{ '{}' }}>::run(proc)?;", processor.adaptor)?; {{ '}' }} {{ '}' }} + writeln!(f, " Ok(())")?; writeln!(f, "{{ '}}' }}")?; writeln!(f, "\n/// Number of configured processor")?; writeln!(f, "#[allow(dead_code)]")?; diff --git a/cargo-prosa/assets/main.rs.j2 b/cargo-prosa/assets/main.rs.j2 index a748cfb..d7c8454 100644 --- a/cargo-prosa/assets/main.rs.j2 +++ b/cargo-prosa/assets/main.rs.j2 @@ -75,7 +75,7 @@ async fn prosa_main(matches: clap::ArgMatches) -> Result<(), Box io::Result<()> { let mut prosa_desc = Desc::default(); prosa_desc.add_proc(ProcDesc::new( "proc".into(), @@ -249,14 +249,15 @@ proc_name = \"proc\" proc = \"crate::proc\" adaptor = \"crate::adaptor\" "; - assert_eq!(prosa_toml, toml::to_string(&prosa_desc).unwrap()); + assert_eq!(Ok(prosa_toml.to_string()), toml::to_string(&prosa_desc)); // FIXME use environment variable when they will be available for unit tests let toml_path_file = Path::new("/tmp/test_prosa_desc.toml"); - let mut toml_file = fs::File::create(toml_path_file).unwrap(); - toml_file.write_all(prosa_toml.as_bytes()).unwrap(); + let mut toml_file = fs::File::create(toml_path_file)?; + toml_file.write_all(prosa_toml.as_bytes())?; - let prosa_desc_from_file = Desc::read(toml_path_file).unwrap(); + let prosa_desc_from_file = Desc::read(toml_path_file)?; assert_eq!(prosa_desc, prosa_desc_from_file); + Ok(()) } } diff --git a/cargo-prosa/src/cargo.rs b/cargo-prosa/src/cargo.rs index e36a614..59e68af 100644 --- a/cargo-prosa/src/cargo.rs +++ b/cargo-prosa/src/cargo.rs @@ -495,8 +495,10 @@ impl CargoMetadata { if cargo_metadata.status.success() { let mut metadata: CargoMetadata = serde_json::from_slice(cargo_metadata.stdout.as_slice())?; - if metadata.packages.len() == 1 { - Ok(metadata.packages.pop().unwrap()) + if let Some(package) = metadata.packages.pop() + && metadata.packages.is_empty() + { + Ok(package) } else { Err(io::Error::new( io::ErrorKind::InvalidData, diff --git a/cargo-prosa/src/main.rs b/cargo-prosa/src/main.rs index 7dd91ca..3185689 100755 --- a/cargo-prosa/src/main.rs +++ b/cargo-prosa/src/main.rs @@ -347,8 +347,8 @@ fn main() -> Result<(), Box> { // Create the new Rust project let cargo_new = std::process::Command::new("cargo").args(args).output()?; - io::stdout().write_all(&cargo_new.stdout).unwrap(); - io::stderr().write_all(&cargo_new.stderr).unwrap(); + io::stdout().write_all(&cargo_new.stdout)?; + io::stderr().write_all(&cargo_new.stderr)?; if cargo_new.status.success() { init_prosa(path, &j2_context)?; @@ -363,8 +363,8 @@ fn main() -> Result<(), Box> { args.push("--name"); args.push(name); j2_context.insert("name", name); - } else if let Some(name) = path.file_name() { - j2_context.insert("name", &tera::Value::String(name.to_str().unwrap().into())); + } else if let Some(name) = path.file_name().and_then(|n| n.to_str()) { + j2_context.insert("name", &tera::Value::String(name.into())); } j2_context.insert("deb_pkg", &matches.get_flag("deb")); @@ -376,8 +376,8 @@ fn main() -> Result<(), Box> { // Init the Rust project let cargo_init = std::process::Command::new("cargo").args(args).output()?; - io::stdout().write_all(&cargo_init.stdout).unwrap(); - io::stderr().write_all(&cargo_init.stderr).unwrap(); + io::stdout().write_all(&cargo_init.stdout)?; + io::stderr().write_all(&cargo_init.stderr)?; if cargo_init.status.success() { init_prosa(path_name, &j2_context)?; diff --git a/cargo-prosa/src/package/container.rs b/cargo-prosa/src/package/container.rs index d775d77..e44be10 100644 --- a/cargo-prosa/src/package/container.rs +++ b/cargo-prosa/src/package/container.rs @@ -86,11 +86,14 @@ impl ContainerFile { impl fmt::Display for ContainerFile { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let img_name = format!( - "{}:{}", - self.ctx.get("name").unwrap().as_str().unwrap(), - self.ctx.get("version").unwrap().as_str().unwrap() - ); + let img_name = if let Some(name) = self.ctx.get("name").and_then(|v| v.as_str()) + && let Some(version) = self.ctx.get("version").and_then(|v| v.as_str()) + { + format!("{name}:{version}") + } else { + "prosa:1.0.0".to_string() + }; + writeln!(f, "To build your container, use the command:")?; if self.is_docker { write!(f, " `docker build")?; @@ -116,9 +119,8 @@ impl fmt::Display for ContainerFile { } else if self.path.is_some() { writeln!( f, - " `podman build -f {} -t {} .`", - self.get_path().display(), - img_name + " `podman build -f {} -t {img_name} .`", + self.get_path().display() ) } else { writeln!(f, " `podman build -t {img_name} .`") diff --git a/cargo-prosa/src/package/install.rs b/cargo-prosa/src/package/install.rs index b542e69..36eec42 100644 --- a/cargo-prosa/src/package/install.rs +++ b/cargo-prosa/src/package/install.rs @@ -185,8 +185,8 @@ impl InstanceInstall { let cargo_build = std::process::Command::new("cargo") .args(build_args) .output()?; - io::stdout().write_all(&cargo_build.stdout).unwrap(); - io::stderr().write_all(&cargo_build.stderr).unwrap(); + io::stdout().write_all(&cargo_build.stdout)?; + io::stderr().write_all(&cargo_build.stderr)?; if !cargo_build.status.success() { return Err(io::Error::new( diff --git a/cargo-prosa/tests/cargo-prosa.rs b/cargo-prosa/tests/cargo-prosa.rs index 49e8b4a..1e80c7e 100644 --- a/cargo-prosa/tests/cargo-prosa.rs +++ b/cargo-prosa/tests/cargo-prosa.rs @@ -27,7 +27,11 @@ fn replace_prosa_dependencies(prosa_path: &PathBuf) { if let Some(opt) = build_opt { cmd.args(opt); } - cmd.args(["--path", test_prosa_dep.to_str().unwrap(), prosa_dep]); + cmd.args([ + "--path", + test_prosa_dep.to_str().expect("PathBuf should be string"), + prosa_dep, + ]); cmd.assert().success(); } } @@ -175,7 +179,12 @@ Package prosa-utils\[[0-9].[0-9].[0-9]\] \(ProSA utils\) assert!(!containerfile_path.exists() && !dockerfile_path.exists()); let mut cmd = cargo_prosa_command(); cmd.current_dir(&prosa_path); - cmd.args(["container", containerfile_path.to_str().unwrap()]); + cmd.args([ + "container", + containerfile_path + .to_str() + .expect("PathBuf should be string"), + ]); cmd.assert().success().stdout(predicate::str::is_match( r"To build your container, use the command: `podman build -f .*/dummy-test-prosa/Containerfile -t dummy-test-prosa:0\.1\.0 \.`", @@ -187,7 +196,7 @@ Package prosa-utils\[[0-9].[0-9].[0-9]\] \(ProSA utils\) "container", "--docker", "-b rust-latest", - dockerfile_path.to_str().unwrap(), + dockerfile_path.to_str().expect("PathBuf should be string"), ]); cmd.assert().success().stdout(predicate::str::is_match( r"To build your container, use the command: diff --git a/prosa/Cargo.toml b/prosa/Cargo.toml index 69a3ac0..3249dfe 100644 --- a/prosa/Cargo.toml +++ b/prosa/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "prosa" -version = "0.4.2" +version = "0.4.3" authors.workspace = true description = "ProSA core" homepage.workspace = true @@ -39,6 +39,9 @@ proc = "stub::proc::StubProc" settings = "stub::proc::StubSettings" adaptor = ["stub::adaptor::StubParotAdaptor"] +[lints.clippy] +unwrap_used = "deny" + [dependencies] prosa-utils = { workspace = true, features = ["msg", "config", "config-observability"] } prosa-macros.workspace = true diff --git a/prosa/examples/proc.rs b/prosa/examples/proc.rs index 47dd0c2..c65caf4 100644 --- a/prosa/examples/proc.rs +++ b/prosa/examples/proc.rs @@ -78,13 +78,13 @@ where let stub_service_name = String::from("STUB_TEST"); if let Some(service) = self.service.get_proc_service(&stub_service_name) { debug!("The service is find: {:?}", service); - service.proc_queue.send(InternalMsg::Request(RequestMsg::new(stub_service_name, tvf.clone(), self.proc.get_service_queue()))).await.unwrap(); + let _ = service.proc_queue.send(InternalMsg::Request(RequestMsg::new(stub_service_name, tvf.clone(), self.proc.get_service_queue()))).await; } let proc_service_name = String::from("PROC_TEST"); if let Some(service) = self.service.get_proc_service(&proc_service_name) { debug!("The service is find: {:?}", service); - service.proc_queue.send(InternalMsg::Request(RequestMsg::new(proc_service_name, tvf, self.proc.get_service_queue()))).await.unwrap(); + let _ = service.proc_queue.send(InternalMsg::Request(RequestMsg::new(proc_service_name, tvf, self.proc.get_service_queue()))).await; } }, Some(msg) = pending_msgs.pull(), if !pending_msgs.is_empty() => { @@ -116,8 +116,7 @@ async fn main() -> Result<(), Box> { let config = Config::builder() .add_source(config::File::with_name("examples/my_prosa_settings.yml")) .add_source(config::Environment::with_prefix("PROSA")) - .build() - .unwrap(); + .build()?; let my_settings = config.try_deserialize::()?; println!("My ProSA settings: {my_settings:?}"); @@ -139,18 +138,18 @@ async fn main() -> Result<(), Box> { bus.clone(), stub_settings, ); - Proc::::run(stub_proc); + Proc::::run(stub_proc)?; // Launch the test processor let proc = MyProcClass::::create_raw(2, String::from("proc_1"), bus.clone()); - Proc::::run(proc); + Proc::::run(proc)?; // Wait before launch the second processor std::thread::sleep(time::Duration::from_secs(2)); // Launch the second test processor let proc2 = MyProcClass::::create_raw(3, String::from("proc_2"), bus.clone()); - Proc::::run(proc2); + Proc::::run(proc2)?; // Wait on main task main.run().await; diff --git a/prosa/src/core/msg.rs b/prosa/src/core/msg.rs index 0786d64..c6b9641 100644 --- a/prosa/src/core/msg.rs +++ b/prosa/src/core/msg.rs @@ -243,10 +243,12 @@ where .send(InternalMsg::Response(ResponseMsg::from_request(self, resp))) .map_err(|e| { e.map(|i| { - if let InternalMsg::Response(mut resp) = i { - resp.take_data().unwrap() + if let InternalMsg::Response(mut resp) = i + && let Some(data) = resp.take_data() + { + data } else { - panic!("Expected InternalMsg::Response") + panic!("Expected InternalMsg::Response with data") } }) }) diff --git a/prosa/src/core/proc.rs b/prosa/src/core/proc.rs index 30cad38..dd91262 100644 --- a/prosa/src/core/proc.rs +++ b/prosa/src/core/proc.rs @@ -142,6 +142,7 @@ use glob::glob; use log::{error, info, warn}; use std::borrow::Cow; use std::fmt::Debug; +use std::io; use std::time::Duration; use tokio::sync::mpsc; use tokio::time::sleep; @@ -191,8 +192,12 @@ pub trait ProcSettings { Config::builder() .add_source( glob(config_path) - .unwrap() - .map(|path| File::from(path.unwrap())) + .map_err(|e| { + ConfigError::Message(format!( + "Wrong adaptor config path pattern `{config_path}`: `{e}`" + )) + })? + .filter_map(|path| path.ok().map(File::from)) .collect::>(), ) .build()? @@ -586,7 +591,7 @@ where /// Proc::::run(proc); /// } /// ``` - fn run(mut self) + fn run(mut self) -> Result<(), io::Error> where Self: Sized + 'static + std::marker::Send, { @@ -606,12 +611,11 @@ where .enable_all() .thread_name(self.name()) .build() - .unwrap() + .expect("Tokio single Runtime can't be build") .block_on(async { proc_run!(self); }) - }) - .unwrap(); + })?; } // Start a Tokio runtime on multiple threads n => { @@ -623,14 +627,15 @@ where .enable_all() .thread_name(self.name()) .build() - .unwrap() + .expect("Tokio Runtime can't be build") .block_on(async { proc_run!(self); }) - }) - .unwrap(); + })?; } } + + Ok(()) } } diff --git a/prosa/src/event/pending.rs b/prosa/src/event/pending.rs index 181d2b1..7e71f74 100644 --- a/prosa/src/event/pending.rs +++ b/prosa/src/event/pending.rs @@ -385,7 +385,7 @@ mod tests { }, InternalMsg::Service(table) => { if let Some(service) = table.get_proc_service("TEST") { - service.proc_queue.send(InternalMsg::Request(RequestMsg::new(String::from("TEST"), Default::default(), self.proc.get_service_queue().clone()))).await.unwrap(); + service.proc_queue.send(InternalMsg::Request(RequestMsg::new(String::from("TEST"), Default::default(), self.proc.get_service_queue().clone()))).await.expect("Internal msg should be send"); } }, _ => return Err(BusError::ProcComm(self.get_proc_id(), 0, String::from("Wrong message"))), @@ -423,7 +423,7 @@ mod tests { if let Some(service) = table.get_proc_service("TEST") { let mut msg: SimpleStringTvf = Default::default(); msg.put_string(1, "good"); - service.proc_queue.send(InternalMsg::Request(RequestMsg::new(String::from("TEST"), msg, self.proc.get_service_queue().clone()))).await.unwrap(); + service.proc_queue.send(InternalMsg::Request(RequestMsg::new(String::from("TEST"), msg, self.proc.get_service_queue().clone()))).await.expect("Internal msg should be send"); } }, _ => return Err(BusError::ProcComm(self.get_proc_id(), 0, String::from("Wrong message"))), @@ -494,7 +494,9 @@ mod tests { .await ); - bus.stop("ProSA unit test end".into()).await.unwrap(); - main_task.await.unwrap(); + bus.stop("ProSA unit test end".into()) + .await + .expect("ProSA should stop"); + main_task.await.expect("Main task should end correctly"); } } diff --git a/prosa/src/event/queue/mpsc.rs b/prosa/src/event/queue/mpsc.rs index 781e7ca..1262e07 100644 --- a/prosa/src/event/queue/mpsc.rs +++ b/prosa/src/event/queue/mpsc.rs @@ -151,10 +151,8 @@ macro_rules! mpsc { } return val; } - Err(QueueError::Empty) => { - if self.send_sem.available_permits() == 0 { - self.send_sem.add_permits(1); - } + Err(QueueError::Empty) if self.send_sem.available_permits() == 0 => { + self.send_sem.add_permits(1); } _ => {} } @@ -182,10 +180,8 @@ macro_rules! mpsc { } Ok(Some(val)) } - Err(QueueError::Empty) => { - if self.send_sem.available_permits() == 0 { - self.send_sem.add_permits(1); - } + Err(QueueError::Empty) if self.send_sem.available_permits() == 0 => { + self.send_sem.add_permits(1); Err(QueueError::Empty) } v => v, diff --git a/prosa/src/event/queue/timed.rs b/prosa/src/event/queue/timed.rs index 3867cb6..d04c33e 100644 --- a/prosa/src/event/queue/timed.rs +++ b/prosa/src/event/queue/timed.rs @@ -183,7 +183,7 @@ macro_rules! spmc { } return val; } - Err(QueueError::Empty) => if self.send_sem.available_permits() == 0 { + Err(QueueError::Empty) if self.send_sem.available_permits() == 0 => { self.send_sem.add_permits(1); } _ => {} diff --git a/prosa/src/inj/proc.rs b/prosa/src/inj/proc.rs index a72e60f..14bf26c 100644 --- a/prosa/src/inj/proc.rs +++ b/prosa/src/inj/proc.rs @@ -255,17 +255,19 @@ where } // Send first transaction - self.service - .get_proc_service(&self.settings.service_name) - .unwrap() - .proc_queue - .send(InternalMsg::Request(RequestMsg::new( - self.settings.service_name.clone(), - next_transaction.take().unwrap(), - self.proc.get_service_queue(), - ))) - .await?; - regulator.notify_send_transaction(); + if let Some(service) = self.service.get_proc_service(&self.settings.service_name) + && let Some(transaction) = next_transaction.take() + { + service + .proc_queue + .send(InternalMsg::Request(RequestMsg::new( + self.settings.service_name.clone(), + transaction, + self.proc.get_service_queue(), + ))) + .await?; + regulator.notify_send_transaction(); + } loop { tokio::select! { diff --git a/prosa/src/io.rs b/prosa/src/io.rs index 768ec44..21f2241 100644 --- a/prosa/src/io.rs +++ b/prosa/src/io.rs @@ -226,7 +226,7 @@ mod tests { #[cfg(feature = "openssl")] use prosa_utils::config::ssl::{SslConfig, SslConfigContext as _, Store}; - use std::os::fd::AsRawFd as _; + use std::{io, os::fd::AsRawFd as _}; use stream::Stream; use tokio::io::{AsyncReadExt as _, AsyncWriteExt}; @@ -234,9 +234,9 @@ mod tests { #[cfg(target_family = "unix")] #[tokio::test] - async fn unix_client_server() { + async fn unix_client_server() -> io::Result<()> { let addr = "/tmp/prosa_unix_client_server_test.sock"; - let listener = StreamListener::Unix(tokio::net::UnixListener::bind(addr).unwrap()); + let listener = StreamListener::Unix(tokio::net::UnixListener::bind(addr)?); assert!(listener.as_raw_fd() > 0); assert!( format!("{listener:?}").contains("UnixListener"), @@ -252,18 +252,18 @@ mod tests { ); let server = async move { - let (mut client_stream, client_addr) = listener.accept().await.unwrap(); + let (mut client_stream, client_addr) = listener.accept().await?; assert!(client_addr.is_loopback()); let mut buf = [0; 5]; - client_stream.read_exact(&mut buf).await.unwrap(); + client_stream.read_exact(&mut buf).await?; assert_eq!(&buf, b"ProSA"); - client_stream.write_all(b"Worldline").await.unwrap(); + client_stream.write_all(b"Worldline").await }; let client = async { - let mut stream = Stream::connect_unix(addr).await.unwrap(); + let mut stream = Stream::connect_unix(addr).await?; assert!(stream.as_raw_fd() > 0); assert!( format!("{stream:?}").contains("UnixStream"), @@ -274,23 +274,25 @@ mod tests { "stream `{stream:?}` don't contain {addr}" ); - stream.write_all(b"ProSA").await.unwrap(); + stream.write_all(b"ProSA").await?; let mut buf = Vec::new(); - stream.read_to_end(&mut buf).await.unwrap(); + stream.read_to_end(&mut buf).await?; assert_eq!(buf, b"Worldline"); let _ = stream.shutdown().await; + Ok(()) }; - future::join(server, client).await; - std::fs::remove_file(addr).unwrap(); + let (res1, res2) = future::join(server, client).await; + std::fs::remove_file(addr)?; + res1.and(res2) } #[tokio::test] - async fn tcp_client_server() { + async fn tcp_client_server() -> io::Result<()> { let addr = "localhost:41800"; - let listener = StreamListener::bind(addr).await.unwrap(); + let listener = StreamListener::bind(addr).await?; assert!(listener.as_raw_fd() > 0); assert!( format!("{listener:?}").contains("Tcp"), @@ -303,21 +305,21 @@ mod tests { assert!(listener.to_string().starts_with("tcp://")); let server = async move { - let (mut client_stream, client_addr) = listener.accept().await.unwrap(); + let (mut client_stream, client_addr) = listener.accept().await?; assert!(client_addr.is_loopback()); let mut buf = [0; 5]; - client_stream.read_exact(&mut buf).await.unwrap(); + client_stream.read_exact(&mut buf).await?; assert_eq!(&buf, b"ProSA"); // Should do nothing - client_stream = listener.handshake(client_stream).await.unwrap(); + client_stream = listener.handshake(client_stream).await?; - client_stream.write_all(b"Worldline").await.unwrap(); + client_stream.write_all(b"Worldline").await }; let client = async { - let mut stream = Stream::connect_tcp(addr).await.unwrap(); + let mut stream = Stream::connect_tcp(addr).await?; assert!(stream.as_raw_fd() > 0); assert!( format!("{stream:?}").contains("Tcp"), @@ -329,38 +331,37 @@ mod tests { ); assert!(stream.to_string().starts_with("tcp://")); - stream.write_all(b"ProSA").await.unwrap(); + stream.write_all(b"ProSA").await?; let mut buf = Vec::new(); - stream.read_to_end(&mut buf).await.unwrap(); + stream.read_to_end(&mut buf).await?; assert_eq!(buf, b"Worldline"); - let _ = stream.shutdown().await; + stream.shutdown().await }; - future::join(server, client).await; + let (res1, res2) = future::join(server, client).await; + res1.and(res2) } #[cfg(feature = "openssl")] #[tokio::test] - async fn openssl_client_server() { + async fn openssl_client_server() -> io::Result<()> { let addr = "localhost:41443"; - let addr_url = Url::parse(format!("tls://{addr}").as_str()).unwrap(); + let addr_url = Url::parse(format!("tls://{addr}").as_str()).expect("Target url is invalid"); let cert_path = std::env::temp_dir() .join("test_openssl_client_server.pem") .to_str() - .unwrap() + .expect("Certificate path should exist") .to_string(); let mut ssl_config = SslConfig::new_self_cert(cert_path.clone()); let listener = { - let ssl_acceptor_builder: ::openssl::ssl::SslAcceptorBuilder = ssl_config - .init_tls_server_context(addr_url.host_str()) - .unwrap(); + let ssl_acceptor_builder: ::openssl::ssl::SslAcceptorBuilder = + ssl_config.init_tls_server_context(addr_url.host_str())?; let ssl_acceptor = ssl_acceptor_builder.build(); StreamListener::bind(addr) - .await - .unwrap() + .await? .ssl_acceptor(ssl_acceptor, Some(ssl_config.get_ssl_timeout())) }; @@ -376,27 +377,25 @@ mod tests { assert!(listener.to_string().starts_with("ssl://")); let server = async move { - let (mut client_stream, client_addr) = listener.accept().await.unwrap(); + let (mut client_stream, client_addr) = listener.accept().await?; assert!(client_addr.is_loopback()); let mut buf = [0; 5]; - client_stream.read_exact(&mut buf).await.unwrap(); + client_stream.read_exact(&mut buf).await?; assert_eq!(&buf, b"ProSA"); - client_stream.write_all(b"Worldline").await.unwrap(); + client_stream.write_all(b"Worldline").await?; - let _ = client_stream.shutdown().await; + client_stream.shutdown().await }; ssl_config.set_store(Store::File { path: cert_path }); let client = async { let mut stream = { let ssl_client_context: ::openssl::ssl::SslConnectorBuilder = - ssl_config.init_tls_client_context().unwrap(); + ssl_config.init_tls_client_context()?; - Stream::connect_openssl(&addr_url, &ssl_client_context.build()) - .await - .unwrap() + Stream::connect_openssl(&addr_url, &ssl_client_context.build()).await? }; assert!(stream.as_raw_fd() > 0); @@ -406,38 +405,37 @@ mod tests { ); assert!(stream.to_string().starts_with("ssl://")); - stream.write_all(b"ProSA").await.unwrap(); + stream.write_all(b"ProSA").await?; let mut buf = Vec::new(); - stream.read_to_end(&mut buf).await.unwrap(); + stream.read_to_end(&mut buf).await?; assert_eq!(buf, b"Worldline"); - let _ = stream.shutdown().await; + stream.shutdown().await }; - future::join(server, client).await; + let (res1, res2) = future::join(server, client).await; + res1.and(res2) } #[cfg(feature = "openssl")] #[tokio::test] - async fn openssl_client_server_raw() { + async fn openssl_client_server_raw() -> io::Result<()> { let addr = "localhost:41453"; - let addr_url = Url::parse(format!("tls://{addr}").as_str()).unwrap(); + let addr_url = Url::parse(format!("tls://{addr}").as_str()).expect("Target url is invalid"); let cert_path = std::env::temp_dir() .join("test_openssl_client_server_raw.pem") .to_str() - .unwrap() + .expect("Certificate path should exist") .to_string(); let mut ssl_config = SslConfig::new_self_cert(cert_path.clone()); let listener = { - let ssl_acceptor_builder: ::openssl::ssl::SslAcceptorBuilder = ssl_config - .init_tls_server_context(addr_url.host_str()) - .unwrap(); + let ssl_acceptor_builder: ::openssl::ssl::SslAcceptorBuilder = + ssl_config.init_tls_server_context(addr_url.host_str())?; let ssl_acceptor = ssl_acceptor_builder.build(); StreamListener::bind(addr) - .await - .unwrap() + .await? .ssl_acceptor(ssl_acceptor, Some(ssl_config.get_ssl_timeout())) }; @@ -453,28 +451,26 @@ mod tests { assert!(listener.to_string().starts_with("ssl://")); let server = async move { - let (mut client_stream, client_addr) = listener.accept_raw().await.unwrap(); + let (mut client_stream, client_addr) = listener.accept_raw().await?; assert!(client_addr.is_loopback()); - client_stream = listener.handshake(client_stream).await.unwrap(); + client_stream = listener.handshake(client_stream).await?; let mut buf = [0; 5]; - client_stream.read_exact(&mut buf).await.unwrap(); + client_stream.read_exact(&mut buf).await?; assert_eq!(&buf, b"ProSA"); - client_stream.write_all(b"Worldline").await.unwrap(); + client_stream.write_all(b"Worldline").await?; - let _ = client_stream.shutdown().await; + client_stream.shutdown().await }; ssl_config.set_store(Store::File { path: cert_path }); let client = async { let mut stream = { let ssl_client_context: ::openssl::ssl::SslConnectorBuilder = - ssl_config.init_tls_client_context().unwrap(); + ssl_config.init_tls_client_context()?; - Stream::connect_openssl(&addr_url, &ssl_client_context.build()) - .await - .unwrap() + Stream::connect_openssl(&addr_url, &ssl_client_context.build()).await? }; assert!(stream.as_raw_fd() > 0); @@ -484,27 +480,28 @@ mod tests { ); assert!(stream.to_string().starts_with("ssl://")); - stream.write_all(b"ProSA").await.unwrap(); + stream.write_all(b"ProSA").await?; let mut buf = Vec::new(); - stream.read_to_end(&mut buf).await.unwrap(); + stream.read_to_end(&mut buf).await?; assert_eq!(buf, b"Worldline"); - let _ = stream.shutdown().await; + stream.shutdown().await }; - future::join(server, client).await; + let (res1, res2) = future::join(server, client).await; + res1.and(res2) } #[cfg(feature = "openssl")] #[tokio::test] - async fn ssl_client_server_with_config() { + async fn ssl_client_server_with_config() -> io::Result<()> { let addr_str = "tls://localhost:41463"; - let addr = Url::parse(addr_str).unwrap(); + let addr = Url::parse(addr_str).expect("Target url is invalid"); let cert_path = std::env::temp_dir() .join("test_ssl_client_server_with_config.pem") .to_str() - .unwrap() + .expect("Certificate path should exist") .to_string(); let mut server_ssl_config = SslConfig::new_self_cert(cert_path.clone()); @@ -524,7 +521,7 @@ mod tests { ); assert!(listener_settings.to_string().starts_with(addr_str)); - let listener = listener_settings.bind().await.unwrap(); + let listener = listener_settings.bind().await?; assert!(listener.as_raw_fd() > 0); assert!( format!("{listener:?}").contains("Ssl"), @@ -536,19 +533,19 @@ mod tests { ); let server = async move { - let (mut client_stream, client_addr) = listener.accept().await.unwrap(); + let (mut client_stream, client_addr) = listener.accept().await?; assert!(client_addr.is_loopback()); let mut buf = [0; 5]; - client_stream.read_exact(&mut buf).await.unwrap(); + client_stream.read_exact(&mut buf).await?; assert_eq!(&buf, b"ProSA"); // Should do nothing - client_stream = listener.handshake(client_stream).await.unwrap(); + client_stream = listener.handshake(client_stream).await?; - client_stream.write_all(b"Worldline").await.unwrap(); + client_stream.write_all(b"Worldline").await?; - let _ = client_stream.shutdown().await; + client_stream.shutdown().await }; let mut client_ssl_config = SslConfig::default(); @@ -558,7 +555,7 @@ mod tests { assert_eq!(addr_str, target_settings.to_string()); let client = async { - let mut stream = target_settings.connect().await.unwrap(); + let mut stream = target_settings.connect().await?; assert!(stream.as_raw_fd() > 0); assert!( format!("{stream:?}").contains("Ssl") || format!("{stream:?}").contains("Tls"), @@ -570,34 +567,35 @@ mod tests { panic!("Should be an SSL stream for client"); } - stream.write_all(b"ProSA").await.unwrap(); + stream.write_all(b"ProSA").await?; let mut buf = Vec::new(); - stream.read_to_end(&mut buf).await.unwrap(); + stream.read_to_end(&mut buf).await?; assert_eq!(buf, b"Worldline"); - let _ = stream.shutdown().await; + stream.shutdown().await }; - future::join(server, client).await; + let (res1, res2) = future::join(server, client).await; + res1.and(res2) } #[cfg(all(feature = "openssl", not(feature = "openssl-vendored")))] #[tokio::test] - async fn ssl_client_public_with_config() { + async fn ssl_client_public_with_config() -> io::Result<()> { let addr_str = "https://worldline.com/"; - let addr = Url::parse(addr_str).unwrap(); + let addr = Url::parse(addr_str).expect("Target url is invalid"); let target_settings = stream::TargetSetting::new(addr, Some(SslConfig::default()), None); assert_eq!(addr_str, target_settings.to_string()); - let mut stream = target_settings.connect().await.unwrap(); + let mut stream = target_settings.connect().await?; assert!(stream.as_raw_fd() > 0); assert!( format!("{stream:?}").contains("Ssl") || format!("{stream:?}").contains("Tls"), "stream `{stream:?}` don't contain Ssl or Tls" ); - let _ = stream.shutdown().await; + stream.shutdown().await } } diff --git a/prosa/src/io/stream.rs b/prosa/src/io/stream.rs index fb19ad2..cc894d4 100644 --- a/prosa/src/io/stream.rs +++ b/prosa/src/io/stream.rs @@ -180,13 +180,13 @@ impl Stream { S: AsyncRead + AsyncWrite + std::marker::Unpin, { let ssl = ssl_connector.configure()?.into_ssl(domain)?; - let mut stream = tokio_openssl::SslStream::new(ssl, tcp_stream).unwrap(); + let mut stream = tokio_openssl::SslStream::new(ssl, tcp_stream)?; if let Err(e) = Pin::new(&mut stream).connect().await && e.code() != openssl::ssl::ErrorCode::ZERO_RETURN { return Err(io::Error::new( io::ErrorKind::Interrupted, - format!("Can't connect the SSL socket `{e}`"), + format!("Can't connect the OpenSSL socket `{e}`"), )); } @@ -851,14 +851,14 @@ impl TargetSetting { let openssl_context = if self.openssl_context.is_some() { self.openssl_context.clone() } else if let Some(ssl_config) = &self.ssl { - let ssl_context_builder: Option = - SslConfigContext::init_tls_client_context(ssl_config).ok(); - ssl_context_builder.map(|c| c.build()) + let ssl_context_builder: openssl::ssl::SslConnectorBuilder = + SslConfigContext::init_tls_client_context(ssl_config)?; + Some(ssl_context_builder.build()) } else if url_is_ssl(&self.url) { let ssl_config = SslConfig::default(); - let ssl_context_builder: Option = - SslConfigContext::init_tls_client_context(&ssl_config).ok(); - ssl_context_builder.map(|c| c.build()) + let ssl_context_builder: openssl::ssl::SslConnectorBuilder = + SslConfigContext::init_tls_client_context(&ssl_config)?; + Some(ssl_context_builder.build()) } else { None }; @@ -1018,7 +1018,7 @@ mod tests { #[test] fn target_settings_test() { let target_without_credential = TargetSetting::new( - Url::parse("https://localhost:4443/v1?var=1").unwrap(), + Url::parse("https://localhost:4443/v1?var=1").expect("Target url is invalid"), None, None, ); @@ -1032,7 +1032,8 @@ mod tests { ); let target_with_user_password = TargetSetting::new( - Url::parse("https://admin:admin@localhost:4443/v1?user=admin&password=admin").unwrap(), + Url::parse("https://admin:admin@localhost:4443/v1?user=admin&password=admin") + .expect("Target url is invalid"), None, None, ); @@ -1050,7 +1051,7 @@ mod tests { ); let target_with_token = TargetSetting::new( - Url::parse("https://:token@localhost:4443/v1").unwrap(), + Url::parse("https://:token@localhost:4443/v1").expect("Target url is invalid"), None, None, ); diff --git a/prosa/src/lib.rs b/prosa/src/lib.rs index 3200030..24f27ad 100644 --- a/prosa/src/lib.rs +++ b/prosa/src/lib.rs @@ -102,7 +102,7 @@ mod tests { /// Test a ProSA with an injector processor sending transactions to a stub processor #[allow(clippy::needless_return)] #[tokio::test] - async fn prosa() { + async fn prosa() -> Result<(), Box> { let test_settings = TestSettings::new(SERVICE_TEST); // Create bus and main processor @@ -118,7 +118,7 @@ mod tests { bus.clone(), test_settings.stub, ); - Proc::::run(stub_proc); + Proc::::run(stub_proc)?; // Launch an inj processor let inj_proc = InjProc::::create( @@ -127,19 +127,21 @@ mod tests { bus.clone(), test_settings.inj, ); - Proc::::run(inj_proc); + Proc::::run(inj_proc)?; // Wait before stopping prosa tokio::time::sleep(WAIT_TIME).await; - bus.stop("ProSA unit test end".into()).await.unwrap(); + bus.stop("ProSA unit test end".into()).await?; // Wait on main task to end (should be immediate with the previous stop) - main_task.await.unwrap(); + main_task.await?; // Check exchanges messages let nb_trans = COUNTER.load(Ordering::SeqCst) as u64; let estimated_trans = WAIT_TIME.as_secs() * 5; assert!(nb_trans > (estimated_trans - 2) && nb_trans < (estimated_trans + 2)); // Should have a coherent number of transaction with the regulator + + Ok(()) } } diff --git a/prosa_macros/Cargo.toml b/prosa_macros/Cargo.toml index 6e2e619..b94abea 100644 --- a/prosa_macros/Cargo.toml +++ b/prosa_macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "prosa-macros" -version = "0.4.1" +version = "0.4.2" authors.workspace = true description = "ProSA macros" homepage.workspace = true @@ -12,6 +12,9 @@ include.workspace = true [lib] proc-macro = true +[lints.clippy] +unwrap_used = "deny" + [dependencies] syn = { version = "2", features = ["full"] } quote = "1" diff --git a/prosa_macros/src/io.rs b/prosa_macros/src/io.rs index e903d71..aa65f4e 100644 --- a/prosa_macros/src/io.rs +++ b/prosa_macros/src/io.rs @@ -44,30 +44,23 @@ fn generate_struct(mut item_struct: syn::ItemStruct) -> syn::parse::Result }) - .unwrap(), + .parse2(quote! { addr: std::option::Option })?, ); // Add the buffer object to read from the net object - fields.named.push( - syn::Field::parse_named - .parse2(quote! { buffer: bytes::BytesMut }) - .unwrap(), - ); + fields + .named + .push(syn::Field::parse_named.parse2(quote! { buffer: bytes::BytesMut })?); // Add the socket id information - fields.named.push( - syn::Field::parse_named - .parse2(quote! { socket_id: u32 }) - .unwrap(), - ); + fields + .named + .push(syn::Field::parse_named.parse2(quote! { socket_id: u32 })?); } // Add the Generic type IO to specify the net object diff --git a/prosa_macros/src/proc.rs b/prosa_macros/src/proc.rs index 00a2683..a6370eb 100644 --- a/prosa_macros/src/proc.rs +++ b/prosa_macros/src/proc.rs @@ -18,8 +18,7 @@ impl ProcParams { ) -> syn::parse::Result<()> { for meta in args { if let syn::Meta::NameValue(v) = meta { - if !v.path.segments.is_empty() { - let name = &v.path.segments.first().unwrap().ident; + if let Some(name) = v.path.segments.first().map(|s| &s.ident) { if name == "settings" { if let syn::Expr::Path(syn::ExprPath { path, .. }) = &v.value { self.settings = Some(path.clone()); @@ -114,34 +113,29 @@ fn generate_struct( // Parameter of the processor fields.named.push( syn::Field::parse_named - .parse2(quote! { proc: std::sync::Arc> }) - .unwrap(), + .parse2(quote! { proc: std::sync::Arc> })?, ); // Service table fields.named.push( - syn::Field::parse_named - .parse2(quote! { service: std::sync::Arc> }) - .unwrap(), + syn::Field::parse_named.parse2( + quote! { service: std::sync::Arc> }, + )?, ); // Add the receiver queue for processor messaging fields.named.push( syn::Field::parse_named .parse2(quote! { internal_rx_queue: tokio::sync::mpsc::Receiver> }) - .unwrap(), + ?, ); // Add the processor settings if needed if let Some(settings) = &args.settings { - fields.named.push( - syn::Field::parse_named - .parse2(quote! { - /// Settings of the processor - pub settings: #settings - }) - .unwrap(), - ); + fields.named.push(syn::Field::parse_named.parse2(quote! { + /// Settings of the processor + pub settings: #settings + })?); } } diff --git a/prosa_macros/src/settings.rs b/prosa_macros/src/settings.rs index 0f3c5f7..ad2f84a 100644 --- a/prosa_macros/src/settings.rs +++ b/prosa_macros/src/settings.rs @@ -7,7 +7,7 @@ use syn::{ /// Function to add default member to Default trait impl fn add_default_member(mut item_impl: ItemImpl, func: F) -> syn::parse::Result where - F: Fn(&mut syn::ExprStruct), + F: Fn(&mut syn::ExprStruct) -> syn::parse::Result<()>, { if let (Some((_, trait_path, _)), syn::Type::Path(self_path)) = (&item_impl.trait_, item_impl.self_ty.as_ref()) @@ -33,18 +33,18 @@ where expr.fields.push_punct(syn::token::Comma::default()); } - func(expr); + func(expr)?; } } // Direct Expr return (Self {..}) - syn::Stmt::Expr(syn::Expr::Struct(expr), _) => { - if expr.path.is_ident(self_ident) { - if !expr.fields.trailing_punct() { - expr.fields.push_punct(syn::token::Comma::default()); - } - - func(expr); + syn::Stmt::Expr(syn::Expr::Struct(expr), _) + if expr.path.is_ident(self_ident) => + { + if !expr.fields.trailing_punct() { + expr.fields.push_punct(syn::token::Comma::default()); } + + func(expr)?; } _ => {} } @@ -82,36 +82,24 @@ fn generate_proc_settings_struct( // Add mandatory fields if let syn::Fields::Named(ref mut fields) = item_struct.fields { // Adaptor config path - fields.named.push( - syn::Field::parse_named - .parse2(quote! { - #[doc = "Path to the adaptor configuration file (if needed by the custom adaptor)"] - adaptor_config_path: std::option::Option - }) - .unwrap(), - ); + fields.named.push(syn::Field::parse_named.parse2(quote! { + #[doc = "Path to the adaptor configuration file (if needed by the custom adaptor)"] + adaptor_config_path: std::option::Option + })?); // Restart duration period - fields.named.push( - syn::Field::parse_named - .parse2(quote! { - #[serde(skip_serializing)] - #[doc = "Duration period between two processor restart (in ms)"] - proc_restart_duration_period: std::option::Option - }) - .unwrap(), - ); + fields.named.push(syn::Field::parse_named.parse2(quote! { + #[serde(skip_serializing)] + #[doc = "Duration period between two processor restart (in ms)"] + proc_restart_duration_period: std::option::Option + })?); // Max restart period - fields.named.push( - syn::Field::parse_named - .parse2(quote! { - #[serde(skip_serializing)] - #[doc = "Maximum number of restart in the given duration period"] - proc_max_restart_period: std::option::Option - }) - .unwrap(), - ); + fields.named.push(syn::Field::parse_named.parse2(quote! { + #[serde(skip_serializing)] + #[doc = "Maximum number of restart in the given duration period"] + proc_max_restart_period: std::option::Option + })?); } Ok(item_struct) @@ -155,9 +143,7 @@ pub(crate) fn proc_settings_impl(item: syn::Item) -> syn::parse::Result syn::parse::Result syn::parse::Result Err(syn::Error::new( @@ -208,18 +191,13 @@ fn generate_settings_struct( // ProSA name setting fields.named.push( syn::Field::parse_named - .parse2(quote! { name: std::option::Option }) - .unwrap(), + .parse2(quote! { name: std::option::Option })?, ); // ProSA observability setting - fields.named.push( - syn::Field::parse_named - .parse2(quote! { - #[serde(default)] - observability: prosa_utils::config::observability::Observability }) - .unwrap(), - ); + fields.named.push(syn::Field::parse_named.parse2(quote! { + #[serde(default)] + observability: prosa_utils::config::observability::Observability })?); } Ok(item_struct) @@ -269,16 +247,17 @@ pub(crate) fn settings_impl(item: syn::Item) -> syn::parse::Result Err(syn::Error::new( diff --git a/prosa_macros/tests/tvf.rs b/prosa_macros/tests/tvf.rs index 77020b4..5312023 100644 --- a/prosa_macros/tests/tvf.rs +++ b/prosa_macros/tests/tvf.rs @@ -26,35 +26,43 @@ mod macro_tests { }); assert_eq!(5, buffer.len()); - assert_eq!(2, buffer.get_unsigned(1).unwrap()); - assert_eq!(4, buffer.get_signed(3).unwrap()); + assert_eq!(Ok(2), buffer.get_unsigned(1)); + assert_eq!(Ok(4), buffer.get_signed(3)); - let subbuffer = buffer.get_buffer(5).unwrap(); - assert_eq!(1u64, subbuffer.get_unsigned(1).unwrap()); - assert_eq!(2.0f64, subbuffer.get_float(2).unwrap()); - assert_eq!(3i64, subbuffer.get_signed(3).unwrap()); - assert_eq!("four", subbuffer.get_string(4).unwrap().to_mut().to_owned()); + let subbuffer = buffer.get_buffer(5).expect("TVF should have a sub buffer"); + assert_eq!(Ok(1u64), subbuffer.get_unsigned(1)); + assert_eq!(Ok(2.0f64), subbuffer.get_float(2)); + assert_eq!(Ok(3i64), subbuffer.get_signed(3)); + assert_eq!( + Ok("four"), + subbuffer.get_string(4).map(|s| s.to_string()).as_deref() + ); - let sub = subbuffer.get_buffer(5).unwrap(); - assert_eq!("object", sub.get_string(1).unwrap().to_mut().to_owned()); + let sub = subbuffer + .get_buffer(5) + .expect("TVF should have a sub buffer"); + assert_eq!( + Ok("object"), + sub.get_string(1).map(|s| s.to_string()).as_deref() + ); assert_eq!( - Bytes::from_static(&[ + Ok(Bytes::from_static(&[ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F - ]), - sub.get_bytes(2).unwrap().to_mut().to_owned() + ])), + sub.get_bytes(2).as_deref().cloned() ); assert_eq!( - NaiveDate::from_ymd_opt(1995, 1, 10).unwrap(), - buffer.get_date(6).unwrap() + Ok(NaiveDate::from_ymd_opt(1995, 1, 10).expect("NaiveDate should be build")), + buffer.get_date(6) ); assert_eq!( - NaiveDate::from_ymd_opt(2023, 6, 5) - .unwrap() + Ok(NaiveDate::from_ymd_opt(2023, 6, 5) + .expect("NaiveDate should be build") .and_hms_opt(15, 2, 0) - .unwrap(), - buffer.get_datetime(200).unwrap() + .expect("NaiveDateTime should be build")), + buffer.get_datetime(200) ); } } diff --git a/prosa_utils/Cargo.toml b/prosa_utils/Cargo.toml index b532448..44b14ea 100644 --- a/prosa_utils/Cargo.toml +++ b/prosa_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "prosa-utils" -version = "0.4.2" +version = "0.4.3" authors.workspace = true description = "ProSA utils" homepage.workspace = true @@ -27,6 +27,9 @@ rustdoc-args = ["--cfg", "docsrs"] [package.metadata.prosa] tvf = ["msg::simple_string_tvf::SimpleStringTvf"] +[lints.clippy] +unwrap_used = "deny" + [dependencies] thiserror.workspace = true bytes.workspace = true diff --git a/prosa_utils/build.rs b/prosa_utils/build.rs index 8b3c562..6400a26 100644 --- a/prosa_utils/build.rs +++ b/prosa_utils/build.rs @@ -4,7 +4,7 @@ fn main() { println!("cargo:rustc-check-cfg=cfg(ossl300)"); if let Ok(v) = env::var("DEP_OPENSSL_VERSION_NUMBER") { - let version = u64::from_str_radix(&v, 16).unwrap(); + let version = u64::from_str_radix(&v, 16).expect("OpenSSL version must be in hexadecimal"); #[allow(clippy::unusual_byte_groupings)] if version >= 0x3_00_00_00_0 { diff --git a/prosa_utils/src/config.rs b/prosa_utils/src/config.rs index 00a78be..6ca6758 100644 --- a/prosa_utils/src/config.rs +++ b/prosa_utils/src/config.rs @@ -4,7 +4,7 @@ #![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/doc_assets/settings.svg"))] //! -use std::{path::PathBuf, process::Command}; +use std::{io, path::PathBuf, process::Command}; use base64::{Engine as _, engine::general_purpose::STANDARD}; use thiserror::Error; @@ -42,6 +42,15 @@ pub enum ConfigError { OpenSsl(#[from] openssl::error::ErrorStack), } +impl From for io::Error { + fn from(err: ConfigError) -> Self { + io::Error::new( + io::ErrorKind::InvalidInput, + format!("ProSA Config error: {}", err), + ) + } +} + /// Method to try get the country name from the OS pub fn os_country() -> Option { if let Some(lang) = option_env!("LANG") { @@ -149,7 +158,8 @@ mod tests { #[test] fn test_url_authentication_basic() { - let basic_auth_target = Url::parse("http://user:pass@localhost:8080").unwrap(); + let basic_auth_target = Url::parse("http://user:pass@localhost:8080") + .expect("Basic auth target URL should be valid"); assert_eq!( Some(String::from("Basic dXNlcjpwYXNz")), url_authentication(&basic_auth_target) @@ -158,7 +168,8 @@ mod tests { #[test] fn test_url_safe_authentication_basic() { - let basic_auth_target = Url::parse("http://user:$ab&cd=@localhost:8080").unwrap(); + let basic_auth_target = Url::parse("http://user:$ab&cd=@localhost:8080") + .expect("Basic auth target URL should be valid"); assert_eq!( Some(String::from("Basic dXNlcjokYWImY2Q9")), url_authentication(&basic_auth_target) @@ -167,7 +178,8 @@ mod tests { #[test] fn test_url_authentication_bearer() { - let bearer_auth_target = Url::parse("http://:token@localhost:8080").unwrap(); + let bearer_auth_target = Url::parse("http://:token@localhost:8080") + .expect("Basic auth target URL should be valid"); assert_eq!( Some(String::from("Bearer token")), url_authentication(&bearer_auth_target) diff --git a/prosa_utils/src/config/observability.rs b/prosa_utils/src/config/observability.rs index 9013af4..5eef647 100644 --- a/prosa_utils/src/config/observability.rs +++ b/prosa_utils/src/config/observability.rs @@ -86,7 +86,7 @@ impl Default for OTLPExporterCfg { fn default() -> Self { Self { level: None, - endpoint: Url::parse("grpc://localhost:4317").unwrap(), + endpoint: Url::parse("grpc://localhost:4317").expect("default OTLP address is invalid"), timeout_sec: None, } } diff --git a/prosa_utils/src/config/ssl.rs b/prosa_utils/src/config/ssl.rs index 2d697d4..cd1f87e 100644 --- a/prosa_utils/src/config/ssl.rs +++ b/prosa_utils/src/config/ssl.rs @@ -408,10 +408,11 @@ tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1 /q4AaOeMSQ+2b1tbFfLn -----END CERTIFICATE-----", ) - .unwrap(); + .expect("SSL certificate configuration should be read"); assert!(format!("{config_store_le_x1_x2}").contains("ISRG Root X")); - let config_store_file: Store = serde_yaml::from_str("path: \"/opt\"").unwrap(); + let config_store_file: Store = serde_yaml::from_str("path: \"/opt\"") + .expect("Certificate configuration path should be read"); assert_eq!( Store::File { path: "/opt".to_string() @@ -423,7 +424,10 @@ tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1 #[test] fn test_tls_server_context() { let ssl_config = SslConfig::default(); - let ssl_acceptor = ssl_config.init_tls_server_context(None).unwrap().build(); + let ssl_acceptor = ssl_config + .init_tls_server_context(None) + .expect("The TLS server context should be init") + .build(); // Check for self signed certificate assert!(ssl_acceptor.context().private_key().is_some()); diff --git a/prosa_utils/src/config/ssl/openssl.rs b/prosa_utils/src/config/ssl/openssl.rs index 7074091..4f4b2e2 100644 --- a/prosa_utils/src/config/ssl/openssl.rs +++ b/prosa_utils/src/config/ssl/openssl.rs @@ -272,8 +272,15 @@ where let serial_number = Asn1Integer::from_bn(&serial_bn)?; cert.set_serial_number(&serial_number)?; - let begin_valid_time = - Asn1Time::from_unix(time::UNIX_EPOCH.elapsed().unwrap().as_secs() as i64 - 360)?; + let begin_valid_time = Asn1Time::from_unix( + time::UNIX_EPOCH + .elapsed() + .map_err(|e| { + ConfigError::WrongValue("time::UNIX_EPOCH".to_string(), e.to_string()) + })? + .as_secs() as i64 + - 360, + )?; cert.set_not_before(&begin_valid_time)?; let end_valid_time = Asn1Time::days_from_now(1461)?; // 4 years from now cert.set_not_after(&end_valid_time)?; @@ -346,9 +353,11 @@ where let mut current_split = alpn; while let Some(length) = current_split.first() { - if current_split.len() > *length as usize { - let (left, right) = current_split.split_at(*length as usize + 1); - cli_alpn.insert(String::from_utf8(left[1..].to_vec()).unwrap(), &left[1..]); + if current_split.len() > *length as usize + && let (left, right) = current_split.split_at(*length as usize + 1) + && let Ok(alpn_key) = String::from_utf8(left[1..].to_vec()) + { + cli_alpn.insert(alpn_key, &left[1..]); current_split = right; } else { return Err(AlpnError::ALERT_FATAL); @@ -517,10 +526,14 @@ tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1 ], }; - let certs: HashMap = store_le_x1_x2.get_certs().unwrap(); + let certs: HashMap = store_le_x1_x2 + .get_certs() + .expect("Certificates should be retrieved"); assert!(!certs.is_empty()); - let ossl_store: ::openssl::x509::store::X509Store = store_le_x1_x2.get_store().unwrap(); + let ossl_store: ::openssl::x509::store::X509Store = store_le_x1_x2 + .get_store() + .expect("Certificates store should be retrieved"); assert!(!ossl_store.all_certificates().is_empty()) } @@ -534,8 +547,9 @@ tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1 #[test] fn test_tls_server_context() { let ssl_config = SslConfig::default(); - let ssl_acceptor_builder: ::openssl::ssl::SslAcceptorBuilder = - ssl_config.init_tls_server_context(None).unwrap(); + let ssl_acceptor_builder: ::openssl::ssl::SslAcceptorBuilder = ssl_config + .init_tls_server_context(None) + .expect("The TLS server context should be init"); let ssl_acceptor = ssl_acceptor_builder.build(); // Check for self signed certificate diff --git a/prosa_utils/src/config/tracing.rs b/prosa_utils/src/config/tracing.rs index a9cd950..177492d 100644 --- a/prosa_utils/src/config/tracing.rs +++ b/prosa_utils/src/config/tracing.rs @@ -234,13 +234,16 @@ mod tests { #[test] fn telemetry_level() { assert!( - TelemetryLevel::try_from("warn").unwrap() < TelemetryLevel::INFO, + TelemetryLevel::try_from("warn").expect("Warn Telemetry level should exist") + < TelemetryLevel::INFO, "{:?} < Info", TelemetryLevel::try_from("warn") ); assert_eq!( "The config parameter TelemetryLevel have an incorrect value `wrong`".to_owned(), - TelemetryLevel::try_from("wrong").err().unwrap().to_string() + TelemetryLevel::try_from("wrong") + .expect_err("Wrong Telemetry level shouldn't exist") + .to_string() ); assert_eq!( diff --git a/prosa_utils/src/msg/simple_string_tvf.rs b/prosa_utils/src/msg/simple_string_tvf.rs index 32d4ed0..73bb6df 100644 --- a/prosa_utils/src/msg/simple_string_tvf.rs +++ b/prosa_utils/src/msg/simple_string_tvf.rs @@ -153,16 +153,6 @@ impl Tvf for SimpleStringTvf { } } - //fn get_datetime_at(&self, id: usize, offset: T) -> Result, TvfError> { - // match self.fields.get(&id) { - // Some(str_value) => match NaiveDateTime::parse_from_str(str_value, SIMPLE_DATETIME_FMT) { - // Ok(d) => Ok(offset.from_utc_datetime(&d)), - // Err(e) => Err(TvfError::ConvertionError(e.to_string())) - // }, - // None => Err(TvfError::FieldNotFound(id)), - // } - //} - fn put_buffer(&mut self, id: usize, buffer: SimpleStringTvf) { self.fields.insert(id, buffer.serialize()); } @@ -202,10 +192,6 @@ impl Tvf for SimpleStringTvf { self.fields .insert(id, datetime.format(SIMPLE_DATETIME_FMT).to_string()); } - - //fn put_datetime_tz(&mut self, id: usize, datetime: chrono::DateTime) { - // self.fields.insert(id, datetime.naive_utc().format(SIMPLE_DATETIME_FMT).to_string()); - //} } impl SimpleStringTvf { @@ -275,17 +261,23 @@ mod tests { tvf.put_string(2, String::from("The great string")); assert!(tvf.contains(2)); tvf.put_signed(3, -1); - assert_eq!(-1, tvf.get_signed(3).unwrap()); + assert_eq!(Ok(-1), tvf.get_signed(3)); tvf.put_float(5, 6.56); tvf.put_byte(6, 32u8); - tvf.put_bytes(7, Bytes::from(hex::decode("aabb77ff").unwrap())); - tvf.put_date(8, NaiveDate::from_ymd_opt(2023, 6, 5).unwrap()); + tvf.put_bytes( + 7, + Bytes::from(hex::decode("aabb77ff").expect("Hexadecimal should be decode")), + ); + tvf.put_date( + 8, + NaiveDate::from_ymd_opt(2023, 6, 5).expect("NaiveDate should be build"), + ); tvf.put_datetime( 9, NaiveDate::from_ymd_opt(2023, 6, 5) - .unwrap() + .expect("NaiveDate should be build") .and_hms_opt(15, 2, 0) - .unwrap(), + .expect("NaiveDateTime should be build"), ); tvf.put_buffer(10, sub_buffer.clone()); assert_eq!(Err(TvfError::TypeMismatch), tvf.get_unsigned(2)); @@ -316,15 +308,21 @@ mod tests { assert_eq!(Ok(6.56), tvf.get_float(5)); assert_eq!(Ok(32), tvf.get_byte(6)); assert_eq!( - Ok(Cow::Owned(Bytes::from(hex::decode("aabb77ff").unwrap()))), + Ok(Cow::Owned(Bytes::from( + hex::decode("aabb77ff").expect("Hexadecimal should be decode") + ))), tvf.get_bytes(7) ); assert_eq!( - Ok(NaiveDate::parse_from_str("2023-06-05", SIMPLE_DATE_FMT).unwrap()), + Ok(NaiveDate::parse_from_str("2023-06-05", SIMPLE_DATE_FMT) + .expect("NaiveDate should be build")), tvf.get_date(8) ); assert_eq!( - Ok(NaiveDateTime::parse_from_str("2023-06-05T15:02:00", SIMPLE_DATETIME_FMT).unwrap()), + Ok( + NaiveDateTime::parse_from_str("2023-06-05T15:02:00", SIMPLE_DATETIME_FMT) + .expect("NaiveDateTime should be build") + ), tvf.get_datetime(9) ); assert_eq!(Ok(Cow::Owned(sub_buffer)), tvf.get_buffer(10)); @@ -350,7 +348,8 @@ mod tests { assert_eq!(keys, into_keys); assert_eq!(9, keys.len()); let serial = simple_tvf.serialize(); - let unserial = SimpleStringTvf::deserialize(&serial).unwrap(); + let unserial = SimpleStringTvf::deserialize(&serial) + .expect("The SimpleStringTvf should be deserialized"); assert_eq!(simple_tvf, unserial); assert_eq!( @@ -395,7 +394,13 @@ mod tests { simple_tvf = TvfTestFilter::filter(simple_tvf); assert_eq!(2, simple_tvf.len()); - assert_eq!("0000", simple_tvf.get_string(1).unwrap().as_str()); - assert_eq!("1234", simple_tvf.get_string(2).unwrap().as_str()); + assert_eq!( + Ok("0000"), + simple_tvf.get_string(1).map(|v| v.to_string()).as_deref() + ); + assert_eq!( + Ok("1234"), + simple_tvf.get_string(2).map(|v| v.to_string()).as_deref() + ); } }