/tauri-api/src/command.rs

https://github.com/tauri-apps/tauri · Rust · 171 lines · 110 code · 29 blank · 32 comment · 9 complexity · eeb6596a27621a8354bffe6d919aec4a MD5 · raw file

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