diff --git a/Cargo.lock b/Cargo.lock index 4f01ae1..7df0ca3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anyhow" version = "1.0.69" @@ -68,12 +77,43 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-integer", + "num-traits", + "time", + "wasm-bindgen", + "winapi", +] + +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + [[package]] name = "console_error_panic_hook" version = "0.1.7" @@ -84,6 +124,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + [[package]] name = "crc32fast" version = "1.3.2" @@ -102,6 +148,59 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "cxx" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86d3488e7665a7a483b57e25bdd90d0aeb2bc7608c8d0346acf2ad3f1caf1d62" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48fcaf066a053a41a81dfb14d57d99738b767febb8b735c3016e469fac5da690" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2ef98b8b717a829ca5603af80e1f9e2e48013ab227b68ef37872ef84ee479bf" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "086c685979a698443656e5cf7856c95c642295a38599f12fb1ff76fb28d19892" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "fake" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d68f517805463f3a896a9d29c1d6ff09d3579ded64a7201b4069f8f9c0d52fd" +dependencies = [ + "rand", +] + [[package]] name = "flate2" version = "1.0.25" @@ -198,6 +297,17 @@ dependencies = [ "slab", ] +[[package]] +name = "getrandom" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + [[package]] name = "gloo" version = "0.8.0" @@ -378,6 +488,30 @@ dependencies = [ "libc", ] +[[package]] +name = "iana-time-zone" +version = "0.1.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "winapi", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +dependencies = [ + "cxx", + "cxx-build", +] + [[package]] name = "implicit-clone" version = "0.3.5" @@ -441,6 +575,15 @@ version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +[[package]] +name = "link-cplusplus" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" +dependencies = [ + "cc", +] + [[package]] name = "log" version = "0.4.17" @@ -456,9 +599,12 @@ version = "0.1.0" dependencies = [ "anyhow", "base64", + "chrono", + "fake", "gloo", "js-sys", "lazy-regex", + "rand", "serde", "thiserror", "web-sys", @@ -482,6 +628,25 @@ dependencies = [ "adler", ] +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + [[package]] name = "num_cpus" version = "1.15.0" @@ -547,6 +712,12 @@ dependencies = [ "thiserror", ] +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + [[package]] name = "prettyplease" version = "0.1.23" @@ -616,6 +787,36 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + [[package]] name = "regex" version = "1.7.1" @@ -645,6 +846,12 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" +[[package]] +name = "scratch" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2" + [[package]] name = "serde" version = "1.0.152" @@ -719,6 +926,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "termcolor" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +dependencies = [ + "winapi-util", +] + [[package]] name = "thiserror" version = "1.0.38" @@ -739,6 +955,17 @@ dependencies = [ "syn", ] +[[package]] +name = "time" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + [[package]] name = "tokio" version = "1.25.0" @@ -799,12 +1026,30 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "wasm-bindgen" version = "0.2.84" @@ -881,6 +1126,37 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-sys" version = "0.42.0" diff --git a/Cargo.toml b/Cargo.toml index 43b08b6..b8ca92c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,9 @@ js-sys = "0.3" base64 = "0.13.0" gloo = "0.8" serde = { version = "1.0", features = ["derive"] } +fake = "2.5" +rand = "0.8" +chrono = "0.4.23" [dependencies.zip] version = "0.6.4" diff --git a/src/generate_sql.rs b/src/generate_sql.rs new file mode 100644 index 0000000..0774804 --- /dev/null +++ b/src/generate_sql.rs @@ -0,0 +1,365 @@ +use std::{rc::Rc, collections::HashSet}; + +use anyhow::{Result, bail}; +use rand::{seq::SliceRandom, Rng, rngs::ThreadRng}; +use chrono::{Local, NaiveDateTime, Days}; +use fake::{faker::{lorem::en::*, name::en::{FirstName, LastName, Name}, phone_number::en::PhoneNumber, internet::en::{DomainSuffix, FreeEmail}, company::en::BsNoun, address::{en::{CityName, StreetName}}}, Fake}; + +use crate::magicdraw_parser::{SQLTable, SQLColumn, SQLType, SQLCheckConstraint}; + +const INDENT: &str = " "; + +#[derive(Debug, PartialEq)] +pub enum SQLIntValueGuess { + Range(i32, i32), + AutoIncrement +} + +#[derive(Debug, PartialEq)] +pub enum SQLTimeValueGuess { + Now, + Future, + Past +} + +#[derive(Debug, PartialEq)] +pub enum SQLStringValueGuess { + LoremIpsum, + FirstName, + LastName, + FullName, + Empty, + PhoneNumber, + CityName, + Address, + Email, + URL, + RandomEnum(Vec), +} + +#[derive(Debug, PartialEq)] +pub enum SQLBoolValueGuess { + True, + False, + Random, +} + +#[derive(Debug, PartialEq)] +pub enum SQLValueGuess { + Int(SQLIntValueGuess), + Date(SQLTimeValueGuess), + Time(SQLTimeValueGuess), + Datetime(SQLTimeValueGuess), + Float(f32, f32), + Bool(SQLBoolValueGuess), + String(usize, SQLStringValueGuess), +} + +// TODO: Check primary key constraint +pub fn generate_fake_entries( + tables: &[Rc], + value_guessess: &Vec>, + rows_per_table: u32 + ) -> Result { + let mut lines = vec![]; + + let mut rng = rand::thread_rng(); + + let mut all_foreign_columns = vec![]; + let mut all_entries = vec![]; + for table in tables { + let mut entries = vec![]; + for _ in 0..rows_per_table { + entries.push(vec![]); + } + all_entries.push(entries); + + let mut foreign_columns = vec![]; + for (i, column) in table.columns.iter().enumerate() { + if let Some((table_name, column_name)) = &column.foreign_key { + let (table_idx, table) = tables.iter() + .enumerate() + .find(|(_, table)| table.name.eq(table_name)) + .expect("Foreign table not found"); + let (column_idx, _) = table.columns + .iter() + .enumerate() + .find(|(_, column)| column.name.eq(column_name)) + .expect("Foreign column not found"); + foreign_columns.push((i, table_idx, column_idx)); + } + } + all_foreign_columns.push(foreign_columns); + } + + let mut entries_with_foreign_keys = HashSet::new(); + for (table_idx, table) in tables.iter().enumerate() { + let entries = &mut all_entries[table_idx]; + + for (column_idx, column) in table.columns.iter().enumerate() { + let mut auto_increment_counter = 0; + let value_guess = &value_guessess[table_idx][column_idx]; + for entry_idx in 0..(rows_per_table as usize) { + if let Some(_) = &column.foreign_key { + entries_with_foreign_keys.insert((table_idx, entry_idx)); + entries[entry_idx].push("".into()); + } else { + entries[entry_idx].push(generate_value(&mut rng, &value_guess, &mut auto_increment_counter)); + } + } + } + } + + while !entries_with_foreign_keys.is_empty() { + let entries_with_foreign_keys_copy = entries_with_foreign_keys.clone(); + let before_retain = entries_with_foreign_keys.len(); + + entries_with_foreign_keys.retain(|(table_idx, entry_idx)| { + for (column_idx, foreign_table_idx, foreign_column_idx) in &all_foreign_columns[*table_idx] { + let available_values: Vec<&str>; + + // If the foreign column, is also a foreign of the other table, ... + // Then we need to filter out available options which have not been filled in + if all_foreign_columns[*foreign_table_idx].iter().find(|(idx, _, _)| idx == foreign_column_idx).is_some() { + available_values = all_entries[*foreign_table_idx].iter() + .enumerate() + .filter(|(i, _)| entries_with_foreign_keys_copy.contains(&(*foreign_table_idx, *i))) + .map(|(_, entry)| entry[*foreign_column_idx].as_str()) + .collect(); + } else { + available_values = all_entries[*foreign_table_idx].iter() + .map(|entry| entry[*foreign_column_idx].as_str()) + .collect(); + } + + if let Some(chosen_value) = available_values.choose(&mut rng) { + all_entries[*table_idx][*entry_idx][*column_idx] = chosen_value.to_string(); + } else { + // Early break, thre are no currently available options + // Try next time + return true; + } + } + + false + }); + + // This is to stop infnite loop, where during each iteration nothing gets removed + if before_retain == entries_with_foreign_keys.len() { + bail!("Failed to resolve foreign keys") + } + } + + for (i, table) in tables.iter().enumerate() { + let mut column_names = vec![]; + for column in &table.columns { + column_names.push(column.name.as_str()); + } + + let entries = &all_entries[i]; + lines.push(format!("INSERT INTO {}", table.name)); + lines.push(format!("{}({})", INDENT, column_names.join(", "))); + lines.push("VALUES".into()); + let entries_str = entries.iter() + .map(|entry| format!("{}({})", INDENT, entry.join(", "))) + .collect::>() + .join(",\n"); + lines.push(format!("{};\n", entries_str)); + } + + Ok(lines.join("\n")) +} + +fn generate_time_value(rng: &mut ThreadRng, guess: &SQLTimeValueGuess) -> NaiveDateTime { + let now = Local::now().naive_local(); + + match guess { + SQLTimeValueGuess::Now => now, + SQLTimeValueGuess::Future => { + let days = rng.gen_range(1..=30); + now.checked_add_days(Days::new(days)).unwrap() + }, + SQLTimeValueGuess::Past => { + let days = rng.gen_range(7..=365); + now.checked_sub_days(Days::new(days)).unwrap() + } + } +} + +fn generate_value(rng: &mut ThreadRng, guess: &SQLValueGuess, auto_increment_counter: &mut u32) -> String { + match guess { + SQLValueGuess::Int(int_guess) => { + match int_guess { + SQLIntValueGuess::Range(min, max) => { + rng.gen_range((*min)..=(*max)).to_string() + }, + SQLIntValueGuess::AutoIncrement => { + let str = auto_increment_counter.to_string(); + *auto_increment_counter += 1; + str + }, + } + }, + SQLValueGuess::Date(time_gues) => { + let datetime = generate_time_value(rng, &time_gues); + format!("'{}'", datetime.format("%Y-%m-%d")) + }, + SQLValueGuess::Time(time_gues) => { + let datetime = generate_time_value(rng, &time_gues); + format!("'{}'", datetime.format("%H:%M:%S")) + }, + SQLValueGuess::Datetime(time_gues) => { + let datetime = generate_time_value(rng, &time_gues); + format!("'{}'", datetime.format("%Y-%m-%d %H:%M:%S")) + }, + SQLValueGuess::Bool(bool_guess) => { + match bool_guess { + SQLBoolValueGuess::True => "1".into(), + SQLBoolValueGuess::False => "0".into(), + SQLBoolValueGuess::Random => rng.gen_range(0..=1).to_string(), + } + }, + SQLValueGuess::Float(min, max) => { + let value = rng.gen_range((*min)..(*max)); + ((value * 100.0 as f32).round() / 100.0).to_string() + }, + SQLValueGuess::String(max_size, string_guess) => { + let mut str = match string_guess { + SQLStringValueGuess::LoremIpsum => { + + let mut current_len = 0; + let mut text = vec![]; + let words: Vec = Words(3..10).fake_with_rng(rng); + for word in words { + current_len += word.len() + 1; + text.push(word); + if current_len > *max_size { break; } + } + text.join(" ").to_string() + }, + SQLStringValueGuess::FirstName => { + FirstName().fake_with_rng(rng) + }, + SQLStringValueGuess::LastName => { + LastName().fake_with_rng(rng) + }, + SQLStringValueGuess::FullName => { + Name().fake_with_rng(rng) + }, + SQLStringValueGuess::PhoneNumber => { + PhoneNumber().fake_with_rng(rng) + }, + SQLStringValueGuess::CityName => { + CityName().fake_with_rng(rng) + }, + SQLStringValueGuess::Address => { + StreetName().fake_with_rng(rng) + }, + SQLStringValueGuess::Email => { + FreeEmail().fake_with_rng(rng) + }, + SQLStringValueGuess::URL => { + let suffix: String = DomainSuffix().fake_with_rng(rng); + let noun: String = BsNoun().fake_with_rng(rng); + let noun: String = noun.to_lowercase() + .chars() + .map(|c| if c.is_whitespace() { '-' } else { c }) + .collect(); + format!("www.{}.{}", noun, suffix) + }, + SQLStringValueGuess::RandomEnum(options) => { + options.choose(rng).unwrap().to_string() + }, + SQLStringValueGuess::Empty => { + "".into() + } + }; + + str.truncate(*max_size); + format!("'{}'", str) + } + } +} + +fn generate_string_guess(column: &SQLColumn) -> SQLStringValueGuess { + if let Some(constraint) = &column.check_constraint { + if let SQLCheckConstraint::OneOf(options) = constraint { + return SQLStringValueGuess::RandomEnum(options.clone()) + } else { + return SQLStringValueGuess::LoremIpsum + } + } + + let name = column.name.to_lowercase(); + if name.contains("first") && name.contains("name") { + SQLStringValueGuess::FirstName + } else if (name.contains("last") && name.contains("name")) || name.contains("surname") { + SQLStringValueGuess::LastName + } else if name.contains("phone") && name.contains("number") { + SQLStringValueGuess::PhoneNumber + } else if name.contains("city") { + SQLStringValueGuess::CityName + } else if name.contains("address") { + SQLStringValueGuess::Address + } else if name.contains("email") { + SQLStringValueGuess::Email + } else if name.contains("homepage") || name.contains("website") || name.contains("url") { + SQLStringValueGuess::URL + } else { + SQLStringValueGuess::LoremIpsum + } +} + +pub fn generate_guess(column: &SQLColumn) -> SQLValueGuess { + match column.sql_type { + SQLType::Int => { + if column.primary_key { + SQLValueGuess::Int(SQLIntValueGuess::AutoIncrement) + } else { + SQLValueGuess::Int(SQLIntValueGuess::Range(0, 100)) + } + }, + SQLType::Float | SQLType::Decimal => { + SQLValueGuess::Float(0.0, 100.0) + }, + SQLType::Date => { + let name = column.name.to_lowercase(); + if name.contains("create") || name.contains("update") { + SQLValueGuess::Date(SQLTimeValueGuess::Past) + } else { + SQLValueGuess::Date(SQLTimeValueGuess::Now) + } + }, + SQLType::Time => { + let name = column.name.to_lowercase(); + if name.contains("create") || name.contains("update") { + SQLValueGuess::Time(SQLTimeValueGuess::Past) + } else { + SQLValueGuess::Time(SQLTimeValueGuess::Now) + } + }, + SQLType::Datetime => { + let name = column.name.to_lowercase(); + if name.contains("create") || name.contains("update") { + SQLValueGuess::Datetime(SQLTimeValueGuess::Past) + } else { + SQLValueGuess::Datetime(SQLTimeValueGuess::Now) + } + }, + SQLType::Bool => { + SQLValueGuess::Bool(SQLBoolValueGuess::Random) + }, + SQLType::Varchar(max_size) => { + SQLValueGuess::String(max_size as usize, generate_string_guess(column)) + }, + SQLType::Char(max_size) => { + SQLValueGuess::String(max_size as usize, generate_string_guess(column)) + } + } +} + +pub fn generate_table_guessess(table: &SQLTable) -> Vec { + table.columns.iter() + .map(|column| generate_guess(column)) + .collect() +} diff --git a/src/magicdraw_parser/mod.rs b/src/magicdraw_parser/mod.rs index 16d8a16..d36a2a9 100644 --- a/src/magicdraw_parser/mod.rs +++ b/src/magicdraw_parser/mod.rs @@ -18,6 +18,8 @@ pub enum SQLType { Int, Decimal, Date, + Time, + Datetime, Float, Bool, Char(u8), @@ -30,6 +32,8 @@ impl Display for SQLType { SQLType::Int => write!(f, "INT"), SQLType::Decimal => write!(f, "DECIMAL"), SQLType::Date => write!(f, "DATE"), + SQLType::Time => write!(f, "TIME"), + SQLType::Datetime => write!(f, "DATETIME"), SQLType::Float => write!(f, "FLOAT"), SQLType::Bool => write!(f, "BOOL"), SQLType::Char(size) => write!(f, "CHAR({})", size), diff --git a/src/main.rs b/src/main.rs index e6cd1d6..09b6b63 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,7 @@ use app::App; mod magicdraw_parser; mod app; mod components; +mod generate_sql; // TODO: Make this work with enumation lookup tables // TODO: Dark theme switch button