use std::fs;
use std::path::{Path, PathBuf};
use crate::DoteurCliError;
use doteur_core::restriction::Restriction;
#[cfg(feature = "mysql_addons")]
use doteur_core::mysql_tools::{get_schemas_from_mysql_params, get_schemas_from_mysql_url};
#[cfg(feature = "mysql_addons")]
use dialoguer::{Input, Password};
#[cfg(feature = "sqlite_addons")]
use doteur_core::sqlite_tools::get_schemas_from_sqlite_instance;
use clap::Parser;
pub const POSSIBLE_DOTS_OUTPUT: [&str; 53] = [
"bmp",
"canon",
"gv",
"xdot",
"xdot1.2",
"xdot1.4",
"cgimage",
"cmap",
"eps",
"eps",
"exr",
"fig",
"gd",
"gd2",
"gif",
"gtk",
"ico",
"imap",
"cmapx",
"imap_np",
"cmapx_np",
"ismap",
"jp2",
"jpg",
"jpeg",
"jpe",
"json",
"json0",
"dot_json",
"xdot_json",
"pct",
"pict",
"pdf",
"pic",
"plain",
"plain-ext",
"png",
"pov",
"ps2",
"psd",
"sgi",
"svg",
"svgz",
"tga",
"tif",
"tiff",
"tk",
"vml",
"vmlz",
"vrml",
"wbmp",
"xlib",
"x11",
];
#[derive(Parser)]
#[clap(
author = "LABEYE Loïc <loic.labeye@pm.me>",
version = "0.5.7",
about = "Parse a SQL configuration and convert it into a .dot file, render the output if Graphviz is installed",
after_help = "Some functionnalities might not appear as they depend on which version this tool has been downloaded or built for."
)]
pub struct Args {
#[clap(required = false, index = 1)]
input: Vec<String>,
#[clap(long = "output", short = 'o', default_value = "output.dot")]
output: String,
#[cfg(feature = "mysql_addons")]
#[clap(long = "url", conflicts_with_all = &["it", "sqlite"])]
url: bool,
#[cfg(feature = "mysql_addons")]
#[clap(long = "it", conflicts_with_all = &["url", "sqlite"])]
interactive: bool,
#[cfg(feature = "sqlite_addons")]
#[clap(long = "sqlite", conflicts_with_all = &["url", "interactive"])]
sqlite: bool,
#[clap(short = 'i', long = "include")]
include: Vec<String>,
#[clap(short = 'x', long = "exclude", conflicts_with = "include")]
exclude: Vec<String>,
#[clap(long = "dark_mode")]
dark_mode: bool,
#[clap(long = "legend")]
legend: bool,
}
impl Args {
pub fn get_data(&self) -> Result<String, Box<dyn std::error::Error>> {
cfg_if! {
if #[cfg(feature="mysql_addons")] {
if self.interactive {
let db_url: String = Input::new()
.with_prompt("Database url or ip")
.default("localhost".into())
.interact_text()
.unwrap();
let db_port: u16 = Input::new()
.with_prompt("Database port")
.default(3306)
.interact_text()
.unwrap();
let db_name: String = Input::new()
.with_prompt("Database name")
.interact_text()
.unwrap();
let db_user: String = Input::new()
.with_prompt("Database user")
.interact_text()
.unwrap();
let db_password: String = Password::new()
.with_prompt("Database user's password")
.interact()
.unwrap();
let data : String = get_schemas_from_mysql_params(db_url, db_port, db_name, db_user, db_password)?;
return Ok(data);
}
if self.url {
if self.input.len() != 1 {
return Err(DoteurCliError::bad_input("Please ensure that if the url argument is present that only one url is passed").into());
} else {
let data : String = get_schemas_from_mysql_url(&self.input[0])?;
return Ok(data);
}
}
}
}
cfg_if! {
if #[cfg(feature="sqlite_addons")] {
if self.sqlite {
if self.input.len() != 1 {
return Err(DoteurCliError::bad_input("Please ensure that only one sqlite database path is passed as argument").into(),
);
} else {
let data : String = get_schemas_from_sqlite_instance(&self.input[0])?;
return Ok(data);
}
}
}
}
if !self.input.is_empty() {
let mut data: Vec<String> = vec![];
for path in self.input.iter() {
if Path::new(path).is_dir() {
for subpath in fs::read_dir(path)? {
let file_path: &PathBuf = &subpath.unwrap().path();
if Path::new(file_path).is_file() {
data.push(fs::read_to_string(file_path)?);
}
}
} else {
data.push(fs::read_to_string(path)?);
}
}
Ok(data.join("\n"))
} else {
Err(DoteurCliError::no_input().into())
}
}
pub fn get_restrictions(&self) -> Option<Restriction> {
if !self.include.is_empty() {
Some(Restriction::new_inclusion(self.include.clone()))
} else if !self.exclude.is_empty() {
Some(Restriction::new_exclusion(self.exclude.clone()))
} else {
None
}
}
pub fn get_output_file_ext(&self) -> &str {
std::path::Path::new(self.output.as_str())
.extension()
.unwrap_or_default()
.to_str()
.unwrap_or_default()
}
pub fn get_output_filename(&self) -> &str {
&self.output
}
pub fn can_render_with_graphviz(&self) -> bool {
let extension: &str = self.get_output_file_ext();
POSSIBLE_DOTS_OUTPUT.iter().any(|&i| i == extension)
}
pub fn get_legend(&self) -> bool {
self.legend
}
pub fn get_dark_mode(&self) -> bool {
self.dark_mode
}
}