aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/src/io/buffer_io.rs
blob: 79fa9c38563e6ca78a2c0845c9f2069486b3afa3 (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
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
117
118
119
120
121
122
123
124
125
//! ref: composer/src/Composer/IO/BufferIO.php

use crate::io::console_io::ConsoleIO;
use anyhow::Result;
use shirabe_external_packages::composer::pcre::preg::Preg;
use shirabe_external_packages::symfony::console::formatter::output_formatter_interface::OutputFormatterInterface;
use shirabe_external_packages::symfony::console::helper::helper_set::HelperSet;
use shirabe_external_packages::symfony::console::helper::question_helper::QuestionHelper;
use shirabe_external_packages::symfony::console::input::streamable_input_interface::StreamableInputInterface;
use shirabe_external_packages::symfony::console::input::string_input::StringInput;
use shirabe_external_packages::symfony::console::output::stream_output::StreamOutput;
use shirabe_php_shim::{
    PHP_EOL, PhpMixed, RuntimeException, fopen, fseek, fwrite, rewind, stream_get_contents,
    strip_tags,
};

#[derive(Debug)]
pub struct BufferIO {
    inner: ConsoleIO,
}

impl BufferIO {
    pub fn new(
        input: String,
        verbosity: i64,
        formatter: Option<Box<dyn OutputFormatterInterface>>,
    ) -> Result<Self> {
        let mut input_obj = StringInput::new(input);
        input_obj.set_interactive(false);

        let stream = fopen("php://memory", "rw");
        if matches!(stream, PhpMixed::Bool(false)) {
            return Err(RuntimeException {
                message: "Unable to open memory output stream".to_string(),
                code: 0,
            }
            .into());
        }

        let decorated = formatter.as_ref().map_or(false, |f| f.is_decorated());
        let output = StreamOutput::new(stream, verbosity, decorated, formatter);

        let inner = ConsoleIO::new(
            input_obj,
            output,
            HelperSet::new(vec![Box::new(QuestionHelper::new())]),
        );

        Ok(Self { inner })
    }

    pub fn get_output(&self) -> String {
        fseek(self.inner.output.get_stream(), 0);

        let output = stream_get_contents(self.inner.output.get_stream()).unwrap_or_default();

        let output = Preg::replace_callback(
            r"{(?<=^|\n|\x08)(.+?)(\x08+)}",
            |matches: &indexmap::IndexMap<
                shirabe_external_packages::composer::pcre::preg::CaptureKey,
                String,
            >|
             -> String {
                let empty = String::new();
                let g1 = matches
                    .get(&shirabe_external_packages::composer::pcre::preg::CaptureKey::ByIndex(1))
                    .unwrap_or(&empty);
                let g2 = matches
                    .get(&shirabe_external_packages::composer::pcre::preg::CaptureKey::ByIndex(2))
                    .unwrap_or(&empty);
                let pre = strip_tags(g1);

                if pre.len() == g2.len() {
                    return String::new();
                }

                // TODO reverse parse the string, skipping span tags and \033\[([0-9;]+)m(.*?)\033\[0m style blobs
                format!("{}\n", g1.trim_end())
            },
            &output,
        );

        output
    }

    pub fn set_user_inputs(&mut self, inputs: Vec<String>) -> Result<()> {
        if self
            .inner
            .input
            .as_any()
            .downcast_ref::<dyn StreamableInputInterface>()
            .is_none()
        {
            return Err(RuntimeException {
                message: "Setting the user inputs requires at least the version 3.2 of the symfony/console component.".to_string(),
                code: 0,
            }
            .into());
        }

        self.inner.input.set_stream(self.create_stream(inputs)?);
        self.inner.input.set_interactive(true);

        Ok(())
    }

    fn create_stream(&self, inputs: Vec<String>) -> Result<PhpMixed> {
        let stream = fopen("php://memory", "r+");
        if matches!(stream, PhpMixed::Bool(false)) {
            return Err(RuntimeException {
                message: "Unable to open memory output stream".to_string(),
                code: 0,
            }
            .into());
        }

        for input in inputs {
            fwrite(stream.clone(), &format!("{}{}", input, PHP_EOL), -1);
        }

        rewind(stream.clone());

        Ok(stream)
    }
}