aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/src/platform/runtime.rs
blob: aa2f4d74e65dba77363c3a8c6bb146c1afc7104a (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
//! ref: composer/src/Composer/Platform/Runtime.php

use anyhow::Result;
use indexmap::IndexMap;
use shirabe_external_packages::composer::pcre::{CaptureKey, Preg};
use shirabe_php_shim::{
    PhpMixed, class_exists, constant, defined, function_exists, get_loaded_extensions,
    html_entity_decode, implode, instantiate_class, ltrim, phpversion, strip_tags, trim,
};

#[derive(Debug)]
pub struct Runtime;

impl Runtime {
    pub fn has_constant(&self, constant_name: &str, class: Option<&str>) -> bool {
        defined(&ltrim(
            &format!("{}::{}", class.unwrap_or(""), constant_name),
            Some(":"),
        ))
    }

    pub fn get_constant(&self, constant_name: &str, class: Option<&str>) -> PhpMixed {
        constant(&ltrim(
            &format!("{}::{}", class.unwrap_or(""), constant_name),
            Some(":"),
        ))
    }

    pub fn has_function(&self, f: &str) -> bool {
        function_exists(f)
    }

    pub fn invoke(
        &self,
        callable: Box<dyn Fn(Vec<PhpMixed>) -> PhpMixed>,
        arguments: Vec<PhpMixed>,
    ) -> PhpMixed {
        callable(arguments)
    }

    pub fn has_class(&self, class: &str) -> bool {
        class_exists(class)
    }

    pub fn construct(&self, class: &str, arguments: Vec<PhpMixed>) -> Result<PhpMixed> {
        if arguments.is_empty() {
            Ok(instantiate_class(class, vec![]))
        } else {
            Ok(instantiate_class(class, arguments))
        }
    }

    pub fn get_extensions(&self) -> Vec<String> {
        get_loaded_extensions()
    }

    pub fn get_extension_version(&self, extension: &str) -> String {
        let version = phpversion(extension);
        version.unwrap_or_else(|| "0".to_string())
    }

    pub fn get_extension_info(&self, extension: &str) -> Result<String> {
        // Depends on \ReflectionExtension::info() and output buffering; no shim equivalent exists.
        let _ = extension;
        todo!()
    }

    pub fn parse_html_extension_info(html: &str) -> String {
        let mut result: Vec<String> = vec![];

        let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
        if Preg::match3(
            r"~<h2>\s*<a[^>]*>([^<]+)</a>\s*</h2>~i",
            html,
            Some(&mut matches),
        ) {
            result.push(trim(
                &html_entity_decode(
                    matches
                        .get(&CaptureKey::ByIndex(1))
                        .map(|s| s.as_str())
                        .unwrap_or(""),
                ),
                None,
            ));
            result.push(String::new());
        }

        let mut matches: IndexMap<CaptureKey, Vec<String>> = IndexMap::new();
        if Preg::match_all3(
            r#"~<tr>\s*<td class="e">\s*(.*?)\s*</td>\s*<td class="v">\s*(.*?)\s*</td>\s*</tr>~is"#,
            html,
            Some(&mut matches),
        ) > 0
        {
            let group1 = matches
                .get(&CaptureKey::ByIndex(1))
                .cloned()
                .unwrap_or_default();
            let group2 = matches
                .get(&CaptureKey::ByIndex(2))
                .cloned()
                .unwrap_or_default();
            let count = std::cmp::min(group1.len(), group2.len());

            for i in 0..count {
                let key = trim(&html_entity_decode(&strip_tags(&group1[i])), None);
                let value = trim(&html_entity_decode(&strip_tags(&group2[i])), None);
                result.push(format!("{} => {}", key, value));
            }
        }

        implode("\n", &result)
    }
}