skysh: Add `--eval` for running queries directly

next
Sayan Nandan 7 months ago
parent 2670e5a974
commit ebd04a557e
No known key found for this signature in database
GPG Key ID: 0EBD769024B24F0A

@ -14,6 +14,7 @@ OPTIONS:
--user Set the user for this client session --user Set the user for this client session
--password Set the password for this client session --password Set the password for this client session
--tls-cert Set the TLS certificate to use (for TLS endpoints) --tls-cert Set the TLS certificate to use (for TLS endpoints)
--eval Execute and print the query (password must be set)
NOTES: NOTES:
- skysh will also look for the `{password_env_var}` environment variable - skysh will also look for the `{password_env_var}` environment variable

@ -68,6 +68,7 @@ pub enum ClientConfigKind {
pub enum Task { pub enum Task {
HelpMessage(String), HelpMessage(String),
OpenShell(ClientConfig), OpenShell(ClientConfig),
ExecOnce(ClientConfig, String),
} }
enum TaskInner { enum TaskInner {
@ -156,10 +157,13 @@ pub fn parse() -> CliResult<Task> {
} }
} }
}; };
let eval = args.remove("--eval");
if args.is_empty() { if args.is_empty() {
Ok(Task::OpenShell(ClientConfig::new( let client = ClientConfig::new(endpoint, username, password);
endpoint, username, password, match eval {
))) Some(query) => Ok(Task::ExecOnce(client, query)),
None => Ok(Task::OpenShell(client)),
}
} else { } else {
Err(CliError::ArgsErr(format!("found unknown arguments"))) Err(CliError::ArgsErr(format!("found unknown arguments")))
} }

@ -50,6 +50,16 @@ fn run() -> error::CliResult<()> {
match args::parse()? { match args::parse()? {
Task::HelpMessage(msg) => println!("{msg}"), Task::HelpMessage(msg) => println!("{msg}"),
Task::OpenShell(cfg) => repl::start(cfg)?, Task::OpenShell(cfg) => repl::start(cfg)?,
Task::ExecOnce(cfg, query) => {
let query = skytable::query!(query);
let resp = query::connect(
cfg,
false,
|mut c| Ok(c.query(&query)),
|mut c| Ok(c.query(&query)),
)??;
resp::format_response(resp, false, false);
}
} }
Ok(()) Ok(())
} }

@ -25,12 +25,46 @@
*/ */
use { use {
crate::error::{CliError, CliResult}, crate::{
args::{ClientConfig, ClientConfigKind},
error::{CliError, CliResult},
},
skytable::{ skytable::{
error::ClientResult, query::SQParam, response::Response, Connection, ConnectionTls, Query, error::ClientResult, query::SQParam, response::Response, Config, Connection, ConnectionTls,
Query,
}, },
}; };
pub fn connect<T>(
cfg: ClientConfig,
print_con_info: bool,
tcp_f: impl Fn(Connection) -> CliResult<T>,
tls_f: impl Fn(ConnectionTls) -> CliResult<T>,
) -> CliResult<T> {
match cfg.kind {
ClientConfigKind::Tcp(host, port) => {
let c = Config::new(&host, port, &cfg.username, &cfg.password).connect()?;
if print_con_info {
println!(
"Authenticated as '{}' on {}:{} over Skyhash/TCP\n---",
&cfg.username, &host, &port
);
}
tcp_f(c)
}
ClientConfigKind::Tls(host, port, cert) => {
let c = Config::new(&host, port, &cfg.username, &cfg.password).connect_tls(&cert)?;
if print_con_info {
println!(
"Authenticated as '{}' on {}:{} over Skyhash/TLS\n---",
&cfg.username, &host, &port
);
}
tls_f(c)
}
}
}
pub trait IsConnection { pub trait IsConnection {
fn execute_query(&mut self, q: Query) -> ClientResult<Response>; fn execute_query(&mut self, q: Query) -> ClientResult<Response>;
} }

