1use crate::proto::b64_decode;
21use serde_json::{json, Value};
22use std::path::PathBuf;
23
24#[derive(Debug, Clone)]
26pub struct ExecOutput {
27 pub code: Option<i64>,
29 pub stdout: Vec<u8>,
31 pub stderr: Vec<u8>,
33}
34
35impl ExecOutput {
36 pub fn stdout_str(&self) -> String {
38 String::from_utf8_lossy(&self.stdout).trim_end().to_string()
39 }
40}
41
42pub fn exec<I, S>(program: &str, args: I) -> Result<ExecOutput, String>
45where
46 I: IntoIterator<Item = S>,
47 S: AsRef<str>,
48{
49 let args: Vec<String> = args.into_iter().map(|s| s.as_ref().to_string()).collect();
50 let v = crate::exec::run(&json!({ "program": program, "args": args }));
51 if v["ok"] != json!(true) {
52 return Err(v["err"].as_str().unwrap_or("exec_failed").to_string());
53 }
54 Ok(ExecOutput {
55 code: v["code"].as_i64(),
56 stdout: v["stdout"]
57 .as_str()
58 .and_then(b64_decode)
59 .unwrap_or_default(),
60 stderr: v["stderr"]
61 .as_str()
62 .and_then(b64_decode)
63 .unwrap_or_default(),
64 })
65}
66
67pub fn exec_stdin<I, S>(program: &str, args: I, input: &str) -> Result<ExecOutput, String>
69where
70 I: IntoIterator<Item = S>,
71 S: AsRef<str>,
72{
73 let args: Vec<String> = args.into_iter().map(|s| s.as_ref().to_string()).collect();
74 let v = crate::exec::run(&json!({ "program": program, "args": args, "stdin": input }));
75 if v["ok"] != json!(true) {
76 return Err(v["err"].as_str().unwrap_or("exec_failed").to_string());
77 }
78 Ok(ExecOutput {
79 code: v["code"].as_i64(),
80 stdout: v["stdout"]
81 .as_str()
82 .and_then(b64_decode)
83 .unwrap_or_default(),
84 stderr: v["stderr"]
85 .as_str()
86 .and_then(b64_decode)
87 .unwrap_or_default(),
88 })
89}
90
91#[derive(Debug, Clone)]
93pub struct Entry {
94 pub path: PathBuf,
96 pub name: String,
98 pub dir: bool,
100 pub size: u64,
102}
103
104pub fn walk(root: &str, ext: Option<&str>) -> Vec<Entry> {
108 let mut req = json!({ "path": root });
109 if let Some(x) = ext {
110 req["ext"] = json!(x);
111 }
112 entries_from(crate::fsops::handle("fs_walk", &req))
113}
114
115pub fn walk_filtered(
118 root: &str,
119 depth: Option<usize>,
120 dirs_only: bool,
121 ext: Option<&str>,
122 contains: Option<&str>,
123) -> Vec<Entry> {
124 let mut req = json!({ "path": root, "dirs_only": dirs_only });
125 if let Some(d) = depth {
126 req["depth"] = json!(d);
127 }
128 if let Some(x) = ext {
129 req["ext"] = json!(x);
130 }
131 if let Some(c) = contains {
132 req["contains"] = json!(c);
133 }
134 entries_from(crate::fsops::handle("fs_walk", &req))
135}
136
137fn entries_from(v: Value) -> Vec<Entry> {
138 v["entries"]
139 .as_array()
140 .map(|a| {
141 a.iter()
142 .map(|e| Entry {
143 path: PathBuf::from(e["path"].as_str().unwrap_or("")),
144 name: e["name"].as_str().unwrap_or("").to_string(),
145 dir: e["dir"].as_bool().unwrap_or(false),
146 size: e["size"].as_u64().unwrap_or(0),
147 })
148 .collect()
149 })
150 .unwrap_or_default()
151}
152
153pub fn read_file(path: &str) -> Result<Vec<u8>, String> {
155 let v = crate::fsops::handle("fs_read", &json!({ "path": path }));
156 if v["ok"] != json!(true) {
157 return Err(v["err"].as_str().unwrap_or("read_failed").to_string());
158 }
159 Ok(v["b64"].as_str().and_then(b64_decode).unwrap_or_default())
160}
161
162pub fn write_file(path: &str, bytes: &[u8]) -> Result<(), String> {
164 let v = crate::fsops::handle(
165 "fs_write",
166 &json!({ "path": path, "b64": crate::proto::b64_encode(bytes) }),
167 );
168 if v["ok"] == json!(true) {
169 Ok(())
170 } else {
171 Err(v["err"].as_str().unwrap_or("write_failed").to_string())
172 }
173}