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 std::io::Cursor;
|
||||
use std::collections::{HashMap, self};
|
||||
use std::rc::Rc;
|
||||
use gloo::console::{console_dbg, console};
|
||||
use gloo::console::{console, console_dbg};
|
||||
use gloo::file::callbacks::FileReader;
|
||||
use gloo::file::File;
|
||||
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 yew::html::TargetCast;
|
||||
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::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 DEFAULT_ROWS_PER_TABLE: u32 = 20;
|
||||
@ -37,7 +37,7 @@ pub struct App {
|
||||
currently_shown_table: usize,
|
||||
all_good_confirmed: bool,
|
||||
generated_sql: Option<String>,
|
||||
rows_per_table: u32
|
||||
rows_per_table: u32,
|
||||
}
|
||||
|
||||
impl Component for App {
|
||||
@ -63,7 +63,7 @@ impl Component for App {
|
||||
all_good_confirmed: true, // TODO: make this false, by default
|
||||
generated_sql: None,
|
||||
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| {
|
||||
// TODO: show error message
|
||||
link.send_message(Msg::Loaded(
|
||||
file_name,
|
||||
res.expect("failed to read file"),
|
||||
))
|
||||
link.send_message(Msg::Loaded(file_name, res.expect("failed to read file")))
|
||||
})
|
||||
};
|
||||
|
||||
self.active_readers.insert(file_name, task);
|
||||
true
|
||||
},
|
||||
}
|
||||
Msg::Noop => false,
|
||||
Msg::UpdateCurrentProject(collection) => {
|
||||
if let Some(collection) = collection {
|
||||
@ -115,38 +112,40 @@ impl Component for App {
|
||||
let guess = generate_table_guessess(table);
|
||||
self.current_guessess.push(Rc::new(RefCell::new(guess)));
|
||||
}
|
||||
self.current_collection = Some(collection.tables.into_iter().map(Rc::new).collect());
|
||||
self.current_collection =
|
||||
Some(collection.tables.into_iter().map(Rc::new).collect());
|
||||
} else {
|
||||
LocalStorage::delete(COLLECTION_STORE_KEY);
|
||||
self.current_collection = None
|
||||
}
|
||||
|
||||
true
|
||||
},
|
||||
}
|
||||
Msg::ShowNextTable => {
|
||||
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;
|
||||
}
|
||||
false
|
||||
},
|
||||
}
|
||||
Msg::ShowPrevTable => {
|
||||
if self.currently_shown_table > 0 {
|
||||
self.currently_shown_table = self.currently_shown_table - 1;
|
||||
return true;
|
||||
}
|
||||
false
|
||||
},
|
||||
}
|
||||
Msg::AllGoodConfirmation => {
|
||||
self.all_good_confirmed = true;
|
||||
true
|
||||
},
|
||||
}
|
||||
Msg::UpdateGenarator(column, generator) => {
|
||||
let mut guessess = self.current_guessess[self.currently_shown_table].borrow_mut();
|
||||
let entry = guessess.get_mut(&column).unwrap();
|
||||
*entry = generator;
|
||||
true
|
||||
},
|
||||
}
|
||||
Msg::GenerateSQL => {
|
||||
let tables = self.current_collection.as_ref().unwrap();
|
||||
let guessess = self.current_guessess.iter().map(|v| v.borrow()).collect();
|
||||
@ -157,7 +156,7 @@ impl Component for App {
|
||||
self.generated_sql = None
|
||||
}
|
||||
true
|
||||
},
|
||||
}
|
||||
Msg::UpdateRowsPerTable(rows_per_table) => {
|
||||
self.rows_per_table = rows_per_table;
|
||||
false
|
||||
|
@ -1,16 +1,17 @@
|
||||
use std::{collections::HashMap, str::FromStr};
|
||||
|
||||
use yew::{Html, html, Callback, TargetCast, AttrValue};
|
||||
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(
|
||||
selected: &str,
|
||||
options: &[AttrValue],
|
||||
onchange: Callback<String>
|
||||
) -> Html {
|
||||
html!{
|
||||
fn show_dropdown_picker(selected: &str, options: &[AttrValue], onchange: Callback<String>) -> Html {
|
||||
html! {
|
||||
<select onchange={Callback::from(move |e: Event| {
|
||||
let value = e.target_unchecked_into::<HtmlInputElement>().value();
|
||||
onchange.emit(value);
|
||||
@ -27,14 +28,21 @@ fn show_dropdown_picker(
|
||||
fn show_enum_dropdown<T: PartialEq + Clone + 'static>(
|
||||
selected: &T,
|
||||
options: HashMap<AttrValue, T>,
|
||||
onchange: Callback<T>
|
||||
) -> Html {
|
||||
onchange: Callback<T>,
|
||||
) -> Html {
|
||||
let keys = options.keys().map(AttrValue::clone).collect::<Vec<_>>();
|
||||
let guess_str = options.iter().find(|(_, v)| v.eq(&selected)).unwrap().0.clone();
|
||||
let guess_str = options
|
||||
.iter()
|
||||
.find(|(_, v)| v.eq(&selected))
|
||||
.unwrap()
|
||||
.0
|
||||
.clone();
|
||||
|
||||
show_dropdown_picker(&guess_str, &keys, onchange.reform(move |value_str: String| {
|
||||
options.get(value_str.as_str()).unwrap().clone()
|
||||
}))
|
||||
show_dropdown_picker(
|
||||
&guess_str,
|
||||
&keys,
|
||||
onchange.reform(move |value_str: String| options.get(value_str.as_str()).unwrap().clone()),
|
||||
)
|
||||
}
|
||||
|
||||
fn show_range_picker<T: FromStr + ToString + Clone + 'static>(
|
||||
@ -43,8 +51,7 @@ fn show_range_picker<T: FromStr + ToString + Clone + 'static>(
|
||||
default_min: T,
|
||||
default_max: T,
|
||||
onchange: Callback<(T, T)>,
|
||||
) -> Html {
|
||||
|
||||
) -> Html {
|
||||
let onchange_min = {
|
||||
let onchange = onchange.clone();
|
||||
let default_min = default_min.clone();
|
||||
@ -91,13 +98,13 @@ fn show_range_picker<T: FromStr + ToString + Clone + 'static>(
|
||||
pub fn generator_picker(
|
||||
column: &SQLColumn,
|
||||
value: &SQLValueGuess,
|
||||
onchange: Callback<SQLValueGuess>
|
||||
onchange: Callback<SQLValueGuess>,
|
||||
) -> Html {
|
||||
// TODO: Refacotr 'time', 'datetime', and 'date'. They are very similar
|
||||
match value {
|
||||
SQLValueGuess::Int(guess) => {
|
||||
if column.primary_key {
|
||||
return html!("Auto increment")
|
||||
return html!("Auto increment");
|
||||
}
|
||||
|
||||
let mut min = 0;
|
||||
@ -108,59 +115,73 @@ pub fn generator_picker(
|
||||
}
|
||||
|
||||
// TODO: Disallow entering floating point numbers
|
||||
show_range_picker(min, max, 0, 100, onchange.reform(|(min, max)| {
|
||||
SQLValueGuess::Int(SQLIntValueGuess::Range(min, max))
|
||||
}))
|
||||
},
|
||||
SQLValueGuess::Float(min, max) => {
|
||||
show_range_picker(*min, *max, 0.0, 100.0, onchange.reform(|(min, max)| {
|
||||
SQLValueGuess::Float(min, max)
|
||||
}))
|
||||
},
|
||||
show_range_picker(
|
||||
min,
|
||||
max,
|
||||
0,
|
||||
100,
|
||||
onchange.reform(|(min, max)| SQLValueGuess::Int(SQLIntValueGuess::Range(min, max))),
|
||||
)
|
||||
}
|
||||
SQLValueGuess::Float(min, max) => show_range_picker(
|
||||
*min,
|
||||
*max,
|
||||
0.0,
|
||||
100.0,
|
||||
onchange.reform(|(min, max)| SQLValueGuess::Float(min, max)),
|
||||
),
|
||||
SQLValueGuess::Date(guess) => {
|
||||
let options = HashMap::from([
|
||||
("Now".into() , SQLTimeValueGuess::Now),
|
||||
("Now".into(), SQLTimeValueGuess::Now),
|
||||
("Future".into(), SQLTimeValueGuess::Future),
|
||||
("Past".into() , SQLTimeValueGuess::Past),
|
||||
("Past".into(), SQLTimeValueGuess::Past),
|
||||
]);
|
||||
|
||||
show_enum_dropdown(guess, options, onchange.reform(|enum_value| {
|
||||
SQLValueGuess::Date(enum_value)
|
||||
}))
|
||||
},
|
||||
show_enum_dropdown(
|
||||
guess,
|
||||
options,
|
||||
onchange.reform(|enum_value| SQLValueGuess::Date(enum_value)),
|
||||
)
|
||||
}
|
||||
SQLValueGuess::Time(guess) => {
|
||||
let options = HashMap::from([
|
||||
("Now".into() , SQLTimeValueGuess::Now),
|
||||
("Now".into(), SQLTimeValueGuess::Now),
|
||||
("Future".into(), SQLTimeValueGuess::Future),
|
||||
("Past".into() , SQLTimeValueGuess::Past),
|
||||
("Past".into(), SQLTimeValueGuess::Past),
|
||||
]);
|
||||
|
||||
show_enum_dropdown(guess, options, onchange.reform(|enum_value| {
|
||||
SQLValueGuess::Time(enum_value)
|
||||
}))
|
||||
},
|
||||
show_enum_dropdown(
|
||||
guess,
|
||||
options,
|
||||
onchange.reform(|enum_value| SQLValueGuess::Time(enum_value)),
|
||||
)
|
||||
}
|
||||
SQLValueGuess::Datetime(guess) => {
|
||||
let options = HashMap::from([
|
||||
("Now".into() , SQLTimeValueGuess::Now),
|
||||
("Now".into(), SQLTimeValueGuess::Now),
|
||||
("Future".into(), SQLTimeValueGuess::Future),
|
||||
("Past".into() , SQLTimeValueGuess::Past),
|
||||
("Past".into(), SQLTimeValueGuess::Past),
|
||||
]);
|
||||
|
||||
show_enum_dropdown(guess, options, onchange.reform(|enum_value| {
|
||||
SQLValueGuess::Datetime(enum_value)
|
||||
}))
|
||||
show_enum_dropdown(
|
||||
guess,
|
||||
options,
|
||||
onchange.reform(|enum_value| SQLValueGuess::Datetime(enum_value)),
|
||||
)
|
||||
}
|
||||
SQLValueGuess::Bool(guess) => {
|
||||
let options = HashMap::from([
|
||||
("Random".into(), SQLBoolValueGuess::Random),
|
||||
("True".into() , SQLBoolValueGuess::True),
|
||||
("False".into() , SQLBoolValueGuess::False),
|
||||
("True".into(), SQLBoolValueGuess::True),
|
||||
("False".into(), SQLBoolValueGuess::False),
|
||||
]);
|
||||
|
||||
show_enum_dropdown(guess, options, onchange.reform(|enum_value| {
|
||||
SQLValueGuess::Bool(enum_value)
|
||||
}))
|
||||
},
|
||||
show_enum_dropdown(
|
||||
guess,
|
||||
options,
|
||||
onchange.reform(|enum_value| SQLValueGuess::Bool(enum_value)),
|
||||
)
|
||||
}
|
||||
SQLValueGuess::String(max_size, guess) => {
|
||||
if let Some(constraint) = &column.check_constraint {
|
||||
if let SQLCheckConstraint::OneOf(_) = constraint {
|
||||
@ -169,22 +190,24 @@ pub fn generator_picker(
|
||||
}
|
||||
|
||||
let options = HashMap::from([
|
||||
("Lorem Ipsum".into() , SQLStringValueGuess::LoremIpsum),
|
||||
("First Name".into() , SQLStringValueGuess::FirstName),
|
||||
("Last Name".into() , SQLStringValueGuess::LastName),
|
||||
("Full Name".into() , SQLStringValueGuess::FullName),
|
||||
("Empty".into() , SQLStringValueGuess::Empty),
|
||||
("Phone number".into() , SQLStringValueGuess::PhoneNumber),
|
||||
("City name".into() , SQLStringValueGuess::CityName),
|
||||
("Address".into() , SQLStringValueGuess::Address),
|
||||
("Email".into() , SQLStringValueGuess::Email),
|
||||
("URL".into() , SQLStringValueGuess::URL),
|
||||
("Lorem Ipsum".into(), SQLStringValueGuess::LoremIpsum),
|
||||
("First Name".into(), SQLStringValueGuess::FirstName),
|
||||
("Last Name".into(), SQLStringValueGuess::LastName),
|
||||
("Full Name".into(), SQLStringValueGuess::FullName),
|
||||
("Empty".into(), SQLStringValueGuess::Empty),
|
||||
("Phone number".into(), SQLStringValueGuess::PhoneNumber),
|
||||
("City name".into(), SQLStringValueGuess::CityName),
|
||||
("Address".into(), SQLStringValueGuess::Address),
|
||||
("Email".into(), SQLStringValueGuess::Email),
|
||||
("URL".into(), SQLStringValueGuess::URL),
|
||||
]);
|
||||
|
||||
let max_size = *max_size;
|
||||
show_enum_dropdown(guess, options, onchange.reform(move |enum_value| {
|
||||
SQLValueGuess::String(max_size, enum_value)
|
||||
}))
|
||||
},
|
||||
show_enum_dropdown(
|
||||
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 sql_column_info;
|
||||
|
@ -1,29 +1,35 @@
|
||||
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)]
|
||||
pub struct SQLTableColumnInfoProps {
|
||||
pub table: Rc<SQLTable>,
|
||||
pub guessess: Rc<RefCell<HashMap<String, SQLValueGuess>>>,
|
||||
pub onchange: Callback<(String, SQLValueGuess)>
|
||||
pub onchange: Callback<(String, SQLValueGuess)>,
|
||||
}
|
||||
|
||||
const CHECK_MARK: &str = "✔️";
|
||||
const CROSS_MARK: &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]
|
||||
pub fn SQLTableColumnInfo(props: &SQLTableColumnInfoProps) -> Html {
|
||||
let table = &props.table;
|
||||
|
||||
let rows = table.columns.iter()
|
||||
.map(|col| {
|
||||
let rows = table.columns.iter().map(|col| {
|
||||
let guessess = &props.guessess.borrow();
|
||||
let generator = guessess.get(&col.name);
|
||||
|
||||
@ -35,7 +41,9 @@ pub fn SQLTableColumnInfo(props: &SQLTableColumnInfoProps) -> Html {
|
||||
}
|
||||
|
||||
let name = col.name.clone();
|
||||
let onchange = props.onchange.reform(move |value: SQLValueGuess| (name.clone(), value));
|
||||
let onchange = props
|
||||
.onchange
|
||||
.reform(move |value: SQLValueGuess| (name.clone(), value));
|
||||
html! {
|
||||
<tr>
|
||||
<td> { &col.name } </td>
|
||||
@ -52,10 +60,9 @@ pub fn SQLTableColumnInfo(props: &SQLTableColumnInfoProps) -> Html {
|
||||
<td> { foreign_key } </td>
|
||||
</tr>
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
html!{
|
||||
html! {
|
||||
<div
|
||||
class="table-column-info flex-column inline-block"
|
||||
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 rand::{seq::SliceRandom, Rng, rngs::ThreadRng};
|
||||
use chrono::{Local, NaiveDateTime, Days};
|
||||
use fake::{faker::{lorem::en::*, name::en::{FirstName, LastName, Name}, phone_number::en::PhoneNumber, internet::en::{DomainSuffix, FreeEmail}, company::en::BsNoun, address::{en::{CityName, StreetName}}}, Fake};
|
||||
use rand::{rngs::ThreadRng, seq::SliceRandom, Rng};
|
||||
|
||||
use crate::magicdraw_parser::{SQLTable, SQLColumn, SQLType, SQLCheckConstraint};
|
||||
use crate::magicdraw_parser::{SQLCheckConstraint, SQLColumn, SQLTable, SQLType};
|
||||
|
||||
const INDENT: &str = " ";
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum SQLIntValueGuess {
|
||||
Range(i32, i32),
|
||||
AutoIncrement
|
||||
AutoIncrement,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum SQLTimeValueGuess {
|
||||
Now,
|
||||
Future,
|
||||
Past
|
||||
Past,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
@ -60,8 +74,8 @@ pub enum SQLValueGuess {
|
||||
pub fn generate_fake_entries(
|
||||
tables: &[Rc<SQLTable>],
|
||||
value_guessess: &Vec<Ref<HashMap<String, SQLValueGuess>>>,
|
||||
rows_per_table: u32
|
||||
) -> Result<String> {
|
||||
rows_per_table: u32,
|
||||
) -> Result<String> {
|
||||
let mut lines = vec![];
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
@ -78,11 +92,13 @@ pub fn generate_fake_entries(
|
||||
let mut foreign_columns = vec![];
|
||||
for (i, column) in table.columns.iter().enumerate() {
|
||||
if let Some((table_name, column_name)) = &column.foreign_key {
|
||||
let (table_idx, table) = tables.iter()
|
||||
let (table_idx, table) = tables
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_, table)| table.name.eq(table_name))
|
||||
.expect("Foreign table not found");
|
||||
let (column_idx, _) = table.columns
|
||||
let (column_idx, _) = table
|
||||
.columns
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_, column)| column.name.eq(column_name))
|
||||
@ -109,7 +125,11 @@ pub fn generate_fake_entries(
|
||||
.get(column.name.as_str())
|
||||
.expect("Failed to get column guess");
|
||||
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();
|
||||
|
||||
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>;
|
||||
|
||||
// If the foreign column, is also a foreign of the other table, ...
|
||||
// Then we need to filter out available options which have not been filled in
|
||||
if all_foreign_columns[*foreign_table_idx].iter().find(|(idx, _, _)| idx == foreign_column_idx).is_some() {
|
||||
available_values = all_entries[*foreign_table_idx].iter()
|
||||
if all_foreign_columns[*foreign_table_idx]
|
||||
.iter()
|
||||
.find(|(idx, _, _)| idx == foreign_column_idx)
|
||||
.is_some()
|
||||
{
|
||||
available_values = all_entries[*foreign_table_idx]
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(i, _)| entries_with_foreign_keys_copy.contains(&(*foreign_table_idx, *i)))
|
||||
.filter(|(i, _)| {
|
||||
entries_with_foreign_keys_copy.contains(&(*foreign_table_idx, *i))
|
||||
})
|
||||
.map(|(_, entry)| entry[*foreign_column_idx].as_str())
|
||||
.collect();
|
||||
} else {
|
||||
available_values = all_entries[*foreign_table_idx].iter()
|
||||
available_values = all_entries[*foreign_table_idx]
|
||||
.iter()
|
||||
.map(|entry| entry[*foreign_column_idx].as_str())
|
||||
.collect();
|
||||
}
|
||||
@ -165,7 +195,8 @@ pub fn generate_fake_entries(
|
||||
lines.push(format!("INSERT INTO {}", table.name));
|
||||
lines.push(format!("{}({})", INDENT, column_names.join(", ")));
|
||||
lines.push("VALUES".into());
|
||||
let entries_str = entries.iter()
|
||||
let entries_str = entries
|
||||
.iter()
|
||||
.map(|entry| format!("{}({})", INDENT, entry.join(", ")))
|
||||
.collect::<Vec<_>>()
|
||||
.join(",\n");
|
||||
@ -183,7 +214,7 @@ fn generate_time_value(rng: &mut ThreadRng, guess: &SQLTimeValueGuess) -> NaiveD
|
||||
SQLTimeValueGuess::Future => {
|
||||
let days = rng.gen_range(1..=30);
|
||||
now.checked_add_days(Days::new(days)).unwrap()
|
||||
},
|
||||
}
|
||||
SQLTimeValueGuess::Past => {
|
||||
let days = rng.gen_range(7..=365);
|
||||
now.checked_sub_days(Days::new(days)).unwrap()
|
||||
@ -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 {
|
||||
SQLValueGuess::Int(int_guess) => {
|
||||
match int_guess {
|
||||
SQLIntValueGuess::Range(min, max) => {
|
||||
rng.gen_range((*min)..=(*max)).to_string()
|
||||
},
|
||||
SQLValueGuess::Int(int_guess) => match int_guess {
|
||||
SQLIntValueGuess::Range(min, max) => rng.gen_range((*min)..=(*max)).to_string(),
|
||||
SQLIntValueGuess::AutoIncrement => {
|
||||
let str = auto_increment_counter.to_string();
|
||||
*auto_increment_counter += 1;
|
||||
str
|
||||
},
|
||||
}
|
||||
},
|
||||
SQLValueGuess::Date(time_gues) => {
|
||||
let datetime = generate_time_value(rng, &time_gues);
|
||||
format!("'{}'", datetime.format("%Y-%m-%d"))
|
||||
},
|
||||
}
|
||||
SQLValueGuess::Time(time_gues) => {
|
||||
let datetime = generate_time_value(rng, &time_gues);
|
||||
format!("'{}'", datetime.format("%H:%M:%S"))
|
||||
},
|
||||
}
|
||||
SQLValueGuess::Datetime(time_gues) => {
|
||||
let datetime = generate_time_value(rng, &time_gues);
|
||||
format!("'{}'", datetime.format("%Y-%m-%d %H:%M:%S"))
|
||||
},
|
||||
SQLValueGuess::Bool(bool_guess) => {
|
||||
match bool_guess {
|
||||
}
|
||||
SQLValueGuess::Bool(bool_guess) => match bool_guess {
|
||||
SQLBoolValueGuess::True => "1".into(),
|
||||
SQLBoolValueGuess::False => "0".into(),
|
||||
SQLBoolValueGuess::Random => rng.gen_range(0..=1).to_string(),
|
||||
}
|
||||
},
|
||||
SQLValueGuess::Float(min, max) => {
|
||||
let value = rng.gen_range((*min)..(*max));
|
||||
((value * 100.0 as f32).round() / 100.0).to_string()
|
||||
},
|
||||
}
|
||||
SQLValueGuess::String(max_size, string_guess) => {
|
||||
let mut str = match string_guess {
|
||||
SQLStringValueGuess::LoremIpsum => {
|
||||
|
||||
let mut current_len = 0;
|
||||
let mut text = vec![];
|
||||
let words: Vec<String> = Words(3..10).fake_with_rng(rng);
|
||||
for word in words {
|
||||
current_len += word.len() + 1;
|
||||
text.push(word);
|
||||
if current_len > *max_size { break; }
|
||||
if current_len > *max_size {
|
||||
break;
|
||||
}
|
||||
}
|
||||
text.join(" ").to_string()
|
||||
},
|
||||
SQLStringValueGuess::FirstName => {
|
||||
FirstName().fake_with_rng(rng)
|
||||
},
|
||||
SQLStringValueGuess::LastName => {
|
||||
LastName().fake_with_rng(rng)
|
||||
},
|
||||
SQLStringValueGuess::FullName => {
|
||||
Name().fake_with_rng(rng)
|
||||
},
|
||||
SQLStringValueGuess::PhoneNumber => {
|
||||
PhoneNumber().fake_with_rng(rng)
|
||||
},
|
||||
SQLStringValueGuess::CityName => {
|
||||
CityName().fake_with_rng(rng)
|
||||
},
|
||||
SQLStringValueGuess::Address => {
|
||||
StreetName().fake_with_rng(rng)
|
||||
},
|
||||
SQLStringValueGuess::Email => {
|
||||
FreeEmail().fake_with_rng(rng)
|
||||
},
|
||||
}
|
||||
SQLStringValueGuess::FirstName => FirstName().fake_with_rng(rng),
|
||||
SQLStringValueGuess::LastName => LastName().fake_with_rng(rng),
|
||||
SQLStringValueGuess::FullName => Name().fake_with_rng(rng),
|
||||
SQLStringValueGuess::PhoneNumber => PhoneNumber().fake_with_rng(rng),
|
||||
SQLStringValueGuess::CityName => CityName().fake_with_rng(rng),
|
||||
SQLStringValueGuess::Address => StreetName().fake_with_rng(rng),
|
||||
SQLStringValueGuess::Email => FreeEmail().fake_with_rng(rng),
|
||||
SQLStringValueGuess::URL => {
|
||||
let suffix: String = DomainSuffix().fake_with_rng(rng);
|
||||
let noun: String = BsNoun().fake_with_rng(rng);
|
||||
let noun: String = noun.to_lowercase()
|
||||
let noun: String = noun
|
||||
.to_lowercase()
|
||||
.chars()
|
||||
.map(|c| if c.is_whitespace() { '-' } else { c })
|
||||
.collect();
|
||||
format!("www.{}.{}", noun, suffix)
|
||||
},
|
||||
}
|
||||
SQLStringValueGuess::RandomEnum(options) => {
|
||||
options.choose(rng).unwrap().to_string()
|
||||
},
|
||||
SQLStringValueGuess::Empty => {
|
||||
"".into()
|
||||
}
|
||||
SQLStringValueGuess::Empty => "".into(),
|
||||
};
|
||||
|
||||
str.truncate(*max_size);
|
||||
@ -289,9 +304,9 @@ fn generate_value(rng: &mut ThreadRng, guess: &SQLValueGuess, auto_increment_cou
|
||||
fn generate_string_guess(column: &SQLColumn) -> SQLStringValueGuess {
|
||||
if let Some(constraint) = &column.check_constraint {
|
||||
if let SQLCheckConstraint::OneOf(options) = constraint {
|
||||
return SQLStringValueGuess::RandomEnum(options.clone())
|
||||
return SQLStringValueGuess::RandomEnum(options.clone());
|
||||
} else {
|
||||
return SQLStringValueGuess::LoremIpsum
|
||||
return SQLStringValueGuess::LoremIpsum;
|
||||
}
|
||||
}
|
||||
|
||||
@ -323,10 +338,8 @@ pub fn generate_guess(column: &SQLColumn) -> SQLValueGuess {
|
||||
} else {
|
||||
SQLValueGuess::Int(SQLIntValueGuess::Range(0, 100))
|
||||
}
|
||||
},
|
||||
SQLType::Float | SQLType::Decimal => {
|
||||
SQLValueGuess::Float(0.0, 100.0)
|
||||
},
|
||||
}
|
||||
SQLType::Float | SQLType::Decimal => SQLValueGuess::Float(0.0, 100.0),
|
||||
SQLType::Date => {
|
||||
let name = column.name.to_lowercase();
|
||||
if name.contains("create") || name.contains("update") {
|
||||
@ -334,7 +347,7 @@ pub fn generate_guess(column: &SQLColumn) -> SQLValueGuess {
|
||||
} else {
|
||||
SQLValueGuess::Date(SQLTimeValueGuess::Now)
|
||||
}
|
||||
},
|
||||
}
|
||||
SQLType::Time => {
|
||||
let name = column.name.to_lowercase();
|
||||
if name.contains("create") || name.contains("update") {
|
||||
@ -342,7 +355,7 @@ pub fn generate_guess(column: &SQLColumn) -> SQLValueGuess {
|
||||
} else {
|
||||
SQLValueGuess::Time(SQLTimeValueGuess::Now)
|
||||
}
|
||||
},
|
||||
}
|
||||
SQLType::Datetime => {
|
||||
let name = column.name.to_lowercase();
|
||||
if name.contains("create") || name.contains("update") {
|
||||
@ -350,13 +363,11 @@ pub fn generate_guess(column: &SQLColumn) -> SQLValueGuess {
|
||||
} else {
|
||||
SQLValueGuess::Datetime(SQLTimeValueGuess::Now)
|
||||
}
|
||||
},
|
||||
SQLType::Bool => {
|
||||
SQLValueGuess::Bool(SQLBoolValueGuess::Random)
|
||||
},
|
||||
}
|
||||
SQLType::Bool => SQLValueGuess::Bool(SQLBoolValueGuess::Random),
|
||||
SQLType::Varchar(max_size) => {
|
||||
SQLValueGuess::String(max_size as usize, generate_string_guess(column))
|
||||
},
|
||||
}
|
||||
SQLType::Char(max_size) => {
|
||||
SQLValueGuess::String(max_size as usize, generate_string_guess(column))
|
||||
}
|
||||
@ -364,7 +375,9 @@ pub fn generate_guess(column: &SQLColumn) -> SQLValueGuess {
|
||||
}
|
||||
|
||||
pub fn generate_table_guessess(table: &SQLTable) -> HashMap<String, SQLValueGuess> {
|
||||
table.columns.iter()
|
||||
table
|
||||
.columns
|
||||
.iter()
|
||||
.filter(|column| column.foreign_key.is_none())
|
||||
.map(|column| (column.name.clone(), generate_guess(column)))
|
||||
.collect()
|
||||
|
@ -1,31 +1,31 @@
|
||||
use std::io::{Read, Seek};
|
||||
|
||||
use anyhow::{Context, Ok, Result};
|
||||
use xml::attribute::OwnedAttribute;
|
||||
use xml::name::OwnedName;
|
||||
use xml::{EventReader, reader::XmlEvent};
|
||||
use xml::{reader::XmlEvent, EventReader};
|
||||
use zip::ZipArchive;
|
||||
use anyhow::{Result, Context, Ok};
|
||||
|
||||
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)]
|
||||
pub struct DDLClass {
|
||||
pub class_id: String,
|
||||
pub property_ids: Vec<String>
|
||||
pub property_ids: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DDLScript {
|
||||
pub script_id: String,
|
||||
pub classess: Vec<DDLClass>
|
||||
pub classess: Vec<DDLClass>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DDLProject {
|
||||
pub model_id: String,
|
||||
pub scripts: Vec<DDLScript>
|
||||
pub scripts: Vec<DDLScript>,
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
|
||||
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 class_id = None;
|
||||
|
||||
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 {
|
||||
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| {
|
||||
@ -57,20 +62,30 @@ fn parse_class<R: Read>(parser: &mut MyEventReader<R>, attrs: &[OwnedAttribute])
|
||||
|
||||
Ok(DDLClass {
|
||||
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 script_id = None;
|
||||
|
||||
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 {
|
||||
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| {
|
||||
@ -84,20 +99,30 @@ fn parse_script<R: Read>(parser: &mut MyEventReader<R>, attrs: &[OwnedAttribute]
|
||||
|
||||
Ok(DDLScript {
|
||||
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 model_id = None;
|
||||
|
||||
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 {
|
||||
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| {
|
||||
@ -111,28 +136,39 @@ fn parse_project<R: Read>(parser: &mut MyEventReader<R>, attrs: &[OwnedAttribute
|
||||
|
||||
Ok(DDLProject {
|
||||
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>> {
|
||||
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();
|
||||
|
||||
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 {
|
||||
match parser.next()? {
|
||||
XmlEvent::StartElement { name, attributes, .. } => {
|
||||
XmlEvent::StartElement {
|
||||
name, attributes, ..
|
||||
} => {
|
||||
if is_project_element(&name, &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 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::{Result, Context};
|
||||
use anyhow::{Context, Result};
|
||||
use lazy_regex::regex_captures;
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
fmt::Display,
|
||||
io::{Read, Seek},
|
||||
};
|
||||
use zip::ZipArchive;
|
||||
|
||||
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)]
|
||||
pub enum SQLType {
|
||||
@ -45,7 +56,7 @@ impl Display for SQLType {
|
||||
#[derive(Debug, PartialEq, Deserialize, Serialize)]
|
||||
pub enum SQLCheckConstraint {
|
||||
OneOf(Vec<String>),
|
||||
Freeform(String)
|
||||
Freeform(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Deserialize, Serialize)]
|
||||
@ -55,7 +66,7 @@ pub struct SQLColumn {
|
||||
pub primary_key: bool,
|
||||
pub nullable: bool,
|
||||
pub foreign_key: Option<(String, String)>,
|
||||
pub check_constraint: Option<SQLCheckConstraint>
|
||||
pub check_constraint: Option<SQLCheckConstraint>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Deserialize, Serialize)]
|
||||
@ -66,7 +77,7 @@ pub struct SQLTable {
|
||||
|
||||
#[derive(Debug, PartialEq, Deserialize, Serialize)]
|
||||
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> {
|
||||
@ -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 {
|
||||
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) {
|
||||
return *nullable;
|
||||
}
|
||||
@ -95,7 +110,7 @@ fn is_primary_key(modifiers: &[UMLModifier], property: &str) -> bool {
|
||||
for modifier in modifiers {
|
||||
if let UMLModifier::PirmaryKey(UMLPrimaryKeyModifier { property_id }) = modifier {
|
||||
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> {
|
||||
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) {
|
||||
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> {
|
||||
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) {
|
||||
return Some(&to_property_id)
|
||||
return Some(&to_property_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
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);
|
||||
if to_id.is_none() {
|
||||
return Ok(None)
|
||||
return Ok(None);
|
||||
}
|
||||
let to_id = to_id.unwrap();
|
||||
|
||||
@ -156,12 +183,14 @@ fn parse_check_constraint(str: &str) -> SQLCheckConstraint {
|
||||
Some(SQLCheckConstraint::OneOf(variants))
|
||||
}
|
||||
|
||||
try_parse_one_of(str)
|
||||
.unwrap_or(SQLCheckConstraint::Freeform(str.to_string()))
|
||||
try_parse_one_of(str).unwrap_or(SQLCheckConstraint::Freeform(str.to_string()))
|
||||
}
|
||||
|
||||
// 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 package in &model.packages {
|
||||
for class in &package.classess {
|
||||
@ -179,7 +208,11 @@ fn get_sql_check_constraint<'a>(models: &'a [UMLModel], property_name: &str) ->
|
||||
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 {
|
||||
SQLTypeName::Int => SQLType::Int,
|
||||
SQLTypeName::Date => SQLType::Date,
|
||||
@ -196,7 +229,7 @@ fn get_sql_type(modifiers: &[UMLModifier], type_name: SQLTypeName, property: &st
|
||||
// For now just pick a defautl arbitrarily
|
||||
SQLType::Char(31)
|
||||
}
|
||||
},
|
||||
}
|
||||
SQLTypeName::Varchar => {
|
||||
if let Some(type_modifier) = get_type_modifier(modifiers, property) {
|
||||
let (_, size) = regex_captures!(r#"^\((\d+)\)$"#, type_modifier)
|
||||
@ -207,12 +240,13 @@ fn get_sql_type(modifiers: &[UMLModifier], type_name: SQLTypeName, property: &st
|
||||
// For now just pick a defautl arbitrarily
|
||||
SQLType::Varchar(255)
|
||||
}
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn get_used_types<'a>(models: &'a [UMLModel]) -> HashSet<&'a String> {
|
||||
models.iter()
|
||||
models
|
||||
.iter()
|
||||
.flat_map(|model| &model.packages)
|
||||
.flat_map(|package| &package.classess)
|
||||
.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 {
|
||||
let mut tables = vec![];
|
||||
|
||||
let model_properties = ddl_script.classess.iter()
|
||||
.flat_map(|class| class.property_ids.iter().map(|prop| (&class.class_id, prop)))
|
||||
let model_properties = ddl_script
|
||||
.classess
|
||||
.iter()
|
||||
.flat_map(|class| {
|
||||
class
|
||||
.property_ids
|
||||
.iter()
|
||||
.map(|prop| (&class.class_id, prop))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut model_classess = vec![];
|
||||
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);
|
||||
}
|
||||
|
||||
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![];
|
||||
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 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 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 {
|
||||
name,
|
||||
columns
|
||||
})
|
||||
tables.push(SQLTable { name, columns })
|
||||
}
|
||||
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 anyhow::{Result, Context, bail};
|
||||
|
||||
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)]
|
||||
struct UsedPackage {
|
||||
share_point_id: String,
|
||||
name: String,
|
||||
needed_types: Vec<String>
|
||||
needed_types: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
@ -23,7 +26,7 @@ pub enum SQLTypeName {
|
||||
Float,
|
||||
Bool,
|
||||
Char,
|
||||
Varchar
|
||||
Varchar,
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
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 project_uri = get_attribute(&attrs, None, "usedProjectURI")?;
|
||||
let name = project_uri.split("/").last().unwrap();
|
||||
|
||||
parse_element(parser, &mut |p, name, attrs| {
|
||||
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(())
|
||||
})?;
|
||||
@ -46,32 +55,47 @@ fn parse_used_package<R: Read>(parser: &mut MyEventReader<R>, attrs: &[OwnedAttr
|
||||
Ok(UsedPackage {
|
||||
name: name.to_string(),
|
||||
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 needed_types_per_package = HashMap::new();
|
||||
for needed_type in needed_types.iter() {
|
||||
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);
|
||||
}
|
||||
|
||||
let mut parser: MyEventReader<_> = EventReader::new(file).into();
|
||||
loop {
|
||||
match parser.next()? {
|
||||
XmlEvent::StartElement { name, attributes, .. } => {
|
||||
XmlEvent::StartElement {
|
||||
name, attributes, ..
|
||||
} => {
|
||||
if check_name(&name, None, "projectUsages") {
|
||||
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) {
|
||||
packages.push(parse_used_package(&mut parser, &attributes, needed_types_for_package)?);
|
||||
if let Some(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,
|
||||
"date" => Date,
|
||||
"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![];
|
||||
|
||||
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| {
|
||||
@ -110,7 +137,7 @@ fn parse_types_package<R: Read>(parser: &mut MyEventReader<R>) -> Result<Vec<(St
|
||||
if !type_name.eq("StructuredExpression") {
|
||||
types.push((
|
||||
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)
|
||||
}
|
||||
|
||||
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 parser: MyEventReader<_> = EventReader::new(reader).into();
|
||||
loop {
|
||||
match parser.next()? {
|
||||
XmlEvent::StartElement { name, attributes, .. } => {
|
||||
XmlEvent::StartElement {
|
||||
name, attributes, ..
|
||||
} => {
|
||||
if check_name(&name, Some("uml"), "Package") {
|
||||
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)?
|
||||
.into_iter()
|
||||
.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
XmlEvent::EndDocument => { break; },
|
||||
}
|
||||
XmlEvent::EndDocument => {
|
||||
break;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@ -147,13 +185,17 @@ fn parse_primitive_types<R: Read>(reader: R, used_packages: &[UsedPackage]) -> R
|
||||
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 meta_model_file = project.by_name("com.nomagic.ci.metamodel.project")?;
|
||||
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))
|
||||
.map(|f| f.to_string())
|
||||
.collect::<Vec<_>>();
|
||||
|
@ -1,19 +1,22 @@
|
||||
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 anyhow::{Result, Context};
|
||||
|
||||
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)]
|
||||
pub struct UMLProperty {
|
||||
pub id: String,
|
||||
pub name: Option<String>,
|
||||
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,
|
||||
@ -33,32 +36,32 @@ pub struct UMLClass {
|
||||
pub id: String,
|
||||
pub name: Option<String>,
|
||||
pub properties: Vec<UMLProperty>,
|
||||
pub constraints: Vec<UMLConstraint>
|
||||
pub constraints: Vec<UMLConstraint>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UMLPackage {
|
||||
pub id: String,
|
||||
pub name: Option<String>,
|
||||
pub classess: Vec<UMLClass>
|
||||
pub classess: Vec<UMLClass>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UMLModel {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub packages: Vec<UMLPackage>
|
||||
pub packages: Vec<UMLPackage>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UMLPrimaryKeyModifier {
|
||||
pub property_id: String
|
||||
pub property_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UMLNullableModifier {
|
||||
pub property_id: String,
|
||||
pub nullable: bool
|
||||
pub nullable: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -69,13 +72,13 @@ pub struct UMLForeignKeyModifier {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UMLUniqueModifier {
|
||||
pub property_id: String
|
||||
pub property_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UMLTypeModifier {
|
||||
pub property_id: String,
|
||||
pub modifier: String
|
||||
pub modifier: String,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -84,13 +87,18 @@ pub enum UMLModifier {
|
||||
PirmaryKey(UMLPrimaryKeyModifier),
|
||||
Nullable(UMLNullableModifier),
|
||||
ForeignKey(UMLForeignKeyModifier),
|
||||
Type(UMLTypeModifier)
|
||||
Type(UMLTypeModifier),
|
||||
}
|
||||
|
||||
fn parse_property<R: Read>(parser: &mut MyEventReader<R>, attrs: &[OwnedAttribute]) -> Result<UMLProperty> {
|
||||
fn parse_property<R: Read>(
|
||||
parser: &mut MyEventReader<R>,
|
||||
attrs: &[OwnedAttribute],
|
||||
) -> 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 is_id = get_attribute(attrs, None, "isID")
|
||||
.unwrap_or("false")
|
||||
.eq("true");
|
||||
let mut type_href = None;
|
||||
|
||||
parse_element(parser, &mut |p, name, attrs| {
|
||||
@ -106,11 +114,14 @@ fn parse_property<R: Read>(parser: &mut MyEventReader<R>, attrs: &[OwnedAttribut
|
||||
id,
|
||||
name,
|
||||
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>(
|
||||
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 language = None;
|
||||
@ -118,7 +129,9 @@ fn parse_constraint<R: Read>(parser: &mut MyEventReader<R>, attrs: &[OwnedAttrib
|
||||
|
||||
parse_element(parser, &mut |p, name, attrs| {
|
||||
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() {
|
||||
let contents = get_element_characters(p)?;
|
||||
if contents.len() > 0 {
|
||||
@ -137,8 +150,8 @@ fn parse_constraint<R: Read>(parser: &mut MyEventReader<R>, attrs: &[OwnedAttrib
|
||||
class_id: Some(constrainted_element_id.context("Missing class id")?),
|
||||
body: Some(format!("in {}", check_body)),
|
||||
property_id: None,
|
||||
property_name: Some(prop_name.into())
|
||||
}))
|
||||
property_name: Some(prop_name.into()),
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@ -147,22 +160,27 @@ fn parse_constraint<R: Read>(parser: &mut MyEventReader<R>, attrs: &[OwnedAttrib
|
||||
property_id: Some(constrainted_element_id.context("Missing property id")?),
|
||||
body: 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 consraints = vec![];
|
||||
let id = get_attribute(attrs, Some("xmi"), "id")?.into();
|
||||
let name = get_attribute(attrs, None, "name").ok().map(str::to_string);
|
||||
|
||||
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 {
|
||||
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| {
|
||||
@ -184,13 +202,17 @@ fn parse_class<R: Read>(parser: &mut MyEventReader<R>, attrs: &[OwnedAttribute])
|
||||
})
|
||||
}
|
||||
|
||||
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 id = get_attribute(attrs, Some("xmi"), "id")?.into();
|
||||
let name = get_attribute(attrs, None, "name").ok().map(str::to_string);
|
||||
|
||||
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| {
|
||||
@ -200,20 +222,20 @@ fn parse_package<R: Read>(parser: &mut MyEventReader<R>, attrs: &[OwnedAttribute
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(UMLPackage {
|
||||
id,
|
||||
name,
|
||||
classess
|
||||
})
|
||||
Ok(UMLPackage { 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 id = get_attribute(attrs, Some("xmi"), "id")?.into();
|
||||
let name = get_attribute(attrs, None, "name")?.into();
|
||||
|
||||
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| {
|
||||
@ -223,11 +245,7 @@ fn parse_model<R: Read>(parser: &mut MyEventReader<R>, attrs: &[OwnedAttribute])
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(UMLModel {
|
||||
id,
|
||||
name,
|
||||
packages
|
||||
})
|
||||
Ok(UMLModel { id, name, packages })
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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 modifiers = vec![];
|
||||
|
||||
@ -255,37 +275,64 @@ pub fn parse_uml_model<R: Read + Seek>(project: &mut ZipArchive<R>) -> Result<(V
|
||||
|
||||
loop {
|
||||
match parser.next()? {
|
||||
XmlEvent::StartElement { name, attributes, .. } => {
|
||||
XmlEvent::StartElement {
|
||||
name, attributes, ..
|
||||
} => {
|
||||
if check_name(&name, Some("uml"), "Model") {
|
||||
models.push(parse_model(&mut parser, &attributes)?);
|
||||
|
||||
} else if check_name(&name, Some("SQLProfile"), "PrimaryKey") {
|
||||
let constraint_id = unwrap_err_continue!(get_attribute(&attributes, None, "base_Constraint"));
|
||||
let constraint = unwrap_opt_continue!(find_constraint_by_id(&models, constraint_id));
|
||||
let 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();
|
||||
modifiers.push(UMLModifier::PirmaryKey(UMLPrimaryKeyModifier { property_id }));
|
||||
|
||||
modifiers.push(UMLModifier::PirmaryKey(UMLPrimaryKeyModifier {
|
||||
property_id,
|
||||
}));
|
||||
} else if check_name(&name, Some("SQLProfile"), "PKMember") {
|
||||
let property_id = unwrap_err_continue!(get_attribute(&attributes, None, "base_Property")).to_string();
|
||||
modifiers.push(UMLModifier::PirmaryKey(UMLPrimaryKeyModifier { property_id }));
|
||||
|
||||
let 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") {
|
||||
let property_id = unwrap_err_continue!(get_attribute(&attributes, None, "base_Property")).to_string();
|
||||
let nullable = unwrap_err_continue!(get_attribute(&attributes, None, "nullable")).eq("true");
|
||||
modifiers.push(UMLModifier::Nullable(UMLNullableModifier { property_id, nullable }));
|
||||
|
||||
let property_id =
|
||||
unwrap_err_continue!(get_attribute(&attributes, None, "base_Property"))
|
||||
.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") {
|
||||
let property_id = unwrap_err_continue!(get_attribute(&attributes, None, "base_Element")).into();
|
||||
let modifier = unwrap_err_continue!(get_attribute(&attributes, None, "typeModifier")).into();
|
||||
modifiers.push(UMLModifier::Type(UMLTypeModifier { property_id, modifier }));
|
||||
|
||||
let property_id =
|
||||
unwrap_err_continue!(get_attribute(&attributes, None, "base_Element"))
|
||||
.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") {
|
||||
let from_property_id = unwrap_err_continue!(get_attribute(&attributes, None, "members")).into();
|
||||
let to_property_id = unwrap_err_continue!(get_attribute(&attributes, None, "referencedMembers")).into();
|
||||
modifiers.push(UMLModifier::ForeignKey(UMLForeignKeyModifier { from_property_id, to_property_id }));
|
||||
let from_property_id =
|
||||
unwrap_err_continue!(get_attribute(&attributes, None, "members")).into();
|
||||
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 xml::{EventReader, reader::XmlEvent, name::OwnedName, attribute::OwnedAttribute};
|
||||
use anyhow::Result;
|
||||
use thiserror::Error;
|
||||
use xml::{attribute::OwnedAttribute, name::OwnedName, reader::XmlEvent, EventReader};
|
||||
|
||||
pub struct MyEventReader<R: Read> {
|
||||
depth: u32,
|
||||
event_reader: EventReader<R>
|
||||
event_reader: EventReader<R>,
|
||||
}
|
||||
|
||||
impl<R: Read> MyEventReader<R> {
|
||||
@ -30,7 +30,7 @@ impl<R: Read> From<EventReader<R>> for MyEventReader<R> {
|
||||
fn from(event_reader: EventReader<R>) -> Self {
|
||||
MyEventReader {
|
||||
depth: 0,
|
||||
event_reader
|
||||
event_reader,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -41,7 +41,7 @@ pub enum ParseProjectError {
|
||||
AttributeNotFound(Option<String>, String),
|
||||
|
||||
#[error("Unexpected end of XML document")]
|
||||
EndOfDocument
|
||||
EndOfDocument,
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
pub fn get_attribute<'a>(attributes: &'a [OwnedAttribute], prefix: Option<&str>, name: &str) -> Result<&'a str, ParseProjectError> {
|
||||
Ok(attributes.iter()
|
||||
pub fn get_attribute<'a>(
|
||||
attributes: &'a [OwnedAttribute],
|
||||
prefix: Option<&str>,
|
||||
name: &str,
|
||||
) -> Result<&'a str, ParseProjectError> {
|
||||
Ok(attributes
|
||||
.iter()
|
||||
.find(|attr| check_name(&attr.name, prefix, name))
|
||||
.map(|attr| &attr.value[..])
|
||||
.ok_or_else(
|
||||
|| ParseProjectError::AttributeNotFound(prefix.map(|s| s.to_owned()), name.to_owned()),
|
||||
)?)
|
||||
.ok_or_else(|| {
|
||||
ParseProjectError::AttributeNotFound(prefix.map(|s| s.to_owned()), name.to_owned())
|
||||
})?)
|
||||
}
|
||||
|
||||
#[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) {
|
||||
return attr.eq(expected_value);
|
||||
}
|
||||
@ -84,10 +94,10 @@ pub fn get_element_characters<R: Read>(parser: &mut MyEventReader<R>) -> Result<
|
||||
match parser.next()? {
|
||||
XmlEvent::Characters(text) => {
|
||||
parts.push(text);
|
||||
},
|
||||
}
|
||||
XmlEvent::EndElement { name } => {
|
||||
break;
|
||||
},
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@ -95,23 +105,30 @@ pub fn get_element_characters<R: Read>(parser: &mut MyEventReader<R>) -> Result<
|
||||
Ok(parts.join(" "))
|
||||
}
|
||||
|
||||
pub fn parse_element<R: Read, F>(parser: &mut MyEventReader<R>, process_element: &mut F) -> Result<()>
|
||||
where F: FnMut(&mut MyEventReader<R>, OwnedName, Vec<OwnedAttribute>) -> Result<()>
|
||||
pub fn parse_element<R: Read, F>(
|
||||
parser: &mut MyEventReader<R>,
|
||||
process_element: &mut F,
|
||||
) -> Result<()>
|
||||
where
|
||||
F: FnMut(&mut MyEventReader<R>, OwnedName, Vec<OwnedAttribute>) -> Result<()>,
|
||||
{
|
||||
|
||||
let starting_depth = parser.depth();
|
||||
loop {
|
||||
match parser.next()? {
|
||||
XmlEvent::StartElement { name, attributes, .. } => {
|
||||
XmlEvent::StartElement {
|
||||
name, attributes, ..
|
||||
} => {
|
||||
process_element(parser, name, attributes)?;
|
||||
if parser.depth() == starting_depth-1 { break; }
|
||||
if parser.depth() == starting_depth - 1 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
XmlEvent::EndElement { name } => {
|
||||
if parser.depth() == starting_depth-1 { break; }
|
||||
},
|
||||
XmlEvent::EndDocument => {
|
||||
Err(ParseProjectError::EndOfDocument)?
|
||||
},
|
||||
if parser.depth() == starting_depth - 1 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
XmlEvent::EndDocument => Err(ParseProjectError::EndOfDocument)?,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@ -124,7 +141,9 @@ macro_rules! unwrap_err_continue {
|
||||
($res:expr) => {
|
||||
match $res {
|
||||
Ok(val) => val,
|
||||
Err(e) => { continue; }
|
||||
Err(e) => {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -134,7 +153,9 @@ macro_rules! unwrap_opt_continue {
|
||||
($res:expr) => {
|
||||
match $res {
|
||||
Some(val) => val,
|
||||
None => { continue; }
|
||||
None => {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -2,10 +2,10 @@ use anyhow::Result;
|
||||
|
||||
use app::App;
|
||||
|
||||
mod magicdraw_parser;
|
||||
mod app;
|
||||
mod components;
|
||||
mod generate_sql;
|
||||
mod magicdraw_parser;
|
||||
|
||||
// TODO: Make this work with enumation lookup tables
|
||||
// TODO: Dark theme switch button
|
||||
|
Loading…
Reference in New Issue
Block a user