diff --git a/src/query.rs b/src/query.rs index 3254c9b..8fb0e33 100644 --- a/src/query.rs +++ b/src/query.rs @@ -390,3 +390,44 @@ impl SQParam for String { self.as_str().append_param(buf) } } + +const LIST_SYM_OPEN: u8 = 0x07; +const LIST_SYM_CLOSE: u8 = ']' as u8; + +/// A list type representing a Skyhash list type, used in parameter lists +#[derive(Debug, PartialEq, Clone)] +pub struct QList<'a, T: SQParam> { + l: &'a [T], +} + +impl<'a, T: SQParam> QList<'a, T> { + /// create a new list + pub fn new(l: &'a [T]) -> Self { + Self { l } + } +} + +impl<'a, T: SQParam> SQParam for QList<'a, T> { + fn append_param(&self, q: &mut Vec) -> usize { + q.push(LIST_SYM_OPEN); + for param in self.l { + param.append_param(q); + } + q.push(LIST_SYM_CLOSE); + 1 + } +} + +#[test] +fn list_param() { + let data = vec!["hello", "giant", "world"]; + let list = QList::new(&data); + let q = query!( + "insert into apps.social(?, ?, ?)", + "username", + "password", + list + ); + assert_eq!(q.param_cnt(), 3); + dbg!(String::from_utf8(q.debug_encode_packet())).unwrap(); +} diff --git a/src/response.rs b/src/response.rs index 1da5f85..5ed2aaf 100644 --- a/src/response.rs +++ b/src/response.rs @@ -128,6 +128,12 @@ impl Row { } } +impl From> for Row { + fn from(values: Vec) -> Self { + Self { values } + } +} + #[derive(Debug, PartialEq, Clone)] /// A response returned by the server pub enum Response { @@ -405,3 +411,57 @@ impl Deref for Rows { &self.0 } } + +/// A list received from a response +#[derive(Debug, PartialEq, Clone)] +pub struct RList(Vec); + +impl From> for RList { + fn from(values: Vec) -> Self { + Self(values) + } +} + +impl RList { + /// Returns the values of the list + pub fn into_values(self) -> Vec { + self.0 + } +} + +impl Deref for RList { + type Target = [T]; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl FromValue for RList { + fn from_value(v: Value) -> ClientResult { + match v { + Value::List(l) => { + let mut ret = Vec::new(); + for value in l { + ret.push(T::from_value(value)?); + } + Ok(Self(ret)) + } + _ => Err(Error::ParseError(ParseError::TypeMismatch)), + } + } +} + +#[test] +fn resp_list_parse() { + let response_list = Response::Row(Row::new(vec![ + Value::String("sayan".to_owned()), + Value::List(vec![ + Value::String("c".to_owned()), + Value::String("assembly".to_owned()), + Value::String("rust".to_owned()), + ]), + ])); + let (name, languages) = response_list.parse::<(String, RList)>().unwrap(); + assert_eq!(name, "sayan"); + assert_eq!(languages.as_ref(), vec!["c", "assembly", "rust"]); +} diff --git a/tests/custom_query_resp.rs b/tests/custom_query_resp.rs new file mode 100644 index 0000000..25109f1 --- /dev/null +++ b/tests/custom_query_resp.rs @@ -0,0 +1,83 @@ +use skytable::{ + query, + query::{QList, SQParam}, + response::{FromResponse, RList, Response, Row, Value}, +}; + +/* + our model looks like: + + create model myapp.data( + username: string, + password: string, + notes: list { type: string }, + ) +*/ + +#[derive(Debug, PartialEq)] +struct BookmarkUser { + username: String, + password: String, + notes: Vec, +} + +impl BookmarkUser { + fn test_user() -> Self { + Self { + username: "sayan".to_owned(), + password: "pass123".to_owned(), + notes: vec![ + "https://example.com".to_owned(), + "https://skytable.org".to_owned(), + "https://docs.skytable.org".to_owned(), + ], + } + } +} + +impl<'a> SQParam for &'a BookmarkUser { + fn append_param(&self, q: &mut Vec) -> usize { + self.username.append_param(q) + + self.password.append_param(q) + + QList::new(&self.notes).append_param(q) + } +} + +impl FromResponse for BookmarkUser { + fn from_response(resp: Response) -> skytable::ClientResult { + let (username, password, notes) = resp.parse::<(String, String, RList)>()?; + Ok(Self { + username, + password, + notes: notes.into_values(), + }) + } +} + +#[test] +fn dynlist_q() { + let bu = BookmarkUser::test_user(); + let q = query!( + "insert into myapp.data { username: ?, password: ?, notes: ? }", + &bu + ); + assert_eq!(q.param_cnt(), 3); +} + +#[test] +fn dynlist_r() { + // assume that this is the response we got from the server (as a row); this may look messy but in a real-world application, the library does this for you + // under the hood, so don't worry! you'll never have to write this yourself! + let resp_from_server = Response::Row(Row::from(vec![ + Value::String("sayan".to_owned()), + Value::String("pass123".to_owned()), + Value::List(vec![ + Value::String("https://example.com".to_owned()), + Value::String("https://skytable.org".to_owned()), + Value::String("https://docs.skytable.org".to_owned()), + ]), + ])); + // now this is our "fetch code" + let user: BookmarkUser = resp_from_server.parse().unwrap(); + assert_eq!(user, BookmarkUser::test_user()); +}