aboutsummaryrefslogtreecommitdiff
path: root/src/common/query.rs
diff options
context:
space:
mode:
authorevuez <julien@mulga.net>2022-11-26 15:38:06 -0500
committerevuez <julien@mulga.net>2024-04-03 22:44:12 +0200
commit86098797034cbc7eb6db0cee54e17f8dcaedbc5d (patch)
tree29b6225ead843eb9022296a54657bbadfa1c4da0 /src/common/query.rs
downloadblom-86098797034cbc7eb6db0cee54e17f8dcaedbc5d.tar.gz
Initial commitHEADmain
Diffstat (limited to 'src/common/query.rs')
-rw-r--r--src/common/query.rs252
1 files changed, 252 insertions, 0 deletions
diff --git a/src/common/query.rs b/src/common/query.rs
new file mode 100644
index 0000000..00d79b2
--- /dev/null
+++ b/src/common/query.rs
@@ -0,0 +1,252 @@
+use crate::common::db::Database;
+use crate::common::expr::Expr;
+use crate::common::ip;
+use std::fmt;
+
+#[derive(Debug)]
+pub enum Error {
+ Empty,
+ Invalid,
+ IP(ip::Error),
+ ExpectedArray(Expr),
+ ExpectedBlob(Expr),
+ InvalidArgsCount {
+ cmd: String,
+ expected: usize,
+ actual: usize,
+ },
+}
+
+#[derive(Debug)]
+pub enum Query {
+ Children { ip_block: ip::Block },
+ Del { ip_block: ip::Block },
+ Echo { msg: Vec<u8> },
+ Exists { ip_block: ip::Block },
+ Get { ip_block: ip::Block },
+ Info { ip_block: ip::Block },
+ Parent { ip_block: ip::Block },
+ Set { ip_block: ip::Block, value: Expr },
+}
+
+impl Query {
+ pub fn from_expr(expr: Expr) -> Result<Self, Error> {
+ let mut xs = if let Expr::Array(xs) = expr {
+ xs
+ } else {
+ return Err(Error::ExpectedArray(expr));
+ };
+
+ if xs.is_empty() {
+ return Err(Error::Empty);
+ }
+ let cmd_expr = xs.remove(0);
+ let cmd = if let Expr::Blob(cmd) = cmd_expr {
+ cmd
+ } else {
+ return Err(Error::ExpectedBlob(cmd_expr));
+ };
+
+ match &*cmd {
+ b"ECHO" => build_echo(xs), // ECHO str
+ b"SET" => build_set(xs), // SET ::/32 Expr
+ b"GET" => build_get(xs), // GET ::/32
+ b"DEL" => build_del(xs), // DEL ::/32
+ b"EXISTS" => build_exists(xs), // EXISTS ::/32
+ b"INFO" => build_info(xs), // EXISTS ::/32
+ b"PARENT" => build_parent(xs), // SUP ::/32
+ b"CHILDREN" => build_children(xs), // SUB ::/32
+ _ => Err(Error::Invalid),
+ }
+ }
+
+ pub fn exec(&self, db: &mut Database) -> Expr {
+ match self {
+ Self::Echo { msg } => Expr::Blob(msg.clone()),
+ Self::Set { ip_block, value: _ } => {
+ db.set(*ip_block, std::collections::HashMap::new());
+ Expr::Blob(b"OK".to_vec())
+ }
+ Self::Get { ip_block } => db.get(ip_block).map(|x| x.into()).unwrap_or(Expr::Null),
+ Self::Del { ip_block } => db
+ .del(ip_block)
+ .map_or(Expr::Null, |_| Expr::Blob(b"OK".to_vec())),
+ Self::Exists { ip_block } => Expr::Bool(db.exists(ip_block)),
+ Self::Info { ip_block } => {
+ let info: Vec<(&str, String)> = db.info(ip_block).into();
+ info.into()
+ }
+ Self::Parent { ip_block } => {
+ db.parent(ip_block).map(|x| x.into()).unwrap_or(Expr::Null)
+ }
+ Self::Children { ip_block } => db.children(ip_block).into(),
+ }
+ }
+}
+
+// Display
+
+impl fmt::Display for Query {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let res = match self {
+ Query::Set { ip_block, value } => {
+ format!("SET {} {}", ip_block, value)
+ }
+ Query::Get { ip_block } => {
+ format!("GET {}", ip_block)
+ }
+ Query::Echo { msg } => {
+ format!("ECHO {:?}", String::from_utf8_lossy(msg).into_owned())
+ }
+ Query::Del { ip_block } => {
+ format!("DEL {}", ip_block)
+ }
+ Query::Exists { ip_block } => {
+ format!("EXISTS {}", ip_block)
+ }
+ Query::Info { ip_block } => {
+ format!("INFO {}", ip_block)
+ }
+ Query::Parent { ip_block } => {
+ format!("PARENT {}", ip_block)
+ }
+ Query::Children { ip_block } => {
+ format!("CHILDREN {}", ip_block)
+ }
+ };
+
+ write!(f, "{}", res)
+ }
+}
+
+impl fmt::Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let res = match self {
+ Error::Empty => "Received an empty query.".to_string(),
+ Error::ExpectedArray(got) => format!("Expected an ARRAY, got: {:?}.", got),
+ Error::ExpectedBlob(got) => format!("Expected a BLOB, got: {:?}.", got),
+ Error::IP(e) => format!("IP error: {e:?}"),
+ Error::Invalid => "Received an invalid query.".to_string(),
+ Error::InvalidArgsCount {
+ cmd,
+ expected,
+ actual,
+ } => format!(
+ "Invalid number of arguments for {:?}. Expected {} but got {}.",
+ cmd, expected, actual
+ ),
+ };
+
+ write!(f, "{}", res)
+ }
+}
+
+// Builders
+
+fn build_echo(mut args: Vec<Expr>) -> Result<Query, Error> {
+ count_args("ECHO", &args, 1)?;
+
+ let expr = args.remove(0);
+ let msg = if let Expr::Blob(k) = expr {
+ k
+ } else {
+ return Err(Error::ExpectedBlob(expr));
+ };
+ Ok(Query::Echo { msg })
+}
+
+fn build_get(mut args: Vec<Expr>) -> Result<Query, Error> {
+ count_args("GET", &args, 1)?;
+
+ let expr = args.remove(0);
+ let ip_block = if let Expr::Blob(ref addr) = expr {
+ ip::Block::from_bytestring(addr).map_err(Error::IP)?
+ } else {
+ return Err(Error::ExpectedBlob(expr));
+ };
+ Ok(Query::Get { ip_block })
+}
+fn build_set(mut args: Vec<Expr>) -> Result<Query, Error> {
+ count_args("SET", &args, 2)?;
+
+ let expr = args.remove(0);
+ let ip_block = if let Expr::Blob(ref addr) = expr {
+ ip::Block::from_bytestring(addr).map_err(Error::IP)?
+ } else {
+ return Err(Error::ExpectedBlob(expr));
+ };
+ Ok(Query::Set {
+ ip_block,
+ value: args.remove(0),
+ })
+}
+fn build_del(mut args: Vec<Expr>) -> Result<Query, Error> {
+ count_args("DEL", &args, 1)?;
+
+ let expr = args.remove(0);
+ let ip_block = if let Expr::Blob(ref addr) = expr {
+ ip::Block::from_bytestring(addr).map_err(Error::IP)?
+ } else {
+ return Err(Error::ExpectedBlob(expr));
+ };
+ Ok(Query::Del { ip_block })
+}
+fn build_exists(mut args: Vec<Expr>) -> Result<Query, Error> {
+ count_args("EXISTS", &args, 1)?;
+
+ let expr = args.remove(0);
+ let ip_block = if let Expr::Blob(ref addr) = expr {
+ ip::Block::from_bytestring(addr).map_err(Error::IP)?
+ } else {
+ return Err(Error::ExpectedBlob(expr));
+ };
+ Ok(Query::Exists { ip_block })
+}
+fn build_info(mut args: Vec<Expr>) -> Result<Query, Error> {
+ count_args("INFO", &args, 1)?;
+
+ let expr = args.remove(0);
+ let ip_block = if let Expr::Blob(ref addr) = expr {
+ ip::Block::from_bytestring(addr).map_err(Error::IP)?
+ } else {
+ return Err(Error::ExpectedBlob(expr));
+ };
+ Ok(Query::Info { ip_block })
+}
+fn build_parent(mut args: Vec<Expr>) -> Result<Query, Error> {
+ count_args("PARENT", &args, 1)?;
+
+ let expr = args.remove(0);
+ let ip_block = if let Expr::Blob(ref addr) = expr {
+ ip::Block::from_bytestring(addr).map_err(Error::IP)?
+ } else {
+ return Err(Error::ExpectedBlob(expr));
+ };
+ Ok(Query::Parent { ip_block })
+}
+fn build_children(mut args: Vec<Expr>) -> Result<Query, Error> {
+ count_args("CHILDREN", &args, 1)?;
+
+ let expr = args.remove(0);
+ let ip_block = if let Expr::Blob(ref addr) = expr {
+ ip::Block::from_bytestring(addr).map_err(Error::IP)?
+ } else {
+ return Err(Error::ExpectedBlob(expr));
+ };
+ Ok(Query::Children { ip_block })
+}
+
+// Helpers
+
+fn count_args<T>(cmd: &str, args: &Vec<T>, expected_count: usize) -> Result<(), Error> {
+ let args_count = args.len();
+ if args_count != expected_count {
+ return Err(Error::InvalidArgsCount {
+ cmd: cmd.to_string(),
+ expected: expected_count,
+ actual: args_count,
+ });
+ }
+
+ Ok(())
+}