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
126
127
128
129
130
131
132
133
/// Return an error message wrapped in [`WithReturnCode`], for use within `#[plugin_fn]`.
#[macro_export]
macro_rules! plugin_err {
    (code = $code:expr, $($arg:tt)+) => {
        WithReturnCode::<Error>::new(anyhow!($($arg)+), $code.into())
    };
    ($($arg:tt)+) => {
        WithReturnCode::<Error>::new(anyhow!($($arg)+), 1)
    };
}

/// Calls the `exec_command` host function to execute a command on
/// the host as a synchronous child process.
#[macro_export]
macro_rules! exec_command {
    (input, $input:expr) => {
        unsafe { exec_command(Json($input))?.0 }
    };

    // Raw result
    (raw, $cmd:literal) => {
        exec_command!(raw, $cmd, Vec::<String>::new())
    };
    (raw, $cmd:expr, $args:expr) => {
        exec_command!(raw, ExecCommandInput::pipe($cmd, $args))
    };
    (raw, $input:expr) => {
        unsafe { exec_command(Json($input)) }
    };

    // Pipe
    (pipe, $cmd:literal) => {
        exec_command!(pipe, $cmd, Vec::<String>::new())
    };
    (pipe, $cmd:expr, $args:expr) => {
        exec_command!(input, ExecCommandInput::pipe($cmd, $args))
    };

    // Inherit
    (inherit, $cmd:literal) => {
        exec_command!(inherit, $cmd, Vec::<String>::new())
    };
    (inherit, $cmd:expr, $args:expr) => {
        exec_command!(input, ExecCommandInput::inherit($cmd, $args))
    };

    // Legacy pipe
    ($cmd:literal) => {
        exec_command!(pipe, $cmd)
    };
    ($cmd:expr, [ $($arg:literal),* ]) => {
        exec_command!(pipe, $cmd, [ $($arg),* ])
    };
    ($cmd:expr, $args:expr) => {
        exec_command!(pipe, $cmd, $args)
    };
}

/// Calls the `get_env_var` or `set_env_var` host function to manage
/// environment variables on the host.
///
/// When setting `PATH`, the provided value will append to `PATH`,
/// not overwrite it. Supports both `;` and `:` delimiters.
#[macro_export]
macro_rules! host_env {
    ($name:expr, $value:expr) => {
        unsafe { set_env_var($name.try_into()?, $value.try_into()?)? };
    };
    ($name:expr) => {
        unsafe {
            let inner = get_env_var($name.try_into()?)?;

            if inner.is_empty() {
                None
            } else {
                Some(inner)
            }
        };
    };
}

/// Calls the `host_log` host function to log a message to the host's terminal.
#[macro_export]
macro_rules! host_log {
    (input, $input:expr) => {
        unsafe {
            host_log(Json($input))?;
        };
    };
    (stdout, $($arg:tt)+) => {
        host_log!(input, HostLogInput {
            message: format!($($arg)+),
            target: HostLogTarget::Stdout,
            ..HostLogInput::default()
        })
    };
    (stderr, $($arg:tt)+) => {
        host_log!(input, HostLogInput {
            message: format!($($arg)+),
            target: HostLogTarget::Stderr,
            ..HostLogInput::default()
        })
    };
    ($($arg:tt)+) => {
        host_log!(input, format!($($arg)+).into())
    };
}

/// Calls `from_virtual_path` on the host to convert the provided value to a real path
/// from a virtual path.
#[macro_export]
macro_rules! real_path {
    (buf, $path:expr) => {
        real_path!($path.to_string_lossy())
    };
    ($path:expr) => {
        std::path::PathBuf::from(unsafe { from_virtual_path($path.try_into()?)? })
    };
}

/// Calls `to_virtual_path` on the host to convert the provided value to a virtual path
/// from a real path.
#[macro_export]
macro_rules! virtual_path {
    (buf, $path:expr) => {
        virtual_path!($path.to_string_lossy())
    };
    ($path:expr) => {{
        let data = unsafe { to_virtual_path($path.try_into()?)? };
        let path: VirtualPath = json::from_str(&data)?;
        path
    }};
}