-
Notifications
You must be signed in to change notification settings - Fork 33
/
script.rs
109 lines (97 loc) · 3.51 KB
/
script.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
use super::error::*;
use super::PierResult;
use serde::{Deserialize, Serialize};
use snafu::ResultExt;
use std::fs::File;
use std::io::prelude::*;
use std::os::unix::fs::PermissionsExt;
use std::process::{Command, Output, Stdio};
use tempfile;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Script {
#[serde(skip)]
pub alias: String,
pub command: String,
pub description: Option<String>,
pub reference: Option<String>,
pub tags: Option<Vec<String>>,
}
impl Script {
pub fn has_shebang(&self) -> bool {
match self.command.lines().nth(0) {
Some(line) => line.starts_with("#!"),
None => false,
}
}
pub fn display_command(&self, display_full: bool, width: usize) -> &str {
match display_full {
true => &self.command,
false => {
match &self.command.lines().nth(0) {
Some(line) => {
match line.chars().count() {
c if c <= width => line,
c if c > width => &line[0..width],
_ => "",
}
}
None => &self.command,
}
}
}
}
/// Runs the script inline using something like sh -c "<script>" or python -c "<script."...
pub fn run_with_cli_interpreter(
&self,
interpreter: &Vec<String>,
args: Vec<String>,
) -> PierResult<Output> {
// First item in interpreter is the binary
let cmd = Command::new(&interpreter[0])
// The following items after the binary is any commandline args that are necessary.
.args(&interpreter[1..])
.arg(&self.command)
.arg(&self.alias)
.args(&args)
.stderr(Stdio::piped())
.spawn()
.context(CommandExec)?
.wait_with_output()
.context(CommandExec)?;
Ok(cmd)
}
/// First creates a temporary file and then executes the file before removing it.
pub fn run_with_shebang(&self, args: Vec<String>) -> PierResult<Output> {
// Creates a temp directory to place our tempfile inside.
let tmpdir = tempfile::Builder::new()
.prefix("pier")
.tempdir()
.context(ExecutableTempFileCreate)?;
let exec_file_path = tmpdir.path().join(&self.alias);
// Creating the file inside a closure is convenient because rust will automatically handle
// closing the file for us so we can go ahead and execute it after writing to it and setting the file permissions.
{
let mut exec_file = File::create(&exec_file_path).context(ExecutableTempFileCreate)?;
exec_file
.write(self.command.as_bytes())
.context(ExecutableTempFileCreate)?;
let mut permissions = exec_file
.metadata()
.context(ExecutableTempFileCreate)?
.permissions();
// Set the file permissions to allow read and execute for the current user.
permissions.set_mode(0o500);
exec_file
.set_permissions(permissions)
.context(ExecutableTempFileCreate)?;
}
let cmd = Command::new(exec_file_path)
.stderr(Stdio::piped())
.args(&args)
.spawn()
.context(CommandExec)?
.wait_with_output()
.context(CommandExec)?;
Ok(cmd)
}
}