mirror of
				https://github.com/lelgenio/dhist.git
				synced 2025-11-03 20:37:37 -03:00 
			
		
		
		
	init
This commit is contained in:
		
						commit
						3ecf8c981d
					
				
					 12 changed files with 832 additions and 0 deletions
				
			
		
							
								
								
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1 @@
 | 
				
			||||||
 | 
					target
 | 
				
			||||||
							
								
								
									
										216
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										216
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,216 @@
 | 
				
			||||||
 | 
					# This file is automatically @generated by Cargo.
 | 
				
			||||||
 | 
					# It is not intended for manual editing.
 | 
				
			||||||
 | 
					version = 3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "atty"
 | 
				
			||||||
 | 
					version = "0.2.14"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "hermit-abi",
 | 
				
			||||||
 | 
					 "libc",
 | 
				
			||||||
 | 
					 "winapi",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "autocfg"
 | 
				
			||||||
 | 
					version = "1.1.0"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "bitflags"
 | 
				
			||||||
 | 
					version = "1.3.2"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "cfg-if"
 | 
				
			||||||
 | 
					version = "1.0.0"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "clap"
 | 
				
			||||||
 | 
					version = "3.1.0"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "e5f1fea81f183005ced9e59cdb01737ef2423956dac5a6d731b06b2ecfaa3467"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "atty",
 | 
				
			||||||
 | 
					 "bitflags",
 | 
				
			||||||
 | 
					 "indexmap",
 | 
				
			||||||
 | 
					 "lazy_static",
 | 
				
			||||||
 | 
					 "os_str_bytes",
 | 
				
			||||||
 | 
					 "strsim",
 | 
				
			||||||
 | 
					 "termcolor",
 | 
				
			||||||
 | 
					 "textwrap",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "dhist"
 | 
				
			||||||
 | 
					version = "0.1.0"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "clap",
 | 
				
			||||||
 | 
					 "dirs",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "dirs"
 | 
				
			||||||
 | 
					version = "4.0.0"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "dirs-sys",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "dirs-sys"
 | 
				
			||||||
 | 
					version = "0.3.6"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "libc",
 | 
				
			||||||
 | 
					 "redox_users",
 | 
				
			||||||
 | 
					 "winapi",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "getrandom"
 | 
				
			||||||
 | 
					version = "0.2.4"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "418d37c8b1d42553c93648be529cb70f920d3baf8ef469b74b9638df426e0b4c"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "cfg-if",
 | 
				
			||||||
 | 
					 "libc",
 | 
				
			||||||
 | 
					 "wasi",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "hashbrown"
 | 
				
			||||||
 | 
					version = "0.11.2"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "hermit-abi"
 | 
				
			||||||
 | 
					version = "0.1.19"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "libc",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "indexmap"
 | 
				
			||||||
 | 
					version = "1.8.0"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "autocfg",
 | 
				
			||||||
 | 
					 "hashbrown",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "lazy_static"
 | 
				
			||||||
 | 
					version = "1.4.0"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "libc"
 | 
				
			||||||
 | 
					version = "0.2.118"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "06e509672465a0504304aa87f9f176f2b2b716ed8fb105ebe5c02dc6dce96a94"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "memchr"
 | 
				
			||||||
 | 
					version = "2.4.1"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "os_str_bytes"
 | 
				
			||||||
 | 
					version = "6.0.0"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "memchr",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "redox_syscall"
 | 
				
			||||||
 | 
					version = "0.2.10"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "bitflags",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "redox_users"
 | 
				
			||||||
 | 
					version = "0.4.0"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "getrandom",
 | 
				
			||||||
 | 
					 "redox_syscall",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "strsim"
 | 
				
			||||||
 | 
					version = "0.10.0"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "termcolor"
 | 
				
			||||||
 | 
					version = "1.1.2"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "winapi-util",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "textwrap"
 | 
				
			||||||
 | 
					version = "0.14.2"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "wasi"
 | 
				
			||||||
 | 
					version = "0.10.2+wasi-snapshot-preview1"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "winapi"
 | 
				
			||||||
 | 
					version = "0.3.9"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "winapi-i686-pc-windows-gnu",
 | 
				
			||||||
 | 
					 "winapi-x86_64-pc-windows-gnu",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "winapi-i686-pc-windows-gnu"
 | 
				
			||||||
 | 
					version = "0.4.0"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "winapi-util"
 | 
				
			||||||
 | 
					version = "0.1.5"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "winapi",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "winapi-x86_64-pc-windows-gnu"
 | 
				
			||||||
 | 
					version = "0.4.0"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 | 
				
			||||||
							
								
								
									
										15
									
								
								Cargo.toml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								Cargo.toml
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,15 @@
 | 
				
			||||||
 | 
					[package]
 | 
				
			||||||
 | 
					name = "dhist"
 | 
				
			||||||
 | 
					description = "Save and sort most often used dmenu-like input"
 | 
				
			||||||
 | 
					authors = ["Leonardo Eugênio <lelgeio@disroot.org>"]
 | 
				
			||||||
 | 
					license = "GPL-3.0"
 | 
				
			||||||
 | 
					version = "0.1.1"
 | 
				
			||||||
 | 
					edition = "2021"
 | 
				
			||||||
 | 
					readme = "README.md"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[dependencies]
 | 
				
			||||||
 | 
					dirs = "4.0.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[dependencies.clap]
 | 
				
			||||||
 | 
					version = "3.0.0-beta.4"
 | 
				
			||||||
 | 
					features = ["color", "cargo"]
 | 
				
			||||||
							
								
								
									
										29
									
								
								README.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								README.md
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,29 @@
 | 
				
			||||||
 | 
					# dhist
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Save and sort most often used dmenu-like input
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					USAGE:
 | 
				
			||||||
 | 
					    dhist [OPTIONS] <SUBCOMMAND>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					OPTIONS:
 | 
				
			||||||
 | 
					    -h, --help       Print help information
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SUBCOMMANDS:
 | 
				
			||||||
 | 
					    help         Print this message or the help of the given subcommand(s)
 | 
				
			||||||
 | 
					    increment    Increase usage of input by 1
 | 
				
			||||||
 | 
					    query        Print history
 | 
				
			||||||
 | 
					    sort         Sort input by history frequency
 | 
				
			||||||
 | 
					    wrap         Wrap a command to sort before and increment after
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Examples
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					# sort input of dmenu based on usage
 | 
				
			||||||
 | 
					printf "%s\n" hello world | dhist wrap -- dmenu
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# same as above, but more verbose
 | 
				
			||||||
 | 
					# dhist increment also prints out it's input, so you can still use it for another program
 | 
				
			||||||
 | 
					printf "%s\n" hello world | dhist sort | dmenu | dhist increment
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
							
								
								
									
										3
									
								
								rust-toolchain.toml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								rust-toolchain.toml
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,3 @@
 | 
				
			||||||
 | 
					[toolchain]
 | 
				
			||||||
 | 
					channel = "stable-2022-01-20"
 | 
				
			||||||
 | 
					targets = [ "x86_64-unknown-linux-gnu", ]
 | 
				
			||||||
							
								
								
									
										65
									
								
								src/cli.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								src/cli.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,65 @@
 | 
				
			||||||
 | 
					use clap::{
 | 
				
			||||||
 | 
					    Command,
 | 
				
			||||||
 | 
					    Arg,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn get_cli() -> clap::Command<'static> {
 | 
				
			||||||
 | 
					    Command::new("dhist")
 | 
				
			||||||
 | 
					        .about(clap::crate_description!())
 | 
				
			||||||
 | 
					        .author(clap::crate_authors!())
 | 
				
			||||||
 | 
					        .arg_required_else_help(true)
 | 
				
			||||||
 | 
					        // .arg(
 | 
				
			||||||
 | 
					        //     Arg::new("histfile-path")
 | 
				
			||||||
 | 
					        //         .short('H')
 | 
				
			||||||
 | 
					        //         .long("history")
 | 
				
			||||||
 | 
					        //         .about("Path to history file"),
 | 
				
			||||||
 | 
					        // )
 | 
				
			||||||
 | 
					        .subcommand(Command::new(Mode::Query).about("Print history"))
 | 
				
			||||||
 | 
					        .subcommand(
 | 
				
			||||||
 | 
					            Command::new(Mode::Sort).about("Sort input by history frequency"),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        .subcommand(
 | 
				
			||||||
 | 
					            Command::new(Mode::Increment).about("Increase usage of input by 1"),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        .subcommand(
 | 
				
			||||||
 | 
					            Command::new(Mode::Wrap)
 | 
				
			||||||
 | 
					                .about("Wrap a command to sort before and increment after")
 | 
				
			||||||
 | 
					                .arg_required_else_help(true)
 | 
				
			||||||
 | 
					                .arg(
 | 
				
			||||||
 | 
					                    Arg::new("command")
 | 
				
			||||||
 | 
					                        .required(true)
 | 
				
			||||||
 | 
					                        .multiple_values(true)
 | 
				
			||||||
 | 
					                        .last(true),
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub enum Mode {
 | 
				
			||||||
 | 
					    Query,
 | 
				
			||||||
 | 
					    Sort,
 | 
				
			||||||
 | 
					    Increment,
 | 
				
			||||||
 | 
					    Wrap,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl From<Mode> for String {
 | 
				
			||||||
 | 
					    fn from(val: Mode) -> Self {
 | 
				
			||||||
 | 
					        match val {
 | 
				
			||||||
 | 
					            Mode::Query => "query".to_string(),
 | 
				
			||||||
 | 
					            Mode::Sort => "sort".to_string(),
 | 
				
			||||||
 | 
					            Mode::Increment => "increment".to_string(),
 | 
				
			||||||
 | 
					            Mode::Wrap => "wrap".to_string(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl From<&str> for Mode {
 | 
				
			||||||
 | 
					    fn from(val: &str) -> Self {
 | 
				
			||||||
 | 
					        match val {
 | 
				
			||||||
 | 
					            "query" => Mode::Query,
 | 
				
			||||||
 | 
					            "sort" => Mode::Sort,
 | 
				
			||||||
 | 
					            "increment" => Mode::Increment,
 | 
				
			||||||
 | 
					            "wrap" => Mode::Wrap,
 | 
				
			||||||
 | 
					            _ => unreachable!(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										67
									
								
								src/db.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								src/db.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,67 @@
 | 
				
			||||||
 | 
					use std::{
 | 
				
			||||||
 | 
					    fs::File,
 | 
				
			||||||
 | 
					    io::{
 | 
				
			||||||
 | 
					        BufRead,
 | 
				
			||||||
 | 
					        BufReader,
 | 
				
			||||||
 | 
					        BufWriter,
 | 
				
			||||||
 | 
					        Write,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    path::PathBuf,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use dirs::data_dir;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::{
 | 
				
			||||||
 | 
					    HistoryItem,
 | 
				
			||||||
 | 
					    HistoryResults,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn get_db_path() -> PathBuf {
 | 
				
			||||||
 | 
					    if let Ok(histfile_path) = std::env::var("DMENU_HISTORY_FILE") {
 | 
				
			||||||
 | 
					        PathBuf::from(histfile_path)
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        data_dir()
 | 
				
			||||||
 | 
					            .expect("Could not get a data dir")
 | 
				
			||||||
 | 
					            .join("dmenu-history")
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn read_history(file: &File) -> HistoryResults {
 | 
				
			||||||
 | 
					    let reader = BufReader::new(file);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    reader
 | 
				
			||||||
 | 
					        .lines()
 | 
				
			||||||
 | 
					        .flatten()
 | 
				
			||||||
 | 
					        .filter_map(|line| -> Option<HistoryItem> {
 | 
				
			||||||
 | 
					            let (count, value) = line.split_once(" ")?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Some(HistoryItem {
 | 
				
			||||||
 | 
					                count: count.parse::<i32>().ok()?,
 | 
				
			||||||
 | 
					                value: value.to_string(),
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        .collect::<Vec<_>>()
 | 
				
			||||||
 | 
					        .into()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn get_history() -> HistoryResults {
 | 
				
			||||||
 | 
					    let mut hist = match &File::open(get_db_path()) {
 | 
				
			||||||
 | 
					        Ok(file) => read_history(file),
 | 
				
			||||||
 | 
					        Err(_) => Vec::new().into(),
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    hist.0.sort_by_key(|i| -i.count);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    hist
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn put_history(res: HistoryResults) {
 | 
				
			||||||
 | 
					    let file = File::create(get_db_path()).expect("Cannot write to data dir");
 | 
				
			||||||
 | 
					    let mut history_writer = BufWriter::new(file);
 | 
				
			||||||
 | 
					    for HistoryItem { count, value } in res.0 {
 | 
				
			||||||
 | 
					        let newline = format!("{} {}\n", count, value);
 | 
				
			||||||
 | 
					        history_writer
 | 
				
			||||||
 | 
					            .write_all(newline.as_bytes())
 | 
				
			||||||
 | 
					            .expect("Cannot write to history file");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										118
									
								
								src/increment.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								src/increment.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,118 @@
 | 
				
			||||||
 | 
					use crate::{
 | 
				
			||||||
 | 
					    HistoryItem,
 | 
				
			||||||
 | 
					    HistoryResults,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn update_history(
 | 
				
			||||||
 | 
					    input: Vec<String>,
 | 
				
			||||||
 | 
					    history: &HistoryResults,
 | 
				
			||||||
 | 
					) -> HistoryResults {
 | 
				
			||||||
 | 
					    use std::collections::HashMap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let input_lines = input
 | 
				
			||||||
 | 
					        .into_iter()
 | 
				
			||||||
 | 
					        // Here we copy stdin to stdout so other programs can use it
 | 
				
			||||||
 | 
					        .inspect(|i| println!("{}", i))
 | 
				
			||||||
 | 
					        .map(|k| HistoryItem { value: k, count: 1 })
 | 
				
			||||||
 | 
					        .collect::<Vec<_>>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut res: HashMap<String, i32> = HashMap::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    history.0.iter().chain(input_lines.iter()).for_each(
 | 
				
			||||||
 | 
					        |hist_item: &HistoryItem| {
 | 
				
			||||||
 | 
					            let entry = res
 | 
				
			||||||
 | 
					                .entry(hist_item.value.clone())
 | 
				
			||||||
 | 
					                .or_insert(hist_item.count - 1);
 | 
				
			||||||
 | 
					            *entry += 1;
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    res.into_iter()
 | 
				
			||||||
 | 
					        .map(|(k, v)| HistoryItem { value: k, count: v })
 | 
				
			||||||
 | 
					        .collect::<Vec<_>>()
 | 
				
			||||||
 | 
					        .into()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn run(
 | 
				
			||||||
 | 
					    history: HistoryResults,
 | 
				
			||||||
 | 
					    input_lines: Vec<String>,
 | 
				
			||||||
 | 
					) -> HistoryResults {
 | 
				
			||||||
 | 
					    // Read
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Proccess
 | 
				
			||||||
 | 
					    update_history(input_lines, &history)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(test)]
 | 
				
			||||||
 | 
					mod tests {
 | 
				
			||||||
 | 
					    use super::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn increment_empty() {
 | 
				
			||||||
 | 
					        let input = vec![].into();
 | 
				
			||||||
 | 
					        let history = vec![].into();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        update_history(input, &history);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert_eq!(history, vec![].into());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn increment_keeps_history() {
 | 
				
			||||||
 | 
					        let input = vec![].into();
 | 
				
			||||||
 | 
					        let history = vec![HistoryItem {
 | 
				
			||||||
 | 
					            count: 1,
 | 
				
			||||||
 | 
					            value: "one".to_string(),
 | 
				
			||||||
 | 
					        }]
 | 
				
			||||||
 | 
					        .into();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        update_history(input, &history);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert_eq!(
 | 
				
			||||||
 | 
					            history,
 | 
				
			||||||
 | 
					            vec![HistoryItem {
 | 
				
			||||||
 | 
					                count: 1,
 | 
				
			||||||
 | 
					                value: "one".to_string(),
 | 
				
			||||||
 | 
					            },]
 | 
				
			||||||
 | 
					            .into()
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn increment_empty_history() {
 | 
				
			||||||
 | 
					        let input = vec!["one".to_string()].into();
 | 
				
			||||||
 | 
					        let history = vec![].into();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let res = update_history(input, &history);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert_eq!(
 | 
				
			||||||
 | 
					            res,
 | 
				
			||||||
 | 
					            vec![HistoryItem {
 | 
				
			||||||
 | 
					                count: 1,
 | 
				
			||||||
 | 
					                value: "one".to_string(),
 | 
				
			||||||
 | 
					            },]
 | 
				
			||||||
 | 
					            .into()
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn increment_history() {
 | 
				
			||||||
 | 
					        let input = vec!["one".to_string()].into();
 | 
				
			||||||
 | 
					        let history = vec![HistoryItem {
 | 
				
			||||||
 | 
					            count: 1,
 | 
				
			||||||
 | 
					            value: "one".to_string(),
 | 
				
			||||||
 | 
					        }]
 | 
				
			||||||
 | 
					        .into();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let res = update_history(input, &history);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert_eq!(
 | 
				
			||||||
 | 
					            res,
 | 
				
			||||||
 | 
					            vec![HistoryItem {
 | 
				
			||||||
 | 
					                count: 2,
 | 
				
			||||||
 | 
					                value: "one".to_string(),
 | 
				
			||||||
 | 
					            },]
 | 
				
			||||||
 | 
					            .into()
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										67
									
								
								src/main.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								src/main.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,67 @@
 | 
				
			||||||
 | 
					use cli::Mode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					mod cli;
 | 
				
			||||||
 | 
					mod db;
 | 
				
			||||||
 | 
					mod increment;
 | 
				
			||||||
 | 
					mod sort;
 | 
				
			||||||
 | 
					mod wrap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
 | 
				
			||||||
 | 
					pub struct HistoryItem {
 | 
				
			||||||
 | 
					    count: i32,
 | 
				
			||||||
 | 
					    value: String,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, PartialEq, Eq, Clone)]
 | 
				
			||||||
 | 
					pub struct HistoryResults(Vec<HistoryItem>);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl From<Vec<HistoryItem>> for HistoryResults {
 | 
				
			||||||
 | 
					    fn from(val: Vec<HistoryItem>) -> Self {
 | 
				
			||||||
 | 
					        HistoryResults(val)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn read_lines() -> Vec<String> {
 | 
				
			||||||
 | 
					    use std::io::{
 | 
				
			||||||
 | 
					        stdin,
 | 
				
			||||||
 | 
					        BufRead,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    stdin().lock().lines().flatten().collect()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn main() {
 | 
				
			||||||
 | 
					    let matches = cli::get_cli().get_matches();
 | 
				
			||||||
 | 
					    let history = db::get_history();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    match matches.subcommand_name().unwrap().into() {
 | 
				
			||||||
 | 
					        Mode::Query => {
 | 
				
			||||||
 | 
					            let history = &history;
 | 
				
			||||||
 | 
					            for line in &history.0 {
 | 
				
			||||||
 | 
					                println!("{} {}", line.count, line.value);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        Mode::Sort => {
 | 
				
			||||||
 | 
					            let input_lines = read_lines();
 | 
				
			||||||
 | 
					            let history = &sort::run(history, input_lines);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for line in &history.0 {
 | 
				
			||||||
 | 
					                println!("{}", line.value);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        Mode::Increment => {
 | 
				
			||||||
 | 
					            let input_lines = read_lines();
 | 
				
			||||||
 | 
					            db::put_history(increment::run(history, input_lines));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        Mode::Wrap => {
 | 
				
			||||||
 | 
					            let input_lines = read_lines();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let args = matches.subcommand().unwrap().1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let cmd = args
 | 
				
			||||||
 | 
					                .values_of("command")
 | 
				
			||||||
 | 
					                .map(|x| x.collect::<Vec<_>>())
 | 
				
			||||||
 | 
					                .unwrap();
 | 
				
			||||||
 | 
					            db::put_history(wrap::run(history, input_lines, cmd));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										162
									
								
								src/sort.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								src/sort.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,162 @@
 | 
				
			||||||
 | 
					use crate::{
 | 
				
			||||||
 | 
					    HistoryItem,
 | 
				
			||||||
 | 
					    HistoryResults,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn increment_with(main: &mut HistoryResults, rhs: &HistoryResults) {
 | 
				
			||||||
 | 
					    for main_item in main.0.iter_mut() {
 | 
				
			||||||
 | 
					        for rhs_item in rhs.0.iter() {
 | 
				
			||||||
 | 
					            if rhs_item.value == main_item.value {
 | 
				
			||||||
 | 
					                main_item.count += rhs_item.count;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn results_from_vec(mut input: Vec<String>) -> HistoryResults {
 | 
				
			||||||
 | 
					    input.dedup();
 | 
				
			||||||
 | 
					    input
 | 
				
			||||||
 | 
					        .into_iter()
 | 
				
			||||||
 | 
					        .map(|item| HistoryItem {
 | 
				
			||||||
 | 
					            count: 1,
 | 
				
			||||||
 | 
					            value: item,
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        .collect::<Vec<_>>()
 | 
				
			||||||
 | 
					        .into()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn sort_lines(
 | 
				
			||||||
 | 
					    history: HistoryResults,
 | 
				
			||||||
 | 
					    mut input: HistoryResults,
 | 
				
			||||||
 | 
					) -> HistoryResults {
 | 
				
			||||||
 | 
					    increment_with(&mut input, &history);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Sort such that items with a bigger count are at the start
 | 
				
			||||||
 | 
					    input.0.sort_by_key(|i| -i.count);
 | 
				
			||||||
 | 
					    input
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn run(history: HistoryResults, stdin_read: Vec<String>) -> HistoryResults {
 | 
				
			||||||
 | 
					    let input = results_from_vec(stdin_read);
 | 
				
			||||||
 | 
					    sort_lines(history, input)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(test)]
 | 
				
			||||||
 | 
					mod tests {
 | 
				
			||||||
 | 
					    use super::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn history_stays_empty() {
 | 
				
			||||||
 | 
					        let input = vec![].into();
 | 
				
			||||||
 | 
					        let history = vec![].into();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let res = sort_lines(input, history);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert_eq!(res, vec![].into());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn history_stays_no_input() {
 | 
				
			||||||
 | 
					        let input = vec![].into();
 | 
				
			||||||
 | 
					        let history = vec![
 | 
				
			||||||
 | 
					            HistoryItem {
 | 
				
			||||||
 | 
					                count: 1,
 | 
				
			||||||
 | 
					                value: "sample text".to_string(),
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            HistoryItem {
 | 
				
			||||||
 | 
					                count: 2,
 | 
				
			||||||
 | 
					                value: "sample text".to_string(),
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            HistoryItem {
 | 
				
			||||||
 | 
					                count: 3,
 | 
				
			||||||
 | 
					                value: "sample text".to_string(),
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					        .into();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let res = sort_lines(history, input);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert_eq!(res, vec![].into());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn history_stays_no_history() {
 | 
				
			||||||
 | 
					        let input = vec![
 | 
				
			||||||
 | 
					            HistoryItem {
 | 
				
			||||||
 | 
					                count: 1,
 | 
				
			||||||
 | 
					                value: "sample text".to_string(),
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            HistoryItem {
 | 
				
			||||||
 | 
					                count: 2,
 | 
				
			||||||
 | 
					                value: "sample text".to_string(),
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            HistoryItem {
 | 
				
			||||||
 | 
					                count: 3,
 | 
				
			||||||
 | 
					                value: "sample text".to_string(),
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					        .into();
 | 
				
			||||||
 | 
					        let history = vec![].into();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let res = sort_lines(history, input);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert_eq!(
 | 
				
			||||||
 | 
					            res,
 | 
				
			||||||
 | 
					            vec![
 | 
				
			||||||
 | 
					                HistoryItem {
 | 
				
			||||||
 | 
					                    count: 3,
 | 
				
			||||||
 | 
					                    value: "sample text".to_string(),
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                HistoryItem {
 | 
				
			||||||
 | 
					                    count: 2,
 | 
				
			||||||
 | 
					                    value: "sample text".to_string(),
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                HistoryItem {
 | 
				
			||||||
 | 
					                    count: 1,
 | 
				
			||||||
 | 
					                    value: "sample text".to_string(),
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					            ]
 | 
				
			||||||
 | 
					            .into()
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn count_occurances() {
 | 
				
			||||||
 | 
					        let input =
 | 
				
			||||||
 | 
					            vec!["one".to_string(), "two".to_string(), "three".to_string()];
 | 
				
			||||||
 | 
					        let history = vec![
 | 
				
			||||||
 | 
					            HistoryItem {
 | 
				
			||||||
 | 
					                count: 1,
 | 
				
			||||||
 | 
					                value: "one".to_string(),
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            HistoryItem {
 | 
				
			||||||
 | 
					                count: 3,
 | 
				
			||||||
 | 
					                value: "two".to_string(),
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            HistoryItem {
 | 
				
			||||||
 | 
					                count: 1,
 | 
				
			||||||
 | 
					                value: "three".to_string(),
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					        .into();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let res = sort_lines(history, results_from_vec(input));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let expected: HistoryResults = vec![
 | 
				
			||||||
 | 
					            HistoryItem {
 | 
				
			||||||
 | 
					                count: 4,
 | 
				
			||||||
 | 
					                value: "two".to_string(),
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            HistoryItem {
 | 
				
			||||||
 | 
					                count: 2,
 | 
				
			||||||
 | 
					                value: "one".to_string(),
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            HistoryItem {
 | 
				
			||||||
 | 
					                count: 2,
 | 
				
			||||||
 | 
					                value: "three".to_string(),
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					        .into();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert_eq!(res, expected);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										0
									
								
								src/tests.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								src/tests.rs
									
										
									
									
									
										Normal file
									
								
							
							
								
								
									
										89
									
								
								src/wrap.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								src/wrap.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,89 @@
 | 
				
			||||||
 | 
					use std::{
 | 
				
			||||||
 | 
					    io::Write,
 | 
				
			||||||
 | 
					    process::{
 | 
				
			||||||
 | 
					        Command,
 | 
				
			||||||
 | 
					        Stdio,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::{
 | 
				
			||||||
 | 
					    increment,
 | 
				
			||||||
 | 
					    sort,
 | 
				
			||||||
 | 
					    HistoryResults,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn run(
 | 
				
			||||||
 | 
					    history: HistoryResults,
 | 
				
			||||||
 | 
					    input_lines: Vec<String>,
 | 
				
			||||||
 | 
					    cmd: Vec<&str>,
 | 
				
			||||||
 | 
					) -> HistoryResults {
 | 
				
			||||||
 | 
					    // let history = ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut arguments = cmd.into_iter().map(|f| f.to_string());
 | 
				
			||||||
 | 
					    let command = arguments.next().unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let command_input = sort::run(history.clone(), input_lines)
 | 
				
			||||||
 | 
					        .0
 | 
				
			||||||
 | 
					        .into_iter()
 | 
				
			||||||
 | 
					        .map(|f| f.value)
 | 
				
			||||||
 | 
					        .collect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let child_output =
 | 
				
			||||||
 | 
					        shell_command(command, arguments.collect(), command_input);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    increment::run(history, child_output)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn shell_command(
 | 
				
			||||||
 | 
					    command: String,
 | 
				
			||||||
 | 
					    args: Vec<String>,
 | 
				
			||||||
 | 
					    stdin: Vec<String>,
 | 
				
			||||||
 | 
					) -> Vec<String> {
 | 
				
			||||||
 | 
					    let mut child = Command::new(&command)
 | 
				
			||||||
 | 
					        .args(args)
 | 
				
			||||||
 | 
					        .stdin(Stdio::piped())
 | 
				
			||||||
 | 
					        .stdout(Stdio::piped())
 | 
				
			||||||
 | 
					        .spawn()
 | 
				
			||||||
 | 
					        .unwrap_or_else(|e| {
 | 
				
			||||||
 | 
					            panic!("failed to spawn program {}: {}", command, e)
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        let mut child_stdin = child
 | 
				
			||||||
 | 
					            .stdin
 | 
				
			||||||
 | 
					            .take()
 | 
				
			||||||
 | 
					            .unwrap_or_else(|| panic!("failed to get stdin of {}", command));
 | 
				
			||||||
 | 
					        for line in &stdin {
 | 
				
			||||||
 | 
					            let text = format!("{}\n", line);
 | 
				
			||||||
 | 
					            child_stdin.write_all(text.as_bytes()).ok();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    let child_output_bytes = child
 | 
				
			||||||
 | 
					        .wait_with_output()
 | 
				
			||||||
 | 
					        .unwrap_or_else(|e| {
 | 
				
			||||||
 | 
					            panic!("failed to get stdout of {}: {}", command, e)
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        .stdout;
 | 
				
			||||||
 | 
					    let child_output = std::str::from_utf8(&child_output_bytes)
 | 
				
			||||||
 | 
					        .unwrap_or_else(|e| {
 | 
				
			||||||
 | 
					            panic!("failed to read output of {}: {}", command, e)
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        .lines()
 | 
				
			||||||
 | 
					        .map(|i| i.to_string())
 | 
				
			||||||
 | 
					        .collect();
 | 
				
			||||||
 | 
					    child_output
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(test)]
 | 
				
			||||||
 | 
					mod tests {
 | 
				
			||||||
 | 
					    use super::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn basic_shell() {
 | 
				
			||||||
 | 
					        let command = "cat".to_string();
 | 
				
			||||||
 | 
					        let input = "hello\nworld!".lines().map(|x| x.to_string()).collect();
 | 
				
			||||||
 | 
					        let expected_output: Vec<String> =
 | 
				
			||||||
 | 
					            "hello\nworld!".lines().map(|x| x.to_string()).collect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert_eq!(shell_command(command, Vec::new(), input), expected_output)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue