2022-08-06 10:34:43 +00:00
|
|
|
use crate::err::Error;
|
|
|
|
use rustyline::error::ReadlineError;
|
2023-03-25 20:49:00 +00:00
|
|
|
use rustyline::DefaultEditor;
|
2023-01-07 08:32:18 +00:00
|
|
|
use surrealdb::engine::any::connect;
|
2022-12-30 21:27:19 +00:00
|
|
|
use surrealdb::error::Api as ApiError;
|
|
|
|
use surrealdb::opt::auth::Root;
|
|
|
|
use surrealdb::sql;
|
|
|
|
use surrealdb::sql::Statement;
|
2023-03-30 10:41:44 +00:00
|
|
|
use surrealdb::sql::Value;
|
2022-12-30 21:27:19 +00:00
|
|
|
use surrealdb::Error as SurrealError;
|
|
|
|
use surrealdb::Response;
|
2022-08-06 10:34:43 +00:00
|
|
|
|
2022-12-31 08:02:41 +00:00
|
|
|
#[tokio::main]
|
2022-12-30 21:27:19 +00:00
|
|
|
pub async fn init(matches: &clap::ArgMatches) -> Result<(), Error> {
|
2023-03-29 18:16:18 +00:00
|
|
|
// Initialize opentelemetry and logging
|
|
|
|
crate::o11y::builder().with_log_level("warn").init();
|
2022-08-06 10:34:43 +00:00
|
|
|
// Parse all other cli arguments
|
2022-12-30 21:27:19 +00:00
|
|
|
let username = matches.value_of("user").unwrap();
|
|
|
|
let password = matches.value_of("pass").unwrap();
|
|
|
|
let endpoint = matches.value_of("conn").unwrap();
|
|
|
|
let mut ns = matches.value_of("ns").map(str::to_string);
|
|
|
|
let mut db = matches.value_of("db").map(str::to_string);
|
2022-08-06 10:34:43 +00:00
|
|
|
// If we should pretty-print responses
|
|
|
|
let pretty = matches.is_present("pretty");
|
2022-12-31 08:02:41 +00:00
|
|
|
// Connect to the database engine
|
2022-12-30 21:27:19 +00:00
|
|
|
let client = connect(endpoint).await?;
|
2023-03-07 09:53:56 +00:00
|
|
|
// Sign in to the server if the specified database engine supports it
|
2022-12-30 21:27:19 +00:00
|
|
|
let root = Root {
|
|
|
|
username,
|
|
|
|
password,
|
2022-12-20 10:30:06 +00:00
|
|
|
};
|
2022-12-30 21:27:19 +00:00
|
|
|
if let Err(error) = client.signin(root).await {
|
|
|
|
match error {
|
|
|
|
// Authentication not supported by this engine, we can safely continue
|
|
|
|
SurrealError::Api(ApiError::AuthNotSupported) => {}
|
|
|
|
error => {
|
|
|
|
return Err(error.into());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-08-06 10:34:43 +00:00
|
|
|
// Create a new terminal REPL
|
2023-03-25 20:49:00 +00:00
|
|
|
let mut rl = DefaultEditor::new().unwrap();
|
2022-08-06 10:34:43 +00:00
|
|
|
// Load the command-line history
|
|
|
|
let _ = rl.load_history("history.txt");
|
2022-12-30 21:27:19 +00:00
|
|
|
// Configure the prompt
|
|
|
|
let mut prompt = "> ".to_owned();
|
2022-08-06 10:34:43 +00:00
|
|
|
// Loop over each command-line input
|
|
|
|
loop {
|
2022-12-30 21:27:19 +00:00
|
|
|
// Use namespace / database if specified
|
2023-05-05 18:12:19 +00:00
|
|
|
match (&ns, &db) {
|
|
|
|
(Some(namespace), Some(database)) => {
|
|
|
|
match client.use_ns(namespace).use_db(database).await {
|
|
|
|
Ok(()) => {
|
|
|
|
prompt = format!("{namespace}/{database}> ");
|
|
|
|
}
|
|
|
|
Err(error) => eprintln!("{error}"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
(Some(namespace), None) => match client.use_ns(namespace).await {
|
2022-12-30 21:27:19 +00:00
|
|
|
Ok(()) => {
|
2023-05-05 18:12:19 +00:00
|
|
|
prompt = format!("{namespace}> ");
|
2022-12-30 21:27:19 +00:00
|
|
|
}
|
|
|
|
Err(error) => eprintln!("{error}"),
|
2023-05-05 18:12:19 +00:00
|
|
|
},
|
|
|
|
(None, Some(database)) => match client.use_db(database).await {
|
|
|
|
Ok(()) => {
|
|
|
|
prompt = format!("/{database}> ");
|
|
|
|
}
|
|
|
|
Err(error) => eprintln!("{error}"),
|
|
|
|
},
|
|
|
|
(None, None) => {}
|
2022-12-30 21:27:19 +00:00
|
|
|
}
|
2022-08-06 10:34:43 +00:00
|
|
|
// Prompt the user to input SQL
|
2022-12-30 21:27:19 +00:00
|
|
|
let readline = rl.readline(&prompt);
|
2022-08-06 10:34:43 +00:00
|
|
|
// Check the user input
|
|
|
|
match readline {
|
|
|
|
// The user typed a query
|
|
|
|
Ok(line) => {
|
2022-09-14 23:39:01 +00:00
|
|
|
// Ignore all empty lines
|
|
|
|
if line.is_empty() {
|
|
|
|
continue;
|
|
|
|
}
|
2022-08-06 10:34:43 +00:00
|
|
|
// Add the entry to the history
|
2023-03-25 20:49:00 +00:00
|
|
|
if let Err(e) = rl.add_history_entry(line.as_str()) {
|
|
|
|
eprintln!("{e}");
|
|
|
|
}
|
2022-12-20 10:30:06 +00:00
|
|
|
// Complete the request
|
2022-12-30 21:27:19 +00:00
|
|
|
match sql::parse(&line) {
|
|
|
|
Ok(query) => {
|
|
|
|
for statement in query.iter() {
|
|
|
|
match statement {
|
|
|
|
Statement::Use(stmt) => {
|
|
|
|
if let Some(namespace) = &stmt.ns {
|
|
|
|
ns = Some(namespace.clone());
|
|
|
|
}
|
|
|
|
if let Some(database) = &stmt.db {
|
|
|
|
db = Some(database.clone());
|
|
|
|
}
|
|
|
|
}
|
2023-03-25 20:49:00 +00:00
|
|
|
Statement::Set(stmt) => {
|
|
|
|
if let Err(e) = client.set(&stmt.name, &stmt.what).await {
|
2023-04-20 18:20:50 +00:00
|
|
|
eprintln!("{e}\n");
|
2022-12-30 21:27:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let res = client.query(query).await;
|
|
|
|
// Get the request response
|
|
|
|
match process(pretty, res) {
|
2023-04-14 18:41:37 +00:00
|
|
|
Ok(v) => {
|
2023-04-20 18:20:50 +00:00
|
|
|
println!("{v}\n");
|
2023-04-14 18:41:37 +00:00
|
|
|
}
|
|
|
|
Err(e) => {
|
2023-04-20 18:20:50 +00:00
|
|
|
eprintln!("{e}\n");
|
2023-04-14 18:41:37 +00:00
|
|
|
}
|
2022-12-30 21:27:19 +00:00
|
|
|
}
|
|
|
|
}
|
2023-04-14 18:41:37 +00:00
|
|
|
Err(e) => {
|
2023-04-20 18:20:50 +00:00
|
|
|
eprintln!("{e}\n");
|
2023-04-14 18:41:37 +00:00
|
|
|
}
|
2022-08-06 10:34:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// The user types CTRL-C
|
|
|
|
Err(ReadlineError::Interrupted) => {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// The user typed CTRL-D
|
|
|
|
Err(ReadlineError::Eof) => {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// There was en error
|
2023-03-25 20:49:00 +00:00
|
|
|
Err(e) => {
|
|
|
|
eprintln!("Error: {e:?}");
|
2022-08-06 10:34:43 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Save the inputs to the history
|
|
|
|
let _ = rl.save_history("history.txt");
|
|
|
|
// Everything OK
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2022-12-30 21:27:19 +00:00
|
|
|
fn process(pretty: bool, res: surrealdb::Result<Response>) -> Result<String, Error> {
|
2023-04-20 18:20:50 +00:00
|
|
|
// Check query response for an error
|
|
|
|
let mut response = res?;
|
|
|
|
// Get the number of statements the query contained
|
|
|
|
let num_statements = response.num_statements();
|
|
|
|
// Prepare a single value from the query response
|
|
|
|
let value = if num_statements > 1 {
|
|
|
|
let mut output = Vec::<Value>::with_capacity(num_statements);
|
|
|
|
for index in 0..num_statements {
|
|
|
|
output.push(response.take(index)?);
|
|
|
|
}
|
|
|
|
Value::from(output)
|
|
|
|
} else {
|
|
|
|
response.take(0)?
|
2023-03-30 10:41:44 +00:00
|
|
|
};
|
2023-04-14 18:41:37 +00:00
|
|
|
// Check if we should prettify
|
|
|
|
Ok(match pretty {
|
|
|
|
// Don't prettify the response
|
|
|
|
false => value.to_string(),
|
|
|
|
// Yes prettify the response
|
|
|
|
true => format!("{value:#}"),
|
|
|
|
})
|
2022-08-06 10:34:43 +00:00
|
|
|
}
|