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 COMMENT 'this is a byte', u16 UInt16 COMMENT 'these are two bytes', u32 UInt32 COMMENT 'and these are four', u64 UInt64 COMMENT 'eight...', u128 UInt128 COMMENT 'come on!', i8 Int8, i16 Int16, i32 Int32, i64 Int64, i128 Int128, bool Boolean, str String, low_str LowCardinality(String), blob String, fs FixedString(5), f32 Float32, f64 Float64, d Date, dt DateTime, dt64 DateTime64(9), ipv4 IPv4, ipv4_opt Nullable(IPv4), ipv6 IPv6, uuid UUID, uuid_opt Nullable(UUID), dec64 Decimal64(9), enum8 Enum8('' = -128, 'Foo Bar' = 0), enum16 Enum16('' = -128, 'fooBar' = 1024), array Array(LowCardinality(String)), tuple Tuple(String, LowCardinality(String)), str_opt 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", "Decimal(18, 9)=u64", "-O", "blob=Vec", "-B", "blob", "-I", "ignored", "--derive", "Clone", "--derive", "PartialEq", ]; 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(); }