1
0

add generator picker

This commit is contained in:
Rokas Puzonas 2023-03-05 12:56:55 +02:00
parent 1fa7c7bbc8
commit dc68f88f9d
8 changed files with 419 additions and 107 deletions

3
Cargo.lock generated
View File

@ -304,8 +304,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"js-sys",
"libc", "libc",
"wasi 0.11.0+wasi-snapshot-preview1", "wasi 0.11.0+wasi-snapshot-preview1",
"wasm-bindgen",
] ]
[[package]] [[package]]
@ -601,6 +603,7 @@ dependencies = [
"base64", "base64",
"chrono", "chrono",
"fake", "fake",
"getrandom",
"gloo", "gloo",
"js-sys", "js-sys",
"lazy-regex", "lazy-regex",

View File

@ -16,6 +16,7 @@ gloo = "0.8"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
fake = "2.5" fake = "2.5"
rand = "0.8" rand = "0.8"
getrandom = { version = "0.2.8", features = ["js"] }
chrono = "0.4.23" chrono = "0.4.23"
[dependencies.zip] [dependencies.zip]

View File

@ -1,3 +1,4 @@
use std::cell::RefCell;
use std::io::Cursor; use std::io::Cursor;
use std::collections::{HashMap, self}; use std::collections::{HashMap, self};
use std::rc::Rc; use std::rc::Rc;
@ -9,24 +10,32 @@ use web_sys::{DragEvent, Event, FileList, HtmlInputElement, MouseEvent};
use yew::html::TargetCast; use yew::html::TargetCast;
use yew::{html, Callback, Component, Context, Html}; use yew::{html, Callback, Component, Context, Html};
use crate::generate_sql::{SQLValueGuess, generate_table_guessess};
use crate::magicdraw_parser::{parse_project, SQLTableCollection, SQLTable}; use crate::magicdraw_parser::{parse_project, SQLTableCollection, SQLTable};
use crate::components::sql_column_info::SQLTableColumnInfo; use crate::components::sql_column_info::SQLTableColumnInfo;
const COLLECTION_STORE_KEY: &str = "current_collection"; const COLLECTION_STORE_KEY: &str = "current_collection";
const DEFAULT_ROWS_PER_TABLE: u32 = 20;
pub enum Msg { pub enum Msg {
Noop, Noop,
Loaded(String, Vec<u8>), Loaded(String, Vec<u8>),
UploadProject(File), UploadProject(File),
UpdateCurrentProject(Option<SQLTableCollection>), UpdateCurrentProject(Option<SQLTableCollection>),
UpdateGenarator(String, SQLValueGuess),
ShowNextTable, ShowNextTable,
ShowPrevTable ShowPrevTable,
AllGoodConfirmation,
GenerateSQL,
} }
pub struct App { pub struct App {
active_readers: HashMap<String, FileReader>, active_readers: HashMap<String, FileReader>,
current_collection: Option<Vec<Rc<SQLTable>>>, current_collection: Option<Vec<Rc<SQLTable>>>,
currently_shown_table: usize current_guessess: Vec<Rc<RefCell<HashMap<String, SQLValueGuess>>>>,
currently_shown_table: usize,
all_good_confirmed: bool,
generated_sql: Option<String>
} }
impl Component for App { impl Component for App {
@ -34,15 +43,24 @@ impl Component for App {
type Properties = (); type Properties = ();
fn create(_ctx: &Context<Self>) -> Self { fn create(_ctx: &Context<Self>) -> Self {
let mut current_guessess = vec![];
let mut current_collection = None; let mut current_collection = None;
if let Ok(collection) = LocalStorage::get::<SQLTableCollection>("current_collection") { if let Ok(collection) = LocalStorage::get::<SQLTableCollection>("current_collection") {
for table in &collection.tables {
let guess = generate_table_guessess(table);
current_guessess.push(Rc::new(RefCell::new(guess)));
}
current_collection = Some(collection.tables.into_iter().map(Rc::new).collect()); current_collection = Some(collection.tables.into_iter().map(Rc::new).collect());
} }
Self { Self {
active_readers: HashMap::default(), active_readers: HashMap::default(),
current_collection, current_collection,
currently_shown_table: 0 currently_shown_table: 0,
all_good_confirmed: true, // TODO: make this false, by default
generated_sql: None,
current_guessess
} }
} }
@ -84,6 +102,14 @@ impl Component for App {
Msg::UpdateCurrentProject(collection) => { Msg::UpdateCurrentProject(collection) => {
if let Some(collection) = collection { if let Some(collection) = collection {
LocalStorage::set(COLLECTION_STORE_KEY, &collection).unwrap(); LocalStorage::set(COLLECTION_STORE_KEY, &collection).unwrap();
self.currently_shown_table = 0;
self.all_good_confirmed = false;
self.generated_sql = None;
self.current_guessess = vec![];
for table in &collection.tables {
let guess = generate_table_guessess(table);
self.current_guessess.push(Rc::new(RefCell::new(guess)));
}
self.current_collection = Some(collection.tables.into_iter().map(Rc::new).collect()); self.current_collection = Some(collection.tables.into_iter().map(Rc::new).collect());
} else { } else {
LocalStorage::delete(COLLECTION_STORE_KEY); LocalStorage::delete(COLLECTION_STORE_KEY);
@ -106,79 +132,36 @@ impl Component for App {
} }
false false
}, },
Msg::AllGoodConfirmation => {
self.all_good_confirmed = true;
true
},
Msg::UpdateGenarator(column, generator) => {
console_dbg!(column, generator);
let mut guessess = self.current_guessess[self.currently_shown_table].borrow_mut();
let entry = guessess.get_mut(&column).unwrap();
*entry = generator;
true
},
Msg::GenerateSQL => {
false
},
} }
} }
fn view(&self, ctx: &Context<Self>) -> Html { fn view(&self, ctx: &Context<Self>) -> Html {
let prevent_default_cb = Callback::from(|event: DragEvent| {
event.prevent_default();
});
html! { html! {
<main class="flex-col 4rem center"> <main class="flex-col 4rem center">
<p class="text-3xl text-center">{ "🪄 MagicDraw SQL Data Generator" }</p> <p class="text-3xl text-center">{ "🪄 MagicDraw SQL Data Generator" }</p>
<div> { self.show_step1(ctx) }
<p class="text-2xl mt-2rem pb-1rem"> if self.current_collection.is_some() {
<span>{ "1. Upload " }</span> { self.show_step2(ctx) }
<code class="bg-dark900 p-0.2rem rounded">{".mdzip"}</code> if self.all_good_confirmed {
<span>{ " project" }</span> { self.show_step3(ctx) }
</p> if self.generated_sql.is_some() {
<label for="file-upload"> { self.show_step4(ctx) }
<div }
class="flex flex-col rounded items-center p-3rem bg-dark800" }
border="dotted dark100 0.2rem"
cursor="pointer"
ondrop={ctx.link().callback(|event: DragEvent| {
event.prevent_default();
let files = event.data_transfer().unwrap().files();
Self::upload_project(files)
})}
ondragover={&prevent_default_cb}
ondragenter={&prevent_default_cb}
>
<div class="i-mdi-file-upload-outline text-4rem"></div>
</div>
</label>
<input
id="file-upload"
type="file"
class = "hidden"
accept=".mdzip"
onchange={ctx.link().callback(move |e: Event| {
let input: HtmlInputElement = e.target_unchecked_into();
Self::upload_project(input.files())
})}
/>
<p class="text-amber300">{ "NOTE: This relies on the fact, that you have a .dll script configured" }</p>
</div>
if let Some(collection) = &self.current_collection {
<div>
<p class="text-2xl mt-2rem">{ "2. Make sure everything looks 👌" }</p>
<div class="mb-0.5rem gap-3 flex flex-row items-center">
<button
class="p-0.5rem btn-white"
onclick={ctx.link().callback(move |_: MouseEvent| {
Msg::ShowPrevTable
})}
>
{ "< Previous" }
</button>
<div> { self.currently_shown_table + 1 } { " / " } { collection.len() } </div>
<button
class="p-0.5rem btn-white"
onclick={ctx.link().callback(move |_: MouseEvent| {
Msg::ShowNextTable
})}
>
{ "Next >" }
</button>
</div>
{ Self::show_table(collection[self.currently_shown_table].clone()) }
<button class="display-block p-1rem mt-1rem btn-emerald">{ "All good?" }</button>
</div>
<div>
<p class="text-2xl mt-2rem">{ "3. Copy & Paste" }</p>
</div>
} }
</main> </main>
} }
@ -186,10 +169,117 @@ impl Component for App {
} }
impl App { impl App {
fn show_step1(&self, ctx: &Context<Self>) -> Html {
let prevent_default_cb = Callback::from(|event: DragEvent| {
event.prevent_default();
});
fn show_table(table: Rc<SQLTable>) -> Html { html! {
html!{ <div>
<SQLTableColumnInfo table={table} /> <p class="text-2xl mt-2rem pb-1rem">
<span>{ "1. Upload " }</span>
<code class="bg-dark900 p-0.2rem rounded">{".mdzip"}</code>
<span>{ " project" }</span>
</p>
<label for="file-upload">
<div
class="flex flex-col rounded items-center p-3rem bg-dark800"
border="dotted dark100 0.2rem"
cursor="pointer"
ondrop={ctx.link().callback(|event: DragEvent| {
event.prevent_default();
let files = event.data_transfer().unwrap().files();
Self::upload_project(files)
})}
ondragover={&prevent_default_cb}
ondragenter={&prevent_default_cb}
>
<div class="i-mdi-file-upload-outline text-4rem"></div>
</div>
</label>
<input
id="file-upload"
type="file"
class = "hidden"
accept=".mdzip"
onchange={ctx.link().callback(move |e: Event| {
let input: HtmlInputElement = e.target_unchecked_into();
Self::upload_project(input.files())
})}
/>
<p class="text-amber300">{ "NOTE: This relies on the fact, that you have a .dll script configured" }</p>
</div>
}
}
fn show_step2(&self, ctx: &Context<Self>) -> Html {
let collection = self.current_collection.as_ref().unwrap();
html! {
<div>
<p class="text-2xl mt-2rem">{ "2. Make sure everything looks 👌" }</p>
<div class="mb-0.5rem gap-3 flex flex-row items-center">
<button
class="p-0.5rem btn-white"
onclick={ctx.link().callback(move |_: MouseEvent| { Msg::ShowPrevTable })}
>
{ "< Previous" }
</button>
<div> { self.currently_shown_table + 1 } { " / " } { collection.len() } </div>
<button
class="p-0.5rem btn-white"
onclick={ctx.link().callback(move |_: MouseEvent| { Msg::ShowNextTable })}
>
{ "Next >" }
</button>
</div>
<SQLTableColumnInfo
table={collection[self.currently_shown_table].clone()}
guessess={self.current_guessess[self.currently_shown_table].clone()}
onchange={ctx.link().callback(|(column_name, generator)| {
Msg::UpdateGenarator(column_name, generator)
})}
/>
<button
class="display-block p-1rem mt-1rem btn-emerald"
onclick={ctx.link().callback(move |_: MouseEvent| { Msg::AllGoodConfirmation })}
>{ "All good?" }</button>
</div>
}
}
fn show_step3(&self, ctx: &Context<Self>) -> Html {
html! {
<div>
<p class="text-2xl mt-2rem">{ "3. Final settings" }</p>
<label for="gen-amount-input">
{ "Entries per table: " }
</label>
<input
id="gen-amount-input"
class="rounded items-center p-0.3rem bg-dark800 text-light100 w-5rem b-0"
value={DEFAULT_ROWS_PER_TABLE.to_string()}
type="number"
/>
<button
class="block mt-1rem p-1rem btn-emerald"
>
{ "Generate" }
</button>
</div>
}
}
fn show_step4(&self, ctx: &Context<Self>) -> Html {
let sql = self.generated_sql.as_ref().unwrap();
html! {
<div>
<p class="text-2xl mt-2rem">{ "4. Copy & Paste" }</p>
<code>
{ sql }
</code>
</div>
} }
} }

View File

@ -0,0 +1,191 @@
use std::{collections::HashMap, str::FromStr};
use gloo::console::console_dbg;
use yew::{Html, html, Callback, TargetCast, AttrValue};
use web_sys::{Event, HtmlInputElement};
use crate::{generate_sql::{SQLValueGuess, SQLStringValueGuess, SQLBoolValueGuess, SQLTimeValueGuess, SQLIntValueGuess}, magicdraw_parser::{SQLColumn, SQLCheckConstraint}};
fn show_dropdown_picker(
selected: &str,
options: &[AttrValue],
onchange: Callback<String>
) -> Html {
html!{
<select onchange={Callback::from(move |e: Event| {
let value = e.target_unchecked_into::<HtmlInputElement>().value();
onchange.emit(value);
})}>
{
options.iter().map(|value| {
html! { <option selected={value.eq(&selected)} value={value.clone()}>{ value }</option> }
}).collect::<Html>()
}
</select>
}
}
fn show_enum_dropdown<T: PartialEq + Clone + 'static>(
selected: &T,
options: HashMap<AttrValue, T>,
onchange: Callback<T>
) -> Html {
let keys = options.keys().map(AttrValue::clone).collect::<Vec<_>>();
let guess_str = options.iter().find(|(_, v)| v.eq(&selected)).unwrap().0.clone();
show_dropdown_picker(&guess_str, &keys, onchange.reform(move |value_str: String| {
options.get(value_str.as_str()).unwrap().clone()
}))
}
fn show_range_picker<T: FromStr + ToString + Clone + 'static>(
min: T,
max: T,
default_min: T,
default_max: T,
onchange: Callback<(T, T)>,
) -> Html {
let onchange_min = {
let onchange = onchange.clone();
let default_min = default_min.clone();
let max = max.clone();
Callback::from(move |e: Event| {
let value = e.target_unchecked_into::<HtmlInputElement>().value();
let min_value = value.parse().unwrap_or(default_min.clone());
onchange.emit((min_value, max.clone()))
})
};
let onchange_max = {
let onchange = onchange.clone();
let default_max = default_max.clone();
let min = min.clone();
Callback::from(move |e: Event| {
let value = e.target_unchecked_into::<HtmlInputElement>().value();
let max_value = value.parse().unwrap_or(default_max.clone());
onchange.emit((min.clone(), max_value))
})
};
html! {
<div class="flex flex-row">
<input
value={min.to_string()}
class="w-5rem"
type="number"
placeholder={default_min.to_string()}
onchange={onchange_min}
/>
<div class="ml-1 mr-1">{ ".." }</div>
<input
value={max.to_string()}
class="w-5rem"
type="number"
placeholder={default_max.to_string()}
onchange={onchange_max}
/>
</div>
}
}
pub fn generator_picker(
column: &SQLColumn,
value: &SQLValueGuess,
onchange: Callback<SQLValueGuess>
) -> Html {
// TODO: Refacotr 'time', 'datetime', and 'date'. They are very similar
match value {
SQLValueGuess::Int(guess) => {
if column.primary_key {
return html!("Auto increment")
}
let mut min = 0;
let mut max = 0;
if let SQLIntValueGuess::Range(range_min, range_max) = guess {
min = *range_min;
max = *range_max;
}
// TODO: Disallow entering floating point numbers
show_range_picker(min, max, 0, 100, onchange.reform(|(min, max)| {
SQLValueGuess::Int(SQLIntValueGuess::Range(min, max))
}))
},
SQLValueGuess::Float(min, max) => {
show_range_picker(*min, *max, 0.0, 100.0, onchange.reform(|(min, max)| {
SQLValueGuess::Float(min, max)
}))
},
SQLValueGuess::Date(guess) => {
let options = HashMap::from([
("Now".into() , SQLTimeValueGuess::Now),
("Future".into(), SQLTimeValueGuess::Future),
("Past".into() , SQLTimeValueGuess::Past),
]);
show_enum_dropdown(guess, options, onchange.reform(|enum_value| {
SQLValueGuess::Date(enum_value)
}))
},
SQLValueGuess::Time(guess) => {
let options = HashMap::from([
("Now".into() , SQLTimeValueGuess::Now),
("Future".into(), SQLTimeValueGuess::Future),
("Past".into() , SQLTimeValueGuess::Past),
]);
show_enum_dropdown(guess, options, onchange.reform(|enum_value| {
SQLValueGuess::Time(enum_value)
}))
},
SQLValueGuess::Datetime(guess) => {
let options = HashMap::from([
("Now".into() , SQLTimeValueGuess::Now),
("Future".into(), SQLTimeValueGuess::Future),
("Past".into() , SQLTimeValueGuess::Past),
]);
show_enum_dropdown(guess, options, onchange.reform(|enum_value| {
SQLValueGuess::Datetime(enum_value)
}))
}
SQLValueGuess::Bool(guess) => {
let options = HashMap::from([
("Random".into(), SQLBoolValueGuess::Random),
("True".into() , SQLBoolValueGuess::True),
("False".into() , SQLBoolValueGuess::False),
]);
show_enum_dropdown(guess, options, onchange.reform(|enum_value| {
SQLValueGuess::Bool(enum_value)
}))
},
SQLValueGuess::String(max_size, guess) => {
if let Some(constraint) = &column.check_constraint {
if let SQLCheckConstraint::OneOf(_) = constraint {
return html!("Random Enum");
}
}
let options = HashMap::from([
("Lorem Ipsum".into() , SQLStringValueGuess::LoremIpsum),
("First Name".into() , SQLStringValueGuess::FirstName),
("Last Name".into() , SQLStringValueGuess::LastName),
("Full Name".into() , SQLStringValueGuess::FullName),
("Empty".into() , SQLStringValueGuess::Empty),
("Phone number".into() , SQLStringValueGuess::PhoneNumber),
("City name".into() , SQLStringValueGuess::CityName),
("Address".into() , SQLStringValueGuess::Address),
("Email".into() , SQLStringValueGuess::Email),
("URL".into() , SQLStringValueGuess::URL),
]);
let max_size = *max_size;
show_enum_dropdown(guess, options, onchange.reform(move |enum_value| {
SQLValueGuess::String(max_size, enum_value)
}))
},
}
}

View File

@ -1 +1,2 @@
pub mod sql_column_info; pub mod sql_column_info;
pub mod generator_picker;

View File

@ -1,12 +1,14 @@
use std::rc::Rc; use std::{rc::Rc, collections::HashMap, cell::RefCell};
use yew::{Properties, html, function_component, Html}; use yew::{Properties, html, function_component, Html, Callback};
use crate::magicdraw_parser::SQLTable; use crate::{magicdraw_parser::SQLTable, generate_sql::SQLValueGuess, components::generator_picker::generator_picker};
#[derive(Properties, PartialEq)] #[derive(Properties, PartialEq)]
pub struct SQLTableColumnInfoProps { pub struct SQLTableColumnInfoProps {
pub table: Rc<SQLTable> pub table: Rc<SQLTable>,
pub guessess: Rc<RefCell<HashMap<String, SQLValueGuess>>>,
pub onchange: Callback<(String, SQLValueGuess)>
} }
const CHECK_MARK: &str = "✔️"; const CHECK_MARK: &str = "✔️";
@ -22,24 +24,36 @@ pub fn SQLTableColumnInfo(props: &SQLTableColumnInfoProps) -> Html {
let rows = table.columns.iter() let rows = table.columns.iter()
.map(|col| { .map(|col| {
let foreign_key; let guessess = &props.guessess.borrow();
if let Some((table_name, prop_name)) = &col.foreign_key { let generator = guessess.get(&col.name);
foreign_key = format!("{} {}", table_name, prop_name);
} else {
foreign_key = CROSS_MARK.into();
}
html! { let foreign_key;
<tr> if let Some((table_name, prop_name)) = &col.foreign_key {
<td> { &col.name } </td> foreign_key = format!("{} {}", table_name, prop_name);
<td> { &col.sql_type } </td> } else {
<td> { bool_to_mark(col.primary_key) } </td> foreign_key = CROSS_MARK.into();
<td> { bool_to_mark(col.nullable) } </td> }
<td> { foreign_key } </td>
</tr> let name = col.name.clone();
} let onchange = props.onchange.reform(move |value: SQLValueGuess| (name.clone(), value));
} html! {
); <tr>
<td> { &col.name } </td>
<td> { &col.sql_type } </td>
<td> {
if let Some(generator) = generator {
generator_picker(col, generator, onchange)
} else {
html!(CROSS_MARK)
}
} </td>
<td> { bool_to_mark(col.primary_key) } </td>
<td> { bool_to_mark(col.nullable) } </td>
<td> { foreign_key } </td>
</tr>
}
}
);
html!{ html!{
<div <div
@ -47,12 +61,11 @@ pub fn SQLTableColumnInfo(props: &SQLTableColumnInfoProps) -> Html {
border="solid dark100 0.2rem collapse" border="solid dark100 0.2rem collapse"
> >
<p class="text-center"> { &table.name } </p> <p class="text-center"> { &table.name } </p>
<table <table border="solid dark100 t-0.2rem collapse">
border="solid dark100 t-0.2rem collapse"
>
<tr> <tr>
<th> { "Column" } </th> <th> { "Column" } </th>
<th> { "Type" } </th> <th> { "Type" } </th>
<th> { "Generator" } </th>
<th> { "Primary?" } </th> <th> { "Primary?" } </th>
<th> { "Nullable?" } </th> <th> { "Nullable?" } </th>
<th> { "Foreign key?" } </th> <th> { "Foreign key?" } </th>

View File

@ -1,4 +1,4 @@
use std::{rc::Rc, collections::HashSet}; use std::{rc::Rc, collections::{HashSet, HashMap}};
use anyhow::{Result, bail}; use anyhow::{Result, bail};
use rand::{seq::SliceRandom, Rng, rngs::ThreadRng}; use rand::{seq::SliceRandom, Rng, rngs::ThreadRng};
@ -9,20 +9,20 @@ use crate::magicdraw_parser::{SQLTable, SQLColumn, SQLType, SQLCheckConstraint};
const INDENT: &str = " "; const INDENT: &str = " ";
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub enum SQLIntValueGuess { pub enum SQLIntValueGuess {
Range(i32, i32), Range(i32, i32),
AutoIncrement AutoIncrement
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub enum SQLTimeValueGuess { pub enum SQLTimeValueGuess {
Now, Now,
Future, Future,
Past Past
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub enum SQLStringValueGuess { pub enum SQLStringValueGuess {
LoremIpsum, LoremIpsum,
FirstName, FirstName,
@ -37,14 +37,14 @@ pub enum SQLStringValueGuess {
RandomEnum(Vec<String>), RandomEnum(Vec<String>),
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub enum SQLBoolValueGuess { pub enum SQLBoolValueGuess {
True, True,
False, False,
Random, Random,
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub enum SQLValueGuess { pub enum SQLValueGuess {
Int(SQLIntValueGuess), Int(SQLIntValueGuess),
Date(SQLTimeValueGuess), Date(SQLTimeValueGuess),
@ -58,7 +58,7 @@ pub enum SQLValueGuess {
// TODO: Check primary key constraint // TODO: Check primary key constraint
pub fn generate_fake_entries( pub fn generate_fake_entries(
tables: &[Rc<SQLTable>], tables: &[Rc<SQLTable>],
value_guessess: &Vec<Vec<SQLValueGuess>>, value_guessess: &Vec<HashMap<&str, SQLValueGuess>>,
rows_per_table: u32 rows_per_table: u32
) -> Result<String> { ) -> Result<String> {
let mut lines = vec![]; let mut lines = vec![];
@ -96,9 +96,9 @@ pub fn generate_fake_entries(
for (table_idx, table) in tables.iter().enumerate() { for (table_idx, table) in tables.iter().enumerate() {
let entries = &mut all_entries[table_idx]; let entries = &mut all_entries[table_idx];
for (column_idx, column) in table.columns.iter().enumerate() { for column in &table.columns {
let mut auto_increment_counter = 0; let mut auto_increment_counter = 0;
let value_guess = &value_guessess[table_idx][column_idx]; let value_guess = value_guessess[table_idx].get(column.name.as_str()).expect("Failed to get column guess");
for entry_idx in 0..(rows_per_table as usize) { for entry_idx in 0..(rows_per_table as usize) {
if let Some(_) = &column.foreign_key { if let Some(_) = &column.foreign_key {
entries_with_foreign_keys.insert((table_idx, entry_idx)); entries_with_foreign_keys.insert((table_idx, entry_idx));
@ -358,8 +358,9 @@ pub fn generate_guess(column: &SQLColumn) -> SQLValueGuess {
} }
} }
pub fn generate_table_guessess(table: &SQLTable) -> Vec<SQLValueGuess> { pub fn generate_table_guessess(table: &SQLTable) -> HashMap<String, SQLValueGuess> {
table.columns.iter() table.columns.iter()
.map(|column| generate_guess(column)) .filter(|column| column.foreign_key.is_none())
.map(|column| (column.name.clone(), generate_guess(column)))
.collect() .collect()
} }

View File

@ -7,3 +7,15 @@
button { button {
border: 0 border: 0
} }
/* Chrome, Safari, Edge, Opera */
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
/* Firefox */
input[type=number] {
-moz-appearance: textfield;
}