use super::{
ebi_file_handler::EbiFileHandler,
ebi_input::{self, EbiInput, EbiInputType},
ebi_output::{EbiExporter, EbiOutput, EbiOutputType},
};
use crate::{
ebi_commands::{
ebi_command_analyse, ebi_command_analyse_non_stochastic, ebi_command_association,
ebi_command_conformance, ebi_command_conformance_non_stochastic, ebi_command_convert,
ebi_command_discover, ebi_command_discover_non_stochastic, ebi_command_filter,
ebi_command_info,
ebi_command_itself::{self},
ebi_command_probability, ebi_command_reduce, ebi_command_sample, ebi_command_test,
ebi_command_validate, ebi_command_visualise,
},
ebi_framework::{ebi_importer_parameters, ebi_output},
prom::java_object_handler::get_possible_inputs_with_java,
text::Joiner,
};
use clap::{Arg, ArgAction, ArgMatches, Command, value_parser};
use ebi_objects::{
EbiObjectType,
anyhow::{Context, Result, anyhow},
ebi_arithmetic::{Fraction, exact::set_exact_globally, parsing::FractionNotParsedYet},
};
use indicatif::{ProgressBar, ProgressStyle};
use itertools::Itertools;
use log::Level;
use logging_timer::timer;
use std::{
collections::BTreeSet,
fmt::{Debug, Display},
hash::Hash,
io::Write,
path::PathBuf,
time::Duration,
};
pub const EBI_COMMANDS: EbiCommand = EbiCommand::Group {
name_short: "Ebi",
name_long: None,
explanation_short: "Ebi: a tool for stochastic process mining.",
explanation_long: None,
children: &[
&ebi_command_analyse::EBI_ANALYSE,
&ebi_command_analyse_non_stochastic::EBI_ANALYSE_NON_STOCHASTIC,
&ebi_command_association::EBI_ASSOCIATION,
&ebi_command_conformance::EBI_CONFORMANCE,
&ebi_command_conformance_non_stochastic::EBI_CONFORMANCE_NON_STOCHASTIC,
&ebi_command_convert::EBI_CONVERT,
&ebi_command_discover::EBI_DISCOVER,
&ebi_command_discover_non_stochastic::EBI_DISCOVER_NON_STOCHASTIC,
&ebi_command_filter::EBI_FILTER,
&ebi_command_itself::EBI_ITSELF,
&ebi_command_info::EBI_INFO,
&ebi_command_probability::EBI_PROBABILITY,
&ebi_command_reduce::EBI_REDUCE,
&ebi_command_sample::EBI_SAMPLE,
&ebi_command_test::EBI_TEST,
&ebi_command_validate::EBI_VALIDATE,
&ebi_command_visualise::EBI_VISUALISE,
],
};
pub const ARG_SHORT_OUTPUT: char = 'o';
pub const ARG_SHORT_OUTPUT_TYPE: char = 't';
pub const ARG_SHORT_APPROX: char = 'a';
pub const ARG_ID_OUTPUT: &str = "output";
pub const ARG_ID_OUTPUT_TYPE: &str = "output_type";
pub enum EbiCommand {
Group {
name_short: &'static str,
name_long: Option<&'static str>,
explanation_short: &'static str,
explanation_long: Option<&'static str>,
children: &'static [&'static EbiCommand],
},
Command {
name_short: &'static str,
name_long: Option<&'static str>,
explanation_short: &'static str,
explanation_long: Option<&'static str>,
latex_link: Option<&'static str>,
cli_command: Option<fn(command: Command) -> Command>,
exact_arithmetic: bool,
input_types: &'static [&'static [&'static EbiInputType]],
input_names: &'static [&'static str],
input_helps: &'static [&'static str],
execute: fn(inputs: Vec<EbiInput>, cli_matches: Option<&ArgMatches>) -> Result<EbiOutput>, output_type: &'static EbiOutputType,
},
}
impl EbiCommand {
pub fn build_cli(&self) -> Command {
let mut command;
match self {
EbiCommand::Group {
name_short,
name_long,
explanation_short,
explanation_long,
children,
} => {
let name = if let Some(x) = name_long {
x
} else {
name_short
};
command = Command::new(name)
.about(explanation_short)
.subcommand_required(true)
.allow_external_subcommands(false);
if name_long.is_some() {
command = command.alias(name_short);
}
if let Some(l) = explanation_long {
command = command.long_about(l);
}
for child in children.iter() {
let subcommand = child.build_cli();
command = command.subcommand(subcommand);
}
}
EbiCommand::Command {
name_short,
name_long,
explanation_short,
explanation_long,
cli_command,
exact_arithmetic,
input_types,
input_helps: input_help,
input_names,
output_type,
..
} => {
let name = if let Some(x) = name_long {
x
} else {
name_short
};
command = Command::new(name).about(explanation_short);
if name_long.is_some() {
command = command.alias(name_short);
}
if let Some(l) = explanation_long {
command = command.long_about(l);
}
for (i, (input_name, (input_type, input_help))) in input_names
.iter()
.zip(input_types.iter().zip(input_help.iter()))
.enumerate()
{
let mut arg = Arg::new(format!("{}x{}", input_name, i))
.action(ArgAction::Set)
.value_name(input_name)
.help(input_help)
.value_parser(EbiInputType::get_parser_of_list(input_type))
.long_help(EbiInputType::possible_inputs_as_strings_with_articles(
input_type, " and ",
));
if let Some(default) = ebi_input::default(input_type) {
arg = arg.required(false).default_value(default);
} else {
arg = arg.required(true);
}
command = command.arg(arg);
}
if let Some(f) = cli_command {
command = (f)(command);
}
if output_type.get_exporters().len() > 1 {
let output_extensions = output_type
.get_exporters()
.iter()
.map(|exporter| exporter.get_extension())
.collect::<Vec<_>>()
.join_with(", ", " and ");
command = command.arg(
Arg::new(ARG_ID_OUTPUT_TYPE)
.short(ARG_SHORT_OUTPUT_TYPE)
.long(ARG_ID_OUTPUT_TYPE)
.action(ArgAction::Set)
.value_name("OUTPUT_TYPE")
.help("Specify the output type.")
.long_help(format!("Specify the output file extension (without period). The default is {}. Possible values are {}.", output_type.get_default_exporter().get_extension(), output_extensions))
.value_parser(value_parser!(String)),
);
};
command = command.arg(
{ let mut arg = Arg::new(ARG_ID_OUTPUT)
.short(ARG_SHORT_OUTPUT)
.long(ARG_ID_OUTPUT)
.action(ArgAction::Set)
.value_name("FILE")
.help("Saves the result to a file.")
.required(false)
.value_parser(value_parser!(PathBuf));
if output_type.get_exporters().len() > 1 {
arg = arg.long_help(format!("Saves the results to a file. The file type is determined by its extension, unless the -{} parameter is also given.", ARG_SHORT_OUTPUT_TYPE))
}
arg
}
);
if *exact_arithmetic {
command = command.arg(
Arg::new("approx")
.short(ARG_SHORT_APPROX)
.long("approximate")
.action(ArgAction::SetTrue)
.num_args(0)
.help("Use approximate arithmetic instead of exact arithmetic.")
.required(false)
.value_parser(value_parser!(bool)),
)
}
for (input_index, inputs) in input_types.iter().enumerate() {
let merged_importer_parameters =
ebi_importer_parameters::merge_importer_parameters(inputs);
command = ebi_importer_parameters::build_cli(
command,
merged_importer_parameters,
input_index,
);
}
}
};
return command;
}
pub fn short_name(&self) -> &str {
match self {
EbiCommand::Group { name_short, .. } => name_short,
EbiCommand::Command { name_short, .. } => name_short,
}
}
pub fn long_name(&self) -> &str {
match self {
EbiCommand::Group {
name_short,
name_long,
..
} => match name_long {
Some(x) => x,
None => &name_short,
},
EbiCommand::Command {
name_short,
name_long,
..
} => match name_long {
Some(x) => x,
None => &name_short,
},
}
}
pub fn explanation_short(&self) -> &str {
match self {
EbiCommand::Group {
explanation_short, ..
} => &explanation_short,
EbiCommand::Command {
explanation_short, ..
} => &explanation_short,
}
}
pub fn explanation_long(&self) -> &str {
match self {
EbiCommand::Group {
explanation_short,
explanation_long,
..
} => match explanation_long {
Some(x) => x,
None => &explanation_short,
},
EbiCommand::Command {
explanation_short,
explanation_long,
..
} => match explanation_long {
Some(x) => x,
None => &explanation_short,
},
}
}
pub fn get_progress_bar_ticks(total_ticks: usize) -> ProgressBar {
let pb = ProgressBar::new(total_ticks.try_into().unwrap());
pb.set_style(
ProgressStyle::with_template(&("[{wide_bar:.cyan/blue}] {pos:>7}/{len:7}".to_owned()))
.unwrap()
.progress_chars("#>-"),
);
pb.set_position(0);
pb
}
pub fn get_progress_bar_message(message: String) -> ProgressBar {
let pb = ProgressBar::new_spinner();
pb.set_style(ProgressStyle::with_template(&("{spinner} {wide_msg}")).unwrap());
pb.enable_steady_tick(Duration::from_millis(100));
pb.set_message(message);
pb.tick();
pb
}
pub fn execute(&self, cli_matches: &ArgMatches) -> Result<()> {
match self {
EbiCommand::Group { children, .. } => {
for child in children.iter() {
if let Some(sub_matches) = cli_matches.subcommand_matches(child.long_name()) {
return child.execute(sub_matches);
}
}
}
EbiCommand::Command {
exact_arithmetic,
input_types: input_typess,
execute,
output_type,
input_names,
..
} => {
if !exact_arithmetic || cli_matches.get_flag("approx") {
log::info!("Use approximate arithmetic");
set_exact_globally(false);
}
let mut inputs = vec![];
for (i, (input_types, input_name)) in
input_typess.iter().zip(input_names.iter()).enumerate()
{
let cli_id = format!("{}x{}", input_name, i);
log::info!("Reading {}", input_name);
let input = Self::attempt_parse(input_types, cli_matches, &cli_id, i)
.with_context(|| format!("Reading parameter {}.", input_name))?;
log::debug!(
"Read {input_name} as {} ({})",
input.get_type(),
input
.importing_file_handler()
.map(|x| format!("file handler: {}", x.name))
.unwrap_or_else(|| "no file handler".to_string())
);
inputs.push(input);
}
log::info!("Starting {} command", self.long_name());
let result = {
let _tmr = timer!(Level::Info; self.long_name());
(execute)(inputs, Some(cli_matches))?
};
if &&result.get_type() != output_type {
return Err(anyhow!(
"Output type {} does not match the declared output of {}.",
result.get_type(),
output_type
));
}
if let Some(to_file) = cli_matches.get_one::<PathBuf>(ARG_ID_OUTPUT) {
let exporter =
Self::select_exporter(output_type, Some(to_file), Some(cli_matches))?;
log::info!(
"Writing result to {:?} as {} {}",
to_file,
exporter.get_article(),
exporter
);
ebi_output::export_object(to_file, result, exporter)?;
} else {
let exporter = Self::select_exporter(output_type, None, Some(cli_matches))?;
log::info!("Writing result as {} {}", exporter.get_article(), exporter);
if exporter.is_binary() {
let mut out = std::io::stdout();
out.write_all(&ebi_output::export_to_bytes(result, &exporter)?)?;
out.flush()?;
} else {
println!("{}", ebi_output::export_to_string(result, &exporter)?);
}
}
return Ok(());
}
}
Err(anyhow!("command not recognised"))
}
pub fn select_exporter(
output_type: &EbiOutputType,
to_file: Option<&PathBuf>,
cli_matches: Option<&ArgMatches>,
) -> Result<EbiExporter> {
let exporters = output_type.get_exporters();
if let Some(cli_matches) = cli_matches
&& exporters.len() > 1
{
if let Some(extension) = cli_matches.get_one::<String>(ARG_ID_OUTPUT_TYPE) {
for exporter in exporters {
if exporter.get_extension() == extension {
return Ok(exporter);
}
}
return Err(anyhow!(
"the requested output file type {} is not available for this command",
extension
));
}
}
match to_file {
Some(file) => {
{
let mut exporters = exporters.clone();
exporters.sort_by(|a, b| a.get_extension().len().cmp(&b.get_extension().len()));
for exporter in exporters {
if let EbiExporter::Object(_, file_handler) = exporter {
if file
.display()
.to_string()
.ends_with(&(".".to_string() + file_handler.file_extension))
{
return Ok(exporter);
}
}
}
}
return Ok(output_type.get_default_exporter());
}
None => {
return Ok(output_type.get_default_exporter());
}
}
}
pub fn attempt_parse(
input_types: &[&'static EbiInputType],
cli_matches: &ArgMatches,
cli_id: &str,
input_index: usize,
) -> Result<EbiInput> {
let mut error = None;
let mut reader = match ebi_input::get_reader(cli_matches, cli_id).context("Getting reader.")
{
Ok(x) => Some(x),
Err(e) => {
error = Some(e);
None
}
};
for input_type in input_types.iter() {
match input_type {
EbiInputType::Trait(etrait) => {
if let Some(ref mut reader) = reader {
match ebi_input::read_as_trait(
etrait,
reader,
Some(cli_matches),
input_index,
)
.with_context(|| format!("Parsing as the trait `{}`.", etrait))
{
Ok((object, file_handler)) => {
return Ok(EbiInput::Trait(object, file_handler));
}
Err(e) => error = Some(e),
}
}
}
EbiInputType::Object(etype) => {
if let Some(ref mut reader) = reader {
match ebi_input::read_as_object(
etype,
reader,
Some(cli_matches),
input_index,
)
.with_context(|| format!("Parsing as the object type `{}`.", etype))
{
Ok((object, file_handler)) => {
return Ok(EbiInput::Object(object, file_handler));
}
Err(e) => error = Some(e),
}
}
}
EbiInputType::AnyObject => {
if let Some(ref mut reader) = reader {
match ebi_input::read_as_any_object(reader, Some(cli_matches), input_index)
.context("Parsing as any object.")
{
Ok((object, file_handler)) => {
return Ok(EbiInput::Object(object, file_handler));
}
Err(e) => error = Some(e),
}
}
}
EbiInputType::FileHandler => {
if let Some(value) = cli_matches.get_one::<EbiFileHandler>(&cli_id) {
return Ok(EbiInput::FileHandler(value.clone()));
}
}
EbiInputType::String(None, _) => {
if let Some(value) = cli_matches.get_one::<String>(&cli_id) {
return Ok(EbiInput::String(value.clone(), &input_type));
}
}
EbiInputType::String(Some(allowed_values), _) => {
if let Some(value) = cli_matches.get_one::<String>(&cli_id) {
if allowed_values.contains(&value.as_str()) {
return Ok(EbiInput::String(value.clone(), &input_type));
} else {
error = Some(anyhow!("value should be one of {:?}", allowed_values));
}
}
}
EbiInputType::Usize(min, max, _) => {
if let Some(value) = cli_matches.get_one::<usize>(&cli_id) {
match (min, max) {
(Some(min), Some(max)) => {
if value < min || value > max {
error =
Some(anyhow!("Value must be between {} and {}.", min, max))
} else {
return Ok(EbiInput::Usize(value.clone(), input_type));
}
}
(Some(min), None) => {
if value < min {
error = Some(anyhow!("Value must be below {}.", min))
} else {
return Ok(EbiInput::Usize(value.clone(), input_type));
}
}
(None, Some(max)) => {
if value > max {
error = Some(anyhow!("Value must be above {}.", max))
} else {
return Ok(EbiInput::Usize(value.clone(), input_type));
}
}
(None, None) => {
return Ok(EbiInput::Usize(value.clone(), input_type));
}
}
}
}
EbiInputType::Fraction(min, max, _) => {
if let Some(value) = cli_matches.get_one::<FractionNotParsedYet>(&cli_id) {
let value: Fraction = value.try_into()?;
match (min, max) {
(Some(min), Some(max)) => {
if min > &value || max < &value {
error =
Some(anyhow!("Value must be between {} and {}.", min, max))
} else {
return Ok(EbiInput::Fraction(value, input_type));
}
}
(Some(min), None) => {
if min > &value {
error = Some(anyhow!("Value must be below {}.", min))
} else {
return Ok(EbiInput::Fraction(value, input_type));
}
}
(None, Some(max)) => {
if max > &value {
error = Some(anyhow!("Value must be above {}.", max))
} else {
return Ok(EbiInput::Fraction(value, input_type));
}
}
(None, None) => {
return Ok(EbiInput::Fraction(value, input_type));
}
}
}
}
}
}
match error {
Some(e) => Err(e),
None => Err(anyhow!("argument was not given")),
}
}
pub fn path_to_string(path: &[&EbiCommand]) -> String {
let result: Vec<&str> = path.iter().map(|command| command.long_name()).collect();
result.join(" ")
}
pub fn path_to_short_string(path: &Vec<&EbiCommand>) -> String {
let result: Vec<&str> = path.iter().map(|command| command.short_name()).collect();
result.join(" ")
}
pub fn find_command_with_string(&self, name: &String) -> Option<Vec<&EbiCommand>> {
for path in self.get_command_paths() {
if Self::path_to_string(&path) == *name {
return Some(path);
}
}
None
}
pub fn get_command_paths(&self) -> BTreeSet<Vec<&'static EbiCommand>> {
let mut result = BTreeSet::new();
self.get_paths_recursive(&EBI_COMMANDS, &mut result, vec![]);
result
}
fn get_paths_recursive(
&self,
command: &'static EbiCommand,
result: &mut BTreeSet<Vec<&'static EbiCommand>>,
prefix: Vec<&'static EbiCommand>,
) {
match command {
EbiCommand::Group { children, .. } => {
for child in children.iter() {
let mut prefix = prefix.clone();
prefix.push(command);
self.get_paths_recursive(child, result, prefix);
}
}
EbiCommand::Command { .. } => {
let mut prefix = prefix.clone();
prefix.push(command);
result.insert(prefix);
}
}
}
pub fn execute_with_inputs(&self, inputs: Vec<EbiInput>) -> Result<EbiOutput> {
match self {
EbiCommand::Command {
execute,
output_type,
..
} => {
let result = (execute)(inputs, None)?;
if &&result.get_type() != output_type {
return Err(anyhow!(
"Output type {} does not match the declared output of {}.",
result.get_type(),
output_type
));
}
Ok(result)
}
_ => Err(anyhow!("Not a command variant.")),
}
}
pub fn is_in_java(&self) -> bool {
if let EbiCommand::Command {
cli_command,
output_type,
input_types: input_typess,
..
} = &self
{
if cli_command.is_some() {
return false;
}
for exporter in output_type.get_exporters() {
for output_java_object_handler in exporter.get_java_object_handlers() {
if let Some(_) = output_java_object_handler.translator_ebi_to_java {
let input_typesss = input_typess
.iter()
.map(|arr| get_possible_inputs_with_java(arr))
.collect::<Vec<_>>();
for _ in input_typesss.iter().multi_cartesian_product() {
return true;
}
}
}
}
return false;
} else {
false
}
}
pub fn is_in_python(&self) -> bool {
if let EbiCommand::Command {
cli_command,
input_types,
..
} = &self
{
return cli_command.is_none() && !input_types.is_empty();
} else {
false
}
}
pub fn is_in_javascript(&self) -> bool {
if let EbiCommand::Command {
cli_command,
input_types,
..
} = &self
{
return cli_command.is_none() && !input_types.is_empty();
} else {
false
}
}
}
impl Ord for EbiCommand {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.long_name().cmp(other.long_name())
}
}
impl PartialOrd for EbiCommand {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.long_name().partial_cmp(other.long_name())
}
}
impl Display for EbiCommand {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.long_name())
}
}
impl Debug for EbiCommand {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Group {
name_short,
name_long,
..
} => f
.debug_struct("Group")
.field("name_short", name_short)
.field("name_long", name_long)
.finish(),
Self::Command {
name_short,
name_long,
..
} => f
.debug_struct("Command")
.field("name_short", name_short)
.field("name_long", name_long)
.finish(),
}
}
}
impl Eq for EbiCommand {}
impl PartialEq for EbiCommand {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(
Self::Group {
name_short: l_name_short,
name_long: l_name_long,
explanation_short: l_explanation_short,
explanation_long: l_explanation_long,
children: l_children,
},
Self::Group {
name_short: r_name_short,
name_long: r_name_long,
explanation_short: r_explanation_short,
explanation_long: r_explanation_long,
children: r_children,
},
) => {
l_name_short == r_name_short
&& l_name_long == r_name_long
&& l_explanation_short == r_explanation_short
&& l_explanation_long == r_explanation_long
&& l_children == r_children
}
(
Self::Command {
name_short: l_name_short,
name_long: l_name_long,
explanation_short: l_explanation_short,
explanation_long: l_explanation_long,
latex_link: l_latex_link,
cli_command: _,
exact_arithmetic: l_exact_arithmetic,
input_types: l_input_types,
input_names: l_input_names,
input_helps: l_input_helps,
execute: _,
output_type: l_output,
},
Self::Command {
name_short: r_name_short,
name_long: r_name_long,
explanation_short: r_explanation_short,
explanation_long: r_explanation_long,
latex_link: r_latex_link,
cli_command: _,
exact_arithmetic: r_exact_arithmetic,
input_types: r_input_types,
input_names: r_input_names,
input_helps: r_input_helps,
execute: _,
output_type: r_output,
},
) => {
l_name_short == r_name_short
&& l_name_long == r_name_long
&& l_explanation_short == r_explanation_short
&& l_explanation_long == r_explanation_long
&& l_latex_link == r_latex_link
&& l_exact_arithmetic == r_exact_arithmetic
&& l_input_types == r_input_types
&& l_input_names == r_input_names
&& l_input_helps == r_input_helps
&& l_output == r_output
}
_ => false,
}
}
}
impl Hash for EbiCommand {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.long_name().hash(state)
}
}
pub fn get_applicable_commands(object_type: &EbiObjectType) -> BTreeSet<Vec<&'static EbiCommand>> {
let mut result = EBI_COMMANDS.get_command_paths();
result.retain(|path| {
if let EbiCommand::Command { input_types, .. } = path[path.len() - 1] {
for input_typess in input_types.iter() {
for input_typesss in input_typess.iter() {
if input_typesss == &&EbiInputType::AnyObject
|| input_typesss == &&EbiInputType::Object(object_type.clone())
{
return true;
}
}
}
}
false
});
result
}
#[cfg(any(feature = "python", feature = "test_generation"))]
pub(crate) fn search_command_in_source_files(path: &Vec<&EbiCommand>) -> Result<String> {
let mut path = path.clone();
path.remove(0);
let (mut last_file, mut command_declaration) = root_command()?;
for child_command in path {
(last_file, command_declaration) = search_child(&command_declaration, child_command)?;
}
let mut file_name = last_file.file_name().unwrap().to_str().unwrap().to_owned();
file_name.pop();
file_name.pop();
file_name.pop();
Ok(format!(
"crate::ebi_commands::{}::{}",
file_name, command_declaration.ident
))
}
#[cfg(any(feature = "python", feature = "test_generation"))]
fn root_command() -> Result<(PathBuf, syn::ItemConst)> {
let file = "src/ebi_framework/ebi_command.rs";
let contents = std::fs::read_to_string(file)?;
let syn_file = syn::parse_file(&contents)?;
for item in &syn_file.items {
if let syn::Item::Const(const_item) = item {
if let syn::Type::Path(x) = &*const_item.ty {
if let Some(j) = x.path.get_ident() {
if j.to_string() == "EbiCommand"
&& const_item.ident.to_string() == "EBI_COMMANDS"
{
return Ok((PathBuf::from(file), const_item.clone()));
}
}
}
}
}
return Err(anyhow!("source file not found"));
}
#[cfg(any(feature = "python", feature = "test_generation"))]
fn search_child(
parent_declaration: &syn::ItemConst,
child_command: &EbiCommand,
) -> Result<(PathBuf, syn::ItemConst)> {
let children_const_names = extract_children_names(parent_declaration)?;
let child_short_name = child_command.short_name();
let paths = std::fs::read_dir("src/ebi_commands")?;
for entry in paths {
let entry = entry?;
let meta = entry.metadata()?;
if meta.is_file() {
let contents = std::fs::read_to_string(entry.path())?;
let file = syn::parse_file(&contents)?;
for item in &file.items {
if let syn::Item::Const(const_item) = item {
if children_const_names.contains(&const_item.ident.to_string()) {
if let syn::Type::Path(x) = &*const_item.ty {
if let Some(j) = x.path.get_ident() {
if j.to_string() == "EbiCommand" {
if let syn::Expr::Struct(x) = &*const_item.expr {
for field in &x.fields {
if let syn::Member::Named(field_name) = &field.member {
if field_name.to_string() == "name_short" {
if let syn::Expr::Lit(lit) = &field.expr {
if let syn::Lit::Str(str) = &lit.lit {
if str.value() == child_short_name {
return Ok((
entry.path(),
const_item.clone(),
));
}
}
}
}
} else {
return Err(anyhow!("unexpected field"));
}
}
} else {
return Err(anyhow!("unexpected expr"));
}
}
}
}
}
}
}
}
}
return Err(anyhow!("todo file not found {}", child_command));
}
#[cfg(any(feature = "python", feature = "test_generation"))]
fn extract_children_names(const_item: &syn::ItemConst) -> Result<Vec<String>> {
if let syn::Expr::Struct(x) = &*const_item.expr {
for field in &x.fields {
if let syn::Member::Named(field_name) = &field.member {
if field_name.to_string() == "children" {
if let syn::Expr::Reference(refe) = &field.expr {
if let syn::Expr::Array(arr) = &*refe.expr {
let mut result = vec![];
for child in &arr.elems {
if let syn::Expr::Reference(child_ref) = child {
if let syn::Expr::Path(command_name) = &*child_ref.expr {
result.push(
command_name
.path
.segments
.last()
.unwrap()
.ident
.to_string(),
);
} else {
return Err(anyhow!("unexpected child found"));
}
} else {
return Err(anyhow!("unexpected child found"));
}
}
return Ok(result);
} else {
return Err(anyhow!("unexpected child found"));
}
} else {
return Err(anyhow!("unexpected child found"));
}
}
}
}
}
Err(anyhow!("cannot extract children"))
}
#[cfg(test)]
pub(crate) mod tests {
use super::{EBI_COMMANDS, EbiCommand};
use std::collections::HashSet;
#[test]
fn build_cli() {
EBI_COMMANDS.build_cli();
}
#[test]
fn basic_calls() {
for command in EBI_COMMANDS.get_command_paths() {
command.first().unwrap().short_name();
command.first().unwrap().explanation_long();
command.last().unwrap().short_name();
command.last().unwrap().explanation_long();
EbiCommand::path_to_short_string(&command);
command.last().unwrap().is_in_java();
command.first().unwrap().is_in_java();
command.last().unwrap().to_string();
let _ = format!("{:?}", command.first().unwrap());
let _ = format!("{:?}", command.last().unwrap());
let _ = command
.first()
.unwrap()
.partial_cmp(&command.last().unwrap());
let _ = command.first().unwrap().eq(command.last().unwrap());
let _ = command.first().unwrap().eq(command.first().unwrap());
let _ = command.last().unwrap().eq(command.last().unwrap());
let mut hash = HashSet::new();
hash.insert(command.last().unwrap());
}
}
}