#![no_std]
#![forbid(unsafe_code)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum FinishError {
InvalidProposal,
ContextChanged,
ValueChanged,
}
impl core::fmt::Display for FinishError {
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
formatter.write_str(match self {
Self::InvalidProposal => "the latest proposal is invalid",
Self::ContextChanged => "the edit context has changed",
Self::ValueChanged => "the live value has changed",
})
}
}
impl core::error::Error for FinishError {}
#[derive(Debug, PartialEq, Eq)]
pub struct Change<T> {
pub before: T,
pub after: T,
}
#[must_use = "a draft must be retained, finished, or deliberately cancelled"]
#[derive(Debug)]
pub struct EditSession<T, C> {
original: T,
preview: T,
context: C,
valid: bool,
}
impl<T: Clone, C> EditSession<T, C> {
pub fn new(original: T, context: C) -> Self {
Self {
preview: original.clone(),
original,
context,
valid: true,
}
}
pub fn reset(&mut self) {
self.preview = self.original.clone();
self.valid = true;
}
}
impl<T, C> EditSession<T, C> {
pub fn original(&self) -> &T {
&self.original
}
pub fn preview(&self) -> &T {
&self.preview
}
pub fn context(&self) -> &C {
&self.context
}
pub fn is_valid(&self) -> bool {
self.valid
}
pub fn propose<E>(&mut self, proposal: Result<T, E>) -> Result<(), E> {
match proposal {
Ok(value) => {
self.preview = value;
self.valid = true;
Ok(())
}
Err(error) => {
self.valid = false;
Err(error)
}
}
}
pub fn cancel(self) {}
}
impl<T: PartialEq, C: PartialEq> EditSession<T, C> {
pub fn check_live(&self, live: &T, context: &C) -> Result<(), FinishError> {
if *context != self.context {
Err(FinishError::ContextChanged)
} else if *live != self.original {
Err(FinishError::ValueChanged)
} else {
Ok(())
}
}
pub fn check(&self, live: &T, context: &C) -> Result<(), FinishError> {
self.check_live(live, context)?;
if !self.valid {
Err(FinishError::InvalidProposal)
} else {
Ok(())
}
}
pub fn finish(self, live: &T, context: &C) -> Result<Option<Change<T>>, FinishError> {
self.check(live, context)?;
if self.original == self.preview {
Ok(None)
} else {
Ok(Some(Change {
before: self.original,
after: self.preview,
}))
}
}
}
const _: () = ();