/src/hanayo/cmd.rs

https://github.com/ffwff/hana · Rust · 249 lines · 227 code · 11 blank · 11 comment · 16 complexity · da06b6e1820552752ece0f79dd53c293 MD5 · raw file

  1. //! Provides Cmd record for executing and handling commands
  2. use crate::vmbindings::record::Record;
  3. use crate::vmbindings::value::Value;
  4. use crate::vmbindings::vm::Vm;
  5. use crate::vmbindings::vmerror::VmError;
  6. use std::borrow::Borrow;
  7. use std::io::Write;
  8. use std::process::{Child, Command, Output, Stdio};
  9. #[hana_function()]
  10. fn constructor(val: Value::Any) -> Value {
  11. let cmd: Command = match val {
  12. Value::Array(arr) => {
  13. let arr = arr.as_ref();
  14. if arr.len() == 0 {
  15. let rec = vm.malloc(Record::new());
  16. rec.as_mut().insert(
  17. "prototype",
  18. Value::Record(vm.stdlib.as_ref().unwrap().invalid_argument_error.clone())
  19. .wrap(),
  20. );
  21. rec.as_mut().insert(
  22. "why",
  23. Value::Str(
  24. vm.malloc(
  25. "Expected argument array to have at least 1 member"
  26. .to_string()
  27. .into(),
  28. ),
  29. )
  30. .wrap(),
  31. );
  32. rec.as_mut().insert("where", Value::Int(0).wrap());
  33. hana_raise!(vm, Value::Record(rec));
  34. }
  35. let mut cmd = Command::new(match unsafe { arr[0].unwrap() } {
  36. Value::Str(s) => (s.as_ref().borrow() as &String).clone(),
  37. _ => {
  38. let rec = vm.malloc(Record::new());
  39. rec.as_mut().insert(
  40. "prototype",
  41. Value::Record(vm.stdlib.as_ref().unwrap().invalid_argument_error.clone())
  42. .wrap(),
  43. );
  44. rec.as_mut().insert(
  45. "why",
  46. Value::Str(
  47. vm.malloc("Expected command to be of string type".to_string().into()),
  48. )
  49. .wrap(),
  50. );
  51. rec.as_mut().insert("where", Value::Int(0).wrap());
  52. hana_raise!(vm, Value::Record(rec));
  53. }
  54. });
  55. if arr.len() > 1 {
  56. let slice = &arr.as_slice()[1..];
  57. for val in slice {
  58. match unsafe { val.unwrap() } {
  59. Value::Str(s) => cmd.arg((s.as_ref().borrow() as &String).clone()),
  60. _ => {
  61. let rec = vm.malloc(Record::new());
  62. rec.as_mut().insert(
  63. "prototype",
  64. Value::Record(
  65. vm.stdlib.as_ref().unwrap().invalid_argument_error.clone(),
  66. )
  67. .wrap(),
  68. );
  69. rec.as_mut().insert(
  70. "why",
  71. Value::Str(vm.malloc(
  72. "Expected argument to be of string type".to_string().into(),
  73. ))
  74. .wrap(),
  75. );
  76. rec.as_mut().insert("where", Value::Int(0).wrap());
  77. hana_raise!(vm, Value::Record(rec));
  78. }
  79. };
  80. }
  81. }
  82. cmd
  83. }
  84. Value::Str(scmd) => {
  85. let mut cmd = Command::new("sh");
  86. cmd.arg("-c")
  87. .arg((scmd.as_ref().borrow() as &String).clone());
  88. cmd
  89. }
  90. _ => {
  91. let rec = vm.malloc(Record::new());
  92. rec.as_mut().insert(
  93. "prototype",
  94. Value::Record(vm.stdlib.as_ref().unwrap().invalid_argument_error.clone()).wrap(),
  95. );
  96. rec.as_mut().insert(
  97. "why",
  98. Value::Str(
  99. vm.malloc(
  100. "Expected argument to be of string or array type"
  101. .to_string()
  102. .into(),
  103. ),
  104. )
  105. .wrap(),
  106. );
  107. rec.as_mut().insert("where", Value::Int(0).wrap());
  108. hana_raise!(vm, Value::Record(rec));
  109. }
  110. };
  111. // cmd object
  112. let rec = vm.malloc(Record::new());
  113. // store native cmd
  114. rec.as_mut().native_field = Some(Box::new(cmd));
  115. rec.as_mut().insert(
  116. "prototype",
  117. Value::Record(vm.stdlib.as_ref().unwrap().cmd_rec.clone()).wrap(),
  118. );
  119. Value::Record(rec)
  120. }
  121. // inputs
  122. #[hana_function()]
  123. fn in_(cmd: Value::Record, input: Value::Str) -> Value {
  124. cmd.as_mut()
  125. .insert("input_buffer", Value::Str(input).wrap());
  126. Value::Record(cmd)
  127. }
  128. // outputs
  129. fn utf8_decoding_error(err: std::string::FromUtf8Error, vm: &Vm) -> Value {
  130. let rec = vm.malloc(Record::new());
  131. rec.as_mut().insert(
  132. "prototype",
  133. Value::Record(vm.stdlib.as_ref().unwrap().utf8_decoding_error.clone()).wrap(),
  134. );
  135. rec.as_mut().insert(
  136. "why",
  137. Value::Str(vm.malloc(format!("{:?}", err).into())).wrap(),
  138. );
  139. rec.as_mut().insert("where", Value::Int(0).wrap());
  140. Value::Record(rec)
  141. }
  142. // helper class
  143. enum OutputResult {
  144. Process(Child),
  145. Output(Result<Output, std::io::Error>),
  146. }
  147. impl OutputResult {
  148. fn as_process(self) -> Child {
  149. match self {
  150. OutputResult::Process(x) => x,
  151. _ => panic!("calling with wrong object, expected process"),
  152. }
  153. }
  154. fn as_output(self) -> Result<Output, std::io::Error> {
  155. match self {
  156. OutputResult::Output(x) => x,
  157. _ => panic!("calling with wrong object, expected output"),
  158. }
  159. }
  160. }
  161. fn get_output(cmd: &mut Record, wait: bool) -> OutputResult {
  162. let field = cmd.native_field.as_mut().unwrap();
  163. let mut p = field
  164. .downcast_mut::<Command>()
  165. .unwrap()
  166. .stdin(Stdio::piped())
  167. .stdout(Stdio::piped())
  168. .stderr(Stdio::piped())
  169. .spawn()
  170. .unwrap();
  171. if let Some(val) = cmd.get(&"input_buffer".to_string()) {
  172. match unsafe { val.unwrap() } {
  173. Value::Str(s) => {
  174. p.stdin.as_mut().unwrap().write_all(s.as_ref().as_bytes());
  175. }
  176. _ => unimplemented!(),
  177. }
  178. }
  179. if wait {
  180. OutputResult::Output(p.wait_with_output())
  181. } else {
  182. OutputResult::Process(p)
  183. }
  184. }
  185. // impls
  186. #[hana_function()]
  187. fn out(cmd: Value::Record) -> Value {
  188. // stdout as string
  189. let out = get_output(cmd.as_mut(), true).as_output().unwrap();
  190. match String::from_utf8(out.stdout) {
  191. Ok(s) => Value::Str(vm.malloc(s.into())),
  192. Err(err) => {
  193. hana_raise!(vm, utf8_decoding_error(err, vm));
  194. }
  195. }
  196. }
  197. #[hana_function()]
  198. fn err(cmd: Value::Record) -> Value {
  199. // stderr as string
  200. let out = get_output(cmd.as_mut(), true).as_output().unwrap();
  201. match String::from_utf8(out.stderr) {
  202. Ok(s) => Value::Str(vm.malloc(s.into())),
  203. Err(err) => {
  204. hana_raise!(vm, utf8_decoding_error(err, vm));
  205. }
  206. }
  207. }
  208. #[hana_function()]
  209. fn outputs(cmd: Value::Record) -> Value {
  210. // array of [stdout, stderr] outputs
  211. let out = get_output(cmd.as_mut(), true).as_output().unwrap();
  212. let arr = vm.malloc(Vec::new());
  213. match String::from_utf8(out.stdout) {
  214. Ok(s) => arr.as_mut().push(Value::Str(vm.malloc(s.into())).wrap()),
  215. Err(err) => {
  216. hana_raise!(vm, utf8_decoding_error(err, vm));
  217. }
  218. }
  219. match String::from_utf8(out.stderr) {
  220. Ok(s) => arr.as_mut().push(Value::Str(vm.malloc(s.into())).wrap()),
  221. Err(err) => {
  222. hana_raise!(vm, utf8_decoding_error(err, vm));
  223. }
  224. }
  225. Value::Array(arr)
  226. }
  227. // spawn
  228. #[hana_function()]
  229. fn spawn(cmd: Value::Record) -> Value {
  230. let p = get_output(cmd.as_mut(), false).as_process();
  231. let prec = vm.malloc(Record::new());
  232. prec.as_mut().native_field = Some(Box::new(p));
  233. prec.as_mut().insert(
  234. "prototype",
  235. Value::Record(vm.stdlib.as_ref().unwrap().proc_rec.clone()).wrap(),
  236. );
  237. Value::Record(prec)
  238. }