lune_utils/process/
args.rs1#![allow(clippy::missing_panics_doc)]
2
3use std::{
4 env::args_os,
5 ffi::OsString,
6 sync::{Arc, Mutex},
7};
8
9use mlua::prelude::*;
10use os_str_bytes::OsStringBytes;
11
12#[derive(Debug, Default)]
15struct ProcessArgsInner {
16 values: Vec<OsString>,
17}
18
19impl FromIterator<OsString> for ProcessArgsInner {
20 fn from_iter<T: IntoIterator<Item = OsString>>(iter: T) -> Self {
21 Self {
22 values: iter.into_iter().collect(),
23 }
24 }
25}
26
27#[derive(Debug, Clone)]
39pub struct ProcessArgs {
40 inner: Arc<Mutex<ProcessArgsInner>>,
41}
42
43impl ProcessArgs {
44 #[must_use]
45 pub fn empty() -> Self {
46 Self {
47 inner: Arc::new(Mutex::new(ProcessArgsInner::default())),
48 }
49 }
50
51 #[must_use]
52 pub fn current() -> Self {
53 Self {
54 inner: Arc::new(Mutex::new(args_os().collect())),
55 }
56 }
57
58 #[must_use]
59 pub fn len(&self) -> usize {
60 let inner = self.inner.lock().unwrap();
61 inner.values.len()
62 }
63
64 #[must_use]
65 pub fn is_empty(&self) -> bool {
66 let inner = self.inner.lock().unwrap();
67 inner.values.is_empty()
68 }
69
70 #[must_use]
73 pub fn all(&self) -> Vec<OsString> {
74 let inner = self.inner.lock().unwrap();
75 inner.values.clone()
76 }
77
78 #[must_use]
79 pub fn get(&self, index: usize) -> Option<OsString> {
80 let inner = self.inner.lock().unwrap();
81 inner.values.get(index).cloned()
82 }
83
84 pub fn set(&self, index: usize, val: impl Into<OsString>) {
85 let mut inner = self.inner.lock().unwrap();
86 if let Some(arg) = inner.values.get_mut(index) {
87 *arg = val.into();
88 }
89 }
90
91 pub fn push(&self, val: impl Into<OsString>) {
92 let mut inner = self.inner.lock().unwrap();
93 inner.values.push(val.into());
94 }
95
96 #[must_use]
97 pub fn pop(&self) -> Option<OsString> {
98 let mut inner = self.inner.lock().unwrap();
99 inner.values.pop()
100 }
101
102 pub fn insert(&self, index: usize, val: impl Into<OsString>) {
103 let mut inner = self.inner.lock().unwrap();
104 if index <= inner.values.len() {
105 inner.values.insert(index, val.into());
106 }
107 }
108
109 #[must_use]
110 pub fn remove(&self, index: usize) -> Option<OsString> {
111 let mut inner = self.inner.lock().unwrap();
112 if index < inner.values.len() {
113 Some(inner.values.remove(index))
114 } else {
115 None
116 }
117 }
118
119 #[must_use]
122 pub fn all_bytes(&self) -> Vec<Vec<u8>> {
123 self.all()
124 .into_iter()
125 .filter_map(OsString::into_io_vec)
126 .collect()
127 }
128
129 #[must_use]
130 pub fn get_bytes(&self, index: usize) -> Option<Vec<u8>> {
131 let val = self.get(index)?;
132 val.into_io_vec()
133 }
134
135 pub fn set_bytes(&self, index: usize, val: impl Into<Vec<u8>>) {
136 if let Some(val_os) = OsString::from_io_vec(val.into()) {
137 self.set(index, val_os);
138 }
139 }
140
141 pub fn push_bytes(&self, val: impl Into<Vec<u8>>) {
142 if let Some(val_os) = OsString::from_io_vec(val.into()) {
143 self.push(val_os);
144 }
145 }
146
147 #[must_use]
148 pub fn pop_bytes(&self) -> Option<Vec<u8>> {
149 self.pop().and_then(OsString::into_io_vec)
150 }
151
152 pub fn insert_bytes(&self, index: usize, val: impl Into<Vec<u8>>) {
153 if let Some(val_os) = OsString::from_io_vec(val.into()) {
154 self.insert(index, val_os);
155 }
156 }
157
158 pub fn remove_bytes(&self, index: usize) -> Option<Vec<u8>> {
159 self.remove(index).and_then(OsString::into_io_vec)
160 }
161
162 #[doc(hidden)]
165 #[allow(clippy::missing_errors_doc)]
166 pub fn into_plain_lua_table(&self, lua: Lua) -> LuaResult<LuaTable> {
167 let all = self.all_bytes();
168 let tab = lua.create_table_with_capacity(all.len(), 0)?;
169
170 for val in all {
171 let val = lua.create_string(val)?;
172 tab.push(val)?;
173 }
174
175 Ok(tab)
176 }
177}
178
179impl IntoIterator for ProcessArgs {
182 type Item = OsString;
183 type IntoIter = std::vec::IntoIter<OsString>;
184
185 fn into_iter(self) -> Self::IntoIter {
186 let inner = self.inner.lock().unwrap();
187 inner.values.clone().into_iter()
188 }
189}
190
191impl<S: Into<OsString>> FromIterator<S> for ProcessArgs {
192 fn from_iter<T: IntoIterator<Item = S>>(iter: T) -> Self {
193 Self {
194 inner: Arc::new(Mutex::new(iter.into_iter().map(Into::into).collect())),
195 }
196 }
197}
198
199impl<S: Into<OsString>> Extend<S> for ProcessArgs {
200 fn extend<T: IntoIterator<Item = S>>(&mut self, iter: T) {
201 let mut inner = self.inner.lock().unwrap();
202 inner.values.extend(iter.into_iter().map(Into::into));
203 }
204}
205
206impl FromLua for ProcessArgs {
209 fn from_lua(value: LuaValue, _: &Lua) -> LuaResult<Self> {
210 if let LuaValue::Nil = value {
211 Ok(Self::from_iter([] as [OsString; 0]))
212 } else if let LuaValue::Boolean(true) = value {
213 Ok(Self::current())
214 } else if let Some(u) = value.as_userdata().and_then(|u| u.borrow::<Self>().ok()) {
215 Ok(u.clone())
216 } else if let LuaValue::Table(arr) = value {
217 let mut args = Vec::new();
218 for pair in arr.pairs::<LuaValue, LuaValue>() {
219 let val_res = pair.map(|p| p.1.clone());
220 let val = super::lua_value_to_os_string(val_res, "ProcessArgs")?;
221
222 super::validate_os_value(&val)?;
223
224 args.push(val);
225 }
226 Ok(Self::from_iter(args))
227 } else {
228 Err(LuaError::FromLuaConversionError {
229 from: value.type_name(),
230 to: String::from("ProcessArgs"),
231 message: Some(format!(
232 "Invalid type for process args - expected table or nil, got '{}'",
233 value.type_name()
234 )),
235 })
236 }
237 }
238}
239
240impl LuaUserData for ProcessArgs {
241 fn add_methods<M: LuaUserDataMethods<Self>>(methods: &mut M) {
242 methods.add_meta_method(LuaMetaMethod::Len, |_, this, (): ()| Ok(this.len()));
243 methods.add_meta_method(LuaMetaMethod::Index, |_, this, index: usize| {
244 if index == 0 {
245 Ok(None)
246 } else {
247 Ok(this.get(index - 1))
248 }
249 });
250 methods.add_meta_method(LuaMetaMethod::NewIndex, |_, _, (): ()| {
251 Err::<(), _>(LuaError::runtime("ProcessArgs is read-only"))
252 });
253 methods.add_meta_method(LuaMetaMethod::Iter, |lua, this, (): ()| {
254 let mut vars = this
255 .clone()
256 .into_iter()
257 .filter_map(OsStringBytes::into_io_vec)
258 .enumerate();
259 lua.create_function_mut(move |lua, (): ()| match vars.next() {
260 None => Ok((LuaValue::Nil, LuaValue::Nil)),
261 Some((index, value)) => Ok((
262 LuaValue::Integer(index as i64),
263 LuaValue::String(lua.create_string(value)?),
264 )),
265 })
266 });
267 }
268}