use std::sync::atomic::{
AtomicBool, AtomicI8, AtomicI16, AtomicI32, AtomicI64, AtomicIsize, AtomicU8, AtomicU16,
AtomicU32, AtomicU64, AtomicUsize,
};
use crate::{
ElicitComplete, ElicitSpec, SpecCategoryBuilder, SpecEntryBuilder, TypeSpec, TypeSpecBuilder,
TypeSpecInventoryKey,
};
fn atomic_construction_category(type_name: &str) -> crate::SpecCategory {
SpecCategoryBuilder::default()
.name("construction".to_string())
.entries(vec![
SpecEntryBuilder::default()
.label("new".to_string())
.description(format!(
"Construct with {type_name}::new(val) where val is the underlying primitive."
))
.expression(Some(format!("{type_name}::new(val)")))
.build()
.expect("valid new entry"),
SpecEntryBuilder::default()
.label("load".to_string())
.description(
"Read the current value with .load(Ordering::SeqCst). \
SeqCst is the safest ordering; use weaker orderings only when you \
understand the memory model implications."
.to_string(),
)
.expression(Some(
"value.load(std::sync::atomic::Ordering::SeqCst)".to_string(),
))
.build()
.expect("valid load entry"),
])
.build()
.expect("valid construction category")
}
fn atomic_thread_safety_category() -> crate::SpecCategory {
SpecCategoryBuilder::default()
.name("thread_safety".to_string())
.entries(vec![
SpecEntryBuilder::default()
.label("send_sync".to_string())
.description(
"All atomic types implement Send + Sync, making them safe to share \
across threads without a Mutex."
.to_string(),
)
.build()
.expect("valid send_sync entry"),
SpecEntryBuilder::default()
.label("not_clone".to_string())
.description(
"Atomic types do not implement Clone. To copy the value, load it \
and construct a new atomic: Atomic*::new(val.load(Ordering::SeqCst))."
.to_string(),
)
.build()
.expect("valid not_clone entry"),
])
.build()
.expect("valid thread_safety category")
}
macro_rules! impl_atomic_bool_spec {
($atomic:ty, $name:literal) => {
impl ElicitSpec for $atomic {
fn type_spec() -> TypeSpec {
let values = SpecCategoryBuilder::default()
.name("values".to_string())
.entries(vec![
SpecEntryBuilder::default()
.label("true".to_string())
.description(
"Logical true. Accepted inputs: \"true\", \"yes\", \"1\", \"y\"."
.to_string(),
)
.build()
.expect("valid entry"),
SpecEntryBuilder::default()
.label("false".to_string())
.description(
"Logical false. Accepted inputs: \"false\", \"no\", \"0\", \"n\"."
.to_string(),
)
.build()
.expect("valid entry"),
])
.build()
.expect("valid values");
TypeSpecBuilder::default()
.type_name($name.to_string())
.summary(concat!(
"A thread-safe atomic boolean (wraps bool). \
Construct with ",
$name,
"::new(true/false)."
).to_string())
.categories(vec![
values,
atomic_construction_category($name),
atomic_thread_safety_category(),
])
.build()
.expect("valid TypeSpec")
}
}
inventory::submit!(TypeSpecInventoryKey::new(
$name,
<$atomic as ElicitSpec>::type_spec,
std::any::TypeId::of::<$atomic>
));
impl ElicitComplete for $atomic {}
};
}
macro_rules! impl_atomic_integer_spec {
(
type = $atomic:ty,
name = $name:literal,
summary = $summary:literal,
min = $min:literal,
max = $max:literal $(,)?
) => {
impl ElicitSpec for $atomic {
fn type_spec() -> TypeSpec {
let bounds = SpecCategoryBuilder::default()
.name("bounds".to_string())
.entries(vec![
SpecEntryBuilder::default()
.label("min".to_string())
.description(concat!("Minimum value: ", $min).to_string())
.build()
.expect("valid min entry"),
SpecEntryBuilder::default()
.label("max".to_string())
.description(concat!("Maximum value: ", $max).to_string())
.build()
.expect("valid max entry"),
])
.build()
.expect("valid bounds category");
TypeSpecBuilder::default()
.type_name($name.to_string())
.summary($summary.to_string())
.categories(vec![
bounds,
atomic_construction_category($name),
atomic_thread_safety_category(),
])
.build()
.expect("valid TypeSpec")
}
}
inventory::submit!(TypeSpecInventoryKey::new(
$name,
<$atomic as ElicitSpec>::type_spec,
std::any::TypeId::of::<$atomic>
));
impl ElicitComplete for $atomic {}
};
}
impl_atomic_bool_spec!(AtomicBool, "std::sync::atomic::AtomicBool");
impl_atomic_integer_spec!(
type = AtomicI8,
name = "std::sync::atomic::AtomicI8",
summary = "Thread-safe atomic 8-bit signed integer (wraps i8). Range: -128 to 127.",
min = "-128",
max = "127",
);
impl_atomic_integer_spec!(
type = AtomicI16,
name = "std::sync::atomic::AtomicI16",
summary = "Thread-safe atomic 16-bit signed integer (wraps i16). Range: -32768 to 32767.",
min = "-32768",
max = "32767",
);
impl_atomic_integer_spec!(
type = AtomicI32,
name = "std::sync::atomic::AtomicI32",
summary = "Thread-safe atomic 32-bit signed integer (wraps i32). Range: -2147483648 to 2147483647.",
min = "-2147483648",
max = "2147483647",
);
impl_atomic_integer_spec!(
type = AtomicI64,
name = "std::sync::atomic::AtomicI64",
summary = "Thread-safe atomic 64-bit signed integer (wraps i64). Range: i64::MIN to i64::MAX.",
min = "-9223372036854775808",
max = "9223372036854775807",
);
impl_atomic_integer_spec!(
type = AtomicIsize,
name = "std::sync::atomic::AtomicIsize",
summary = "Thread-safe atomic pointer-sized signed integer (wraps isize, platform-dependent size).",
min = "isize::MIN (platform-dependent)",
max = "isize::MAX (platform-dependent)",
);
impl_atomic_integer_spec!(
type = AtomicU8,
name = "std::sync::atomic::AtomicU8",
summary = "Thread-safe atomic 8-bit unsigned integer (wraps u8). Range: 0 to 255.",
min = "0",
max = "255",
);
impl_atomic_integer_spec!(
type = AtomicU16,
name = "std::sync::atomic::AtomicU16",
summary = "Thread-safe atomic 16-bit unsigned integer (wraps u16). Range: 0 to 65535.",
min = "0",
max = "65535",
);
impl_atomic_integer_spec!(
type = AtomicU32,
name = "std::sync::atomic::AtomicU32",
summary = "Thread-safe atomic 32-bit unsigned integer (wraps u32). Range: 0 to 4294967295.",
min = "0",
max = "4294967295",
);
impl_atomic_integer_spec!(
type = AtomicU64,
name = "std::sync::atomic::AtomicU64",
summary = "Thread-safe atomic 64-bit unsigned integer (wraps u64). Range: 0 to u64::MAX.",
min = "0",
max = "18446744073709551615",
);
impl_atomic_integer_spec!(
type = AtomicUsize,
name = "std::sync::atomic::AtomicUsize",
summary = "Thread-safe atomic pointer-sized unsigned integer (wraps usize, platform-dependent size).",
min = "0",
max = "usize::MAX (platform-dependent)",
);