#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RequestKind {
Algebraic { family: String, operation: String },
Host { service: String },
Query { name: String },
SourceHole { site: String, contract: String },
}
impl RequestKind {
pub fn algebraic(family: impl Into<String>, operation: impl Into<String>) -> Self {
Self::Algebraic {
family: family.into(),
operation: operation.into(),
}
}
pub fn source_hole(site: impl Into<String>, contract: impl Into<String>) -> Self {
Self::SourceHole {
site: site.into(),
contract: contract.into(),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RequestInterface {
answer: String,
requests: Vec<RequestKind>,
}
impl RequestInterface {
pub fn new(answer: impl Into<String>) -> Self {
Self {
answer: answer.into(),
requests: Vec::new(),
}
}
pub fn with(mut self, request: RequestKind) -> Self {
self.requests.push(request);
self
}
pub fn answer(&self) -> &str {
&self.answer
}
pub fn requests(&self) -> &[RequestKind] {
&self.requests
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RequestSite {
kind: RequestKind,
result: String,
local_answer: String,
}
impl RequestSite {
pub fn algebraic(
family: impl Into<String>,
operation: impl Into<String>,
result: impl Into<String>,
local_answer: impl Into<String>,
) -> Self {
Self {
kind: RequestKind::algebraic(family, operation),
result: result.into(),
local_answer: local_answer.into(),
}
}
pub fn residual_interface(&self) -> RequestInterface {
RequestInterface::new(self.local_answer.clone()).with(self.kind.clone())
}
pub fn result(&self) -> &str {
&self.result
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Delimiter {
input_answer: String,
output_answer: String,
handled: Vec<RequestKind>,
}
impl Delimiter {
pub fn new(input_answer: impl Into<String>, output_answer: impl Into<String>) -> Self {
Self {
input_answer: input_answer.into(),
output_answer: output_answer.into(),
handled: Vec::new(),
}
}
pub fn handles(mut self, family: impl Into<String>, operation: impl Into<String>) -> Self {
self.handled.push(RequestKind::algebraic(family, operation));
self
}
pub fn transform(&self, interface: &RequestInterface) -> RequestInterface {
assert_eq!(interface.answer(), self.input_answer);
interface
.requests()
.iter()
.filter(|request| !self.handled.contains(request))
.cloned()
.fold(
RequestInterface::new(self.output_answer.clone()),
RequestInterface::with,
)
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct CommuteEvidence {
request_order: bool,
resource: bool,
boundary: bool,
}
impl CommuteEvidence {
pub fn all() -> Self {
Self {
request_order: true,
resource: true,
boundary: true,
}
}
pub fn resource_only() -> Self {
Self {
resource: true,
..Self::default()
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CommuteError {
MissingOrderProof,
MissingResourceProof,
MissingBoundaryProof,
}
pub fn can_commute(
_left: &RequestSite,
_right: &RequestSite,
evidence: CommuteEvidence,
) -> Result<(), CommuteError> {
if !evidence.request_order {
return Err(CommuteError::MissingOrderProof);
}
if !evidence.resource {
return Err(CommuteError::MissingResourceProof);
}
if !evidence.boundary {
return Err(CommuteError::MissingBoundaryProof);
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn site_exports_interface() {
let site = RequestSite::algebraic("Console", "read_line", "String", "Answer");
let interface = site.residual_interface();
assert_eq!(interface.answer(), "Answer");
assert_eq!(
interface.requests(),
&[RequestKind::algebraic("Console", "read_line")]
);
}
#[test]
fn transform_keeps_unhandled_request() {
let input = RequestInterface::new("A")
.with(RequestKind::algebraic("State", "get"))
.with(RequestKind::algebraic("State", "put"));
let delimiter = Delimiter::new("A", "B").handles("State", "get");
let output = delimiter.transform(&input);
assert_eq!(output.answer(), "B");
assert_eq!(output.requests(), &[RequestKind::algebraic("State", "put")]);
}
#[test]
fn commute_requires_all_evidence() {
let a = RequestSite::algebraic("Log", "emit", "Unit", "Answer");
let b = RequestSite::algebraic("Log", "emit", "Unit", "Answer");
assert_eq!(
can_commute(&a, &b, CommuteEvidence::resource_only()),
Err(CommuteError::MissingOrderProof)
);
assert!(can_commute(&a, &b, CommuteEvidence::all()).is_ok());
}
}