use core::fmt::{self, Write};
use crate::{
path::Path,
update::Update,
value::{Num, ValueOrRef},
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Math {
pub(crate) dst: Path,
pub(crate) src: Option<Path>,
op: MathOp,
pub(crate) num: ValueOrRef,
}
impl Math {
pub fn builder<T>(dst: T) -> Builder
where
T: Into<Path>,
{
Builder {
dst: dst.into(),
src: None,
}
}
pub fn and<T>(self, other: T) -> Update
where
T: Into<Update>,
{
Update::from(self).and(other)
}
}
impl fmt::Display for Math {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.dst.fmt(f)?;
f.write_str(" = ")?;
self.src.as_ref().unwrap_or(&self.dst).fmt(f)?;
f.write_char(' ')?;
self.op.fmt(f)?;
f.write_char(' ')?;
self.num.fmt(f)
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
enum MathOp {
Add,
Sub,
}
impl fmt::Debug for MathOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
impl fmt::Display for MathOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_char(match self {
MathOp::Add => '+',
MathOp::Sub => '-',
})
}
}
#[must_use = "Consume this `Builder` by using its `.add()` or `.sub()` methods"]
#[derive(Debug, Clone)]
pub struct Builder {
dst: Path,
src: Option<Path>,
}
impl Builder {
pub fn src<T>(mut self, src: T) -> Self
where
T: Into<Path>,
{
self.src = Some(src.into());
self
}
#[allow(clippy::should_implement_trait)]
pub fn add<T>(self, num: T) -> Math
where
T: Into<Num>,
{
self.with_op(MathOp::Add, num)
}
#[allow(clippy::should_implement_trait)]
pub fn sub<T>(self, num: T) -> Math
where
T: Into<Num>,
{
self.with_op(MathOp::Sub, num)
}
fn with_op<T>(self, op: MathOp, num: T) -> Math
where
T: Into<Num>,
{
let Self { dst, src } = self;
Math {
dst,
src,
op,
num: num.into().into(),
}
}
}
#[cfg(test)]
mod test {
use pretty_assertions::assert_eq;
use crate::{
update::{Assign, Set, SetAction},
Num, Path,
};
use super::Math;
#[test]
fn and() -> Result<(), Box<dyn std::error::Error>> {
let math: Math = "foo".parse::<Path>()?.math().add(1);
let assign: Assign = "bar".parse::<Path>()?.set(Num::new(8));
let combined = math.clone().and(assign.clone());
assert_eq!(r#"SET foo = foo + 1, bar = 8"#, combined.to_string());
let combined = math.clone().and(SetAction::from(assign.clone()));
assert_eq!(r#"SET foo = foo + 1, bar = 8"#, combined.to_string());
let set: Set = [
SetAction::from(assign),
SetAction::from("baz".parse::<Path>()?.list_append().list(["d", "e", "f"])),
]
.into_iter()
.collect();
let combined = math.clone().and(set);
assert_eq!(
r#"SET foo = foo + 1, bar = 8, baz = list_append(baz, ["d", "e", "f"])"#,
combined.to_string()
);
let combined = math.clone().and("quux".parse::<Path>()?.remove());
assert_eq!(r#"SET foo = foo + 1 REMOVE quux"#, combined.to_string());
let combined = math.and("quux".parse::<Path>()?.remove());
assert_eq!(r#"SET foo = foo + 1 REMOVE quux"#, combined.to_string());
Ok(())
}
}