diff --git a/Cargo.lock b/Cargo.lock index 5bfd4bb..c9cc2dc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1083,6 +1083,7 @@ dependencies = [ "libcosmic", "log", "mpris-server", + "pico-args", "rust-embed", "serde", "smol_str", diff --git a/Cargo.toml b/Cargo.toml index 34f95d8..d18e5bc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ serde = { version = "1", features = ["serde_derive"] } tempfile = "3" tokio = "1" url = "2" +pico-args = "0.5" # Internationalization icu_collator = "1.5" icu_provider = { version = "1.5", features = ["sync"] } diff --git a/src/argparse.rs b/src/argparse.rs new file mode 100644 index 0000000..296ea6a --- /dev/null +++ b/src/argparse.rs @@ -0,0 +1,71 @@ +// Copyright 2024 System76 +// SPDX-License-Identifier: GPL-3.0-only + +use std::{fs, io}; + +use log::warn; +use url::Url; + +#[derive(Debug, Default)] +pub struct Arguments { + /// URLs to play with associated metadata + pub urls: Vec, +} + +impl Arguments { + pub fn from_args() -> Result { + let mut parser = pico_args::Arguments::from_env(); + + // Freestanding arguments are treated as URLs + let urls: Vec = std::iter::from_fn(|| { + parser + .opt_free_from_fn(|arg| Source::try_from(arg)) + .ok() + .flatten() + }) + .map(|source| source.0) + .collect(); + + let remainder = parser.finish(); + for arg in remainder { + warn!("Unused argument: {arg:?}"); + } + + Ok(Arguments { urls }) + } +} + +// #[derive(Debug)] +// pub enum Source { +// File(Url), +// Directory(Url), +// // TODO: GStreamer handles streaming out of the box +// Other(Url), +// } + +struct Source(Url); + +impl TryFrom<&str> for Source { + type Error = io::Error; + + fn try_from(arg: &str) -> Result { + match url::Url::parse(arg) { + Ok(url) => Ok(Source(url)), + Err(_) => match fs::canonicalize(arg) { + Ok(path) => { + match Url::from_file_path(&path).or_else(|_| Url::from_directory_path(&path)) { + Ok(url) => Ok(Source(url)), + Err(()) => { + warn!("failed to parse path {:?}", path); + Err(io::Error::other("Invalid URL and path")) + } + } + } + Err(err) => { + warn!("failed to parse argument {:?}: {}", arg, err); + Err(err) + } + }, + } + } +} diff --git a/src/main.rs b/src/main.rs index 170c947..44ba40c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -37,6 +37,7 @@ use crate::{ project::ProjectNode, }; +mod argparse; mod config; mod key_bind; mod localize; @@ -110,25 +111,8 @@ pub fn main() -> Result<(), Box> { settings = settings.theme(config.app_theme.theme()); settings = settings.size_limits(Limits::NONE.min_width(360.0).min_height(180.0)); - let url_opt = match std::env::args().nth(1) { - Some(arg) => match url::Url::parse(&arg) { - Ok(url) => Some(url), - Err(_) => match fs::canonicalize(&arg) { - Ok(path) => match url::Url::from_file_path(&path).or_else(|_| url::Url::from_directory_path(&path)) { - Ok(url) => Some(url), - Err(()) => { - log::warn!("failed to parse path {:?}", path); - None - } - }, - Err(err) => { - log::warn!("failed to parse argument {:?}: {}", arg, err); - None - } - }, - }, - None => None, - }; + let args = argparse::Arguments::from_args().unwrap_or_default(); + let url_opt = args.urls.into_iter().next(); let flags = Flags { config_handler,