diff --git a/Cargo.toml b/Cargo.toml index 69c9421..317f6b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,4 +2,5 @@ [workspace] members = [ "orientdb-client", + "orientdb-macro" ] diff --git a/ci/script.sh b/ci/script.sh index 1b77d19..81ea4a4 100755 --- a/ci/script.sh +++ b/ci/script.sh @@ -1,9 +1,9 @@ cd orientdb-client if [[ $TRAVIS_RUST_VERSION == "stable" ]]; then - cargo test --all --features=async-std-runtime,uuid - cargo test --all --features=tokio-runtime,uuid + cargo test --all --features=async-std-runtime,uuid,derive + cargo test --all --features=tokio-runtime,uuid,derive elif [[ $TRAVIS_RUST_VERSION == "nightly" ]]; then - cargo test --all --features=async-std-runtime,uuid - cargo test --all --features=tokio-runtime,uuid + cargo test --all --features=async-std-runtime,uuid,derive + cargo test --all --features=tokio-runtime,uuid,derive fi cd .. \ No newline at end of file diff --git a/orientdb-client/Cargo.toml b/orientdb-client/Cargo.toml index 96384ab..b6ef546 100644 --- a/orientdb-client/Cargo.toml +++ b/orientdb-client/Cargo.toml @@ -15,7 +15,7 @@ default = [] async = ["async-trait","futures"] tokio-runtime = ["async","tokio","mobc/tokio"] async-std-runtime=["async","async-std","mobc/async-std"] - +derive = ["orientdb-macro"] [badges] travis-ci = { repository = "wolf4ood/orientdb-rs" } @@ -35,6 +35,7 @@ futures = { version="0.3.4", optional=true } mobc = {version = "0.5.7", optional = true, default-features=false, features = ["unstable"] } tokio = { version = "0.2", optional=true, features = ["full"] } uuid = { version = "0.8", optional=true } +orientdb-macro = { path="../orientdb-macro", version="0.1", optional=true } diff --git a/orientdb-client/src/asynchronous/statement.rs b/orientdb-client/src/asynchronous/statement.rs index 49fc874..63b97df 100644 --- a/orientdb-client/src/asynchronous/statement.rs +++ b/orientdb-client/src/asynchronous/statement.rs @@ -2,10 +2,14 @@ use super::session::OSession; use crate::common::protocol::messages::request::Query; use crate::common::types::value::{IntoOValue, OValue}; use crate::common::types::OResult; +use crate::types::result::FromResult; use crate::OrientResult; use futures::Stream; use std::collections::HashMap; +#[cfg(feature = "derive")] +use futures::StreamExt; + pub struct Statement<'a> { session: &'a OSession, stm: String, @@ -64,6 +68,54 @@ impl<'a> Statement<'a> { pub async fn run(self) -> OrientResult>> { self.session.run(self.into()).await } + + #[cfg(feature = "derive")] + pub async fn fetch_one(self) -> OrientResult> + where + T: FromResult, + { + let mut stream = self + .session + .run(self.into()) + .await? + .map(|r| r.and_then(T::from_result)); + + match stream.next().await { + Some(r) => Ok(Some(r?)), + None => Ok(None), + } + } + + #[cfg(feature = "derive")] + pub async fn fetch(self) -> OrientResult> + where + T: FromResult, + { + let mut stream = self + .session + .run(self.into()) + .await? + .map(|r| r.and_then(T::from_result)); + + let mut results = Vec::new(); + + while let Some(r) = stream.next().await { + results.push(r?); + } + Ok(results) + } + + #[cfg(feature = "derive")] + pub async fn stream(self) -> OrientResult>> + where + T: FromResult, + { + Ok(self + .session + .run(self.into()) + .await? + .map(|r| r.and_then(T::from_result))) + } } impl<'a> From> for Query { diff --git a/orientdb-client/src/asynchronous/types/resultset.rs b/orientdb-client/src/asynchronous/types/resultset.rs index ffbec9e..b2fb201 100644 --- a/orientdb-client/src/asynchronous/types/resultset.rs +++ b/orientdb-client/src/asynchronous/types/resultset.rs @@ -43,21 +43,6 @@ impl PagedResultSet { state: ResultState::Looping, } } - - #[cfg(feature = "async-std-runtime")] - async fn close_result(&mut self) -> OrientResult<()> { - if self.response.has_next { - let mut conn = self.server.connection().await?; - let msg = QueryClose::new( - self.session_id, - self.token.clone(), - self.response.query_id.as_str(), - ); - conn.send(msg.into()).await?; - self.response.has_next = false; - } - Ok(()) - } } impl futures::Stream for PagedResultSet { @@ -105,15 +90,15 @@ impl futures::Stream for PagedResultSet { impl Drop for PagedResultSet { #[allow(unused_must_use)] fn drop(&mut self) { - #[cfg(feature = "async-std-runtime")] - task::block_on(self.close_result()); + // #[cfg(feature = "async-std-runtime")] + // task::block_on(self.close_result()); - #[cfg(feature = "tokio-runtime")] + // #[cfg(feature = "tokio-runtime")] spawn_drop(self); } } -#[cfg(feature = "tokio-runtime")] +// #[cfg(feature = "tokio-runtime")] fn spawn_drop(resultset: &mut PagedResultSet) { let has_next = resultset.response.has_next; let server = resultset.server.clone(); @@ -126,7 +111,7 @@ fn spawn_drop(resultset: &mut PagedResultSet) { } } -#[cfg(feature = "tokio-runtime")] +// #[cfg(feature = "tokio-runtime")] async fn close_result( server: Arc, query_id: String, diff --git a/orientdb-client/src/common/types/result.rs b/orientdb-client/src/common/types/result.rs index 07e6864..08a77ed 100644 --- a/orientdb-client/src/common/types/result.rs +++ b/orientdb-client/src/common/types/result.rs @@ -79,3 +79,9 @@ impl From for OResult { } } } + +pub trait FromResult { + fn from_result(result: OResult) -> OrientResult + where + Self: Sized; +} diff --git a/orientdb-client/src/lib.rs b/orientdb-client/src/lib.rs index 7e89c4a..0a56a19 100644 --- a/orientdb-client/src/lib.rs +++ b/orientdb-client/src/lib.rs @@ -56,3 +56,8 @@ pub mod aio { pub use crate::asynchronous::session::{OSession, SessionPool}; pub use crate::asynchronous::OrientDB; } + +#[cfg(feature = "derive")] +pub mod derive { + pub use orientdb_macro::FromResult; +} diff --git a/orientdb-client/src/sync/statement.rs b/orientdb-client/src/sync/statement.rs index 8ae54af..c258e06 100644 --- a/orientdb-client/src/sync/statement.rs +++ b/orientdb-client/src/sync/statement.rs @@ -2,6 +2,7 @@ use super::session::OSession; use crate::common::protocol::messages::request::Query; use crate::common::types::value::{IntoOValue, OValue}; use crate::sync::types::resultset::ResultSet; +use crate::types::result::FromResult; use crate::OrientResult; use std::collections::HashMap; @@ -63,6 +64,44 @@ impl<'a> Statement<'a> { pub fn run(self) -> OrientResult { self.session.run(self.into()) } + + #[cfg(feature = "derive")] + pub fn fetch_one(self) -> OrientResult> + where + T: FromResult, + { + match self + .session + .run(self.into())? + .map(|r| r.and_then(T::from_result)) + .next() + { + Some(s) => Ok(Some(s?)), + None => Ok(None), + } + } + + #[cfg(feature = "derive")] + pub fn fetch(self) -> OrientResult> + where + T: FromResult, + { + self.session + .run(self.into())? + .map(|r| r.and_then(T::from_result)) + .collect() + } + + #[cfg(feature = "derive")] + pub fn iter(self) -> OrientResult>> + where + T: FromResult, + { + Ok(self + .session + .run(self.into())? + .map(|r| r.and_then(T::from_result))) + } } impl<'a> From> for Query { diff --git a/orientdb-client/tests/orient-session.rs b/orientdb-client/tests/orient-session.rs index 8312f90..d0b3226 100644 --- a/orientdb-client/tests/orient-session.rs +++ b/orientdb-client/tests/orient-session.rs @@ -124,6 +124,61 @@ fn session_query_test_with_page_size_and_close() { }); } +#[cfg(feature = "derive")] +#[test] +fn session_query_one() { + run_with_session("session_query_one", |session| { + #[derive(orientdb_client::derive::FromResult, Debug, PartialEq)] + struct Person { + name: String, + } + + let result: Option = session + .query("select from OUser where name = ?") + .positional(&[&"admin"]) + .fetch_one() + .unwrap(); + + assert_eq!( + Some(Person { + name: String::from("admin"), + }), + result, + ) + }); +} + +#[cfg(feature = "derive")] +#[test] +fn session_query_all() { + run_with_session("session_query_all", |session| { + #[derive(orientdb_client::derive::FromResult, Debug, PartialEq)] + struct Person { + name: String, + } + + let results: Vec = session.query("select from OUser").fetch().unwrap(); + + assert_eq!(3, results.len(),) + }); +} + +#[cfg(feature = "derive")] +#[test] +fn session_query_iter() { + run_with_session("session_query_iter", |session| { + #[derive(orientdb_client::derive::FromResult, Debug, PartialEq)] + struct Person { + name: String, + } + + let results: Result, _> = + session.query("select from OUser").iter().unwrap().collect(); + + assert_eq!(3, results.unwrap().len()) + }); +} + #[cfg(feature = "uuid")] #[test] fn session_query_with_uuid() { @@ -589,4 +644,63 @@ mod asynchronous { } assert_eq!(3, counter); } + + #[cfg_attr(feature = "async-std-runtime", async_std::test)] + #[cfg_attr(feature = "tokio-runtime", tokio::test)] + #[cfg(feature = "derive")] + async fn session_query_one() { + #[derive(orientdb_client::derive::FromResult, Debug, PartialEq)] + struct Person { + name: String, + } + let session = session("async_session_query_one").await; + let result: Option = session + .query("select from OUser where name = ?") + .positional(&[&"admin"]) + .fetch_one() + .await + .unwrap(); + + assert_eq!( + Some(Person { + name: String::from("admin"), + }), + result, + ) + } + + #[cfg_attr(feature = "async-std-runtime", async_std::test)] + #[cfg_attr(feature = "tokio-runtime", tokio::test)] + #[cfg(feature = "derive")] + async fn session_query_all() { + #[derive(orientdb_client::derive::FromResult, Debug, PartialEq)] + struct Person { + name: String, + } + let session = session("async_session_query_all").await; + let result: Vec = session.query("select from OUser").fetch().await.unwrap(); + + assert_eq!(3, result.len(),) + } + + #[cfg_attr(feature = "async-std-runtime", async_std::test)] + #[cfg_attr(feature = "tokio-runtime", tokio::test)] + #[cfg(feature = "derive")] + async fn session_query_stream() { + use futures::StreamExt; + #[derive(orientdb_client::derive::FromResult, Debug, PartialEq)] + struct Person { + name: String, + } + let session = session("async_session_query_stream").await; + let results = session + .query("select from OUser") + .stream::() + .await + .unwrap() + .collect::>() + .await; + + assert_eq!(3, results.len(),) + } } diff --git a/orientdb-macro/Cargo.toml b/orientdb-macro/Cargo.toml new file mode 100644 index 0000000..f943641 --- /dev/null +++ b/orientdb-macro/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "orientdb-macro" +version = "0.1.0" +authors = ["wolf4ood "] +edition = "2018" + +[lib] +proc-macro = true + +[dependencies] +proc-macro2 = { version = "1.0.9", default-features = false } +syn = { version = "1.0.16", default-features = false, features = [ "full" ] } +quote = { version = "1.0.2", default-features = false } \ No newline at end of file diff --git a/orientdb-macro/src/lib.rs b/orientdb-macro/src/lib.rs new file mode 100644 index 0000000..ca04d3b --- /dev/null +++ b/orientdb-macro/src/lib.rs @@ -0,0 +1,13 @@ +use proc_macro::TokenStream; + +mod result; + +#[proc_macro_derive(FromResult)] +pub fn derive_from_result(input: TokenStream) -> TokenStream { + let input = syn::parse_macro_input!(input as syn::DeriveInput); + + match result::derive(&input) { + Ok(ts) => ts.into(), + Err(e) => e.to_compile_error().into(), + } +} diff --git a/orientdb-macro/src/result.rs b/orientdb-macro/src/result.rs new file mode 100644 index 0000000..2ed3ac9 --- /dev/null +++ b/orientdb-macro/src/result.rs @@ -0,0 +1,44 @@ +use quote::quote; +use syn::{parse_quote, Data, DataStruct, DeriveInput, Fields, FieldsNamed, Stmt}; + +pub fn derive(input: &DeriveInput) -> syn::Result { + match &input.data { + Data::Struct(DataStruct { + fields: Fields::Named(FieldsNamed { named, .. }), + .. + }) => { + let ident = &input.ident; + + let reads = named.iter().filter_map(|field| -> Option { + let id = &field.ident.as_ref()?; + let id_s = id.to_string(); + let ty = &field.ty; + + Some(parse_quote!( + let #id: #ty = result.get_checked(#id_s)?; + )) + }); + + let names = named.iter().map(|field| &field.ident); + + Ok(quote! { + + impl orientdb_client::types::result::FromResult for #ident { + + fn from_result(result : orientdb_client::types::OResult) -> orientdb_client::OrientResult where Self: Sized { + + #(#reads)* + + Ok(#ident { + #(#names),* + }) + } + } + }) + } + _ => Err(syn::Error::new_spanned( + input, + "Only structs are supported for Repo derive", + )), + } +} pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy