1#![allow(dead_code, clippy::type_complexity, clippy::too_many_arguments)]
7mod exclusive;
29mod shared;
30
31pub use exclusive::{ExclusiveLatch, ExclusiveLatchGuard};
32pub use shared::{SharedLatch, SharedLatchReadGuard, SharedLatchWriteGuard};
33
34use std::fmt;
35use std::time::Duration;
36
37pub const DEFAULT_LATCH_TIMEOUT: Duration = Duration::from_secs(5);
39
40#[derive(Debug, Clone)]
45pub struct LatchContext {
46 pub name: String,
48 pub timeout: Duration,
50}
51
52impl LatchContext {
53 pub fn new(name: impl Into<String>) -> Self {
55 LatchContext { name: name.into(), timeout: DEFAULT_LATCH_TIMEOUT }
56 }
57
58 pub fn with_timeout(name: impl Into<String>, timeout: Duration) -> Self {
60 LatchContext { name: name.into(), timeout }
61 }
62}
63
64impl fmt::Display for LatchContext {
65 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66 write!(f, "{}", self.name)
67 }
68}
69
70#[derive(Debug)]
72pub enum LatchError {
73 AlreadyHeld(String),
75 NotHeld(String),
77 Timeout(String),
79}
80
81impl fmt::Display for LatchError {
82 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83 match self {
84 LatchError::AlreadyHeld(msg) => {
85 write!(f, "Latch already held: {}", msg)
86 }
87 LatchError::NotHeld(msg) => write!(f, "Latch not held: {}", msg),
88 LatchError::Timeout(msg) => write!(f, "Latch timeout: {}", msg),
89 }
90 }
91}
92
93impl std::error::Error for LatchError {}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98
99 #[test]
100 fn test_latch_context_default_timeout() {
101 let ctx = LatchContext::new("my-latch");
102 assert_eq!(ctx.name, "my-latch");
103 assert_eq!(ctx.timeout, DEFAULT_LATCH_TIMEOUT);
104 }
105
106 #[test]
107 fn test_latch_context_with_timeout() {
108 use std::time::Duration;
109 let ctx =
110 LatchContext::with_timeout("custom", Duration::from_millis(100));
111 assert_eq!(ctx.name, "custom");
112 assert_eq!(ctx.timeout, Duration::from_millis(100));
113 }
114
115 #[test]
116 fn test_latch_context_display() {
117 let ctx = LatchContext::new("test-display");
118 assert_eq!(format!("{}", ctx), "test-display");
119 }
120
121 #[test]
122 fn test_latch_error_display() {
123 let e1 = LatchError::AlreadyHeld("foo".to_string());
124 assert!(
125 format!("{}", e1).contains("already held")
126 || format!("{}", e1).contains("Latch already held")
127 );
128
129 let e2 = LatchError::NotHeld("bar".to_string());
130 assert!(
131 format!("{}", e2).contains("not held")
132 || format!("{}", e2).contains("Latch not held")
133 );
134
135 let e3 = LatchError::Timeout("baz".to_string());
136 assert!(
137 format!("{}", e3).contains("timeout")
138 || format!("{}", e3).contains("Latch timeout")
139 );
140 }
141
142 #[test]
143 fn test_latch_error_is_error() {
144 use std::error::Error;
145 let e = LatchError::Timeout("x".to_string());
146 let _: &dyn Error = &e;
147 }
148}