aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/src/console/input/input_option.rs
blob: 069c3f47f60d8578eea7089f84d469a2d0ea2d2f (plain)
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
//! ref: composer/src/Composer/Console/Input/InputOption.php

use anyhow::Result;
use shirabe_external_packages::symfony::console::completion::completion_input::CompletionInput;
use shirabe_external_packages::symfony::console::completion::completion_suggestions::CompletionSuggestions;
use shirabe_external_packages::symfony::console::completion::suggestion::Suggestion;
use shirabe_external_packages::symfony::console::input::input_option::InputOption as BaseInputOption;
use shirabe_php_shim::LogicException;
use shirabe_php_shim::PhpMixed;

pub enum SuggestedValues {
    List(Vec<String>),
    Closure(Box<dyn Fn(&CompletionInput, &mut CompletionSuggestions) -> Vec<StringOrSuggestion>>),
}

impl std::fmt::Debug for SuggestedValues {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            SuggestedValues::List(list) => write!(f, "SuggestedValues::List({:?})", list),
            SuggestedValues::Closure(_) => write!(f, "SuggestedValues::Closure(<closure>)"),
        }
    }
}

pub enum StringOrSuggestion {
    Str(String),
    Suggestion(Suggestion),
}

#[derive(Debug)]
pub struct InputOption {
    inner: BaseInputOption,
    suggested_values: SuggestedValues,
}

impl InputOption {
    pub fn new(
        name: &str,
        shortcut: Option<PhpMixed>,
        mode: Option<i64>,
        description: &str,
        default: Option<PhpMixed>,
        suggested_values: SuggestedValues,
    ) -> Result<Self> {
        let inner = BaseInputOption::new(name, shortcut, mode, description, default)?;
        let this = Self {
            inner,
            suggested_values,
        };

        if let SuggestedValues::List(ref list) = this.suggested_values {
            if !list.is_empty() && !this.inner.accept_value() {
                return Err(LogicException {
                    message: "Cannot set suggested values if the option does not accept a value.".to_string(),
                    code: 0,
                }.into());
            }
        } else if let SuggestedValues::Closure(_) = this.suggested_values {
            if !this.inner.accept_value() {
                return Err(LogicException {
                    message: "Cannot set suggested values if the option does not accept a value.".to_string(),
                    code: 0,
                }.into());
            }
        }

        Ok(this)
    }

    pub fn complete(
        &self,
        input: &CompletionInput,
        suggestions: &mut CompletionSuggestions,
    ) -> Result<()> {
        let values: Vec<StringOrSuggestion> = match &self.suggested_values {
            SuggestedValues::List(list) => list
                .iter()
                .map(|s| StringOrSuggestion::Str(s.clone()))
                .collect(),
            SuggestedValues::Closure(closure) => closure(input, suggestions),
        };
        if !values.is_empty() {
            suggestions.suggest_values(values);
        }
        Ok(())
    }
}