diff --git a/Cargo.lock b/Cargo.lock index 04fcbd3d..fd1b0933 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -58,7 +58,7 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" dependencies = [ - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -68,7 +68,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" dependencies = [ "anstyle", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -140,7 +140,7 @@ dependencies = [ "js-sys", "num-traits", "wasm-bindgen", - "windows-targets", + "windows-targets 0.52.4", ] [[package]] @@ -205,7 +205,7 @@ dependencies = [ "lazy_static", "libc", "unicode-width", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -248,6 +248,27 @@ dependencies = [ "zeroize", ] +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + [[package]] name = "dlib" version = "0.5.2" @@ -282,7 +303,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -341,6 +362,17 @@ dependencies = [ "thread_local", ] +[[package]] +name = "getrandom" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "glob" version = "0.3.1" @@ -365,7 +397,7 @@ version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" dependencies = [ - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -466,7 +498,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ "cfg-if", - "windows-targets", + "windows-targets 0.52.4", +] + +[[package]] +name = "libredox" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +dependencies = [ + "bitflags 2.5.0", + "libc", + "redox_syscall", ] [[package]] @@ -594,6 +637,12 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "os_pipe" version = "1.1.5" @@ -601,7 +650,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57119c3b893986491ec9aa85056780d3a0f3cf4da7cc09dd3650dbd6c6738fb9" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -681,6 +730,26 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_users" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + [[package]] name = "rustix" version = "0.38.32" @@ -691,7 +760,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -700,6 +769,35 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" +[[package]] +name = "serde" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_spanned" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +dependencies = [ + "serde", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -715,6 +813,15 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" +[[package]] +name = "shellexpand" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da03fa3b94cc19e3ebfc88c4229c49d8f08cdbd1228870a45f0ffdf84988e14b" +dependencies = [ + "dirs", +] + [[package]] name = "simd-adler32" version = "0.3.7" @@ -753,7 +860,7 @@ dependencies = [ "cfg-if", "fastrand", "rustix", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -786,6 +893,40 @@ dependencies = [ "once_cell", ] +[[package]] +name = "toml" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e40bb779c5187258fd7aad0eb68cb8706a0a81fa712fbea808ab43c4b8374c4" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "tracing" version = "0.1.40" @@ -881,6 +1022,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "wasm-bindgen" version = "0.2.92" @@ -1015,11 +1162,15 @@ dependencies = [ "chrono", "clap", "dialoguer", + "dirs", "eyre", "flate2", "image", "libwayshot", "nix 0.28.0", + "serde", + "shellexpand", + "toml", "tracing", "tracing-subscriber", "wl-clipboard-rs", @@ -1062,7 +1213,16 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets", + "windows-targets 0.52.4", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", ] [[package]] @@ -1071,7 +1231,22 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.4", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -1080,57 +1255,108 @@ version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.4", + "windows_aarch64_msvc 0.52.4", + "windows_i686_gnu 0.52.4", + "windows_i686_msvc 0.52.4", + "windows_x86_64_gnu 0.52.4", + "windows_x86_64_gnullvm 0.52.4", + "windows_x86_64_msvc 0.52.4", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_aarch64_msvc" version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_gnu" version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_i686_msvc" version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnu" version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "windows_x86_64_msvc" version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" +[[package]] +name = "winnow" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8" +dependencies = [ + "memchr", +] + [[package]] name = "wl-clipboard-rs" version = "0.8.1" diff --git a/wayshot/Cargo.toml b/wayshot/Cargo.toml index 2835b246..41688da3 100644 --- a/wayshot/Cargo.toml +++ b/wayshot/Cargo.toml @@ -37,6 +37,10 @@ chrono = "0.4.35" wl-clipboard-rs = "0.8.0" nix = { version = "0.28.0", features = ["process"] } +toml = { version = "0.8.12", default-features = false, features = ["parse"] } +serde = { version = "1.0.197", features = ["derive"] } +dirs = "5.0.1" +shellexpand = "3.1.0" [[bin]] name = "wayshot" diff --git a/wayshot/config.toml b/wayshot/config.toml new file mode 100644 index 00000000..082d41b8 --- /dev/null +++ b/wayshot/config.toml @@ -0,0 +1,23 @@ +# base screenshot properties +[base] +# output to take screenshot +# output = "default" +# should contain cursor? +cursor = false +# should copy screenshot to clipborad? +clipboard = false +# should write screenshot as file in filesystem? +file = true +# should write screenshot in stdout? +stdout = false +# max logging level +log_level = "info" + +# properties related to writing screenshot as file in filesystem +[file] +# screenshots directory +# path = "$HOME/images/screenshots" +# screenshot filename format +format = "wayshot-%Y_%m_%d-%H_%M_%S" +# screenshot file encoding +encoding = "png" diff --git a/wayshot/src/cli.rs b/wayshot/src/cli.rs index 0ad61c23..0a159a58 100644 --- a/wayshot/src/cli.rs +++ b/wayshot/src/cli.rs @@ -1,43 +1,46 @@ -use std::path::PathBuf; - -use clap::arg; - -use clap::Parser; -use eyre::WrapErr; - use crate::utils::EncodingFormat; -use clap::builder::TypedValueParser; +use clap::Parser; +use std::path::PathBuf; +use tracing::Level; #[derive(Parser)] #[command(version, about)] pub struct Cli { - /// Custom output path can be of the following types: - /// 1. Directory (Default naming scheme is used for the image output). + /// Custom screenshot file path can be of the following types: + /// 1. Directory (Default naming scheme is used for the image screenshot file). /// 2. Path (Encoding is automatically inferred from the extension). - /// 3. `-` (Indicates writing to terminal [stdout]). - #[arg(value_name = "OUTPUT", verbatim_doc_comment)] + /// 3. None (the screenshot file with default filename_format will be created in current directory) + #[arg(value_name = "FILE", verbatim_doc_comment)] pub file: Option, - /// Copy image to clipboard. Can be used simultaneously with [OUTPUT] or stdout. + /// Write screenshot to terminal/stdout. + /// Defaults to config value (`false`) + #[arg(long, verbatim_doc_comment)] + pub stdout: Option, + + /// Copy image to clipboard. Can be used simultaneously with [FILE] or stdout. /// Wayshot persists in the background offering the image till the clipboard is overwritten. + /// Defaults to config value (`false`) #[arg(long, verbatim_doc_comment)] - pub clipboard: bool, + pub clipboard: Option, /// Log level to be used for printing to stderr - #[arg(long, default_value = "info", value_parser = clap::builder::PossibleValuesParser::new(["trace", "debug", "info", "warn", "error"]).map(|s| -> tracing::Level{ s.parse().wrap_err_with(|| format!("Failed to parse log level: {}", s)).unwrap()}))] - pub log_level: tracing::Level, + /// Defaults to config value (`info`) + #[arg(long, verbatim_doc_comment)] + pub log_level: Option, /// Arguments to call slurp with for selecting a region #[arg(short, long, value_name = "SLURP_ARGS")] pub slurp: Option, - /// Enable cursor in screenshots - #[arg(short, long)] - pub cursor: bool, + /// Enable cursor in screenshots. + /// Defaults to config value (`false`) + #[arg(short, long, verbatim_doc_comment)] + pub cursor: Option, - /// Set image encoder, by default uses the file extension from the OUTPUT - /// positional argument. Otherwise defaults to png. - #[arg(long, verbatim_doc_comment, visible_aliases = ["extension", "format", "output-format"], value_name = "FILE_EXTENSION")] + /// Set image encoder, by default uses the file extension from the FILE + /// positional argument. Otherwise defaults to config value (`png`). + #[arg(long, verbatim_doc_comment, visible_aliases = ["extension", "format", "file-format"], value_name = "FILE_EXTENSION")] pub encoding: Option, /// List all valid outputs @@ -51,4 +54,17 @@ pub struct Cli { /// Present a fuzzy selector for output/display selection #[arg(long, alias = "chooseoutput", conflicts_with_all = ["slurp", "output"])] pub choose_output: bool, + + /// Path to your config file. + /// Defaults to: + /// 1. `$XDG_CONFIG_HOME/wayshot/config.toml` + /// 2. `$HOME/wayshot/config.toml` -- if `$XDG_CONFIG_HOME` variable doesn't exist + /// 3. `None` -- if the config isn't found, the `Config::default()` will be used + #[arg(long, verbatim_doc_comment)] + pub config: Option, + + /// Output filename's formatting. + /// Defaults to config value (`wayshot-%Y_%m_%d-%H_%M_%S`) + #[arg(long, verbatim_doc_comment)] + pub filename_format: Option, } diff --git a/wayshot/src/config.rs b/wayshot/src/config.rs new file mode 100644 index 00000000..f9b8bde9 --- /dev/null +++ b/wayshot/src/config.rs @@ -0,0 +1,90 @@ +use crate::utils::EncodingFormat; +use serde::{Deserialize, Serialize}; +use std::{env, io::Read, path::PathBuf}; +use tracing::Level; + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct Config { + pub base: Option, + pub file: Option, +} + +impl Default for Config { + fn default() -> Self { + Config { + base: Some(Base::default()), + file: Some(File::default()), + } + } +} + +impl Config { + pub fn load(path: &PathBuf) -> Option { + let mut config_file = std::fs::File::open(path).ok()?; + let mut config_str = String::new(); + config_file.read_to_string(&mut config_str).ok()?; + + toml::from_str(&config_str).ok()? + } + + pub fn get_default_path() -> PathBuf { + dirs::config_local_dir() + .map(|path| path.join("wayshot").join("config.toml")) + .unwrap_or_default() + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct Base { + pub output: Option, + pub cursor: Option, + pub clipboard: Option, + pub file: Option, + pub stdout: Option, + pub log_level: Option, +} + +impl Default for Base { + fn default() -> Self { + Base { + output: None, + cursor: Some(false), + clipboard: Some(false), + file: Some(true), + stdout: Some(false), + log_level: Some("info".to_string()), + } + } +} + +impl Base { + pub fn get_log_level(&self) -> Level { + self.log_level + .as_ref() + .map_or(Level::INFO, |level| match level.as_str() { + "trace" => Level::TRACE, + "debug" => Level::DEBUG, + "info" => Level::INFO, + "warn" => Level::WARN, + "error" => Level::ERROR, + _ => Level::INFO, + }) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct File { + pub path: Option, + pub format: Option, + pub encoding: Option, +} + +impl Default for File { + fn default() -> Self { + File { + path: Some(env::current_dir().unwrap_or_default()), + format: Some("wayshot-%Y_%m_%d-%H_%M_%S".to_string()), + encoding: Some(EncodingFormat::Png), + } + } +} diff --git a/wayshot/src/utils.rs b/wayshot/src/utils.rs index 1f8bf8b4..b4f3a040 100644 --- a/wayshot/src/utils.rs +++ b/wayshot/src/utils.rs @@ -1,10 +1,14 @@ +use chrono::Local; use clap::ValueEnum; use eyre::{bail, ContextCompat, Error, Result}; - -use std::{fmt::Display, path::PathBuf, str::FromStr}; - -use chrono::{DateTime, Local}; use libwayshot::region::{LogicalRegion, Position, Region, Size}; +use serde::{Deserialize, Serialize}; +use std::{ + env, + fmt::{Display, Write}, + path::{Path, PathBuf}, + str::FromStr, +}; pub fn parse_geometry(g: &str) -> Result { let tail = g.trim(); @@ -48,7 +52,8 @@ pub fn parse_geometry(g: &str) -> Result { } /// Supported image encoding formats. -#[derive(Debug, Copy, Clone, PartialEq, Eq, ValueEnum)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, ValueEnum, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] pub enum EncodingFormat { /// JPG/JPEG encoder. Jpg, @@ -128,14 +133,45 @@ impl FromStr for EncodingFormat { "png" => Self::Png, "ppm" => Self::Ppm, "qoi" => Self::Qoi, + "webp" => Self::Webp, _ => bail!("unsupported extension '{s}'"), }) } } -pub fn get_default_file_name(extension: EncodingFormat) -> PathBuf { - let current_datetime: DateTime = Local::now(); - let formated_time = format!("{}", current_datetime.format("%Y_%m_%d-%H_%M_%S")); +pub fn get_checked_path(path: &str) -> PathBuf { + let checked_path = shellexpand::full(path); + + if let Ok(checked_path) = checked_path { + PathBuf::from(checked_path.into_owned()) + } else { + env::current_dir().unwrap_or_default() + } +} + +pub fn get_full_file_name(dir: &Path, filename_format: &str, encoding: EncodingFormat) -> PathBuf { + let filename = get_default_file_name(filename_format, encoding); - format!("wayshot-{formated_time}.{extension}").into() + let checked_path = get_checked_path(dir.to_str().unwrap_or_default()); + checked_path.join(filename) +} + +pub fn get_default_file_name(filename_format: &str, encoding: EncodingFormat) -> PathBuf { + let now = Local::now(); + let format = now.format(filename_format); + + let mut file_name = String::new(); + let write_result = write!(file_name, "{format}.{encoding}"); + + if write_result.is_ok() { + file_name.into() + } else { + tracing::warn!( + "Couldn't write proposed filename_format: '{filename_format}', using default value." + ); + + let format = now.format("wayshot-%Y_%m_%d-%H_%M_%S"); + + format!("{format}.{encoding}").into() + } } diff --git a/wayshot/src/wayshot.rs b/wayshot/src/wayshot.rs index 2d25e098..78641a01 100644 --- a/wayshot/src/wayshot.rs +++ b/wayshot/src/wayshot.rs @@ -1,22 +1,20 @@ -use std::{ - io::{stdout, BufWriter, Cursor, Write}, - process::Command, -}; - use clap::Parser; +use config::Config; +use dialoguer::{theme::ColorfulTheme, FuzzySelect}; use eyre::{bail, Result}; use libwayshot::{region::LogicalRegion, WayshotConnection}; +use nix::unistd::{fork, ForkResult}; +use std::{ + env, + io::{self, BufWriter, Cursor, Write}, + process::Command, +}; +use wl_clipboard_rs::copy::{MimeType, Options, Source}; mod cli; +mod config; mod utils; -use dialoguer::{theme::ColorfulTheme, FuzzySelect}; -use utils::EncodingFormat; - -use wl_clipboard_rs::copy::{MimeType, Options, Source}; - -use nix::unistd::{fork, ForkResult}; - fn select_ouput(ouputs: &[T]) -> Option where T: ToString, @@ -33,28 +31,79 @@ where } fn main() -> Result<()> { + // cli args let cli = cli::Cli::parse(); + + // config path + let config_path = cli.config.unwrap_or(Config::get_default_path()); + + // config + let config = Config::load(&config_path).unwrap_or_default(); + let base = config.base.unwrap_or_default(); + let file = config.file.unwrap_or_default(); + + // pre-work vars definitions + let log_level = cli.log_level.unwrap_or(base.get_log_level()); tracing_subscriber::fmt() - .with_max_level(cli.log_level) - .with_writer(std::io::stderr) + .with_max_level(log_level) + .with_writer(io::stderr) .init(); + let cursor = cli.cursor.unwrap_or(base.cursor.unwrap_or_default()); + let clipboard = cli.clipboard.unwrap_or(base.clipboard.unwrap_or_default()); + let filename_format = cli.filename_format.unwrap_or( + file.format + .unwrap_or("wayshot-%Y_%m_%d-%H_%M_%S".to_string()), + ); + let input_encoding = cli .file .as_ref() .and_then(|pathbuf| pathbuf.try_into().ok()); - let requested_encoding = cli - .encoding - .or(input_encoding) - .unwrap_or(EncodingFormat::default()); + let requested_encoding = cli.encoding.or(input_encoding); + let encoding = match requested_encoding { + Some(e) => { + if let Some(input_encoding) = input_encoding { + if input_encoding.ne(&e) { + tracing::warn!( + "The encoding requested '{e}' does not match the output file's encoding '{input_encoding}'. Still using the requested encoding however.", + ); + } - if let Some(input_encoding) = input_encoding { - if input_encoding != requested_encoding { - tracing::warn!( - "The encoding requested '{requested_encoding}' does not match the output file's encoding '{input_encoding}'. Still using the requested encoding however.", - ); + input_encoding + } else { + e + } } - } + _ => file.encoding.unwrap_or_default(), + }; + + let stdout_print = cli.stdout.unwrap_or(base.stdout.unwrap_or_default()); + let file_path = match cli.file { + Some(mut f) => { + if f.is_dir() { + Some(utils::get_full_file_name(&f, &filename_format, encoding)) + } else { + f.set_extension(""); + let dir = file.path.unwrap_or(env::current_dir().unwrap_or_default()); + Some(utils::get_full_file_name( + &dir, + f.to_str().unwrap(), + encoding, + )) + } + } + _ => { + if base.file.unwrap_or_default() { + let dir = file.path.unwrap_or(env::current_dir().unwrap_or_default()); + Some(utils::get_full_file_name(&dir, &filename_format, encoding)) + } else { + None + } + } + }; + + let output = cli.output.or(base.output); let wayshot_conn = WayshotConnection::new()?; @@ -66,6 +115,7 @@ fn main() -> Result<()> { return Ok(()); } + // take a screenshot let image_buffer = if let Some(slurp_region) = cli.slurp { let slurp_region = slurp_region.clone(); wayshot_conn.screenshot_freeze( @@ -80,12 +130,12 @@ fn main() -> Result<()> { }() .map_err(|_| libwayshot::Error::FreezeCallbackError) }), - cli.cursor, + cursor, )? - } else if let Some(output_name) = cli.output { + } else if let Some(output_name) = output { let outputs = wayshot_conn.get_all_outputs(); if let Some(output) = outputs.iter().find(|output| output.name == output_name) { - wayshot_conn.screenshot_single_output(output, cli.cursor)? + wayshot_conn.screenshot_single_output(output, cursor)? } else { bail!("No output found!"); } @@ -96,54 +146,35 @@ fn main() -> Result<()> { .map(|display| display.name.as_str()) .collect(); if let Some(index) = select_ouput(&output_names) { - wayshot_conn.screenshot_single_output(&outputs[index], cli.cursor)? + wayshot_conn.screenshot_single_output(&outputs[index], cursor)? } else { bail!("No output found!"); } } else { - wayshot_conn.screenshot_all(cli.cursor)? - }; - - let mut stdout_print = false; - let file = match cli.file { - Some(mut pathbuf) => { - if pathbuf.to_string_lossy() == "-" { - stdout_print = true; - None - } else { - if pathbuf.is_dir() { - pathbuf.push(utils::get_default_file_name(requested_encoding)); - } - Some(pathbuf) - } - } - None => { - if cli.clipboard { - None - } else { - Some(utils::get_default_file_name(requested_encoding)) - } - } + wayshot_conn.screenshot_all(cursor)? }; + // save the screenshot data let mut image_buf: Option>> = None; - if let Some(file) = file { - image_buffer.save(file)?; - } else if stdout_print { + if let Some(file_path) = file_path { + image_buffer.save(file_path)?; + } + + if stdout_print { let mut buffer = Cursor::new(Vec::new()); - image_buffer.write_to(&mut buffer, requested_encoding)?; - let stdout = stdout(); + image_buffer.write_to(&mut buffer, encoding)?; + let stdout = io::stdout(); let mut writer = BufWriter::new(stdout.lock()); writer.write_all(buffer.get_ref())?; image_buf = Some(buffer); } - if cli.clipboard { + if clipboard { clipboard_daemonize(match image_buf { Some(buf) => buf, None => { let mut buffer = Cursor::new(Vec::new()); - image_buffer.write_to(&mut buffer, requested_encoding)?; + image_buffer.write_to(&mut buffer, encoding)?; buffer } })?;