use crate::{AsyncExposureBoundary, Constitution, ModuleBoundary, Severity};
#[derive(Debug, Clone)]
pub struct SansIoPure {
crate_package: String,
module: String,
time_prefix: String,
read_verbs: Vec<String>,
severity: Severity,
reason: String,
}
impl SansIoPure {
pub fn in_crate(package: &str) -> SansIoPureCrateDraft {
SansIoPureCrateDraft {
crate_package: package.to_string(),
}
}
}
pub struct SansIoPureCrateDraft {
crate_package: String,
}
impl SansIoPureCrateDraft {
pub fn module(self, module: &str) -> SansIoPureModuleDraft {
SansIoPureModuleDraft {
crate_package: self.crate_package,
module: module.to_string(),
}
}
}
pub struct SansIoPureModuleDraft {
crate_package: String,
module: String,
}
impl SansIoPureModuleDraft {
pub fn reading_clock_via<I, S>(self, time_prefix: &str, read_verbs: I) -> SansIoPureDraft
where
I: IntoIterator<Item = S>,
S: Into<String>,
{
SansIoPureDraft {
crate_package: self.crate_package,
module: self.module,
time_prefix: time_prefix.to_string(),
read_verbs: read_verbs.into_iter().map(Into::into).collect(),
severity: Severity::Enforce,
}
}
}
pub struct SansIoPureDraft {
crate_package: String,
module: String,
time_prefix: String,
read_verbs: Vec<String>,
severity: Severity,
}
impl SansIoPureDraft {
pub fn warn(mut self) -> Self {
self.severity = Severity::Warn;
self
}
pub fn because(self, reason: &str) -> SansIoPure {
SansIoPure {
crate_package: self.crate_package,
module: self.module,
time_prefix: self.time_prefix,
read_verbs: self.read_verbs,
severity: self.severity,
reason: reason.to_string(),
}
}
}
impl Constitution {
pub fn sans_io_pure(self, profile: SansIoPure) -> Self {
let SansIoPure {
crate_package,
module,
time_prefix,
read_verbs,
severity,
reason,
} = profile;
let clock = ModuleBoundary::in_crate(&crate_package)
.module(&module)
.must_not_call_inline(&time_prefix)
.ending_with(read_verbs.iter().cloned());
let clock = if severity == Severity::Warn {
clock.warn()
} else {
clock
};
let sync_api = AsyncExposureBoundary::in_crate(&crate_package)
.module(&module)
.must_not_expose_async_fn()
.including_submodules();
let sync_api = if severity == Severity::Warn {
sync_api.warn()
} else {
sync_api
};
self.boundary(clock.because(&reason))
.async_exposure_boundary(sync_api.because(&reason))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::constitution_markdown;
fn hand_composed(prefix: &str, verbs: &[&str], warn: bool) -> Constitution {
let clock = ModuleBoundary::in_crate("pacta-contract")
.module("crate::kernel")
.must_not_call_inline(prefix)
.ending_with(verbs.iter().copied());
let clock = if warn { clock.warn() } else { clock };
let sync_api = AsyncExposureBoundary::in_crate("pacta-contract")
.module("crate::kernel")
.must_not_expose_async_fn()
.including_submodules();
let sync_api = if warn { sync_api.warn() } else { sync_api };
Constitution::new("pacta")
.boundary(clock.because("the kernel stays sans-I/O"))
.async_exposure_boundary(sync_api.because("the kernel stays sans-I/O"))
}
fn via_profile(prefix: &str, verbs: &[&str], warn: bool) -> Constitution {
let profile = SansIoPure::in_crate("pacta-contract")
.module("crate::kernel")
.reading_clock_via(prefix, verbs.iter().copied());
let profile = if warn { profile.warn() } else { profile };
Constitution::new("pacta").sans_io_pure(profile.because("the kernel stays sans-I/O"))
}
#[test]
fn sans_io_pure_composes_faithfully() {
assert_eq!(
constitution_markdown(&via_profile("std::time", &["now"], false)),
constitution_markdown(&hand_composed("std::time", &["now"], false)),
);
}
#[test]
fn sans_io_pure_threads_severity_to_both() {
assert_eq!(
constitution_markdown(&via_profile("std::time", &["now"], true)),
constitution_markdown(&hand_composed("std::time", &["now"], true)),
);
assert_ne!(
constitution_markdown(&via_profile("std::time", &["now"], true)),
constitution_markdown(&via_profile("std::time", &["now"], false)),
);
}
#[test]
fn sans_io_pure_bakes_no_defaults() {
assert_eq!(
constitution_markdown(&via_profile("quanta::clock", &["fetch", "raw"], false)),
constitution_markdown(&hand_composed("quanta::clock", &["fetch", "raw"], false)),
);
}
}