ripht_php_sapi/execution/
context.rs1use std::ffi::CString;
2use std::fmt;
3use std::path::PathBuf;
4
5use crate::sapi::ServerVars;
6use crate::ExecutionError;
7
8#[derive(Debug, Clone)]
13pub struct ExecutionContext {
14 pub input: Vec<u8>,
15 pub script_path: PathBuf,
16 pub server_vars: ServerVars,
17 pub env_vars: Vec<(String, String)>,
18 pub ini_overrides: Vec<(String, String)>,
19 pub log_to_stderr: bool,
20}
21
22impl ExecutionContext {
23 pub fn script(path: impl Into<PathBuf>) -> Self {
24 Self {
25 input: Vec::new(),
26 script_path: path.into(),
27 server_vars: ServerVars::new(),
28 env_vars: Vec::new(),
29 ini_overrides: Vec::new(),
30 log_to_stderr: false,
31 }
32 }
33
34 pub fn var(
35 mut self,
36 key: impl Into<String>,
37 value: impl Into<String>,
38 ) -> Self {
39 self.server_vars
40 .set(key, value);
41 self
42 }
43
44 pub fn vars<I, K, V>(mut self, iter: I) -> Self
45 where
46 I: IntoIterator<Item = (K, V)>,
47 K: Into<String>,
48 V: Into<String>,
49 {
50 self.server_vars.extend(iter);
51 self
52 }
53
54 pub fn input(mut self, bytes: impl Into<Vec<u8>>) -> Self {
55 self.input = bytes.into();
56 self
57 }
58
59 pub fn env(
60 mut self,
61 key: impl Into<String>,
62 value: impl Into<String>,
63 ) -> Self {
64 self.env_vars
65 .push((key.into(), value.into()));
66 self
67 }
68
69 pub fn envs<I, K, V>(mut self, iter: I) -> Self
70 where
71 I: IntoIterator<Item = (K, V)>,
72 K: Into<String>,
73 V: Into<String>,
74 {
75 self.env_vars.extend(
76 iter.into_iter()
77 .map(|(k, v)| (k.into(), v.into())),
78 );
79 self
80 }
81
82 pub fn ini(
83 mut self,
84 key: impl Into<String>,
85 value: impl Into<String>,
86 ) -> Self {
87 self.ini_overrides
88 .push((key.into(), value.into()));
89 self
90 }
91
92 pub fn path_as_cstring(&self) -> Result<CString, ExecutionError> {
93 let path_str = self
94 .script_path
95 .to_string_lossy();
96 CString::new(path_str.as_bytes()).map_err(|_| {
97 ExecutionError::InvalidPath("Path contains null byte".to_string())
98 })
99 }
100}
101
102impl fmt::Display for ExecutionContext {
103 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104 writeln!(f, "ExecutionContext {{")?;
105 writeln!(f, " script: {}", self.script_path.display())?;
106
107 let var_count = self.server_vars.len();
108 if var_count == 0 {
109 writeln!(f, " server_vars: []")?;
110 } else {
111 writeln!(f, " server_vars: [")?;
112
113 let display_count = var_count.min(15);
114 for (key, value) in self
115 .server_vars
116 .iter()
117 .take(display_count)
118 {
119 let escaped_value = escape_control_chars(value);
120 let truncated = if escaped_value.len() > 60 {
121 format!("{}...", &escaped_value[..57])
122 } else {
123 escaped_value
124 };
125 writeln!(f, " {} = \"{}\"", key, truncated)?;
126 }
127
128 if var_count > display_count {
129 writeln!(f, " ... ({} more)", var_count - display_count)?;
130 }
131 writeln!(f, " ]")?;
132 }
133
134 writeln!(f, " input: {} bytes", self.input.len())?;
135 write!(f, "}}")
136 }
137}
138
139fn escape_control_chars(s: &str) -> String {
140 let mut result = String::with_capacity(s.len());
141 for c in s.chars() {
142 if c.is_control() && c != '\t' && c != '\n' {
143 result.push_str(&format!("\\x{:02x}", c as u32));
144 } else {
145 result.push(c);
146 }
147 }
148 result
149}