PageRenderTime 43ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/src/bot.rs

https://gitlab.com/Schmiddiii/ntd
Rust | 120 lines | 102 code | 18 blank | 0 comment | 3 complexity | dcc97812a6e4901fd321962294d6a7aa MD5 | raw file
  1. use std::{
  2. env,
  3. fs::{self, OpenOptions},
  4. io::{Read, Write},
  5. path::{Path, PathBuf},
  6. process::{Command, Stdio},
  7. };
  8. use crate::error::Error;
  9. pub trait Bot {
  10. fn name(&self) -> String;
  11. fn help(&self) -> Result<String, Error>;
  12. fn execute(&self, args: &str) -> Result<String, Error>;
  13. }
  14. pub struct CommandBot {
  15. path: PathBuf,
  16. }
  17. impl CommandBot {
  18. pub fn new<P: AsRef<Path>>(path: P) -> Self {
  19. Self {
  20. path: path.as_ref().to_path_buf(),
  21. }
  22. }
  23. fn command(&self) -> Command {
  24. Command::new(&self.path)
  25. }
  26. }
  27. impl Bot for CommandBot {
  28. fn name(&self) -> String {
  29. self.path
  30. .as_path()
  31. .file_stem()
  32. .map(|s| s.to_string_lossy().into_owned())
  33. .unwrap_or_default()
  34. }
  35. fn help(&self) -> Result<String, Error> {
  36. let output = self.command().arg("--help").output()?;
  37. Ok(String::from_utf8_lossy(&output.stdout).into_owned())
  38. }
  39. fn execute(&self, args: &str) -> Result<String, Error> {
  40. let mut command = self
  41. .command()
  42. .stdin(Stdio::piped())
  43. .stdout(Stdio::piped())
  44. .spawn()?;
  45. {
  46. let mut stdin = command.stdin.take().expect("Command to have stdin");
  47. write!(stdin, "{}", args)?;
  48. }
  49. let out = command.wait_with_output().expect("Failed output").stdout;
  50. Ok(String::from_utf8_lossy(&out).to_string())
  51. }
  52. }
  53. pub struct EditBot {
  54. tempfile: PathBuf,
  55. }
  56. impl EditBot {
  57. pub fn new() -> Result<Self, Error> {
  58. Ok(Self {
  59. tempfile: crate::util::create_tempfile_with_name("ntd-edit.txt")?,
  60. })
  61. }
  62. }
  63. impl Bot for EditBot {
  64. fn name(&self) -> String {
  65. "edit".to_string()
  66. }
  67. fn help(&self) -> Result<String, Error> {
  68. Ok("@edit Edit the input using your $EDITOR.
  69. Note that this is currently hardcoded in Rust as most editors need access to terminal stdin and stdout.
  70. Needs: -
  71. Provides: -
  72. ".to_string())
  73. }
  74. fn execute(&self, args: &str) -> Result<String, Error> {
  75. {
  76. let mut file = OpenOptions::new()
  77. .write(true)
  78. .create(true)
  79. .truncate(true)
  80. .open(&self.tempfile)?;
  81. write!(file, "{}", args)?;
  82. }
  83. let editor = env::var("EDITOR").unwrap_or_else(|_| "nano".to_string());
  84. let _command = Command::new(&editor)
  85. .arg(self.tempfile.as_os_str())
  86. .spawn()?
  87. .wait()?;
  88. let content = {
  89. let mut file = OpenOptions::new().read(true).open(&self.tempfile)?;
  90. let mut string = String::new();
  91. file.read_to_string(&mut string)?;
  92. string
  93. };
  94. Ok(content)
  95. }
  96. }
  97. impl Drop for EditBot {
  98. fn drop(&mut self) {
  99. let _ = fs::remove_file(&self.tempfile);
  100. }
  101. }