aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/src/question/strict_confirmation_question.rs
blob: e291c568429b8b5713dfc9ea95c901b1b11cfedb (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
//! ref: composer/src/Composer/Question/StrictConfirmationQuestion.php

use anyhow::Result;
use shirabe_external_packages::composer::pcre::Preg;
use shirabe_external_packages::symfony::console::exception::InvalidArgumentException;
use shirabe_external_packages::symfony::console::question::Question;
use shirabe_php_shim::{PhpMixed, empty, is_bool};

pub struct StrictConfirmationQuestion {
    inner: Question,
    true_answer_regex: String,
    false_answer_regex: String,
}

impl StrictConfirmationQuestion {
    pub fn new(
        question: String,
        default: bool,
        true_answer_regex: String,
        false_answer_regex: String,
    ) -> Self {
        let inner = Question::new(&question, Some(PhpMixed::Bool(default)));
        let mut this = Self {
            inner,
            true_answer_regex,
            false_answer_regex,
        };
        let normalizer = this.get_default_normalizer();
        let validator = this.get_default_validator();
        this.inner.set_normalizer(normalizer);
        this.inner.set_validator(Some(validator));
        this
    }

    fn get_default_normalizer(&self) -> Box<dyn Fn(&PhpMixed) -> PhpMixed> {
        let default = self.inner.get_default();
        let true_regex = self.true_answer_regex.clone();
        let false_regex = self.false_answer_regex.clone();

        Box::new(move |answer: &PhpMixed| {
            if is_bool(answer) {
                return answer.clone();
            }
            if empty(answer) && default.as_ref().is_some_and(|d| !empty(d)) {
                return default.clone().unwrap_or(PhpMixed::Null);
            }
            if let PhpMixed::String(s) = answer {
                if Preg::is_match(&true_regex, s).unwrap_or(false) {
                    return PhpMixed::Bool(true);
                }
                if Preg::is_match(&false_regex, s).unwrap_or(false) {
                    return PhpMixed::Bool(false);
                }
            }
            PhpMixed::Null
        })
    }

    fn get_default_validator(&self) -> Box<dyn Fn(Option<PhpMixed>) -> Result<PhpMixed>> {
        Box::new(|answer: Option<PhpMixed>| {
            let answer = answer.unwrap_or(PhpMixed::Null);
            if !is_bool(&answer) {
                return Err(InvalidArgumentException {
                    message: "Please answer yes, y, no, or n.".to_string(),
                    code: 0,
                }
                .into());
            }
            Ok(answer)
        })
    }
}