1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
use crate::config::Config;
use crate::instruction_presets::get_instruction_preset_library;
use crate::llm_providers::LLMProviderType;
use anyhow::Result;
use clap::Args;
use std::str::FromStr;

#[derive(Clone, Copy, PartialEq, Debug)]
pub enum DetailLevel {
    Minimal,
    Standard,
    Detailed,
}

impl FromStr for DetailLevel {
    type Err = anyhow::Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s.to_lowercase().as_str() {
            "minimal" => Ok(DetailLevel::Minimal),
            "standard" => Ok(DetailLevel::Standard),
            "detailed" => Ok(DetailLevel::Detailed),
            _ => Err(anyhow::anyhow!("Invalid detail level: {}", s)),
        }
    }
}

impl DetailLevel {
    pub fn as_str(&self) -> &'static str {
        match self {
            DetailLevel::Minimal => "minimal",
            DetailLevel::Standard => "standard",
            DetailLevel::Detailed => "detailed",
        }
    }
}

#[derive(Args, Clone, Default, Debug)]
pub struct CommonParams {
    /// Override default LLM provider
    #[arg(long, help = "Override default LLM provider", value_parser = available_providers_parser)]
    pub provider: Option<String>,

    /// Custom instructions for this operation
    #[arg(short, long, help = "Custom instructions for this operation")]
    pub instructions: Option<String>,

    /// Select an instruction preset
    #[arg(long, help = "Select an instruction preset")]
    pub preset: Option<String>,

    /// Enable or disable Gitmoji
    #[arg(long, help = "Enable or disable Gitmoji")]
    pub gitmoji: Option<bool>,

    /// Set the detail level
    #[arg(
        long,
        help = "Set the detail level (minimal, standard, detailed)",
        default_value = "standard"
    )]
    pub detail_level: String,
}

impl CommonParams {
    pub fn apply_to_config(&self, config: &mut Config) -> Result<()> {
        if let Some(provider) = &self.provider {
            config.default_provider = LLMProviderType::from_str(provider)?.to_string();
        }
        if let Some(instructions) = &self.instructions {
            config.set_temp_instructions(Some(instructions.clone()));
        }
        if let Some(preset) = &self.preset {
            config.set_temp_preset(Some(preset.clone()));
        }
        if let Some(use_gitmoji) = self.gitmoji {
            config.use_gitmoji = use_gitmoji;
        }
        Ok(())
    }
}

/// Validate provider input against available providers
pub fn available_providers_parser(s: &str) -> Result<String, String> {
    let available_providers = crate::llm::get_available_provider_names();
    if available_providers.contains(&s.to_lowercase()) && s.to_lowercase() != "test" {
        Ok(s.to_lowercase())
    } else {
        Err(format!(
            "Invalid provider. Available providers are: {}",
            available_providers.join(", ")
        ))
    }
}

pub fn get_combined_instructions(config: &Config) -> String {
    let mut prompt = String::from("\n\n");

    let preset_library = get_instruction_preset_library();
    if let Some(preset_instructions) = preset_library.get_preset(config.instruction_preset.as_str())
    {
        prompt.push_str(&format!(
            "\n\nUse this style for the commit message:\n{}\n\n",
            preset_instructions.instructions
        ));
    }

    if !config.instructions.is_empty() {
        prompt.push_str(&format!(
            "\n\nAdditional instructions for the commit message:\n{}\n\n",
            config.instructions
        ));
    }

    prompt
}