tusks-lib 2.1.1

Easily build a CLI based on a Rust Module/Function Structure
Documentation
use syn::{Item, ItemFn, ItemMod, parse_quote};
use syn::{Attribute, Meta};

use crate::{AttributeValue, attribute::models::TasksConfig};

pub fn add_use_staements(module: &mut ItemMod) {
    let use_statement: Item = parse_quote! {
        use ::tusks::clap::{CommandFactory, Parser};
    };
    
    if let Some((_, ref mut items)) = module.content {
        items.insert(0, use_statement);
    }
}

pub fn set_allow_external_subcommands(module: &mut ItemMod) {
    if module.get_attribute_bool("command", "allow_external_subcommands") {
        return;
    }
    
    // Suche nach existierendem #[command(...)]-Attribut
    if let Some(attr) = module.attrs.iter_mut().find(|a| a.path().is_ident("command")) {
        // Vorhandenes command-Attribut gefunden
        match &mut attr.meta {
            Meta::List(list) => {
                // Prüfe, ob tokens mit Komma enden
                let tokens_str = list.tokens.to_string();
                let has_trailing_comma = tokens_str.trim_end().ends_with(',');
                
                let tokens = &list.tokens;
                if tokens.is_empty() {
                    list.tokens = parse_quote! { allow_external_subcommands = true };
                } else if has_trailing_comma {
                    // Komma ist bereits da, kein zusätzliches einfügen
                    list.tokens = parse_quote! { #tokens allow_external_subcommands = true };
                } else {
                    // Komma hinzufügen
                    list.tokens = parse_quote! { #tokens, allow_external_subcommands = true };
                }
            }
            Meta::Path(_) => {
                // #[command] ohne Argumente -> konvertiere zu #[command(allow_external_subcommands = true)]
                *attr = parse_quote! { #[command(allow_external_subcommands = true)] };
            }
            Meta::NameValue(_) => {
                // Unerwarteter Fall, ersetze komplett
                *attr = parse_quote! { #[command(allow_external_subcommands = true)] };
            }
        }
    } else {
        // Kein command-Attribut vorhanden -> füge neues hinzu
        let new_attr: Attribute = parse_quote! { #[command(allow_external_subcommands = true)] };
        module.attrs.push(new_attr);
    }
}

pub fn add_execute_task_function(module: &mut ItemMod, config: &TasksConfig) {
    let cmd_name = "tasks"; // TODO
    let separator = &config.separator;
    let max_groupsize = &config.max_groupsize;
    let max_depth = &config.max_depth;
    let use_colors = &config.use_colors;

    let function: ItemFn = parse_quote! {
        #[command(about = "Execute a task", hide=true)]
        #[default]
        pub fn _execute_task(external_args: Vec<String>) -> Option<u8> {
            let mut transformed_arguments = Vec::new();
            if let Some(first) = external_args.first() {
                transformed_arguments.push(#cmd_name.to_string());
                transformed_arguments.extend(first.split(#separator).map(|s| s.to_string()));
                transformed_arguments.extend_from_slice(&external_args[1..]);
                let cli = __internal_tusks_module::cli::Cli::parse_from(
                    transformed_arguments
                );
                __internal_tusks_module::handle_matches(&cli)
            }
            else {
                let command = __internal_tusks_module::cli::Cli::command();
                if external_args.len() == 0 {
                    let task_list = ::tusks::tasks::task_list::models::TaskList::from_command(
                        &command,
                        #separator.to_string(),
                        #max_groupsize,
                        #max_depth
                    );
                    let mut render_config = ::tusks::tasks::list::models::RenderConfig::default();
                    render_config.use_colors = #use_colors;
                    task_list.to_list().print(&render_config);
                }
                else {
                    let cli = __internal_tusks_module::cli::Cli::parse_from(
                        std::iter::once(#cmd_name).chain(external_args.iter().map(String::as_str))
                    );
                    __internal_tusks_module::handle_matches(&cli);
                }
                Some(0)
            }
        }
    };
    
    if let Some((_, ref mut items)) = module.content {
        items.push(Item::Fn(function));
    }
}

pub fn add_show_help_for_task(module: &mut ItemMod, config: &TasksConfig) {
    let cmd_name = "tasks"; // TODO
    let separator = &config.separator;
    let max_groupsize = &config.max_groupsize;
    let max_depth = &config.max_depth;

    let function: ItemFn = parse_quote! {
        #[command(about = "Show the help for a task", name="h", hide=true)]
        pub fn _show_help_for_task(#[arg()] task: Option<String>) {
            if let Some(task) = task {
                let parts: Vec<&str> = task.split(#separator).collect();

                let args: Vec<&str> = std::iter::once(#cmd_name)
                    .chain(parts.iter().copied())
                    .chain(std::iter::once("--help"))
                    .collect();

                let cli = __internal_tusks_module::cli::Cli::parse_from(args);
                __internal_tusks_module::handle_matches(&cli);
            }
            else {
                let command = __internal_tusks_module::cli::Cli::command();
                let task_list = ::tusks::tasks::task_list::models::TaskList::from_command(
                    &command,
                    #separator.to_string(),
                    #max_groupsize,
                    #max_depth
                );
                task_list.to_list().print(&::tusks::tasks::list::models::RenderConfig::default());
            }
        }
    };

    if let Some((_, ref mut items)) = module.content {
        items.push(Item::Fn(function));
    }
}