diff --git a/Cargo.lock b/Cargo.lock index a4c090c..5754e1d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,6 +26,56 @@ dependencies = [ "memchr", ] +[[package]] +name = "anstream" +version = "0.6.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.59.0", +] + [[package]] name = "anyhow" version = "1.0.93" @@ -208,6 +258,46 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[package]] +name = "clap" +version = "4.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" + [[package]] name = "clipboard-win" version = "5.4.0" @@ -217,6 +307,12 @@ dependencies = [ "error-code", ] +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + [[package]] name = "colored" version = "3.0.0" @@ -633,6 +729,12 @@ dependencies = [ "hashbrown 0.15.1", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "itertools" version = "0.13.0" @@ -825,6 +927,12 @@ version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +[[package]] +name = "once_cell_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" + [[package]] name = "parking_lot" version = "0.12.3" @@ -1220,6 +1328,7 @@ name = "simpledb-rs" version = "0.1.0" dependencies = [ "anyhow", + "clap", "colored", "enum_dispatch", "lalrpop", @@ -1280,6 +1389,12 @@ dependencies = [ "precomputed-hash", ] +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "syn" version = "2.0.85" diff --git a/Cargo.toml b/Cargo.toml index a193f51..ec25e76 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ tokio = { version = "1.0", features = ["rt-multi-thread", "macros"] } tonic = "0.12.3" rustyline = "16.0.0" colored = "3.0.0" +clap = { version = "4.5.4", features = ["derive"] } [dev-dependencies] serial_test = "2.0.0" diff --git a/src/client.rs b/src/client.rs index ada2d2f..2a61def 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,18 +1,38 @@ use std::io::{stdout, Write}; use std::path::{Path, PathBuf}; +use clap::Parser; use colored::Colorize; use rustyline::{error::ReadlineError, DefaultEditor}; use simpledb_rs::{ driver::{ - embedded::EmbeddedDriver, ConnectionControl, Driver, DriverControl, MetadataControl, - ResultSetControl, Statement, StatementControl, + embedded::EmbeddedDriver, network::driver::NetworkDriver, ConnectionControl, Driver, + DriverControl, MetadataControl, ResultSetControl, Statement, StatementControl, }, record::field::Type, }; +#[derive(Parser)] +#[command(name = "client")] +struct Args { + /// Enable verbose output + #[arg(long)] + verbose: bool, + + /// Network server host. If omitted, use embedded driver + #[arg(long)] + host: Option, + + /// Network server port + #[arg(long, default_value_t = 50051)] + port: u16, + + /// Database URL + db_url: Option, +} + trait ClientEditor { fn readline(&mut self, prompt: &str) -> Result; fn load_history + ?Sized>(&mut self, path: &P) -> rustyline::Result<()>; @@ -316,23 +336,19 @@ fn run_client( } fn main() -> Result<(), anyhow::Error> { + let args = Args::parse(); + let mut editor = DefaultEditor::new()?; - let mut db_url = None; - let mut verbose = false; - for arg in std::env::args().skip(1) { - if arg == "--verbose" { - verbose = true; - } else { - db_url = Some(arg); - } - } - simpledb_rs::config::set_verbose(verbose); - run_client( - Driver::Embedded(EmbeddedDriver::new()), - &mut editor, - &mut stdout(), - db_url.as_deref(), - ) + + simpledb_rs::config::set_verbose(args.verbose); + + let driver = if let Some(h) = args.host { + Driver::Network(NetworkDriver::new(&h, args.port)) + } else { + Driver::Embedded(EmbeddedDriver::new()) + }; + + run_client(driver, &mut editor, &mut stdout(), args.db_url.as_deref()) } #[cfg(test)] diff --git a/src/driver/network/connection.rs b/src/driver/network/connection.rs index faf57a0..e2b92b1 100644 --- a/src/driver/network/connection.rs +++ b/src/driver/network/connection.rs @@ -82,10 +82,7 @@ impl ConnectionService for RemoteConnection { .expect(&format!("Unknown connection_id: {}", connection_id)); connection.close().unwrap(); - self.embedded_connection_dict - .lock() - .unwrap() - .remove(&connection_id); + lock.remove(&connection_id); Ok(Response::new(ConnectionCloseResponse {})) } diff --git a/src/driver/network/driver.rs b/src/driver/network/driver.rs index e8c09ee..ac4f889 100644 --- a/src/driver/network/driver.rs +++ b/src/driver/network/driver.rs @@ -73,44 +73,44 @@ impl DriverService for RemoteDriver { pub struct NetworkDriver { runtime: Arc, + host: String, + port: u16, } impl NetworkDriver { - pub fn new() -> Self { + pub fn new(host: &str, port: u16) -> Self { let runtime = Builder::new_current_thread().enable_all().build().unwrap(); let runtime = Arc::new(runtime); - NetworkDriver { runtime } + NetworkDriver { + runtime, + host: host.to_string(), + port, + } } } impl DriverControl for NetworkDriver { - fn connect(&self, db_url: &str) -> Result<(String, Connection), anyhow::Error> { - if let Some(idx) = db_url.find("//") { - let db_url = db_url[idx + 2..].trim(); - let url = format!("http://{}:50051", db_url); - dbg!(&url); - let endpoint = Endpoint::from_shared(url)?; - let channel = self.runtime.block_on(endpoint.connect())?; - let mut client = DriverServiceClient::new(channel.clone()); - let response = self - .runtime - .block_on(client.create_connection(DriverCreateConnectionRequest { - url: db_url.to_string(), - }))? - .into_inner(); - let connection_id = response.connection_id; - - return Ok(( - "".to_string(), - Connection::Network(NetworkConnection::new( - self.runtime.clone(), - channel, - connection_id, - )?), - )); - } else { - panic!("Invalid URL"); - } + fn connect(&self, db_name: &str) -> Result<(String, Connection), anyhow::Error> { + let url = format!("http://{}:{}", self.host, self.port); + let endpoint = Endpoint::from_shared(url)?; + let channel = self.runtime.block_on(endpoint.connect())?; + let mut client = DriverServiceClient::new(channel.clone()); + let response = self + .runtime + .block_on(client.create_connection(DriverCreateConnectionRequest { + url: db_name.to_string(), + }))? + .into_inner(); + let connection_id = response.connection_id; + + Ok(( + "".to_string(), + Connection::Network(NetworkConnection::new( + self.runtime.clone(), + channel, + connection_id, + )?), + )) } } @@ -168,8 +168,8 @@ mod tests { // give the server a moment to start thread::sleep(Duration::from_millis(100)); - let driver = NetworkDriver::new(); - let (_db_name, connection) = driver.connect("jdbc:simpledb://127.0.0.1")?; + let driver = NetworkDriver::new("127.0.0.1", 50051); + let (_db_name, connection) = driver.connect("sample")?; let mut statement = connection.create_statement()?; statement.execute_update("create table test (A I32, B VARCHAR(20))")?; diff --git a/src/server.rs b/src/server.rs index 384cc4f..8f656b5 100644 --- a/src/server.rs +++ b/src/server.rs @@ -8,7 +8,7 @@ use tonic::transport::Server; #[tokio::main] async fn main() -> Result<(), Box> { - let addr = "[::1]:50051".parse()?; + let addr = "127.0.0.1:50051".parse()?; let remote_driver = RemoteDriver::new(); let remote_connection = remote_driver.create_remote_connection();