cursive_open_editor/
lib.rs

1pub mod strategy;
2
3use std::{
4  ffi::OsString,
5  fs::File,
6  io::{self, Read},
7  path::PathBuf,
8  process::{Command, ExitStatus},
9};
10
11use cursive::Cursive;
12use strategy::{EditPathStrategy, EditPathStrategyOut, FindEditorStrategy};
13
14/// How the editor will be opened.
15pub struct CursiveOpenEditorOptions {
16  /// How to pick which editor program to run.
17  /// By default, check (in order) the `CURSIVE_EDITOR`, `EDITOR`, and `VISUAL`
18  /// environment variables.
19  pub editor_strategy: FindEditorStrategy,
20  /// Additional arguments to pass to the editor program invocation.
21  /// The file path to edit is always passed as the last argument, after
22  /// all of these.
23  pub additional_args: Vec<OsString>,
24  /// How to pick what file to edit.
25  /// By default, create a temporary file.
26  pub edit_path_strategy: EditPathStrategy,
27}
28
29impl Default for CursiveOpenEditorOptions {
30  fn default() -> Self {
31    Self {
32      editor_strategy: FindEditorStrategy::default(),
33      edit_path_strategy: EditPathStrategy::default(),
34      additional_args: Vec::new(),
35    }
36  }
37}
38
39/// The main entrypoint to the library.
40/// Open the editor over the given Cursive.
41pub fn open_editor(
42  siv: &mut Cursive,
43  options: CursiveOpenEditorOptions,
44) -> io::Result<EditorOpened> {
45  let editor_path = options.editor_strategy.editor_path().ok_or_else(|| {
46    io::Error::new(
47      io::ErrorKind::NotFound,
48      "could not find an editor to launch",
49    )
50  })?;
51  let edit_path = options.edit_path_strategy.file_path()?;
52
53  let dump = siv.dump();
54  siv.clear();
55
56  let mut cmd_builder = Command::new(editor_path);
57  cmd_builder.args(&options.additional_args);
58  cmd_builder.arg(&edit_path.path());
59
60  // run the editor!
61  let mut kid = cmd_builder.spawn()?;
62  let status = kid.wait()?;
63
64  siv.restore(dump);
65  Ok(EditorOpened {
66    status,
67    edited_path: edit_path,
68  })
69}
70
71/// Information about the editor ran.
72pub struct EditorOpened {
73  pub status: ExitStatus,
74  pub edited_path: EditPathStrategyOut,
75}
76
77impl EditorOpened {
78  /// Returns an [`io::Error`] if the [`ExitStatus`] of the editor program
79  /// wasn't a success.
80  ///
81  /// Note that the file could still have been successfully edited!
82  /// This is just a helper to use with the `?` operator, or whatever.
83  pub fn status_ok(&self) -> io::Result<()> {
84    if self.status.success() {
85      Ok(())
86    } else {
87      Err(io::Error::other(format!("non-OK exit: {}", &self.status)))
88    }
89  }
90
91  /// Open the edited file as a read-only [`File`].
92  pub fn edited_file(&self) -> io::Result<File> {
93    let path = self.edited_path.path();
94    File::open(&path)
95  }
96
97  /// The path that was edited.
98  pub fn edited_path(&self) -> PathBuf {
99    self.edited_path.path()
100  }
101
102  /// Read the edited file to a string.
103  pub fn read_to_string(&self) -> io::Result<String> {
104    let mut s = String::new();
105    self.edited_file()?.read_to_string(&mut s)?;
106    Ok(s)
107  }
108}