/src/script.rs

https://github.com/BenSchZA/pier · Rust · 109 lines · 89 code · 12 blank · 8 comment · 6 complexity · 052bd13868b06d3d7ccbe833568f434b MD5 · raw file

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