initial commit
This commit is contained in:
parent
067934fd56
commit
c1b73d15fb
13 changed files with 728 additions and 0 deletions
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
.idea/
|
||||
.vscode/
|
||||
|
||||
.DS_Store
|
||||
|
||||
/target
|
||||
Cargo.lock
|
18
Cargo.toml
Normal file
18
Cargo.toml
Normal file
|
@ -0,0 +1,18 @@
|
|||
[package]
|
||||
name = "sonic-channel"
|
||||
version = "0.1.0-rc1"
|
||||
authors = ["Dmitriy Pleshevskiy <dmitriy@ideascup.me>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
lazy_static = "1.4.0"
|
||||
regex = "1.3.4"
|
||||
|
||||
[features]
|
||||
default = ["search"]
|
||||
|
||||
ingest = []
|
||||
search = []
|
||||
control = []
|
114
README.md
Normal file
114
README.md
Normal file
|
@ -0,0 +1,114 @@
|
|||
# Sonic Channel
|
||||
|
||||
Rust client for [sonic] search backend.
|
||||
|
||||
We recommend you start with the [documentation].
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
Add `sonic-channel = { version = "0.1" }` as a dependency in `Cargo.toml`.
|
||||
|
||||
`Cargo.toml` example:
|
||||
|
||||
```toml
|
||||
[package]
|
||||
name = "my-crate"
|
||||
version = "0.1.0"
|
||||
authors = ["Me <user@rust-lang.org>"]
|
||||
|
||||
[dependencies]
|
||||
sonic-channel = { version = "0.1" }
|
||||
```
|
||||
|
||||
|
||||
## Example usage
|
||||
|
||||
```rust
|
||||
use std::itconfig;
|
||||
use std::env;
|
||||
//use dotenv::dotenv;
|
||||
|
||||
config! {
|
||||
DEBUG: bool => false,
|
||||
|
||||
#[env_name = "APP_HOST"]
|
||||
HOST: String => "127.0.0.1",
|
||||
|
||||
DATABASE_URL < (
|
||||
"postgres://",
|
||||
POSTGRES_USERNAME => "user",
|
||||
":",
|
||||
POSTGRES_PASSWORD => "pass",
|
||||
"@",
|
||||
POSTGRES_HOST => "localhost:5432",
|
||||
"/",
|
||||
POSTGRES_DB => "test",
|
||||
),
|
||||
|
||||
APP {
|
||||
static BASE_URL => "/api", // &'static str by default
|
||||
|
||||
ARTICLE {
|
||||
static PER_PAGE: u32 => 15,
|
||||
}
|
||||
|
||||
#[cfg(feature = "companies")]
|
||||
COMPANY {
|
||||
#[env_name = "INSTITUTIONS_PER_PAGE"]
|
||||
static PER_PAGE: u32 => 15,
|
||||
}
|
||||
}
|
||||
|
||||
FEATURE {
|
||||
NEW_MENU: bool => false,
|
||||
|
||||
COMPANY {
|
||||
PROFILE: bool => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main () {
|
||||
// dotenv().expect("dotenv setup to be successful");
|
||||
// or
|
||||
env::set_var("FEATURE_NEW_MENU", "t");
|
||||
|
||||
config::init();
|
||||
assert_eq!(config::HOST(), String::from("127.0.0.1"));
|
||||
assert_eq!(config::DATABASE_URL(), String::from("postgres://user:pass@localhost:5432/test"));
|
||||
assert_eq!(config::APP:ARTICLE:PER_PAGE(), 15);
|
||||
assert_eq!(config::FEATURE::NEW_MENU(), true);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Macro is an optional feature, disabled by default. You can use this library without macro
|
||||
|
||||
```rust
|
||||
use itconfig::*;
|
||||
use std::env;
|
||||
// use dotenv::dotenv;
|
||||
|
||||
fn main() {
|
||||
// dotenv().expect("dotenv setup to be successful");
|
||||
// or
|
||||
env::set_var("DATABASE_URL", "postgres://127.0.0.1:5432/test");
|
||||
|
||||
let database_url = get_env::<String>("DATABASE_URL").unwrap();
|
||||
let new_profile: bool = get_env_or_default("FEATURE_NEW_PROFILE", false);
|
||||
let articles_per_page: u32 = get_env_or_set_default("ARTICLES_PER_PAGE", 10);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Available features
|
||||
|
||||
* **default** - ["search"]
|
||||
* **search** - Add sonic search mode with methods
|
||||
* **ignite** - Add sonic ignite mode with methods
|
||||
* **control** - Add sonic control mode with methods
|
||||
|
||||
|
||||
[sonic]: https://github.com/valeriansaliou/sonic
|
||||
[documentation]: https://docs.rs/sonic-channel
|
214
src/channel.rs
Normal file
214
src/channel.rs
Normal file
|
@ -0,0 +1,214 @@
|
|||
use crate::commands::*;
|
||||
use crate::errors::SonicError;
|
||||
use std::fmt;
|
||||
use std::io::{BufRead, BufReader, BufWriter, Write};
|
||||
use std::net::{TcpStream, ToSocketAddrs};
|
||||
|
||||
const DEFAULT_SONIC_PROTOCOL_VERSION: usize = 1;
|
||||
const MAX_LINE_BUFFER_SIZE: usize = 20000;
|
||||
const UNINITIALIZED_MODE_MAX_BUFFER_SIZE: usize = 200;
|
||||
const BUFFER_LINE_SEPARATOR: u8 = '\n' as u8;
|
||||
|
||||
macro_rules! init_commands {
|
||||
(
|
||||
$(
|
||||
use $cmd_name:ident
|
||||
for fn $fn_name:ident
|
||||
$(<$($lt:lifetime)+>)?
|
||||
($($args:tt)*)
|
||||
;
|
||||
)*
|
||||
) => {
|
||||
$(init_commands!(use $cmd_name for fn $fn_name $(<$($lt)+>)? ($($args)*));)*
|
||||
};
|
||||
|
||||
(use $cmd_name:ident for fn $fn_name:ident $(<$($lt:lifetime)+>)? ($($arg_name:ident : $arg_type:ty,)*)) => {
|
||||
pub fn $fn_name $(<$($lt)+>)? (
|
||||
&self,
|
||||
$($arg_name: $arg_type),*
|
||||
) -> Result<
|
||||
<$cmd_name as crate::commands::StreamCommand>::Response,
|
||||
crate::errors::SonicError
|
||||
> {
|
||||
let command = $cmd_name { $($arg_name,)* ..Default::default() };
|
||||
self.run_command(command)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum ChannelMode {
|
||||
#[cfg(feature = "search")]
|
||||
Search,
|
||||
|
||||
#[cfg(feature = "ingest")]
|
||||
Ingest,
|
||||
|
||||
#[cfg(feature = "control")]
|
||||
Control,
|
||||
}
|
||||
|
||||
impl ChannelMode {
|
||||
pub fn to_str(&self) -> &str {
|
||||
#[cfg(any(feature = "ingest", feature = "search", feature = "control"))]
|
||||
match self {
|
||||
#[cfg(feature = "search")]
|
||||
ChannelMode::Search => "search",
|
||||
|
||||
#[cfg(feature = "ingest")]
|
||||
ChannelMode::Ingest => "ingest",
|
||||
|
||||
#[cfg(feature = "control")]
|
||||
ChannelMode::Control => "control",
|
||||
}
|
||||
|
||||
// Actually we'll not see this text because we cannot call this function for enum
|
||||
// without enum value, but Rust compiler want this case.
|
||||
#[cfg(all(
|
||||
not(feature = "ingest"),
|
||||
not(feature = "search"),
|
||||
not(feature = "control")
|
||||
))]
|
||||
"unitialized"
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ChannelMode {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
write!(f, "{}", self.to_str())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SonicChannel {
|
||||
stream: TcpStream,
|
||||
mode: Option<ChannelMode>, // None – Uninitialized mode
|
||||
max_buffer_size: usize,
|
||||
protocol_version: usize,
|
||||
}
|
||||
|
||||
impl SonicChannel {
|
||||
pub fn connect<A: ToSocketAddrs>(addr: A) -> Result<Self, SonicError> {
|
||||
let stream = TcpStream::connect(addr).map_err(|_| SonicError::ConnectToServer)?;
|
||||
|
||||
let channel = SonicChannel {
|
||||
stream,
|
||||
mode: None,
|
||||
max_buffer_size: UNINITIALIZED_MODE_MAX_BUFFER_SIZE,
|
||||
protocol_version: DEFAULT_SONIC_PROTOCOL_VERSION,
|
||||
};
|
||||
|
||||
let message = channel.read(1)?;
|
||||
dbg!(&message);
|
||||
// TODO: need to add support for versions
|
||||
if message.starts_with("CONNECTED") {
|
||||
Ok(channel)
|
||||
} else {
|
||||
Err(SonicError::ConnectToServer)
|
||||
}
|
||||
}
|
||||
|
||||
fn write<SC: StreamCommand>(&self, command: &SC) -> Result<(), SonicError> {
|
||||
let mut writer = BufWriter::with_capacity(self.max_buffer_size, &self.stream);
|
||||
let message = command.message();
|
||||
dbg!(&message);
|
||||
writer
|
||||
.write_all(message.as_bytes())
|
||||
.map_err(|_| SonicError::WriteToStream)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read(&self, max_read_lines: usize) -> Result<String, SonicError> {
|
||||
let mut reader = BufReader::with_capacity(self.max_buffer_size, &self.stream);
|
||||
let mut message = String::new();
|
||||
|
||||
let mut lines_read = 0;
|
||||
while lines_read < max_read_lines {
|
||||
reader
|
||||
.read_line(&mut message)
|
||||
.map_err(|_| SonicError::ReadStream)?;
|
||||
lines_read += 1;
|
||||
}
|
||||
|
||||
Ok(message)
|
||||
}
|
||||
|
||||
pub fn run_command<SC: StreamCommand>(&self, command: SC) -> Result<SC::Response, SonicError> {
|
||||
self.write(&command)?;
|
||||
let message = self.read(SC::READ_LINES_COUNT)?;
|
||||
command.receive(message)
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "ingest", feature = "search", feature = "control"))]
|
||||
pub fn start<S: ToString>(&mut self, mode: ChannelMode, password: S) -> Result<(), SonicError> {
|
||||
if self.mode.is_some() {
|
||||
return Err(SonicError::RunCommand);
|
||||
}
|
||||
|
||||
let command = StartCommand {
|
||||
mode,
|
||||
password: password.to_string(),
|
||||
};
|
||||
let response = self.run_command(command)?;
|
||||
|
||||
self.max_buffer_size = response.max_buffer_size;
|
||||
self.protocol_version = response.protocol_version;
|
||||
self.mode = Some(response.mode);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
init_commands! {
|
||||
use QuitCommand for fn quit();
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "ingest", feature = "search", feature = "control"))]
|
||||
init_commands! {
|
||||
use PingCommand for fn ping();
|
||||
}
|
||||
|
||||
#[cfg(feature = "ingest")]
|
||||
init_commands! {
|
||||
use PushCommand for fn push<'a>(
|
||||
collection: &'a str,
|
||||
bucket: &'a str,
|
||||
object: &'a str,
|
||||
text: &'a str,
|
||||
);
|
||||
|
||||
use PushCommand for fn push_with_locale<'a>(
|
||||
collection: &'a str,
|
||||
bucket: &'a str,
|
||||
object: &'a str,
|
||||
text: &'a str,
|
||||
locale: Option<&'a str>,
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "search")]
|
||||
init_commands! {
|
||||
use QueryCommand for fn query<'a>(
|
||||
collection: &'a str,
|
||||
bucket: &'a str,
|
||||
terms: &'a str,
|
||||
);
|
||||
|
||||
use QueryCommand for fn query_with_limit<'a>(
|
||||
collection: &'a str,
|
||||
bucket: &'a str,
|
||||
terms: &'a str,
|
||||
limit: Option<usize>,
|
||||
);
|
||||
|
||||
use QueryCommand for fn query_with_limit_and_offset<'a>(
|
||||
collection: &'a str,
|
||||
bucket: &'a str,
|
||||
terms: &'a str,
|
||||
limit: Option<usize>,
|
||||
offset: Option<usize>,
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "control")]
|
||||
init_commands! {}
|
||||
}
|
28
src/commands/mod.rs
Normal file
28
src/commands/mod.rs
Normal file
|
@ -0,0 +1,28 @@
|
|||
mod quit;
|
||||
mod start;
|
||||
mod ping;
|
||||
#[cfg(feature = "ingest")]
|
||||
mod push;
|
||||
#[cfg(feature = "search")]
|
||||
mod query;
|
||||
|
||||
pub use quit::QuitCommand;
|
||||
pub use start::StartCommand;
|
||||
|
||||
pub use ping::PingCommand;
|
||||
#[cfg(feature = "ingest")]
|
||||
pub use push::PushCommand;
|
||||
#[cfg(feature = "search")]
|
||||
pub use query::QueryCommand;
|
||||
|
||||
use crate::errors::SonicError;
|
||||
|
||||
pub trait StreamCommand {
|
||||
type Response;
|
||||
|
||||
const READ_LINES_COUNT: usize = 1;
|
||||
|
||||
fn message(&self) -> String;
|
||||
|
||||
fn receive(&self, message: String) -> Result<Self::Response, SonicError>;
|
||||
}
|
18
src/commands/ping.rs
Normal file
18
src/commands/ping.rs
Normal file
|
@ -0,0 +1,18 @@
|
|||
use super::StreamCommand;
|
||||
use crate::errors::SonicError;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct PingCommand;
|
||||
|
||||
impl StreamCommand for PingCommand {
|
||||
type Response = bool;
|
||||
|
||||
fn message(&self) -> String {
|
||||
String::from("PING\r\n")
|
||||
}
|
||||
|
||||
fn receive(&self, message: String) -> Result<<Self as StreamCommand>::Response, SonicError> {
|
||||
dbg!(&message);
|
||||
Ok(message == "PONG\r\n")
|
||||
}
|
||||
}
|
32
src/commands/push.rs
Normal file
32
src/commands/push.rs
Normal file
|
@ -0,0 +1,32 @@
|
|||
use super::StreamCommand;
|
||||
use crate::errors::SonicError;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct PushCommand<'a> {
|
||||
pub collection: &'a str,
|
||||
pub bucket: &'a str,
|
||||
pub object: &'a str,
|
||||
pub text: &'a str,
|
||||
pub locale: Option<&'a str>,
|
||||
}
|
||||
|
||||
impl StreamCommand for PushCommand<'_> {
|
||||
type Response = bool;
|
||||
|
||||
fn message(&self) -> String {
|
||||
let mut message = format!(
|
||||
r#"PUSH {} {} {} "{}""#,
|
||||
self.collection, self.bucket, self.object, self.text
|
||||
);
|
||||
if let Some(locale) = self.locale.as_ref() {
|
||||
message.push_str(&format!(" LANG({})", locale));
|
||||
}
|
||||
message.push_str("\r\n");
|
||||
message
|
||||
}
|
||||
|
||||
fn receive(&self, message: String) -> Result<<Self as StreamCommand>::Response, SonicError> {
|
||||
dbg!(&message);
|
||||
Ok(message == "OK\r\n")
|
||||
}
|
||||
}
|
68
src/commands/query.rs
Normal file
68
src/commands/query.rs
Normal file
|
@ -0,0 +1,68 @@
|
|||
use super::StreamCommand;
|
||||
use crate::errors::SonicError;
|
||||
use regex::Regex;
|
||||
|
||||
const RE_QUERY_RECEIVED_MESSAGE: &str = r"(?x)
|
||||
^PENDING\s(?P<pending_query_id>\w+)\r\n
|
||||
EVENT\sQUERY\s(?P<event_query_id>\w+)\s(?P<objects>.*?)\r\n$
|
||||
";
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct QueryCommand<'a> {
|
||||
pub collection: &'a str,
|
||||
pub bucket: &'a str,
|
||||
pub terms: &'a str,
|
||||
pub limit: Option<usize>,
|
||||
pub offset: Option<usize>,
|
||||
}
|
||||
|
||||
impl StreamCommand for QueryCommand<'_> {
|
||||
type Response = Vec<String>;
|
||||
|
||||
const READ_LINES_COUNT: usize = 2;
|
||||
|
||||
fn message(&self) -> String {
|
||||
let mut message = format!(
|
||||
r#"QUERY {} {} "{}""#,
|
||||
self.collection, self.bucket, self.terms
|
||||
);
|
||||
if let Some(limit) = self.limit.as_ref() {
|
||||
message.push_str(&format!(" LIMIT({})", limit));
|
||||
}
|
||||
if let Some(offset) = self.offset.as_ref() {
|
||||
message.push_str(&format!(" OFFSET({})", offset));
|
||||
}
|
||||
message.push_str("\r\n");
|
||||
message
|
||||
}
|
||||
|
||||
fn receive(&self, message: String) -> Result<Self::Response, SonicError> {
|
||||
lazy_static! {
|
||||
static ref RE: Regex = Regex::new(RE_QUERY_RECEIVED_MESSAGE).unwrap();
|
||||
}
|
||||
|
||||
dbg!(&message);
|
||||
|
||||
match RE.captures(&message) {
|
||||
None => Err(SonicError::QueryResponseError(
|
||||
"Sonic response are wrong. Please write issue to github.",
|
||||
)),
|
||||
Some(caps) => {
|
||||
if &caps["pending_query_id"] != &caps["event_query_id"] {
|
||||
Err(SonicError::QueryResponseError(
|
||||
"Pending id and event id don't match",
|
||||
))
|
||||
} else if caps["objects"].is_empty() {
|
||||
Ok(vec![])
|
||||
} else {
|
||||
let objects = caps["objects"]
|
||||
.split(" ")
|
||||
.map(String::from)
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
Ok(objects)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
18
src/commands/quit.rs
Normal file
18
src/commands/quit.rs
Normal file
|
@ -0,0 +1,18 @@
|
|||
use super::StreamCommand;
|
||||
use crate::errors::SonicError;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct QuitCommand;
|
||||
|
||||
impl StreamCommand for QuitCommand {
|
||||
type Response = bool;
|
||||
|
||||
fn message(&self) -> String {
|
||||
String::from("QUIT\r\n")
|
||||
}
|
||||
|
||||
fn receive(&self, message: String) -> Result<<Self as StreamCommand>::Response, SonicError> {
|
||||
dbg!(&message);
|
||||
Ok(message.starts_with("ENDED "))
|
||||
}
|
||||
}
|
62
src/commands/start.rs
Normal file
62
src/commands/start.rs
Normal file
|
@ -0,0 +1,62 @@
|
|||
use super::StreamCommand;
|
||||
use crate::channel::ChannelMode;
|
||||
use crate::errors::SonicError;
|
||||
use regex::Regex;
|
||||
|
||||
const RE_START_RECEIVED_MESSAGE: &str = r"(?x)
|
||||
STARTED
|
||||
\s # started with mode
|
||||
(?P<mode>search|ingest|control)
|
||||
\s # wich protocol used
|
||||
protocol\((?P<protocol>\d+)\)
|
||||
\s # maximum buffer size
|
||||
buffer\((?P<buffer_size>\d+)\)
|
||||
";
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct StartCommand {
|
||||
pub mode: ChannelMode,
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
pub struct StartCommandResponse {
|
||||
pub protocol_version: usize,
|
||||
pub max_buffer_size: usize,
|
||||
pub mode: ChannelMode,
|
||||
}
|
||||
|
||||
impl StreamCommand for StartCommand {
|
||||
type Response = StartCommandResponse;
|
||||
|
||||
fn message(&self) -> String {
|
||||
format!("START {} {}\r\n", self.mode, self.password)
|
||||
}
|
||||
|
||||
fn receive(&self, message: String) -> Result<Self::Response, SonicError> {
|
||||
lazy_static! {
|
||||
static ref RE: Regex = Regex::new(RE_START_RECEIVED_MESSAGE).unwrap();
|
||||
}
|
||||
|
||||
dbg!(&message);
|
||||
|
||||
match RE.captures(&message) {
|
||||
None => Err(SonicError::SwitchMode),
|
||||
Some(caps) => {
|
||||
if self.mode.to_str() != &caps["mode"] {
|
||||
return Err(SonicError::SwitchMode);
|
||||
}
|
||||
|
||||
let protocol_version: usize =
|
||||
caps["protocol"].parse().expect("Must be digit by regex");
|
||||
let max_buffer_size: usize =
|
||||
caps["buffer_size"].parse().expect("Must be digit by regex");
|
||||
|
||||
Ok(StartCommandResponse {
|
||||
protocol_version,
|
||||
max_buffer_size,
|
||||
mode: self.mode,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
27
src/errors.rs
Normal file
27
src/errors.rs
Normal file
|
@ -0,0 +1,27 @@
|
|||
use std::fmt;
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SonicError {
|
||||
ConnectToServer,
|
||||
WriteToStream,
|
||||
ReadStream,
|
||||
SwitchMode,
|
||||
RunCommand,
|
||||
QueryResponseError(&'static str),
|
||||
}
|
||||
|
||||
impl fmt::Display for SonicError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
let message = match self {
|
||||
SonicError::ConnectToServer => String::from("Cannot connect to server"),
|
||||
SonicError::WriteToStream => String::from("Cannot write data to stream"),
|
||||
SonicError::ReadStream => String::from("Cannot read sonic response from stream"),
|
||||
SonicError::SwitchMode => String::from("Cannot switch channel mode"),
|
||||
SonicError::RunCommand => String::from("Cannot run command in current mode"),
|
||||
SonicError::QueryResponseError(message) => format!("Error in query response: {}", message)
|
||||
};
|
||||
|
||||
write!(f, "{}", message)
|
||||
}
|
||||
}
|
27
src/lib.rs
Normal file
27
src/lib.rs
Normal file
|
@ -0,0 +1,27 @@
|
|||
#![allow(dead_code)]
|
||||
|
||||
mod channel;
|
||||
mod commands;
|
||||
mod errors;
|
||||
|
||||
pub use channel::*;
|
||||
pub use commands::*;
|
||||
pub use errors::*;
|
||||
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
extern crate regex;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::channel::ChannelMode;
|
||||
|
||||
#[test]
|
||||
fn format_channel_enums() {
|
||||
assert_eq!(format!("{}", ChannelMode::Search), String::from("search"));
|
||||
assert_eq!(format!("{}", ChannelMode::Ingest), String::from("ingest"));
|
||||
assert_eq!(format!("{}", ChannelMode::Control), String::from("control"));
|
||||
}
|
||||
|
||||
//TODO: write tests with sonic server
|
||||
}
|
95
src/main.rs
Normal file
95
src/main.rs
Normal file
|
@ -0,0 +1,95 @@
|
|||
use sonic_channel::*;
|
||||
|
||||
|
||||
fn main() -> Result<(), SonicError> {
|
||||
// let mut stream = TcpStream::connect("localhost:1491")?;
|
||||
|
||||
// let mut buffer = [0; 128];
|
||||
// stream.read(&mut buffer)?;
|
||||
|
||||
// let res = String::from_utf8(buffer.to_vec()).expect("Cannot convert response buffer to utf8");
|
||||
// dbg!(res);
|
||||
|
||||
// let command = "START search SecretPassword";
|
||||
|
||||
// stream.write(command.as_bytes())?;
|
||||
|
||||
// let mut buffer = [0; 1000];
|
||||
// loop {
|
||||
// let mut line_buffer = [0; 128];
|
||||
// let res_length = stream.read(&mut line_buffer)?;
|
||||
|
||||
// }
|
||||
// stream.read(&mut buffer)?;
|
||||
|
||||
// let res = String::from_utf8(buffer.to_vec()).expect("Cannot convert response buffer to utf8");
|
||||
|
||||
// dbg!(res);
|
||||
|
||||
// let mut buffer = [0; 128];
|
||||
// stream.read(&mut buffer)?;
|
||||
// let res = String::from_utf8(buffer.to_vec()).expect("Cannot convert response buffer to utf8");
|
||||
// dbg!(res);
|
||||
|
||||
|
||||
// let (tx, rx) = mpsc::channel();
|
||||
|
||||
// thread::spawn(|| {
|
||||
// let listener = TcpListener::bind("localhost:7777").expect("Should open connection for tests");
|
||||
|
||||
// for stream in listener.incoming() {
|
||||
// let mut stream = stream.expect("Should connect socket successfully");
|
||||
// stream.write("CONNECTED\r\n".as_bytes()).unwrap();
|
||||
|
||||
// loop {
|
||||
// let mut buffer = [0; 60];
|
||||
// println!("wait from client");
|
||||
// let n = stream.read(&mut buffer).expect("Cannot read stream from client");
|
||||
|
||||
// let message = String::from_utf8(buffer[0..n].to_vec()).expect("Should convert response buffer to utf8");
|
||||
// dbg!(&message);
|
||||
|
||||
// if message.starts_with("START") {
|
||||
// stream.write("STARTED search protocol(1) buffer(20000)\r\n".as_bytes()).unwrap();
|
||||
// } else if message.starts_with("PING") {
|
||||
// stream.write("PONG\r\n".as_bytes()).unwrap();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
|
||||
|
||||
// let client = thread::spawn(|| {
|
||||
// let mut stream = TcpStream::connect("localhost:7777").expect("Should open connection for tests");
|
||||
|
||||
// let message = "hello world".as_bytes();
|
||||
// stream.write_all(message).expect("Should write to stream successfully");
|
||||
// });
|
||||
|
||||
// client.join().unwrap();
|
||||
// server.join().unwrap();
|
||||
|
||||
// let received = rx.recv().expect("Should recieve message from thread");
|
||||
|
||||
// dbg!(received);
|
||||
|
||||
let mut channel = SonicChannel::connect("localhost:1491")?;
|
||||
// std::thread::sleep(std::time::Duration::from_secs(5));
|
||||
// let mut channel = SonicChannel::connect("localhost:7777")?;
|
||||
// channel.start(ChannelMode::Ingest, "SecretPassword")?;
|
||||
// std::thread::sleep(std::time::Duration::from_secs(1));
|
||||
// let pong = channel.ping()?;
|
||||
// dbg!(pong);
|
||||
// let pushed = channel.push("collection", "bucket", "user:1", "my best recipe")?;
|
||||
// dbg!(pushed);
|
||||
|
||||
channel.start(ChannelMode::Search, "SecretPassword")?;
|
||||
std::thread::sleep(std::time::Duration::from_secs(1));
|
||||
let pong = channel.ping()?;
|
||||
dbg!(pong);
|
||||
|
||||
let objects = channel.query("collection", "bucket", "recipe")?;
|
||||
dbg!(objects);
|
||||
|
||||
Ok(())
|
||||
}
|
Loading…
Reference in a new issue