From d8055c6e6a6ca84b87e504a5cf88e9c84c869191 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Tue, 17 Mar 2020 06:23:33 -0700 Subject: [PATCH] Allow opening magnet links after creation Magnet links can now be opened after creation: imdl torrent link --open --input METAINFO type: added --- src/consts.rs | 24 ++++----- src/platform_interface.rs | 17 +++++- src/subcommand/torrent/create.rs | 4 +- src/subcommand/torrent/link.rs | 92 +++++++++++++++++++++++++++++++- 4 files changed, 120 insertions(+), 17 deletions(-) diff --git a/src/consts.rs b/src/consts.rs index 43a5c07b..df65b560 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -1,3 +1,11 @@ +pub(crate) const ABOUT: &str = concat!( + env!("CARGO_PKG_DESCRIPTION"), + " - ", + env!("CARGO_PKG_HOMEPAGE") +); + +pub(crate) const AUTHOR: &str = env!("CARGO_PKG_AUTHORS"); + /// Default value for `created by` torrent metainfo field. /// /// Example: imdl/0.0.0 (1234567890AB) @@ -12,20 +20,8 @@ pub(crate) const CREATED_BY_DEFAULT: &str = concat!( /// Value for `encoding` torrent metainfo field. pub(crate) const ENCODING_UTF8: &str = "UTF-8"; -pub(crate) const ABOUT: &str = concat!( - env!("CARGO_PKG_DESCRIPTION"), - " - ", - env!("CARGO_PKG_HOMEPAGE") -); - -pub(crate) const VERSION: &str = concat!("v", env!("CARGO_PKG_VERSION")); - -pub(crate) const AUTHOR: &str = env!("CARGO_PKG_AUTHORS"); - pub(crate) const HELP_MESSAGE: &str = "Print help message."; -pub(crate) const VERSION_MESSAGE: &str = "Print version number."; - /// The pogress chars are from the /// [Block Elements unicode block](https://en.wikipedia.org/wiki/Block_Elements). pub(crate) const PROGRESS_CHARS: &str = "█▉▊▋▌▍▎▏ "; @@ -66,3 +62,7 @@ pub(crate) const TICK_CHARS: &str = concat!( "⢰⢱⢲⢳⢴⢵⢶⢷⣰⣱⣲⣳⣴⣵⣶⣷", // 0b1110---- "⢸⢹⢺⢻⢼⢽⢾⢿⣸⣹⣺⣻⣼⣽⣾⣿", // 0b1111---- ); + +pub(crate) const VERSION: &str = concat!("v", env!("CARGO_PKG_VERSION")); + +pub(crate) const VERSION_MESSAGE: &str = "Print version number."; diff --git a/src/platform_interface.rs b/src/platform_interface.rs index 1f3256b2..d40fa3f5 100644 --- a/src/platform_interface.rs +++ b/src/platform_interface.rs @@ -1,9 +1,22 @@ use crate::common::*; pub(crate) trait PlatformInterface { - fn open(path: &Path) -> Result<(), Error> { + fn open_file(path: &Path) -> Result<(), Error> { + Self::open_raw(path.as_os_str()) + } + + fn open_url(url: &Url) -> Result<(), Error> { + if cfg!(windows) { + let escaped = format!("\"{}\"", url); + Self::open_raw(escaped.as_str().as_ref()) + } else { + Self::open_raw(url.as_str().as_ref()) + } + } + + fn open_raw(target: &OsStr) -> Result<(), Error> { let mut command = Self::opener()?; - command.push(OsString::from(path)); + command.push(OsString::from(target)); let command_string = || { command diff --git a/src/subcommand/torrent/create.rs b/src/subcommand/torrent/create.rs index 2728b7e8..c3d1ff92 100644 --- a/src/subcommand/torrent/create.rs +++ b/src/subcommand/torrent/create.rs @@ -140,7 +140,7 @@ pub(crate) struct Create { long = "open", short = "O", help = "Open `.torrent` file after creation. Uses `xdg-open`, `gnome-open`, or `kde-open` on \ - Linux; `open` on macOS; and `cmd /C start on Windows" + Linux; `open` on macOS; and `cmd /C start` on Windows" )] open: bool, #[structopt( @@ -396,7 +396,7 @@ impl Create { if let OutputTarget::File(path) = output { if self.open { - Platform::open(&path)?; + Platform::open_file(&path)?; } } diff --git a/src/subcommand/torrent/link.rs b/src/subcommand/torrent/link.rs index ba8ab45d..8b2eb332 100644 --- a/src/subcommand/torrent/link.rs +++ b/src/subcommand/torrent/link.rs @@ -15,6 +15,13 @@ pub(crate) struct Link { parse(from_os_str) )] input: PathBuf, + #[structopt( + long = "open", + short = "O", + help = "Open generated magnet link. Uses `xdg-open`, `gnome-open`, or `kde-open` on Linux; \ + `open` on macOS; and `cmd /C start` on Windows" + )] + open: bool, #[structopt( long = "peer", short = "p", @@ -47,7 +54,13 @@ impl Link { link.add_peer(peer); } - outln!(env, "{}", link.to_url())?; + let url = link.to_url(); + + outln!(env, "{}", url)?; + + if self.open { + Platform::open_url(&url)?; + } Ok(()) } @@ -229,4 +242,81 @@ mod tests { if path == env.resolve("foo.torrent") ); } + + #[test] + fn open() { + let mut create_env = test_env! { + args: [ + "torrent", + "create", + "--input", + "foo", + ], + tree: { + foo: "", + }, + }; + + assert_matches!(create_env.run(), Ok(())); + + let torrent = create_env.resolve("foo.torrent"); + + let mut env = test_env! { + args: [ + "torrent", + "link", + "--input", + &torrent, + "--open", + ], + tree: {}, + }; + + let opened = env.resolve("opened.txt"); + + let link = "magnet:?xt=urn:btih:516735f4b80f2b5487eed5f226075bdcde33a54e&dn=foo"; + + let expected = if cfg!(target_os = "windows") { + let script = env.resolve("open.bat"); + fs::write(&script, format!("echo > {}", opened.display())).unwrap(); + format!("ECHO is on.\r\n") + } else { + let script = env.resolve(&Platform::opener().unwrap()[0]); + fs::write( + &script, + format!("#!/usr/bin/env sh\necho $1 > {}", opened.display()), + ) + .unwrap(); + + Command::new("chmod") + .arg("+x") + .arg(&script) + .status() + .unwrap(); + + format!("{}\n", link) + }; + + const KEY: &str = "PATH"; + let path = env::var_os(KEY).unwrap(); + let mut split = env::split_paths(&path) + .into_iter() + .collect::>(); + split.insert(0, env.dir().to_owned()); + let new = env::join_paths(split).unwrap(); + env::set_var(KEY, new); + + assert_matches!(env.run(), Ok(())); + + let start = Instant::now(); + + while start.elapsed() < Duration::new(2, 0) { + if let Ok(text) = fs::read_to_string(&opened) { + assert_eq!(text, expected); + return; + } + } + + panic!("Failed to read `opened.txt`."); + } }