@ -24,18 +24,15 @@
* *
*/ */
use crate::query::ExecKind;
use { use {
crate::{ crate::{
args::{ClientConfig, ClientConfigKind}, args::ClientConfig,
error::{CliError, CliResult}, error::{CliError, CliResult},
query::{self, IsConnection}, query::{self, ExecKind, IsConnection},
resp, resp,
}, },
crossterm::{cursor, execute, terminal}, crossterm::{cursor, execute, terminal},
rustyline::{config::Configurer, error::ReadlineError, DefaultEditor}, rustyline::{config::Configurer, error::ReadlineError, DefaultEditor},
skytable::Config,
std::io::{stdout, ErrorKind}, std::io::{stdout, ErrorKind},
}; };
@ -43,24 +40,7 @@ const SKYSH_HISTORY_FILE: &str = ".sky_history";
const TXT_WELCOME: &str = include_str!("../help_text/welcome"); const TXT_WELCOME: &str = include_str!("../help_text/welcome");
pub fn start(cfg: ClientConfig) -> CliResult<()> { pub fn start(cfg: ClientConfig) -> CliResult<()> {
match cfg.kind { query::connect(cfg, true, repl, repl)
ClientConfigKind::Tcp(host, port) => {
let c = Config::new(&host, port, &cfg.username, &cfg.password).connect()?;
println!(
"Authenticated as '{}' on {}:{} over Skyhash/TCP\n---",
&cfg.username, &host, &port
);
repl(c)
}
ClientConfigKind::Tls(host, port, cert) => {
let c = Config::new(&host, port, &cfg.username, &cfg.password).connect_tls(&cert)?;
println!(
"Authenticated as '{}' on {}:{} over Skyhash/TLS\n---",
&cfg.username, &host, &port
);
repl(c)
}
}
} }
fn repl<C: IsConnection>(mut con: C) -> CliResult<()> { fn repl<C: IsConnection>(mut con: C) -> CliResult<()> {
@ -123,7 +103,7 @@ fn repl<C: IsConnection>(mut con: C) -> CliResult<()> {
q q
} }
}; };
if resp::format_response(con.execute_query(q)?, special) { if resp::format_response(con.execute_query(q)?, special, true) {
if let Some(pr) = new_prompt { if let Some(pr) = new_prompt {
prompt = pr; prompt = pr;
} }

@ -29,28 +29,43 @@ use {
skytable::response::{Response, Row, Value}, skytable::response::{Response, Row, Value},
}; };
pub fn format_response(resp: Response, print_special: bool) -> bool { macro_rules! pprint {
($pretty:expr, $base:literal$(.$f:ident())*) => {
if $pretty {
let pretty = $base$(.$f())*;
println!("{}", pretty);
} else {
println!("{}", $base);
}
}
}
pub fn format_response(resp: Response, print_special: bool, pretty_format: bool) -> bool {
match resp { match resp {
Response::Empty => println!("{}", "(Okay)".cyan()), Response::Empty => pprint!(pretty_format, "(Okay)".cyan()),
Response::Error(e) => { Response::Error(e) => {
println!("{}", format!("(server error code: {e})").red()); println!("{}", format!("(server error code: {e})").red());
return false; return false;
} }
Response::Value(v) => { Response::Value(v) => {
print_value(v, print_special); print_value(v, print_special, pretty_format);
println!(); println!();
} }
Response::Row(r) => { Response::Row(r) => {
print_row(r); print_row(r, pretty_format);
println!(); println!();
} }
Response::Rows(rows) => { Response::Rows(rows) => {
if rows.is_empty() { if rows.is_empty() {
println!("{}", "[0 rows returned]".grey().italic()); pprint!(pretty_format, "[0 rows returned]".grey().italic());
} else { } else {
for (i, row) in rows.into_iter().enumerate().map(|(i, r)| (i + 1, r)) { for (i, row) in rows.into_iter().enumerate().map(|(i, r)| (i + 1, r)) {
print!("{} ", format!("({i})").grey().bold()); if pretty_format {
print_row(row); println!("{}", "({i})".grey().bold())
} else {
println!("({i})")
}
print_row(row, pretty_format);
println!(); println!();
} }
} }
@ -59,11 +74,11 @@ pub fn format_response(resp: Response, print_special: bool) -> bool {
true true
} }
fn print_row(r: Row) { fn print_row(r: Row, pretty_format: bool) {
print!("("); print!("(");
let mut columns = r.into_values().into_iter().peekable(); let mut columns = r.into_values().into_iter().peekable();
while let Some(cell) = columns.next() { while let Some(cell) = columns.next() {
print_value(cell, false); print_value(cell, false, pretty_format);
if columns.peek().is_some() { if columns.peek().is_some() {
print!(", "); print!(", ");
} }
@ -71,10 +86,10 @@ fn print_row(r: Row) {
print!(")"); print!(")");
} }
fn print_value(v: Value, print_special: bool) { fn print_value(v: Value, print_special: bool, pretty_format: bool) {
match v { match v {
Value::Null => print!("{}", "null".grey().italic()), Value::Null => pprint!(pretty_format, "null".grey().italic()),
Value::String(s) => print_string(&s, print_special), Value::String(s) => print_string(&s, print_special, pretty_format),
Value::Binary(b) => print_binary(&b), Value::Binary(b) => print_binary(&b),
Value::Bool(b) => print!("{b}"), Value::Bool(b) => print!("{b}"),
Value::UInt8(i) => print!("{i}"), Value::UInt8(i) => print!("{i}"),
@ -91,7 +106,7 @@ fn print_value(v: Value, print_special: bool) {
print!("["); print!("[");
let mut items = items.into_iter().peekable(); let mut items = items.into_iter().peekable();
while let Some(item) = items.next() { while let Some(item) = items.next() {
print_value(item, print_special); print_value(item, print_special, pretty_format);
if items.peek().is_some() { if items.peek().is_some() {
print!(", "); print!(", ");
} }
@ -113,22 +128,26 @@ fn print_binary(b: &[u8]) {
print!("]"); print!("]");
} }
fn print_string(s: &str, print_special: bool) { fn print_string(s: &str, print_special: bool, pretty_format: bool) {
if print_special { if !pretty_format {
print!("{}", s.italic().grey()); print!("{s}");
} else { } else {
print!("\""); if print_special {
for ch in s.chars() { print!("{}", s.italic().grey());
if ch == '"' { } else {
print!("\\{ch}"); print!("\"");
} else if ch == '\t' { for ch in s.chars() {
print!("\\t"); if ch == '"' {
} else if ch == '\n' { print!("\\{ch}");
print!("\\n"); } else if ch == '\t' {
} else { print!("\\t");
print!("{ch}"); } else if ch == '\n' {
print!("\\n");
} else {
print!("{ch}");
}
} }
print!("\"");
} }
print!("\"");
} }
} }

Loading…
Cancel
Save