use core::fmt::{self, Write};
use crate::{
path::Path,
update::Update,
value::{List, ValueOrRef},
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ListAppend {
pub(crate) dst: Path,
pub(crate) src: Option<Path>,
pub(crate) list: ValueOrRef,
after: bool,
}
impl ListAppend {
pub fn builder<T>(dst: T) -> Builder
where
T: Into<Path>,
{
Builder {
dst: dst.into(),
src: None,
after: true,
}
}
pub fn and<T>(self, other: T) -> Update
where
T: Into<Update>,
{
Update::from(self).and(other)
}
}
impl fmt::Display for ListAppend {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.dst.fmt(f)?;
f.write_str(" = list_append(")?;
let src = self.src.as_ref().unwrap_or(&self.dst);
let (first, second): (&dyn fmt::Display, &dyn fmt::Display) = if self.after {
(src, &self.list)
} else {
(&self.list, src)
};
first.fmt(f)?;
f.write_str(", ")?;
second.fmt(f)?;
f.write_char(')')
}
}
#[must_use = "Consume this `Builder` by using its `.list()` method"]
#[derive(Debug, Clone)]
pub struct Builder {
dst: Path,
src: Option<Path>,
after: bool,
}
impl Builder {
pub fn src<T>(mut self, src: T) -> Self
where
T: Into<Path>,
{
self.src = Some(src.into());
self
}
pub fn after(mut self) -> Self {
self.after = true;
self
}
pub fn before(mut self) -> Self {
self.after = false;
self
}
pub fn list<T>(self, list: T) -> ListAppend
where
T: Into<List>,
{
let Self { dst, src, after } = self;
ListAppend {
dst,
src,
after,
list: list.into().into(),
}
}
}
#[cfg(test)]
mod test {
use pretty_assertions::assert_eq;
use crate::{
update::{Assign, Set, SetAction},
Num, Path,
};
use super::ListAppend;
#[test]
fn display() -> Result<(), Box<dyn std::error::Error>> {
let append = ListAppend::builder("foo".parse::<Path>()?)
.src("bar".parse::<Path>()?)
.after()
.list(["a", "b"]);
assert_eq!(r#"foo = list_append(bar, ["a", "b"])"#, append.to_string());
let append = ListAppend::builder("foo".parse::<Path>()?)
.src("bar".parse::<Path>()?)
.list(["a", "b"]);
assert_eq!(r#"foo = list_append(bar, ["a", "b"])"#, append.to_string());
let append = ListAppend::builder("foo".parse::<Path>()?)
.src("bar".parse::<Path>()?)
.before()
.list(["a", "b"]);
assert_eq!(r#"foo = list_append(["a", "b"], bar)"#, append.to_string());
let append = ListAppend::builder("foo".parse::<Path>()?)
.after()
.list(["a", "b"]);
assert_eq!(r#"foo = list_append(foo, ["a", "b"])"#, append.to_string());
let append = ListAppend::builder("foo".parse::<Path>()?).list(["a", "b"]);
assert_eq!(r#"foo = list_append(foo, ["a", "b"])"#, append.to_string());
let append = ListAppend::builder("foo".parse::<Path>()?)
.before()
.list(["a", "b"]);
assert_eq!(r#"foo = list_append(["a", "b"], foo)"#, append.to_string());
Ok(())
}
#[test]
fn and() -> Result<(), Box<dyn std::error::Error>> {
let list_append = "foo".parse::<Path>()?.list_append().list(["d", "e", "f"]);
let assign: Assign = "bar".parse::<Path>()?.set(Num::new(8));
let combined = list_append.clone().and(assign.clone());
assert_eq!(
r#"SET foo = list_append(foo, ["d", "e", "f"]), bar = 8"#,
combined.to_string()
);
let combined = list_append.clone().and(SetAction::from(assign.clone()));
assert_eq!(
r#"SET foo = list_append(foo, ["d", "e", "f"]), bar = 8"#,
combined.to_string()
);
let set: Set = [
SetAction::from(assign),
SetAction::from("baz".parse::<Path>()?.math().add(1)),
]
.into_iter()
.collect();
let combined = list_append.clone().and(set);
assert_eq!(
r#"SET foo = list_append(foo, ["d", "e", "f"]), bar = 8, baz = baz + 1"#,
combined.to_string()
);
let combined = list_append.clone().and("quux".parse::<Path>()?.remove());
assert_eq!(
r#"SET foo = list_append(foo, ["d", "e", "f"]) REMOVE quux"#,
combined.to_string()
);
let combined = list_append.and("quux".parse::<Path>()?.remove());
assert_eq!(
r#"SET foo = list_append(foo, ["d", "e", "f"]) REMOVE quux"#,
combined.to_string()
);
Ok(())
}
}