mirror of
https://github.com/pimalaya/himalaya.git
synced 2026-06-18 05:47:54 +08:00
c490bd5a27
* feat: Add configs for `reply-with` and `forward-with` commands Config extended from: (Old) ```toml [message.composer.mml] command = "mml compose" ``` , where `compose-with`, `reply-with`, and `forward-with` all share the same composer command, to: (New) ```toml [message.composer.mml] default = true compose-command = "mml compose" reply-command = "mml reply" forward-command = "mml forward" ``` * docs: ComposerConfig * refactor(account): simplify composer resolution with `get_composer` - Implement `Account::get_composer`, and `Account::get_reader` to fetch config by name or default. - Remove the redundant `resolve_composer`, `default_composer` `resolve_reader`, and `default_reader` helpers. - Simplify the configuration retrieval architecture. * refactor: update composer and reader config to use std::process::Command - Remove `_command` suffix from `compose`, `reply`, and `forward` configuration fields. - Replace composer and reader config command type from `String` to `std::process::Command`. - Remove `Clone` derives from `Config`, `Account`, and related structs due to `Command` type limitations. Refs: #687
70 lines
2.4 KiB
Rust
70 lines
2.4 KiB
Rust
// This file is part of Himalaya, a CLI to manage emails.
|
|
//
|
|
// Copyright (C) 2022-2026 soywod <pimalaya.org@posteo.net>
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify it under
|
|
// the terms of the GNU Affero General Public License as published by the Free
|
|
// Software Foundation, either version 3 of the License, or (at your option) any
|
|
// later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
|
// details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
//! Spawns user-defined composer and reader commands.
|
|
//!
|
|
//! A composer/reader is just a shell command line invoked via
|
|
//! `sh -c`. Himalaya pipes source MIME bytes (empty for new messages)
|
|
//! into the child's stdin, captures stdout (the produced MIME draft
|
|
//! or the interpreted text), and inherits stderr so the spawned
|
|
//! command can prompt the user or print errors directly to the
|
|
//! terminal. TUI composers that need interactive input can re-open
|
|
//! `/dev/tty` once they've consumed stdin — standard Unix practice.
|
|
|
|
use std::{
|
|
io::Write,
|
|
process::{Command, Stdio},
|
|
};
|
|
|
|
use anyhow::{Result, anyhow, bail};
|
|
|
|
/// Spawns `command`, writes `stdin_bytes` to its
|
|
/// stdin, and returns the captured stdout bytes.
|
|
/// Stderr is inherited.
|
|
/// Bails on a non-zero exit status.
|
|
pub fn run(command: &mut Command, stdin_bytes: &[u8]) -> Result<Vec<u8>> {
|
|
let mut child = command
|
|
.stdin(Stdio::piped())
|
|
.stdout(Stdio::piped())
|
|
.stderr(Stdio::inherit())
|
|
.spawn()
|
|
.map_err(|err| anyhow!("spawn `{command:?}`: {err}"))?;
|
|
|
|
if let Some(mut stdin) = child.stdin.take() {
|
|
stdin
|
|
.write_all(stdin_bytes)
|
|
.map_err(|err| anyhow!("write stdin to `{command:?}`: {err}"))?;
|
|
}
|
|
|
|
let output = child
|
|
.wait_with_output()
|
|
.map_err(|err| anyhow!("wait `{command:?}`: {err}"))?;
|
|
|
|
if !output.status.success() {
|
|
bail!(
|
|
"`{command:?}` exited with status {}",
|
|
output
|
|
.status
|
|
.code()
|
|
.map(|c| c.to_string())
|
|
.unwrap_or_else(|| "?".to_string())
|
|
);
|
|
}
|
|
|
|
Ok(output.stdout)
|
|
}
|