nestrs-cli-rs 0.1.0

Rust port of the Nest CLI for the nestrs organization.
Documentation
//! Upstream source: `../nest-cli/lib/utils/remaining-flags.ts`.

use std::collections::BTreeMap;

#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct CommanderOption {
    pub short: Option<String>,
    pub long: String,
}

pub fn get_remaining_flags(
    raw_args: &[String],
    consumed_options: &[CommanderOption],
    consumed_values: &BTreeMap<String, String>,
) -> Vec<String> {
    let start = raw_args
        .iter()
        .position(|item| item.starts_with("--"))
        .unwrap_or(0);

    raw_args[start..]
        .iter()
        .enumerate()
        .filter_map(|(index, item)| {
            if consumed_options
                .iter()
                .any(|option| option.short.as_deref() == Some(item) || option.long == *item)
            {
                return None;
            }

            if let Some(previous) = index
                .checked_sub(1)
                .and_then(|previous| raw_args[start..].get(previous))
            {
                let previous_key =
                    camel_case_flag(&previous.replace("--", "").replacen("no", "", 1));
                if consumed_values
                    .get(&previous_key)
                    .is_some_and(|value| value == item)
                {
                    return None;
                }
            }

            Some(item.clone())
        })
        .collect()
}

pub fn get_remaining_flags_simple(raw_args: &[String], consumed_options: &[&str]) -> Vec<String> {
    let options = consumed_options
        .iter()
        .map(|option| CommanderOption {
            short: None,
            long: (*option).to_string(),
        })
        .collect::<Vec<_>>();
    get_remaining_flags(raw_args, &options, &BTreeMap::new())
}

pub fn camel_case_flag(flag: &str) -> String {
    let mut words = flag.split('-');
    let Some(first) = words.next() else {
        return String::new();
    };

    words.fold(first.to_string(), |mut output, word| {
        let mut chars = word.chars();
        if let Some(first_char) = chars.next() {
            output.push(first_char.to_ascii_uppercase());
            output.push_str(chars.as_str());
        }
        output
    })
}