diff options
author | evuez <julien@mulga.net> | 2022-11-26 15:38:06 -0500 |
---|---|---|
committer | evuez <julien@mulga.net> | 2024-04-03 22:44:12 +0200 |
commit | 86098797034cbc7eb6db0cee54e17f8dcaedbc5d (patch) | |
tree | 29b6225ead843eb9022296a54657bbadfa1c4da0 /src/common/query.rs | |
download | blom-86098797034cbc7eb6db0cee54e17f8dcaedbc5d.tar.gz |
Diffstat (limited to 'src/common/query.rs')
-rw-r--r-- | src/common/query.rs | 252 |
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(()) +} |