Radish alpha
r
rad:zwTxygwuz5LDGBq255RA2CbNGrz8
Radicle CI broker
Radicle
Git
chore: deny clippy warning about unwrap being used
Merged liw opened 1 year ago

An unwrap (or unwrap_err, or their expect variants) can result in run time panics: unwrapping an Option that is None can’t return a useful result, so it panics. Similar for a Result. I’ve previously tried to grep for uses of unwrap, but the clippy warning is much more precise.

Add “deny(clippy::unwrap_used)” to the top of src/lib.rs, so it affects the whole crate. Then fix any places where unwrap is used.

It’s OK to use unwrap in tests: if the value is expected, but isn’t there, the test should fail, and panic is a fine way to fail the test.

Everywhere else it’s better to make sure unwrap can’t fail, and if it can, return a Result and have the caller handle that.

Signed-off-by: Lars Wirzenius liw@liw.fi

9 files changed +67 -22 a9fea0fd 47064e57
modified src/bin/cib.rs
@@ -18,7 +18,7 @@ use radicle_ci_broker::{
    config::{Config, ConfigError},
    db::{Db, DbError},
    logger,
-
    notif::NotificationChannel,
+
    notif::{NotificationChannel, NotificationError},
    pages::StatusPage,
    queueadd::{AdderError, QueueAdderBuilder},
    queueproc::{QueueError, QueueProcessorBuilder},
@@ -116,7 +116,7 @@ impl InsertCmd {
    fn run(&self, args: &Args, config: &Config) -> Result<(), CibError> {
        let mut events_notification = NotificationChannel::default();
        let adder = QueueAdderBuilder::default()
-
            .events_tx(events_notification.tx())
+
            .events_tx(events_notification.tx().map_err(CibError::notification)?)
            .db(args.open_db(config)?)
            .filters(&config.filters)
            .build()
@@ -151,14 +151,18 @@ impl QueuedCmd {
        broker.set_default_adapter(&adapter);

        let mut event_notifications = NotificationChannel::default();
-
        event_notifications.tx().send(()).ok();
+
        event_notifications
+
            .tx()
+
            .map_err(CibError::notification)?
+
            .send(())
+
            .ok();

        let mut run_notifications = NotificationChannel::default();

        let db = args.open_db(config)?;
        let processor = QueueProcessorBuilder::default()
-
            .events_rx(event_notifications.rx())
-
            .run_tx(run_notifications.tx())
+
            .events_rx(event_notifications.rx().map_err(CibError::notification)?)
+
            .run_tx(run_notifications.tx().map_err(CibError::notification)?)
            .db(db)
            .broker(broker)
            .build()
@@ -174,7 +178,12 @@ impl QueuedCmd {
        if let Some(dirname) = &config.report_dir {
            page.set_output_dir(dirname);
        }
-
        let page_updater = page.update_in_thread(run_notifications.rx(), profile, db, true);
+
        let page_updater = page.update_in_thread(
+
            run_notifications.rx().map_err(CibError::notification)?,
+
            profile,
+
            db,
+
            true,
+
        );
        page_updater
            .join()
            .expect("wait for page updater thread to finish")
@@ -193,7 +202,7 @@ impl ProcessEventsCmd {
        let mut run_notification = NotificationChannel::default();

        let adder = QueueAdderBuilder::default()
-
            .events_tx(events_notification.tx())
+
            .events_tx(events_notification.tx().map_err(CibError::notification)?)
            .db(args.open_db(config)?)
            .filters(&config.filters)
            .build()
@@ -208,7 +217,12 @@ impl ProcessEventsCmd {
        if let Some(dirname) = &config.report_dir {
            page.set_output_dir(dirname);
        }
-
        let page_updater = page.update_in_thread(run_notification.rx(), profile, db, false);
+
        let page_updater = page.update_in_thread(
+
            run_notification.rx().map_err(CibError::notification)?,
+
            profile,
+
            db,
+
            false,
+
        );

        let mut broker = Broker::new(config.db()).map_err(CibError::new_broker)?;
        let spec =
@@ -224,8 +238,8 @@ impl ProcessEventsCmd {
        broker.set_default_adapter(&adapter);

        let processor = QueueProcessorBuilder::default()
-
            .events_rx(events_notification.rx())
-
            .run_tx(run_notification.tx())
+
            .events_rx(events_notification.rx().map_err(CibError::notification)?)
+
            .run_tx(run_notification.tx().map_err(CibError::notification)?)
            .db(args.open_db(config)?)
            .broker(broker)
            .build()
@@ -282,6 +296,9 @@ enum CibError {

    #[error("default adapter is not in list of adapters")]
    UnknownDefaultAdapter(String),
+

+
    #[error("programming error: failed to set up inter-thread notification channel")]
+
    Notification(#[source] NotificationError),
}

impl CibError {
@@ -316,4 +333,8 @@ impl CibError {
    fn add_events(e: AdderError) -> Self {
        Self::AddEvents(e)
    }
+

+
    fn notification(e: NotificationError) -> Self {
+
        Self::Notification(e)
+
    }
}
modified src/bin/cibtool.rs
@@ -34,7 +34,7 @@ use radicle_ci_broker::{
    db::{Db, DbError, QueueId, QueuedCiEvent},
    logger,
    msg::{RunId, RunResult},
-
    notif::NotificationChannel,
+
    notif::{NotificationChannel, NotificationError},
    pages::PageError,
    run::{Run, RunState, Whence},
    util::{self, UtilError},
@@ -349,4 +349,7 @@ enum CibToolError {

    #[error("failed to construct a CiEvent::BranchCreated")]
    BranchCreted(#[source] CiEventError),
+

+
    #[error("programming error: failed to set up inter-thread notification channel")]
+
    Notification(#[source] NotificationError),
}
modified src/bin/cibtoolcmd/report.rs
@@ -21,7 +21,12 @@ impl Leaf for ReportCmd {
        page.set_output_dir(&self.output_dir);

        let mut run_notification = NotificationChannel::default();
-
        let thread = page.update_in_thread(run_notification.rx(), profile, db, true);
+
        let thread = page.update_in_thread(
+
            run_notification.rx().map_err(CibToolError::Notification)?,
+
            profile,
+
            db,
+
            true,
+
        );
        thread.join().unwrap()?;

        Ok(())
modified src/ci_event.rs
@@ -241,6 +241,7 @@ impl CiEventError {
}

#[cfg(test)]
+
#[allow(clippy::unwrap_used)]
mod test {
    use super::*;
    use radicle::{prelude::NodeId, storage::RefUpdate};
@@ -533,6 +534,7 @@ impl ParseError {
}

#[cfg(test)]
+
#[allow(clippy::unwrap_used)]
mod test_namespaced_branch {
    use super::{namespaced_branch, ParseError};

@@ -568,6 +570,7 @@ mod test_namespaced_branch {
}

#[cfg(test)]
+
#[allow(clippy::unwrap_used)]
mod test_patch_id {
    use super::{patch_id, Oid, ParseError};

modified src/ci_event_source.rs
@@ -24,14 +24,14 @@ impl CiEventSource {
    pub fn event(&mut self) -> Result<Option<Vec<CiEvent>>, CiEventSourceError> {
        let result = self.source.node_event();
        logger::debug2(format!("ci_event_source: result={result:?}"));
-
        match &result {
-
            Err(NodeEventError::BrokenConnection) => {
+
        match result {
+
            Err(err) if matches!(err, NodeEventError::BrokenConnection) => {
                logger::ci_event_source_disconnected();
-
                Err(CiEventSourceError::BrokenConnection(result.unwrap_err()))
+
                Err(CiEventSourceError::BrokenConnection(err))
            }
            Err(err) => {
                logger::error("error reading event from node", &err);
-
                Err(CiEventSourceError::NodeEventError(result.unwrap_err()))
+
                Err(CiEventSourceError::NodeEventError(err))
            }
            Ok(None) => {
                logger::ci_event_source_end();
@@ -39,7 +39,7 @@ impl CiEventSource {
            }
            Ok(Some(event)) => {
                let ci_events =
-
                    CiEvent::from_node_event(event).map_err(CiEventSourceError::CiEvent)?;
+
                    CiEvent::from_node_event(&event).map_err(CiEventSourceError::CiEvent)?;
                logger::ci_event_source_got_events(&ci_events);
                Ok(Some(ci_events))
            }
modified src/filter.rs
@@ -119,6 +119,7 @@ impl Filters {
}

#[cfg(test)]
+
#[allow(clippy::unwrap_used)]
mod test {
    use radicle::prelude::{Did, RepoId};

modified src/lib.rs
@@ -5,6 +5,8 @@
//! node, filter the events, and to communicate with a adapter spawned
//! as a child process.

+
#![deny(clippy::unwrap_used)]
+

pub mod adapter;
pub mod broker;
pub mod ci_event;
modified src/msg.rs
@@ -407,7 +407,7 @@ impl<'a> RequestBuilder<'a> {
                    }),
                })
            }
-
            _ => Err(MessageError::UnknownCiEvent(self.ci_event.unwrap().clone())),
+
            Some(event) => Err(MessageError::UnknownCiEvent(event.clone())),
        }
    }
}
@@ -906,6 +906,7 @@ pub enum MessageError {
}

#[cfg(test)]
+
#[allow(clippy::unwrap_used)] // OK in tests: panic is fine
pub mod trigger_from_ci_event_tests {
    use crate::ci_event::CiEvent;
    use crate::msg::{EventType, Request, RequestBuilder};
modified src/notif.rs
@@ -35,13 +35,22 @@ impl Default for NotificationChannel {
impl NotificationChannel {
    /// Return the transmit endpoint of the notification channel. This
    /// can only be called once.
-
    pub fn tx(&mut self) -> NotificationSender {
-
        self.tx.take().unwrap()
+
    pub fn tx(&mut self) -> Result<NotificationSender, NotificationError> {
+
        self.tx.take().ok_or(NotificationError::Sender)
    }

    /// Return the receive endpoint of the notification channel. This
    /// can only be called once.
-
    pub fn rx(&mut self) -> NotificationReceiver {
-
        self.rx.take().unwrap()
+
    pub fn rx(&mut self) -> Result<NotificationReceiver, NotificationError> {
+
        self.rx.take().ok_or(NotificationError::Receiver)
    }
}
+

+
#[derive(Debug, thiserror::Error)]
+
pub enum NotificationError {
+
    #[error("receiver end point already extracted")]
+
    Receiver,
+

+
    #[error("sender end point already extracted")]
+
    Sender,
+
}