use std::{ fs::{self, File}, io::{self, Write}, }; use clickhouse::Client; use structopt::StructOpt; const URL: &str = "http://localhost:8123"; const CREATE_TABLE_DDL: &str = " CREATE TABLE ch2rs_test ( u8 UInt8, u16 UInt16, u32 UInt32, u64 UInt64, i8 Int8, i16 Int16, i32 Int32, i64 Int64, bool Bool, str String, low_str LowCardinality(String), blob String, fs FixedString(5), f32 Float32, f64 Float64, d Date, dt DateTime, dt64 DateTime64(9), ipv4 IPv4, ipv6 IPv6, uuid UUID, dec64 Decimal64(9), enum8 Enum8('' = -128, 'Foo Bar' = 0), enum16 Enum16('' = -128, 'fooBar' = 1024), array Array(LowCardinality(String)), tuple Tuple(String, LowCardinality(String)), opt_str Nullable(String), map_str Map(String, String), map_f32 Map(String, Float32), default DEFAULT u16, material MATERIALIZED u16, alias ALIAS u16, ignored Int8 ) ENGINE = MergeTree ORDER BY u8 "; async fn recreate_table() { let client = Client::default() .with_url(URL) .with_option("allow_experimental_map_type", "1"); client .query("DROP TABLE IF EXISTS ch2rs_test") .execute() .await .expect("failed to drop an old table"); client .query(CREATE_TABLE_DDL) .execute() .await .expect("failed to create a table"); } async fn run_one(args: Vec<&str>) { let options = ch2rs::Options::from_iter(args); let code = ch2rs::generate(options) .await .expect("failed to generate a struct"); insta::assert_snapshot!("all", code); } async fn generate_all() { for t1 in &["-S", "-D", "-SD"] { for t2 in &["--owned", ""] { let args = vec![ "ch2rs", "ch2rs_test", "-U", URL, t1, t2, "-T", "FixedString(5)=[u8; 5]", "-T", "Date=u16", "-T", "DateTime=u32", "-T", "DateTime64(9)=u64", "-T", "IPv4=u32", "-T", "IPv6=[u8; 16]", "-T", "UUID=[u8; 16]", "-T", "Decimal(18, 9)=u64", "-O", "blob=Vec", "-B", "blob", "-I", "ignored", ]; let args = args.into_iter().filter(|s| !s.is_empty()).collect(); let tmp = format!("{}_{}", t1, t2); let suffix = tmp.trim_end_matches('_'); let mut settings = insta::Settings::clone_current(); settings.set_snapshot_suffix(suffix); settings.set_prepend_module_to_snapshot(false); settings.bind_async(run_one(args)).await; } } } fn extract_code_from_snapshots() { match fs::remove_dir_all("target/snapshots") { Ok(_) => {} Err(err) if err.kind() == io::ErrorKind::NotFound => {} Err(err) => panic!("failed to remove target/snapshots: {}", err), } fs::create_dir_all("target/snapshots").expect("failed to create tests/snapshots"); for entry in fs::read_dir("tests/snapshots").expect("failed to read tests/snapshots") { let entry = entry.expect("invalid entry"); let content = fs::read_to_string(entry.path()).expect("failed to read the snapshot file"); let code = content.rsplit("---").next().expect("invalid snapshot"); let mut file = File::create(format!( "target/snapshots/{}.rs", entry.file_name().to_string_lossy() )) .expect("failed to create the source file"); // TODO: connect to CH in tests. file.write_all(format!("{}\nfn main() {{}}", code).as_bytes()) .expect("failed to write to the source file"); } } fn compile_snapshots() { let t = trybuild::TestCases::new(); t.pass("target/snapshots/*.rs"); } #[tokio::test] async fn all() { recreate_table().await; generate_all().await; extract_code_from_snapshots(); compile_snapshots(); }