reformat code
This commit is contained in:
parent
bb52e3e84f
commit
004560ca3d
1
rustfmt.toml
Normal file
1
rustfmt.toml
Normal file
@ -0,0 +1 @@
|
|||||||
|
hard_tabs = true
|
43
src/app.rs
43
src/app.rs
@ -1,18 +1,18 @@
|
|||||||
use std::cell::RefCell;
|
use gloo::console::{console, console_dbg};
|
||||||
use std::io::Cursor;
|
|
||||||
use std::collections::{HashMap, self};
|
|
||||||
use std::rc::Rc;
|
|
||||||
use gloo::console::{console_dbg, console};
|
|
||||||
use gloo::file::callbacks::FileReader;
|
use gloo::file::callbacks::FileReader;
|
||||||
use gloo::file::File;
|
use gloo::file::File;
|
||||||
use gloo::storage::{LocalStorage, Storage};
|
use gloo::storage::{LocalStorage, Storage};
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::collections::{self, HashMap};
|
||||||
|
use std::io::Cursor;
|
||||||
|
use std::rc::Rc;
|
||||||
use web_sys::{DragEvent, Event, FileList, HtmlInputElement, MouseEvent};
|
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, generate_fake_entries};
|
|
||||||
use crate::magicdraw_parser::{parse_project, SQLTableCollection, SQLTable};
|
|
||||||
use crate::components::sql_column_info::SQLTableColumnInfo;
|
use crate::components::sql_column_info::SQLTableColumnInfo;
|
||||||
|
use crate::generate_sql::{generate_fake_entries, generate_table_guessess, SQLValueGuess};
|
||||||
|
use crate::magicdraw_parser::{parse_project, SQLTable, SQLTableCollection};
|
||||||
|
|
||||||
const COLLECTION_STORE_KEY: &str = "current_collection";
|
const COLLECTION_STORE_KEY: &str = "current_collection";
|
||||||
const DEFAULT_ROWS_PER_TABLE: u32 = 20;
|
const DEFAULT_ROWS_PER_TABLE: u32 = 20;
|
||||||
@ -37,7 +37,7 @@ pub struct App {
|
|||||||
currently_shown_table: usize,
|
currently_shown_table: usize,
|
||||||
all_good_confirmed: bool,
|
all_good_confirmed: bool,
|
||||||
generated_sql: Option<String>,
|
generated_sql: Option<String>,
|
||||||
rows_per_table: u32
|
rows_per_table: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Component for App {
|
impl Component for App {
|
||||||
@ -63,7 +63,7 @@ impl Component for App {
|
|||||||
all_good_confirmed: true, // TODO: make this false, by default
|
all_good_confirmed: true, // TODO: make this false, by default
|
||||||
generated_sql: None,
|
generated_sql: None,
|
||||||
current_guessess,
|
current_guessess,
|
||||||
rows_per_table: DEFAULT_ROWS_PER_TABLE
|
rows_per_table: DEFAULT_ROWS_PER_TABLE,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,16 +93,13 @@ impl Component for App {
|
|||||||
|
|
||||||
gloo::file::callbacks::read_as_bytes(&file, move |res| {
|
gloo::file::callbacks::read_as_bytes(&file, move |res| {
|
||||||
// TODO: show error message
|
// TODO: show error message
|
||||||
link.send_message(Msg::Loaded(
|
link.send_message(Msg::Loaded(file_name, res.expect("failed to read file")))
|
||||||
file_name,
|
|
||||||
res.expect("failed to read file"),
|
|
||||||
))
|
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
self.active_readers.insert(file_name, task);
|
self.active_readers.insert(file_name, task);
|
||||||
true
|
true
|
||||||
},
|
}
|
||||||
Msg::Noop => false,
|
Msg::Noop => false,
|
||||||
Msg::UpdateCurrentProject(collection) => {
|
Msg::UpdateCurrentProject(collection) => {
|
||||||
if let Some(collection) = collection {
|
if let Some(collection) = collection {
|
||||||
@ -115,38 +112,40 @@ impl Component for App {
|
|||||||
let guess = generate_table_guessess(table);
|
let guess = generate_table_guessess(table);
|
||||||
self.current_guessess.push(Rc::new(RefCell::new(guess)));
|
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);
|
||||||
self.current_collection = None
|
self.current_collection = None
|
||||||
}
|
}
|
||||||
|
|
||||||
true
|
true
|
||||||
},
|
}
|
||||||
Msg::ShowNextTable => {
|
Msg::ShowNextTable => {
|
||||||
if let Some(collection) = &self.current_collection {
|
if let Some(collection) = &self.current_collection {
|
||||||
self.currently_shown_table = (self.currently_shown_table + 1).min(collection.len()-1);
|
self.currently_shown_table =
|
||||||
|
(self.currently_shown_table + 1).min(collection.len() - 1);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
},
|
}
|
||||||
Msg::ShowPrevTable => {
|
Msg::ShowPrevTable => {
|
||||||
if self.currently_shown_table > 0 {
|
if self.currently_shown_table > 0 {
|
||||||
self.currently_shown_table = self.currently_shown_table - 1;
|
self.currently_shown_table = self.currently_shown_table - 1;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
},
|
}
|
||||||
Msg::AllGoodConfirmation => {
|
Msg::AllGoodConfirmation => {
|
||||||
self.all_good_confirmed = true;
|
self.all_good_confirmed = true;
|
||||||
true
|
true
|
||||||
},
|
}
|
||||||
Msg::UpdateGenarator(column, generator) => {
|
Msg::UpdateGenarator(column, generator) => {
|
||||||
let mut guessess = self.current_guessess[self.currently_shown_table].borrow_mut();
|
let mut guessess = self.current_guessess[self.currently_shown_table].borrow_mut();
|
||||||
let entry = guessess.get_mut(&column).unwrap();
|
let entry = guessess.get_mut(&column).unwrap();
|
||||||
*entry = generator;
|
*entry = generator;
|
||||||
true
|
true
|
||||||
},
|
}
|
||||||
Msg::GenerateSQL => {
|
Msg::GenerateSQL => {
|
||||||
let tables = self.current_collection.as_ref().unwrap();
|
let tables = self.current_collection.as_ref().unwrap();
|
||||||
let guessess = self.current_guessess.iter().map(|v| v.borrow()).collect();
|
let guessess = self.current_guessess.iter().map(|v| v.borrow()).collect();
|
||||||
@ -157,7 +156,7 @@ impl Component for App {
|
|||||||
self.generated_sql = None
|
self.generated_sql = None
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
},
|
}
|
||||||
Msg::UpdateRowsPerTable(rows_per_table) => {
|
Msg::UpdateRowsPerTable(rows_per_table) => {
|
||||||
self.rows_per_table = rows_per_table;
|
self.rows_per_table = rows_per_table;
|
||||||
false
|
false
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
use std::{collections::HashMap, str::FromStr};
|
use std::{collections::HashMap, str::FromStr};
|
||||||
|
|
||||||
use yew::{Html, html, Callback, TargetCast, AttrValue};
|
|
||||||
use web_sys::{Event, HtmlInputElement};
|
use web_sys::{Event, HtmlInputElement};
|
||||||
|
use yew::{html, AttrValue, Callback, Html, TargetCast};
|
||||||
|
|
||||||
use crate::{generate_sql::{SQLValueGuess, SQLStringValueGuess, SQLBoolValueGuess, SQLTimeValueGuess, SQLIntValueGuess}, magicdraw_parser::{SQLColumn, SQLCheckConstraint}};
|
use crate::{
|
||||||
|
generate_sql::{
|
||||||
|
SQLBoolValueGuess, SQLIntValueGuess, SQLStringValueGuess, SQLTimeValueGuess, SQLValueGuess,
|
||||||
|
},
|
||||||
|
magicdraw_parser::{SQLCheckConstraint, SQLColumn},
|
||||||
|
};
|
||||||
|
|
||||||
fn show_dropdown_picker(
|
fn show_dropdown_picker(selected: &str, options: &[AttrValue], onchange: Callback<String>) -> Html {
|
||||||
selected: &str,
|
html! {
|
||||||
options: &[AttrValue],
|
|
||||||
onchange: Callback<String>
|
|
||||||
) -> Html {
|
|
||||||
html!{
|
|
||||||
<select onchange={Callback::from(move |e: Event| {
|
<select onchange={Callback::from(move |e: Event| {
|
||||||
let value = e.target_unchecked_into::<HtmlInputElement>().value();
|
let value = e.target_unchecked_into::<HtmlInputElement>().value();
|
||||||
onchange.emit(value);
|
onchange.emit(value);
|
||||||
@ -27,24 +28,30 @@ fn show_dropdown_picker(
|
|||||||
fn show_enum_dropdown<T: PartialEq + Clone + 'static>(
|
fn show_enum_dropdown<T: PartialEq + Clone + 'static>(
|
||||||
selected: &T,
|
selected: &T,
|
||||||
options: HashMap<AttrValue, T>,
|
options: HashMap<AttrValue, T>,
|
||||||
onchange: Callback<T>
|
onchange: Callback<T>,
|
||||||
) -> Html {
|
) -> Html {
|
||||||
let keys = options.keys().map(AttrValue::clone).collect::<Vec<_>>();
|
let keys = options.keys().map(AttrValue::clone).collect::<Vec<_>>();
|
||||||
let guess_str = options.iter().find(|(_, v)| v.eq(&selected)).unwrap().0.clone();
|
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| {
|
show_dropdown_picker(
|
||||||
options.get(value_str.as_str()).unwrap().clone()
|
&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>(
|
fn show_range_picker<T: FromStr + ToString + Clone + 'static>(
|
||||||
min: T,
|
min: T,
|
||||||
max: T,
|
max: T,
|
||||||
default_min: T,
|
default_min: T,
|
||||||
default_max: T,
|
default_max: T,
|
||||||
onchange: Callback<(T, T)>,
|
onchange: Callback<(T, T)>,
|
||||||
) -> Html {
|
) -> Html {
|
||||||
|
|
||||||
let onchange_min = {
|
let onchange_min = {
|
||||||
let onchange = onchange.clone();
|
let onchange = onchange.clone();
|
||||||
let default_min = default_min.clone();
|
let default_min = default_min.clone();
|
||||||
@ -91,13 +98,13 @@ fn show_range_picker<T: FromStr + ToString + Clone + 'static>(
|
|||||||
pub fn generator_picker(
|
pub fn generator_picker(
|
||||||
column: &SQLColumn,
|
column: &SQLColumn,
|
||||||
value: &SQLValueGuess,
|
value: &SQLValueGuess,
|
||||||
onchange: Callback<SQLValueGuess>
|
onchange: Callback<SQLValueGuess>,
|
||||||
) -> Html {
|
) -> Html {
|
||||||
// TODO: Refacotr 'time', 'datetime', and 'date'. They are very similar
|
// TODO: Refacotr 'time', 'datetime', and 'date'. They are very similar
|
||||||
match value {
|
match value {
|
||||||
SQLValueGuess::Int(guess) => {
|
SQLValueGuess::Int(guess) => {
|
||||||
if column.primary_key {
|
if column.primary_key {
|
||||||
return html!("Auto increment")
|
return html!("Auto increment");
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut min = 0;
|
let mut min = 0;
|
||||||
@ -108,59 +115,73 @@ pub fn generator_picker(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Disallow entering floating point numbers
|
// TODO: Disallow entering floating point numbers
|
||||||
show_range_picker(min, max, 0, 100, onchange.reform(|(min, max)| {
|
show_range_picker(
|
||||||
SQLValueGuess::Int(SQLIntValueGuess::Range(min, max))
|
min,
|
||||||
}))
|
max,
|
||||||
},
|
0,
|
||||||
SQLValueGuess::Float(min, max) => {
|
100,
|
||||||
show_range_picker(*min, *max, 0.0, 100.0, onchange.reform(|(min, max)| {
|
onchange.reform(|(min, max)| SQLValueGuess::Int(SQLIntValueGuess::Range(min, max))),
|
||||||
SQLValueGuess::Float(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) => {
|
SQLValueGuess::Date(guess) => {
|
||||||
let options = HashMap::from([
|
let options = HashMap::from([
|
||||||
("Now".into() , SQLTimeValueGuess::Now),
|
("Now".into(), SQLTimeValueGuess::Now),
|
||||||
("Future".into(), SQLTimeValueGuess::Future),
|
("Future".into(), SQLTimeValueGuess::Future),
|
||||||
("Past".into() , SQLTimeValueGuess::Past),
|
("Past".into(), SQLTimeValueGuess::Past),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
show_enum_dropdown(guess, options, onchange.reform(|enum_value| {
|
show_enum_dropdown(
|
||||||
SQLValueGuess::Date(enum_value)
|
guess,
|
||||||
}))
|
options,
|
||||||
},
|
onchange.reform(|enum_value| SQLValueGuess::Date(enum_value)),
|
||||||
|
)
|
||||||
|
}
|
||||||
SQLValueGuess::Time(guess) => {
|
SQLValueGuess::Time(guess) => {
|
||||||
let options = HashMap::from([
|
let options = HashMap::from([
|
||||||
("Now".into() , SQLTimeValueGuess::Now),
|
("Now".into(), SQLTimeValueGuess::Now),
|
||||||
("Future".into(), SQLTimeValueGuess::Future),
|
("Future".into(), SQLTimeValueGuess::Future),
|
||||||
("Past".into() , SQLTimeValueGuess::Past),
|
("Past".into(), SQLTimeValueGuess::Past),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
show_enum_dropdown(guess, options, onchange.reform(|enum_value| {
|
show_enum_dropdown(
|
||||||
SQLValueGuess::Time(enum_value)
|
guess,
|
||||||
}))
|
options,
|
||||||
},
|
onchange.reform(|enum_value| SQLValueGuess::Time(enum_value)),
|
||||||
|
)
|
||||||
|
}
|
||||||
SQLValueGuess::Datetime(guess) => {
|
SQLValueGuess::Datetime(guess) => {
|
||||||
let options = HashMap::from([
|
let options = HashMap::from([
|
||||||
("Now".into() , SQLTimeValueGuess::Now),
|
("Now".into(), SQLTimeValueGuess::Now),
|
||||||
("Future".into(), SQLTimeValueGuess::Future),
|
("Future".into(), SQLTimeValueGuess::Future),
|
||||||
("Past".into() , SQLTimeValueGuess::Past),
|
("Past".into(), SQLTimeValueGuess::Past),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
show_enum_dropdown(guess, options, onchange.reform(|enum_value| {
|
show_enum_dropdown(
|
||||||
SQLValueGuess::Datetime(enum_value)
|
guess,
|
||||||
}))
|
options,
|
||||||
|
onchange.reform(|enum_value| SQLValueGuess::Datetime(enum_value)),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
SQLValueGuess::Bool(guess) => {
|
SQLValueGuess::Bool(guess) => {
|
||||||
let options = HashMap::from([
|
let options = HashMap::from([
|
||||||
("Random".into(), SQLBoolValueGuess::Random),
|
("Random".into(), SQLBoolValueGuess::Random),
|
||||||
("True".into() , SQLBoolValueGuess::True),
|
("True".into(), SQLBoolValueGuess::True),
|
||||||
("False".into() , SQLBoolValueGuess::False),
|
("False".into(), SQLBoolValueGuess::False),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
show_enum_dropdown(guess, options, onchange.reform(|enum_value| {
|
show_enum_dropdown(
|
||||||
SQLValueGuess::Bool(enum_value)
|
guess,
|
||||||
}))
|
options,
|
||||||
},
|
onchange.reform(|enum_value| SQLValueGuess::Bool(enum_value)),
|
||||||
|
)
|
||||||
|
}
|
||||||
SQLValueGuess::String(max_size, guess) => {
|
SQLValueGuess::String(max_size, guess) => {
|
||||||
if let Some(constraint) = &column.check_constraint {
|
if let Some(constraint) = &column.check_constraint {
|
||||||
if let SQLCheckConstraint::OneOf(_) = constraint {
|
if let SQLCheckConstraint::OneOf(_) = constraint {
|
||||||
@ -169,22 +190,24 @@ pub fn generator_picker(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let options = HashMap::from([
|
let options = HashMap::from([
|
||||||
("Lorem Ipsum".into() , SQLStringValueGuess::LoremIpsum),
|
("Lorem Ipsum".into(), SQLStringValueGuess::LoremIpsum),
|
||||||
("First Name".into() , SQLStringValueGuess::FirstName),
|
("First Name".into(), SQLStringValueGuess::FirstName),
|
||||||
("Last Name".into() , SQLStringValueGuess::LastName),
|
("Last Name".into(), SQLStringValueGuess::LastName),
|
||||||
("Full Name".into() , SQLStringValueGuess::FullName),
|
("Full Name".into(), SQLStringValueGuess::FullName),
|
||||||
("Empty".into() , SQLStringValueGuess::Empty),
|
("Empty".into(), SQLStringValueGuess::Empty),
|
||||||
("Phone number".into() , SQLStringValueGuess::PhoneNumber),
|
("Phone number".into(), SQLStringValueGuess::PhoneNumber),
|
||||||
("City name".into() , SQLStringValueGuess::CityName),
|
("City name".into(), SQLStringValueGuess::CityName),
|
||||||
("Address".into() , SQLStringValueGuess::Address),
|
("Address".into(), SQLStringValueGuess::Address),
|
||||||
("Email".into() , SQLStringValueGuess::Email),
|
("Email".into(), SQLStringValueGuess::Email),
|
||||||
("URL".into() , SQLStringValueGuess::URL),
|
("URL".into(), SQLStringValueGuess::URL),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let max_size = *max_size;
|
let max_size = *max_size;
|
||||||
show_enum_dropdown(guess, options, onchange.reform(move |enum_value| {
|
show_enum_dropdown(
|
||||||
SQLValueGuess::String(max_size, enum_value)
|
guess,
|
||||||
}))
|
options,
|
||||||
},
|
onchange.reform(move |enum_value| SQLValueGuess::String(max_size, enum_value)),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
pub mod sql_column_info;
|
|
||||||
pub mod generator_picker;
|
pub mod generator_picker;
|
||||||
|
pub mod sql_column_info;
|
||||||
|
@ -1,61 +1,68 @@
|
|||||||
use std::{rc::Rc, collections::HashMap, cell::RefCell};
|
use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
||||||
|
|
||||||
use yew::{Properties, html, function_component, Html, Callback};
|
use yew::{function_component, html, Callback, Html, Properties};
|
||||||
|
|
||||||
use crate::{magicdraw_parser::SQLTable, generate_sql::SQLValueGuess, components::generator_picker::generator_picker};
|
use crate::{
|
||||||
|
components::generator_picker::generator_picker, generate_sql::SQLValueGuess,
|
||||||
|
magicdraw_parser::SQLTable,
|
||||||
|
};
|
||||||
|
|
||||||
#[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 guessess: Rc<RefCell<HashMap<String, SQLValueGuess>>>,
|
||||||
pub onchange: Callback<(String, SQLValueGuess)>
|
pub onchange: Callback<(String, SQLValueGuess)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const CHECK_MARK: &str = "✔️";
|
const CHECK_MARK: &str = "✔️";
|
||||||
const CROSS_MARK: &str = "❌";
|
const CROSS_MARK: &str = "❌";
|
||||||
|
|
||||||
fn bool_to_mark(value: bool) -> &'static str {
|
fn bool_to_mark(value: bool) -> &'static str {
|
||||||
if value { CHECK_MARK } else { CROSS_MARK }
|
if value {
|
||||||
|
CHECK_MARK
|
||||||
|
} else {
|
||||||
|
CROSS_MARK
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[function_component]
|
#[function_component]
|
||||||
pub fn SQLTableColumnInfo(props: &SQLTableColumnInfoProps) -> Html {
|
pub fn SQLTableColumnInfo(props: &SQLTableColumnInfoProps) -> Html {
|
||||||
let table = &props.table;
|
let table = &props.table;
|
||||||
|
|
||||||
let rows = table.columns.iter()
|
let rows = table.columns.iter().map(|col| {
|
||||||
.map(|col| {
|
let guessess = &props.guessess.borrow();
|
||||||
let guessess = &props.guessess.borrow();
|
let generator = guessess.get(&col.name);
|
||||||
let generator = guessess.get(&col.name);
|
|
||||||
|
|
||||||
let foreign_key;
|
let foreign_key;
|
||||||
if let Some((table_name, prop_name)) = &col.foreign_key {
|
if let Some((table_name, prop_name)) = &col.foreign_key {
|
||||||
foreign_key = format!("{} {}", table_name, prop_name);
|
foreign_key = format!("{} {}", table_name, prop_name);
|
||||||
} else {
|
} else {
|
||||||
foreign_key = CROSS_MARK.into();
|
foreign_key = CROSS_MARK.into();
|
||||||
}
|
|
||||||
|
|
||||||
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!{
|
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! {
|
||||||
<div
|
<div
|
||||||
class="table-column-info flex-column inline-block"
|
class="table-column-info flex-column inline-block"
|
||||||
border="solid dark100 0.2rem collapse"
|
border="solid dark100 0.2rem collapse"
|
||||||
|
@ -1,26 +1,40 @@
|
|||||||
use std::{rc::Rc, collections::{HashSet, HashMap}, cell::Ref};
|
use std::{
|
||||||
|
cell::Ref,
|
||||||
|
collections::{HashMap, HashSet},
|
||||||
|
rc::Rc,
|
||||||
|
};
|
||||||
|
|
||||||
use anyhow::{Result, bail};
|
use anyhow::{bail, Result};
|
||||||
|
use chrono::{Days, Local, NaiveDateTime};
|
||||||
|
use fake::{
|
||||||
|
faker::{
|
||||||
|
address::en::{CityName, StreetName},
|
||||||
|
company::en::BsNoun,
|
||||||
|
internet::en::{DomainSuffix, FreeEmail},
|
||||||
|
lorem::en::*,
|
||||||
|
name::en::{FirstName, LastName, Name},
|
||||||
|
phone_number::en::PhoneNumber,
|
||||||
|
},
|
||||||
|
Fake,
|
||||||
|
};
|
||||||
use gloo::console::console_dbg;
|
use gloo::console::console_dbg;
|
||||||
use rand::{seq::SliceRandom, Rng, rngs::ThreadRng};
|
use rand::{rngs::ThreadRng, seq::SliceRandom, Rng};
|
||||||
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};
|
use crate::magicdraw_parser::{SQLCheckConstraint, SQLColumn, SQLTable, SQLType};
|
||||||
|
|
||||||
const INDENT: &str = " ";
|
const INDENT: &str = " ";
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub enum SQLIntValueGuess {
|
pub enum SQLIntValueGuess {
|
||||||
Range(i32, i32),
|
Range(i32, i32),
|
||||||
AutoIncrement
|
AutoIncrement,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub enum SQLTimeValueGuess {
|
pub enum SQLTimeValueGuess {
|
||||||
Now,
|
Now,
|
||||||
Future,
|
Future,
|
||||||
Past
|
Past,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
@ -58,10 +72,10 @@ 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<Ref<HashMap<String, SQLValueGuess>>>,
|
value_guessess: &Vec<Ref<HashMap<String, SQLValueGuess>>>,
|
||||||
rows_per_table: u32
|
rows_per_table: u32,
|
||||||
) -> Result<String> {
|
) -> Result<String> {
|
||||||
let mut lines = vec![];
|
let mut lines = vec![];
|
||||||
|
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
@ -78,11 +92,13 @@ pub fn generate_fake_entries(
|
|||||||
let mut foreign_columns = vec![];
|
let mut foreign_columns = vec![];
|
||||||
for (i, column) in table.columns.iter().enumerate() {
|
for (i, column) in table.columns.iter().enumerate() {
|
||||||
if let Some((table_name, column_name)) = &column.foreign_key {
|
if let Some((table_name, column_name)) = &column.foreign_key {
|
||||||
let (table_idx, table) = tables.iter()
|
let (table_idx, table) = tables
|
||||||
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.find(|(_, table)| table.name.eq(table_name))
|
.find(|(_, table)| table.name.eq(table_name))
|
||||||
.expect("Foreign table not found");
|
.expect("Foreign table not found");
|
||||||
let (column_idx, _) = table.columns
|
let (column_idx, _) = table
|
||||||
|
.columns
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.find(|(_, column)| column.name.eq(column_name))
|
.find(|(_, column)| column.name.eq(column_name))
|
||||||
@ -109,7 +125,11 @@ pub fn generate_fake_entries(
|
|||||||
.get(column.name.as_str())
|
.get(column.name.as_str())
|
||||||
.expect("Failed to get column guess");
|
.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) {
|
||||||
entries[entry_idx].push(generate_value(&mut rng, &value_guess, &mut auto_increment_counter));
|
entries[entry_idx].push(generate_value(
|
||||||
|
&mut rng,
|
||||||
|
&value_guess,
|
||||||
|
&mut auto_increment_counter,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -120,19 +140,29 @@ pub fn generate_fake_entries(
|
|||||||
let before_retain = entries_with_foreign_keys.len();
|
let before_retain = entries_with_foreign_keys.len();
|
||||||
|
|
||||||
entries_with_foreign_keys.retain(|(table_idx, entry_idx)| {
|
entries_with_foreign_keys.retain(|(table_idx, entry_idx)| {
|
||||||
for (column_idx, foreign_table_idx, foreign_column_idx) in &all_foreign_columns[*table_idx] {
|
for (column_idx, foreign_table_idx, foreign_column_idx) in
|
||||||
|
&all_foreign_columns[*table_idx]
|
||||||
|
{
|
||||||
let available_values: Vec<&str>;
|
let available_values: Vec<&str>;
|
||||||
|
|
||||||
// If the foreign column, is also a foreign of the other table, ...
|
// 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
|
// 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() {
|
if all_foreign_columns[*foreign_table_idx]
|
||||||
available_values = all_entries[*foreign_table_idx].iter()
|
.iter()
|
||||||
|
.find(|(idx, _, _)| idx == foreign_column_idx)
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
available_values = all_entries[*foreign_table_idx]
|
||||||
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.filter(|(i, _)| entries_with_foreign_keys_copy.contains(&(*foreign_table_idx, *i)))
|
.filter(|(i, _)| {
|
||||||
|
entries_with_foreign_keys_copy.contains(&(*foreign_table_idx, *i))
|
||||||
|
})
|
||||||
.map(|(_, entry)| entry[*foreign_column_idx].as_str())
|
.map(|(_, entry)| entry[*foreign_column_idx].as_str())
|
||||||
.collect();
|
.collect();
|
||||||
} else {
|
} else {
|
||||||
available_values = all_entries[*foreign_table_idx].iter()
|
available_values = all_entries[*foreign_table_idx]
|
||||||
|
.iter()
|
||||||
.map(|entry| entry[*foreign_column_idx].as_str())
|
.map(|entry| entry[*foreign_column_idx].as_str())
|
||||||
.collect();
|
.collect();
|
||||||
}
|
}
|
||||||
@ -165,7 +195,8 @@ pub fn generate_fake_entries(
|
|||||||
lines.push(format!("INSERT INTO {}", table.name));
|
lines.push(format!("INSERT INTO {}", table.name));
|
||||||
lines.push(format!("{}({})", INDENT, column_names.join(", ")));
|
lines.push(format!("{}({})", INDENT, column_names.join(", ")));
|
||||||
lines.push("VALUES".into());
|
lines.push("VALUES".into());
|
||||||
let entries_str = entries.iter()
|
let entries_str = entries
|
||||||
|
.iter()
|
||||||
.map(|entry| format!("{}({})", INDENT, entry.join(", ")))
|
.map(|entry| format!("{}({})", INDENT, entry.join(", ")))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(",\n");
|
.join(",\n");
|
||||||
@ -183,7 +214,7 @@ fn generate_time_value(rng: &mut ThreadRng, guess: &SQLTimeValueGuess) -> NaiveD
|
|||||||
SQLTimeValueGuess::Future => {
|
SQLTimeValueGuess::Future => {
|
||||||
let days = rng.gen_range(1..=30);
|
let days = rng.gen_range(1..=30);
|
||||||
now.checked_add_days(Days::new(days)).unwrap()
|
now.checked_add_days(Days::new(days)).unwrap()
|
||||||
},
|
}
|
||||||
SQLTimeValueGuess::Past => {
|
SQLTimeValueGuess::Past => {
|
||||||
let days = rng.gen_range(7..=365);
|
let days = rng.gen_range(7..=365);
|
||||||
now.checked_sub_days(Days::new(days)).unwrap()
|
now.checked_sub_days(Days::new(days)).unwrap()
|
||||||
@ -191,93 +222,77 @@ fn generate_time_value(rng: &mut ThreadRng, guess: &SQLTimeValueGuess) -> NaiveD
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_value(rng: &mut ThreadRng, guess: &SQLValueGuess, auto_increment_counter: &mut u32) -> String {
|
fn generate_value(
|
||||||
|
rng: &mut ThreadRng,
|
||||||
|
guess: &SQLValueGuess,
|
||||||
|
auto_increment_counter: &mut u32,
|
||||||
|
) -> String {
|
||||||
match guess {
|
match guess {
|
||||||
SQLValueGuess::Int(int_guess) => {
|
SQLValueGuess::Int(int_guess) => match int_guess {
|
||||||
match int_guess {
|
SQLIntValueGuess::Range(min, max) => rng.gen_range((*min)..=(*max)).to_string(),
|
||||||
SQLIntValueGuess::Range(min, max) => {
|
SQLIntValueGuess::AutoIncrement => {
|
||||||
rng.gen_range((*min)..=(*max)).to_string()
|
let str = auto_increment_counter.to_string();
|
||||||
},
|
*auto_increment_counter += 1;
|
||||||
SQLIntValueGuess::AutoIncrement => {
|
str
|
||||||
let str = auto_increment_counter.to_string();
|
|
||||||
*auto_increment_counter += 1;
|
|
||||||
str
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
SQLValueGuess::Date(time_gues) => {
|
SQLValueGuess::Date(time_gues) => {
|
||||||
let datetime = generate_time_value(rng, &time_gues);
|
let datetime = generate_time_value(rng, &time_gues);
|
||||||
format!("'{}'", datetime.format("%Y-%m-%d"))
|
format!("'{}'", datetime.format("%Y-%m-%d"))
|
||||||
},
|
}
|
||||||
SQLValueGuess::Time(time_gues) => {
|
SQLValueGuess::Time(time_gues) => {
|
||||||
let datetime = generate_time_value(rng, &time_gues);
|
let datetime = generate_time_value(rng, &time_gues);
|
||||||
format!("'{}'", datetime.format("%H:%M:%S"))
|
format!("'{}'", datetime.format("%H:%M:%S"))
|
||||||
},
|
}
|
||||||
SQLValueGuess::Datetime(time_gues) => {
|
SQLValueGuess::Datetime(time_gues) => {
|
||||||
let datetime = generate_time_value(rng, &time_gues);
|
let datetime = generate_time_value(rng, &time_gues);
|
||||||
format!("'{}'", datetime.format("%Y-%m-%d %H:%M:%S"))
|
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::Bool(bool_guess) => {
|
SQLValueGuess::Float(min, max) => {
|
||||||
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));
|
let value = rng.gen_range((*min)..(*max));
|
||||||
((value * 100.0 as f32).round() / 100.0).to_string()
|
((value * 100.0 as f32).round() / 100.0).to_string()
|
||||||
},
|
}
|
||||||
SQLValueGuess::String(max_size, string_guess) => {
|
SQLValueGuess::String(max_size, string_guess) => {
|
||||||
let mut str = match string_guess {
|
let mut str = match string_guess {
|
||||||
SQLStringValueGuess::LoremIpsum => {
|
SQLStringValueGuess::LoremIpsum => {
|
||||||
|
|
||||||
let mut current_len = 0;
|
let mut current_len = 0;
|
||||||
let mut text = vec![];
|
let mut text = vec![];
|
||||||
let words: Vec<String> = Words(3..10).fake_with_rng(rng);
|
let words: Vec<String> = Words(3..10).fake_with_rng(rng);
|
||||||
for word in words {
|
for word in words {
|
||||||
current_len += word.len() + 1;
|
current_len += word.len() + 1;
|
||||||
text.push(word);
|
text.push(word);
|
||||||
if current_len > *max_size { break; }
|
if current_len > *max_size {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
text.join(" ").to_string()
|
text.join(" ").to_string()
|
||||||
},
|
}
|
||||||
SQLStringValueGuess::FirstName => {
|
SQLStringValueGuess::FirstName => FirstName().fake_with_rng(rng),
|
||||||
FirstName().fake_with_rng(rng)
|
SQLStringValueGuess::LastName => LastName().fake_with_rng(rng),
|
||||||
},
|
SQLStringValueGuess::FullName => Name().fake_with_rng(rng),
|
||||||
SQLStringValueGuess::LastName => {
|
SQLStringValueGuess::PhoneNumber => PhoneNumber().fake_with_rng(rng),
|
||||||
LastName().fake_with_rng(rng)
|
SQLStringValueGuess::CityName => CityName().fake_with_rng(rng),
|
||||||
},
|
SQLStringValueGuess::Address => StreetName().fake_with_rng(rng),
|
||||||
SQLStringValueGuess::FullName => {
|
SQLStringValueGuess::Email => FreeEmail().fake_with_rng(rng),
|
||||||
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 => {
|
SQLStringValueGuess::URL => {
|
||||||
let suffix: String = DomainSuffix().fake_with_rng(rng);
|
let suffix: String = DomainSuffix().fake_with_rng(rng);
|
||||||
let noun: String = BsNoun().fake_with_rng(rng);
|
let noun: String = BsNoun().fake_with_rng(rng);
|
||||||
let noun: String = noun.to_lowercase()
|
let noun: String = noun
|
||||||
|
.to_lowercase()
|
||||||
.chars()
|
.chars()
|
||||||
.map(|c| if c.is_whitespace() { '-' } else { c })
|
.map(|c| if c.is_whitespace() { '-' } else { c })
|
||||||
.collect();
|
.collect();
|
||||||
format!("www.{}.{}", noun, suffix)
|
format!("www.{}.{}", noun, suffix)
|
||||||
},
|
}
|
||||||
SQLStringValueGuess::RandomEnum(options) => {
|
SQLStringValueGuess::RandomEnum(options) => {
|
||||||
options.choose(rng).unwrap().to_string()
|
options.choose(rng).unwrap().to_string()
|
||||||
},
|
|
||||||
SQLStringValueGuess::Empty => {
|
|
||||||
"".into()
|
|
||||||
}
|
}
|
||||||
|
SQLStringValueGuess::Empty => "".into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
str.truncate(*max_size);
|
str.truncate(*max_size);
|
||||||
@ -289,74 +304,70 @@ fn generate_value(rng: &mut ThreadRng, guess: &SQLValueGuess, auto_increment_cou
|
|||||||
fn generate_string_guess(column: &SQLColumn) -> SQLStringValueGuess {
|
fn generate_string_guess(column: &SQLColumn) -> SQLStringValueGuess {
|
||||||
if let Some(constraint) = &column.check_constraint {
|
if let Some(constraint) = &column.check_constraint {
|
||||||
if let SQLCheckConstraint::OneOf(options) = constraint {
|
if let SQLCheckConstraint::OneOf(options) = constraint {
|
||||||
return SQLStringValueGuess::RandomEnum(options.clone())
|
return SQLStringValueGuess::RandomEnum(options.clone());
|
||||||
} else {
|
} else {
|
||||||
return SQLStringValueGuess::LoremIpsum
|
return SQLStringValueGuess::LoremIpsum;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let name = column.name.to_lowercase();
|
let name = column.name.to_lowercase();
|
||||||
if name.contains("first") && name.contains("name") {
|
if name.contains("first") && name.contains("name") {
|
||||||
SQLStringValueGuess::FirstName
|
SQLStringValueGuess::FirstName
|
||||||
} else if (name.contains("last") && name.contains("name")) || name.contains("surname") {
|
} else if (name.contains("last") && name.contains("name")) || name.contains("surname") {
|
||||||
SQLStringValueGuess::LastName
|
SQLStringValueGuess::LastName
|
||||||
} else if name.contains("phone") && name.contains("number") {
|
} else if name.contains("phone") && name.contains("number") {
|
||||||
SQLStringValueGuess::PhoneNumber
|
SQLStringValueGuess::PhoneNumber
|
||||||
} else if name.contains("city") {
|
} else if name.contains("city") {
|
||||||
SQLStringValueGuess::CityName
|
SQLStringValueGuess::CityName
|
||||||
} else if name.contains("address") {
|
} else if name.contains("address") {
|
||||||
SQLStringValueGuess::Address
|
SQLStringValueGuess::Address
|
||||||
} else if name.contains("email") {
|
} else if name.contains("email") {
|
||||||
SQLStringValueGuess::Email
|
SQLStringValueGuess::Email
|
||||||
} else if name.contains("homepage") || name.contains("website") || name.contains("url") {
|
} else if name.contains("homepage") || name.contains("website") || name.contains("url") {
|
||||||
SQLStringValueGuess::URL
|
SQLStringValueGuess::URL
|
||||||
} else {
|
} else {
|
||||||
SQLStringValueGuess::LoremIpsum
|
SQLStringValueGuess::LoremIpsum
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_guess(column: &SQLColumn) -> SQLValueGuess {
|
pub fn generate_guess(column: &SQLColumn) -> SQLValueGuess {
|
||||||
match column.sql_type {
|
match column.sql_type {
|
||||||
SQLType::Int => {
|
SQLType::Int => {
|
||||||
if column.primary_key {
|
if column.primary_key {
|
||||||
SQLValueGuess::Int(SQLIntValueGuess::AutoIncrement)
|
SQLValueGuess::Int(SQLIntValueGuess::AutoIncrement)
|
||||||
} else {
|
} else {
|
||||||
SQLValueGuess::Int(SQLIntValueGuess::Range(0, 100))
|
SQLValueGuess::Int(SQLIntValueGuess::Range(0, 100))
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
SQLType::Float | SQLType::Decimal => {
|
SQLType::Float | SQLType::Decimal => SQLValueGuess::Float(0.0, 100.0),
|
||||||
SQLValueGuess::Float(0.0, 100.0)
|
SQLType::Date => {
|
||||||
},
|
|
||||||
SQLType::Date => {
|
|
||||||
let name = column.name.to_lowercase();
|
let name = column.name.to_lowercase();
|
||||||
if name.contains("create") || name.contains("update") {
|
if name.contains("create") || name.contains("update") {
|
||||||
SQLValueGuess::Date(SQLTimeValueGuess::Past)
|
SQLValueGuess::Date(SQLTimeValueGuess::Past)
|
||||||
} else {
|
} else {
|
||||||
SQLValueGuess::Date(SQLTimeValueGuess::Now)
|
SQLValueGuess::Date(SQLTimeValueGuess::Now)
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
SQLType::Time => {
|
SQLType::Time => {
|
||||||
let name = column.name.to_lowercase();
|
let name = column.name.to_lowercase();
|
||||||
if name.contains("create") || name.contains("update") {
|
if name.contains("create") || name.contains("update") {
|
||||||
SQLValueGuess::Time(SQLTimeValueGuess::Past)
|
SQLValueGuess::Time(SQLTimeValueGuess::Past)
|
||||||
} else {
|
} else {
|
||||||
SQLValueGuess::Time(SQLTimeValueGuess::Now)
|
SQLValueGuess::Time(SQLTimeValueGuess::Now)
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
SQLType::Datetime => {
|
SQLType::Datetime => {
|
||||||
let name = column.name.to_lowercase();
|
let name = column.name.to_lowercase();
|
||||||
if name.contains("create") || name.contains("update") {
|
if name.contains("create") || name.contains("update") {
|
||||||
SQLValueGuess::Datetime(SQLTimeValueGuess::Past)
|
SQLValueGuess::Datetime(SQLTimeValueGuess::Past)
|
||||||
} else {
|
} else {
|
||||||
SQLValueGuess::Datetime(SQLTimeValueGuess::Now)
|
SQLValueGuess::Datetime(SQLTimeValueGuess::Now)
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
SQLType::Bool => {
|
SQLType::Bool => SQLValueGuess::Bool(SQLBoolValueGuess::Random),
|
||||||
SQLValueGuess::Bool(SQLBoolValueGuess::Random)
|
|
||||||
},
|
|
||||||
SQLType::Varchar(max_size) => {
|
SQLType::Varchar(max_size) => {
|
||||||
SQLValueGuess::String(max_size as usize, generate_string_guess(column))
|
SQLValueGuess::String(max_size as usize, generate_string_guess(column))
|
||||||
},
|
}
|
||||||
SQLType::Char(max_size) => {
|
SQLType::Char(max_size) => {
|
||||||
SQLValueGuess::String(max_size as usize, generate_string_guess(column))
|
SQLValueGuess::String(max_size as usize, generate_string_guess(column))
|
||||||
}
|
}
|
||||||
@ -364,7 +375,9 @@ pub fn generate_guess(column: &SQLColumn) -> SQLValueGuess {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_table_guessess(table: &SQLTable) -> HashMap<String, SQLValueGuess> {
|
pub fn generate_table_guessess(table: &SQLTable) -> HashMap<String, SQLValueGuess> {
|
||||||
table.columns.iter()
|
table
|
||||||
|
.columns
|
||||||
|
.iter()
|
||||||
.filter(|column| column.foreign_key.is_none())
|
.filter(|column| column.foreign_key.is_none())
|
||||||
.map(|column| (column.name.clone(), generate_guess(column)))
|
.map(|column| (column.name.clone(), generate_guess(column)))
|
||||||
.collect()
|
.collect()
|
||||||
|
@ -1,31 +1,31 @@
|
|||||||
use std::io::{Read, Seek};
|
use std::io::{Read, Seek};
|
||||||
|
|
||||||
|
use anyhow::{Context, Ok, Result};
|
||||||
use xml::attribute::OwnedAttribute;
|
use xml::attribute::OwnedAttribute;
|
||||||
use xml::name::OwnedName;
|
use xml::name::OwnedName;
|
||||||
use xml::{EventReader, reader::XmlEvent};
|
use xml::{reader::XmlEvent, EventReader};
|
||||||
use zip::ZipArchive;
|
use zip::ZipArchive;
|
||||||
use anyhow::{Result, Context, Ok};
|
|
||||||
|
|
||||||
use crate::magicdraw_parser::utils::get_attribute;
|
use crate::magicdraw_parser::utils::get_attribute;
|
||||||
|
|
||||||
use super::utils::{check_name, check_attribute, MyEventReader, parse_element};
|
use super::utils::{check_attribute, check_name, parse_element, MyEventReader};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct DDLClass {
|
pub struct DDLClass {
|
||||||
pub class_id: String,
|
pub class_id: String,
|
||||||
pub property_ids: Vec<String>
|
pub property_ids: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct DDLScript {
|
pub struct DDLScript {
|
||||||
pub script_id: String,
|
pub script_id: String,
|
||||||
pub classess: Vec<DDLClass>
|
pub classess: Vec<DDLClass>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct DDLProject {
|
pub struct DDLProject {
|
||||||
pub model_id: String,
|
pub model_id: String,
|
||||||
pub scripts: Vec<DDLScript>
|
pub scripts: Vec<DDLScript>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_id_from_href(attrs: &[OwnedAttribute]) -> Option<String> {
|
fn get_id_from_href(attrs: &[OwnedAttribute]) -> Option<String> {
|
||||||
@ -34,16 +34,21 @@ fn get_id_from_href(attrs: &[OwnedAttribute]) -> Option<String> {
|
|||||||
Some(parts.1.to_string())
|
Some(parts.1.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_class<R: Read>(parser: &mut MyEventReader<R>, attrs: &[OwnedAttribute]) -> Result<DDLClass> {
|
fn parse_class<R: Read>(
|
||||||
|
parser: &mut MyEventReader<R>,
|
||||||
|
attrs: &[OwnedAttribute],
|
||||||
|
) -> Result<DDLClass> {
|
||||||
let mut property_ids = vec![];
|
let mut property_ids = vec![];
|
||||||
let mut class_id = None;
|
let mut class_id = None;
|
||||||
|
|
||||||
fn is_model_element(name: &OwnedName, attributes: &[OwnedAttribute]) -> bool {
|
fn is_model_element(name: &OwnedName, attributes: &[OwnedAttribute]) -> bool {
|
||||||
check_name(name, None, "modelElement") && check_attribute(&attributes, Some("xsi"), "type", "uml:Class")
|
check_name(name, None, "modelElement")
|
||||||
|
&& check_attribute(&attributes, Some("xsi"), "type", "uml:Class")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_property_element(name: &OwnedName, attributes: &[OwnedAttribute]) -> bool {
|
fn is_property_element(name: &OwnedName, attributes: &[OwnedAttribute]) -> bool {
|
||||||
check_name(name, None, "modelElement") && check_attribute(&attributes, Some("xsi"), "type", "uml:Property")
|
check_name(name, None, "modelElement")
|
||||||
|
&& check_attribute(&attributes, Some("xsi"), "type", "uml:Property")
|
||||||
}
|
}
|
||||||
|
|
||||||
parse_element(parser, &mut |p, name, attrs| {
|
parse_element(parser, &mut |p, name, attrs| {
|
||||||
@ -57,20 +62,30 @@ fn parse_class<R: Read>(parser: &mut MyEventReader<R>, attrs: &[OwnedAttribute])
|
|||||||
|
|
||||||
Ok(DDLClass {
|
Ok(DDLClass {
|
||||||
class_id: class_id.context("Missing class id")?,
|
class_id: class_id.context("Missing class id")?,
|
||||||
property_ids
|
property_ids,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_script<R: Read>(parser: &mut MyEventReader<R>, attrs: &[OwnedAttribute]) -> Result<DDLScript> {
|
fn parse_script<R: Read>(
|
||||||
|
parser: &mut MyEventReader<R>,
|
||||||
|
attrs: &[OwnedAttribute],
|
||||||
|
) -> Result<DDLScript> {
|
||||||
let mut classess = vec![];
|
let mut classess = vec![];
|
||||||
let mut script_id = None;
|
let mut script_id = None;
|
||||||
|
|
||||||
fn is_model_element(name: &OwnedName, attributes: &[OwnedAttribute]) -> bool {
|
fn is_model_element(name: &OwnedName, attributes: &[OwnedAttribute]) -> bool {
|
||||||
check_name(name, None, "modelElement") && check_attribute(&attributes, Some("xsi"), "type", "uml:Component")
|
check_name(name, None, "modelElement")
|
||||||
|
&& check_attribute(&attributes, Some("xsi"), "type", "uml:Component")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_class_element(name: &OwnedName, attributes: &[OwnedAttribute]) -> bool {
|
fn is_class_element(name: &OwnedName, attributes: &[OwnedAttribute]) -> bool {
|
||||||
check_name(name, None, "objects") && check_attribute(&attributes, Some("xsi"), "type", "md.ce.rt.objects:RTClassObject")
|
check_name(name, None, "objects")
|
||||||
|
&& check_attribute(
|
||||||
|
&attributes,
|
||||||
|
Some("xsi"),
|
||||||
|
"type",
|
||||||
|
"md.ce.rt.objects:RTClassObject",
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
parse_element(parser, &mut |p, name, attrs| {
|
parse_element(parser, &mut |p, name, attrs| {
|
||||||
@ -84,20 +99,30 @@ fn parse_script<R: Read>(parser: &mut MyEventReader<R>, attrs: &[OwnedAttribute]
|
|||||||
|
|
||||||
Ok(DDLScript {
|
Ok(DDLScript {
|
||||||
script_id: script_id.context("Missing script id")?,
|
script_id: script_id.context("Missing script id")?,
|
||||||
classess
|
classess,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_project<R: Read>(parser: &mut MyEventReader<R>, attrs: &[OwnedAttribute]) -> Result<DDLProject> {
|
fn parse_project<R: Read>(
|
||||||
|
parser: &mut MyEventReader<R>,
|
||||||
|
attrs: &[OwnedAttribute],
|
||||||
|
) -> Result<DDLProject> {
|
||||||
let mut scripts = vec![];
|
let mut scripts = vec![];
|
||||||
let mut model_id = None;
|
let mut model_id = None;
|
||||||
|
|
||||||
fn is_model_element(name: &OwnedName, attributes: &[OwnedAttribute]) -> bool {
|
fn is_model_element(name: &OwnedName, attributes: &[OwnedAttribute]) -> bool {
|
||||||
check_name(name, None, "modelElement") && check_attribute(&attributes, Some("xsi"), "type", "uml:Model")
|
check_name(name, None, "modelElement")
|
||||||
|
&& check_attribute(&attributes, Some("xsi"), "type", "uml:Model")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_component_element(name: &OwnedName, attributes: &[OwnedAttribute]) -> bool {
|
fn is_component_element(name: &OwnedName, attributes: &[OwnedAttribute]) -> bool {
|
||||||
check_name(name, None, "objects") && check_attribute(&attributes, Some("xsi"), "type", "md.ce.rt.objects:RTComponent")
|
check_name(name, None, "objects")
|
||||||
|
&& check_attribute(
|
||||||
|
&attributes,
|
||||||
|
Some("xsi"),
|
||||||
|
"type",
|
||||||
|
"md.ce.rt.objects:RTComponent",
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
parse_element(parser, &mut |p, name, attrs| {
|
parse_element(parser, &mut |p, name, attrs| {
|
||||||
@ -111,28 +136,39 @@ fn parse_project<R: Read>(parser: &mut MyEventReader<R>, attrs: &[OwnedAttribute
|
|||||||
|
|
||||||
Ok(DDLProject {
|
Ok(DDLProject {
|
||||||
model_id: model_id.context("Missing model id")?,
|
model_id: model_id.context("Missing model id")?,
|
||||||
scripts
|
scripts,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_ddl_scripts<R: Read + Seek>(project: &mut ZipArchive<R>) -> Result<Vec<DDLProject>> {
|
pub fn parse_ddl_scripts<R: Read + Seek>(project: &mut ZipArchive<R>) -> Result<Vec<DDLProject>> {
|
||||||
let mut ddl_scripts = vec![];
|
let mut ddl_scripts = vec![];
|
||||||
|
|
||||||
let file = project.by_name("personal-com.nomagic.magicdraw.ce.dmn.personaldmncodeengineering")?;
|
let file =
|
||||||
|
project.by_name("personal-com.nomagic.magicdraw.ce.dmn.personaldmncodeengineering")?;
|
||||||
let mut parser: MyEventReader<_> = EventReader::new(file).into();
|
let mut parser: MyEventReader<_> = EventReader::new(file).into();
|
||||||
|
|
||||||
fn is_project_element(name: &OwnedName, attributes: &[OwnedAttribute]) -> bool {
|
fn is_project_element(name: &OwnedName, attributes: &[OwnedAttribute]) -> bool {
|
||||||
check_name(name, None, "contents") && check_attribute(&attributes, Some("xsi"), "type", "md.ce.ddl.rt.objects:DDLProjectObject")
|
check_name(name, None, "contents")
|
||||||
|
&& check_attribute(
|
||||||
|
&attributes,
|
||||||
|
Some("xsi"),
|
||||||
|
"type",
|
||||||
|
"md.ce.ddl.rt.objects:DDLProjectObject",
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
match parser.next()? {
|
match parser.next()? {
|
||||||
XmlEvent::StartElement { name, attributes, .. } => {
|
XmlEvent::StartElement {
|
||||||
|
name, attributes, ..
|
||||||
|
} => {
|
||||||
if is_project_element(&name, &attributes) {
|
if is_project_element(&name, &attributes) {
|
||||||
ddl_scripts.push(parse_project(&mut parser, &attributes)?);
|
ddl_scripts.push(parse_project(&mut parser, &attributes)?);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
XmlEvent::EndDocument => { break; },
|
XmlEvent::EndDocument => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,28 @@
|
|||||||
mod utils;
|
|
||||||
mod uml_model_parser;
|
|
||||||
mod ddl_parser;
|
mod ddl_parser;
|
||||||
mod sql_types_parser;
|
mod sql_types_parser;
|
||||||
use serde::{Serialize, Deserialize};
|
mod uml_model_parser;
|
||||||
|
mod utils;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use std::{io::{Read, Seek}, collections::HashSet, fmt::Display};
|
use anyhow::{Context, Result};
|
||||||
use anyhow::{Result, Context};
|
|
||||||
use lazy_regex::regex_captures;
|
use lazy_regex::regex_captures;
|
||||||
|
use std::{
|
||||||
|
collections::HashSet,
|
||||||
|
fmt::Display,
|
||||||
|
io::{Read, Seek},
|
||||||
|
};
|
||||||
use zip::ZipArchive;
|
use zip::ZipArchive;
|
||||||
|
|
||||||
use crate::unwrap_opt_continue;
|
use crate::unwrap_opt_continue;
|
||||||
|
|
||||||
use self::{uml_model_parser::{parse_uml_model, UMLModel, UMLClass, UMLModifier, UMLNullableModifier, UMLPrimaryKeyModifier, UMLTypeModifier, UMLForeignKeyModifier}, ddl_parser::parse_ddl_scripts, sql_types_parser::{parse_sql_types, SQLTypeName}};
|
use self::{
|
||||||
|
ddl_parser::parse_ddl_scripts,
|
||||||
|
sql_types_parser::{parse_sql_types, SQLTypeName},
|
||||||
|
uml_model_parser::{
|
||||||
|
parse_uml_model, UMLClass, UMLForeignKeyModifier, UMLModel, UMLModifier,
|
||||||
|
UMLNullableModifier, UMLPrimaryKeyModifier, UMLTypeModifier,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Deserialize, Serialize)]
|
#[derive(Debug, PartialEq, Deserialize, Serialize)]
|
||||||
pub enum SQLType {
|
pub enum SQLType {
|
||||||
@ -29,15 +40,15 @@ pub enum SQLType {
|
|||||||
impl Display for SQLType {
|
impl Display for SQLType {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
SQLType::Int => write!(f, "INT"),
|
SQLType::Int => write!(f, "INT"),
|
||||||
SQLType::Decimal => write!(f, "DECIMAL"),
|
SQLType::Decimal => write!(f, "DECIMAL"),
|
||||||
SQLType::Date => write!(f, "DATE"),
|
SQLType::Date => write!(f, "DATE"),
|
||||||
SQLType::Time => write!(f, "TIME"),
|
SQLType::Time => write!(f, "TIME"),
|
||||||
SQLType::Datetime => write!(f, "DATETIME"),
|
SQLType::Datetime => write!(f, "DATETIME"),
|
||||||
SQLType::Float => write!(f, "FLOAT"),
|
SQLType::Float => write!(f, "FLOAT"),
|
||||||
SQLType::Bool => write!(f, "BOOL"),
|
SQLType::Bool => write!(f, "BOOL"),
|
||||||
SQLType::Char(size) => write!(f, "CHAR({})", size),
|
SQLType::Char(size) => write!(f, "CHAR({})", size),
|
||||||
SQLType::Varchar(size) => write!(f, "VARCHAR({})", size),
|
SQLType::Varchar(size) => write!(f, "VARCHAR({})", size),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -45,7 +56,7 @@ impl Display for SQLType {
|
|||||||
#[derive(Debug, PartialEq, Deserialize, Serialize)]
|
#[derive(Debug, PartialEq, Deserialize, Serialize)]
|
||||||
pub enum SQLCheckConstraint {
|
pub enum SQLCheckConstraint {
|
||||||
OneOf(Vec<String>),
|
OneOf(Vec<String>),
|
||||||
Freeform(String)
|
Freeform(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Deserialize, Serialize)]
|
#[derive(Debug, PartialEq, Deserialize, Serialize)]
|
||||||
@ -55,7 +66,7 @@ pub struct SQLColumn {
|
|||||||
pub primary_key: bool,
|
pub primary_key: bool,
|
||||||
pub nullable: bool,
|
pub nullable: bool,
|
||||||
pub foreign_key: Option<(String, String)>,
|
pub foreign_key: Option<(String, String)>,
|
||||||
pub check_constraint: Option<SQLCheckConstraint>
|
pub check_constraint: Option<SQLCheckConstraint>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Deserialize, Serialize)]
|
#[derive(Debug, PartialEq, Deserialize, Serialize)]
|
||||||
@ -66,7 +77,7 @@ pub struct SQLTable {
|
|||||||
|
|
||||||
#[derive(Debug, PartialEq, Deserialize, Serialize)]
|
#[derive(Debug, PartialEq, Deserialize, Serialize)]
|
||||||
pub struct SQLTableCollection {
|
pub struct SQLTableCollection {
|
||||||
pub tables: Vec<SQLTable>
|
pub tables: Vec<SQLTable>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_class_by_id<'a>(models: &'a [UMLModel], id: &str) -> Option<&'a UMLClass> {
|
fn find_class_by_id<'a>(models: &'a [UMLModel], id: &str) -> Option<&'a UMLClass> {
|
||||||
@ -82,7 +93,11 @@ fn find_class_by_id<'a>(models: &'a [UMLModel], id: &str) -> Option<&'a UMLClass
|
|||||||
|
|
||||||
fn is_nullabe(modifiers: &[UMLModifier], property: &str) -> bool {
|
fn is_nullabe(modifiers: &[UMLModifier], property: &str) -> bool {
|
||||||
for modifier in modifiers {
|
for modifier in modifiers {
|
||||||
if let UMLModifier::Nullable(UMLNullableModifier { property_id, nullable }) = modifier {
|
if let UMLModifier::Nullable(UMLNullableModifier {
|
||||||
|
property_id,
|
||||||
|
nullable,
|
||||||
|
}) = modifier
|
||||||
|
{
|
||||||
if property_id.eq(property) {
|
if property_id.eq(property) {
|
||||||
return *nullable;
|
return *nullable;
|
||||||
}
|
}
|
||||||
@ -95,7 +110,7 @@ fn is_primary_key(modifiers: &[UMLModifier], property: &str) -> bool {
|
|||||||
for modifier in modifiers {
|
for modifier in modifiers {
|
||||||
if let UMLModifier::PirmaryKey(UMLPrimaryKeyModifier { property_id }) = modifier {
|
if let UMLModifier::PirmaryKey(UMLPrimaryKeyModifier { property_id }) = modifier {
|
||||||
if property_id.eq(property) {
|
if property_id.eq(property) {
|
||||||
return true
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -104,9 +119,13 @@ fn is_primary_key(modifiers: &[UMLModifier], property: &str) -> bool {
|
|||||||
|
|
||||||
fn get_type_modifier<'a>(modifiers: &'a [UMLModifier], property: &str) -> Option<&'a str> {
|
fn get_type_modifier<'a>(modifiers: &'a [UMLModifier], property: &str) -> Option<&'a str> {
|
||||||
for modifier in modifiers {
|
for modifier in modifiers {
|
||||||
if let UMLModifier::Type(UMLTypeModifier { property_id, modifier }) = modifier {
|
if let UMLModifier::Type(UMLTypeModifier {
|
||||||
|
property_id,
|
||||||
|
modifier,
|
||||||
|
}) = modifier
|
||||||
|
{
|
||||||
if property_id.eq(property) {
|
if property_id.eq(property) {
|
||||||
return Some(modifier)
|
return Some(modifier);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -115,19 +134,27 @@ fn get_type_modifier<'a>(modifiers: &'a [UMLModifier], property: &str) -> Option
|
|||||||
|
|
||||||
fn get_foreign_key_constraint<'a>(modifiers: &'a [UMLModifier], from_id: &str) -> Option<&'a str> {
|
fn get_foreign_key_constraint<'a>(modifiers: &'a [UMLModifier], from_id: &str) -> Option<&'a str> {
|
||||||
for modifier in modifiers {
|
for modifier in modifiers {
|
||||||
if let UMLModifier::ForeignKey(UMLForeignKeyModifier { from_property_id, to_property_id }) = modifier {
|
if let UMLModifier::ForeignKey(UMLForeignKeyModifier {
|
||||||
|
from_property_id,
|
||||||
|
to_property_id,
|
||||||
|
}) = modifier
|
||||||
|
{
|
||||||
if from_property_id.eq(from_id) {
|
if from_property_id.eq(from_id) {
|
||||||
return Some(&to_property_id)
|
return Some(&to_property_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_foreign_key(modifiers: &[UMLModifier], classess: &[&UMLClass], property: &str) -> Result<Option<(String, String)>> {
|
fn get_foreign_key(
|
||||||
|
modifiers: &[UMLModifier],
|
||||||
|
classess: &[&UMLClass],
|
||||||
|
property: &str,
|
||||||
|
) -> Result<Option<(String, String)>> {
|
||||||
let to_id = get_foreign_key_constraint(modifiers, property);
|
let to_id = get_foreign_key_constraint(modifiers, property);
|
||||||
if to_id.is_none() {
|
if to_id.is_none() {
|
||||||
return Ok(None)
|
return Ok(None);
|
||||||
}
|
}
|
||||||
let to_id = to_id.unwrap();
|
let to_id = to_id.unwrap();
|
||||||
|
|
||||||
@ -156,12 +183,14 @@ fn parse_check_constraint(str: &str) -> SQLCheckConstraint {
|
|||||||
Some(SQLCheckConstraint::OneOf(variants))
|
Some(SQLCheckConstraint::OneOf(variants))
|
||||||
}
|
}
|
||||||
|
|
||||||
try_parse_one_of(str)
|
try_parse_one_of(str).unwrap_or(SQLCheckConstraint::Freeform(str.to_string()))
|
||||||
.unwrap_or(SQLCheckConstraint::Freeform(str.to_string()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Refactor this function, less nesting would be good
|
// TODO: Refactor this function, less nesting would be good
|
||||||
fn get_sql_check_constraint<'a>(models: &'a [UMLModel], property_name: &str) -> Option<SQLCheckConstraint> {
|
fn get_sql_check_constraint<'a>(
|
||||||
|
models: &'a [UMLModel],
|
||||||
|
property_name: &str,
|
||||||
|
) -> Option<SQLCheckConstraint> {
|
||||||
for model in models {
|
for model in models {
|
||||||
for package in &model.packages {
|
for package in &model.packages {
|
||||||
for class in &package.classess {
|
for class in &package.classess {
|
||||||
@ -179,14 +208,18 @@ fn get_sql_check_constraint<'a>(models: &'a [UMLModel], property_name: &str) ->
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_sql_type(modifiers: &[UMLModifier], type_name: SQLTypeName, property: &str) -> Result<SQLType> {
|
fn get_sql_type(
|
||||||
|
modifiers: &[UMLModifier],
|
||||||
|
type_name: SQLTypeName,
|
||||||
|
property: &str,
|
||||||
|
) -> Result<SQLType> {
|
||||||
Ok(match type_name {
|
Ok(match type_name {
|
||||||
SQLTypeName::Int => SQLType::Int,
|
SQLTypeName::Int => SQLType::Int,
|
||||||
SQLTypeName::Date => SQLType::Date,
|
SQLTypeName::Date => SQLType::Date,
|
||||||
SQLTypeName::Float => SQLType::Float,
|
SQLTypeName::Float => SQLType::Float,
|
||||||
SQLTypeName::Bool => SQLType::Bool,
|
SQLTypeName::Bool => SQLType::Bool,
|
||||||
SQLTypeName::Decimal => SQLType::Decimal,
|
SQLTypeName::Decimal => SQLType::Decimal,
|
||||||
SQLTypeName::Char => {
|
SQLTypeName::Char => {
|
||||||
if let Some(type_modifier) = get_type_modifier(modifiers, property) {
|
if let Some(type_modifier) = get_type_modifier(modifiers, property) {
|
||||||
let (_, size) = regex_captures!(r#"^\((\d+)\)$"#, type_modifier)
|
let (_, size) = regex_captures!(r#"^\((\d+)\)$"#, type_modifier)
|
||||||
.context("Type modifier doesn't match format")?;
|
.context("Type modifier doesn't match format")?;
|
||||||
@ -196,8 +229,8 @@ fn get_sql_type(modifiers: &[UMLModifier], type_name: SQLTypeName, property: &st
|
|||||||
// For now just pick a defautl arbitrarily
|
// For now just pick a defautl arbitrarily
|
||||||
SQLType::Char(31)
|
SQLType::Char(31)
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
SQLTypeName::Varchar => {
|
SQLTypeName::Varchar => {
|
||||||
if let Some(type_modifier) = get_type_modifier(modifiers, property) {
|
if let Some(type_modifier) = get_type_modifier(modifiers, property) {
|
||||||
let (_, size) = regex_captures!(r#"^\((\d+)\)$"#, type_modifier)
|
let (_, size) = regex_captures!(r#"^\((\d+)\)$"#, type_modifier)
|
||||||
.context("Type modifier doesn't match format")?;
|
.context("Type modifier doesn't match format")?;
|
||||||
@ -207,12 +240,13 @@ fn get_sql_type(modifiers: &[UMLModifier], type_name: SQLTypeName, property: &st
|
|||||||
// For now just pick a defautl arbitrarily
|
// For now just pick a defautl arbitrarily
|
||||||
SQLType::Varchar(255)
|
SQLType::Varchar(255)
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_used_types<'a>(models: &'a [UMLModel]) -> HashSet<&'a String> {
|
fn get_used_types<'a>(models: &'a [UMLModel]) -> HashSet<&'a String> {
|
||||||
models.iter()
|
models
|
||||||
|
.iter()
|
||||||
.flat_map(|model| &model.packages)
|
.flat_map(|model| &model.packages)
|
||||||
.flat_map(|package| &package.classess)
|
.flat_map(|package| &package.classess)
|
||||||
.flat_map(|class| &class.properties)
|
.flat_map(|class| &class.properties)
|
||||||
@ -232,26 +266,43 @@ pub fn parse_project<R: Read + Seek>(project_file: R) -> Result<Vec<SQLTableColl
|
|||||||
for ddl_script in ddl_project.scripts {
|
for ddl_script in ddl_project.scripts {
|
||||||
let mut tables = vec![];
|
let mut tables = vec![];
|
||||||
|
|
||||||
let model_properties = ddl_script.classess.iter()
|
let model_properties = ddl_script
|
||||||
.flat_map(|class| class.property_ids.iter().map(|prop| (&class.class_id, prop)))
|
.classess
|
||||||
|
.iter()
|
||||||
|
.flat_map(|class| {
|
||||||
|
class
|
||||||
|
.property_ids
|
||||||
|
.iter()
|
||||||
|
.map(|prop| (&class.class_id, prop))
|
||||||
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let mut model_classess = vec![];
|
let mut model_classess = vec![];
|
||||||
for ddl_class in &ddl_script.classess {
|
for ddl_class in &ddl_script.classess {
|
||||||
let model_class = find_class_by_id(&models, &ddl_class.class_id).context("UML class not found")?;
|
let model_class = find_class_by_id(&models, &ddl_class.class_id)
|
||||||
|
.context("UML class not found")?;
|
||||||
model_classess.push(model_class);
|
model_classess.push(model_class);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (ddl_class, model_class) in ddl_script.classess.iter().zip(&model_classess) {
|
for (ddl_class, model_class) in ddl_script.classess.iter().zip(&model_classess) {
|
||||||
let name = model_class.name.clone().context("UML class name not found")?;
|
let name = model_class
|
||||||
|
.name
|
||||||
|
.clone()
|
||||||
|
.context("UML class name not found")?;
|
||||||
|
|
||||||
let mut columns = vec![];
|
let mut columns = vec![];
|
||||||
for property_id in &ddl_class.property_ids {
|
for property_id in &ddl_class.property_ids {
|
||||||
let property = model_class.properties.iter().find(|p| p.id.eq(property_id)).context("Property not found")?;
|
let property = model_class
|
||||||
|
.properties
|
||||||
|
.iter()
|
||||||
|
.find(|p| p.id.eq(property_id))
|
||||||
|
.context("Property not found")?;
|
||||||
let prop_name = unwrap_opt_continue!(&property.name).clone();
|
let prop_name = unwrap_opt_continue!(&property.name).clone();
|
||||||
|
|
||||||
let type_href = unwrap_opt_continue!(&property.type_href);
|
let type_href = unwrap_opt_continue!(&property.type_href);
|
||||||
let type_name = sql_type_names.get(type_href).context("Proerty type name conversion not found")?;
|
let type_name = sql_type_names
|
||||||
|
.get(type_href)
|
||||||
|
.context("Proerty type name conversion not found")?;
|
||||||
|
|
||||||
let check_constraint = get_sql_check_constraint(&models, &prop_name);
|
let check_constraint = get_sql_check_constraint(&models, &prop_name);
|
||||||
let foreign_key = get_foreign_key(&modifiers, &model_classess, property_id)?;
|
let foreign_key = get_foreign_key(&modifiers, &model_classess, property_id)?;
|
||||||
@ -266,10 +317,7 @@ pub fn parse_project<R: Read + Seek>(project_file: R) -> Result<Vec<SQLTableColl
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
tables.push(SQLTable {
|
tables.push(SQLTable { name, columns })
|
||||||
name,
|
|
||||||
columns
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
collections.push(SQLTableCollection { tables })
|
collections.push(SQLTableCollection { tables })
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,21 @@
|
|||||||
use std::{io::{Read, Seek}, collections::{HashMap, HashSet}};
|
use std::{
|
||||||
|
collections::{HashMap, HashSet},
|
||||||
|
io::{Read, Seek},
|
||||||
|
};
|
||||||
|
|
||||||
use xml::{EventReader, reader::XmlEvent, attribute::OwnedAttribute, name::OwnedName};
|
use anyhow::{bail, Context, Result};
|
||||||
|
use xml::{attribute::OwnedAttribute, name::OwnedName, reader::XmlEvent, EventReader};
|
||||||
use zip::ZipArchive;
|
use zip::ZipArchive;
|
||||||
use anyhow::{Result, Context, bail};
|
|
||||||
|
|
||||||
use crate::unwrap_opt_continue;
|
use crate::unwrap_opt_continue;
|
||||||
|
|
||||||
use super::utils::{MyEventReader, check_name, parse_element, get_attribute, check_attribute};
|
use super::utils::{check_attribute, check_name, get_attribute, parse_element, MyEventReader};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct UsedPackage {
|
struct UsedPackage {
|
||||||
share_point_id: String,
|
share_point_id: String,
|
||||||
name: String,
|
name: String,
|
||||||
needed_types: Vec<String>
|
needed_types: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
@ -23,7 +26,7 @@ pub enum SQLTypeName {
|
|||||||
Float,
|
Float,
|
||||||
Bool,
|
Bool,
|
||||||
Char,
|
Char,
|
||||||
Varchar
|
Varchar,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_used_project_name(attrs: &[OwnedAttribute]) -> Option<&str> {
|
fn get_used_project_name(attrs: &[OwnedAttribute]) -> Option<&str> {
|
||||||
@ -31,14 +34,20 @@ fn get_used_project_name(attrs: &[OwnedAttribute]) -> Option<&str> {
|
|||||||
project_uri.split("/").last()
|
project_uri.split("/").last()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_used_package<R: Read>(parser: &mut MyEventReader<R>, attrs: &[OwnedAttribute], needed_types: &[&str]) -> Result<UsedPackage> {
|
fn parse_used_package<R: Read>(
|
||||||
|
parser: &mut MyEventReader<R>,
|
||||||
|
attrs: &[OwnedAttribute],
|
||||||
|
needed_types: &[&str],
|
||||||
|
) -> Result<UsedPackage> {
|
||||||
let mut share_point_id = None;
|
let mut share_point_id = None;
|
||||||
let project_uri = get_attribute(&attrs, None, "usedProjectURI")?;
|
let project_uri = get_attribute(&attrs, None, "usedProjectURI")?;
|
||||||
let name = project_uri.split("/").last().unwrap();
|
let name = project_uri.split("/").last().unwrap();
|
||||||
|
|
||||||
parse_element(parser, &mut |p, name, attrs| {
|
parse_element(parser, &mut |p, name, attrs| {
|
||||||
if share_point_id.is_none() && check_name(&name, None, "mountPoints") {
|
if share_point_id.is_none() && check_name(&name, None, "mountPoints") {
|
||||||
share_point_id = get_attribute(&attrs, None, "sharePointID").ok().map(str::to_string);
|
share_point_id = get_attribute(&attrs, None, "sharePointID")
|
||||||
|
.ok()
|
||||||
|
.map(str::to_string);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
@ -46,32 +55,47 @@ fn parse_used_package<R: Read>(parser: &mut MyEventReader<R>, attrs: &[OwnedAttr
|
|||||||
Ok(UsedPackage {
|
Ok(UsedPackage {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
share_point_id: share_point_id.context("Share point id not found")?,
|
share_point_id: share_point_id.context("Share point id not found")?,
|
||||||
needed_types: needed_types.iter().map(|s| s.to_string()).collect()
|
needed_types: needed_types.iter().map(|s| s.to_string()).collect(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list_used_packages<R: Read>(file: R, needed_types: &HashSet<&String>) -> Result<Vec<UsedPackage>> {
|
fn list_used_packages<R: Read>(
|
||||||
|
file: R,
|
||||||
|
needed_types: &HashSet<&String>,
|
||||||
|
) -> Result<Vec<UsedPackage>> {
|
||||||
let mut packages = vec![];
|
let mut packages = vec![];
|
||||||
|
|
||||||
let mut needed_types_per_package = HashMap::new();
|
let mut needed_types_per_package = HashMap::new();
|
||||||
for needed_type in needed_types.iter() {
|
for needed_type in needed_types.iter() {
|
||||||
let (package_name, type_id) = unwrap_opt_continue!(needed_type.split_once("#"));
|
let (package_name, type_id) = unwrap_opt_continue!(needed_type.split_once("#"));
|
||||||
let ids = needed_types_per_package.entry(package_name).or_insert(vec![]);
|
let ids = needed_types_per_package
|
||||||
|
.entry(package_name)
|
||||||
|
.or_insert(vec![]);
|
||||||
ids.push(type_id);
|
ids.push(type_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut parser: MyEventReader<_> = EventReader::new(file).into();
|
let mut parser: MyEventReader<_> = EventReader::new(file).into();
|
||||||
loop {
|
loop {
|
||||||
match parser.next()? {
|
match parser.next()? {
|
||||||
XmlEvent::StartElement { name, attributes, .. } => {
|
XmlEvent::StartElement {
|
||||||
|
name, attributes, ..
|
||||||
|
} => {
|
||||||
if check_name(&name, None, "projectUsages") {
|
if check_name(&name, None, "projectUsages") {
|
||||||
let project_name = unwrap_opt_continue!(get_used_project_name(&attributes));
|
let project_name = unwrap_opt_continue!(get_used_project_name(&attributes));
|
||||||
if let Some(needed_types_for_package) = needed_types_per_package.get(&project_name) {
|
if let Some(needed_types_for_package) =
|
||||||
packages.push(parse_used_package(&mut parser, &attributes, needed_types_for_package)?);
|
needed_types_per_package.get(&project_name)
|
||||||
|
{
|
||||||
|
packages.push(parse_used_package(
|
||||||
|
&mut parser,
|
||||||
|
&attributes,
|
||||||
|
needed_types_for_package,
|
||||||
|
)?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
XmlEvent::EndDocument => { break; },
|
XmlEvent::EndDocument => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -93,15 +117,18 @@ fn parse_type_name(str: &str) -> Result<SQLTypeName> {
|
|||||||
"Integer" | "integer" | "int" => Int,
|
"Integer" | "integer" | "int" => Int,
|
||||||
"date" => Date,
|
"date" => Date,
|
||||||
"Boolean" => Bool,
|
"Boolean" => Bool,
|
||||||
_ => bail!("Unknown SQL type: '{}'", str)
|
_ => bail!("Unknown SQL type: '{}'", str),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_types_package<R: Read>(parser: &mut MyEventReader<R>) -> Result<Vec<(String, SQLTypeName)>> {
|
fn parse_types_package<R: Read>(
|
||||||
|
parser: &mut MyEventReader<R>,
|
||||||
|
) -> Result<Vec<(String, SQLTypeName)>> {
|
||||||
let mut types = vec![];
|
let mut types = vec![];
|
||||||
|
|
||||||
fn is_primitive_type_element(name: &OwnedName, attrs: &[OwnedAttribute]) -> bool {
|
fn is_primitive_type_element(name: &OwnedName, attrs: &[OwnedAttribute]) -> bool {
|
||||||
check_name(&name, None, "packagedElement") && check_attribute(&attrs, Some("xsi"), "type", "uml:PrimitiveType")
|
check_name(&name, None, "packagedElement")
|
||||||
|
&& check_attribute(&attrs, Some("xsi"), "type", "uml:PrimitiveType")
|
||||||
}
|
}
|
||||||
|
|
||||||
parse_element(parser, &mut |p, name, attrs| {
|
parse_element(parser, &mut |p, name, attrs| {
|
||||||
@ -109,8 +136,8 @@ fn parse_types_package<R: Read>(parser: &mut MyEventReader<R>) -> Result<Vec<(St
|
|||||||
let type_name = get_attribute(&attrs, None, "name")?;
|
let type_name = get_attribute(&attrs, None, "name")?;
|
||||||
if !type_name.eq("StructuredExpression") {
|
if !type_name.eq("StructuredExpression") {
|
||||||
types.push((
|
types.push((
|
||||||
get_attribute(&attrs, Some("xmi"), "id")?.to_string(),
|
get_attribute(&attrs, Some("xmi"), "id")?.to_string(),
|
||||||
parse_type_name(type_name)?
|
parse_type_name(type_name)?,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -120,26 +147,37 @@ fn parse_types_package<R: Read>(parser: &mut MyEventReader<R>) -> Result<Vec<(St
|
|||||||
Ok(types)
|
Ok(types)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_primitive_types<R: Read>(reader: R, used_packages: &[UsedPackage]) -> Result<Vec<(String, SQLTypeName)>> {
|
fn parse_primitive_types<R: Read>(
|
||||||
|
reader: R,
|
||||||
|
used_packages: &[UsedPackage],
|
||||||
|
) -> Result<Vec<(String, SQLTypeName)>> {
|
||||||
let mut types = vec![];
|
let mut types = vec![];
|
||||||
|
|
||||||
let mut parser: MyEventReader<_> = EventReader::new(reader).into();
|
let mut parser: MyEventReader<_> = EventReader::new(reader).into();
|
||||||
loop {
|
loop {
|
||||||
match parser.next()? {
|
match parser.next()? {
|
||||||
XmlEvent::StartElement { name, attributes, .. } => {
|
XmlEvent::StartElement {
|
||||||
|
name, attributes, ..
|
||||||
|
} => {
|
||||||
if check_name(&name, Some("uml"), "Package") {
|
if check_name(&name, Some("uml"), "Package") {
|
||||||
if let Some(id) = get_attribute(&attributes, None, "ID").ok() {
|
if let Some(id) = get_attribute(&attributes, None, "ID").ok() {
|
||||||
if let Some(package) = used_packages.iter().find(|p| p.share_point_id.eq(id)) {
|
if let Some(package) =
|
||||||
|
used_packages.iter().find(|p| p.share_point_id.eq(id))
|
||||||
|
{
|
||||||
let package_types = parse_types_package(&mut parser)?
|
let package_types = parse_types_package(&mut parser)?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|t| package.needed_types.contains(&t.0))
|
.filter(|t| package.needed_types.contains(&t.0))
|
||||||
.map(|(id, type_name)| (format!("{}#{}", package.name, id), type_name));
|
.map(|(id, type_name)| {
|
||||||
|
(format!("{}#{}", package.name, id), type_name)
|
||||||
|
});
|
||||||
types.extend(package_types);
|
types.extend(package_types);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
XmlEvent::EndDocument => { break; },
|
XmlEvent::EndDocument => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -147,13 +185,17 @@ fn parse_primitive_types<R: Read>(reader: R, used_packages: &[UsedPackage]) -> R
|
|||||||
Ok(types)
|
Ok(types)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_sql_types<R: Read + Seek>(project: &mut ZipArchive<R>, needed_types: &HashSet<&String>) -> Result<HashMap<String, SQLTypeName>> {
|
pub fn parse_sql_types<R: Read + Seek>(
|
||||||
|
project: &mut ZipArchive<R>,
|
||||||
|
needed_types: &HashSet<&String>,
|
||||||
|
) -> Result<HashMap<String, SQLTypeName>> {
|
||||||
let mut type_names = HashMap::new();
|
let mut type_names = HashMap::new();
|
||||||
|
|
||||||
let meta_model_file = project.by_name("com.nomagic.ci.metamodel.project")?;
|
let meta_model_file = project.by_name("com.nomagic.ci.metamodel.project")?;
|
||||||
let used_packages = list_used_packages(meta_model_file, needed_types)?;
|
let used_packages = list_used_packages(meta_model_file, needed_types)?;
|
||||||
|
|
||||||
let snapshot_files = project.file_names()
|
let snapshot_files = project
|
||||||
|
.file_names()
|
||||||
.filter(|f| is_umodel_snapshot_file(f))
|
.filter(|f| is_umodel_snapshot_file(f))
|
||||||
.map(|f| f.to_string())
|
.map(|f| f.to_string())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
@ -1,19 +1,22 @@
|
|||||||
use std::io::{Read, Seek};
|
use std::io::{Read, Seek};
|
||||||
|
|
||||||
use xml::{EventReader, reader::XmlEvent, attribute::OwnedAttribute, name::OwnedName};
|
use anyhow::{Context, Result};
|
||||||
|
use xml::{attribute::OwnedAttribute, name::OwnedName, reader::XmlEvent, EventReader};
|
||||||
use zip::ZipArchive;
|
use zip::ZipArchive;
|
||||||
use anyhow::{Result, Context};
|
|
||||||
|
|
||||||
use crate::{unwrap_err_continue, unwrap_opt_continue};
|
use crate::{unwrap_err_continue, unwrap_opt_continue};
|
||||||
|
|
||||||
use super::utils::{check_name, parse_element, get_attribute, check_attribute, MyEventReader, ParseProjectError, get_element_characters};
|
use super::utils::{
|
||||||
|
check_attribute, check_name, get_attribute, get_element_characters, parse_element,
|
||||||
|
MyEventReader, ParseProjectError,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct UMLProperty {
|
pub struct UMLProperty {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
pub is_id: bool,
|
pub is_id: bool,
|
||||||
pub type_href: Option<String>
|
pub type_href: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Make this an enum? Because from what I have seen there were only 2 cases,
|
// TODO: Make this an enum? Because from what I have seen there were only 2 cases,
|
||||||
@ -33,32 +36,32 @@ pub struct UMLClass {
|
|||||||
pub id: String,
|
pub id: String,
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
pub properties: Vec<UMLProperty>,
|
pub properties: Vec<UMLProperty>,
|
||||||
pub constraints: Vec<UMLConstraint>
|
pub constraints: Vec<UMLConstraint>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct UMLPackage {
|
pub struct UMLPackage {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
pub classess: Vec<UMLClass>
|
pub classess: Vec<UMLClass>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct UMLModel {
|
pub struct UMLModel {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub packages: Vec<UMLPackage>
|
pub packages: Vec<UMLPackage>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct UMLPrimaryKeyModifier {
|
pub struct UMLPrimaryKeyModifier {
|
||||||
pub property_id: String
|
pub property_id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct UMLNullableModifier {
|
pub struct UMLNullableModifier {
|
||||||
pub property_id: String,
|
pub property_id: String,
|
||||||
pub nullable: bool
|
pub nullable: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -69,13 +72,13 @@ pub struct UMLForeignKeyModifier {
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct UMLUniqueModifier {
|
pub struct UMLUniqueModifier {
|
||||||
pub property_id: String
|
pub property_id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct UMLTypeModifier {
|
pub struct UMLTypeModifier {
|
||||||
pub property_id: String,
|
pub property_id: String,
|
||||||
pub modifier: String
|
pub modifier: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -84,13 +87,18 @@ pub enum UMLModifier {
|
|||||||
PirmaryKey(UMLPrimaryKeyModifier),
|
PirmaryKey(UMLPrimaryKeyModifier),
|
||||||
Nullable(UMLNullableModifier),
|
Nullable(UMLNullableModifier),
|
||||||
ForeignKey(UMLForeignKeyModifier),
|
ForeignKey(UMLForeignKeyModifier),
|
||||||
Type(UMLTypeModifier)
|
Type(UMLTypeModifier),
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_property<R: Read>(parser: &mut MyEventReader<R>, attrs: &[OwnedAttribute]) -> Result<UMLProperty> {
|
fn parse_property<R: Read>(
|
||||||
let id = get_attribute(attrs, Some("xmi"), "id")?.into();
|
parser: &mut MyEventReader<R>,
|
||||||
let name = get_attribute(attrs, None, "name").ok().map(str::to_string);
|
attrs: &[OwnedAttribute],
|
||||||
let is_id = get_attribute(attrs, None, "isID").unwrap_or("false").eq("true");
|
) -> Result<UMLProperty> {
|
||||||
|
let id = get_attribute(attrs, Some("xmi"), "id")?.into();
|
||||||
|
let name = get_attribute(attrs, None, "name").ok().map(str::to_string);
|
||||||
|
let is_id = get_attribute(attrs, None, "isID")
|
||||||
|
.unwrap_or("false")
|
||||||
|
.eq("true");
|
||||||
let mut type_href = None;
|
let mut type_href = None;
|
||||||
|
|
||||||
parse_element(parser, &mut |p, name, attrs| {
|
parse_element(parser, &mut |p, name, attrs| {
|
||||||
@ -103,22 +111,27 @@ fn parse_property<R: Read>(parser: &mut MyEventReader<R>, attrs: &[OwnedAttribut
|
|||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(UMLProperty {
|
Ok(UMLProperty {
|
||||||
id,
|
id,
|
||||||
name,
|
name,
|
||||||
is_id,
|
is_id,
|
||||||
type_href
|
type_href,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_constraint<R: Read>(parser: &mut MyEventReader<R>, attrs: &[OwnedAttribute]) -> Result<Option<UMLConstraint>> {
|
fn parse_constraint<R: Read>(
|
||||||
let id = get_attribute(attrs, Some("xmi"), "id")?.into();
|
parser: &mut MyEventReader<R>,
|
||||||
|
attrs: &[OwnedAttribute],
|
||||||
|
) -> Result<Option<UMLConstraint>> {
|
||||||
|
let id = get_attribute(attrs, Some("xmi"), "id")?.into();
|
||||||
let mut constrainted_element_id = None;
|
let mut constrainted_element_id = None;
|
||||||
let mut language = None;
|
let mut language = None;
|
||||||
let mut body = None;
|
let mut body = None;
|
||||||
|
|
||||||
parse_element(parser, &mut |p, name, attrs| {
|
parse_element(parser, &mut |p, name, attrs| {
|
||||||
if check_name(&name, None, "constrainedElement") && constrainted_element_id.is_none() {
|
if check_name(&name, None, "constrainedElement") && constrainted_element_id.is_none() {
|
||||||
constrainted_element_id = get_attribute(&attrs, Some("xmi"), "idref").ok().map(str::to_string);
|
constrainted_element_id = get_attribute(&attrs, Some("xmi"), "idref")
|
||||||
|
.ok()
|
||||||
|
.map(str::to_string);
|
||||||
} else if check_name(&name, None, "body") && body.is_none() {
|
} else if check_name(&name, None, "body") && body.is_none() {
|
||||||
let contents = get_element_characters(p)?;
|
let contents = get_element_characters(p)?;
|
||||||
if contents.len() > 0 {
|
if contents.len() > 0 {
|
||||||
@ -137,32 +150,37 @@ fn parse_constraint<R: Read>(parser: &mut MyEventReader<R>, attrs: &[OwnedAttrib
|
|||||||
class_id: Some(constrainted_element_id.context("Missing class id")?),
|
class_id: Some(constrainted_element_id.context("Missing class id")?),
|
||||||
body: Some(format!("in {}", check_body)),
|
body: Some(format!("in {}", check_body)),
|
||||||
property_id: None,
|
property_id: None,
|
||||||
property_name: Some(prop_name.into())
|
property_name: Some(prop_name.into()),
|
||||||
}))
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(Some(UMLConstraint {
|
return Ok(Some(UMLConstraint {
|
||||||
id,
|
id,
|
||||||
property_id: Some(constrainted_element_id.context("Missing property id")?),
|
property_id: Some(constrainted_element_id.context("Missing property id")?),
|
||||||
body: None,
|
body: None,
|
||||||
class_id: None,
|
class_id: None,
|
||||||
property_name: None
|
property_name: None,
|
||||||
}))
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_class<R: Read>(parser: &mut MyEventReader<R>, attrs: &[OwnedAttribute]) -> Result<UMLClass> {
|
fn parse_class<R: Read>(
|
||||||
|
parser: &mut MyEventReader<R>,
|
||||||
|
attrs: &[OwnedAttribute],
|
||||||
|
) -> Result<UMLClass> {
|
||||||
let mut properties = vec![];
|
let mut properties = vec![];
|
||||||
let mut consraints = vec![];
|
let mut consraints = vec![];
|
||||||
let id = get_attribute(attrs, Some("xmi"), "id")?.into();
|
let id = get_attribute(attrs, Some("xmi"), "id")?.into();
|
||||||
let name = get_attribute(attrs, None, "name").ok().map(str::to_string);
|
let name = get_attribute(attrs, None, "name").ok().map(str::to_string);
|
||||||
|
|
||||||
fn is_property_element(name: &OwnedName, attrs: &[OwnedAttribute]) -> bool {
|
fn is_property_element(name: &OwnedName, attrs: &[OwnedAttribute]) -> bool {
|
||||||
check_name(name, None, "ownedAttribute") && check_attribute(&attrs, Some("xmi"), "type", "uml:Property")
|
check_name(name, None, "ownedAttribute")
|
||||||
|
&& check_attribute(&attrs, Some("xmi"), "type", "uml:Property")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_constraint_element(name: &OwnedName, attrs: &[OwnedAttribute]) -> bool {
|
fn is_constraint_element(name: &OwnedName, attrs: &[OwnedAttribute]) -> bool {
|
||||||
check_name(name, None, "ownedRule") && check_attribute(&attrs, Some("xmi"), "type", "uml:Constraint")
|
check_name(name, None, "ownedRule")
|
||||||
|
&& check_attribute(&attrs, Some("xmi"), "type", "uml:Constraint")
|
||||||
}
|
}
|
||||||
|
|
||||||
parse_element(parser, &mut |p, name, attrs| {
|
parse_element(parser, &mut |p, name, attrs| {
|
||||||
@ -177,20 +195,24 @@ fn parse_class<R: Read>(parser: &mut MyEventReader<R>, attrs: &[OwnedAttribute])
|
|||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(UMLClass {
|
Ok(UMLClass {
|
||||||
id,
|
id,
|
||||||
name,
|
name,
|
||||||
properties,
|
properties,
|
||||||
constraints: consraints,
|
constraints: consraints,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_package<R: Read>(parser: &mut MyEventReader<R>, attrs: &[OwnedAttribute]) -> Result<UMLPackage> {
|
fn parse_package<R: Read>(
|
||||||
|
parser: &mut MyEventReader<R>,
|
||||||
|
attrs: &[OwnedAttribute],
|
||||||
|
) -> Result<UMLPackage> {
|
||||||
let mut classess = vec![];
|
let mut classess = vec![];
|
||||||
let id = get_attribute(attrs, Some("xmi"), "id")?.into();
|
let id = get_attribute(attrs, Some("xmi"), "id")?.into();
|
||||||
let name = get_attribute(attrs, None, "name").ok().map(str::to_string);
|
let name = get_attribute(attrs, None, "name").ok().map(str::to_string);
|
||||||
|
|
||||||
fn is_class_element(name: &OwnedName, attrs: &[OwnedAttribute]) -> bool {
|
fn is_class_element(name: &OwnedName, attrs: &[OwnedAttribute]) -> bool {
|
||||||
check_name(name, None, "packagedElement") && check_attribute(&attrs, Some("xmi"), "type", "uml:Class")
|
check_name(name, None, "packagedElement")
|
||||||
|
&& check_attribute(&attrs, Some("xmi"), "type", "uml:Class")
|
||||||
}
|
}
|
||||||
|
|
||||||
parse_element(parser, &mut |p, name, attrs| {
|
parse_element(parser, &mut |p, name, attrs| {
|
||||||
@ -200,20 +222,20 @@ fn parse_package<R: Read>(parser: &mut MyEventReader<R>, attrs: &[OwnedAttribute
|
|||||||
Ok(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(UMLPackage {
|
Ok(UMLPackage { id, name, classess })
|
||||||
id,
|
|
||||||
name,
|
|
||||||
classess
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_model<R: Read>(parser: &mut MyEventReader<R>, attrs: &[OwnedAttribute]) -> Result<UMLModel> {
|
fn parse_model<R: Read>(
|
||||||
|
parser: &mut MyEventReader<R>,
|
||||||
|
attrs: &[OwnedAttribute],
|
||||||
|
) -> Result<UMLModel> {
|
||||||
let mut packages = vec![];
|
let mut packages = vec![];
|
||||||
let id = get_attribute(attrs, Some("xmi"), "id")?.into();
|
let id = get_attribute(attrs, Some("xmi"), "id")?.into();
|
||||||
let name = get_attribute(attrs, None, "name")?.into();
|
let name = get_attribute(attrs, None, "name")?.into();
|
||||||
|
|
||||||
fn is_package_element(name: &OwnedName, attrs: &[OwnedAttribute]) -> bool {
|
fn is_package_element(name: &OwnedName, attrs: &[OwnedAttribute]) -> bool {
|
||||||
check_name(name, None, "packagedElement") && check_attribute(&attrs, Some("xmi"), "type", "uml:Package")
|
check_name(name, None, "packagedElement")
|
||||||
|
&& check_attribute(&attrs, Some("xmi"), "type", "uml:Package")
|
||||||
}
|
}
|
||||||
|
|
||||||
parse_element(parser, &mut |p, name, attrs| {
|
parse_element(parser, &mut |p, name, attrs| {
|
||||||
@ -223,11 +245,7 @@ fn parse_model<R: Read>(parser: &mut MyEventReader<R>, attrs: &[OwnedAttribute])
|
|||||||
Ok(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(UMLModel {
|
Ok(UMLModel { id, name, packages })
|
||||||
id,
|
|
||||||
name,
|
|
||||||
packages
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_constraint_by_id<'a>(models: &'a [UMLModel], id: &str) -> Option<&'a UMLConstraint> {
|
fn find_constraint_by_id<'a>(models: &'a [UMLModel], id: &str) -> Option<&'a UMLConstraint> {
|
||||||
@ -246,7 +264,9 @@ fn find_constraint_by_id<'a>(models: &'a [UMLModel], id: &str) -> Option<&'a UML
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_uml_model<R: Read + Seek>(project: &mut ZipArchive<R>) -> Result<(Vec<UMLModel>, Vec<UMLModifier>)> {
|
pub fn parse_uml_model<R: Read + Seek>(
|
||||||
|
project: &mut ZipArchive<R>,
|
||||||
|
) -> Result<(Vec<UMLModel>, Vec<UMLModifier>)> {
|
||||||
let mut models = vec![];
|
let mut models = vec![];
|
||||||
let mut modifiers = vec![];
|
let mut modifiers = vec![];
|
||||||
|
|
||||||
@ -255,37 +275,64 @@ pub fn parse_uml_model<R: Read + Seek>(project: &mut ZipArchive<R>) -> Result<(V
|
|||||||
|
|
||||||
loop {
|
loop {
|
||||||
match parser.next()? {
|
match parser.next()? {
|
||||||
XmlEvent::StartElement { name, attributes, .. } => {
|
XmlEvent::StartElement {
|
||||||
|
name, attributes, ..
|
||||||
|
} => {
|
||||||
if check_name(&name, Some("uml"), "Model") {
|
if check_name(&name, Some("uml"), "Model") {
|
||||||
models.push(parse_model(&mut parser, &attributes)?);
|
models.push(parse_model(&mut parser, &attributes)?);
|
||||||
|
|
||||||
} else if check_name(&name, Some("SQLProfile"), "PrimaryKey") {
|
} else if check_name(&name, Some("SQLProfile"), "PrimaryKey") {
|
||||||
let constraint_id = unwrap_err_continue!(get_attribute(&attributes, None, "base_Constraint"));
|
let constraint_id =
|
||||||
let constraint = unwrap_opt_continue!(find_constraint_by_id(&models, constraint_id));
|
unwrap_err_continue!(get_attribute(&attributes, None, "base_Constraint"));
|
||||||
|
let constraint =
|
||||||
|
unwrap_opt_continue!(find_constraint_by_id(&models, constraint_id));
|
||||||
let property_id = unwrap_opt_continue!(&constraint.property_id).clone();
|
let property_id = unwrap_opt_continue!(&constraint.property_id).clone();
|
||||||
modifiers.push(UMLModifier::PirmaryKey(UMLPrimaryKeyModifier { property_id }));
|
modifiers.push(UMLModifier::PirmaryKey(UMLPrimaryKeyModifier {
|
||||||
|
property_id,
|
||||||
|
}));
|
||||||
} else if check_name(&name, Some("SQLProfile"), "PKMember") {
|
} else if check_name(&name, Some("SQLProfile"), "PKMember") {
|
||||||
let property_id = unwrap_err_continue!(get_attribute(&attributes, None, "base_Property")).to_string();
|
let property_id =
|
||||||
modifiers.push(UMLModifier::PirmaryKey(UMLPrimaryKeyModifier { property_id }));
|
unwrap_err_continue!(get_attribute(&attributes, None, "base_Property"))
|
||||||
|
.to_string();
|
||||||
|
modifiers.push(UMLModifier::PirmaryKey(UMLPrimaryKeyModifier {
|
||||||
|
property_id,
|
||||||
|
}));
|
||||||
} else if check_name(&name, Some("SQLProfile"), "Column") {
|
} else if check_name(&name, Some("SQLProfile"), "Column") {
|
||||||
let property_id = unwrap_err_continue!(get_attribute(&attributes, None, "base_Property")).to_string();
|
let property_id =
|
||||||
let nullable = unwrap_err_continue!(get_attribute(&attributes, None, "nullable")).eq("true");
|
unwrap_err_continue!(get_attribute(&attributes, None, "base_Property"))
|
||||||
modifiers.push(UMLModifier::Nullable(UMLNullableModifier { property_id, nullable }));
|
.to_string();
|
||||||
|
let nullable =
|
||||||
|
unwrap_err_continue!(get_attribute(&attributes, None, "nullable"))
|
||||||
|
.eq("true");
|
||||||
|
modifiers.push(UMLModifier::Nullable(UMLNullableModifier {
|
||||||
|
property_id,
|
||||||
|
nullable,
|
||||||
|
}));
|
||||||
} else if check_name(&name, Some("MagicDraw_Profile"), "typeModifier") {
|
} else if check_name(&name, Some("MagicDraw_Profile"), "typeModifier") {
|
||||||
let property_id = unwrap_err_continue!(get_attribute(&attributes, None, "base_Element")).into();
|
let property_id =
|
||||||
let modifier = unwrap_err_continue!(get_attribute(&attributes, None, "typeModifier")).into();
|
unwrap_err_continue!(get_attribute(&attributes, None, "base_Element"))
|
||||||
modifiers.push(UMLModifier::Type(UMLTypeModifier { property_id, modifier }));
|
.into();
|
||||||
|
let modifier =
|
||||||
|
unwrap_err_continue!(get_attribute(&attributes, None, "typeModifier"))
|
||||||
|
.into();
|
||||||
|
modifiers.push(UMLModifier::Type(UMLTypeModifier {
|
||||||
|
property_id,
|
||||||
|
modifier,
|
||||||
|
}));
|
||||||
} else if check_name(&name, Some("SQLProfile"), "FK") {
|
} else if check_name(&name, Some("SQLProfile"), "FK") {
|
||||||
let from_property_id = unwrap_err_continue!(get_attribute(&attributes, None, "members")).into();
|
let from_property_id =
|
||||||
let to_property_id = unwrap_err_continue!(get_attribute(&attributes, None, "referencedMembers")).into();
|
unwrap_err_continue!(get_attribute(&attributes, None, "members")).into();
|
||||||
modifiers.push(UMLModifier::ForeignKey(UMLForeignKeyModifier { from_property_id, to_property_id }));
|
let to_property_id =
|
||||||
|
unwrap_err_continue!(get_attribute(&attributes, None, "referencedMembers"))
|
||||||
|
.into();
|
||||||
|
modifiers.push(UMLModifier::ForeignKey(UMLForeignKeyModifier {
|
||||||
|
from_property_id,
|
||||||
|
to_property_id,
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
XmlEvent::EndDocument => { break; },
|
XmlEvent::EndDocument => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
|
|
||||||
use xml::{EventReader, reader::XmlEvent, name::OwnedName, attribute::OwnedAttribute};
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
use xml::{attribute::OwnedAttribute, name::OwnedName, reader::XmlEvent, EventReader};
|
||||||
|
|
||||||
pub struct MyEventReader<R: Read> {
|
pub struct MyEventReader<R: Read> {
|
||||||
depth: u32,
|
depth: u32,
|
||||||
event_reader: EventReader<R>
|
event_reader: EventReader<R>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Read> MyEventReader<R> {
|
impl<R: Read> MyEventReader<R> {
|
||||||
@ -27,12 +27,12 @@ impl<R: Read> MyEventReader<R> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Read> From<EventReader<R>> for MyEventReader<R> {
|
impl<R: Read> From<EventReader<R>> for MyEventReader<R> {
|
||||||
fn from(event_reader: EventReader<R>) -> Self {
|
fn from(event_reader: EventReader<R>) -> Self {
|
||||||
MyEventReader {
|
MyEventReader {
|
||||||
depth: 0,
|
depth: 0,
|
||||||
event_reader
|
event_reader,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug, PartialEq)]
|
#[derive(Error, Debug, PartialEq)]
|
||||||
@ -41,7 +41,7 @@ pub enum ParseProjectError {
|
|||||||
AttributeNotFound(Option<String>, String),
|
AttributeNotFound(Option<String>, String),
|
||||||
|
|
||||||
#[error("Unexpected end of XML document")]
|
#[error("Unexpected end of XML document")]
|
||||||
EndOfDocument
|
EndOfDocument,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_name_from_parts(prefix: &Option<String>, local_name: &str) -> String {
|
fn format_name_from_parts(prefix: &Option<String>, local_name: &str) -> String {
|
||||||
@ -60,17 +60,27 @@ pub fn check_name(name: &OwnedName, prefix: Option<&str>, local_name: &str) -> b
|
|||||||
name.local_name.eq(local_name) && name.prefix_ref().eq(&prefix)
|
name.local_name.eq(local_name) && name.prefix_ref().eq(&prefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_attribute<'a>(attributes: &'a [OwnedAttribute], prefix: Option<&str>, name: &str) -> Result<&'a str, ParseProjectError> {
|
pub fn get_attribute<'a>(
|
||||||
Ok(attributes.iter()
|
attributes: &'a [OwnedAttribute],
|
||||||
|
prefix: Option<&str>,
|
||||||
|
name: &str,
|
||||||
|
) -> Result<&'a str, ParseProjectError> {
|
||||||
|
Ok(attributes
|
||||||
|
.iter()
|
||||||
.find(|attr| check_name(&attr.name, prefix, name))
|
.find(|attr| check_name(&attr.name, prefix, name))
|
||||||
.map(|attr| &attr.value[..])
|
.map(|attr| &attr.value[..])
|
||||||
.ok_or_else(
|
.ok_or_else(|| {
|
||||||
|| ParseProjectError::AttributeNotFound(prefix.map(|s| s.to_owned()), name.to_owned()),
|
ParseProjectError::AttributeNotFound(prefix.map(|s| s.to_owned()), name.to_owned())
|
||||||
)?)
|
})?)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn check_attribute(attributes: &[OwnedAttribute], prefix: Option<&str>, name: &str, expected_value: &str) -> bool {
|
pub fn check_attribute(
|
||||||
|
attributes: &[OwnedAttribute],
|
||||||
|
prefix: Option<&str>,
|
||||||
|
name: &str,
|
||||||
|
expected_value: &str,
|
||||||
|
) -> bool {
|
||||||
if let Ok(attr) = get_attribute(attributes, prefix, name) {
|
if let Ok(attr) = get_attribute(attributes, prefix, name) {
|
||||||
return attr.eq(expected_value);
|
return attr.eq(expected_value);
|
||||||
}
|
}
|
||||||
@ -84,10 +94,10 @@ pub fn get_element_characters<R: Read>(parser: &mut MyEventReader<R>) -> Result<
|
|||||||
match parser.next()? {
|
match parser.next()? {
|
||||||
XmlEvent::Characters(text) => {
|
XmlEvent::Characters(text) => {
|
||||||
parts.push(text);
|
parts.push(text);
|
||||||
},
|
}
|
||||||
XmlEvent::EndElement { name } => {
|
XmlEvent::EndElement { name } => {
|
||||||
break;
|
break;
|
||||||
},
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -95,23 +105,30 @@ pub fn get_element_characters<R: Read>(parser: &mut MyEventReader<R>) -> Result<
|
|||||||
Ok(parts.join(" "))
|
Ok(parts.join(" "))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_element<R: Read, F>(parser: &mut MyEventReader<R>, process_element: &mut F) -> Result<()>
|
pub fn parse_element<R: Read, F>(
|
||||||
where F: FnMut(&mut MyEventReader<R>, OwnedName, Vec<OwnedAttribute>) -> Result<()>
|
parser: &mut MyEventReader<R>,
|
||||||
|
process_element: &mut F,
|
||||||
|
) -> Result<()>
|
||||||
|
where
|
||||||
|
F: FnMut(&mut MyEventReader<R>, OwnedName, Vec<OwnedAttribute>) -> Result<()>,
|
||||||
{
|
{
|
||||||
|
|
||||||
let starting_depth = parser.depth();
|
let starting_depth = parser.depth();
|
||||||
loop {
|
loop {
|
||||||
match parser.next()? {
|
match parser.next()? {
|
||||||
XmlEvent::StartElement { name, attributes, .. } => {
|
XmlEvent::StartElement {
|
||||||
|
name, attributes, ..
|
||||||
|
} => {
|
||||||
process_element(parser, name, attributes)?;
|
process_element(parser, name, attributes)?;
|
||||||
if parser.depth() == starting_depth-1 { break; }
|
if parser.depth() == starting_depth - 1 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
XmlEvent::EndElement { name } => {
|
XmlEvent::EndElement { name } => {
|
||||||
if parser.depth() == starting_depth-1 { break; }
|
if parser.depth() == starting_depth - 1 {
|
||||||
},
|
break;
|
||||||
XmlEvent::EndDocument => {
|
}
|
||||||
Err(ParseProjectError::EndOfDocument)?
|
}
|
||||||
},
|
XmlEvent::EndDocument => Err(ParseProjectError::EndOfDocument)?,
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -121,20 +138,24 @@ pub fn parse_element<R: Read, F>(parser: &mut MyEventReader<R>, process_element:
|
|||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! unwrap_err_continue {
|
macro_rules! unwrap_err_continue {
|
||||||
($res:expr) => {
|
($res:expr) => {
|
||||||
match $res {
|
match $res {
|
||||||
Ok(val) => val,
|
Ok(val) => val,
|
||||||
Err(e) => { continue; }
|
Err(e) => {
|
||||||
}
|
continue;
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! unwrap_opt_continue {
|
macro_rules! unwrap_opt_continue {
|
||||||
($res:expr) => {
|
($res:expr) => {
|
||||||
match $res {
|
match $res {
|
||||||
Some(val) => val,
|
Some(val) => val,
|
||||||
None => { continue; }
|
None => {
|
||||||
}
|
continue;
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,10 @@ use anyhow::Result;
|
|||||||
|
|
||||||
use app::App;
|
use app::App;
|
||||||
|
|
||||||
mod magicdraw_parser;
|
|
||||||
mod app;
|
mod app;
|
||||||
mod components;
|
mod components;
|
||||||
mod generate_sql;
|
mod generate_sql;
|
||||||
|
mod magicdraw_parser;
|
||||||
|
|
||||||
// TODO: Make this work with enumation lookup tables
|
// TODO: Make this work with enumation lookup tables
|
||||||
// TODO: Dark theme switch button
|
// TODO: Dark theme switch button
|
||||||
|
Loading…
Reference in New Issue
Block a user