Skip to main content

wslplugins_rs/api/wsl_command/
prepared_wsl_command.rs

1use super::wsl_command_execution::WSLCommandExecution;
2use super::WSLCommand;
3use std::ffi::CString;
4use std::net::TcpStream;
5
6use crate::{
7    api::{utils, ApiV1, Result as ApiResult},
8    DistributionID, SessionID,
9};
10
11/// A command pre-encoded for the WSL Plugin API.
12///
13/// `PreparedWSLCommand` stores:
14/// - a NUL-terminated program path (`c_path`),
15/// - a NUL-terminated `argv` pointer array (`argv`),
16/// - owned C strings backing `argv` (`_c_args`).
17///
18/// This avoids rebuilding C-compatible buffers when executing the same command
19/// multiple times.
20///
21/// Instances are usually created from [`WSLCommand`] via [`WSLCommand::prepare`]
22/// or `From<WSLCommand>`.
23#[derive(Debug)]
24pub struct PreparedWSLCommand<'a> {
25    api: &'a ApiV1,
26    session_id: SessionID,
27    distribution_id: DistributionID,
28    c_path: Box<[u8]>,
29    argv: Box<[*const u8]>,
30    _c_args: Box<[CString]>,
31}
32
33impl WSLCommandExecution for PreparedWSLCommand<'_> {
34    /// Executes the prepared command via the underlying WSL Plugin API.
35    ///
36    /// # Behavior
37    ///
38    /// - [`DistributionID::System`] uses `ExecuteBinary`.
39    /// - [`DistributionID::User`] uses `ExecuteBinaryInDistribution`.
40    ///
41    /// # Errors
42    ///
43    /// Returns an API error if the call fails, including version requirements
44    /// for distribution-scoped execution.
45    #[doc(alias = "ExecuteBinary")]
46    #[doc(alias = "ExecuteBinaryInDistribution")]
47    #[inline]
48    fn execute(&self) -> ApiResult<TcpStream> {
49        match self.distribution_id {
50            // Safety: the caller ensures that the path and argv are correctly encoded
51            DistributionID::System => unsafe {
52                self.api
53                    .execute_binary_internal(self.session_id, &self.c_path, &self.argv)
54                    .map_err(Into::into)
55            },
56            // Safety: the caller ensures that the path and argv are correctly encoded
57            DistributionID::User(id) => unsafe {
58                self.api.execute_binary_in_distribution_internal(
59                    self.session_id,
60                    id,
61                    &self.c_path,
62                    &self.argv,
63                )
64            },
65        }
66    }
67}
68
69impl<'a> From<WSLCommand<'a>> for PreparedWSLCommand<'a> {
70    #[inline]
71    fn from(value: WSLCommand<'a>) -> Self {
72        let (c_args, argv) = utils::encode_c_argv(value.argv());
73        Self {
74            api: value.api,
75            session_id: value.session_id,
76            distribution_id: value.distribution_id,
77            c_path: utils::encode_c_path(&value.path).into_boxed_slice(),
78            argv: argv.into_boxed_slice(),
79            _c_args: c_args.into_boxed_slice(),
80        }
81    }
82}
83
84impl<'a> From<&WSLCommand<'a>> for PreparedWSLCommand<'a> {
85    #[inline]
86    fn from(value: &WSLCommand<'a>) -> Self {
87        let (c_args, argv) = utils::encode_c_argv(value.argv());
88        Self {
89            api: value.api,
90            session_id: value.session_id,
91            distribution_id: value.distribution_id,
92            c_path: utils::encode_c_path(&value.path).into_boxed_slice(),
93            argv: argv.into_boxed_slice(),
94            _c_args: c_args.into_boxed_slice(),
95        }
96    }
97}