Add :open attachments command (#31)

Fixes #27
This commit is contained in:
Benjamin Große 2023-01-28 12:29:06 +00:00 committed by Ulyssa
parent a1574c6b8d
commit 1d93461183
No known key found for this signature in database
GPG key ID: 1B3965A3D18B9B64
5 changed files with 96 additions and 24 deletions

18
Cargo.lock generated
View file

@ -1160,6 +1160,7 @@ dependencies = [
name = "iamb" name = "iamb"
version = "0.0.4" version = "0.0.4"
dependencies = [ dependencies = [
"bitflags",
"chrono", "chrono",
"clap", "clap",
"css-color-parser", "css-color-parser",
@ -1172,6 +1173,7 @@ dependencies = [
"mime", "mime",
"mime_guess", "mime_guess",
"modalkit", "modalkit",
"open",
"regex", "regex",
"rpassword", "rpassword",
"serde", "serde",
@ -1765,6 +1767,16 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "open"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2078c0039e6a54a0c42c28faa984e115fb4c2d5bf2208f77d1961002df8576f8"
dependencies = [
"pathdiff",
"windows-sys",
]
[[package]] [[package]]
name = "os_str_bytes" name = "os_str_bytes"
version = "6.4.1" version = "6.4.1"
@ -1842,6 +1854,12 @@ dependencies = [
"subtle", "subtle",
] ]
[[package]]
name = "pathdiff"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd"
[[package]] [[package]]
name = "pbkdf2" name = "pbkdf2"
version = "0.11.0" version = "0.11.0"

View file

@ -14,6 +14,7 @@ categories = ["command-line-utilities"]
rust-version = "1.66" rust-version = "1.66"
[dependencies] [dependencies]
bitflags = "1.3.2"
chrono = "0.4" chrono = "0.4"
clap = {version = "4.0", features = ["derive"]} clap = {version = "4.0", features = ["derive"]}
css-color-parser = "0.1.2" css-color-parser = "0.1.2"
@ -23,6 +24,7 @@ html5ever = "0.26.0"
markup5ever_rcdom = "0.2.0" markup5ever_rcdom = "0.2.0"
mime = "^0.3.16" mime = "^0.3.16"
mime_guess = "^2.0.4" mime_guess = "^2.0.4"
open = "3.2.0"
regex = "^1.5" regex = "^1.5"
rpassword = "^7.2" rpassword = "^7.2"
serde = "^1.0" serde = "^1.0"

View file

@ -81,9 +81,9 @@ pub enum MessageAction {
/// Download an attachment to the given path. /// Download an attachment to the given path.
/// ///
/// The [bool] argument controls whether to overwrite any already existing file at the /// The second argument controls whether to overwrite any already existing file at the
/// destination path. /// destination path, or to open the attachment after downloading.
Download(Option<String>, bool), Download(Option<String>, DownloadFlags),
/// Edit a sent message. /// Edit a sent message.
Edit, Edit,
@ -95,6 +95,18 @@ pub enum MessageAction {
Reply, Reply,
} }
bitflags::bitflags! {
pub struct DownloadFlags: u32 {
const NONE = 0b00000000;
/// Overwrite file if it already exists.
const FORCE = 0b00000001;
/// Open file after downloading.
const OPEN = 0b00000010;
}
}
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
pub enum RoomField { pub enum RoomField {
Name, Name,

View file

@ -10,6 +10,7 @@ use modalkit::{
}; };
use crate::base::{ use crate::base::{
DownloadFlags,
IambAction, IambAction,
IambId, IambId,
MessageAction, MessageAction,
@ -317,7 +318,29 @@ fn iamb_download(desc: CommandDescription, ctx: &mut ProgContext) -> ProgResult
return Result::Err(CommandError::InvalidArgument); return Result::Err(CommandError::InvalidArgument);
} }
let mact = MessageAction::Download(args.pop(), desc.bang); let mut flags = DownloadFlags::NONE;
if desc.bang {
flags |= DownloadFlags::FORCE;
};
let mact = MessageAction::Download(args.pop(), flags);
let iact = IambAction::from(mact);
let step = CommandStep::Continue(iact.into(), ctx.context.take());
return Ok(step);
}
fn iamb_open(desc: CommandDescription, ctx: &mut ProgContext) -> ProgResult {
let mut args = desc.arg.strings()?;
if args.len() > 1 {
return Result::Err(CommandError::InvalidArgument);
}
let mut flags = DownloadFlags::OPEN;
if desc.bang {
flags |= DownloadFlags::FORCE;
};
let mact = MessageAction::Download(args.pop(), flags);
let iact = IambAction::from(mact); let iact = IambAction::from(mact);
let step = CommandStep::Continue(iact.into(), ctx.context.take()); let step = CommandStep::Continue(iact.into(), ctx.context.take());
@ -328,6 +351,7 @@ fn add_iamb_commands(cmds: &mut ProgramCommands) {
cmds.add_command(ProgramCommand { names: vec!["cancel".into()], f: iamb_cancel }); cmds.add_command(ProgramCommand { names: vec!["cancel".into()], f: iamb_cancel });
cmds.add_command(ProgramCommand { names: vec!["dms".into()], f: iamb_dms }); cmds.add_command(ProgramCommand { names: vec!["dms".into()], f: iamb_dms });
cmds.add_command(ProgramCommand { names: vec!["download".into()], f: iamb_download }); cmds.add_command(ProgramCommand { names: vec!["download".into()], f: iamb_download });
cmds.add_command(ProgramCommand { names: vec!["open".into()], f: iamb_open });
cmds.add_command(ProgramCommand { names: vec!["edit".into()], f: iamb_edit }); cmds.add_command(ProgramCommand { names: vec!["edit".into()], f: iamb_edit });
cmds.add_command(ProgramCommand { names: vec!["invite".into()], f: iamb_invite }); cmds.add_command(ProgramCommand { names: vec!["invite".into()], f: iamb_invite });
cmds.add_command(ProgramCommand { names: vec!["join".into()], f: iamb_join }); cmds.add_command(ProgramCommand { names: vec!["join".into()], f: iamb_join });

View file

@ -4,6 +4,8 @@ use std::fs;
use std::ops::Deref; use std::ops::Deref;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use tokio;
use matrix_sdk::{ use matrix_sdk::{
attachment::AttachmentConfig, attachment::AttachmentConfig,
media::{MediaFormat, MediaRequest}, media::{MediaFormat, MediaRequest},
@ -55,6 +57,7 @@ use modalkit::editing::{
}; };
use crate::base::{ use crate::base::{
DownloadFlags,
IambAction, IambAction,
IambBufferId, IambBufferId,
IambError, IambError,
@ -155,7 +158,7 @@ impl ChatState {
Ok(None) Ok(None)
}, },
MessageAction::Download(filename, force) => { MessageAction::Download(filename, flags) => {
if let MessageEvent::Original(ev) = &msg.event { if let MessageEvent::Original(ev) = &msg.event {
let media = client.media(); let media = client.media();
@ -202,9 +205,10 @@ impl ChatState {
}, },
}; };
if !force && filename.exists() { if filename.exists() {
if !flags.contains(DownloadFlags::FORCE) {
let msg = format!( let msg = format!(
"The file {} already exists; use :download! to overwrite it.", "The file {} already exists; add ! to end of command to overwrite it.",
filename.display() filename.display()
); );
let err = UIError::Failure(msg); let err = UIError::Failure(msg);
@ -220,11 +224,23 @@ impl ChatState {
fs::write(filename.as_path(), bytes.as_slice())?; fs::write(filename.as_path(), bytes.as_slice())?;
msg.downloaded = true; msg.downloaded = true;
}
let info = InfoMessage::from(format!( let info = if flags.contains(DownloadFlags::OPEN) {
// open::that may not return until the spawned program closes.
let target = filename.clone().into_os_string();
tokio::task::spawn_blocking(move || open::that(target));
InfoMessage::from(format!(
"Attachment downloaded to {} and opened",
filename.display()
))
} else {
InfoMessage::from(format!(
"Attachment downloaded to {}", "Attachment downloaded to {}",
filename.display() filename.display()
)); ))
};
return Ok(info.into()); return Ok(info.into());
} }