├── reql ├── src │ ├── cmd │ │ ├── config.rs │ │ ├── date.rs │ │ ├── day.rs │ │ ├── db_list.rs │ │ ├── fill.rs │ │ ├── hours.rs │ │ ├── keys.rs │ │ ├── minutes.rs │ │ ├── month.rs │ │ ├── now.rs │ │ ├── seconds.rs │ │ ├── status.rs │ │ ├── sync.rs │ │ ├── to_json.rs │ │ ├── type_of.rs │ │ ├── ungroup.rs │ │ ├── values.rs │ │ ├── year.rs │ │ ├── zip.rs │ │ ├── day_of_week.rs │ │ ├── day_of_year.rs │ │ ├── index_list.rs │ │ ├── rebalance.rs │ │ ├── table_list.rs │ │ ├── time_of_day.rs │ │ ├── timezone.rs │ │ ├── to_geojson.rs │ │ ├── to_iso8601.rs │ │ ├── get_write_hook.rs │ │ ├── to_epoch_time.rs │ │ ├── eq_join.rs │ │ ├── skip.rs │ │ ├── concat_map.rs │ │ ├── index_rename.rs │ │ ├── index_status.rs │ │ ├── index_wait.rs │ │ ├── inner_join.rs │ │ ├── outer_join.rs │ │ ├── with_fields.rs │ │ ├── set_write_hook.rs │ │ ├── args.rs │ │ ├── bit_not.rs │ │ ├── avg.rs │ │ ├── ceil.rs │ │ ├── fold.rs │ │ ├── ge.rs │ │ ├── gt.rs │ │ ├── http.rs │ │ ├── info.rs │ │ ├── json.rs │ │ ├── le.rs │ │ ├── line.rs │ │ ├── lt.rs │ │ ├── max.rs │ │ ├── min.rs │ │ ├── ne.rs │ │ ├── not.rs │ │ ├── or.rs │ │ ├── sum.rs │ │ ├── time.rs │ │ ├── wait.rs │ │ ├── bit_sal.rs │ │ ├── bit_sar.rs │ │ ├── circle.rs │ │ ├── count.rs │ │ ├── during.rs │ │ ├── floor.rs │ │ ├── grant.rs │ │ ├── js.rs │ │ ├── match_.rs │ │ ├── merge.rs │ │ ├── object.rs │ │ ├── pluck.rs │ │ ├── point.rs │ │ ├── random.rs │ │ ├── range.rs │ │ ├── reduce.rs │ │ ├── round.rs │ │ ├── sample.rs │ │ ├── slice.rs │ │ ├── split.rs │ │ ├── union.rs │ │ ├── upcase.rs │ │ ├── change_at.rs │ │ ├── coerce_to.rs │ │ ├── contains.rs │ │ ├── default.rs │ │ ├── distance.rs │ │ ├── distinct.rs │ │ ├── downcase.rs │ │ ├── for_each.rs │ │ ├── geojson.rs │ │ ├── includes.rs │ │ ├── insert_at.rs │ │ ├── is_empty.rs │ │ ├── iso8601.rs │ │ ├── literal.rs │ │ ├── polygon.rs │ │ ├── prepend.rs │ │ ├── set_union.rs │ │ ├── splice_at.rs │ │ ├── without.rs │ │ ├── difference.rs │ │ ├── epoch_time.rs │ │ ├── get_nearest.rs │ │ ├── in_timezone.rs │ │ ├── intersects.rs │ │ ├── offsets_of.rs │ │ ├── polygon_sub.rs │ │ ├── reconfigure.rs │ │ ├── set_insert.rs │ │ ├── expr.rs │ │ ├── set_difference.rs │ │ ├── get_intersecting.rs │ │ ├── set_intersection.rs │ │ ├── and.rs │ │ ├── append.rs │ │ ├── bracket.rs │ │ ├── has_fields.rs │ │ ├── nth.rs │ │ ├── limit.rs │ │ ├── error.rs │ │ ├── group.rs │ │ ├── db_drop.rs │ │ ├── db_create.rs │ │ ├── table_drop.rs │ │ ├── eq.rs │ │ ├── div.rs │ │ ├── mul.rs │ │ ├── rem.rs │ │ ├── sub.rs │ │ ├── bit_or.rs │ │ ├── bit_and.rs │ │ ├── bit_xor.rs │ │ ├── desc.rs │ │ ├── func.rs │ │ ├── get_field.rs │ │ ├── index_drop.rs │ │ ├── delete_at.rs │ │ ├── uuid.rs │ │ ├── db.rs │ │ ├── asc.rs │ │ ├── binary.rs │ │ ├── add.rs │ │ ├── get.rs │ │ ├── delete.rs │ │ ├── index.rs │ │ ├── branch.rs │ │ ├── filter.rs │ │ ├── order_by.rs │ │ ├── between.rs │ │ ├── replace.rs │ │ ├── update.rs │ │ ├── close.rs │ │ ├── get_all.rs │ │ ├── table.rs │ │ ├── index_create.rs │ │ ├── map.rs │ │ ├── insert.rs │ │ ├── table_create.rs │ │ ├── do_.rs │ │ ├── changes.rs │ │ ├── connect.rs │ │ └── run.rs │ ├── err.rs │ ├── proto.rs │ └── lib.rs ├── tests │ ├── table.rs │ ├── do.rs │ ├── concurrency.rs │ ├── order_by.rs │ ├── changefeeds.rs │ └── index_create.rs ├── Cargo.toml └── README.md ├── examples ├── src │ └── main.rs ├── Cargo.toml ├── reql │ ├── minimal.rs │ ├── changes.rs │ ├── closing-changefeeds.rs │ └── closing-spawned-feed.rs └── mobc │ └── simple.rs ├── .gitignore ├── Cargo.toml ├── macros ├── src │ ├── lib.rs │ ├── func │ │ ├── tests.rs │ │ └── mod.rs │ └── options.rs └── Cargo.toml ├── README.md ├── types ├── Cargo.toml └── src │ ├── lib.rs │ └── date_time.rs ├── mobc ├── Cargo.toml ├── README.md └── src │ └── lib.rs ├── .travis.yml ├── LICENSE-MIT └── LICENSE-APACHE /reql/src/cmd/config.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /reql/src/cmd/date.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /reql/src/cmd/day.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /reql/src/cmd/db_list.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /reql/src/cmd/fill.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /reql/src/cmd/hours.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /reql/src/cmd/keys.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /reql/src/cmd/minutes.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /reql/src/cmd/month.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /reql/src/cmd/now.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /reql/src/cmd/seconds.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /reql/src/cmd/status.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /reql/src/cmd/sync.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /reql/src/cmd/to_json.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /reql/src/cmd/type_of.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /reql/src/cmd/ungroup.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /reql/src/cmd/values.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /reql/src/cmd/year.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /reql/src/cmd/zip.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /reql/src/cmd/day_of_week.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /reql/src/cmd/day_of_year.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /reql/src/cmd/index_list.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /reql/src/cmd/rebalance.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /reql/src/cmd/table_list.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /reql/src/cmd/time_of_day.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /reql/src/cmd/timezone.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /reql/src/cmd/to_geojson.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /reql/src/cmd/to_iso8601.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /examples/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() {} 2 | -------------------------------------------------------------------------------- /reql/src/cmd/get_write_hook.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /reql/src/cmd/to_epoch_time.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | *.bk 3 | *.swp 4 | Cargo.lock 5 | rethinkdb_data 6 | -------------------------------------------------------------------------------- /reql/src/cmd/eq_join.rs: -------------------------------------------------------------------------------- 1 | use crate::cmd; 2 | 3 | pub trait Arg { 4 | fn arg(self) -> cmd::Arg<()>; 5 | } 6 | -------------------------------------------------------------------------------- /reql/src/cmd/skip.rs: -------------------------------------------------------------------------------- 1 | use crate::cmd; 2 | 3 | pub trait Arg { 4 | fn arg(self) -> cmd::Arg<()>; 5 | } 6 | -------------------------------------------------------------------------------- /reql/src/cmd/concat_map.rs: -------------------------------------------------------------------------------- 1 | use crate::cmd; 2 | 3 | pub trait Arg { 4 | fn arg(self) -> cmd::Arg<()>; 5 | } 6 | -------------------------------------------------------------------------------- /reql/src/cmd/index_rename.rs: -------------------------------------------------------------------------------- 1 | use crate::cmd; 2 | 3 | pub trait Arg { 4 | fn arg(self) -> cmd::Arg<()>; 5 | } 6 | -------------------------------------------------------------------------------- /reql/src/cmd/index_status.rs: -------------------------------------------------------------------------------- 1 | use crate::cmd; 2 | 3 | pub trait Arg { 4 | fn arg(self) -> cmd::Arg<()>; 5 | } 6 | -------------------------------------------------------------------------------- /reql/src/cmd/index_wait.rs: -------------------------------------------------------------------------------- 1 | use crate::cmd; 2 | 3 | pub trait Arg { 4 | fn arg(self) -> cmd::Arg<()>; 5 | } 6 | -------------------------------------------------------------------------------- /reql/src/cmd/inner_join.rs: -------------------------------------------------------------------------------- 1 | use crate::cmd; 2 | 3 | pub trait Arg { 4 | fn arg(self) -> cmd::Arg<()>; 5 | } 6 | -------------------------------------------------------------------------------- /reql/src/cmd/outer_join.rs: -------------------------------------------------------------------------------- 1 | use crate::cmd; 2 | 3 | pub trait Arg { 4 | fn arg(self) -> cmd::Arg<()>; 5 | } 6 | -------------------------------------------------------------------------------- /reql/src/cmd/with_fields.rs: -------------------------------------------------------------------------------- 1 | use crate::cmd; 2 | 3 | pub trait Arg { 4 | fn arg(self) -> cmd::Arg<()>; 5 | } 6 | -------------------------------------------------------------------------------- /reql/src/cmd/set_write_hook.rs: -------------------------------------------------------------------------------- 1 | use crate::cmd; 2 | 3 | pub trait Arg { 4 | fn arg(self) -> cmd::Arg<()>; 5 | } 6 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "mobc", 4 | "reql", 5 | "types", 6 | "macros", 7 | "examples", 8 | ] 9 | -------------------------------------------------------------------------------- /reql/src/cmd/args.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] 2 | pub struct Args(pub(crate) T); 3 | -------------------------------------------------------------------------------- /reql/src/cmd/bit_not.rs: -------------------------------------------------------------------------------- 1 | use crate::Command; 2 | use ql2::term::TermType; 3 | use std::ops::Not; 4 | 5 | impl Not for Command { 6 | type Output = Self; 7 | 8 | fn not(self) -> Self { 9 | Self::new(TermType::Not).with_arg(self) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /reql/src/cmd/avg.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Avg).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/ceil.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Ceil).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/fold.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Fold).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/ge.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Ge).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/gt.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Gt).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/http.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Http).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/info.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Info).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/json.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Json).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/le.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Le).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/line.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Line).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/lt.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Lt).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/max.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Max).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/min.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Min).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/ne.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Ne).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/not.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Not).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/or.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Or).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/sum.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Sum).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/time.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Time).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/wait.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Wait).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/bit_sal.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::BitSal).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/bit_sar.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::BitSar).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/circle.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Circle).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/count.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Count).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/during.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::During).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/floor.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Floor).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/grant.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Grant).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/js.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Javascript).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/match_.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Match).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/merge.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Merge).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/object.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Object).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/pluck.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Pluck).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/point.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Point).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/random.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Random).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/range.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Range).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/reduce.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Reduce).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/round.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Round).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/sample.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Sample).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/slice.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Slice).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/split.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Split).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/union.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Union).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/upcase.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Upcase).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/change_at.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::ChangeAt).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/coerce_to.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::CoerceTo).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/contains.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Contains).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/default.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Default).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/distance.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Distance).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/distinct.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Distinct).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/downcase.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Downcase).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/for_each.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::ForEach).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/geojson.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Geojson).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/includes.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Includes).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/insert_at.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::InsertAt).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/is_empty.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::IsEmpty).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/iso8601.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Iso8601).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/literal.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Literal).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/polygon.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Polygon).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/prepend.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Prepend).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/set_union.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::SetUnion).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/splice_at.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::SpliceAt).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/without.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Without).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/difference.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Difference).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/epoch_time.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::EpochTime).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/get_nearest.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::GetNearest).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/in_timezone.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::InTimezone).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/intersects.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Intersects).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/offsets_of.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::OffsetsOf).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/polygon_sub.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::PolygonSub).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/reconfigure.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Reconfigure).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/set_insert.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::SetInsert).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/expr.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use serde::Serialize; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for T 9 | where 10 | T: Serialize, 11 | { 12 | fn arg(self) -> cmd::Arg<()> { 13 | Command::from_json(self).into_arg() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /reql/src/cmd/set_difference.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::SetDifference).with_arg(self).into_arg() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /reql/src/cmd/get_intersecting.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::GetIntersecting) 11 | .with_arg(self) 12 | .into_arg() 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /reql/src/cmd/set_intersection.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::SetIntersection) 11 | .with_arg(self) 12 | .into_arg() 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /reql/src/cmd/and.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for cmd::Arg<()> { 9 | fn arg(self) -> cmd::Arg<()> { 10 | self 11 | } 12 | } 13 | 14 | impl Arg for Command { 15 | fn arg(self) -> cmd::Arg<()> { 16 | Self::new(TermType::And).with_arg(self).into_arg() 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /macros/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate proc_macro; 2 | 3 | mod func; 4 | mod options; 5 | 6 | use func::Func; 7 | use proc_macro::TokenStream; 8 | 9 | #[proc_macro] 10 | pub fn func(input: TokenStream) -> TokenStream { 11 | Func::new(input.into()).process().into() 12 | } 13 | 14 | #[proc_macro_derive(CommandOptions)] 15 | pub fn command_opts(input: TokenStream) -> TokenStream { 16 | options::parse(input) 17 | } 18 | -------------------------------------------------------------------------------- /reql/src/cmd/append.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for cmd::Arg<()> { 9 | fn arg(self) -> cmd::Arg<()> { 10 | self 11 | } 12 | } 13 | 14 | impl Arg for Command { 15 | fn arg(self) -> cmd::Arg<()> { 16 | Self::new(TermType::Append).with_arg(self).into_arg() 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /reql/src/cmd/bracket.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | use serde::Serialize; 4 | 5 | pub trait Arg { 6 | fn arg(self) -> cmd::Arg<()>; 7 | } 8 | 9 | impl Arg for T 10 | where 11 | T: Serialize, 12 | { 13 | fn arg(self) -> cmd::Arg<()> { 14 | let arg = Command::from_json(self); 15 | Command::new(TermType::Bracket).with_arg(arg).into_arg() 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /reql/src/cmd/has_fields.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | use serde::Serialize; 4 | 5 | pub trait Arg { 6 | fn arg(self) -> cmd::Arg<()>; 7 | } 8 | 9 | impl Arg for T 10 | where 11 | T: Serialize, 12 | { 13 | fn arg(self) -> cmd::Arg<()> { 14 | let arg = Command::from_json(self); 15 | Command::new(TermType::HasFields).with_arg(arg).into_arg() 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /reql/tests/table.rs: -------------------------------------------------------------------------------- 1 | use futures::TryStreamExt; 2 | use reql::r; 3 | use serde_json::Value; 4 | 5 | #[tokio::test] 6 | async fn table() -> reql::Result<()> { 7 | tracing_subscriber::fmt::init(); 8 | let conn = r.connect(()).await?; 9 | let mut query = r.db("rethinkdb").table("users").run(&conn); 10 | let user: Option = query.try_next().await?; 11 | assert!(user.is_some()); 12 | Ok(()) 13 | } 14 | -------------------------------------------------------------------------------- /reql/src/cmd/nth.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Nth).with_arg(self).into_arg() 11 | } 12 | } 13 | 14 | impl Arg for isize { 15 | fn arg(self) -> cmd::Arg<()> { 16 | Command::from_json(self).arg() 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /reql/src/cmd/limit.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Limit).with_arg(self).into_arg() 11 | } 12 | } 13 | 14 | impl Arg for isize { 15 | fn arg(self) -> cmd::Arg<()> { 16 | Command::from_json(self).arg() 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /reql/tests/do.rs: -------------------------------------------------------------------------------- 1 | use futures::TryStreamExt; 2 | use reql::{func, r}; 3 | use serde_json::Value; 4 | 5 | #[tokio::test] 6 | async fn do_query() -> reql::Result<()> { 7 | tracing_subscriber::fmt::init(); 8 | let conn = r.connect(()).await?; 9 | let mut query = r.do_(r.args(([10, 20], func!(|x, y| x + y)))).run(&conn); 10 | let val: Option = query.try_next().await?; 11 | assert!(val.is_some()); 12 | Ok(()) 13 | } 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RethinkDB Rust driver 2 | 3 | ## Overview 4 | 5 | ### What is RethinkDB? 6 | RethinkDB is the first open-source scalable database built for realtime applications. It exposes a new database access model -- instead of polling for changes, the developer can tell the database to continuously push updated query results to applications in realtime. RethinkDB allows developers to build scalable realtime apps in a fraction of the time with less effort. 7 | -------------------------------------------------------------------------------- /reql/src/cmd/error.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Error).with_arg(self).into_arg() 11 | } 12 | } 13 | 14 | impl Arg for T 15 | where 16 | T: Into, 17 | { 18 | fn arg(self) -> cmd::Arg<()> { 19 | Command::from_json(self.into()).arg() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /reql/src/cmd/group.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Group).with_arg(self).into_arg() 11 | } 12 | } 13 | 14 | impl Arg for T 15 | where 16 | T: Into, 17 | { 18 | fn arg(self) -> cmd::Arg<()> { 19 | Command::from_json(self.into()).arg() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /reql/src/cmd/db_drop.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::DbDrop).with_arg(self).into_arg() 11 | } 12 | } 13 | 14 | impl Arg for T 15 | where 16 | T: Into, 17 | { 18 | fn arg(self) -> cmd::Arg<()> { 19 | Command::from_json(self.into()).arg() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /reql/src/cmd/db_create.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::DbCreate).with_arg(self).into_arg() 11 | } 12 | } 13 | 14 | impl Arg for T 15 | where 16 | T: Into, 17 | { 18 | fn arg(self) -> cmd::Arg<()> { 19 | Command::from_json(self.into()).arg() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /reql/src/cmd/table_drop.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::TableDrop).with_arg(self).into_arg() 11 | } 12 | } 13 | 14 | impl Arg for T 15 | where 16 | T: Into, 17 | { 18 | fn arg(self) -> cmd::Arg<()> { 19 | Command::from_json(self.into()).arg() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /reql/src/cmd/eq.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | use serde::Serialize; 4 | 5 | pub trait Arg { 6 | fn arg(self) -> cmd::Arg<()>; 7 | } 8 | 9 | impl Arg for cmd::Arg<()> { 10 | fn arg(self) -> cmd::Arg<()> { 11 | self 12 | } 13 | } 14 | 15 | impl Arg for T 16 | where 17 | T: Serialize, 18 | { 19 | fn arg(self) -> cmd::Arg<()> { 20 | let arg = Command::from_json(self); 21 | Command::new(TermType::Eq).with_arg(arg).into_arg() 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /reql/src/cmd/div.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | use std::ops::Div; 4 | 5 | pub trait Arg { 6 | fn arg(self) -> cmd::Arg<()>; 7 | } 8 | 9 | impl Arg for Command { 10 | fn arg(self) -> cmd::Arg<()> { 11 | Self::new(TermType::Div).with_arg(self).into_arg() 12 | } 13 | } 14 | 15 | impl Div for Command 16 | where 17 | T: Arg, 18 | { 19 | type Output = Self; 20 | 21 | fn div(self, arg: T) -> Self { 22 | arg.arg().with_parent(self).into_cmd() 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /reql/src/cmd/mul.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | use std::ops::Mul; 4 | 5 | pub trait Arg { 6 | fn arg(self) -> cmd::Arg<()>; 7 | } 8 | 9 | impl Arg for Command { 10 | fn arg(self) -> cmd::Arg<()> { 11 | Self::new(TermType::Mul).with_arg(self).into_arg() 12 | } 13 | } 14 | 15 | impl Mul for Command 16 | where 17 | T: Arg, 18 | { 19 | type Output = Self; 20 | 21 | fn mul(self, arg: T) -> Self { 22 | arg.arg().with_parent(self).into_cmd() 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /reql/src/cmd/rem.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | use std::ops::Rem; 4 | 5 | pub trait Arg { 6 | fn arg(self) -> cmd::Arg<()>; 7 | } 8 | 9 | impl Arg for Command { 10 | fn arg(self) -> cmd::Arg<()> { 11 | Self::new(TermType::Mod).with_arg(self).into_arg() 12 | } 13 | } 14 | 15 | impl Rem for Command 16 | where 17 | T: Arg, 18 | { 19 | type Output = Self; 20 | 21 | fn rem(self, arg: T) -> Self { 22 | arg.arg().with_parent(self).into_cmd() 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /reql/src/cmd/sub.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | use std::ops::Sub; 4 | 5 | pub trait Arg { 6 | fn arg(self) -> cmd::Arg<()>; 7 | } 8 | 9 | impl Arg for Command { 10 | fn arg(self) -> cmd::Arg<()> { 11 | Self::new(TermType::Sub).with_arg(self).into_arg() 12 | } 13 | } 14 | 15 | impl Sub for Command 16 | where 17 | T: Arg, 18 | { 19 | type Output = Self; 20 | 21 | fn sub(self, arg: T) -> Self { 22 | arg.arg().into_cmd().with_parent(self) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /reql/tests/concurrency.rs: -------------------------------------------------------------------------------- 1 | use futures::stream::{select_all, TryStreamExt}; 2 | use reql::r; 3 | 4 | #[tokio::test] 5 | async fn concurrency() -> reql::Result<()> { 6 | let conn = r.connect(()).await?; 7 | 8 | let mut streams = Vec::new(); 9 | 10 | let num = 10_000; 11 | for i in 0..num { 12 | streams.push(r.expr(format!("message {}", i)).run::<_, String>(&conn)); 13 | } 14 | 15 | let mut list = select_all(streams); 16 | 17 | while list.try_next().await?.is_some() {} 18 | 19 | Ok(()) 20 | } 21 | -------------------------------------------------------------------------------- /reql/tests/order_by.rs: -------------------------------------------------------------------------------- 1 | use futures::TryStreamExt; 2 | use reql::r; 3 | use serde_json::Value; 4 | 5 | #[tokio::test] 6 | async fn order_by() -> reql::Result<()> { 7 | tracing_subscriber::fmt::init(); 8 | let conn = r.connect(()).await?; 9 | let mut query = r 10 | .db("rethinkdb") 11 | .table("server_status") 12 | .order_by(r.args(("name", r.index(r.desc("id"))))) 13 | .run(&conn); 14 | let user: Option = query.try_next().await?; 15 | assert!(user.is_some()); 16 | Ok(()) 17 | } 18 | -------------------------------------------------------------------------------- /reql/src/cmd/bit_or.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | use std::ops::BitOr; 4 | 5 | pub trait Arg { 6 | fn arg(self) -> cmd::Arg<()>; 7 | } 8 | 9 | impl Arg for Command { 10 | fn arg(self) -> cmd::Arg<()> { 11 | Self::new(TermType::BitOr).with_arg(self).into_arg() 12 | } 13 | } 14 | 15 | impl BitOr for Command 16 | where 17 | T: Arg, 18 | { 19 | type Output = Self; 20 | 21 | fn bitor(self, arg: T) -> Self { 22 | arg.arg().with_parent(self).into_cmd() 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /reql/src/cmd/bit_and.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | use std::ops::BitAnd; 4 | 5 | pub trait Arg { 6 | fn arg(self) -> cmd::Arg<()>; 7 | } 8 | 9 | impl Arg for Command { 10 | fn arg(self) -> cmd::Arg<()> { 11 | Self::new(TermType::BitAnd).with_arg(self).into_arg() 12 | } 13 | } 14 | 15 | impl BitAnd for Command 16 | where 17 | T: Arg, 18 | { 19 | type Output = Self; 20 | 21 | fn bitand(self, arg: T) -> Self { 22 | arg.arg().with_parent(self).into_cmd() 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /reql/src/cmd/bit_xor.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | use std::ops::BitXor; 4 | 5 | pub trait Arg { 6 | fn arg(self) -> cmd::Arg<()>; 7 | } 8 | 9 | impl Arg for Command { 10 | fn arg(self) -> cmd::Arg<()> { 11 | Self::new(TermType::BitXor).with_arg(self).into_arg() 12 | } 13 | } 14 | 15 | impl BitXor for Command 16 | where 17 | T: Arg, 18 | { 19 | type Output = Self; 20 | 21 | fn bitxor(self, arg: T) -> Self { 22 | arg.arg().with_parent(self).into_cmd() 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /reql/src/cmd/desc.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | #[derive(Debug, Clone)] 5 | pub struct Desc(pub(crate) Command); 6 | 7 | pub trait Arg { 8 | fn arg(self) -> cmd::Arg<()>; 9 | } 10 | 11 | impl Arg for Command { 12 | fn arg(self) -> cmd::Arg<()> { 13 | Self::new(TermType::Desc).with_arg(self).into_arg() 14 | } 15 | } 16 | 17 | impl Arg for T 18 | where 19 | T: Into, 20 | { 21 | fn arg(self) -> cmd::Arg<()> { 22 | Command::from_json(self.into()).arg() 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "reql-macros" 3 | description = "Macros for the reql crate" 4 | version = "0.3.0" 5 | license = "MIT/Apache-2.0" 6 | repository = "https://github.com/RethinkDB/rethinkdb-rs" 7 | documentation = "https://docs.rs/reql-macros" 8 | authors = ["rushmorem "] 9 | edition = "2018" 10 | 11 | [dependencies] 12 | quote = "1.0.10" 13 | syn = { version = "1.0.80", default-features = false, features = ["parsing", "proc-macro", "derive", "printing"] } 14 | proc-macro2 = "1.0.30" 15 | 16 | [lib] 17 | proc-macro = true 18 | -------------------------------------------------------------------------------- /macros/src/func/tests.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[test] 4 | fn with_move() { 5 | Func::new(quote!(move |doc| { 6 | doc.get_field("author").bracket("name") 7 | })) 8 | .process(); 9 | } 10 | 11 | #[test] 12 | fn with_no_arg() { 13 | Func::new(quote!(|| r.expr("Hello world!"))).process(); 14 | } 15 | 16 | #[test] 17 | fn with_one_arg() { 18 | Func::new(quote!(|doc| { doc.get_field("author").bracket("name") })).process(); 19 | } 20 | 21 | #[test] 22 | fn with_multiple_args() { 23 | Func::new(quote!(|with, multiple, args| r.expr(with, multiple, args))).process(); 24 | } 25 | -------------------------------------------------------------------------------- /reql/src/cmd/func.rs: -------------------------------------------------------------------------------- 1 | use crate::Command; 2 | use ql2::term::TermType; 3 | 4 | #[derive(Debug)] 5 | pub struct Func(pub(crate) Command); 6 | 7 | impl Func { 8 | pub fn new(ids: Vec, body: T) -> Self 9 | where 10 | T: Into, 11 | { 12 | Func( 13 | Command::new(TermType::Func) 14 | .with_arg(Command::from_json(ids)) 15 | .with_arg(body), 16 | ) 17 | } 18 | 19 | pub(crate) fn row(body: T) -> Self 20 | where 21 | T: Into, 22 | { 23 | Self::new(vec![0], body) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /types/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "reql-types" 3 | description = "Some useful types to use along with the reql crate" 4 | version = "0.3.3" 5 | license = "MIT/Apache-2.0" 6 | repository = "https://github.com/RethinkDB/rethinkdb-rs" 7 | documentation = "https://docs.rs/reql-types" 8 | authors = ["rushmorem "] 9 | edition = "2018" 10 | 11 | [dependencies] 12 | serde = { version = "1.0.137", features = ["derive"] } 13 | serde_json = "1.0.81" 14 | uuid = { version = "1.1.2", features = ["v4", "serde"] } 15 | base64 = "0.13.0" 16 | time = { version = "0.3.9", features = ["macros", "formatting", "parsing"] } 17 | 18 | [features] 19 | -------------------------------------------------------------------------------- /reql/src/cmd/get_field.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::GetField).with_arg(self).into_arg() 11 | } 12 | } 13 | 14 | impl Arg for &str { 15 | fn arg(self) -> cmd::Arg<()> { 16 | Command::from_json(self).arg() 17 | } 18 | } 19 | 20 | impl Arg for &String { 21 | fn arg(self) -> cmd::Arg<()> { 22 | Command::from_json(self.as_str()).arg() 23 | } 24 | } 25 | 26 | impl Arg for String { 27 | fn arg(self) -> cmd::Arg<()> { 28 | Command::from_json(self).arg() 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /reql/src/cmd/index_drop.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::IndexDrop).with_arg(self).into_arg() 11 | } 12 | } 13 | 14 | impl Arg for &str { 15 | fn arg(self) -> cmd::Arg<()> { 16 | Command::from_json(self).arg() 17 | } 18 | } 19 | 20 | impl Arg for &String { 21 | fn arg(self) -> cmd::Arg<()> { 22 | Command::from_json(self.as_str()).arg() 23 | } 24 | } 25 | 26 | impl Arg for String { 27 | fn arg(self) -> cmd::Arg<()> { 28 | Command::from_json(self).arg() 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /reql/src/cmd/delete_at.rs: -------------------------------------------------------------------------------- 1 | use super::args::Args; 2 | use crate::{cmd, Command}; 3 | use ql2::term::TermType; 4 | 5 | pub trait Arg { 6 | fn arg(self) -> cmd::Arg<()>; 7 | } 8 | 9 | impl Arg for Command { 10 | fn arg(self) -> cmd::Arg<()> { 11 | Self::new(TermType::DeleteAt).with_arg(self).into_arg() 12 | } 13 | } 14 | 15 | impl Arg for i64 { 16 | fn arg(self) -> cmd::Arg<()> { 17 | Command::from_json(self).arg() 18 | } 19 | } 20 | 21 | impl Arg for Args<[i64; 2]> { 22 | fn arg(self) -> cmd::Arg<()> { 23 | let Args([offset, end_offset]) = self; 24 | Command::from_json(offset) 25 | .arg() 26 | .with_arg(Command::from_json(end_offset)) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /reql/src/cmd/uuid.rs: -------------------------------------------------------------------------------- 1 | use super::args::Args; 2 | use crate::{cmd, Command}; 3 | use ql2::term::TermType; 4 | 5 | pub trait Arg { 6 | fn arg(self) -> cmd::Arg<()>; 7 | } 8 | 9 | impl Arg for cmd::Arg<()> { 10 | fn arg(self) -> cmd::Arg<()> { 11 | self 12 | } 13 | } 14 | 15 | impl Arg for () { 16 | fn arg(self) -> cmd::Arg<()> { 17 | Command::new(TermType::Uuid).into_arg() 18 | } 19 | } 20 | 21 | impl Arg for Command { 22 | fn arg(self) -> cmd::Arg<()> { 23 | ().arg().with_arg(self) 24 | } 25 | } 26 | 27 | impl Arg for Args 28 | where 29 | T: Into, 30 | { 31 | fn arg(self) -> cmd::Arg<()> { 32 | let Args(arg) = self; 33 | Command::from_json(arg.into()).arg() 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /reql/src/cmd/db.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | 4 | pub trait Arg { 5 | fn arg(self) -> cmd::Arg<()>; 6 | } 7 | 8 | impl Arg for Command { 9 | fn arg(self) -> cmd::Arg<()> { 10 | Self::new(TermType::Db).with_arg(self).into_arg() 11 | } 12 | } 13 | 14 | impl Arg for T 15 | where 16 | T: Into, 17 | { 18 | fn arg(self) -> cmd::Arg<()> { 19 | Command::from_json(self.into()).arg() 20 | } 21 | } 22 | 23 | #[cfg(test)] 24 | mod tests { 25 | use crate::{cmd, r}; 26 | 27 | #[test] 28 | fn r_db() { 29 | let query = r.db("foo"); 30 | let serialised = cmd::serialise(&query); 31 | let expected = r#"[14,["foo"]]"#; 32 | assert_eq!(serialised, expected); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /mobc/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mobc-reql" 3 | description = "RethinkDB support for the mobc connection pool" 4 | version = "0.6.4" 5 | license = "MIT/Apache-2.0" 6 | repository = "https://github.com/rethinkdb/rethinkdb-rs/tree/main/mobc" 7 | documentation = "https://docs.rs/mobc-reql" 8 | readme = "README.md" 9 | keywords = ["async", "database", "pool", "reql"] 10 | authors = ["rushmorem "] 11 | edition = "2018" 12 | 13 | [features] 14 | default = ["tokio"] 15 | tokio = ["mobc/tokio"] 16 | async-std = ["mobc/async-std"] 17 | 18 | [dependencies] 19 | blocking = "1.0.2" 20 | futures = "0.3.17" 21 | futures-timer = { version = "3.0.2", default-features = false } 22 | mobc = { version = "0.7.3", default-features = false } 23 | reql = { version = "0.11.0" } 24 | tracing = "0.1.29" 25 | -------------------------------------------------------------------------------- /reql/src/cmd/asc.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command, Func}; 2 | use ql2::term::TermType; 3 | 4 | #[derive(Debug, Clone)] 5 | pub struct Asc(pub(crate) Command); 6 | 7 | pub trait Arg { 8 | fn arg(self) -> cmd::Arg<()>; 9 | } 10 | 11 | impl Arg for cmd::Arg<()> { 12 | fn arg(self) -> cmd::Arg<()> { 13 | self 14 | } 15 | } 16 | 17 | impl Arg for Command { 18 | fn arg(self) -> cmd::Arg<()> { 19 | Self::new(TermType::Asc).with_arg(self).into_arg() 20 | } 21 | } 22 | 23 | impl Arg for T 24 | where 25 | T: Into, 26 | { 27 | fn arg(self) -> cmd::Arg<()> { 28 | Command::from_json(self.into()).arg() 29 | } 30 | } 31 | 32 | impl Arg for Func { 33 | fn arg(self) -> cmd::Arg<()> { 34 | let Func(func) = self; 35 | func.arg() 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /reql/src/cmd/binary.rs: -------------------------------------------------------------------------------- 1 | use crate::types::Binary; 2 | use crate::{cmd, r, Command}; 3 | use ql2::term::TermType; 4 | 5 | pub trait Arg { 6 | fn arg(self) -> cmd::Arg<()>; 7 | } 8 | 9 | impl Arg for Command { 10 | fn arg(self) -> cmd::Arg<()> { 11 | Self::new(TermType::Binary).with_arg(self).into_arg() 12 | } 13 | } 14 | 15 | impl Arg for Binary { 16 | fn arg(self) -> cmd::Arg<()> { 17 | r.expr(self).arg() 18 | } 19 | } 20 | 21 | impl Arg for &[u8] { 22 | fn arg(self) -> cmd::Arg<()> { 23 | Binary::new(self).arg() 24 | } 25 | } 26 | 27 | impl Arg for &Vec { 28 | fn arg(self) -> cmd::Arg<()> { 29 | Binary::new(self).arg() 30 | } 31 | } 32 | 33 | impl Arg for Vec { 34 | fn arg(self) -> cmd::Arg<()> { 35 | Binary::new(&self).arg() 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /reql/src/cmd/add.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | use serde::Serialize; 4 | use std::ops::Add; 5 | 6 | pub trait Arg { 7 | fn arg(self) -> cmd::Arg<()>; 8 | } 9 | 10 | impl Arg for cmd::Arg<()> { 11 | fn arg(self) -> cmd::Arg<()> { 12 | self 13 | } 14 | } 15 | 16 | impl Arg for Command { 17 | fn arg(self) -> cmd::Arg<()> { 18 | Command::new(TermType::Add).with_arg(self).into_arg() 19 | } 20 | } 21 | 22 | impl Arg for T 23 | where 24 | T: Serialize, 25 | { 26 | fn arg(self) -> cmd::Arg<()> { 27 | Command::from_json(self).arg() 28 | } 29 | } 30 | 31 | impl Add for Command 32 | where 33 | T: Arg, 34 | { 35 | type Output = Self; 36 | 37 | fn add(self, arg: T) -> Self { 38 | arg.arg().with_parent(self).into_cmd() 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: trusty 2 | sudo: false 3 | 4 | language: rust 5 | 6 | rust: 7 | - stable 8 | - beta 9 | - nightly 10 | 11 | cache: cargo 12 | 13 | env: RUST_LOG=reql,mobc_reql 14 | 15 | matrix: 16 | include: 17 | - env: TARGET=x86_64-unknown-linux-musl 18 | addons: 19 | apt: 20 | packages: 21 | - musl-tools 22 | 23 | addons: 24 | rethinkdb: '2.4' 25 | 26 | before_script: 27 | - rethinkdb --port-offset 1 --directory rethinkdb_data1 --join localhost:29015 > /dev/null 2>&1 & 28 | - rethinkdb --port-offset 2 --directory rethinkdb_data2 --join localhost:29015 > /dev/null 2>&1 & 29 | - if [[ -n "$TARGET" ]]; then rustup target add "$TARGET"; fi 30 | 31 | script: 32 | - if [[ -n "$TARGET" ]]; then cargo test --target "$TARGET" --lib -- --test-threads=1; fi 33 | - if [[ -z "$TARGET" ]]; then cargo test -- --test-threads=1; fi 34 | -------------------------------------------------------------------------------- /examples/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "reql-examples" 3 | version = "0.1.0" 4 | authors = ["rushmorem "] 5 | edition = "2018" 6 | publish = false 7 | 8 | [dependencies] 9 | futures = "0.3.17" 10 | futures-timer = "3.0.2" 11 | mobc-reql = { path = "../mobc" } 12 | reql = { path = "../reql" } 13 | serde_json = "1.0.68" 14 | tokio = { version = "1.12.0", features = ["macros", "rt-multi-thread", "sync"] } 15 | tracing = "0.1.29" 16 | tracing-subscriber = "0.3.1" 17 | 18 | [[example]] 19 | name = "minimal" 20 | path = "reql/minimal.rs" 21 | 22 | [[example]] 23 | name = "changes" 24 | path = "reql/changes.rs" 25 | 26 | [[example]] 27 | name = "closing-changefeeds" 28 | path = "reql/closing-changefeeds.rs" 29 | 30 | [[example]] 31 | name = "closing-spawned-feed" 32 | path = "reql/closing-spawned-feed.rs" 33 | 34 | [[example]] 35 | name = "simple" 36 | path = "mobc/simple.rs" 37 | -------------------------------------------------------------------------------- /reql/src/cmd/get.rs: -------------------------------------------------------------------------------- 1 | use crate::{cmd, Command}; 2 | use ql2::term::TermType; 3 | use serde::Serialize; 4 | 5 | pub trait Arg { 6 | fn arg(self) -> cmd::Arg<()>; 7 | } 8 | 9 | impl Arg for Command { 10 | fn arg(self) -> cmd::Arg<()> { 11 | Command::new(TermType::Get).with_arg(self).into_arg() 12 | } 13 | } 14 | 15 | impl Arg for T 16 | where 17 | T: Serialize, 18 | { 19 | fn arg(self) -> cmd::Arg<()> { 20 | Command::from_json(self).arg() 21 | } 22 | } 23 | 24 | #[cfg(test)] 25 | mod tests { 26 | use crate::{cmd, r}; 27 | 28 | #[test] 29 | fn r_db_table_get() { 30 | let query = r 31 | .db("foo") 32 | .table("bar") 33 | .get("84fc23ac-9e85-43af-b6f7-f86be17237e1"); 34 | let serialised = cmd::serialise(&query); 35 | let expected = r#"[16,[[15,[[14,["foo"]],"bar"]],"84fc23ac-9e85-43af-b6f7-f86be17237e1"]]"#; 36 | assert_eq!(serialised, expected); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /reql/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "reql" 3 | description = "A native ReQL driver" 4 | version = "0.11.2" 5 | license = "MIT/Apache-2.0" 6 | repository = "https://github.com/rethinkdb/rethinkdb-rs" 7 | documentation = "https://docs.rs/reql" 8 | readme = "README.md" 9 | keywords = ["async", "database", "rethinkdb", "reql", "driver"] 10 | categories = ["database"] 11 | authors = ["rushmorem "] 12 | edition = "2018" 13 | 14 | [dependencies] 15 | async-net = "1.7.0" 16 | async-stream = "0.3.5" 17 | dashmap = "5.5.0" 18 | futures = "0.3.28" 19 | ql2 = "2.1.1" 20 | reql-macros = { version = "0.3.0", path = "../macros" } 21 | reql-types = { version = "0.3.3", path = "../types" } 22 | scram = "0.6.0" 23 | serde = { version = "1.0.175", features = ["derive"] } 24 | serde_json = "1.0.103" 25 | tracing = "0.1.37" 26 | 27 | [dev-dependencies] 28 | tokio = { version = "1.29.1", features = ["macros", "rt-multi-thread"] } 29 | tracing-subscriber = "0.3.17" 30 | 31 | [features] 32 | -------------------------------------------------------------------------------- /reql/src/cmd/delete.rs: -------------------------------------------------------------------------------- 1 | use crate::cmd::{self, Durability, ReturnChanges}; 2 | use crate::Command; 3 | use ql2::term::TermType; 4 | use reql_macros::CommandOptions; 5 | use serde::Serialize; 6 | 7 | // TODO finish this struct 8 | #[derive(Debug, Clone, Copy, CommandOptions, Serialize, Default, PartialEq, PartialOrd)] 9 | #[non_exhaustive] 10 | pub struct Options { 11 | #[serde(skip_serializing_if = "Option::is_none")] 12 | pub durability: Option, 13 | #[serde(skip_serializing_if = "Option::is_none")] 14 | pub return_changes: Option, 15 | } 16 | 17 | pub trait Arg { 18 | fn arg(self) -> cmd::Arg; 19 | } 20 | 21 | impl Arg for cmd::Arg { 22 | fn arg(self) -> cmd::Arg { 23 | self 24 | } 25 | } 26 | 27 | impl Arg for () { 28 | fn arg(self) -> cmd::Arg { 29 | Command::new(TermType::Delete).into_arg() 30 | } 31 | } 32 | 33 | impl Arg for Options { 34 | fn arg(self) -> cmd::Arg { 35 | ().arg().with_opts(self) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /reql/tests/changefeeds.rs: -------------------------------------------------------------------------------- 1 | use futures::stream::{select_all, TryStreamExt}; 2 | use reql::{r, Driver, Error}; 3 | use serde_json::Value; 4 | 5 | #[tokio::test] 6 | async fn changefeeds_should_use_dedicated_connections() { 7 | tracing_subscriber::fmt::init(); 8 | 9 | match changefeeds().await.unwrap_err() { 10 | Error::Driver(Driver::ConnectionLocked) => {} 11 | error => panic!("{:?}", error), 12 | } 13 | } 14 | 15 | async fn changefeeds() -> reql::Result<()> { 16 | let conn = r.connect(()).await?; 17 | 18 | let _ = r 19 | .table_create("foo") 20 | .run::<_, Value>(&conn) 21 | .try_next() 22 | .await; 23 | let foo = r.table("foo").changes(()).run::<_, Value>(&conn); 24 | 25 | let _ = r 26 | .table_create("bar") 27 | .run::<_, Value>(&conn) 28 | .try_next() 29 | .await; 30 | let bar = r.table("bar").changes(()).run::<_, Value>(&conn); 31 | 32 | let mut list = select_all(vec![foo, bar]); 33 | 34 | while let Some(_) = list.try_next().await? {} 35 | 36 | Ok(()) 37 | } 38 | -------------------------------------------------------------------------------- /reql/src/cmd/index.rs: -------------------------------------------------------------------------------- 1 | use super::asc::Asc; 2 | use super::desc::Desc; 3 | use crate::cmd; 4 | use crate::proto::{Command, Query}; 5 | use serde::Serialize; 6 | 7 | #[derive(Debug, Clone)] 8 | pub struct Index(pub(crate) Command); 9 | 10 | #[derive(Serialize)] 11 | struct Inner<'a> { 12 | index: Query<'a>, 13 | } 14 | 15 | pub trait Arg { 16 | fn arg(self) -> cmd::Arg<()>; 17 | } 18 | 19 | impl Arg for T 20 | where 21 | T: Into, 22 | { 23 | fn arg(self) -> cmd::Arg<()> { 24 | let cmd = Command::from_json(self.into()); 25 | Command::from_json(Inner { index: Query(&cmd) }).into_arg() 26 | } 27 | } 28 | 29 | impl Arg for Asc { 30 | fn arg(self) -> cmd::Arg<()> { 31 | let Asc(index) = self; 32 | Command::from_json(Inner { 33 | index: Query(&index), 34 | }) 35 | .into_arg() 36 | } 37 | } 38 | 39 | impl Arg for Desc { 40 | fn arg(self) -> cmd::Arg<()> { 41 | let Desc(index) = self; 42 | Command::from_json(Inner { 43 | index: Query(&index), 44 | }) 45 | .into_arg() 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /reql/src/cmd/branch.rs: -------------------------------------------------------------------------------- 1 | use super::args::Args; 2 | use crate::{cmd, Command}; 3 | use ql2::term::TermType; 4 | 5 | pub trait Arg { 6 | fn arg(self) -> cmd::Arg<()>; 7 | } 8 | 9 | impl Arg for Command { 10 | fn arg(self) -> cmd::Arg<()> { 11 | Self::new(TermType::Branch).with_arg(self).into_arg() 12 | } 13 | } 14 | 15 | impl Arg for Args<(Command, Command, Command)> { 16 | fn arg(self) -> cmd::Arg<()> { 17 | let Args((test, true_action, false_action)) = self; 18 | test.arg().with_arg(true_action).with_arg(false_action) 19 | } 20 | } 21 | 22 | #[allow(array_into_iter)] 23 | #[allow(clippy::into_iter_on_ref)] 24 | impl Arg for Args<([(Command, Command); N], Command)> { 25 | fn arg(self) -> cmd::Arg<()> { 26 | let Args((arr, false_action)) = self; 27 | let mut query = Command::new(TermType::Branch); 28 | // TODO remove the clone in Rust v1.53 29 | for (test, true_action) in arr.into_iter().cloned() { 30 | query = query.with_arg(test).with_arg(true_action); 31 | } 32 | query.with_arg(false_action).into_arg() 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /examples/reql/minimal.rs: -------------------------------------------------------------------------------- 1 | use futures::TryStreamExt; 2 | use reql::r; 3 | use reql::types::ServerStatus; 4 | 5 | // We are using `tokio` here as an example but you can use this crate 6 | // with any runtime 7 | #[tokio::main] 8 | async fn main() -> reql::Result<()> { 9 | // Initialise the logger if you need to debug this crate 10 | tracing_subscriber::fmt::init(); 11 | 12 | // Create a RethinkDB connection out of the stream 13 | // See the API docs for more options you can configure 14 | let conn = r.connect(()).await?; 15 | 16 | // Create the query you want to run 17 | // The query returns a `Stream` of responses from RethinkDB 18 | let mut query = r.db("rethinkdb").table("server_status").run(&conn); 19 | 20 | // Execute the query and handle the result 21 | if let Some(server_status) = query.try_next().await? { 22 | handle(&server_status)?; 23 | } 24 | 25 | Ok(()) 26 | } 27 | 28 | // We are just going to print the JSON response for this example 29 | fn handle(server_status: &ServerStatus) -> reql::Result<()> { 30 | println!("{}", serde_json::to_string(server_status)?); 31 | Ok(()) 32 | } 33 | -------------------------------------------------------------------------------- /examples/mobc/simple.rs: -------------------------------------------------------------------------------- 1 | use futures::TryStreamExt; 2 | use mobc_reql::{GetSession, Pool, SessionManager}; 3 | use reql::cmd::connect::Options; 4 | use reql::r; 5 | use std::time::Instant; 6 | 7 | #[tokio::main] 8 | async fn main() { 9 | tracing_subscriber::fmt::init(); 10 | 11 | let manager = SessionManager::new(Options::new()); 12 | tokio::spawn(manager.discover_hosts()); 13 | let pool = Pool::builder().max_open(20).build(manager); 14 | const MAX: usize = 5000; 15 | 16 | let now = Instant::now(); 17 | let (tx, mut rx) = tokio::sync::mpsc::channel::(16); 18 | for i in 0..MAX { 19 | let pool = pool.clone(); 20 | let tx_c = tx.clone(); 21 | tokio::spawn(async move { 22 | let sum = r.expr(1) + r.expr(2); 23 | let conn = pool.session().await.unwrap(); 24 | let value: i32 = sum.run(&conn).try_next().await.unwrap().unwrap(); 25 | assert_eq!(value, 3); 26 | tx_c.send(i).await.unwrap(); 27 | }); 28 | } 29 | for _ in 0..MAX { 30 | rx.recv().await.unwrap(); 31 | } 32 | 33 | println!("cost: {:?}", now.elapsed()); 34 | } 35 | -------------------------------------------------------------------------------- /examples/reql/changes.rs: -------------------------------------------------------------------------------- 1 | use futures::TryStreamExt; 2 | use reql::r; 3 | use reql::types::Change; 4 | use serde_json::Value; 5 | 6 | // We are using `tokio` here as an example but you can use this crate 7 | // with any runtime 8 | #[tokio::main] 9 | async fn main() -> reql::Result<()> { 10 | // Initialise the logger if you need to debug this crate 11 | tracing_subscriber::fmt::init(); 12 | 13 | // Create a RethinkDB connection out of the stream 14 | // See the API docs for more options you can configure 15 | let conn = r.connect(()).await?; 16 | 17 | // Create the query you want to run 18 | // The query returns a `Stream` of responses from RethinkDB 19 | let mut query = r.db("rethinkdb").table("jobs").changes(()).run(&conn); 20 | 21 | // Execute the query and handle the result 22 | while let Some(change) = query.try_next().await? { 23 | handle(change)?; 24 | } 25 | 26 | Ok(()) 27 | } 28 | 29 | // We are just going to print the JSON response for this example 30 | fn handle(change: Change) -> reql::Result<()> { 31 | println!("{}", serde_json::to_string(&change)?); 32 | Ok(()) 33 | } 34 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 The Rust Project Developers 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /mobc/README.md: -------------------------------------------------------------------------------- 1 | # mobc-reql 2 | 3 | ReQL connection pool implementation 4 | 5 | ```rust 6 | use mobc_reql::{GetSession, Pool, SessionManager}; 7 | 8 | // Create the session manager 9 | let manager = SessionManager::new(Default::default()); 10 | 11 | // Pull the rest of your nodes from your cluster. The connection pool 12 | // connects to the node with the lowest latency. 13 | // It is optional but highly recommended. This way, your app will 14 | // continue working even when nodes go up and down. 15 | tokio::spawn(manager.discover_hosts()); 16 | 17 | // Create the pool 18 | let pool = Pool::builder().max_open(20).build(manager); 19 | 20 | // Get a session from the pool 21 | let session = pool.session().await?; 22 | 23 | // You can pass a reference of the session to run. 24 | // This allows you to use the same underlying connection for multiple 25 | // queries as long as none of them is a change-feed. 26 | // You can even use just one connection for your entire app, even running 27 | // the queries concurrently. 28 | // 29 | // Change feeds on the other hand, require dedicated connections, 30 | // so for each changefeed you need to grab a new session from the pool. 31 | r.expr("Hello world!").run(&session); 32 | ``` 33 | -------------------------------------------------------------------------------- /reql/src/cmd/filter.rs: -------------------------------------------------------------------------------- 1 | use super::args::Args; 2 | use crate::{cmd, Command, Func}; 3 | use ql2::term::TermType; 4 | use reql_macros::CommandOptions; 5 | use serde::Serialize; 6 | 7 | #[derive( 8 | Debug, Clone, Copy, CommandOptions, Serialize, Default, Eq, PartialEq, Ord, PartialOrd, Hash, 9 | )] 10 | #[non_exhaustive] 11 | pub struct Options { 12 | #[serde(skip_serializing_if = "Option::is_none")] 13 | pub default: Option, 14 | } 15 | 16 | pub trait Arg { 17 | fn arg(self) -> cmd::Arg; 18 | } 19 | 20 | impl Arg for Command { 21 | fn arg(self) -> cmd::Arg { 22 | Self::new(TermType::Filter).with_arg(self).into_arg() 23 | } 24 | } 25 | 26 | impl Arg for Args<(Command, Options)> { 27 | fn arg(self) -> cmd::Arg { 28 | let Args((arg, opts)) = self; 29 | arg.arg().with_opts(opts) 30 | } 31 | } 32 | 33 | impl Arg for Func { 34 | fn arg(self) -> cmd::Arg { 35 | let Func(arg) = self; 36 | arg.arg() 37 | } 38 | } 39 | 40 | impl Arg for Args<(Func, Options)> { 41 | fn arg(self) -> cmd::Arg { 42 | let Args((Func(arg), opts)) = self; 43 | arg.arg().with_opts(opts) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /reql/tests/index_create.rs: -------------------------------------------------------------------------------- 1 | use futures::TryStreamExt; 2 | use reql::{func, r}; 3 | use serde_json::Value; 4 | 5 | #[tokio::test] 6 | async fn index_create() -> reql::Result<()> { 7 | tracing_subscriber::fmt::init(); 8 | 9 | let conn = r.connect(()).await?; 10 | 11 | let _ = r 12 | .table_create("comments") 13 | .run::<_, Value>(&conn) 14 | .try_next() 15 | .await; 16 | 17 | let _ = r 18 | .table("comments") 19 | .index_drop("author_name") 20 | .run::<_, Value>(&conn) 21 | .try_next() 22 | .await; 23 | 24 | let _ = r 25 | .table("comments") 26 | .index_create(r.args(( 27 | "author_name", 28 | func!(|doc| doc.bracket("author").bracket("name")), 29 | ))) 30 | .run::<_, Value>(&conn) 31 | .try_next() 32 | .await?; 33 | 34 | let _ = r 35 | .table("comments") 36 | .index_drop("post_and_date") 37 | .run::<_, Value>(&conn) 38 | .try_next() 39 | .await; 40 | 41 | let _ = r 42 | .table("comments") 43 | .index_create(r.args(( 44 | "post_and_date", 45 | func!(|doc| [doc.clone().bracket("post_id"), doc.bracket("date")]), 46 | ))) 47 | .run::<_, Value>(&conn) 48 | .try_next() 49 | .await?; 50 | 51 | Ok(()) 52 | } 53 | -------------------------------------------------------------------------------- /reql/README.md: -------------------------------------------------------------------------------- 1 | # ReQL Driver 2 | 3 | This is a [ReQL] driver written in [Rust]. 4 | 5 | [![travis-badge][]][travis] 6 | [![cratesio-badge][]][cratesio] 7 | [![docsrs-badge][]][docsrs] 8 | 9 | ## Sponsors 10 | 11 | Development of this driver has in part been sponsored by [WideFind AB], an 12 | IoT company based in Sweden, developing high-accuracy, high performance 13 | indoor positioning hardware and software solutions. 14 | 15 | ## License 16 | 17 | Licensed under either of 18 | * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or [asl2]) 19 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 20 | at your option. 21 | 22 | ### Contribution 23 | 24 | Unless you explicitly state otherwise, any contribution intentionally submitted 25 | for inclusion in the work by you shall be dual licensed as above, without any 26 | additional terms or conditions. 27 | 28 | [ReQL]: https://rethinkdb.com/api 29 | [Rust]: https://rust-lang.org 30 | [travis-badge]: https://travis-ci.org/rethinkdb/rethinkdb-rs.svg?branch=next 31 | [travis]: https://travis-ci.org/rethinkdb/rethinkdb-rs 32 | [cratesio-badge]: https://img.shields.io/crates/v/reql.svg 33 | [cratesio]: https://crates.io/crates/reql 34 | [docsrs-badge]: https://docs.rs/reql/badge.svg 35 | [docsrs]: https://docs.rs/reql 36 | [WideFind AB]: https://www.widefind.se 37 | [asl2]: http://www.apache.org/licenses/LICENSE-2.0 38 | -------------------------------------------------------------------------------- /reql/src/cmd/order_by.rs: -------------------------------------------------------------------------------- 1 | use super::args::Args; 2 | use super::index::Index; 3 | use crate::{ 4 | cmd, 5 | cmd::{asc::Asc, desc::Desc}, 6 | Command, Func, 7 | }; 8 | use ql2::term::TermType; 9 | 10 | pub trait Arg { 11 | fn arg(self) -> cmd::Arg<()>; 12 | } 13 | 14 | impl Arg for Command { 15 | fn arg(self) -> cmd::Arg<()> { 16 | Self::new(TermType::OrderBy).with_arg(self).into_arg() 17 | } 18 | } 19 | 20 | impl Arg for Desc { 21 | fn arg(self) -> cmd::Arg<()> { 22 | Command::new(TermType::OrderBy).with_arg(self.0).into_arg() 23 | } 24 | } 25 | 26 | impl Arg for Asc { 27 | fn arg(self) -> cmd::Arg<()> { 28 | Command::new(TermType::OrderBy).with_arg(self.0).into_arg() 29 | } 30 | } 31 | 32 | impl Arg for T 33 | where 34 | T: Into, 35 | { 36 | fn arg(self) -> cmd::Arg<()> { 37 | Command::from_json(self.into()).arg() 38 | } 39 | } 40 | 41 | impl Arg for Args<(T, Index)> 42 | where 43 | T: Into, 44 | { 45 | fn arg(self) -> cmd::Arg<()> { 46 | let Args((key, Index(index))) = self; 47 | Command::from_json(key.into()).arg().with_arg(index) 48 | } 49 | } 50 | 51 | impl Arg for Func { 52 | fn arg(self) -> cmd::Arg<()> { 53 | let Func(func) = self; 54 | func.arg() 55 | } 56 | } 57 | 58 | impl Arg for Args<(Func, Index)> { 59 | fn arg(self) -> cmd::Arg<()> { 60 | let Args((Func(func), Index(index))) = self; 61 | func.arg().with_arg(index) 62 | } 63 | } 64 | 65 | impl Arg for Index { 66 | fn arg(self) -> cmd::Arg<()> { 67 | let Index(query) = self; 68 | query.arg() 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /reql/src/cmd/between.rs: -------------------------------------------------------------------------------- 1 | use super::args::Args; 2 | use crate::{cmd, Command}; 3 | use ql2::term::TermType; 4 | use reql_macros::CommandOptions; 5 | use serde::Serialize; 6 | 7 | #[derive( 8 | Debug, Clone, Copy, CommandOptions, Serialize, Default, Eq, PartialEq, Ord, PartialOrd, Hash, 9 | )] 10 | #[non_exhaustive] 11 | pub struct Options<'a> { 12 | #[serde(skip_serializing_if = "Option::is_none")] 13 | pub index: Option<&'a str>, 14 | #[serde(skip_serializing_if = "Option::is_none")] 15 | pub left_bound: Option, 16 | #[serde(skip_serializing_if = "Option::is_none")] 17 | pub right_bound: Option, 18 | } 19 | 20 | #[derive(Debug, Clone, Copy, Serialize, Eq, PartialEq, Ord, PartialOrd, Hash)] 21 | #[serde(rename_all = "lowercase")] 22 | #[non_exhaustive] 23 | pub enum Status { 24 | Open, 25 | Closed, 26 | } 27 | 28 | pub trait Arg<'a> { 29 | fn arg(self) -> cmd::Arg>; 30 | } 31 | 32 | impl<'a> Arg<'a> for Command { 33 | fn arg(self) -> cmd::Arg> { 34 | Self::new(TermType::Between).with_arg(self).into_arg() 35 | } 36 | } 37 | 38 | impl<'a> Arg<'a> for Args<(Command, Options<'a>)> { 39 | fn arg(self) -> cmd::Arg> { 40 | let Args((query, opts)) = self; 41 | query.arg().with_opts(opts) 42 | } 43 | } 44 | 45 | impl<'a, T> Arg<'a> for Args<(T, T)> 46 | where 47 | T: Serialize, 48 | { 49 | fn arg(self) -> cmd::Arg> { 50 | let Args((min, max)) = self; 51 | let max = Command::from_json(max); 52 | Command::from_json(min).arg().with_arg(max) 53 | } 54 | } 55 | 56 | impl<'a, T> Arg<'a> for Args<(T, T, Options<'a>)> 57 | where 58 | T: Serialize, 59 | { 60 | fn arg(self) -> cmd::Arg> { 61 | let Args((min, max, opts)) = self; 62 | let max = Command::from_json(max); 63 | Command::from_json(min).arg().with_arg(max).with_opts(opts) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /reql/src/cmd/replace.rs: -------------------------------------------------------------------------------- 1 | use super::args::Args; 2 | use crate::cmd::{self, Durability, ReturnChanges}; 3 | use crate::{Command, Func}; 4 | use ql2::term::TermType; 5 | use reql_macros::CommandOptions; 6 | use serde::Serialize; 7 | 8 | // TODO finish this struct 9 | #[derive(Debug, Clone, Copy, CommandOptions, Serialize, Default, PartialEq, PartialOrd)] 10 | #[non_exhaustive] 11 | pub struct Options { 12 | #[serde(skip_serializing_if = "Option::is_none")] 13 | pub durability: Option, 14 | #[serde(skip_serializing_if = "Option::is_none")] 15 | pub return_changes: Option, 16 | } 17 | 18 | pub trait Arg { 19 | fn arg(self) -> cmd::Arg; 20 | } 21 | 22 | impl Arg for cmd::Arg { 23 | fn arg(self) -> cmd::Arg { 24 | self 25 | } 26 | } 27 | 28 | impl Arg for Command { 29 | fn arg(self) -> cmd::Arg { 30 | Command::new(TermType::Replace).with_arg(self).into_arg() 31 | } 32 | } 33 | 34 | impl Arg for T 35 | where 36 | T: Serialize, 37 | { 38 | fn arg(self) -> cmd::Arg { 39 | Command::from_json(self).arg() 40 | } 41 | } 42 | 43 | impl Arg for Args<(Command, Options)> { 44 | fn arg(self) -> cmd::Arg { 45 | let Args((arg, opts)) = self; 46 | arg.arg().with_opts(opts) 47 | } 48 | } 49 | 50 | impl Arg for Args<(T, Options)> 51 | where 52 | T: Serialize, 53 | { 54 | fn arg(self) -> cmd::Arg { 55 | let Args((arg, opts)) = self; 56 | let arg = Command::from_json(arg); 57 | arg.arg().with_opts(opts) 58 | } 59 | } 60 | 61 | impl Arg for Func { 62 | fn arg(self) -> cmd::Arg { 63 | let Func(func) = self; 64 | func.arg() 65 | } 66 | } 67 | 68 | impl Arg for Args<(Func, Options)> { 69 | fn arg(self) -> cmd::Arg { 70 | let Args((Func(func), opts)) = self; 71 | func.arg().with_opts(opts) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /reql/src/cmd/update.rs: -------------------------------------------------------------------------------- 1 | use super::args::Args; 2 | use crate::cmd::{self, Durability, ReturnChanges}; 3 | use crate::{Command, Func}; 4 | use ql2::term::TermType; 5 | use reql_macros::CommandOptions; 6 | use serde::Serialize; 7 | 8 | // TODO finish this struct 9 | #[derive(Debug, Clone, Copy, CommandOptions, Serialize, Default, PartialEq, PartialOrd)] 10 | #[non_exhaustive] 11 | pub struct Options { 12 | #[serde(skip_serializing_if = "Option::is_none")] 13 | pub durability: Option, 14 | #[serde(skip_serializing_if = "Option::is_none")] 15 | pub return_changes: Option, 16 | } 17 | 18 | pub trait Arg { 19 | fn arg(self) -> cmd::Arg; 20 | } 21 | 22 | impl Arg for cmd::Arg { 23 | fn arg(self) -> cmd::Arg { 24 | self 25 | } 26 | } 27 | 28 | impl Arg for Command { 29 | fn arg(self) -> cmd::Arg { 30 | Command::new(TermType::Update).with_arg(self).into_arg() 31 | } 32 | } 33 | 34 | impl Arg for T 35 | where 36 | T: Serialize, 37 | { 38 | fn arg(self) -> cmd::Arg { 39 | Command::from_json(self).arg() 40 | } 41 | } 42 | 43 | impl Arg for Args<(Command, Options)> { 44 | fn arg(self) -> cmd::Arg { 45 | let Args((arg, opts)) = self; 46 | arg.arg().with_opts(opts) 47 | } 48 | } 49 | 50 | impl Arg for Args<(T, Options)> 51 | where 52 | T: Serialize, 53 | { 54 | fn arg(self) -> cmd::Arg { 55 | let Args((arg, opts)) = self; 56 | let arg = Command::from_json(arg); 57 | arg.arg().with_opts(opts) 58 | } 59 | } 60 | 61 | impl Arg for Func { 62 | fn arg(self) -> cmd::Arg { 63 | let Func(func) = self; 64 | func.arg() 65 | } 66 | } 67 | 68 | impl Arg for Args<(Func, Options)> { 69 | fn arg(self) -> cmd::Arg { 70 | let Args((Func(func), opts)) = self; 71 | func.arg().with_opts(opts) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /reql/src/cmd/close.rs: -------------------------------------------------------------------------------- 1 | //! Close an open connection 2 | //! 3 | //! Closing a connection normally waits until all outstanding requests have 4 | //! finished and then frees any open resources associated with the 5 | //! connection. By passing `SkipNoreplyWait` as the argument, the connection 6 | //! will be closed immediately, possibly aborting any outstanding noreply 7 | //! writes. 8 | //! 9 | //! A noreply query is executed by passing the `noreply` option to the 10 | //! [run](crate::Command::run) command, indicating that `run()` should not 11 | //! wait for the query to complete before returning. You may also 12 | //! explicitly wait for a noreply query to complete by using the 13 | //! [noreply_wait](crate::Session::noreply_wait) command. 14 | //! 15 | //! 16 | //! ## Example 17 | //! 18 | //! Close an open connection, waiting for noreply writes to finish. 19 | //! 20 | //! ``` 21 | //! # async fn example() -> reql::Result<()> { 22 | //! # let session = reql::r.connect(()).await?; 23 | //! # let mut conn = session.connection()?; 24 | //! conn.close(()).await 25 | //! # } 26 | //! ``` 27 | //! 28 | //! ## Example 29 | //! 30 | //! Close an open connection immediately. 31 | //! 32 | //! ``` 33 | //! # use reql::cmd::close::SkipNoreplyWait; 34 | //! # async fn example() -> reql::Result<()> { 35 | //! # let session = reql::r.connect(()).await?; 36 | //! # let mut conn = session.connection()?; 37 | //! conn.close(SkipNoreplyWait).await 38 | //! # } 39 | //! ``` 40 | //! 41 | //! ## Related commands 42 | //! 43 | //! * [connect](crate::r::connect) 44 | //! * [use](crate::Session::use_) 45 | 46 | /// Skip waiting for `noreply` queries 47 | #[derive(Debug)] 48 | pub struct SkipNoreplyWait; 49 | 50 | pub trait Arg { 51 | fn noreply_wait(self) -> bool; 52 | } 53 | 54 | impl Arg for () { 55 | fn noreply_wait(self) -> bool { 56 | true 57 | } 58 | } 59 | 60 | impl Arg for SkipNoreplyWait { 61 | fn noreply_wait(self) -> bool { 62 | false 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /examples/reql/closing-changefeeds.rs: -------------------------------------------------------------------------------- 1 | use futures::TryStreamExt; 2 | use reql::r; 3 | use serde_json::Value; 4 | 5 | // We are using `tokio` here as an example but you can use this crate 6 | // with any runtime 7 | #[tokio::main] 8 | async fn main() -> reql::Result<()> { 9 | // Initialise the logger if you need to debug this crate 10 | tracing_subscriber::fmt::init(); 11 | 12 | // Connect to create a RethinkDB session 13 | let session = r.connect(()).await?; 14 | 15 | // Manually get a connection from the session 16 | // Usually, this is not necessary (see other examples) but in this 17 | // case we need a handle to the connection so we can call close on 18 | // it later 19 | let mut connection = session.connection()?; 20 | 21 | // Clone the connection to get an instance to use for our feed below 22 | let conn = connection.clone(); 23 | 24 | // Create the query you want to run 25 | // The query returns a `Stream` of responses from RethinkDB 26 | let mut query = r.db("rethinkdb").table("jobs").changes(()).run(conn); 27 | 28 | // Execute the query and handle the result 29 | while let Some(change) = query.try_next().await? { 30 | // We are just going to print the first result 31 | print_json(change)?; 32 | // and then close the changefeed 33 | connection.close(()).await?; 34 | break; 35 | } 36 | 37 | // We can now use the same connection to run more queries 38 | // We wouldn't be able to do this otherwise since this driver 39 | // returns an error if you try to run more queries on a 40 | // connection that is running a changefeed 41 | let mut query = r.db("rethinkdb").table("server_status").run(connection); 42 | 43 | // Execute the query and print the result 44 | if let Some(server_status) = query.try_next().await? { 45 | print_json(server_status)?; 46 | } 47 | 48 | Ok(()) 49 | } 50 | 51 | // We are just going to print the JSON response for this example 52 | fn print_json(json: Value) -> reql::Result<()> { 53 | println!("{}", serde_json::to_string(&json)?); 54 | Ok(()) 55 | } 56 | -------------------------------------------------------------------------------- /reql/src/cmd/get_all.rs: -------------------------------------------------------------------------------- 1 | use super::args::Args; 2 | use crate::{cmd, Command}; 3 | use ql2::term::TermType; 4 | use reql_macros::CommandOptions; 5 | use serde::Serialize; 6 | use std::borrow::Cow; 7 | 8 | #[derive(Debug, Clone, CommandOptions, Serialize, Default, PartialEq, PartialOrd)] 9 | #[non_exhaustive] 10 | pub struct Options { 11 | pub index: Option>, 12 | } 13 | 14 | pub trait Arg { 15 | fn arg(self) -> cmd::Arg; 16 | } 17 | 18 | impl Arg for Command { 19 | fn arg(self) -> cmd::Arg { 20 | Self::new(TermType::GetAll).with_arg(self).into_arg() 21 | } 22 | } 23 | 24 | impl Arg for T 25 | where 26 | T: Into, 27 | { 28 | fn arg(self) -> cmd::Arg { 29 | Command::from_json(self.into()).arg() 30 | } 31 | } 32 | 33 | impl Arg for Args<(&str, Options)> { 34 | fn arg(self) -> cmd::Arg { 35 | let Args((key, opts)) = self; 36 | key.arg().with_opts(opts) 37 | } 38 | } 39 | 40 | #[allow(array_into_iter)] 41 | #[allow(clippy::into_iter_on_ref)] 42 | impl Arg for Args<[T; N]> 43 | where 44 | T: Into + Clone, 45 | { 46 | fn arg(self) -> cmd::Arg { 47 | let Args(arr) = self; 48 | let mut query = Command::new(TermType::GetAll); 49 | // TODO get rid of the clone in Rust v1.53 50 | for arg in arr.into_iter().cloned() { 51 | let arg = Command::from_json(arg.into()); 52 | query = query.with_arg(arg); 53 | } 54 | query.into_arg() 55 | } 56 | } 57 | 58 | #[allow(array_into_iter)] 59 | #[allow(clippy::into_iter_on_ref)] 60 | impl Arg for Args<([T; N], Options)> 61 | where 62 | T: Into + Clone, 63 | { 64 | fn arg(self) -> cmd::Arg { 65 | let Args((arr, opts)) = self; 66 | let mut query = Command::new(TermType::GetAll); 67 | // TODO get rid of the clone in Rust v1.53 68 | for arg in arr.into_iter().cloned() { 69 | let arg = Command::from_json(arg.into()); 70 | query = query.with_arg(arg); 71 | } 72 | query.with_opts(opts).into_arg() 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /reql/src/cmd/table.rs: -------------------------------------------------------------------------------- 1 | use super::args::Args; 2 | use super::ReadMode; 3 | use crate::{cmd, Command}; 4 | use ql2::term::TermType; 5 | use reql_macros::CommandOptions; 6 | use serde::Serialize; 7 | 8 | #[derive(Debug, Clone, Copy, CommandOptions, Serialize, Default, PartialEq, PartialOrd)] 9 | #[non_exhaustive] 10 | pub struct Options { 11 | #[serde(skip_serializing_if = "Option::is_none")] 12 | pub read_mode: Option, 13 | #[serde(skip_serializing_if = "Option::is_none")] 14 | pub identifier_format: Option, 15 | } 16 | 17 | #[derive(Debug, Clone, Copy, Serialize, PartialEq, PartialOrd)] 18 | #[non_exhaustive] 19 | #[serde(rename_all = "lowercase")] 20 | pub enum IdentifierFormat { 21 | Name, 22 | Uuid, 23 | } 24 | 25 | pub trait Arg { 26 | fn arg(self) -> cmd::Arg; 27 | } 28 | 29 | impl Arg for Command { 30 | fn arg(self) -> cmd::Arg { 31 | Self::new(TermType::Table).with_arg(self).into_arg() 32 | } 33 | } 34 | 35 | impl Arg for T 36 | where 37 | T: Into, 38 | { 39 | fn arg(self) -> cmd::Arg { 40 | Command::from_json(self.into()).arg() 41 | } 42 | } 43 | 44 | impl Arg for Args<(Command, Options)> { 45 | fn arg(self) -> cmd::Arg { 46 | let Args((query, options)) = self; 47 | query.arg().with_opts(options) 48 | } 49 | } 50 | 51 | impl Arg for Args<(T, Options)> 52 | where 53 | T: Into, 54 | { 55 | fn arg(self) -> cmd::Arg { 56 | let Args((name, options)) = self; 57 | name.arg().with_opts(options) 58 | } 59 | } 60 | 61 | #[cfg(test)] 62 | mod tests { 63 | use crate::{cmd, r}; 64 | 65 | #[test] 66 | fn r_table() { 67 | let query = r.table("foo"); 68 | let serialised = cmd::serialise(&query); 69 | let expected = r#"[15,["foo"]]"#; 70 | assert_eq!(serialised, expected); 71 | } 72 | 73 | #[test] 74 | fn r_db_table() { 75 | let query = r.db("foo").table("bar"); 76 | let serialised = cmd::serialise(&query); 77 | let expected = r#"[15,[[14,["foo"]],"bar"]]"#; 78 | assert_eq!(serialised, expected); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /reql/src/cmd/index_create.rs: -------------------------------------------------------------------------------- 1 | use super::args::Args; 2 | use crate::{cmd, Command, Func}; 3 | use ql2::term::TermType; 4 | use reql_macros::CommandOptions; 5 | use serde::Serialize; 6 | 7 | #[derive(Debug, Clone, Copy, CommandOptions, Serialize, Default, PartialEq, PartialOrd)] 8 | pub struct Options { 9 | #[serde(skip_serializing_if = "Option::is_none")] 10 | pub multi: Option, 11 | #[serde(skip_serializing_if = "Option::is_none")] 12 | pub geo: Option, 13 | } 14 | 15 | pub trait Arg { 16 | fn arg(self) -> cmd::Arg; 17 | } 18 | 19 | impl Arg for Command { 20 | fn arg(self) -> cmd::Arg { 21 | Self::new(TermType::IndexCreate).with_arg(self).into_arg() 22 | } 23 | } 24 | 25 | impl Arg for T 26 | where 27 | T: Into, 28 | { 29 | fn arg(self) -> cmd::Arg { 30 | Command::from_json(self.into()).arg() 31 | } 32 | } 33 | 34 | impl Arg for Args<(T, Func)> 35 | where 36 | T: Into, 37 | { 38 | fn arg(self) -> cmd::Arg { 39 | let Args((name, Func(func))) = self; 40 | name.arg().with_arg(func) 41 | } 42 | } 43 | 44 | impl Arg for Args<(T, R)> 45 | where 46 | T: Into, 47 | R: Into, 48 | { 49 | fn arg(self) -> cmd::Arg { 50 | let Args((name, query)) = self; 51 | let func = Func::row(query); 52 | Args((name, func)).arg() 53 | } 54 | } 55 | 56 | impl Arg for Args<(T, Options)> 57 | where 58 | T: Into, 59 | { 60 | fn arg(self) -> cmd::Arg { 61 | let Args((name, opts)) = self; 62 | name.arg().with_opts(opts) 63 | } 64 | } 65 | 66 | impl Arg for Args<(T, Func, Options)> 67 | where 68 | T: Into, 69 | { 70 | fn arg(self) -> cmd::Arg { 71 | let Args((name, Func(func), opts)) = self; 72 | name.arg().with_arg(func).with_opts(opts) 73 | } 74 | } 75 | 76 | impl Arg for Args<(T, R, Options)> 77 | where 78 | T: Into, 79 | R: Into, 80 | { 81 | fn arg(self) -> cmd::Arg { 82 | let Args((name, query, opts)) = self; 83 | let Func(func) = Func::row(query); 84 | name.arg().with_arg(func).with_opts(opts) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /reql/src/cmd/map.rs: -------------------------------------------------------------------------------- 1 | use super::args::Args; 2 | use crate::{cmd, Command, Func}; 3 | use ql2::term::TermType; 4 | use serde::Serialize; 5 | 6 | pub trait Arg { 7 | fn arg(self) -> cmd::Arg<()>; 8 | } 9 | 10 | impl Arg for cmd::Arg<()> { 11 | fn arg(self) -> cmd::Arg<()> { 12 | self 13 | } 14 | } 15 | 16 | impl Arg for Command { 17 | fn arg(self) -> cmd::Arg<()> { 18 | Command::new(TermType::Map).with_arg(self).into_arg() 19 | } 20 | } 21 | 22 | impl Arg for Func { 23 | fn arg(self) -> cmd::Arg<()> { 24 | let Func(func) = self; 25 | func.arg() 26 | } 27 | } 28 | 29 | impl Arg for Args<(Command, Func)> { 30 | fn arg(self) -> cmd::Arg<()> { 31 | let Args((sequence, Func(func))) = self; 32 | sequence.arg().with_arg(func) 33 | } 34 | } 35 | 36 | #[allow(array_into_iter)] 37 | #[allow(clippy::into_iter_on_ref)] 38 | impl Arg for Args<([Command; N], Func)> { 39 | fn arg(self) -> cmd::Arg<()> { 40 | let Args((sequence, Func(func))) = self; 41 | let mut cmd = cmd::Arg::new(); 42 | if N == 0 { 43 | func.arg() 44 | } else { 45 | for (i, arg) in sequence.into_iter().cloned().enumerate() { 46 | if i == 0 { 47 | cmd = arg.arg(); 48 | } else { 49 | cmd = cmd.with_arg(arg); 50 | } 51 | } 52 | cmd.with_arg(func) 53 | } 54 | } 55 | } 56 | 57 | #[allow(array_into_iter)] 58 | #[allow(clippy::into_iter_on_ref)] 59 | impl Arg for Args<([T; N], Func)> 60 | where 61 | T: Serialize + Clone, 62 | { 63 | fn arg(self) -> cmd::Arg<()> { 64 | let Args((sequence, Func(func))) = self; 65 | let mut cmd = cmd::Arg::new(); 66 | if N == 0 { 67 | func.arg() 68 | } else { 69 | for (i, arg) in sequence.into_iter().cloned().enumerate() { 70 | let arg = Command::from_json(arg); 71 | if i == 0 { 72 | cmd = arg.arg(); 73 | } else { 74 | cmd = cmd.with_arg(arg); 75 | } 76 | } 77 | cmd.with_arg(func) 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /reql/src/cmd/insert.rs: -------------------------------------------------------------------------------- 1 | use super::args::Args; 2 | use crate::cmd::{Conflict, Durability, ReturnChanges}; 3 | use crate::{cmd, Command}; 4 | use ql2::term::TermType; 5 | use reql_macros::CommandOptions; 6 | use serde::Serialize; 7 | 8 | // TODO finish this struct 9 | #[derive(Debug, Clone, Copy, CommandOptions, Serialize, Default, PartialEq, PartialOrd)] 10 | #[non_exhaustive] 11 | pub struct Options { 12 | #[serde(skip_serializing_if = "Option::is_none")] 13 | pub durability: Option, 14 | #[serde(skip_serializing_if = "Option::is_none")] 15 | pub return_changes: Option, 16 | #[serde(skip_serializing_if = "Option::is_none")] 17 | pub conflict: Option, 18 | } 19 | 20 | pub trait Arg { 21 | fn arg(self) -> cmd::Arg; 22 | } 23 | 24 | impl Arg for cmd::Arg { 25 | fn arg(self) -> cmd::Arg { 26 | self 27 | } 28 | } 29 | 30 | impl Arg for Command { 31 | fn arg(self) -> cmd::Arg { 32 | Command::new(TermType::Insert).with_arg(self).into_arg() 33 | } 34 | } 35 | 36 | impl Arg for T 37 | where 38 | T: Serialize, 39 | { 40 | fn arg(self) -> cmd::Arg { 41 | let arg = Command::from_json(self); 42 | Command::new(TermType::Insert).with_arg(arg).into_arg() 43 | } 44 | } 45 | 46 | impl Arg for Args<(Command, Options)> { 47 | fn arg(self) -> cmd::Arg { 48 | let Args((val, options)) = self; 49 | val.arg().with_opts(options) 50 | } 51 | } 52 | 53 | impl Arg for Args<(T, Options)> 54 | where 55 | T: Serialize, 56 | { 57 | fn arg(self) -> cmd::Arg { 58 | let Args((val, options)) = self; 59 | Command::from_json(val).arg().with_opts(options) 60 | } 61 | } 62 | 63 | #[cfg(test)] 64 | mod tests { 65 | use crate::{cmd, r}; 66 | use serde::Serialize; 67 | 68 | #[derive(Serialize)] 69 | struct Document<'a> { 70 | item: &'a str, 71 | } 72 | 73 | #[test] 74 | fn r_table_insert() { 75 | let query = r.table("foo").insert(Document { item: "bar" }); 76 | let serialised = cmd::serialise(&query); 77 | let expected = r#"[56,[[15,["foo"]],{"item":"bar"}]]"#; 78 | assert_eq!(serialised, expected); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /examples/reql/closing-spawned-feed.rs: -------------------------------------------------------------------------------- 1 | use futures::{StreamExt, TryStreamExt}; 2 | use futures_timer::Delay; 3 | use reql::r; 4 | use serde_json::Value; 5 | use std::time::Duration; 6 | use tracing::error; 7 | 8 | // We are using `tokio` here as an example but you can use this crate 9 | // with any runtime 10 | #[tokio::main] 11 | async fn main() -> reql::Result<()> { 12 | // Initialise the logger if you need to debug this crate 13 | tracing_subscriber::fmt::init(); 14 | 15 | // Connect to create a RethinkDB session 16 | let session = r.connect(()).await?; 17 | 18 | // Manually get a connection from the session 19 | // Usually, this is not necessary (see other examples) but in this 20 | // case we need a handle to the connection so we can call close on 21 | // it later 22 | let mut connection = session.connection()?; 23 | 24 | // Clone the connection to get an instance to use for our feed below 25 | let conn = connection.clone(); 26 | 27 | // Spawn the changefeed to run it in the background 28 | let feed_handle = tokio::spawn(async { 29 | // Create the query you want to run 30 | // The query returns a `Stream` of responses from RethinkDB 31 | let mut query = r.db("rethinkdb").table("jobs").changes(()).run(conn); 32 | 33 | // Execute the query and handle the result 34 | while let Some(change) = query.next().await { 35 | match change { 36 | // We are going to continue printing jobs until the feed is closed 37 | Ok(change) => { 38 | if let Err(msg) = print_json(change) { 39 | error!("failed to parse response; error: {}", msg); 40 | } 41 | } 42 | Err(msg) => error!("feed returned an error: {}", msg), 43 | } 44 | } 45 | }); 46 | 47 | // Delay a bit to let the feed run before closing it 48 | Delay::new(Duration::from_secs(2)).await; 49 | 50 | // and then close the changefeed 51 | connection.close(()).await?; 52 | 53 | // Wait for the feed to make sure it has finished running 54 | // This shouldn't block because we have closed the feed above 55 | let _ = feed_handle.await; 56 | 57 | // We can now use the same connection to run more queries 58 | // We wouldn't be able to do this otherwise since this driver 59 | // returns an error if you try to run more queries on a 60 | // connection that is running a changefeed 61 | let mut query = r.db("rethinkdb").table("server_status").run(connection); 62 | 63 | // Execute the query and print the result 64 | if let Some(server_status) = query.try_next().await? { 65 | print_json(server_status)?; 66 | } 67 | 68 | Ok(()) 69 | } 70 | 71 | // We are just going to print the JSON response for this example 72 | fn print_json(json: Value) -> reql::Result<()> { 73 | println!("{}", serde_json::to_string(&json)?); 74 | Ok(()) 75 | } 76 | -------------------------------------------------------------------------------- /macros/src/func/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests; 3 | 4 | use proc_macro2::{TokenStream, TokenTree}; 5 | use quote::quote; 6 | use std::iter::FromIterator; 7 | use syn::Ident; 8 | 9 | #[derive(Debug)] 10 | pub(super) struct Func { 11 | mv: Option, 12 | args: Vec, 13 | body: TokenStream, 14 | } 15 | 16 | impl Func { 17 | pub(super) fn new(input: TokenStream) -> Self { 18 | let mut iter = input.into_iter(); 19 | let mv = iter.next().map(first).unwrap(); 20 | if mv.is_some() { 21 | iter.next().filter(is_pipe).unwrap(); 22 | } 23 | let mut args = Vec::new(); 24 | while let Some(token) = iter.next() { 25 | if is_pipe(&token) { 26 | break; 27 | } 28 | args.push(ident(token)); 29 | let token = iter.next().unwrap(); 30 | if is_pipe(&token) { 31 | break; 32 | } 33 | assert_comma(&token); 34 | } 35 | let body = TokenStream::from_iter(iter); 36 | Self { mv, args, body } 37 | } 38 | 39 | pub(super) fn process(self) -> TokenStream { 40 | let Self { mv, args, body } = self; 41 | let mut header = quote!(#mv |); 42 | let mut params = TokenStream::new(); 43 | let func_args = args.len(); 44 | for (i, arg) in args.into_iter().enumerate() { 45 | let var = quote!(reql::Command::var(*ids.get(#i).unwrap())); 46 | if i == func_args - 1 { 47 | header.extend(quote!(#arg: reql::Command)); 48 | params.extend(quote!(#var)); 49 | } else { 50 | header.extend(quote!(#arg: reql::Command, )); 51 | params.extend(quote!(#var, )); 52 | } 53 | } 54 | header.extend(quote!(|)); 55 | let closure = quote!(#header #body); 56 | quote!({ 57 | let closure = #closure; 58 | let mut ids = Vec::with_capacity(#func_args); 59 | for _ in 0..#func_args { 60 | let id = reql::var_counter(); 61 | ids.push(id); 62 | } 63 | let func = closure(#params); 64 | reql::Func::new(ids, func) 65 | }) 66 | } 67 | } 68 | 69 | fn first(token: TokenTree) -> Option { 70 | if is_pipe(&token) { 71 | return None; 72 | } 73 | match token { 74 | TokenTree::Ident(ident) if ident == "move" => Some(ident), 75 | _ => panic!("invalid closure"), 76 | } 77 | } 78 | 79 | fn ident(token: TokenTree) -> Ident { 80 | match token { 81 | TokenTree::Ident(ident) => ident, 82 | _ => panic!("invalid closure"), 83 | } 84 | } 85 | 86 | fn is_pipe(token: &TokenTree) -> bool { 87 | matches!(token, TokenTree::Punct(punct) if punct.as_char() == '|') 88 | } 89 | 90 | fn assert_comma(token: &TokenTree) { 91 | match token { 92 | TokenTree::Punct(punct) if punct.as_char() == ',' => {} 93 | _ => panic!("invalid closure"), 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /reql/src/cmd/table_create.rs: -------------------------------------------------------------------------------- 1 | use super::args::Args; 2 | use super::Durability; 3 | use crate::{cmd, Command}; 4 | use ql2::term::TermType; 5 | use reql_macros::CommandOptions; 6 | use serde::{Serialize, Serializer}; 7 | use std::borrow::Cow; 8 | use std::collections::HashMap; 9 | 10 | #[derive(Debug, Clone, CommandOptions, Default, PartialEq)] 11 | #[non_exhaustive] 12 | pub struct Options { 13 | pub primary_key: Option>, 14 | pub durability: Option, 15 | pub shards: Option, 16 | pub replicas: Option, 17 | } 18 | 19 | #[derive(Debug, Clone, PartialEq)] 20 | #[non_exhaustive] 21 | pub enum Replicas { 22 | Int(u8), 23 | Map { 24 | replicas: HashMap, u8>, 25 | primary_replica_tag: Cow<'static, str>, 26 | }, 27 | } 28 | 29 | impl Serialize for Options { 30 | fn serialize(&self, serializer: S) -> Result 31 | where 32 | S: Serializer, 33 | { 34 | #[derive(Serialize)] 35 | struct InnerOptions<'a> { 36 | #[serde(skip_serializing_if = "Option::is_none")] 37 | primary_key: Option<&'a Cow<'static, str>>, 38 | #[serde(skip_serializing_if = "Option::is_none")] 39 | durability: Option, 40 | #[serde(skip_serializing_if = "Option::is_none")] 41 | shards: Option, 42 | #[serde(skip_serializing_if = "Option::is_none")] 43 | replicas: Option>, 44 | #[serde(skip_serializing_if = "Option::is_none")] 45 | primary_replica_tag: Option<&'a Cow<'static, str>>, 46 | } 47 | 48 | #[derive(Serialize)] 49 | #[serde(untagged)] 50 | enum InnerReplicas<'a> { 51 | Int(u8), 52 | Map(&'a HashMap, u8>), 53 | } 54 | 55 | let (replicas, primary_replica_tag) = match &self.replicas { 56 | Some(Replicas::Int(i)) => (Some(InnerReplicas::Int(*i)), None), 57 | Some(Replicas::Map { 58 | replicas, 59 | primary_replica_tag, 60 | }) => ( 61 | Some(InnerReplicas::Map(replicas)), 62 | Some(primary_replica_tag), 63 | ), 64 | None => (None, None), 65 | }; 66 | 67 | let opts = InnerOptions { 68 | replicas, 69 | primary_replica_tag, 70 | primary_key: self.primary_key.as_ref(), 71 | durability: self.durability, 72 | shards: self.shards, 73 | }; 74 | 75 | opts.serialize(serializer) 76 | } 77 | } 78 | 79 | pub trait Arg { 80 | fn arg(self) -> cmd::Arg; 81 | } 82 | 83 | impl Arg for Command { 84 | fn arg(self) -> cmd::Arg { 85 | Self::new(TermType::TableCreate).with_arg(self).into_arg() 86 | } 87 | } 88 | 89 | impl Arg for Args<(Command, Options)> { 90 | fn arg(self) -> cmd::Arg { 91 | let Args((query, options)) = self; 92 | query.arg().with_opts(options) 93 | } 94 | } 95 | 96 | impl Arg for T 97 | where 98 | T: Into, 99 | { 100 | fn arg(self) -> cmd::Arg { 101 | Command::from_json(self.into()).arg() 102 | } 103 | } 104 | 105 | impl Arg for Args<(T, Options)> 106 | where 107 | T: Into, 108 | { 109 | fn arg(self) -> cmd::Arg { 110 | let Args((name, options)) = self; 111 | name.arg().with_opts(options) 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /macros/src/options.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | use quote::{quote, ToTokens}; 3 | use syn::{parse_macro_input, Data, DeriveInput, Fields, GenericArgument, PathArguments, Type}; 4 | 5 | pub(super) fn parse(input: proc_macro::TokenStream) -> proc_macro::TokenStream { 6 | let input = parse_macro_input!(input as DeriveInput); 7 | 8 | let mut methods = TokenStream::new(); 9 | 10 | match input.data { 11 | Data::Struct(data) => match data.fields { 12 | Fields::Named(fields) => { 13 | for field in fields.named { 14 | let name = field.ident; 15 | let mut generics = TokenStream::new(); 16 | let mut where_clause = TokenStream::new(); 17 | let mut value = quote!(#name); 18 | let mut param = param(&field.ty); 19 | 20 | if param.is_cow || param.is_db { 21 | generics = quote!(); 22 | where_clause = quote!(where T: crate::cmd::StaticString); 23 | param.ty = quote!(T); 24 | value = quote!(#name.static_string()); 25 | } 26 | 27 | if param.is_db { 28 | value = quote!(Db(#value)) 29 | } 30 | 31 | if param.is_option { 32 | value = quote!(Some(#value)) 33 | } 34 | 35 | let ty = param.ty; 36 | 37 | methods.extend(quote! { 38 | pub fn #name #generics(mut self, #name: #ty) -> Self #where_clause { 39 | self.#name = #value; 40 | self 41 | } 42 | }); 43 | } 44 | } 45 | _ => unimplemented!(), 46 | }, 47 | Data::Enum(_) | Data::Union(_) => unimplemented!(), 48 | } 49 | 50 | let name = input.ident; 51 | let generics = input.generics; 52 | 53 | let options = quote! { 54 | impl #generics #name #generics { 55 | pub fn new() -> Self { 56 | Default::default() 57 | } 58 | 59 | #methods 60 | } 61 | }; 62 | 63 | options.into() 64 | } 65 | 66 | struct Param { 67 | ty: TokenStream, 68 | is_option: bool, 69 | is_cow: bool, 70 | is_db: bool, 71 | } 72 | 73 | fn param(typ: &Type) -> Param { 74 | if let Type::Path(typ) = typ { 75 | if let Some(typ) = typ.path.segments.first() { 76 | let mut param = Param { 77 | ty: typ.to_token_stream(), 78 | is_option: typ.ident == "Option", 79 | is_cow: typ.ident == "Cow", 80 | is_db: false, 81 | }; 82 | if !param.is_option { 83 | return param; 84 | } else if let PathArguments::AngleBracketed(path) = &typ.arguments { 85 | if let Some(typ) = path.args.first() { 86 | param.ty = typ.to_token_stream(); 87 | if let GenericArgument::Type(Type::Path(typ)) = typ { 88 | if let Some(typ) = typ.path.segments.first() { 89 | param.is_cow = typ.ident == "Cow"; 90 | param.is_db = typ.ident == "Db"; 91 | } 92 | } 93 | return param; 94 | } 95 | } 96 | } 97 | } 98 | unimplemented!() 99 | } 100 | -------------------------------------------------------------------------------- /reql/src/err.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | use std::{error, fmt, io}; 3 | 4 | /// The most generic error message in ReQL 5 | #[derive(Debug, Clone)] 6 | pub enum Error { 7 | Compile(String), 8 | Runtime(Runtime), 9 | Driver(Driver), 10 | } 11 | 12 | impl error::Error for Error {} 13 | 14 | impl fmt::Display for Error { 15 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 16 | match self { 17 | Self::Compile(msg) => write!(f, "compile error; {}", msg), 18 | Self::Runtime(msg) => write!(f, "runtime error; {}", msg), 19 | Self::Driver(msg) => write!(f, "client error; {}", msg), 20 | } 21 | } 22 | } 23 | 24 | /// The parent class of all runtime errors 25 | /// 26 | /// All errors on the server unrelated to compilation. Programs may use this to catch any runtime 27 | /// error, but the server will always return a more specific error class. 28 | #[derive(Debug, Clone)] 29 | pub enum Runtime { 30 | /// The query contains a logical impossibility, such as adding a number to a string. 31 | QueryLogic(String), 32 | NonExistence(String), 33 | ResourceLimit(String), 34 | User(String), 35 | Internal(String), 36 | Availability(Availability), 37 | Permission(String), 38 | } 39 | 40 | impl From for Error { 41 | fn from(err: Runtime) -> Error { 42 | Error::Runtime(err) 43 | } 44 | } 45 | 46 | impl fmt::Display for Runtime { 47 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 48 | match self { 49 | Self::QueryLogic(msg) => write!(f, "query logic; {}", msg), 50 | Self::NonExistence(msg) => write!(f, "non-existence error; {}", msg), 51 | Self::ResourceLimit(msg) => write!(f, "resource limit error; {}", msg), 52 | Self::User(msg) => write!(f, "user error; {}", msg), 53 | Self::Internal(msg) => write!(f, "internal error; {}", msg), 54 | Self::Availability(msg) => write!(f, "availability error; {}", msg), 55 | Self::Permission(msg) => write!(f, "permission error; {}", msg), 56 | } 57 | } 58 | } 59 | 60 | /// A server in the cluster is unavailable 61 | /// 62 | /// The parent class of `OpFailedError` and `OpIndeterminateError`. Programs may use this 63 | /// to catch any availability error, but the server will always return one of this class’s 64 | /// children. 65 | #[derive(Debug, Clone)] 66 | pub enum Availability { 67 | OpFailed(String), 68 | OpIndeterminate(String), 69 | } 70 | 71 | impl From for Error { 72 | fn from(err: Availability) -> Error { 73 | Runtime::Availability(err).into() 74 | } 75 | } 76 | 77 | impl fmt::Display for Availability { 78 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 79 | match self { 80 | Self::OpFailed(msg) => write!(f, "operation failed; {}", msg), 81 | Self::OpIndeterminate(msg) => write!(f, "operation indeterminate; {}", msg), 82 | } 83 | } 84 | } 85 | 86 | /// An error has occurred within the driver 87 | /// 88 | /// This may be a driver bug, or it may be an unfulfillable command, such as an unserializable 89 | /// query. 90 | #[derive(Debug, Clone)] 91 | #[non_exhaustive] 92 | pub enum Driver { 93 | Auth(String), 94 | ConnectionBroken, 95 | ConnectionLocked, 96 | Io(io::ErrorKind, String), 97 | Json(Arc), 98 | Other(String), 99 | } 100 | 101 | impl From for Error { 102 | fn from(err: Driver) -> Error { 103 | Error::Driver(err) 104 | } 105 | } 106 | 107 | impl fmt::Display for Driver { 108 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 109 | match self { 110 | Self::Auth(msg) => write!(f, "auth error; {}", msg), 111 | Self::ConnectionBroken => write!(f, "connection broken"), 112 | Self::ConnectionLocked => write!( 113 | f, 114 | "another query is running a changefeed on this connection" 115 | ), 116 | Self::Io(_, error) => write!(f, "{}", error), 117 | Self::Json(error) => write!(f, "{}", error), 118 | Self::Other(msg) => write!(f, "{}", msg), 119 | } 120 | } 121 | } 122 | 123 | impl From for Error { 124 | fn from(err: io::Error) -> Error { 125 | Driver::Io(err.kind(), err.to_string()).into() 126 | } 127 | } 128 | 129 | impl From for Error { 130 | fn from(err: serde_json::Error) -> Error { 131 | Driver::Json(Arc::new(err)).into() 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /reql/src/cmd/do_.rs: -------------------------------------------------------------------------------- 1 | use super::args::Args; 2 | use crate::{cmd, Command, Func}; 3 | use ql2::term::TermType; 4 | use serde::Serialize; 5 | 6 | pub trait Arg { 7 | fn arg(self, parent: Option) -> cmd::Arg<()>; 8 | } 9 | 10 | impl Arg for Command { 11 | fn arg(self, parent: Option) -> cmd::Arg<()> { 12 | let cmd = Command::new(TermType::Funcall).with_arg(self); 13 | match parent { 14 | Some(parent) => cmd.with_arg(parent).into_arg(), 15 | None => cmd.into_arg(), 16 | } 17 | } 18 | } 19 | 20 | impl Arg for T 21 | where 22 | T: Serialize, 23 | { 24 | fn arg(self, parent: Option) -> cmd::Arg<()> { 25 | Command::from_json(self).arg(parent) 26 | } 27 | } 28 | 29 | impl Arg for Args<(Command, Command)> { 30 | fn arg(self, parent: Option) -> cmd::Arg<()> { 31 | let Args((arg, expr)) = self; 32 | expr.arg(parent).with_arg(arg) 33 | } 34 | } 35 | 36 | #[allow(array_into_iter)] 37 | #[allow(clippy::into_iter_on_ref)] 38 | impl Arg for Args<([Command; N], Command)> { 39 | fn arg(self, parent: Option) -> cmd::Arg<()> { 40 | let Args((args, expr)) = self; 41 | let mut cmd = expr.arg(parent); 42 | for arg in args.into_iter().cloned() { 43 | cmd = cmd.with_arg(arg); 44 | } 45 | cmd 46 | } 47 | } 48 | 49 | #[allow(array_into_iter)] 50 | #[allow(clippy::into_iter_on_ref)] 51 | impl Arg for Args<([T; N], Command)> 52 | where 53 | T: Serialize + Clone, 54 | { 55 | fn arg(self, parent: Option) -> cmd::Arg<()> { 56 | let Args((args, expr)) = self; 57 | let mut cmd = expr.arg(parent); 58 | for arg in args.into_iter().cloned() { 59 | let arg = Command::from_json(arg); 60 | cmd = cmd.with_arg(arg); 61 | } 62 | cmd 63 | } 64 | } 65 | 66 | impl Arg for Args<(Command, Func)> { 67 | fn arg(self, parent: Option) -> cmd::Arg<()> { 68 | let Args((arg, Func(func))) = self; 69 | func.arg(parent).with_arg(arg) 70 | } 71 | } 72 | 73 | #[allow(array_into_iter)] 74 | #[allow(clippy::into_iter_on_ref)] 75 | impl Arg for Args<([Command; N], Func)> { 76 | fn arg(self, parent: Option) -> cmd::Arg<()> { 77 | let Args((args, Func(func))) = self; 78 | let mut cmd = func.arg(parent); 79 | for arg in args.into_iter().cloned() { 80 | cmd = cmd.with_arg(arg); 81 | } 82 | cmd 83 | } 84 | } 85 | 86 | #[allow(array_into_iter)] 87 | #[allow(clippy::into_iter_on_ref)] 88 | impl Arg for Args<([T; N], Func)> 89 | where 90 | T: Serialize + Clone, 91 | { 92 | fn arg(self, parent: Option) -> cmd::Arg<()> { 93 | let Args((args, Func(func))) = self; 94 | let mut cmd = func.arg(parent); 95 | for arg in args.into_iter().cloned() { 96 | let arg = Command::from_json(arg); 97 | cmd = cmd.with_arg(arg); 98 | } 99 | cmd 100 | } 101 | } 102 | 103 | impl Arg for Func { 104 | fn arg(self, parent: Option) -> cmd::Arg<()> { 105 | let Func(func) = self; 106 | func.arg(parent) 107 | } 108 | } 109 | 110 | #[cfg(test)] 111 | mod tests { 112 | use crate::{self as reql, cmd, func, r}; 113 | 114 | #[test] 115 | fn r_do() { 116 | let counter = crate::current_counter(); 117 | let query = r.do_(r.args(([10, 20], func!(|x, y| x + y)))); 118 | let serialised = cmd::serialise(&query); 119 | let expected = format!( 120 | r#"[64,[[69,[[2,[2,3]],[24,[[10,[{}]],[10,[{}]]]]]],10,20]]"#, 121 | counter, 122 | counter + 1 123 | ); 124 | assert_eq!(serialised, expected); 125 | } 126 | 127 | #[test] 128 | fn r_db_table_get_do() { 129 | let counter = crate::current_counter(); 130 | let query = r 131 | .db("mydb") 132 | .table("table1") 133 | .get("johndoe@example.com") 134 | .do_(func!(|doc| r 135 | .db("mydb") 136 | .table("table2") 137 | .get(doc.get_field("id")))); 138 | let serialised = cmd::serialise(&query); 139 | let expected = format!( 140 | r#"[64,[[69,[[2,[1]],[16,[[15,[[14,["mydb"]],"table2"]],[31,[[10,[{}]],"id"]]]]]],[16,[[15,[[14,["mydb"]],"table1"]],"johndoe@example.com"]]]]"#, 141 | counter 142 | ); 143 | assert_eq!(serialised, expected); 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /types/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Common ReQL data types 2 | 3 | mod date_time; 4 | 5 | use serde::{Deserialize, Serialize}; 6 | use serde_json::Value; 7 | use std::collections::HashMap; 8 | use std::net::IpAddr; 9 | use time::OffsetDateTime; 10 | use uuid::Uuid; 11 | 12 | #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] 13 | pub struct DateTime(OffsetDateTime); 14 | 15 | impl From for DateTime { 16 | fn from(dt: OffsetDateTime) -> Self { 17 | Self(dt) 18 | } 19 | } 20 | 21 | impl From for OffsetDateTime { 22 | fn from(DateTime(dt): DateTime) -> Self { 23 | dt 24 | } 25 | } 26 | 27 | /// Status returned by a write command 28 | #[derive(Debug, Clone, Deserialize, Serialize)] 29 | #[non_exhaustive] 30 | pub struct WriteStatus { 31 | pub inserted: u32, 32 | pub replaced: u32, 33 | pub unchanged: u32, 34 | pub skipped: u32, 35 | pub deleted: u32, 36 | pub errors: u32, 37 | pub first_error: Option, 38 | pub generated_keys: Option>, 39 | pub warnings: Option>, 40 | pub changes: Option>>, 41 | } 42 | 43 | /// Structure of data in `cluster_config` table 44 | #[derive(Debug, Clone, Deserialize, Serialize, Eq, PartialEq, Ord, PartialOrd, Hash)] 45 | #[non_exhaustive] 46 | pub struct ClusterConfig { 47 | pub id: String, 48 | pub heartbeat_timeout_secs: u32, 49 | } 50 | 51 | /// Structure of data in `current_issues` table 52 | #[derive(Debug, Clone, Deserialize, Serialize, Eq, PartialEq, Ord, PartialOrd, Hash)] 53 | #[non_exhaustive] 54 | pub struct CurrentIssue {} 55 | 56 | /// Structure of data in `db_config` table 57 | #[derive(Debug, Clone, Deserialize, Serialize, Eq, PartialEq, Ord, PartialOrd, Hash)] 58 | #[non_exhaustive] 59 | pub struct DbConfig {} 60 | 61 | /// Structure of data in `jobs` table 62 | #[derive(Debug, Clone, Deserialize, Serialize, Eq, PartialEq, Ord, PartialOrd, Hash)] 63 | #[non_exhaustive] 64 | pub struct Job {} 65 | 66 | /// Structure of data in `logs` table 67 | #[derive(Debug, Clone, Deserialize, Serialize, Eq, PartialEq, Ord, PartialOrd, Hash)] 68 | #[non_exhaustive] 69 | pub struct Log {} 70 | 71 | /// Structure of data in `permissions` table 72 | #[derive(Debug, Clone, Deserialize, Serialize, Eq, PartialEq, Ord, PartialOrd, Hash)] 73 | #[non_exhaustive] 74 | pub struct Permission {} 75 | 76 | /// Structure of data in `server_config` table 77 | #[derive(Debug, Clone, Deserialize, Serialize, Eq, PartialEq, Ord, PartialOrd, Hash)] 78 | #[non_exhaustive] 79 | pub struct ServerConfig {} 80 | 81 | #[derive(Debug, Clone, Deserialize, Serialize, Eq, PartialEq, Ord, PartialOrd, Hash)] 82 | #[non_exhaustive] 83 | pub struct CanonicalAddress { 84 | pub host: IpAddr, 85 | pub port: u16, 86 | } 87 | 88 | #[derive(Debug, Clone, Deserialize, Serialize)] 89 | #[non_exhaustive] 90 | pub struct Network { 91 | pub canonical_addresses: Vec, 92 | pub cluster_port: u16, 93 | pub connected_to: HashMap, 94 | pub hostname: String, 95 | pub http_admin_port: u16, 96 | pub reql_port: u16, 97 | pub time_connected: DateTime, 98 | } 99 | 100 | #[derive(Debug, Clone, Deserialize, Serialize)] 101 | #[non_exhaustive] 102 | pub struct Process { 103 | pub argv: Vec, 104 | pub cache_size_mb: f64, 105 | pub pid: u64, 106 | pub time_started: DateTime, 107 | pub version: String, 108 | } 109 | 110 | /// Structure of data in `server_status` table 111 | #[derive(Debug, Clone, Deserialize, Serialize)] 112 | #[non_exhaustive] 113 | pub struct ServerStatus { 114 | pub id: Uuid, 115 | pub name: String, 116 | pub network: Network, 117 | pub process: Process, 118 | } 119 | 120 | /// Structure of data in `cluster_config` table 121 | #[derive(Debug, Clone, Deserialize, Serialize, Eq, PartialEq, Ord, PartialOrd, Hash)] 122 | #[non_exhaustive] 123 | pub struct Stat {} 124 | 125 | /// Structure of data in `cluster_config` table 126 | #[derive(Debug, Clone, Deserialize, Serialize, Eq, PartialEq, Ord, PartialOrd, Hash)] 127 | #[non_exhaustive] 128 | pub struct TableConfig {} 129 | 130 | /// Structure of data in `table_status` table 131 | #[derive(Debug, Clone, Deserialize, Serialize, Eq, PartialEq, Ord, PartialOrd, Hash)] 132 | #[non_exhaustive] 133 | pub struct TableStatus {} 134 | 135 | /// Structure of data in `uses` table 136 | #[derive(Debug, Clone, Deserialize, Serialize, Eq, PartialEq, Ord, PartialOrd, Hash)] 137 | #[non_exhaustive] 138 | pub struct User {} 139 | 140 | #[derive(Debug, Clone, Deserialize, Serialize, Eq, PartialEq, Ord, PartialOrd, Hash)] 141 | #[non_exhaustive] 142 | pub struct Change { 143 | pub old_val: Option, 144 | pub new_val: Option, 145 | #[serde(rename = "type")] 146 | pub result_type: Option, 147 | pub old_offset: Option, 148 | pub new_offset: Option, 149 | pub state: Option, 150 | } 151 | 152 | #[derive(Debug, Clone, Deserialize, Serialize, Eq, PartialEq, Ord, PartialOrd, Hash)] 153 | #[non_exhaustive] 154 | pub struct ServerInfo { 155 | pub id: Uuid, 156 | pub proxy: bool, 157 | pub name: Option, 158 | } 159 | 160 | #[derive(Debug, Serialize, Deserialize)] 161 | #[allow(clippy::upper_case_acronyms)] 162 | struct BINARY; 163 | 164 | #[derive(Debug, Serialize, Deserialize)] 165 | pub struct Binary { 166 | #[serde(rename = "$reql_type$")] 167 | reql_type: BINARY, 168 | pub data: String, 169 | } 170 | 171 | impl Binary { 172 | pub fn new(bytes: &[u8]) -> Self { 173 | Self { 174 | reql_type: BINARY, 175 | data: base64::encode(bytes), 176 | } 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /types/src/date_time.rs: -------------------------------------------------------------------------------- 1 | use crate::DateTime; 2 | use serde::{de, ser}; 3 | use serde::{Deserialize, Deserializer, Serialize, Serializer}; 4 | use std::ops::Deref; 5 | use time::{format_description, OffsetDateTime, UtcOffset}; 6 | 7 | const NANOS_PER_SEC: i128 = 1_000_000_000; 8 | const NANOS_PER_MSEC: i128 = 1_000_000; 9 | 10 | #[derive(Debug, Serialize, Deserialize)] 11 | #[non_exhaustive] 12 | struct Time { 13 | #[serde(rename = "$reql_type$")] 14 | reql_type: String, 15 | #[serde(with = "epoch_time")] 16 | epoch_time: String, 17 | timezone: String, 18 | } 19 | 20 | impl<'de> Deserialize<'de> for DateTime { 21 | fn deserialize(deserializer: D) -> Result 22 | where 23 | D: Deserializer<'de>, 24 | { 25 | let time = Time::deserialize(deserializer)?; 26 | let format = match format_description::parse("[offset_hour]:[offset_minute]") { 27 | Ok(fmt) => fmt, 28 | Err(error) => { 29 | return Err(de::Error::custom(error)); 30 | } 31 | }; 32 | let offset = match UtcOffset::parse(&time.timezone, &format) { 33 | Ok(offset) => offset, 34 | Err(error) => { 35 | return Err(de::Error::custom(error)); 36 | } 37 | }; 38 | let (secs, msecs) = match time.epoch_time.split_once('.') { 39 | Some(parts) => parts, 40 | None => (time.epoch_time.as_str(), "0"), 41 | }; 42 | let secs = match secs.parse::() { 43 | Ok(secs) => match secs.checked_mul(NANOS_PER_SEC) { 44 | Some(secs) => secs, 45 | None => { 46 | return Err(de::Error::custom("seconds to nanosecond overflow")); 47 | } 48 | }, 49 | Err(..) => { 50 | return Err(de::Error::custom("invalid epoch time seconds")); 51 | } 52 | }; 53 | // RethinkDB timestamps have millisecond precision so we need 54 | // to convert the milliseconds to nanoseconds first 55 | let msecs = match msecs.parse::() { 56 | Ok(int) => { 57 | let msecs = match msecs.len() { 58 | 3 => int, 59 | 2 => int * 10, 60 | 1 => int * 100, 61 | _ => { 62 | return Err(de::Error::custom("invalid epoch milliseconds")); 63 | } 64 | }; 65 | match msecs.checked_mul(NANOS_PER_MSEC) { 66 | Some(msecs) => msecs, 67 | None => { 68 | return Err(de::Error::custom("millisecond to nanosecond overflow")); 69 | } 70 | } 71 | } 72 | Err(..) => { 73 | return Err(de::Error::custom("invalid epoch time milliseconds")); 74 | } 75 | }; 76 | let timestamp = match secs.checked_add(msecs) { 77 | Some(timestamp) => timestamp, 78 | None => { 79 | return Err(de::Error::custom("timestamp addition overflow")); 80 | } 81 | }; 82 | let dt = match OffsetDateTime::from_unix_timestamp_nanos(timestamp) { 83 | Ok(date_time) => date_time.to_offset(offset), 84 | Err(error) => { 85 | return Err(de::Error::custom(error)); 86 | } 87 | }; 88 | Ok(DateTime(dt)) 89 | } 90 | } 91 | 92 | impl Serialize for DateTime { 93 | fn serialize(&self, serializer: S) -> Result 94 | where 95 | S: Serializer, 96 | { 97 | let dt = &self.0; 98 | let offset = dt.offset(); 99 | let timezone = { 100 | let (hours, minutes, _) = offset.as_hms(); 101 | format!( 102 | "{}{:02}:{:02}", 103 | if offset.is_negative() { '-' } else { '+' }, 104 | hours.abs(), 105 | minutes.abs(), 106 | ) 107 | }; 108 | let time = Time { 109 | reql_type: "TIME".to_owned(), 110 | epoch_time: format!("{}.{:03}", dt.unix_timestamp(), dt.millisecond()), 111 | timezone, 112 | }; 113 | time.serialize(serializer) 114 | } 115 | } 116 | 117 | impl Deref for DateTime { 118 | type Target = OffsetDateTime; 119 | 120 | fn deref(&self) -> &Self::Target { 121 | &self.0 122 | } 123 | } 124 | 125 | mod epoch_time { 126 | use super::*; 127 | 128 | pub fn serialize(epoch_time: &str, serializer: S) -> Result 129 | where 130 | S: Serializer, 131 | { 132 | match epoch_time.parse::() { 133 | Ok(timestamp) => serializer.serialize_f64(timestamp), 134 | Err(..) => Err(ser::Error::custom("invalid epoch timestamp")), 135 | } 136 | } 137 | 138 | pub fn deserialize<'de, D>(deserializer: D) -> Result 139 | where 140 | D: Deserializer<'de>, 141 | { 142 | let timestamp = f64::deserialize(deserializer)?; 143 | Ok(timestamp.to_string()) 144 | } 145 | } 146 | 147 | #[cfg(test)] 148 | mod test { 149 | use super::*; 150 | use time::macros::datetime; 151 | 152 | #[test] 153 | fn with_milliseconds() { 154 | let dt = DateTime(datetime!(2042-10-28 17:53:47.060 +1:30)); 155 | let serialized = serde_json::to_string(&dt).unwrap(); 156 | let parsed = serde_json::from_str(&serialized).unwrap(); 157 | assert_eq!(dt, parsed); 158 | } 159 | 160 | #[test] 161 | fn with_seconds() { 162 | let dt = DateTime(datetime!(2042-10-28 17:53:47 +1:30)); 163 | let serialized = serde_json::to_string(&dt).unwrap(); 164 | let parsed = serde_json::from_str(&serialized).unwrap(); 165 | assert_eq!(dt, parsed); 166 | } 167 | 168 | #[test] 169 | fn with_minutes() { 170 | let dt = DateTime(datetime!(2042-10-28 17:53 +1:30)); 171 | let serialized = serde_json::to_string(&dt).unwrap(); 172 | let parsed = serde_json::from_str(&serialized).unwrap(); 173 | assert_eq!(dt, parsed); 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /mobc/src/lib.rs: -------------------------------------------------------------------------------- 1 | use blocking::unblock; 2 | use futures::lock::Mutex; 3 | use futures::{Future, TryStreamExt}; 4 | use futures_timer::Delay; 5 | use mobc::{async_trait, Manager}; 6 | use reql::cmd::connect::Options; 7 | use reql::cmd::run::{self, Arg}; 8 | use reql::types::{Change, ServerStatus}; 9 | use reql::{r, Command, Connection, Driver, Error, Result}; 10 | use std::cmp::Ordering; 11 | use std::io; 12 | use std::net::{IpAddr, TcpStream}; 13 | use std::ops::Deref; 14 | use std::sync::Arc; 15 | use std::time::{Duration, Instant}; 16 | use tracing::trace; 17 | 18 | pub type Pool = mobc::Pool; 19 | 20 | #[async_trait] 21 | pub trait GetSession { 22 | async fn session(&self) -> Result; 23 | } 24 | 25 | #[async_trait] 26 | impl GetSession for Pool { 27 | async fn session(&self) -> Result { 28 | Ok(Session { 29 | conn: self.get().await.map_err(to_reql)?, 30 | }) 31 | } 32 | } 33 | 34 | pub struct Session { 35 | conn: mobc::Connection, 36 | } 37 | 38 | impl Deref for Session { 39 | type Target = reql::Session; 40 | 41 | fn deref(&self) -> &Self::Target { 42 | &self.conn 43 | } 44 | } 45 | 46 | impl AsRef for Session { 47 | fn as_ref(&self) -> &reql::Session { 48 | self.deref() 49 | } 50 | } 51 | 52 | impl Arg for &Session { 53 | fn into_run_opts(self) -> Result<(Connection, run::Options)> { 54 | self.deref().into_run_opts() 55 | } 56 | } 57 | 58 | #[derive(Debug, Clone, Eq)] 59 | struct Server { 60 | name: String, 61 | addresses: Vec, 62 | port: u16, 63 | latency: Duration, 64 | } 65 | 66 | impl Ord for Server { 67 | fn cmp(&self, other: &Server) -> Ordering { 68 | self.latency.cmp(&other.latency) 69 | } 70 | } 71 | 72 | impl PartialOrd for Server { 73 | fn partial_cmp(&self, other: &Server) -> Option { 74 | Some(self.cmp(other)) 75 | } 76 | } 77 | 78 | impl PartialEq for Server { 79 | fn eq(&self, other: &Server) -> bool { 80 | self.name == other.name 81 | } 82 | } 83 | 84 | impl Server { 85 | fn from_status(status: ServerStatus) -> Self { 86 | let network = status.network; 87 | let addresses = network.canonical_addresses.into_iter().map(move |x| x.host); 88 | Self { 89 | name: status.name, 90 | addresses: addresses.collect(), 91 | port: network.reql_port, 92 | latency: Duration::from_millis(u64::MAX), 93 | } 94 | } 95 | } 96 | 97 | #[derive(Clone)] 98 | pub struct SessionManager { 99 | opts: Options, 100 | servers: Arc>>, 101 | pool: Option, 102 | } 103 | 104 | impl SessionManager { 105 | pub fn new(opts: Options) -> Self { 106 | Self { 107 | opts, 108 | servers: Arc::new(Mutex::new(Vec::new())), 109 | pool: None, 110 | } 111 | } 112 | 113 | pub fn discover_hosts(&self) -> impl Future { 114 | let mut manager = self.clone(); 115 | manager.pool = Some(Pool::builder().max_open(2).build(self.clone())); 116 | async move { 117 | let mut wait = 0; 118 | loop { 119 | if let Err(error) = manager.listen_for_hosts(&mut wait).await { 120 | trace!( 121 | "listening for host changes; error: {}, wait: {}s", 122 | error, 123 | wait 124 | ); 125 | Delay::new(Duration::from_secs(wait)).await; 126 | wait = 300.min(wait + 1); 127 | } 128 | } 129 | } 130 | } 131 | 132 | async fn listen_for_hosts(&self, wait: &mut u64) -> Result<()> { 133 | let conn = self.pool.as_ref().unwrap().session().await?; 134 | let mut query = server_status() 135 | .changes(()) 136 | .run::<_, Change>(&conn); 137 | while query.try_next().await?.is_some() { 138 | let servers = self.get_servers().await?; 139 | *self.servers.lock().await = servers; 140 | *wait = 0; 141 | } 142 | Ok(()) 143 | } 144 | 145 | async fn get_servers(&self) -> Result> { 146 | let mut servers = Vec::new(); 147 | let conn = self.pool.as_ref().unwrap().session().await?; 148 | let mut query = server_status().run(&conn); 149 | while let Some(status) = query.try_next().await? { 150 | servers.push(Server::from_status(status)); 151 | } 152 | set_latency(&mut servers).await; 153 | servers.sort(); 154 | Ok(servers) 155 | } 156 | } 157 | 158 | async fn set_latency(servers: &mut Vec) { 159 | for server in servers { 160 | let port = server.port; 161 | for (i, host) in server.addresses.iter().enumerate() { 162 | let host = *host; 163 | let latency = unblock(move || { 164 | let start = Instant::now(); 165 | if TcpStream::connect((host, port)).is_ok() { 166 | return Some(start.elapsed()); 167 | } 168 | None 169 | }) 170 | .await; 171 | if let Some(latency) = latency { 172 | if latency > server.latency || i == 0 { 173 | server.latency = latency; 174 | } 175 | } 176 | } 177 | } 178 | } 179 | 180 | fn server_status() -> Command { 181 | r.db("rethinkdb").table("server_status") 182 | } 183 | 184 | #[async_trait] 185 | impl Manager for SessionManager { 186 | type Connection = reql::Session; 187 | type Error = Error; 188 | 189 | async fn connect(&self) -> Result { 190 | let opts = &self.opts; 191 | let servers = &self.servers.lock().await; 192 | if servers.is_empty() { 193 | trace!( 194 | "no discovered servers; host: {}, port: {}", 195 | opts.host, 196 | opts.port 197 | ); 198 | return r.connect(opts.clone()).await; 199 | } else { 200 | for server in servers.iter() { 201 | for host in &server.addresses { 202 | trace!( 203 | "discovered server {}; host: {}, port: {}", 204 | server.name, 205 | host, 206 | server.port 207 | ); 208 | let addr = (*host, server.port); 209 | if let Ok(conn) = r.connect(r.args((addr, opts.clone()))).await { 210 | return Ok(conn); 211 | } 212 | } 213 | } 214 | } 215 | Err(io::Error::new( 216 | io::ErrorKind::ConnectionRefused, 217 | "no RethinkDB servers available", 218 | ) 219 | .into()) 220 | } 221 | 222 | async fn check(&self, conn: Self::Connection) -> Result { 223 | let msg = 200; 224 | match r.expr(msg).run(&conn).try_next().await? { 225 | Some(res) => verify(res, msg)?, 226 | None => { 227 | return Err(Driver::ConnectionBroken.into()); 228 | } 229 | } 230 | Ok(conn) 231 | } 232 | 233 | fn validate(&self, conn: &mut Self::Connection) -> bool { 234 | !conn.is_broken() 235 | } 236 | } 237 | 238 | fn verify(res: u32, msg: u32) -> Result<()> { 239 | if res != msg { 240 | return Err(Driver::ConnectionBroken.into()); 241 | } 242 | Ok(()) 243 | } 244 | 245 | fn to_reql(error: mobc::Error) -> Error { 246 | match error { 247 | mobc::Error::Inner(error) => error, 248 | mobc::Error::Timeout => io::Error::from(io::ErrorKind::TimedOut).into(), 249 | mobc::Error::BadConn => Driver::ConnectionBroken.into(), 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /reql/src/cmd/changes.rs: -------------------------------------------------------------------------------- 1 | //! Turn a query into a changefeed, an infinite stream of objects 2 | //! representing changes to the query's results as they occur 3 | //! 4 | //! A changefeed may return changes to a table or an individual document 5 | //! (a "point" changefeed). Commands such as `filter` or `map` may be used 6 | //! before the `changes` command to transform or filter the output, and 7 | //! many commands that operate on sequences can be chained after `changes`. 8 | //! 9 | //! If the table becomes unavailable, the changefeed will be disconnected, 10 | //! and a runtime exception will be thrown by the driver. 11 | //! 12 | //! Changefeed notifications take the form of a two-field object: 13 | //! 14 | //! ```js 15 | //! { 16 | //! "old_val": , 17 | //! "new_val": 18 | //! } 19 | //! ``` 20 | //! 21 | //! When `include_types` is `true`, there will be three fields: 22 | //! 23 | //! ```js 24 | //! { 25 | //! "old_val": , 26 | //! "new_val": , 27 | //! "type": 28 | //! } 29 | //! ``` 30 | //! 31 | //! When a document is deleted, `new_val` will be `null`; when a document is 32 | //! inserted, `old_val` will be `null`. 33 | //! 34 | //! Certain document transformation commands can be chained before changefeeds. 35 | //! For more information, read the [discussion of changefeeds](https://rethinkdb.com/docs/changefeeds/) 36 | //! in the "Command language" documentation. 37 | //! 38 | //! Changefeeds ignore the `read_mode` flag to `run`, and always behave as if 39 | //! it is set to `single` (i.e., the values they return are in memory on the primary 40 | //! replica, but have not necessarily been written to disk yet). For more details 41 | //! read [Consistency guarantees](https://rethinkdb.com/docs/consistency). 42 | //! 43 | //! The server will buffer up to `changefeed_queue_size` elements (default 100,000). 44 | //! If the buffer limit is hit, early changes will be discarded, and the client will 45 | //! receive an object of the form 46 | //! `{error: "Changefeed cache over array size limit, skipped X elements."}` 47 | //! where `X` is the number of elements skipped. 48 | //! 49 | //! Commands that operate on streams (such as [filter](super::filter) or [map](super::map)) 50 | //! can usually be chained after `changes`. However, since the stream produced by 51 | //! `changes` has no ending, commands that need to consume the entire stream before 52 | //! returning (such as [reduce](super::reduce) or [count](super::count)) cannot. 53 | //! 54 | //! # Examples 55 | //! 56 | //! Subscribe to the changes on a table. 57 | //! 58 | //! Start monitoring the changefeed in one client: 59 | //! 60 | //! ``` 61 | //! # reql::example(|r, conn| async_stream::stream! { 62 | //! r.table("games").changes(()).run(conn) 63 | //! # }); 64 | //! ``` 65 | //! 66 | //! As these queries are performed in a second client 67 | //! 68 | //! ``` 69 | //! # use serde_json::json; 70 | //! # reql::example(|r, conn| async_stream::stream! { 71 | //! r.table("games").insert(json!({"id": 1})).run(conn) 72 | //! # }); 73 | //! ``` 74 | //! 75 | //! the first client would receive and print the following objects: 76 | //! 77 | //! ```json 78 | //! {old_val: null, new_val: {id: 1}} 79 | //! ``` 80 | 81 | use crate::{cmd, Command}; 82 | use ql2::term::TermType; 83 | use reql_macros::CommandOptions; 84 | use serde::Serialize; 85 | 86 | /// Optional arguments to `changes` 87 | #[derive(Debug, Clone, Copy, CommandOptions, Serialize, Default, PartialEq, PartialOrd)] 88 | #[non_exhaustive] 89 | pub struct Options { 90 | /// Controls how change notifications are batched 91 | #[serde(skip_serializing_if = "Option::is_none")] 92 | pub squash: Option, 93 | /// The number of changes the server will buffer between client reads 94 | /// before it starts dropping changes and generates an error 95 | /// (default: 100,000). 96 | #[serde(skip_serializing_if = "Option::is_none")] 97 | pub changefeed_queue_size: Option, 98 | /// If `true`, the changefeed stream will begin with the current contents 99 | /// of the table or selection being monitored. These initial results will 100 | /// have `new_val` fields, but no `old_val` fields. The initial results 101 | /// may be intermixed with actual changes, as long as an initial result 102 | /// for the changed document has already been given. If an initial result 103 | /// for a document has been sent and a change is made to that document 104 | /// that would move it to the unsent part of the result set (e.g., a 105 | /// changefeed monitors the top 100 posters, the first 50 have been sent, 106 | /// and poster 48 has become poster 52), an "uninitial" notification will 107 | /// be sent, with an `old_val` field but no `new_val` field. 108 | #[serde(skip_serializing_if = "Option::is_none")] 109 | pub include_initial: Option, 110 | /// If `true`, the changefeed stream will include special status documents 111 | /// consisting of the field `state` and a string indicating a change in the 112 | /// feed's state. These documents can occur at any point in the feed between 113 | /// the notification documents described below. If `includeStates` is `false` 114 | /// (the default), the status documents will not be sent. 115 | #[serde(skip_serializing_if = "Option::is_none")] 116 | pub include_states: Option, 117 | /// If `true`, a changefeed stream on an `order_by.limit` changefeed will 118 | /// include `old_offset` and `new_offset` fields in status documents that 119 | /// include `old_val` and `new_val`. This allows applications to maintain 120 | /// ordered lists of the stream's result set. If `old_offset` is set and not 121 | /// `null`, the element at `old_offset` is being deleted; if `new_offset` is 122 | /// set and not `null`, then `new_val` is being inserted at `new_offset`. 123 | /// Setting `include_offsets` to `true` on a changefeed that does not support 124 | /// it will raise an error. 125 | #[serde(skip_serializing_if = "Option::is_none")] 126 | pub include_offsets: Option, 127 | /// If `true`, every result on a changefeed will include a `type` field with 128 | /// a string that indicates the kind of change the result represents: 129 | /// `add`, `remove`, `change`, `initial`, `uninitial`, `state`. 130 | /// Defaults to `false`. 131 | /// 132 | /// There are currently two states: 133 | /// 134 | /// * `{state: 'initializing'}` indicates the following documents represent 135 | /// initial values on the feed rather than changes. This will be the first 136 | /// document of a feed that returns initial values. 137 | /// * `{state: 'ready'}` indicates the following documents represent changes. 138 | /// This will be the first document of a feed that does *not* return initial 139 | /// values; otherwise, it will indicate the initial values have all been sent. 140 | #[serde(skip_serializing_if = "Option::is_none")] 141 | pub include_types: Option, 142 | } 143 | 144 | /// Controls how change notifications are batched 145 | #[derive(Debug, Clone, Copy, Serialize, PartialEq, PartialOrd)] 146 | #[non_exhaustive] 147 | #[serde(untagged)] 148 | pub enum Squash { 149 | /// `true`: When multiple changes to the same document occur before a 150 | /// batch of notifications is sent, the changes are "squashed" into one 151 | /// change. The client receives a notification that will bring it fully 152 | /// up to date with the server. 153 | /// `false`: All changes will be sent to the client verbatim. This is 154 | /// the default. 155 | Bool(bool), 156 | /// `n`: A numeric value (floating point). Similar to `true`, but the 157 | /// server will wait `n` seconds to respond in order to squash as many 158 | /// changes together as possible, reducing network traffic. The first 159 | /// batch will always be returned immediately. 160 | Float(f32), 161 | } 162 | 163 | pub trait Arg { 164 | fn arg(self) -> cmd::Arg; 165 | } 166 | 167 | impl Arg for () { 168 | fn arg(self) -> cmd::Arg { 169 | Command::new(TermType::Changes) 170 | .mark_change_feed() 171 | .into_arg() 172 | } 173 | } 174 | 175 | impl Arg for Options { 176 | fn arg(self) -> cmd::Arg { 177 | ().arg().with_opts(self) 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /reql/src/proto.rs: -------------------------------------------------------------------------------- 1 | use crate::cmd::run::{Db, Options}; 2 | use crate::{err, r}; 3 | use ql2::query::QueryType; 4 | use ql2::term::TermType; 5 | use serde::ser::{self, Serialize, Serializer}; 6 | use serde_json::value::{Number, Value}; 7 | use std::collections::{HashMap, VecDeque}; 8 | use std::{fmt, str}; 9 | 10 | #[derive(Debug, Clone, Eq, PartialEq)] 11 | pub(crate) enum Datum { 12 | Null, 13 | Bool(bool), 14 | Number(Number), 15 | String(String), 16 | Array(Vec), 17 | Object(HashMap), 18 | } 19 | 20 | impl Default for Datum { 21 | fn default() -> Self { 22 | Self::Null 23 | } 24 | } 25 | 26 | impl Serialize for Datum { 27 | fn serialize(&self, serializer: S) -> Result 28 | where 29 | S: Serializer, 30 | { 31 | match self { 32 | Self::Null => serializer.serialize_none(), 33 | Self::Bool(boolean) => boolean.serialize(serializer), 34 | Self::Number(num) => num.serialize(serializer), 35 | Self::String(string) => string.serialize(serializer), 36 | Self::Array(arr) => (TermType::MakeArray as i32, arr).serialize(serializer), 37 | Self::Object(map) => map.serialize(serializer), 38 | } 39 | } 40 | } 41 | 42 | #[allow(array_into_iter)] 43 | #[allow(clippy::into_iter_on_ref)] 44 | impl From<[Command; N]> for Command { 45 | fn from(arr: [Command; N]) -> Self { 46 | let mut query = Self::new(TermType::MakeArray); 47 | // TODO remove this clone on Rust v1.53 once 48 | // https://twitter.com/m_ou_se/status/1385966446254166020 49 | // is available on stable 50 | for arg in arr.into_iter().cloned() { 51 | query = query.with_arg(arg); 52 | } 53 | query 54 | } 55 | } 56 | 57 | impl From for Datum { 58 | fn from(value: Value) -> Self { 59 | match value { 60 | Value::Null => Self::Null, 61 | Value::Bool(boolean) => Self::Bool(boolean), 62 | Value::Number(num) => Self::Number(num), 63 | Value::String(string) => Self::String(string), 64 | Value::Array(arr) => Self::Array(arr.into_iter().map(Into::into).collect()), 65 | Value::Object(map) => Self::Object( 66 | map.into_iter() 67 | .map(|(key, value)| (key, value.into())) 68 | .collect(), 69 | ), 70 | } 71 | } 72 | } 73 | 74 | /// The query that will be sent to RethinkDB 75 | #[derive(Debug, Clone)] 76 | pub struct Command { 77 | typ: TermType, 78 | datum: Option>, 79 | #[doc(hidden)] 80 | pub args: VecDeque>, 81 | opts: Option>, 82 | change_feed: bool, 83 | } 84 | 85 | impl Command { 86 | #[doc(hidden)] 87 | pub fn new(typ: TermType) -> Self { 88 | Self { 89 | typ, 90 | datum: None, 91 | args: VecDeque::new(), 92 | opts: None, 93 | change_feed: false, 94 | } 95 | } 96 | 97 | #[doc(hidden)] 98 | pub fn var(id: u64) -> Self { 99 | let index = Self::from_json(id); 100 | Self::new(TermType::Var).with_arg(index) 101 | } 102 | 103 | pub(crate) fn with_parent(mut self, parent: Command) -> Self { 104 | self.change_feed = self.change_feed || parent.change_feed; 105 | self.args.push_front(Ok(parent)); 106 | self 107 | } 108 | 109 | #[doc(hidden)] 110 | pub fn with_arg(mut self, arg: T) -> Self 111 | where 112 | T: Into, 113 | { 114 | let arg = arg.into(); 115 | self.args.push_back(Ok(arg)); 116 | self 117 | } 118 | 119 | pub(crate) fn with_opts(mut self, opts: T) -> Self 120 | where 121 | T: Serialize, 122 | { 123 | let opts = serde_json::to_value(&opts) 124 | .map(Into::into) 125 | .map_err(Into::into); 126 | self.opts = Some(opts); 127 | self 128 | } 129 | 130 | #[doc(hidden)] 131 | pub fn from_json(arg: T) -> Self 132 | where 133 | T: Serialize, 134 | { 135 | serde_json::to_value(arg).map_err(super::Error::from).into() 136 | } 137 | 138 | pub(crate) fn mark_change_feed(mut self) -> Self { 139 | self.change_feed = true; 140 | self 141 | } 142 | 143 | pub(crate) fn change_feed(&self) -> bool { 144 | self.change_feed 145 | } 146 | 147 | pub(crate) fn into_arg(self) -> Arg { 148 | Arg { 149 | arg: self, 150 | opts: None, 151 | } 152 | } 153 | } 154 | 155 | impl From for Command { 156 | fn from(datum: Datum) -> Self { 157 | Ok(datum).into() 158 | } 159 | } 160 | 161 | impl From> for Command { 162 | fn from(result: super::Result) -> Self { 163 | let mut query = Self::new(TermType::Datum); 164 | query.datum = Some(result); 165 | query 166 | } 167 | } 168 | 169 | #[doc(hidden)] 170 | impl From for Command { 171 | fn from(value: Value) -> Self { 172 | Datum::from(value).into() 173 | } 174 | } 175 | 176 | #[doc(hidden)] 177 | impl From> for Command { 178 | fn from(result: super::Result) -> Self { 179 | match result { 180 | Ok(value) => Datum::from(value).into(), 181 | Err(error) => (Err(error) as super::Result).into(), 182 | } 183 | } 184 | } 185 | 186 | #[derive(Debug, Clone)] 187 | pub(crate) struct Query<'a>(pub(crate) &'a Command); 188 | 189 | impl Serialize for Query<'_> { 190 | fn serialize(&self, serializer: S) -> Result 191 | where 192 | S: Serializer, 193 | { 194 | let Query(cmd) = self; 195 | match cmd.typ { 196 | TermType::Datum => match &cmd.datum { 197 | Some(Ok(datum)) => datum.serialize(serializer), 198 | Some(Err(error)) => Err(ser::Error::custom(error)), 199 | _ => (None as Option).serialize(serializer), 200 | }, 201 | _ => { 202 | let typ = cmd.typ as i32; 203 | match &cmd.opts { 204 | Some(Ok(map)) => ( 205 | typ, 206 | to_query_result(&cmd.args).map_err(ser::Error::custom)?, 207 | map, 208 | ) 209 | .serialize(serializer), 210 | None => (typ, to_query_result(&cmd.args).map_err(ser::Error::custom)?) 211 | .serialize(serializer), 212 | Some(Err(error)) => Err(ser::Error::custom(error)), 213 | } 214 | } 215 | } 216 | } 217 | } 218 | 219 | fn to_query_result(args: &VecDeque>) -> super::Result>> { 220 | let mut vec = Vec::with_capacity(args.len()); 221 | for result in args { 222 | let arg = result.as_ref().map_err(|error| error.clone())?; 223 | vec.push(Query(arg)); 224 | } 225 | Ok(vec) 226 | } 227 | 228 | #[derive(Debug, Clone)] 229 | pub struct Arg { 230 | #[doc(hidden)] 231 | pub arg: Command, 232 | #[doc(hidden)] 233 | pub opts: Option, 234 | } 235 | 236 | impl Arg 237 | where 238 | T: Serialize, 239 | { 240 | pub(crate) fn new() -> Self { 241 | Self { 242 | arg: Command::new(TermType::Datum), 243 | opts: None, 244 | } 245 | } 246 | 247 | pub(crate) fn with_parent(mut self, parent: Command) -> Self { 248 | self.arg = self.arg.with_parent(parent); 249 | self 250 | } 251 | 252 | pub(crate) fn with_arg(mut self, arg: Q) -> Self 253 | where 254 | Q: Into, 255 | { 256 | self.arg = self.arg.with_arg(arg); 257 | self 258 | } 259 | 260 | pub(crate) fn with_opts(mut self, opts: T) -> Self { 261 | self.opts = Some(opts); 262 | self 263 | } 264 | 265 | pub(crate) fn into_cmd(self) -> Command { 266 | match self.opts { 267 | Some(opts) => self.arg.with_opts(opts), 268 | None => self.arg, 269 | } 270 | } 271 | } 272 | 273 | #[derive(Debug, Clone)] 274 | pub(crate) struct Payload<'a>( 275 | pub(crate) QueryType, 276 | pub(crate) Option>, 277 | pub(crate) Options, 278 | ); 279 | 280 | impl Serialize for Payload<'_> { 281 | fn serialize(&self, serializer: S) -> Result 282 | where 283 | S: Serializer, 284 | { 285 | let Payload(typ, qry, opts) = self; 286 | let typ = *typ as i32; 287 | match qry { 288 | Some(query) => (typ, query, opts).serialize(serializer), 289 | None => (typ,).serialize(serializer), 290 | } 291 | } 292 | } 293 | 294 | impl Payload<'_> { 295 | pub(crate) fn to_bytes(&self) -> Result, err::Error> { 296 | Ok(serde_json::to_vec(self)?) 297 | } 298 | } 299 | 300 | // for debugging purposes only 301 | impl fmt::Display for Payload<'_> { 302 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 303 | // print the serialised string if we can 304 | if let Ok(payload) = self.to_bytes() { 305 | if let Ok(payload) = str::from_utf8(&payload) { 306 | return write!(f, "{}", payload); 307 | } 308 | } 309 | // otherwise just print the debug form 310 | write!(f, "{:?}", self) 311 | } 312 | } 313 | 314 | impl Serialize for Db { 315 | fn serialize(&self, serializer: S) -> Result 316 | where 317 | S: Serializer, 318 | { 319 | let Self(name) = self; 320 | let cmd = r.db(name.as_ref()); 321 | Query(&cmd).serialize(serializer) 322 | } 323 | } 324 | -------------------------------------------------------------------------------- /reql/src/cmd/connect.rs: -------------------------------------------------------------------------------- 1 | //! Create a new connection to the database server 2 | 3 | use super::args::Args; 4 | use super::{bytes_to_string, StaticString}; 5 | use crate::{err, InnerSession, Result, Session}; 6 | use async_net::{AsyncToSocketAddrs, TcpStream}; 7 | use dashmap::DashMap; 8 | use futures::io::{AsyncReadExt, AsyncWriteExt}; 9 | use futures::lock::Mutex; 10 | use ql2::version_dummy::Version; 11 | use reql_macros::CommandOptions; 12 | use scram::client::{ScramClient, ServerFinal, ServerFirst}; 13 | use serde::{Deserialize, Serialize}; 14 | use std::borrow::Cow; 15 | use std::net::SocketAddr; 16 | use std::sync::atomic::{AtomicBool, AtomicU64}; 17 | use std::sync::Arc; 18 | use tracing::trace; 19 | 20 | const BUF_SIZE: usize = 1024; 21 | const NULL_BYTE: u8 = b'\0'; 22 | const PROTOCOL_VERSION: usize = 0; 23 | 24 | pub(crate) const DEFAULT_DB: &str = "test"; 25 | 26 | /// Options accepted by [crate::r::connect] 27 | #[derive(Debug, Clone, CommandOptions, Eq, PartialEq, Ord, PartialOrd, Hash)] 28 | #[non_exhaustive] 29 | pub struct Options { 30 | /// Host of the RethinkDB instance. The default value is `localhost`. 31 | pub host: Cow<'static, str>, 32 | /// The driver port, by default `28015`. 33 | pub port: u16, 34 | /// The database used if not explicitly specified in a query, by default `test`. 35 | pub db: Cow<'static, str>, 36 | /// The user account to connect as (default `admin`). 37 | pub user: Cow<'static, str>, 38 | /// The password for the user account to connect as (default `""`, empty). 39 | pub password: Cow<'static, str>, 40 | } 41 | 42 | impl Default for Options { 43 | fn default() -> Self { 44 | Self { 45 | host: "localhost".static_string(), 46 | port: 28015, 47 | db: DEFAULT_DB.static_string(), 48 | user: "admin".static_string(), 49 | password: "".static_string(), 50 | } 51 | } 52 | } 53 | 54 | /// The arguments accepted by [crate::r::connect] 55 | pub trait Arg { 56 | type ToAddrs: AsyncToSocketAddrs; 57 | 58 | fn into_connect_opts(self) -> (Option, Options); 59 | } 60 | 61 | impl Arg for () { 62 | type ToAddrs = SocketAddr; 63 | 64 | fn into_connect_opts(self) -> (Option, Options) { 65 | (None, Default::default()) 66 | } 67 | } 68 | 69 | impl Arg for Options { 70 | type ToAddrs = SocketAddr; 71 | 72 | fn into_connect_opts(self) -> (Option, Options) { 73 | (None, self) 74 | } 75 | } 76 | 77 | impl<'a> Arg for &'a str { 78 | type ToAddrs = (&'a str, u16); 79 | 80 | fn into_connect_opts(self) -> (Option, Options) { 81 | let opts = Options::default(); 82 | (Some((self, opts.port)), opts) 83 | } 84 | } 85 | 86 | impl Arg for Args<(T, Options)> 87 | where 88 | T: AsyncToSocketAddrs, 89 | { 90 | type ToAddrs = T; 91 | 92 | fn into_connect_opts(self) -> (Option, Options) { 93 | let Args((addr, opts)) = self; 94 | (Some(addr), opts) 95 | } 96 | } 97 | 98 | pub(crate) async fn new((addr, options): (Option, Options)) -> Result 99 | where 100 | T: AsyncToSocketAddrs, 101 | { 102 | let stream = match addr { 103 | Some(addr) => TcpStream::connect(addr).await?, 104 | None => TcpStream::connect((options.host.as_ref(), options.port)).await?, 105 | }; 106 | let inner = InnerSession { 107 | stream: Mutex::new(handshake(stream, &options).await?), 108 | db: Mutex::new(options.db), 109 | channels: DashMap::new(), 110 | token: AtomicU64::new(0), 111 | broken: AtomicBool::new(false), 112 | change_feed: AtomicBool::new(false), 113 | }; 114 | Ok(Session { 115 | inner: Arc::new(inner), 116 | }) 117 | } 118 | 119 | // Performs the actual handshake 120 | // 121 | // This method optimises message exchange as suggested in the RethinkDB 122 | // documentation by sending message 3 right after message 1, without waiting 123 | // for message 2 first. 124 | async fn handshake(mut stream: TcpStream, opts: &Options) -> Result { 125 | trace!("sending supported version to RethinkDB"); 126 | stream 127 | .write_all(&(Version::V10 as i32).to_le_bytes()) 128 | .await?; // message 1 129 | 130 | let scram = ScramClient::new(opts.user.as_ref(), opts.password.as_ref(), None); 131 | let (scram, msg) = client_first(scram)?; 132 | trace!("sending client first message"); 133 | stream.write_all(&msg).await?; // message 3 134 | 135 | let mut buf = [0u8; BUF_SIZE]; 136 | 137 | trace!("receiving message(s) from RethinkDB"); 138 | stream.read(&mut buf).await?; // message 2 139 | let (len, resp) = bytes(&buf, 0); 140 | trace!("received server info; info: {}", bytes_to_string(resp)); 141 | ServerInfo::validate(resp)?; 142 | 143 | let offset = len + 1; 144 | let resp = if offset < BUF_SIZE && buf[offset] != NULL_BYTE { 145 | bytes(&buf, offset).1 146 | } else { 147 | trace!("reading auth response"); 148 | stream.read(&mut buf).await?; // message 4 149 | bytes(&buf, 0).1 150 | }; 151 | trace!("received auth response"); 152 | let info = AuthResponse::from_slice(resp)?; 153 | let auth = match info.authentication { 154 | Some(auth) => auth, 155 | None => { 156 | let msg = String::from("server did not send authentication info"); 157 | return Err(err::Driver::Other(msg).into()); 158 | } 159 | }; 160 | 161 | let (scram, msg) = client_final(scram, &auth)?; 162 | trace!("sending client final message"); 163 | stream.write_all(&msg).await?; // message 5 164 | 165 | trace!("reading server final message"); 166 | stream.read(&mut buf).await?; // message 6 167 | let resp = bytes(&buf, 0).1; 168 | trace!("received server final message"); 169 | server_final(scram, resp)?; 170 | 171 | trace!("client connected successfully"); 172 | 173 | Ok(stream) 174 | } 175 | 176 | fn bytes(buf: &[u8], offset: usize) -> (usize, &[u8]) { 177 | let len = (&buf[offset..]) 178 | .iter() 179 | .take_while(|x| **x != NULL_BYTE) 180 | .count(); 181 | let max = offset + len; 182 | (max, &buf[offset..max]) 183 | } 184 | 185 | // We are going to use &str for `server_version` because it is safe to do so. 186 | // Unfortunately, the other fields that are using String, are doing so because 187 | // because they can potentially contain an escaped double quote which is not 188 | // supported by serde in &str. 189 | #[derive(Serialize, Deserialize, Debug)] 190 | struct ServerInfo<'a> { 191 | success: bool, 192 | min_protocol_version: usize, 193 | max_protocol_version: usize, 194 | server_version: &'a str, 195 | } 196 | 197 | impl ServerInfo<'_> { 198 | fn validate(resp: &[u8]) -> Result<()> { 199 | let info = serde_json::from_slice::(resp)?; 200 | if !info.success { 201 | return Err(err::Runtime::Internal(bytes_to_string(resp)).into()); 202 | } 203 | #[allow(clippy::absurd_extreme_comparisons)] 204 | if PROTOCOL_VERSION < info.min_protocol_version 205 | || info.max_protocol_version < PROTOCOL_VERSION 206 | { 207 | let msg = format!( 208 | "unsupported protocol version {version}, expected between {min} and {max}", 209 | version = PROTOCOL_VERSION, 210 | min = info.min_protocol_version, 211 | max = info.max_protocol_version, 212 | ); 213 | return Err(err::Driver::Other(msg).into()); 214 | } 215 | Ok(()) 216 | } 217 | } 218 | 219 | #[derive(Serialize, Deserialize, Debug)] 220 | struct AuthRequest { 221 | protocol_version: usize, 222 | authentication_method: &'static str, 223 | authentication: String, 224 | } 225 | 226 | fn client_first(scram: ScramClient<'_>) -> Result<(ServerFirst<'_>, Vec)> { 227 | let (scram, client_first) = scram.client_first(); 228 | let ar = AuthRequest { 229 | protocol_version: PROTOCOL_VERSION, 230 | authentication_method: "SCRAM-SHA-256", 231 | authentication: client_first, 232 | }; 233 | let mut msg = serde_json::to_vec(&ar)?; 234 | msg.push(NULL_BYTE); 235 | Ok((scram, msg)) 236 | } 237 | 238 | #[derive(Serialize, Deserialize, Debug)] 239 | struct AuthConfirmation { 240 | authentication: String, 241 | } 242 | 243 | fn client_final(scram: ServerFirst<'_>, auth: &str) -> Result<(ServerFinal, Vec)> { 244 | let scram = scram 245 | .handle_server_first(auth) 246 | .map_err(|x| x.to_string()) 247 | .map_err(err::Driver::Other)?; 248 | let (scram, client_final) = scram.client_final(); 249 | let conf = AuthConfirmation { 250 | authentication: client_final, 251 | }; 252 | let mut msg = serde_json::to_vec(&conf)?; 253 | msg.push(NULL_BYTE); 254 | Ok((scram, msg)) 255 | } 256 | 257 | #[derive(Serialize, Deserialize, Debug)] 258 | struct AuthResponse { 259 | success: bool, 260 | authentication: Option, 261 | error_code: Option, 262 | error: Option, 263 | } 264 | 265 | impl AuthResponse { 266 | fn from_slice(resp: &[u8]) -> Result { 267 | let info = serde_json::from_slice::(resp)?; 268 | if !info.success { 269 | // If error code is between 10 and 20, this is an auth error 270 | if let Some(10..=20) = info.error_code { 271 | if let Some(msg) = info.error { 272 | return Err(err::Driver::Auth(msg).into()); 273 | } 274 | } 275 | return Err(err::Runtime::Internal(bytes_to_string(resp)).into()); 276 | } 277 | Ok(info) 278 | } 279 | } 280 | 281 | fn server_final(scram: ServerFinal, resp: &[u8]) -> Result<()> { 282 | let info = AuthResponse::from_slice(resp)?; 283 | if let Some(auth) = info.authentication { 284 | if let Err(error) = scram.handle_server_final(&auth) { 285 | return Err(err::Driver::Other(error.to_string()).into()); 286 | } 287 | } 288 | Ok(()) 289 | } 290 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /reql/src/cmd/run.rs: -------------------------------------------------------------------------------- 1 | use super::args::Args; 2 | use super::connect::DEFAULT_DB; 3 | use crate::cmd::{Durability, ReadMode}; 4 | use crate::proto::{Payload, Query}; 5 | use crate::{err, r, Command, Connection, Result, Session}; 6 | use async_stream::try_stream; 7 | use futures::io::{AsyncReadExt, AsyncWriteExt}; 8 | use futures::stream::{Stream, StreamExt}; 9 | use ql2::query::QueryType; 10 | use ql2::response::{ErrorType, ResponseType}; 11 | use reql_macros::CommandOptions; 12 | use serde::de::DeserializeOwned; 13 | use serde::{Deserialize, Serialize}; 14 | use serde_json::Value; 15 | use std::borrow::Cow; 16 | use std::str; 17 | use std::sync::atomic::Ordering; 18 | use tracing::trace; 19 | 20 | const DATA_SIZE: usize = 4; 21 | const TOKEN_SIZE: usize = 8; 22 | const HEADER_SIZE: usize = DATA_SIZE + TOKEN_SIZE; 23 | 24 | #[derive(Deserialize, Debug)] 25 | #[allow(dead_code)] 26 | pub(crate) struct Response { 27 | t: i32, 28 | e: Option, 29 | pub(crate) r: Value, 30 | b: Option, 31 | p: Option, 32 | n: Option, 33 | } 34 | 35 | impl Response { 36 | fn new() -> Self { 37 | Self { 38 | t: ResponseType::SuccessAtom as i32, 39 | e: None, 40 | r: Value::Array(Vec::new()), 41 | b: None, 42 | p: None, 43 | n: None, 44 | } 45 | } 46 | } 47 | 48 | #[derive( 49 | Debug, Clone, CommandOptions, Serialize, Default, Eq, PartialEq, Ord, PartialOrd, Hash, 50 | )] 51 | #[non_exhaustive] 52 | pub struct Options { 53 | #[serde(skip_serializing_if = "Option::is_none")] 54 | pub read_mode: Option, 55 | #[serde(skip_serializing_if = "Option::is_none")] 56 | pub time_format: Option, 57 | #[serde(skip_serializing_if = "Option::is_none")] 58 | pub profile: Option, 59 | #[serde(skip_serializing_if = "Option::is_none")] 60 | pub durability: Option, 61 | #[serde(skip_serializing_if = "Option::is_none")] 62 | pub group_format: Option, 63 | #[serde(skip_serializing_if = "Option::is_none")] 64 | pub noreply: Option, 65 | #[serde(skip_serializing_if = "Option::is_none")] 66 | pub db: Option, 67 | } 68 | 69 | #[derive(Debug, Clone, Copy, Serialize, Eq, PartialEq, Ord, PartialOrd, Hash)] 70 | #[non_exhaustive] 71 | #[serde(rename_all = "lowercase")] 72 | pub enum Format { 73 | Native, 74 | Raw, 75 | } 76 | 77 | #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] 78 | pub struct Db(pub Cow<'static, str>); 79 | 80 | impl Options { 81 | async fn default_db(self, session: &Session) -> Options { 82 | let session_db = session.inner.db.lock().await; 83 | if self.db.is_none() && *session_db != DEFAULT_DB { 84 | return self.db(&*session_db); 85 | } 86 | self 87 | } 88 | } 89 | 90 | pub trait Arg { 91 | fn into_run_opts(self) -> Result<(Connection, Options)>; 92 | } 93 | 94 | impl Arg for &Session { 95 | fn into_run_opts(self) -> Result<(Connection, Options)> { 96 | let conn = self.connection()?; 97 | Ok((conn, Default::default())) 98 | } 99 | } 100 | 101 | impl Arg for Connection { 102 | fn into_run_opts(self) -> Result<(Connection, Options)> { 103 | Ok((self, Default::default())) 104 | } 105 | } 106 | 107 | impl Arg for Args<(&Session, Options)> { 108 | fn into_run_opts(self) -> Result<(Connection, Options)> { 109 | let Args((session, options)) = self; 110 | let conn = session.connection()?; 111 | Ok((conn, options)) 112 | } 113 | } 114 | 115 | impl Arg for Args<(Connection, Options)> { 116 | fn into_run_opts(self) -> Result<(Connection, Options)> { 117 | let Args(arg) = self; 118 | Ok(arg) 119 | } 120 | } 121 | 122 | impl Arg for &mut Session { 123 | fn into_run_opts(self) -> Result<(Connection, Options)> { 124 | self.connection()?.into_run_opts() 125 | } 126 | } 127 | 128 | impl Arg for Args<(&mut Session, Options)> { 129 | fn into_run_opts(self) -> Result<(Connection, Options)> { 130 | let Args((session, options)) = self; 131 | let conn = session.connection()?; 132 | r.args((conn, options)).into_run_opts() 133 | } 134 | } 135 | 136 | pub(crate) fn new(query: Command, arg: A) -> impl Stream> 137 | where 138 | A: Arg, 139 | T: Unpin + DeserializeOwned, 140 | { 141 | try_stream! { 142 | let (mut conn, mut opts) = arg.into_run_opts()?; 143 | opts = opts.default_db(&conn.session).await; 144 | let change_feed = query.change_feed(); 145 | if change_feed { 146 | conn.session.inner.mark_change_feed(); 147 | } 148 | let noreply = opts.noreply.unwrap_or_default(); 149 | let mut payload = Payload(QueryType::Start, Some(Query(&query)), opts); 150 | loop { 151 | let (response_type, resp) = conn.request(&payload, noreply).await?; 152 | trace!("yielding response; token: {}", conn.token); 153 | match response_type { 154 | ResponseType::SuccessAtom | ResponseType::SuccessSequence | ResponseType::ServerInfo => { 155 | for val in serde_json::from_value::>(resp.r)? { 156 | yield val; 157 | } 158 | break; 159 | } 160 | ResponseType::SuccessPartial => { 161 | if conn.closed() { 162 | // reopen so we can use the connection in future 163 | conn.set_closed(false); 164 | trace!("connection closed; token: {}", conn.token); 165 | break; 166 | } 167 | payload = Payload(QueryType::Continue, None, Default::default()); 168 | for val in serde_json::from_value::>(resp.r)? { 169 | yield val; 170 | } 171 | continue; 172 | } 173 | ResponseType::WaitComplete => { break; } 174 | typ => { 175 | let msg = error_message(resp.r)?; 176 | match typ { 177 | // This feed has been closed by conn.close(). 178 | ResponseType::ClientError if change_feed && msg.contains("not in stream cache") => { break; } 179 | _ => Err(response_error(typ, resp.e, msg))?, 180 | } 181 | } 182 | } 183 | } 184 | } 185 | } 186 | 187 | impl Payload<'_> { 188 | fn encode(&self, token: u64) -> Result> { 189 | let bytes = self.to_bytes()?; 190 | let data_len = bytes.len(); 191 | let mut buf = Vec::with_capacity(HEADER_SIZE + data_len); 192 | buf.extend_from_slice(&token.to_le_bytes()); 193 | buf.extend_from_slice(&(data_len as u32).to_le_bytes()); 194 | buf.extend_from_slice(&bytes); 195 | Ok(buf) 196 | } 197 | } 198 | 199 | impl Connection { 200 | fn send_response(&self, db_token: u64, resp: Result<(ResponseType, Response)>) { 201 | if let Some(tx) = self.session.inner.channels.get(&db_token) { 202 | if let Err(error) = tx.unbounded_send(resp) { 203 | if error.is_disconnected() { 204 | self.session.inner.channels.remove(&db_token); 205 | } 206 | } 207 | } 208 | } 209 | 210 | pub(crate) async fn request<'a>( 211 | &mut self, 212 | query: &'a Payload<'a>, 213 | noreply: bool, 214 | ) -> Result<(ResponseType, Response)> { 215 | self.submit(query, noreply).await; 216 | match self.rx.lock().await.next().await { 217 | Some(resp) => resp, 218 | None => Ok((ResponseType::SuccessAtom, Response::new())), 219 | } 220 | } 221 | 222 | async fn submit<'a>(&self, query: &'a Payload<'a>, noreply: bool) { 223 | let mut db_token = self.token; 224 | let result = self.exec(query, noreply, &mut db_token).await; 225 | self.send_response(db_token, result); 226 | } 227 | 228 | async fn exec<'a>( 229 | &self, 230 | query: &'a Payload<'a>, 231 | noreply: bool, 232 | db_token: &mut u64, 233 | ) -> Result<(ResponseType, Response)> { 234 | let buf = query.encode(self.token)?; 235 | 236 | let guard = self.session.inner.stream.lock().await; 237 | let mut stream = guard.clone(); 238 | 239 | trace!("sending query; token: {}, payload: {}", self.token, query); 240 | stream.write_all(&buf).await?; 241 | trace!("query sent; token: {}", self.token); 242 | 243 | if noreply { 244 | return Ok((ResponseType::SuccessAtom, Response::new())); 245 | } 246 | 247 | trace!("reading header; token: {}", self.token); 248 | let mut header = [0u8; HEADER_SIZE]; 249 | stream.read_exact(&mut header).await?; 250 | 251 | let mut buf = [0u8; TOKEN_SIZE]; 252 | buf.copy_from_slice(&header[..TOKEN_SIZE]); 253 | *db_token = { 254 | let token = u64::from_le_bytes(buf); 255 | trace!("db_token: {}", token); 256 | if token > self.session.inner.token.load(Ordering::SeqCst) { 257 | self.session.inner.mark_broken(); 258 | return Err(err::Driver::ConnectionBroken.into()); 259 | } 260 | token 261 | }; 262 | 263 | let mut buf = [0u8; DATA_SIZE]; 264 | buf.copy_from_slice(&header[TOKEN_SIZE..]); 265 | let len = u32::from_le_bytes(buf) as usize; 266 | trace!( 267 | "header read; token: {}, db_token: {}, response_len: {}", 268 | self.token, 269 | db_token, 270 | len 271 | ); 272 | 273 | trace!("reading body; token: {}", self.token); 274 | let mut buf = vec![0u8; len]; 275 | stream.read_exact(&mut buf).await?; 276 | 277 | trace!( 278 | "body read; token: {}, db_token: {}, body: {}", 279 | self.token, 280 | db_token, 281 | super::bytes_to_string(&buf), 282 | ); 283 | 284 | let resp = serde_json::from_slice::(&buf)?; 285 | trace!("response successfully parsed; token: {}", self.token,); 286 | 287 | let response_type = ResponseType::from_i32(resp.t) 288 | .ok_or_else(|| err::Driver::Other(format!("unknown response type `{}`", resp.t)))?; 289 | 290 | if let Some(error_type) = resp.e { 291 | let msg = error_message(resp.r)?; 292 | return Err(response_error(response_type, Some(error_type), msg)); 293 | } 294 | 295 | Ok((response_type, resp)) 296 | } 297 | } 298 | 299 | fn error_message(response: Value) -> Result { 300 | let messages = serde_json::from_value::>(response)?; 301 | Ok(messages.join(" ")) 302 | } 303 | 304 | fn response_error(response_type: ResponseType, error_type: Option, msg: String) -> err::Error { 305 | match response_type { 306 | ResponseType::ClientError => err::Driver::Other(msg).into(), 307 | ResponseType::CompileError => err::Error::Compile(msg), 308 | ResponseType::RuntimeError => match error_type 309 | .map(ErrorType::from_i32) 310 | .ok_or_else(|| err::Driver::Other(format!("unexpected runtime error: {}", msg))) 311 | { 312 | Ok(Some(ErrorType::Internal)) => err::Runtime::Internal(msg).into(), 313 | Ok(Some(ErrorType::ResourceLimit)) => err::Runtime::ResourceLimit(msg).into(), 314 | Ok(Some(ErrorType::QueryLogic)) => err::Runtime::QueryLogic(msg).into(), 315 | Ok(Some(ErrorType::NonExistence)) => err::Runtime::NonExistence(msg).into(), 316 | Ok(Some(ErrorType::OpFailed)) => err::Availability::OpFailed(msg).into(), 317 | Ok(Some(ErrorType::OpIndeterminate)) => err::Availability::OpIndeterminate(msg).into(), 318 | Ok(Some(ErrorType::User)) => err::Runtime::User(msg).into(), 319 | Ok(Some(ErrorType::PermissionError)) => err::Runtime::Permission(msg).into(), 320 | Err(error) => error.into(), 321 | _ => err::Driver::Other(format!("unexpected runtime error: {}", msg)).into(), 322 | }, 323 | _ => err::Driver::Other(format!("unexpected response: {}", msg)).into(), 324 | } 325 | } 326 | -------------------------------------------------------------------------------- /reql/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! ReQL is the RethinkDB query language. It offers a very powerful and 2 | //! convenient way to manipulate JSON documents. 3 | //! 4 | //! # Start the server # 5 | //! 6 | //! ## Linux and OS X ## 7 | //! 8 | //! Start the server from a terminal window. 9 | //! 10 | //! ```bash 11 | //! $ rethinkdb 12 | //! ``` 13 | //! 14 | //! ## Windows ## 15 | //! 16 | //! Start the server from the Windows command prompt. 17 | //! 18 | //! ```bash 19 | //! C:\Path\To\RethinkDB\>rethinkdb.exe 20 | //! ``` 21 | //! 22 | //! # Import the driver # 23 | //! 24 | //! First, make sure you have `protoc` installed and in your `PATH`. See 25 | //! [`prost-build` documentation](https://docs.rs/prost-build/0.7.0/prost_build/#sourcing-protoc) 26 | //! for more details if it fails to compile. 27 | //! 28 | //! Add this crate (`reql`) and the `futures` crate to your dependencies in `Cargo.toml`. 29 | //! 30 | //! Now import the RethinkDB driver: 31 | //! 32 | //! ``` 33 | //! use reql::r; 34 | //! ``` 35 | //! 36 | //! You can now access RethinkDB commands through the [`r` struct](r). 37 | //! 38 | //! # Open a connection # 39 | //! 40 | //! When you first start RethinkDB, the server opens a port for the client 41 | //! drivers (`28015` by default). Let's open a connection: 42 | //! 43 | //! ``` 44 | //! use reql::r; 45 | //! 46 | //! # async fn example() -> reql::Result<()> { 47 | //! let session = r.connect(()).await?; 48 | //! # Ok(()) }; 49 | //! ``` 50 | //! 51 | //! The variable `connection` is now initialized and we can run queries. 52 | //! 53 | //! # Send a query to the database # 54 | //! 55 | //! ``` 56 | //! # reql::example(|r, conn| async_stream::stream! { 57 | //! r.expr("Hello world!").run(conn) 58 | //! # }); 59 | //! ``` 60 | //! 61 | //! [See the `r` struct for more available commands](r) 62 | 63 | #![allow(clippy::wrong_self_convention)] 64 | 65 | pub mod cmd; 66 | mod err; 67 | mod proto; 68 | 69 | use async_net::TcpStream; 70 | use cmd::run::Response; 71 | use cmd::StaticString; 72 | use dashmap::DashMap; 73 | use futures::channel::mpsc::{self, UnboundedReceiver, UnboundedSender}; 74 | use futures::lock::Mutex; 75 | use proto::{Payload, Query}; 76 | use ql2::query::QueryType; 77 | use ql2::response::ResponseType; 78 | use ql2::term::TermType; 79 | use serde_json::json; 80 | use std::borrow::Cow; 81 | use std::ops::Drop; 82 | use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; 83 | use std::sync::Arc; 84 | use tracing::trace; 85 | use types::ServerInfo; 86 | 87 | #[doc(hidden)] 88 | pub use cmd::func::Func; 89 | pub use err::*; 90 | pub use proto::Command; 91 | pub use reql_macros::func; 92 | #[doc(inline)] 93 | pub use reql_types as types; 94 | 95 | #[doc(hidden)] 96 | pub static VAR_COUNTER: AtomicU64 = AtomicU64::new(1); 97 | 98 | #[doc(hidden)] 99 | pub fn var_counter() -> u64 { 100 | VAR_COUNTER.fetch_add(1, Ordering::SeqCst) 101 | } 102 | 103 | #[cfg(test)] 104 | fn current_counter() -> u64 { 105 | VAR_COUNTER.load(Ordering::SeqCst) 106 | } 107 | 108 | /// Custom result returned by various ReQL commands 109 | pub type Result = std::result::Result; 110 | 111 | type Sender = UnboundedSender>; 112 | type Receiver = UnboundedReceiver>; 113 | 114 | #[derive(Debug)] 115 | struct InnerSession { 116 | db: Mutex>, 117 | stream: Mutex, 118 | channels: DashMap, 119 | token: AtomicU64, 120 | broken: AtomicBool, 121 | change_feed: AtomicBool, 122 | } 123 | 124 | impl InnerSession { 125 | fn token(&self) -> u64 { 126 | let token = self 127 | .token 128 | .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(x + 1)) 129 | .unwrap(); 130 | if token == u64::MAX { 131 | self.mark_broken(); 132 | } 133 | token 134 | } 135 | 136 | fn mark_broken(&self) { 137 | self.broken.store(true, Ordering::SeqCst); 138 | } 139 | 140 | fn broken(&self) -> Result<()> { 141 | if self.broken.load(Ordering::SeqCst) { 142 | return Err(err::Driver::ConnectionBroken.into()); 143 | } 144 | Ok(()) 145 | } 146 | 147 | fn mark_change_feed(&self) { 148 | self.change_feed.store(true, Ordering::SeqCst); 149 | } 150 | 151 | fn unmark_change_feed(&self) { 152 | self.change_feed.store(false, Ordering::SeqCst); 153 | } 154 | 155 | fn is_change_feed(&self) -> bool { 156 | self.change_feed.load(Ordering::SeqCst) 157 | } 158 | 159 | fn change_feed(&self) -> Result<()> { 160 | if self.change_feed.load(Ordering::SeqCst) { 161 | return Err(err::Driver::ConnectionLocked.into()); 162 | } 163 | Ok(()) 164 | } 165 | } 166 | 167 | /// The connection object returned by `r.connect()` 168 | #[derive(Debug, Clone)] 169 | pub struct Session { 170 | inner: Arc, 171 | } 172 | 173 | impl Session { 174 | pub fn connection(&self) -> Result { 175 | self.inner.broken()?; 176 | self.inner.change_feed()?; 177 | let token = self.inner.token(); 178 | let (tx, rx) = mpsc::unbounded(); 179 | self.inner.channels.insert(token, tx); 180 | Ok(Connection::new(self.clone(), rx, token)) 181 | } 182 | 183 | /// Change the default database on this connection 184 | /// 185 | /// ## Example 186 | /// 187 | /// Change the default database so that we don’t need to specify the 188 | /// database when referencing a table. 189 | /// 190 | /// ``` 191 | /// # reql::example(|r, conn| async_stream::stream! { 192 | /// conn.use_("marvel").await; 193 | /// r.table("heroes").run(conn) // refers to r.db("marvel").table("heroes") 194 | /// # }); 195 | /// ``` 196 | /// 197 | /// ## Related commands 198 | /// * [connect](r::connect) 199 | /// * [close](Connection::close) 200 | pub async fn use_(&mut self, db_name: T) 201 | where 202 | T: StaticString, 203 | { 204 | *self.inner.db.lock().await = db_name.static_string(); 205 | } 206 | 207 | /// Ensures that previous queries with the `noreply` flag have been 208 | /// processed by the server 209 | /// 210 | /// Note that this guarantee only applies to queries run on the given 211 | /// connection. 212 | /// 213 | /// ## Example 214 | /// 215 | /// We have previously run queries with [noreply](cmd::run::Options::noreply()) 216 | /// set to `true`. Now wait until the server has processed them. 217 | /// 218 | /// ``` 219 | /// # async fn example() -> reql::Result<()> { 220 | /// # let session = reql::r.connect(()).await?; 221 | /// session.noreply_wait().await 222 | /// # } 223 | /// ``` 224 | /// 225 | pub async fn noreply_wait(&self) -> Result<()> { 226 | let mut conn = self.connection()?; 227 | let payload = Payload(QueryType::NoreplyWait, None, Default::default()); 228 | trace!( 229 | "waiting for noreply operations to finish; token: {}", 230 | conn.token 231 | ); 232 | let (typ, _) = conn.request(&payload, false).await?; 233 | trace!( 234 | "session.noreply_wait() run; token: {}, response type: {:?}", 235 | conn.token, 236 | typ, 237 | ); 238 | Ok(()) 239 | } 240 | 241 | pub async fn server(&self) -> Result { 242 | let mut conn = self.connection()?; 243 | let payload = Payload(QueryType::ServerInfo, None, Default::default()); 244 | trace!("retrieving server information; token: {}", conn.token); 245 | let (typ, resp) = conn.request(&payload, false).await?; 246 | trace!( 247 | "session.server() run; token: {}, response type: {:?}", 248 | conn.token, 249 | typ, 250 | ); 251 | let mut vec = serde_json::from_value::>(resp.r)?; 252 | let info = vec 253 | .pop() 254 | .ok_or_else(|| Driver::Other("server info is empty".into()))?; 255 | Ok(info) 256 | } 257 | 258 | #[doc(hidden)] 259 | pub fn is_broken(&self) -> bool { 260 | self.inner.broken.load(Ordering::SeqCst) 261 | } 262 | } 263 | 264 | #[derive(Debug, Clone)] 265 | pub struct Connection { 266 | session: Session, 267 | rx: Arc>, 268 | token: u64, 269 | closed: Arc, 270 | } 271 | 272 | impl Connection { 273 | fn new(session: Session, rx: Receiver, token: u64) -> Connection { 274 | Connection { 275 | session, 276 | token, 277 | rx: Arc::new(Mutex::new(rx)), 278 | closed: Arc::new(AtomicBool::new(false)), 279 | } 280 | } 281 | 282 | /// Close an open connection 283 | /// 284 | /// ## Example 285 | /// 286 | /// Close an open connection, waiting for noreply writes to finish. 287 | /// 288 | /// ``` 289 | /// # async fn example() -> reql::Result<()> { 290 | /// # let session = reql::r.connect(()).await?; 291 | /// # let mut conn = session.connection()?; 292 | /// conn.close(()).await 293 | /// # } 294 | /// ``` 295 | /// 296 | /// [Read more about this command →](cmd::close) 297 | pub async fn close(&mut self, arg: T) -> Result<()> 298 | where 299 | T: cmd::close::Arg, 300 | { 301 | if !self.session.inner.is_change_feed() { 302 | trace!( 303 | "ignoring conn.close() called on a normal connection; token: {}", 304 | self.token 305 | ); 306 | return Ok(()); 307 | } 308 | self.set_closed(true); 309 | let arg = if arg.noreply_wait() { 310 | None 311 | } else { 312 | Some(r.expr(json!({ "noreply": false }))) 313 | }; 314 | let payload = Payload(QueryType::Stop, arg.as_ref().map(Query), Default::default()); 315 | trace!("closing a changefeed; token: {}", self.token); 316 | let (typ, _) = self.request(&payload, false).await?; 317 | self.session.inner.unmark_change_feed(); 318 | trace!( 319 | "conn.close() run; token: {}, response type: {:?}", 320 | self.token, 321 | typ, 322 | ); 323 | Ok(()) 324 | } 325 | 326 | fn closed(&self) -> bool { 327 | self.closed.load(Ordering::SeqCst) 328 | } 329 | 330 | fn set_closed(&self, closed: bool) { 331 | self.closed.store(closed, Ordering::SeqCst); 332 | } 333 | } 334 | 335 | impl Drop for Connection { 336 | fn drop(&mut self) { 337 | self.session.inner.channels.remove(&self.token); 338 | if self.session.inner.is_change_feed() { 339 | self.session.inner.unmark_change_feed(); 340 | } 341 | } 342 | } 343 | 344 | /// The top-level ReQL namespace 345 | /// 346 | /// # Example 347 | /// 348 | /// Set up your top-level namespace. 349 | /// 350 | /// ``` 351 | /// use reql::r; 352 | /// ``` 353 | #[allow(non_camel_case_types)] 354 | pub struct r; 355 | 356 | impl r { 357 | /// Create a new connection to the database server 358 | /// 359 | /// # Example 360 | /// 361 | /// Open a connection using the default host and port, specifying the default database. 362 | /// 363 | /// ``` 364 | /// use reql::{r, cmd::connect::Options}; 365 | /// 366 | /// # async fn example() -> reql::Result<()> { 367 | /// let session = r.connect(Options::new().db("marvel")).await?; 368 | /// # Ok(()) } 369 | /// ``` 370 | /// 371 | /// Read more about this command [connect](cmd::connect) 372 | pub async fn connect(self, options: T) -> Result 373 | where 374 | T: cmd::connect::Arg, 375 | { 376 | cmd::connect::new(options.into_connect_opts()).await 377 | } 378 | 379 | pub fn db_create(self, arg: T) -> Command 380 | where 381 | T: cmd::db_create::Arg, 382 | { 383 | arg.arg().into_cmd() 384 | } 385 | 386 | pub fn db_drop(self, arg: T) -> Command 387 | where 388 | T: cmd::db_drop::Arg, 389 | { 390 | arg.arg().into_cmd() 391 | } 392 | 393 | pub fn db_list(self) -> Command { 394 | Command::new(TermType::DbList) 395 | } 396 | 397 | /// Reference a database 398 | /// 399 | /// The `db` command is optional. If it is not present in a query, the 400 | /// query will run against the default database for the connection, 401 | /// specified in the `db` argument to [connect](r::connect). 402 | /// 403 | /// # Examples 404 | /// 405 | /// Explicitly specify a database for a query. 406 | /// 407 | /// ``` 408 | /// # reql::example(|r, conn| async_stream::stream! { 409 | /// r.db("heroes").table("marvel").run(conn) 410 | /// # }); 411 | /// ``` 412 | pub fn db(self, arg: T) -> Command 413 | where 414 | T: cmd::db::Arg, 415 | { 416 | arg.arg().into_cmd() 417 | } 418 | 419 | /// See [Command::table_create] 420 | pub fn table_create(self, arg: T) -> Command 421 | where 422 | T: cmd::table_create::Arg, 423 | { 424 | arg.arg().into_cmd() 425 | } 426 | 427 | pub fn table(self, arg: T) -> Command 428 | where 429 | T: cmd::table::Arg, 430 | { 431 | arg.arg().into_cmd() 432 | } 433 | 434 | pub fn map(self, arg: T) -> Command 435 | where 436 | T: cmd::map::Arg, 437 | { 438 | arg.arg().into_cmd() 439 | } 440 | 441 | pub fn union(self, arg: T) -> Command 442 | where 443 | T: cmd::union::Arg, 444 | { 445 | arg.arg().into_cmd() 446 | } 447 | 448 | pub fn group(self, arg: T) -> Command 449 | where 450 | T: cmd::group::Arg, 451 | { 452 | arg.arg().into_cmd() 453 | } 454 | 455 | pub fn reduce(self, arg: T) -> Command 456 | where 457 | T: cmd::reduce::Arg, 458 | { 459 | arg.arg().into_cmd() 460 | } 461 | 462 | pub fn count(self, arg: T) -> Command 463 | where 464 | T: cmd::count::Arg, 465 | { 466 | arg.arg().into_cmd() 467 | } 468 | 469 | pub fn sum(self, arg: T) -> Command 470 | where 471 | T: cmd::sum::Arg, 472 | { 473 | arg.arg().into_cmd() 474 | } 475 | 476 | pub fn avg(self, arg: T) -> Command 477 | where 478 | T: cmd::avg::Arg, 479 | { 480 | arg.arg().into_cmd() 481 | } 482 | 483 | pub fn min(self, arg: T) -> Command 484 | where 485 | T: cmd::min::Arg, 486 | { 487 | arg.arg().into_cmd() 488 | } 489 | 490 | pub fn max(self, arg: T) -> Command 491 | where 492 | T: cmd::max::Arg, 493 | { 494 | arg.arg().into_cmd() 495 | } 496 | 497 | pub fn distinct(self, arg: T) -> Command 498 | where 499 | T: cmd::distinct::Arg, 500 | { 501 | arg.arg().into_cmd() 502 | } 503 | 504 | pub fn contains(self, arg: T) -> Command 505 | where 506 | T: cmd::contains::Arg, 507 | { 508 | arg.arg().into_cmd() 509 | } 510 | 511 | pub fn literal(self, arg: T) -> Command 512 | where 513 | T: cmd::literal::Arg, 514 | { 515 | arg.arg().into_cmd() 516 | } 517 | 518 | pub fn object(self, arg: T) -> Command 519 | where 520 | T: cmd::object::Arg, 521 | { 522 | arg.arg().into_cmd() 523 | } 524 | 525 | pub fn random(self, arg: T) -> Command 526 | where 527 | T: cmd::random::Arg, 528 | { 529 | arg.arg().into_cmd() 530 | } 531 | 532 | pub fn round(self, arg: T) -> Command 533 | where 534 | T: cmd::round::Arg, 535 | { 536 | arg.arg().into_cmd() 537 | } 538 | 539 | pub fn ceil(self, arg: T) -> Command 540 | where 541 | T: cmd::ceil::Arg, 542 | { 543 | arg.arg().into_cmd() 544 | } 545 | 546 | pub fn floor(self, arg: T) -> Command 547 | where 548 | T: cmd::floor::Arg, 549 | { 550 | arg.arg().into_cmd() 551 | } 552 | 553 | pub fn now(self) -> Command { 554 | Command::new(TermType::Now) 555 | } 556 | 557 | pub fn time(self, arg: T) -> Command 558 | where 559 | T: cmd::time::Arg, 560 | { 561 | arg.arg().into_cmd() 562 | } 563 | 564 | pub fn epoch_time(self, arg: T) -> Command 565 | where 566 | T: cmd::epoch_time::Arg, 567 | { 568 | arg.arg().into_cmd() 569 | } 570 | 571 | pub fn iso8601(self, arg: T) -> Command 572 | where 573 | T: cmd::iso8601::Arg, 574 | { 575 | arg.arg().into_cmd() 576 | } 577 | 578 | pub fn do_(self, arg: T) -> Command 579 | where 580 | T: cmd::do_::Arg, 581 | { 582 | arg.arg(None).into_cmd() 583 | } 584 | 585 | pub fn branch(self, arg: T) -> Command 586 | where 587 | T: cmd::branch::Arg, 588 | { 589 | arg.arg().into_cmd() 590 | } 591 | 592 | pub fn range(self, arg: T) -> Command 593 | where 594 | T: cmd::range::Arg, 595 | { 596 | arg.arg().into_cmd() 597 | } 598 | 599 | pub fn error(self, arg: T) -> Command 600 | where 601 | T: cmd::error::Arg, 602 | { 603 | arg.arg().into_cmd() 604 | } 605 | 606 | pub fn expr(self, arg: T) -> Command 607 | where 608 | T: cmd::expr::Arg, 609 | { 610 | arg.arg().into_cmd() 611 | } 612 | 613 | pub fn js(self, arg: T) -> Command 614 | where 615 | T: cmd::js::Arg, 616 | { 617 | arg.arg().into_cmd() 618 | } 619 | 620 | pub fn info(self, arg: T) -> Command 621 | where 622 | T: cmd::info::Arg, 623 | { 624 | arg.arg().into_cmd() 625 | } 626 | 627 | pub fn json(self, arg: T) -> Command 628 | where 629 | T: cmd::json::Arg, 630 | { 631 | arg.arg().into_cmd() 632 | } 633 | 634 | pub fn http(self, arg: T) -> Command 635 | where 636 | T: cmd::http::Arg, 637 | { 638 | arg.arg().into_cmd() 639 | } 640 | 641 | pub fn uuid(self, arg: T) -> Command 642 | where 643 | T: cmd::uuid::Arg, 644 | { 645 | arg.arg().into_cmd() 646 | } 647 | 648 | pub fn circle(self, arg: T) -> Command 649 | where 650 | T: cmd::circle::Arg, 651 | { 652 | arg.arg().into_cmd() 653 | } 654 | 655 | pub fn distance(self, arg: T) -> Command 656 | where 657 | T: cmd::distance::Arg, 658 | { 659 | arg.arg().into_cmd() 660 | } 661 | 662 | pub fn geojson(self, arg: T) -> Command 663 | where 664 | T: cmd::geojson::Arg, 665 | { 666 | arg.arg().into_cmd() 667 | } 668 | 669 | pub fn intersects(self, arg: T) -> Command 670 | where 671 | T: cmd::intersects::Arg, 672 | { 673 | arg.arg().into_cmd() 674 | } 675 | 676 | pub fn line(self, arg: T) -> Command 677 | where 678 | T: cmd::line::Arg, 679 | { 680 | arg.arg().into_cmd() 681 | } 682 | 683 | pub fn point(self, arg: T) -> Command 684 | where 685 | T: cmd::point::Arg, 686 | { 687 | arg.arg().into_cmd() 688 | } 689 | 690 | pub fn polygon(self, arg: T) -> Command 691 | where 692 | T: cmd::polygon::Arg, 693 | { 694 | arg.arg().into_cmd() 695 | } 696 | 697 | pub fn grant(self, arg: T) -> Command 698 | where 699 | T: cmd::grant::Arg, 700 | { 701 | arg.arg().into_cmd() 702 | } 703 | 704 | pub fn wait(self, arg: T) -> Command 705 | where 706 | T: cmd::wait::Arg, 707 | { 708 | arg.arg().into_cmd() 709 | } 710 | 711 | pub fn asc(self, arg: T) -> cmd::asc::Asc 712 | where 713 | T: cmd::asc::Arg, 714 | { 715 | cmd::asc::Asc(arg.arg().into_cmd()) 716 | } 717 | 718 | pub fn desc(self, arg: T) -> cmd::desc::Desc 719 | where 720 | T: cmd::desc::Arg, 721 | { 722 | cmd::desc::Desc(arg.arg().into_cmd()) 723 | } 724 | 725 | pub fn index(self, arg: T) -> cmd::index::Index 726 | where 727 | T: cmd::index::Arg, 728 | { 729 | cmd::index::Index(arg.arg().into_cmd()) 730 | } 731 | 732 | pub fn args(self, arg: T) -> cmd::args::Args { 733 | cmd::args::Args(arg) 734 | } 735 | } 736 | 737 | // Helper for making writing examples less verbose 738 | #[doc(hidden)] 739 | pub fn example<'a, Q, F>(_query: Q) 740 | where 741 | Q: FnOnce(r, &'a mut Session) -> F, 742 | { 743 | } 744 | --------------------------------------------------------------------------------