command.rs 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. use std::process::{Child, Command, Stdio};
  2. use tauri_utils::platform;
  3. pub fn get_output(cmd: String, args: Vec<String>, stdout: Stdio) -> crate::Result<String> {
  4. Command::new(cmd)
  5. .args(args)
  6. .stdout(stdout)
  7. .output()
  8. .map_err(|err| crate::Error::with_chain(err, "Command: get output failed"))
  9. .and_then(|output| {
  10. if output.status.success() {
  11. Ok(String::from_utf8_lossy(&output.stdout).to_string())
  12. } else {
  13. Err(crate::ErrorKind::Command(String::from_utf8_lossy(&output.stderr).to_string()).into())
  14. }
  15. })
  16. }
  17. pub fn format_command(path: String, command: String) -> String {
  18. if cfg!(windows) {
  19. format!("{}/./{}.exe", path, command)
  20. } else {
  21. format!("{}/./{}", path, command)
  22. }
  23. }
  24. pub fn relative_command(command: String) -> crate::Result<String> {
  25. match std::env::current_exe()?.parent() {
  26. Some(exe_dir) => Ok(format_command(exe_dir.display().to_string(), command)),
  27. None => Err(crate::ErrorKind::Command("Could not evaluate executable dir".to_string()).into()),
  28. }
  29. }
  30. pub fn command_path(command: String) -> crate::Result<String> {
  31. match std::env::current_exe()?.parent() {
  32. #[cfg(not(windows))]
  33. Some(exe_dir) => Ok(format!("{}/{}", exe_dir.display().to_string(), command)),
  34. #[cfg(windows)]
  35. Some(exe_dir) => Ok(format!("{}/{}.exe", exe_dir.display().to_string(), command)),
  36. None => Err(crate::ErrorKind::Command("Could not evaluate executable dir".to_string()).into()),
  37. }
  38. }
  39. pub fn spawn_relative_command(
  40. command: String,
  41. args: Vec<String>,
  42. stdout: Stdio,
  43. ) -> crate::Result<Child> {
  44. let cmd = relative_command(command)?;
  45. Ok(Command::new(cmd).args(args).stdout(stdout).spawn()?)
  46. }
  47. pub fn binary_command(binary_name: String) -> crate::Result<String> {
  48. Ok(format!("{}-{}", binary_name, platform::target_triple()?))
  49. }
  50. // tests for the commands functions.
  51. #[cfg(test)]
  52. mod test {
  53. use super::*;
  54. use crate::{Error, ErrorKind};
  55. use totems::{assert_err, assert_ok};
  56. #[test]
  57. // test the get_output function with a unix cat command.
  58. fn test_cmd_output() {
  59. // create a string with cat in it.
  60. let cmd = String::from("cat");
  61. // call get_output with cat and the argument test/test.txt on the stdio.
  62. let res = get_output(cmd, vec!["test/test.txt".to_string()], Stdio::piped());
  63. // assert that the result is an Ok() type
  64. assert_ok!(&res);
  65. // if the assertion passes, assert the incoming data.
  66. if let Ok(s) = &res {
  67. // assert that cat returns the string in the test.txt document.
  68. assert_eq!(*s, "This is a test doc!".to_string());
  69. }
  70. }
  71. #[test]
  72. // test the failure case for get_output
  73. fn test_cmd_fail() {
  74. // queue up a string with cat in it.
  75. let cmd = String::from("cat");
  76. // call get output with test/ as an argument on the stdio.
  77. let res = get_output(cmd, vec!["test/".to_string()], Stdio::piped());
  78. // assert that the result is an Error type.
  79. assert_err!(&res);
  80. // destruct the Error to check the ErrorKind and test that it is a Command type.
  81. if let Err(Error(ErrorKind::Command(e), _)) = &res {
  82. // assert that the message in the error matches this string.
  83. assert_eq!(*e, "cat: test/: Is a directory\n".to_string());
  84. }
  85. }
  86. #[test]
  87. // test the relative_command function
  88. fn check_relateive_cmd() {
  89. // generate a cat string
  90. let cmd = String::from("cat");
  91. // call relative command on the cat string
  92. let res = relative_command(cmd.clone());
  93. // assert that the result comes back with Ok()
  94. assert_ok!(res);
  95. // get the parent directory of the current executable.
  96. let current_exe = std::env::current_exe()
  97. .unwrap()
  98. .parent()
  99. .unwrap()
  100. .display()
  101. .to_string();
  102. // check the string inside of the Ok
  103. if let Ok(s) = &res {
  104. // match the string against the call to format command with the current_exe.
  105. assert_eq!(*s, format_command(current_exe, cmd));
  106. }
  107. }
  108. #[test]
  109. // test the command_path function
  110. fn check_command_path() {
  111. // generate a string for cat
  112. let cmd = String::from("cat");
  113. // call command_path on cat
  114. let res = command_path(cmd);
  115. // assert that the result is an OK() type.
  116. assert_ok!(res);
  117. }
  118. #[test]
  119. // check the spawn_relative_command function
  120. fn check_spawn_cmd() {
  121. // generate a cat string
  122. let cmd = String::from("cat");
  123. // call spawn_relative_command with cat and the argument test/test.txt on the Stdio.
  124. let res = spawn_relative_command(cmd, vec!["test/test.txt".to_string()], Stdio::piped());
  125. // this fails because there is no cat binary in the relative parent folder of this current executing command.
  126. assert_err!(&res);
  127. // after asserting that the result is an error, check that the error kind is ErrorKind::Io
  128. if let Err(Error(ErrorKind::Io(s), _)) = &res {
  129. // assert that the ErrorKind inside of the ErrorKind Io is ErrorKind::NotFound
  130. assert_eq!(s.kind(), std::io::ErrorKind::NotFound);
  131. }
  132. }
  133. }