iceoryx2_bb_elementary/
scope_guard.rs

1// Copyright (c) 2023 Contributors to the Eclipse Foundation
2//
3// See the NOTICE file(s) distributed with this work for additional
4// information regarding copyright ownership.
5//
6// This program and the accompanying materials are made available under the
7// terms of the Apache Software License 2.0 which is available at
8// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license
9// which is available at https://opensource.org/licenses/MIT.
10//
11// SPDX-License-Identifier: Apache-2.0 OR MIT
12
13//! A building block to handle resources that need to be explicitly cleaned up.
14//!
15//! Useful when building higher level abstractions of low level hardware/OS resources.
16//!
17//! # Example
18//!
19//! ```
20//! use iceoryx2_bb_elementary::scope_guard::*;
21//!
22//! fn acquire_resource() -> u64 { 123 }
23//! fn release_resource(value: u64) { println!("release resource {}", value); }
24//! fn do_stuff_with_resource(value: u64) { println!("do stuff with resource {}", value); }
25//!
26//! pub enum ResourceCreationFailure {
27//!     OutOfResources
28//! }
29//!
30//! pub fn do_things_with_resources() -> Result<(), ResourceCreationFailure> {
31//!     let resource = ScopeGuardBuilder::new(acquire_resource())
32//!                     .on_init(|resource| {
33//!                         if *resource == 0 {
34//!                             return Err(ResourceCreationFailure::OutOfResources);
35//!                         }
36//!
37//!                         println!("acquired resource: {}", resource);
38//!                         Ok(())
39//!                     })
40//!                     .on_drop(|resource| {
41//!                         release_resource(*resource);
42//!                     }).create()?;
43//!
44//!     do_stuff_with_resource(*resource.get());
45//!
46//!     // resource goes out of scope and `release_resource` is called automatically
47//!     Ok(())
48//! }
49//! ```
50
51/// The builder to create a [`ScopeGuard`].
52pub struct ScopeGuardBuilder<T, E, Finit: FnOnce(&mut T) -> Result<(), E>, Fdrop: FnOnce(&mut T)> {
53    value: T,
54    on_drop: Option<Fdrop>,
55    on_init: Option<Finit>,
56}
57
58impl<T, E, Finit: FnOnce(&mut T) -> Result<(), E>, Fdrop: FnOnce(&mut T)>
59    ScopeGuardBuilder<T, E, Finit, Fdrop>
60{
61    /// Creates a new scope guard be providing the initial value of the guarded object
62    pub fn new(value: T) -> Self {
63        ScopeGuardBuilder {
64            value,
65            on_drop: None,
66            on_init: None,
67        }
68    }
69
70    /// Callback which is called directly after create. Can be used to verify if the resource
71    /// was acquired correctly. Must return a [`Result`] of type `Result<(), E>`
72    pub fn on_init(mut self, on_init: Finit) -> Self {
73        self.on_init = Some(on_init);
74        self
75    }
76
77    /// Callback which is called when the [`ScopeGuard`] goes out of scope. It shall be used to
78    /// clean up the acquired resource.
79    pub fn on_drop(mut self, on_drop: Fdrop) -> Self {
80        self.on_drop = Some(on_drop);
81        self
82    }
83
84    /// Creates a new scope guard. If the provided callback in [`ScopeGuardBuilder::on_init()`]
85    /// succeeds it returns the [`ScopeGuard`] otherwise the error value which was returned by the
86    /// init function.
87    pub fn create(mut self) -> Result<ScopeGuard<T, Fdrop>, E> {
88        if self.on_init.is_some() {
89            self.on_init.unwrap()(&mut self.value)?;
90        }
91
92        Ok(ScopeGuard {
93            value: self.value,
94            on_drop: self.on_drop,
95        })
96    }
97}
98
99/// A guard which calls a callback to cleanup resources when it goes out of scope. Can be created
100/// with the [`ScopeGuardBuilder`].
101pub struct ScopeGuard<T, F: FnOnce(&mut T)> {
102    value: T,
103    on_drop: Option<F>,
104}
105
106impl<T, F: FnOnce(&mut T)> ScopeGuard<T, F> {
107    /// Returns a mutable reference to the underlying object
108    pub fn get_mut(&mut self) -> &mut T {
109        &mut self.value
110    }
111
112    /// Returns a reference to the underlying object
113    pub fn get(&self) -> &T {
114        &self.value
115    }
116}
117
118impl<T, F: FnOnce(&mut T)> Drop for ScopeGuard<T, F> {
119    fn drop(&mut self) {
120        if let Some(f) = self.on_drop.take() {
121            f(&mut self.value);
122        }
123    }
124}