Small Rust scripts

Switch to native cargo script

authored by stavola.xyz and committed by stavola.xyz 2f26de67 43af1f56

+1091 -1035
+1 -2
README.md
··· 6 6 7 7 Some of them do useful things, some of them were just written for fun. Please do not have the expectation that these are written in a super robust fashion; most were written for one-off tasks and experiments. 8 8 9 - If you want to play around with these programs, you just need [Rust][rust] and [cargo-eval][cargo-eval]. 9 + If you want to play around with these programs, you just need [Rust][rust]. Scripting support is currently available only on Nightly (current as of 1.93.0), but once stabilized I'll remove the +nightly flags. Please note that these scripts are built in debug mode, not release. This makes it easy to make quick changes but performance will suffer for more involved tasks, though these scripts aren't meant for that kind of work. 10 10 11 11 Be aware that dev work for this repo is done on UNIX-y systems, so they might not work on Windows. 12 12 13 13 [stb]: https://www.youtube.com/watch?v=eAhWIO1Ra6M 14 14 [rust]: https://www.rust-lang.org/tools/install 15 - [cargo-eval]: https://github.com/reitermarkus/cargo-eval
-55
cidrcheck
··· 1 - #!/usr/bin/env -S cargo eval -- 2 - 3 - // cargo-deps: argh = "0.1", cidr = "0.2.1" 4 - 5 - use std::net::IpAddr; 6 - use std::str::FromStr; 7 - 8 - use argh::FromArgs; 9 - use cidr::IpCidr; 10 - 11 - #[derive(FromArgs)] 12 - #[argh(description = "Display the ip range for a CIDR and check if an ip falls within.")] 13 - struct App { 14 - #[argh( 15 - option, 16 - description = "ip that will be checked against the provided CIDR.", 17 - from_str_fn(parse_ip) 18 - )] 19 - ip: Option<IpAddr>, 20 - 21 - #[argh(positional, description = "CIDR to inspect", from_str_fn(parse_cidr))] 22 - cidr: IpCidr, 23 - } 24 - 25 - fn main() { 26 - let App { ip, cidr } = argh::from_env(); 27 - println!( 28 - "CIDR {} has a range of: {} to {}", 29 - cidr, 30 - cidr.first_address(), 31 - cidr.last_address() 32 - ); 33 - 34 - println!("CIDR {} has a netmask of {}", cidr, cidr.mask()); 35 - 36 - if let Some(ip) = ip { 37 - print!("IP {}", ip); 38 - let falls_within = cidr.contains(&ip); 39 - if falls_within { 40 - print!(" falls "); 41 - } else { 42 - print!(" does not fall "); 43 - } 44 - 45 - println!("within {}", cidr); 46 - } 47 - } 48 - 49 - fn parse_ip(value: &str) -> Result<IpAddr, String> { 50 - IpAddr::from_str(value).map_err(|e| format!("{}", e)) 51 - } 52 - 53 - fn parse_cidr(value: &str) -> Result<IpCidr, String> { 54 - IpCidr::from_str(value).map_err(|e| format!("{}", e)) 55 - }
+61
cidrcheck.rs
··· 1 + #!/usr/bin/env -S cargo +nightly -Zscript 2 + 3 + --- 4 + [package] 5 + edition = "2024" 6 + [dependencies] 7 + argh = "0.1" 8 + cidr = "0.2.1" 9 + --- 10 + 11 + use std::net::IpAddr; 12 + use std::str::FromStr; 13 + 14 + use argh::FromArgs; 15 + use cidr::IpCidr; 16 + 17 + #[derive(FromArgs)] 18 + #[argh(description = "Display the ip range for a CIDR and check if an ip falls within.")] 19 + struct App { 20 + #[argh( 21 + option, 22 + description = "ip that will be checked against the provided CIDR.", 23 + from_str_fn(parse_ip) 24 + )] 25 + ip: Option<IpAddr>, 26 + 27 + #[argh(positional, description = "CIDR to inspect", from_str_fn(parse_cidr))] 28 + cidr: IpCidr, 29 + } 30 + 31 + fn main() { 32 + let App { ip, cidr } = argh::from_env(); 33 + println!( 34 + "CIDR {} has a range of: {} to {}", 35 + cidr, 36 + cidr.first_address(), 37 + cidr.last_address() 38 + ); 39 + 40 + println!("CIDR {} has a netmask of {}", cidr, cidr.mask()); 41 + 42 + if let Some(ip) = ip { 43 + print!("IP {}", ip); 44 + let falls_within = cidr.contains(&ip); 45 + if falls_within { 46 + print!(" falls "); 47 + } else { 48 + print!(" does not fall "); 49 + } 50 + 51 + println!("within {}", cidr); 52 + } 53 + } 54 + 55 + fn parse_ip(value: &str) -> Result<IpAddr, String> { 56 + IpAddr::from_str(value).map_err(|e| format!("{}", e)) 57 + } 58 + 59 + fn parse_cidr(value: &str) -> Result<IpCidr, String> { 60 + IpCidr::from_str(value).map_err(|e| format!("{}", e)) 61 + }
-43
dbg_server
··· 1 - #!/usr/bin/env -S cargo eval -- 2 - 3 - // cargo-deps: argh = "0.1" 4 - 5 - use std::io::{BufRead, BufReader}; 6 - use std::net::TcpListener; 7 - use std::str; 8 - 9 - use argh::FromArgs; 10 - 11 - #[derive(FromArgs)] 12 - #[argh(description = "simple debug server which logs the data sent to it.")] 13 - struct App { 14 - #[argh( 15 - option, 16 - description = "interface to listen on.", 17 - default = "\"0.0.0.0\".to_string()" 18 - )] 19 - host: String, 20 - 21 - #[argh(option, description = "port to listen on.", default = "7331")] 22 - port: u16, 23 - } 24 - 25 - fn main() { 26 - let app: App = argh::from_env(); 27 - 28 - let address = format!("{}:{}", app.host, app.port); 29 - let listener = TcpListener::bind(&address).expect("should be able to start server"); 30 - 31 - println!("Listening on {}", address); 32 - 33 - for stream in listener.incoming() { 34 - let stream = stream.expect("stream should be present"); 35 - let mut stream = BufReader::new(stream); 36 - 37 - // This is not the "best" way to get the data sent over 38 - // but it sure is the easiest :^) 39 - let data = stream.fill_buf().expect("should have data"); 40 - let output = str::from_utf8(data).expect("should be able to parse data as text"); 41 - println!("{}", output); 42 - } 43 - }
+48
dbg_server.rs
··· 1 + #!/usr/bin/env -S cargo +nightly -Zscript 2 + 3 + --- 4 + [package] 5 + edition = "2024" 6 + [dependencies] 7 + argh = "0.1" 8 + --- 9 + 10 + use std::io::{BufRead, BufReader}; 11 + use std::net::TcpListener; 12 + use std::str; 13 + 14 + use argh::FromArgs; 15 + 16 + #[derive(FromArgs)] 17 + #[argh(description = "simple debug server which logs the data sent to it.")] 18 + struct App { 19 + #[argh( 20 + option, 21 + description = "interface to listen on.", 22 + default = "\"0.0.0.0\".to_string()" 23 + )] 24 + host: String, 25 + 26 + #[argh(option, description = "port to listen on.", default = "7331")] 27 + port: u16, 28 + } 29 + 30 + fn main() { 31 + let app: App = argh::from_env(); 32 + 33 + let address = format!("{}:{}", app.host, app.port); 34 + let listener = TcpListener::bind(&address).expect("should be able to start server"); 35 + 36 + println!("Listening on {}", address); 37 + 38 + for stream in listener.incoming() { 39 + let stream = stream.expect("stream should be present"); 40 + let mut stream = BufReader::new(stream); 41 + 42 + // This is not the "best" way to get the data sent over 43 + // but it sure is the easiest :^) 44 + let data = stream.fill_buf().expect("should have data"); 45 + let output = str::from_utf8(data).expect("should be able to parse data as text"); 46 + println!("{}", output); 47 + } 48 + }
-278
emojidex
··· 1 - #!/usr/bin/env -S cargo eval -- 2 - 3 - //! ```cargo 4 - //! [dependencies] 5 - //! argh = "0.1" 6 - //! reqwest = { version = "0.11.3", features = ["blocking", "json"] } 7 - //! serde = { version = "1.0", features = ["derive"] } 8 - //! viuer = "0.4.0" 9 - //! image = "0.23.14" 10 - //! ``` 11 - 12 - use std::io::{BufReader, Cursor}; 13 - use std::str::FromStr; 14 - 15 - use argh::FromArgs; 16 - use image::io::Reader; 17 - use reqwest::blocking::Client; 18 - use serde::Deserialize; 19 - use viuer::Config; 20 - 21 - const BASE_API_URL: &str = "https://www.emojidex.com/api/v1"; 22 - const BASE_CDN_URL: &str = "https://cdn.emojidex.com/emoji"; 23 - 24 - #[derive(Copy, Clone, Eq, PartialEq)] 25 - enum Resolution { 26 - None, 27 - LDPI, 28 - MDPI, 29 - HDPI, 30 - XHDPI, 31 - XXHDPI, 32 - XXXHDPI, 33 - PX8, 34 - PX16, 35 - PX32, 36 - PX64, 37 - PX128, 38 - PX256, 39 - PX512, 40 - Hanko, 41 - Seal, 42 - } 43 - 44 - impl FromStr for Resolution { 45 - type Err = String; 46 - 47 - fn from_str(value: &str) -> Result<Resolution, String> { 48 - let resolution = match value.to_uppercase().as_str() { 49 - "NONE" | "OFF" => Resolution::None, 50 - "LDPI" => Resolution::LDPI, 51 - "MDPI" => Resolution::MDPI, 52 - "HDPI" => Resolution::HDPI, 53 - "XHDPI" => Resolution::XHDPI, 54 - "XXHDPI" => Resolution::XXHDPI, 55 - "XXXHDPI" => Resolution::XXXHDPI, 56 - "PX8" => Resolution::PX8, 57 - "PX16" => Resolution::PX16, 58 - "PX32" => Resolution::PX32, 59 - "PX64" => Resolution::PX64, 60 - "PX128" => Resolution::PX128, 61 - "PX256" => Resolution::PX256, 62 - "PX512" => Resolution::PX512, 63 - "HANKO" => Resolution::Hanko, 64 - "SEAL" => Resolution::Seal, 65 - _ => { 66 - let msg = "Unrecognized resolution, try one of: none, ldpi, mdpi, hdpi, xhdpi, xxhdpi, xxxdpi, px8, px16, px32, px64, px128, px256, px512, hanko, seal."; 67 - return Err(msg.to_string()); 68 - } 69 - }; 70 - 71 - Ok(resolution) 72 - } 73 - } 74 - 75 - impl Into<String> for Resolution { 76 - fn into(self) -> String { 77 - let resolution = match self { 78 - Resolution::LDPI => "ldpi", 79 - Resolution::MDPI => "mdpi", 80 - Resolution::HDPI => "hdpi", 81 - Resolution::XHDPI => "xhdpi", 82 - Resolution::XXHDPI => "xxhdpi", 83 - Resolution::XXXHDPI => "xxxhdpi", 84 - Resolution::PX8 => "px8", 85 - Resolution::PX16 => "px16", 86 - Resolution::PX32 => "px32", 87 - Resolution::PX64 => "px64", 88 - Resolution::PX128 => "px128", 89 - Resolution::PX256 => "px256", 90 - Resolution::PX512 => "px512", 91 - Resolution::Hanko => "hanko", 92 - Resolution::Seal => "seal", 93 - _ => panic!("unsupported resolution string"), 94 - }; 95 - 96 - resolution.to_string() 97 - } 98 - } 99 - 100 - #[derive(Deserialize, Debug)] 101 - struct SearchResponse { 102 - meta: SearchMeta, 103 - emoji: Vec<Emoji>, 104 - } 105 - 106 - #[derive(Deserialize, Debug)] 107 - struct SearchMeta { 108 - count: usize, 109 - total_count: usize, 110 - page: usize, 111 - } 112 - 113 - #[derive(Deserialize, Debug)] 114 - struct Emoji { 115 - code: String, 116 - moji: Option<String>, 117 - unicode: Option<String>, 118 - category: String, 119 - tags: Vec<String>, 120 - variants: Vec<String>, 121 - link: Option<String>, 122 - base: String, 123 - r18: bool, 124 - } 125 - 126 - #[derive(FromArgs)] 127 - #[argh(description = "Searches and displays emoji from emojidex.")] 128 - struct App { 129 - #[argh( 130 - option, 131 - description = "emoji preview resolution.", 132 - default = "Resolution::HDPI" 133 - )] 134 - resolution: Resolution, 135 - 136 - #[argh(option, description = "account auth token (required for r18 content).")] 137 - auth: Option<String>, 138 - 139 - #[argh(option, description = "limit the amount of results returned.")] 140 - limit: Option<usize>, 141 - 142 - #[argh(switch, description = "only displays images.")] 143 - images_only: bool, 144 - 145 - #[argh(switch, description = "filter out r18 emoji.")] 146 - sfw: bool, 147 - 148 - #[argh(option, description = "list of tags to search by.")] 149 - tag: Vec<String>, 150 - 151 - #[argh(option, description = "list of categories to search by.")] 152 - category: Vec<String>, 153 - 154 - #[argh(option, description = "code to search for.")] 155 - code: Option<String>, 156 - } 157 - 158 - fn main() { 159 - let App { 160 - sfw, 161 - tag, 162 - category, 163 - code, 164 - resolution, 165 - limit, 166 - auth, 167 - images_only, 168 - } = argh::from_env(); 169 - 170 - let tags = tag 171 - .into_iter() 172 - .map(|tag| ("tags[]", tag)) 173 - .collect::<Vec<_>>(); 174 - 175 - let category = category 176 - .into_iter() 177 - .map(|category| ("categories[]", category)) 178 - .collect::<Vec<_>>(); 179 - 180 - let code = if code.is_none() && tags.is_empty() && category.is_empty() { 181 - vec![("code_cont", "ferris".to_string())] 182 - } else { 183 - vec![( 184 - "code_cont", 185 - code.map(|code| code.replace(" ", "_")) 186 - .unwrap_or_else(|| "".to_string()), 187 - )] 188 - }; 189 - 190 - let auth_token = auth 191 - .map(|auth| vec![("auth_token", auth)]) 192 - .unwrap_or_else(|| vec![]); 193 - 194 - let terms = tags 195 - .into_iter() 196 - .chain(category) 197 - .chain(code) 198 - .chain(auth_token) 199 - .collect::<Vec<(&str, String)>>(); 200 - 201 - let client = Client::new(); 202 - let res = client 203 - .get(format!("{}/search/emoji", BASE_API_URL)) 204 - .form(&terms) 205 - .send() 206 - .expect("unexpected error while uploading file"); 207 - 208 - if res.status() != 200 { 209 - panic!("Unexpected status code {}", res.status()); 210 - } 211 - 212 - let SearchResponse { emoji, .. } = res.json().expect("should be able to deserialize body"); 213 - for emoji in emoji.into_iter().take(limit.unwrap_or(usize::MAX)) { 214 - if sfw && emoji.r18 { 215 - continue; 216 - } 217 - 218 - println!("\x1B[1m[{}]\x1B[0m\n", emoji.code.clone()); 219 - 220 - let encoded_code = emoji.code.replace(" ", "_"); 221 - let svg_url = format!("{}/{}.svg", BASE_CDN_URL, encoded_code.clone()); 222 - 223 - if !images_only { 224 - println!("\x1B[1mURL:\x1B[0m\n\t{}\n", svg_url.clone()); 225 - 226 - println!( 227 - "\x1B[1mHTML:\x1B[0m\n\t<img class=\"emojidex-emoji\" src=\"{}\" emoji-code=\"{}\" alt=\"{}\"{} />\n", 228 - svg_url.clone(), 229 - encoded_code.clone(), 230 - emoji.code.clone(), 231 - emoji 232 - .moji 233 - .as_ref() 234 - .map(|moji| format!(" emoji-moji=\"{}\"", moji)) 235 - .unwrap_or_else(|| "".to_string()), 236 - ); 237 - 238 - println!( 239 - "\x1B[1mMD:\x1B[0m\n\t![{}]({} \"{}\")\n", 240 - emoji.moji.unwrap_or(encoded_code.clone()), 241 - svg_url.clone(), 242 - emoji.code.clone(), 243 - ); 244 - } 245 - 246 - if resolution != Resolution::None { 247 - let res = client 248 - .get(format!( 249 - "{}/{}/{}.png", 250 - BASE_CDN_URL, 251 - <Resolution as Into<String>>::into(resolution), 252 - encoded_code, 253 - )) 254 - .send() 255 - .expect("should be able to get image"); 256 - 257 - let bytes = res.bytes().expect("should be able to get bytes"); 258 - let reader = Reader::new(BufReader::new(Cursor::new(bytes))) 259 - .with_guessed_format() 260 - .expect("should be able to guess format"); 261 - 262 - let image = reader 263 - .decode() 264 - .expect("should be able to interpret as image"); 265 - 266 - viuer::print( 267 - &image, 268 - &Config { 269 - absolute_offset: false, 270 - ..Default::default() 271 - }, 272 - ) 273 - .expect("Image printing failed."); 274 - } 275 - 276 - println!(); 277 - } 278 - }
+280
emojidex.rs
··· 1 + #!/usr/bin/env -S cargo +nightly -Zscript 2 + 3 + --- 4 + [package] 5 + edition = "2024" 6 + [dependencies] 7 + argh = "0.1" 8 + reqwest = { version = "0.11.3", features = ["blocking", "json"] } 9 + serde = { version = "1.0", features = ["derive"] } 10 + viuer = "0.4.0" 11 + image = "0.23.14" 12 + --- 13 + 14 + use std::io::{BufReader, Cursor}; 15 + use std::str::FromStr; 16 + 17 + use argh::FromArgs; 18 + use image::io::Reader; 19 + use reqwest::blocking::Client; 20 + use serde::Deserialize; 21 + use viuer::Config; 22 + 23 + const BASE_API_URL: &str = "https://www.emojidex.com/api/v1"; 24 + const BASE_CDN_URL: &str = "https://cdn.emojidex.com/emoji"; 25 + 26 + #[derive(Copy, Clone, Eq, PartialEq)] 27 + enum Resolution { 28 + None, 29 + LDPI, 30 + MDPI, 31 + HDPI, 32 + XHDPI, 33 + XXHDPI, 34 + XXXHDPI, 35 + PX8, 36 + PX16, 37 + PX32, 38 + PX64, 39 + PX128, 40 + PX256, 41 + PX512, 42 + Hanko, 43 + Seal, 44 + } 45 + 46 + impl FromStr for Resolution { 47 + type Err = String; 48 + 49 + fn from_str(value: &str) -> Result<Resolution, String> { 50 + let resolution = match value.to_uppercase().as_str() { 51 + "NONE" | "OFF" => Resolution::None, 52 + "LDPI" => Resolution::LDPI, 53 + "MDPI" => Resolution::MDPI, 54 + "HDPI" => Resolution::HDPI, 55 + "XHDPI" => Resolution::XHDPI, 56 + "XXHDPI" => Resolution::XXHDPI, 57 + "XXXHDPI" => Resolution::XXXHDPI, 58 + "PX8" => Resolution::PX8, 59 + "PX16" => Resolution::PX16, 60 + "PX32" => Resolution::PX32, 61 + "PX64" => Resolution::PX64, 62 + "PX128" => Resolution::PX128, 63 + "PX256" => Resolution::PX256, 64 + "PX512" => Resolution::PX512, 65 + "HANKO" => Resolution::Hanko, 66 + "SEAL" => Resolution::Seal, 67 + _ => { 68 + let msg = "Unrecognized resolution, try one of: none, ldpi, mdpi, hdpi, xhdpi, xxhdpi, xxxdpi, px8, px16, px32, px64, px128, px256, px512, hanko, seal."; 69 + return Err(msg.to_string()); 70 + } 71 + }; 72 + 73 + Ok(resolution) 74 + } 75 + } 76 + 77 + impl Into<String> for Resolution { 78 + fn into(self) -> String { 79 + let resolution = match self { 80 + Resolution::LDPI => "ldpi", 81 + Resolution::MDPI => "mdpi", 82 + Resolution::HDPI => "hdpi", 83 + Resolution::XHDPI => "xhdpi", 84 + Resolution::XXHDPI => "xxhdpi", 85 + Resolution::XXXHDPI => "xxxhdpi", 86 + Resolution::PX8 => "px8", 87 + Resolution::PX16 => "px16", 88 + Resolution::PX32 => "px32", 89 + Resolution::PX64 => "px64", 90 + Resolution::PX128 => "px128", 91 + Resolution::PX256 => "px256", 92 + Resolution::PX512 => "px512", 93 + Resolution::Hanko => "hanko", 94 + Resolution::Seal => "seal", 95 + _ => panic!("unsupported resolution string"), 96 + }; 97 + 98 + resolution.to_string() 99 + } 100 + } 101 + 102 + #[derive(Deserialize, Debug)] 103 + struct SearchResponse { 104 + meta: SearchMeta, 105 + emoji: Vec<Emoji>, 106 + } 107 + 108 + #[derive(Deserialize, Debug)] 109 + struct SearchMeta { 110 + count: usize, 111 + total_count: usize, 112 + page: usize, 113 + } 114 + 115 + #[derive(Deserialize, Debug)] 116 + struct Emoji { 117 + code: String, 118 + moji: Option<String>, 119 + unicode: Option<String>, 120 + category: String, 121 + tags: Vec<String>, 122 + variants: Vec<String>, 123 + link: Option<String>, 124 + base: String, 125 + r18: bool, 126 + } 127 + 128 + #[derive(FromArgs)] 129 + #[argh(description = "Searches and displays emoji from emojidex.")] 130 + struct App { 131 + #[argh( 132 + option, 133 + description = "emoji preview resolution.", 134 + default = "Resolution::HDPI" 135 + )] 136 + resolution: Resolution, 137 + 138 + #[argh(option, description = "account auth token (required for r18 content).")] 139 + auth: Option<String>, 140 + 141 + #[argh(option, description = "limit the amount of results returned.")] 142 + limit: Option<usize>, 143 + 144 + #[argh(switch, description = "only displays images.")] 145 + images_only: bool, 146 + 147 + #[argh(switch, description = "filter out r18 emoji.")] 148 + sfw: bool, 149 + 150 + #[argh(option, description = "list of tags to search by.")] 151 + tag: Vec<String>, 152 + 153 + #[argh(option, description = "list of categories to search by.")] 154 + category: Vec<String>, 155 + 156 + #[argh(option, description = "code to search for.")] 157 + code: Option<String>, 158 + } 159 + 160 + fn main() { 161 + let App { 162 + sfw, 163 + tag, 164 + category, 165 + code, 166 + resolution, 167 + limit, 168 + auth, 169 + images_only, 170 + } = argh::from_env(); 171 + 172 + let tags = tag 173 + .into_iter() 174 + .map(|tag| ("tags[]", tag)) 175 + .collect::<Vec<_>>(); 176 + 177 + let category = category 178 + .into_iter() 179 + .map(|category| ("categories[]", category)) 180 + .collect::<Vec<_>>(); 181 + 182 + let code = if code.is_none() && tags.is_empty() && category.is_empty() { 183 + vec![("code_cont", "ferris".to_string())] 184 + } else { 185 + vec![( 186 + "code_cont", 187 + code.map(|code| code.replace(" ", "_")) 188 + .unwrap_or_else(|| "".to_string()), 189 + )] 190 + }; 191 + 192 + let auth_token = auth 193 + .map(|auth| vec![("auth_token", auth)]) 194 + .unwrap_or_else(|| vec![]); 195 + 196 + let terms = tags 197 + .into_iter() 198 + .chain(category) 199 + .chain(code) 200 + .chain(auth_token) 201 + .collect::<Vec<(&str, String)>>(); 202 + 203 + let client = Client::new(); 204 + let res = client 205 + .get(format!("{}/search/emoji", BASE_API_URL)) 206 + .form(&terms) 207 + .send() 208 + .expect("unexpected error while uploading file"); 209 + 210 + if res.status() != 200 { 211 + panic!("Unexpected status code {}", res.status()); 212 + } 213 + 214 + let SearchResponse { emoji, .. } = res.json().expect("should be able to deserialize body"); 215 + for emoji in emoji.into_iter().take(limit.unwrap_or(usize::MAX)) { 216 + if sfw && emoji.r18 { 217 + continue; 218 + } 219 + 220 + println!("\x1B[1m[{}]\x1B[0m\n", emoji.code.clone()); 221 + 222 + let encoded_code = emoji.code.replace(" ", "_"); 223 + let svg_url = format!("{}/{}.svg", BASE_CDN_URL, encoded_code.clone()); 224 + 225 + if !images_only { 226 + println!("\x1B[1mURL:\x1B[0m\n\t{}\n", svg_url.clone()); 227 + 228 + println!( 229 + "\x1B[1mHTML:\x1B[0m\n\t<img class=\"emojidex-emoji\" src=\"{}\" emoji-code=\"{}\" alt=\"{}\"{} />\n", 230 + svg_url.clone(), 231 + encoded_code.clone(), 232 + emoji.code.clone(), 233 + emoji 234 + .moji 235 + .as_ref() 236 + .map(|moji| format!(" emoji-moji=\"{}\"", moji)) 237 + .unwrap_or_else(|| "".to_string()), 238 + ); 239 + 240 + println!( 241 + "\x1B[1mMD:\x1B[0m\n\t![{}]({} \"{}\")\n", 242 + emoji.moji.unwrap_or(encoded_code.clone()), 243 + svg_url.clone(), 244 + emoji.code.clone(), 245 + ); 246 + } 247 + 248 + if resolution != Resolution::None { 249 + let res = client 250 + .get(format!( 251 + "{}/{}/{}.png", 252 + BASE_CDN_URL, 253 + <Resolution as Into<String>>::into(resolution), 254 + encoded_code, 255 + )) 256 + .send() 257 + .expect("should be able to get image"); 258 + 259 + let bytes = res.bytes().expect("should be able to get bytes"); 260 + let reader = Reader::new(BufReader::new(Cursor::new(bytes))) 261 + .with_guessed_format() 262 + .expect("should be able to guess format"); 263 + 264 + let image = reader 265 + .decode() 266 + .expect("should be able to interpret as image"); 267 + 268 + viuer::print( 269 + &image, 270 + &Config { 271 + absolute_offset: false, 272 + ..Default::default() 273 + }, 274 + ) 275 + .expect("Image printing failed."); 276 + } 277 + 278 + println!(); 279 + } 280 + }
-50
exiftool
··· 1 - #!/usr/bin/env -S cargo eval -- 2 - 3 - // cargo-deps: argh = "0.1", rexiv2 = "0.9.1" 4 - 5 - // This script requires gexiv2 6 - // Available through: 7 - // (Mac) brew install gexiv2 8 - 9 - use argh::FromArgs; 10 - use rexiv2::Metadata; 11 - 12 - #[derive(FromArgs)] 13 - #[argh(description = "Show and strip EXIF data from image files.")] 14 - struct App { 15 - #[argh(switch, description = "show EXIF info (just tags for now)")] 16 - show: bool, 17 - 18 - #[argh(switch, description = "strip EXIF info")] 19 - strip: bool, 20 - 21 - #[argh(positional, description = "path to file(s)")] 22 - paths: Vec<String>, 23 - } 24 - 25 - fn main() { 26 - let App { show, strip, paths } = argh::from_env(); 27 - 28 - if !show && !strip { 29 - eprintln!("Either --show or --strip is required"); 30 - return; 31 - } else if paths.is_empty() { 32 - eprintln!("Must provide at least one path"); 33 - return; 34 - } 35 - 36 - for path in paths { 37 - let meta = Metadata::new_from_path(&path).expect("should be able to open metadata"); 38 - 39 - if show { 40 - let tags = meta.get_exif_tags().expect("should be able to get tags"); 41 - println!("Available EXIF tags: {}", tags.join(", ")); 42 - } 43 - 44 - if strip { 45 - println!("Clearing EXIF data from: {:?}", path); 46 - meta.clear_exif(); 47 - meta.save_to_file(&path).expect("should be able to resave"); 48 - } 49 - } 50 - }
+57
exiftool.rs
··· 1 + 2 + #!/usr/bin/env -S cargo +nightly -Zscript 3 + 4 + --- 5 + [package] 6 + edition = "2024" 7 + [dependencies] 8 + argh = "0.1" 9 + rexiv2 = "0.9.1" 10 + --- 11 + 12 + // This script requires gexiv2 13 + // Available through: 14 + // (Mac) brew install gexiv2 15 + 16 + use argh::FromArgs; 17 + use rexiv2::Metadata; 18 + 19 + #[derive(FromArgs)] 20 + #[argh(description = "Show and strip EXIF data from image files.")] 21 + struct App { 22 + #[argh(switch, description = "show EXIF info (just tags for now)")] 23 + show: bool, 24 + 25 + #[argh(switch, description = "strip EXIF info")] 26 + strip: bool, 27 + 28 + #[argh(positional, description = "path to file(s)")] 29 + paths: Vec<String>, 30 + } 31 + 32 + fn main() { 33 + let App { show, strip, paths } = argh::from_env(); 34 + 35 + if !show && !strip { 36 + eprintln!("Either --show or --strip is required"); 37 + return; 38 + } else if paths.is_empty() { 39 + eprintln!("Must provide at least one path"); 40 + return; 41 + } 42 + 43 + for path in paths { 44 + let meta = Metadata::new_from_path(&path).expect("should be able to open metadata"); 45 + 46 + if show { 47 + let tags = meta.get_exif_tags().expect("should be able to get tags"); 48 + println!("Available EXIF tags: {}", tags.join(", ")); 49 + } 50 + 51 + if strip { 52 + println!("Clearing EXIF data from: {:?}", path); 53 + meta.clear_exif(); 54 + meta.save_to_file(&path).expect("should be able to resave"); 55 + } 56 + } 57 + }
-49
flush_cache
··· 1 - #!/usr/bin/env -S cargo eval -- 2 - 3 - // cargo-deps: argh = "0.1" 4 - 5 - use std::io::{BufRead, BufReader, Write}; 6 - use std::net::{Shutdown, TcpStream}; 7 - 8 - use argh::FromArgs; 9 - 10 - const FLUSH_COMMAND: &str = "FLUSHALL\r\n"; 11 - 12 - #[derive(FromArgs)] 13 - #[argh(description = "Flush the cache of a redis instance.")] 14 - struct App { 15 - #[argh( 16 - option, 17 - description = "host for the redis instance.", 18 - default = "\"127.0.0.1\".to_string()" 19 - )] 20 - host: String, 21 - 22 - #[argh(option, description = "port redis is listening on.", default = "6379")] 23 - port: u16, 24 - } 25 - 26 - fn main() { 27 - let app: App = argh::from_env(); 28 - 29 - let address = format!("{}:{}", app.host, app.port); 30 - let mut stream = TcpStream::connect(address).expect("should be able to connect to redis"); 31 - 32 - stream 33 - .write(FLUSH_COMMAND.as_bytes()) 34 - .expect("should be able to write flush command"); 35 - stream 36 - .flush() 37 - .expect("should be able to send flush command"); 38 - stream 39 - .shutdown(Shutdown::Write) 40 - .expect("should be able to shutdown writer"); 41 - 42 - let mut stream = BufReader::new(stream); 43 - 44 - let mut output = String::new(); 45 - let _ = stream 46 - .read_line(&mut output) 47 - .expect("should be able to read response"); 48 - println!("{}", &output); 49 - }
+54
flush_cache.rs
··· 1 + #!/usr/bin/env -S cargo +nightly -Zscript 2 + 3 + --- 4 + [package] 5 + edition = "2024" 6 + [dependencies] 7 + argh = "0.1" 8 + --- 9 + 10 + use std::io::{BufRead, BufReader, Write}; 11 + use std::net::{Shutdown, TcpStream}; 12 + 13 + use argh::FromArgs; 14 + 15 + const FLUSH_COMMAND: &str = "FLUSHALL\r\n"; 16 + 17 + #[derive(FromArgs)] 18 + #[argh(description = "Flush the cache of a redis instance.")] 19 + struct App { 20 + #[argh( 21 + option, 22 + description = "host for the redis instance.", 23 + default = "\"127.0.0.1\".to_string()" 24 + )] 25 + host: String, 26 + 27 + #[argh(option, description = "port redis is listening on.", default = "6379")] 28 + port: u16, 29 + } 30 + 31 + fn main() { 32 + let app: App = argh::from_env(); 33 + 34 + let address = format!("{}:{}", app.host, app.port); 35 + let mut stream = TcpStream::connect(address).expect("should be able to connect to redis"); 36 + 37 + stream 38 + .write(FLUSH_COMMAND.as_bytes()) 39 + .expect("should be able to write flush command"); 40 + stream 41 + .flush() 42 + .expect("should be able to send flush command"); 43 + stream 44 + .shutdown(Shutdown::Write) 45 + .expect("should be able to shutdown writer"); 46 + 47 + let mut stream = BufReader::new(stream); 48 + 49 + let mut output = String::new(); 50 + let _ = stream 51 + .read_line(&mut output) 52 + .expect("should be able to read response"); 53 + println!("{}", &output); 54 + }
-147
ghoshare
··· 1 - #!/usr/bin/env -S cargo eval -- 2 - 3 - //! ```cargo 4 - //! [dependencies] 5 - //! argh = "0.1" 6 - //! reqwest = { version = "0.11.3", features = ["blocking"] } 7 - //! ``` 8 - 9 - use std::fs::OpenOptions; 10 - use std::io::{BufReader, Read}; 11 - use std::path::PathBuf; 12 - use std::str::FromStr; 13 - 14 - use argh::FromArgs; 15 - use reqwest::blocking::Client; 16 - use reqwest::redirect::Policy; 17 - 18 - const BASE_URL: &str = "https://ghostbin.co"; 19 - 20 - #[derive(Debug)] 21 - enum ExpirationTime { 22 - Never, 23 - TenMinutes, 24 - OneHour, 25 - OneDay, 26 - Fortnight, 27 - } 28 - 29 - impl FromStr for ExpirationTime { 30 - type Err = String; 31 - 32 - fn from_str(value: &str) -> Result<ExpirationTime, String> { 33 - let expiration = match value { 34 - "never" => ExpirationTime::Never, 35 - "10m" | "10 m" | "10minutes" | "10 minutes" | "ten m" | "ten minutes" => { 36 - ExpirationTime::TenMinutes 37 - } 38 - "1h" | "1 h" | "1hour" | "1 hour" | "one h" | "one hour" => ExpirationTime::OneHour, 39 - "1d" | "1 d" | "1day" | "1 day" | "one d" | "one day" => ExpirationTime::OneDay, 40 - "14d" | "14 d" | "14days" | "14 days" | "fourteen d" | "fourteen days" 41 - | "fortnight" => ExpirationTime::Fortnight, 42 - _ => return Err("unrecoginized duration. try: never, 10m, 1h, 1d, or 14d.".to_string()), 43 - }; 44 - 45 - Ok(expiration) 46 - } 47 - } 48 - 49 - impl Into<String> for ExpirationTime { 50 - fn into(self) -> String { 51 - match self { 52 - ExpirationTime::Never => "-1".to_string(), 53 - ExpirationTime::TenMinutes => "10m".to_string(), 54 - ExpirationTime::OneHour => "1h".to_string(), 55 - ExpirationTime::OneDay => "1d".to_string(), 56 - ExpirationTime::Fortnight => "14d".to_string(), 57 - } 58 - } 59 - } 60 - 61 - #[derive(FromArgs)] 62 - #[argh(description = "Copies a local file onto ghostbin.co.")] 63 - struct App { 64 - #[argh( 65 - option, 66 - description = "language to apply syntax highlighting.", 67 - default = r#""text".to_string()"# 68 - )] 69 - language: String, 70 - 71 - #[argh(option, description = "how long to keep the file online.")] 72 - expiration: Option<ExpirationTime>, 73 - 74 - #[argh(option, description = "title of the uploaded file.")] 75 - title: Option<String>, 76 - 77 - #[argh( 78 - option, 79 - description = "password to access the file.", 80 - default = r#""".to_string()"# 81 - )] 82 - password: String, 83 - 84 - #[argh(positional, description = "file to share.")] 85 - file: PathBuf, 86 - } 87 - 88 - fn main() { 89 - let app: App = argh::from_env(); 90 - 91 - let file = OpenOptions::new() 92 - .read(true) 93 - .open(app.file.clone()) 94 - .expect("could not open file"); 95 - 96 - let mut reader = BufReader::new(file); 97 - 98 - let mut contents = String::new(); 99 - reader 100 - .read_to_string(&mut contents) 101 - .expect("should be able to read file contents"); 102 - 103 - let expiration: String = app.expiration.unwrap_or(ExpirationTime::TenMinutes).into(); 104 - 105 - let filename = app 106 - .file 107 - .file_name() 108 - .and_then(|name| name.to_str()) 109 - .map(|name| name.to_string()); 110 - 111 - let title = app 112 - .title 113 - .or_else(|| filename) 114 - .unwrap_or_else(|| "".to_string()); 115 - 116 - println!("Uploading file"); 117 - let client = Client::builder() 118 - .redirect(Policy::none()) 119 - .build() 120 - .expect("should be able to build client"); 121 - 122 - let res = client 123 - .post(format!("{}/paste/new", BASE_URL)) 124 - .header("User-Agent", "ghoshare") 125 - .form(&[ 126 - ("expire", expiration), 127 - ("password", app.password), 128 - ("lang", app.language), 129 - ("title", title), 130 - ("text", contents), 131 - ]) 132 - .send() 133 - .expect("unexpected error while uploading file"); 134 - 135 - if res.status() != 303 { 136 - panic!("Unexpected status code {}", res.status()); 137 - } 138 - 139 - let headers = res.headers(); 140 - let path = headers 141 - .get("location") 142 - .expect("we should be redirected!") 143 - .to_str() 144 - .expect("should be able to get location header value"); 145 - 146 - println!("URL: {}", format!("{}{}", BASE_URL, path)); 147 - }
+149
ghoshare.rs
··· 1 + #!/usr/bin/env -S cargo +nightly -Zscript 2 + 3 + --- 4 + [package] 5 + edition = "2024" 6 + [dependencies] 7 + argh = "0.1" 8 + reqwest = { version = "0.11.3", features = ["blocking"] } 9 + --- 10 + 11 + use std::fs::OpenOptions; 12 + use std::io::{BufReader, Read}; 13 + use std::path::PathBuf; 14 + use std::str::FromStr; 15 + 16 + use argh::FromArgs; 17 + use reqwest::blocking::Client; 18 + use reqwest::redirect::Policy; 19 + 20 + const BASE_URL: &str = "https://ghostbin.co"; 21 + 22 + #[derive(Debug)] 23 + enum ExpirationTime { 24 + Never, 25 + TenMinutes, 26 + OneHour, 27 + OneDay, 28 + Fortnight, 29 + } 30 + 31 + impl FromStr for ExpirationTime { 32 + type Err = String; 33 + 34 + fn from_str(value: &str) -> Result<ExpirationTime, String> { 35 + let expiration = match value { 36 + "never" => ExpirationTime::Never, 37 + "10m" | "10 m" | "10minutes" | "10 minutes" | "ten m" | "ten minutes" => { 38 + ExpirationTime::TenMinutes 39 + } 40 + "1h" | "1 h" | "1hour" | "1 hour" | "one h" | "one hour" => ExpirationTime::OneHour, 41 + "1d" | "1 d" | "1day" | "1 day" | "one d" | "one day" => ExpirationTime::OneDay, 42 + "14d" | "14 d" | "14days" | "14 days" | "fourteen d" | "fourteen days" 43 + | "fortnight" => ExpirationTime::Fortnight, 44 + _ => return Err("unrecoginized duration. try: never, 10m, 1h, 1d, or 14d.".to_string()), 45 + }; 46 + 47 + Ok(expiration) 48 + } 49 + } 50 + 51 + impl Into<String> for ExpirationTime { 52 + fn into(self) -> String { 53 + match self { 54 + ExpirationTime::Never => "-1".to_string(), 55 + ExpirationTime::TenMinutes => "10m".to_string(), 56 + ExpirationTime::OneHour => "1h".to_string(), 57 + ExpirationTime::OneDay => "1d".to_string(), 58 + ExpirationTime::Fortnight => "14d".to_string(), 59 + } 60 + } 61 + } 62 + 63 + #[derive(FromArgs)] 64 + #[argh(description = "Copies a local file onto ghostbin.co.")] 65 + struct App { 66 + #[argh( 67 + option, 68 + description = "language to apply syntax highlighting.", 69 + default = r#""text".to_string()"# 70 + )] 71 + language: String, 72 + 73 + #[argh(option, description = "how long to keep the file online.")] 74 + expiration: Option<ExpirationTime>, 75 + 76 + #[argh(option, description = "title of the uploaded file.")] 77 + title: Option<String>, 78 + 79 + #[argh( 80 + option, 81 + description = "password to access the file.", 82 + default = r#""".to_string()"# 83 + )] 84 + password: String, 85 + 86 + #[argh(positional, description = "file to share.")] 87 + file: PathBuf, 88 + } 89 + 90 + fn main() { 91 + let app: App = argh::from_env(); 92 + 93 + let file = OpenOptions::new() 94 + .read(true) 95 + .open(app.file.clone()) 96 + .expect("could not open file"); 97 + 98 + let mut reader = BufReader::new(file); 99 + 100 + let mut contents = String::new(); 101 + reader 102 + .read_to_string(&mut contents) 103 + .expect("should be able to read file contents"); 104 + 105 + let expiration: String = app.expiration.unwrap_or(ExpirationTime::TenMinutes).into(); 106 + 107 + let filename = app 108 + .file 109 + .file_name() 110 + .and_then(|name| name.to_str()) 111 + .map(|name| name.to_string()); 112 + 113 + let title = app 114 + .title 115 + .or_else(|| filename) 116 + .unwrap_or_else(|| "".to_string()); 117 + 118 + println!("Uploading file"); 119 + let client = Client::builder() 120 + .redirect(Policy::none()) 121 + .build() 122 + .expect("should be able to build client"); 123 + 124 + let res = client 125 + .post(format!("{}/paste/new", BASE_URL)) 126 + .header("User-Agent", "ghoshare") 127 + .form(&[ 128 + ("expire", expiration), 129 + ("password", app.password), 130 + ("lang", app.language), 131 + ("title", title), 132 + ("text", contents), 133 + ]) 134 + .send() 135 + .expect("unexpected error while uploading file"); 136 + 137 + if res.status() != 303 { 138 + panic!("Unexpected status code {}", res.status()); 139 + } 140 + 141 + let headers = res.headers(); 142 + let path = headers 143 + .get("location") 144 + .expect("we should be redirected!") 145 + .to_str() 146 + .expect("should be able to get location header value"); 147 + 148 + println!("URL: {}", format!("{}{}", BASE_URL, path)); 149 + }
-23
ipecho
··· 1 - #!/usr/bin/env -S cargo eval -- 2 - 3 - //! ```cargo 4 - //! [dependencies] 5 - //! reqwest = { version = "0.11.3", features = ["blocking"] } 6 - //! ``` 7 - 8 - use reqwest::blocking::Client; 9 - 10 - fn main() { 11 - let client = Client::new(); 12 - let res = client 13 - .get("https://ipecho.net/plain") 14 - .send() 15 - .expect("unexpected error while trying to reach ipecho.net"); 16 - 17 - if res.status() != 200 { 18 - panic!("Unexpected status code {}", res.status()); 19 - } 20 - 21 - let ip = res.text().expect("should be able to deserialize body"); 22 - println!("{}", ip); 23 - }
+25
ipecho.rs
··· 1 + #!/usr/bin/env -S cargo +nightly -Zscript 2 + 3 + --- 4 + [package] 5 + edition = "2024" 6 + [dependencies] 7 + reqwest = { version = "0.11.3", features = ["blocking"] } 8 + --- 9 + 10 + use reqwest::blocking::Client; 11 + 12 + fn main() { 13 + let client = Client::new(); 14 + let res = client 15 + .get("https://ipecho.net/plain") 16 + .send() 17 + .expect("unexpected error while trying to reach ipecho.net"); 18 + 19 + if res.status() != 200 { 20 + panic!("Unexpected status code {}", res.status()); 21 + } 22 + 23 + let ip = res.text().expect("should be able to deserialize body"); 24 + println!("{}", ip); 25 + }
-69
kill_port
··· 1 - #!/usr/bin/env -S cargo eval -- 2 - 3 - // cargo-deps: argh = "0.1" 4 - 5 - use std::process::{self, Command}; 6 - 7 - use argh::FromArgs; 8 - 9 - #[derive(FromArgs)] 10 - #[argh(description = "Simple program to kill a process listening on a specific port.")] 11 - struct App { 12 - #[argh(switch, description = "check udp protocol ports.")] 13 - udp: bool, 14 - 15 - #[argh( 16 - option, 17 - description = "which signal to send to the process. defaults to kill.", 18 - default = "String::from(\"kill\")" 19 - )] 20 - signal: String, 21 - 22 - #[argh(positional, description = "what port to free.")] 23 - port: u16, 24 - } 25 - 26 - fn main() { 27 - let app: App = argh::from_env(); 28 - 29 - let address = format!("{}:{}", if app.udp { "udp" } else { "tcp" }, app.port); 30 - let output = Command::new("lsof") 31 - .arg("-t") 32 - .arg("-i") 33 - .arg(address) 34 - .output() 35 - .expect("failed to execute lsof"); 36 - 37 - if !output.status.success() && !output.stderr.is_empty() { 38 - let error = String::from_utf8(output.stderr).expect("must be valid utf8"); 39 - eprintln!("Failed to lsof port '{}'. Reason:\n{}", app.port, error); 40 - process::exit(1); 41 - } 42 - 43 - let pids = String::from_utf8(output.stdout).expect("must be valid utf8"); 44 - let pids = pids.split_ascii_whitespace().collect::<Vec<_>>(); 45 - 46 - if pids.is_empty() { 47 - println!("Port '{}' is not bound to any process", app.port); 48 - return; 49 - } 50 - 51 - println!("Found {} process(es) bound to '{}'", pids.len(), app.port); 52 - 53 - for pid in pids { 54 - println!("Killing process {}", pid); 55 - 56 - let output = Command::new("kill") 57 - .arg("-s") 58 - .arg(&app.signal) 59 - .arg(pid) 60 - .output() 61 - .expect("failed to execute kill"); 62 - 63 - if !output.status.success() { 64 - let error = String::from_utf8(output.stderr).expect("must be valid utf8"); 65 - eprintln!("Failed to kill process '{}'. Reason:\n{}", pid, error); 66 - process::exit(1); 67 - } 68 - } 69 - }
+74
kill_port.rs
··· 1 + #!/usr/bin/env -S cargo +nightly -Zscript 2 + 3 + --- 4 + [package] 5 + edition = "2024" 6 + [dependencies] 7 + argh = "0.1" 8 + --- 9 + 10 + use std::process::{self, Command}; 11 + 12 + use argh::FromArgs; 13 + 14 + #[derive(FromArgs)] 15 + #[argh(description = "Simple program to kill a process listening on a specific port.")] 16 + struct App { 17 + #[argh(switch, description = "check udp protocol ports.")] 18 + udp: bool, 19 + 20 + #[argh( 21 + option, 22 + description = "which signal to send to the process. defaults to kill.", 23 + default = "String::from(\"kill\")" 24 + )] 25 + signal: String, 26 + 27 + #[argh(positional, description = "what port to free.")] 28 + port: u16, 29 + } 30 + 31 + fn main() { 32 + let app: App = argh::from_env(); 33 + 34 + let address = format!("{}:{}", if app.udp { "udp" } else { "tcp" }, app.port); 35 + let output = Command::new("lsof") 36 + .arg("-t") 37 + .arg("-i") 38 + .arg(address) 39 + .output() 40 + .expect("failed to execute lsof"); 41 + 42 + if !output.status.success() && !output.stderr.is_empty() { 43 + let error = String::from_utf8(output.stderr).expect("must be valid utf8"); 44 + eprintln!("Failed to lsof port '{}'. Reason:\n{}", app.port, error); 45 + process::exit(1); 46 + } 47 + 48 + let pids = String::from_utf8(output.stdout).expect("must be valid utf8"); 49 + let pids = pids.split_ascii_whitespace().collect::<Vec<_>>(); 50 + 51 + if pids.is_empty() { 52 + println!("Port '{}' is not bound to any process", app.port); 53 + return; 54 + } 55 + 56 + println!("Found {} process(es) bound to '{}'", pids.len(), app.port); 57 + 58 + for pid in pids { 59 + println!("Killing process {}", pid); 60 + 61 + let output = Command::new("kill") 62 + .arg("-s") 63 + .arg(&app.signal) 64 + .arg(pid) 65 + .output() 66 + .expect("failed to execute kill"); 67 + 68 + if !output.status.success() { 69 + let error = String::from_utf8(output.stderr).expect("must be valid utf8"); 70 + eprintln!("Failed to kill process '{}'. Reason:\n{}", pid, error); 71 + process::exit(1); 72 + } 73 + } 74 + }
+4 -4
rustfmt.toml
··· 1 - version = "Two" 2 - edition = "2021" 1 + edition = "2024" 2 + style_edition = "2024" 3 3 unstable_features = true 4 - imports_granularity = "Module" 4 + imports_granularity = "Crate" 5 5 reorder_impl_items = true 6 6 use_field_init_shorthand = true 7 - group_imports = "StdExternalCrate" 7 + group_imports = "StdExternalCrate"
-118
sierpinski
··· 1 - #!/usr/bin/env -S cargo eval -- 2 - 3 - // cargo-deps: argh = "0.1" 4 - 5 - use std::fmt; 6 - use std::str::FromStr; 7 - 8 - use argh::FromArgs; 9 - 10 - const DELTA: &str = "Δ"; 11 - const FILLED_TRIANGLE: &str = "▲"; 12 - const HOLLOW_TRIANGLE: &str = "△"; 13 - const DOT: &str = "."; 14 - const CARAT: &str = "^"; 15 - 16 - #[derive(Copy, Clone)] 17 - enum TriangleType { 18 - Delta, 19 - Filled, 20 - Hollow, 21 - Dot, 22 - Carat, 23 - } 24 - 25 - impl fmt::Display for TriangleType { 26 - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 27 - let displayable = match self { 28 - TriangleType::Delta => DELTA, 29 - TriangleType::Filled => FILLED_TRIANGLE, 30 - TriangleType::Hollow => HOLLOW_TRIANGLE, 31 - TriangleType::Dot => DOT, 32 - TriangleType::Carat => CARAT, 33 - }; 34 - 35 - write!(f, "{}", displayable) 36 - } 37 - } 38 - 39 - impl FromStr for TriangleType { 40 - type Err = String; 41 - 42 - fn from_str(value: &str) -> Result<TriangleType, String> { 43 - let triangle = 44 - match value.to_lowercase().as_str() { 45 - "delta" => TriangleType::Delta, 46 - "filled" => TriangleType::Filled, 47 - "hollow" => TriangleType::Hollow, 48 - "dot" => TriangleType::Dot, 49 - "carat" => TriangleType::Carat, 50 - _ => return Err( 51 - "unrecognized triangle type. try one of: delta, filled, hollow, dot, or carat." 52 - .to_string(), 53 - ), 54 - }; 55 - 56 - Ok(triangle) 57 - } 58 - } 59 - 60 - #[derive(FromArgs)] 61 - #[argh(description = "Prints a Sierpinski triangle.")] 62 - struct App { 63 - #[argh(switch, description = "inverts the triangle.")] 64 - invert: bool, 65 - 66 - #[argh( 67 - option, 68 - description = "triangle type to use.", 69 - default = "TriangleType::Filled" 70 - )] 71 - triangle: TriangleType, 72 - 73 - #[argh(positional, description = "size of the Sierpinski triangle.")] 74 - size: Option<u8>, 75 - } 76 - 77 - fn main() { 78 - let App { 79 - size, 80 - triangle, 81 - invert, 82 - } = argh::from_env(); 83 - let size = size.unwrap_or(1); 84 - 85 - let height = 2_usize.pow(size as u32); 86 - let width = (2 * height) - 1; 87 - 88 - let mut bounding_box = vec![vec![0_u16; width]; height]; 89 - bounding_box[0][height - 1] = 1; 90 - 91 - let mut last_row = bounding_box[0].clone(); 92 - for row in bounding_box.iter_mut().skip(1) { 93 - for (j, cell) in row.iter_mut().enumerate() { 94 - let top_left = last_row.get(j - 1).unwrap_or(&0); 95 - let top_right = last_row.get(j + 1).unwrap_or(&0); 96 - 97 - *cell = top_left + top_right; 98 - } 99 - last_row = row.clone(); 100 - } 101 - 102 - let rows: Box<dyn Iterator<Item = Vec<u16>>> = if invert { 103 - Box::new(bounding_box.into_iter().rev()) 104 - } else { 105 - Box::new(bounding_box.into_iter()) 106 - }; 107 - 108 - for row in rows { 109 - for cell in row { 110 - if cell == 0 || cell % 2 == 0 { 111 - print!(" "); 112 - } else { 113 - print!("{}", triangle); 114 - } 115 - } 116 - println!(); 117 - } 118 - }
+123
sierpinski.rs
··· 1 + #!/usr/bin/env -S cargo +nightly -Zscript 2 + 3 + --- 4 + [package] 5 + edition = "2024" 6 + [dependencies] 7 + argh = "0.1" 8 + --- 9 + 10 + use std::fmt; 11 + use std::str::FromStr; 12 + 13 + use argh::FromArgs; 14 + 15 + const DELTA: &str = "Δ"; 16 + const FILLED_TRIANGLE: &str = "▲"; 17 + const HOLLOW_TRIANGLE: &str = "△"; 18 + const DOT: &str = "."; 19 + const CARAT: &str = "^"; 20 + 21 + #[derive(Copy, Clone)] 22 + enum TriangleType { 23 + Delta, 24 + Filled, 25 + Hollow, 26 + Dot, 27 + Carat, 28 + } 29 + 30 + impl fmt::Display for TriangleType { 31 + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 32 + let displayable = match self { 33 + TriangleType::Delta => DELTA, 34 + TriangleType::Filled => FILLED_TRIANGLE, 35 + TriangleType::Hollow => HOLLOW_TRIANGLE, 36 + TriangleType::Dot => DOT, 37 + TriangleType::Carat => CARAT, 38 + }; 39 + 40 + write!(f, "{}", displayable) 41 + } 42 + } 43 + 44 + impl FromStr for TriangleType { 45 + type Err = String; 46 + 47 + fn from_str(value: &str) -> Result<TriangleType, String> { 48 + let triangle = 49 + match value.to_lowercase().as_str() { 50 + "delta" => TriangleType::Delta, 51 + "filled" => TriangleType::Filled, 52 + "hollow" => TriangleType::Hollow, 53 + "dot" => TriangleType::Dot, 54 + "carat" => TriangleType::Carat, 55 + _ => return Err( 56 + "unrecognized triangle type. try one of: delta, filled, hollow, dot, or carat." 57 + .to_string(), 58 + ), 59 + }; 60 + 61 + Ok(triangle) 62 + } 63 + } 64 + 65 + #[derive(FromArgs)] 66 + #[argh(description = "Prints a Sierpinski triangle.")] 67 + struct App { 68 + #[argh(switch, description = "inverts the triangle.")] 69 + invert: bool, 70 + 71 + #[argh( 72 + option, 73 + description = "triangle type to use.", 74 + default = "TriangleType::Filled" 75 + )] 76 + triangle: TriangleType, 77 + 78 + #[argh(positional, description = "size of the Sierpinski triangle.")] 79 + size: Option<u8>, 80 + } 81 + 82 + fn main() { 83 + let App { 84 + size, 85 + triangle, 86 + invert, 87 + } = argh::from_env(); 88 + let size = size.unwrap_or(1); 89 + 90 + let height = 2_usize.pow(size as u32); 91 + let width = (2 * height) - 1; 92 + 93 + let mut bounding_box = vec![vec![0_u8; width]; height]; 94 + bounding_box[0][height - 1] = 1; 95 + 96 + let mut last_row = bounding_box[0].clone(); 97 + for row in bounding_box.iter_mut().skip(1) { 98 + for (j, cell) in row.iter_mut().enumerate() { 99 + let top_left = j.checked_sub(1).and_then(|i| last_row.get(i)).unwrap_or(&0); 100 + let top_right = j.checked_add(1).and_then(|i| last_row.get(i)).unwrap_or(&0); 101 + 102 + *cell = (top_left + top_right) % 2; 103 + } 104 + last_row = row.clone(); 105 + } 106 + 107 + let rows: Box<dyn Iterator<Item = Vec<u8>>> = if invert { 108 + Box::new(bounding_box.into_iter().rev()) 109 + } else { 110 + Box::new(bounding_box.into_iter()) 111 + }; 112 + 113 + for row in rows { 114 + for cell in row { 115 + if cell == 0 || cell % 2 == 0 { 116 + print!(" "); 117 + } else { 118 + print!("{}", triangle); 119 + } 120 + } 121 + println!(); 122 + } 123 + }
-58
stopwatch
··· 1 - #!/usr/bin/env -S cargo eval -- 2 - 3 - // cargo-deps: argh = "0.1", humantime = "2.1.0" 4 - 5 - use std::thread; 6 - use std::time::{Duration, Instant}; 7 - 8 - use argh::FromArgs; 9 - 10 - #[derive(FromArgs)] 11 - #[argh(description = "Keep track of how long a task runs or set a timer for some period of time.")] 12 - struct App { 13 - #[argh( 14 - option, 15 - description = "how long to wait before starting the stopwatch." 16 - )] 17 - delay: Option<humantime::Duration>, 18 - 19 - #[argh( 20 - option, 21 - description = "how long to wait between timer updates.", 22 - default = "\"1ms\".parse::<humantime::Duration>().unwrap()" 23 - )] 24 - pause: humantime::Duration, 25 - 26 - #[argh(option, description = "how long to run the stopwatch.")] 27 - duration: Option<humantime::Duration>, 28 - } 29 - 30 - fn main() { 31 - let app: App = argh::from_env(); 32 - 33 - let delay: Option<Duration> = app.delay.map(|d| d.into()); 34 - let pause = app.pause.into(); 35 - let duration = app 36 - .duration 37 - .map(|d| d.into()) 38 - .unwrap_or_else(|| Duration::new(u64::MAX, 0)); 39 - 40 - if let Some(delay) = delay { 41 - println!("Waiting {:?}...", delay); 42 - thread::sleep(delay); 43 - } 44 - 45 - let now = Instant::now(); 46 - 47 - let mut elapsed = now.elapsed(); 48 - while elapsed < duration { 49 - print!("\x1B[2K\rElapsed: {}", humantime::format_duration(elapsed)); 50 - thread::sleep(pause); 51 - elapsed = now.elapsed(); 52 - } 53 - 54 - print!( 55 - "\x1B[2K\rElapsed: {}\nStopped\x07", 56 - humantime::format_duration(elapsed) 57 - ); 58 - }
+64
stopwatch.rs
··· 1 + #!/usr/bin/env -S cargo +nightly -Zscript 2 + 3 + --- 4 + [package] 5 + edition = "2024" 6 + [dependencies] 7 + argh = "0.1" 8 + humantime = "2.1.0" 9 + --- 10 + 11 + use std::thread; 12 + use std::time::{Duration, Instant}; 13 + 14 + use argh::FromArgs; 15 + 16 + #[derive(FromArgs)] 17 + #[argh(description = "Keep track of how long a task runs or set a timer for some period of time.")] 18 + struct App { 19 + #[argh( 20 + option, 21 + description = "how long to wait before starting the stopwatch." 22 + )] 23 + delay: Option<humantime::Duration>, 24 + 25 + #[argh( 26 + option, 27 + description = "how long to wait between timer updates.", 28 + default = "\"1ms\".parse::<humantime::Duration>().unwrap()" 29 + )] 30 + pause: humantime::Duration, 31 + 32 + #[argh(option, description = "how long to run the stopwatch.")] 33 + duration: Option<humantime::Duration>, 34 + } 35 + 36 + fn main() { 37 + let app: App = argh::from_env(); 38 + 39 + let delay: Option<Duration> = app.delay.map(|d| d.into()); 40 + let pause = app.pause.into(); 41 + let duration = app 42 + .duration 43 + .map(|d| d.into()) 44 + .unwrap_or_else(|| Duration::new(u64::MAX, 0)); 45 + 46 + if let Some(delay) = delay { 47 + println!("Waiting {:?}...", delay); 48 + thread::sleep(delay); 49 + } 50 + 51 + let now = Instant::now(); 52 + 53 + let mut elapsed = now.elapsed(); 54 + while elapsed < duration { 55 + print!("\x1B[2K\rElapsed: {}", humantime::format_duration(elapsed)); 56 + thread::sleep(pause); 57 + elapsed = now.elapsed(); 58 + } 59 + 60 + print!( 61 + "\x1B[2K\rElapsed: {}\nStopped\x07", 62 + humantime::format_duration(elapsed) 63 + ); 64 + }
-111
strs
··· 1 - #!/usr/bin/env -S cargo eval -- 2 - 3 - // cargo-deps: argh = "0.1", object = "0.24.0" 4 - 5 - use std::fs::OpenOptions; 6 - use std::io::{self, BufReader, Read, Write}; 7 - use std::path::PathBuf; 8 - 9 - use argh::FromArgs; 10 - use object::{File, Object, ObjectSection, SectionKind}; 11 - 12 - const MIN_STR_LEN: usize = 4; 13 - 14 - const ELF_SIG: [u8; 4] = [0x7F, 0x45, 0x4C, 0x46]; 15 - const MACH_O_32_SIG: [u8; 4] = [0xFE, 0xED, 0xFA, 0xCE]; 16 - const MACH_O_64_SIG: [u8; 4] = [0xFE, 0xED, 0xFA, 0xCF]; 17 - const MACH_O_FAT_SIG: [u8; 4] = [0xCA, 0xFE, 0xBA, 0xBE]; 18 - const MACH_O_32_REV_SIG: [u8; 4] = [0xCE, 0xFA, 0xED, 0xFE]; 19 - const MACH_O_64_REV_SIG: [u8; 4] = [0xCF, 0xFA, 0xED, 0xFE]; 20 - 21 - #[derive(FromArgs)] 22 - #[argh(description = "Rust clone of strings(1).")] 23 - struct App { 24 - #[argh( 25 - switch, 26 - description = "ignore heuristics and read all sections of an object file." 27 - )] 28 - all: bool, 29 - 30 - #[argh( 31 - option, 32 - description = "the minimum length a valid string needs to be in order to be printed.", 33 - default = "MIN_STR_LEN" 34 - )] 35 - min_length: usize, 36 - 37 - #[argh(positional, description = "input file.")] 38 - file: PathBuf, 39 - } 40 - 41 - fn main() { 42 - let app: App = argh::from_env(); 43 - 44 - let file = OpenOptions::new() 45 - .read(true) 46 - .open(app.file) 47 - .expect("could not open source"); 48 - 49 - let mut reader = BufReader::new(file); 50 - 51 - // Preallocate 4MB of space since we're usually dealing with files 52 - let mut contents = Vec::with_capacity(1024 * 1024 * 4); 53 - reader.read_to_end(&mut contents); 54 - 55 - let stdout = io::stdout(); 56 - let mut handle = stdout.lock(); 57 - 58 - let mut magic = [0, 0, 0, 0]; 59 - if contents.len() >= 4 { 60 - magic.copy_from_slice(&contents[..4]); 61 - } 62 - 63 - if is_object_file(&magic) { 64 - print_object_strings(&mut handle, &contents, app.all, app.min_length); 65 - } else { 66 - print_strings(&mut handle, &contents, app.min_length); 67 - } 68 - } 69 - 70 - fn is_object_file(magic: &[u8; 4]) -> bool { 71 - magic == &ELF_SIG 72 - || magic == &MACH_O_FAT_SIG 73 - || magic == &MACH_O_32_SIG 74 - || magic == &MACH_O_64_SIG 75 - || magic == &MACH_O_32_REV_SIG 76 - || magic == &MACH_O_64_REV_SIG 77 - } 78 - 79 - fn print_object_strings(handle: &mut impl Write, contents: &[u8], all: bool, min_length: usize) { 80 - // We could also read this in a lazy fashion but... I'm lazy at the moment ;) 81 - let object = File::parse(contents).expect("Could not parse object file"); 82 - object 83 - .sections() 84 - .filter(|section| all || section.kind() == SectionKind::Data) 85 - .for_each(|section| { 86 - let contents = section.data().expect("section should have data"); 87 - print_strings(handle, contents, min_length); 88 - }); 89 - } 90 - 91 - fn print_strings(handle: &mut impl Write, contents: &[u8], min_length: usize) { 92 - let mut start = 0; 93 - let mut end = 0; 94 - 95 - for c in contents.iter() { 96 - let is_printable = c.is_ascii_graphic() || c.is_ascii_whitespace(); 97 - let has_minimum_len = (end - start) >= min_length; 98 - 99 - if !is_printable && has_minimum_len { 100 - handle.write(&contents[start..end]); 101 - handle.write(&[b'\n']); 102 - start = end + 1; 103 - } else if !is_printable { 104 - start = end + 1; 105 - } 106 - 107 - end += 1; 108 - } 109 - 110 - handle.flush(); 111 - }
+117
strs.rs
··· 1 + #!/usr/bin/env -S cargo +nightly -Zscript 2 + 3 + --- 4 + [package] 5 + edition = "2024" 6 + [dependencies] 7 + argh = "0.1" 8 + object = "0.24.0" 9 + --- 10 + 11 + use std::fs::OpenOptions; 12 + use std::io::{self, BufReader, Read, Write}; 13 + use std::path::PathBuf; 14 + 15 + use argh::FromArgs; 16 + use object::{File, Object, ObjectSection, SectionKind}; 17 + 18 + const MIN_STR_LEN: usize = 4; 19 + 20 + const ELF_SIG: [u8; 4] = [0x7F, 0x45, 0x4C, 0x46]; 21 + const MACH_O_32_SIG: [u8; 4] = [0xFE, 0xED, 0xFA, 0xCE]; 22 + const MACH_O_64_SIG: [u8; 4] = [0xFE, 0xED, 0xFA, 0xCF]; 23 + const MACH_O_FAT_SIG: [u8; 4] = [0xCA, 0xFE, 0xBA, 0xBE]; 24 + const MACH_O_32_REV_SIG: [u8; 4] = [0xCE, 0xFA, 0xED, 0xFE]; 25 + const MACH_O_64_REV_SIG: [u8; 4] = [0xCF, 0xFA, 0xED, 0xFE]; 26 + 27 + #[derive(FromArgs)] 28 + #[argh(description = "Rust clone of strings(1).")] 29 + struct App { 30 + #[argh( 31 + switch, 32 + description = "ignore heuristics and read all sections of an object file." 33 + )] 34 + all: bool, 35 + 36 + #[argh( 37 + option, 38 + description = "the minimum length a valid string needs to be in order to be printed.", 39 + default = "MIN_STR_LEN" 40 + )] 41 + min_length: usize, 42 + 43 + #[argh(positional, description = "input file.")] 44 + file: PathBuf, 45 + } 46 + 47 + fn main() { 48 + let app: App = argh::from_env(); 49 + 50 + let file = OpenOptions::new() 51 + .read(true) 52 + .open(app.file) 53 + .expect("could not open source"); 54 + 55 + let mut reader = BufReader::new(file); 56 + 57 + // Preallocate 4MB of space since we're usually dealing with files 58 + let mut contents = Vec::with_capacity(1024 * 1024 * 4); 59 + reader.read_to_end(&mut contents); 60 + 61 + let stdout = io::stdout(); 62 + let mut handle = stdout.lock(); 63 + 64 + let mut magic = [0, 0, 0, 0]; 65 + if contents.len() >= 4 { 66 + magic.copy_from_slice(&contents[..4]); 67 + } 68 + 69 + if is_object_file(&magic) { 70 + print_object_strings(&mut handle, &contents, app.all, app.min_length); 71 + } else { 72 + print_strings(&mut handle, &contents, app.min_length); 73 + } 74 + } 75 + 76 + fn is_object_file(magic: &[u8; 4]) -> bool { 77 + magic == &ELF_SIG 78 + || magic == &MACH_O_FAT_SIG 79 + || magic == &MACH_O_32_SIG 80 + || magic == &MACH_O_64_SIG 81 + || magic == &MACH_O_32_REV_SIG 82 + || magic == &MACH_O_64_REV_SIG 83 + } 84 + 85 + fn print_object_strings(handle: &mut impl Write, contents: &[u8], all: bool, min_length: usize) { 86 + // We could also read this in a lazy fashion but... I'm lazy at the moment ;) 87 + let object = File::parse(contents).expect("Could not parse object file"); 88 + object 89 + .sections() 90 + .filter(|section| all || section.kind() == SectionKind::Data) 91 + .for_each(|section| { 92 + let contents = section.data().expect("section should have data"); 93 + print_strings(handle, contents, min_length); 94 + }); 95 + } 96 + 97 + fn print_strings(handle: &mut impl Write, contents: &[u8], min_length: usize) { 98 + let mut start = 0; 99 + let mut end = 0; 100 + 101 + for c in contents.iter() { 102 + let is_printable = c.is_ascii_graphic() || c.is_ascii_whitespace(); 103 + let has_minimum_len = (end - start) >= min_length; 104 + 105 + if !is_printable && has_minimum_len { 106 + handle.write(&contents[start..end]); 107 + handle.write(&[b'\n']); 108 + start = end + 1; 109 + } else if !is_printable { 110 + start = end + 1; 111 + } 112 + 113 + end += 1; 114 + } 115 + 116 + handle.flush(); 117 + }
-28
tag_release
··· 1 - #!/usr/bin/env -S cargo eval -- 2 - 3 - // cargo-deps: argh = "0.1", chrono = "0.4" 4 - 5 - use argh::FromArgs; 6 - use chrono::prelude::*; 7 - 8 - #[derive(FromArgs)] 9 - #[argh(description = "Simple program to kill a process listening on a specific port.")] 10 - struct App { 11 - #[argh( 12 - option, 13 - description = "how to format the date tag.", 14 - default = "\"v%Y%m%d-%H%M\".to_string()" 15 - )] 16 - format: String, 17 - } 18 - 19 - fn main() { 20 - let app: App = argh::from_env(); 21 - 22 - let tag = Utc::now().format(&app.format).to_string(); 23 - 24 - println!("# Run the following to tag and release this version"); 25 - println!(); 26 - println!("git tag -a {} -m \"\"", tag); 27 - println!("git push origin --tags"); 28 - }
+34
tag_release.rs
··· 1 + #!/usr/bin/env -S cargo +nightly -Zscript 2 + 3 + --- 4 + [package] 5 + edition = "2024" 6 + [dependencies] 7 + argh = "0.1" 8 + crono = "0.4" 9 + --- 10 + 11 + use argh::FromArgs; 12 + use chrono::prelude::*; 13 + 14 + #[derive(FromArgs)] 15 + #[argh(description = "Utility to help tag a git release.")] 16 + struct App { 17 + #[argh( 18 + option, 19 + description = "how to format the date tag.", 20 + default = "\"v%Y%m%d-%H%M\".to_string()" 21 + )] 22 + format: String, 23 + } 24 + 25 + fn main() { 26 + let app: App = argh::from_env(); 27 + 28 + let tag = Utc::now().format(&app.format).to_string(); 29 + 30 + println!("# Run the following to tag and release this version"); 31 + println!(); 32 + println!("git tag -a {} -m \"\"", tag); 33 + println!("git push origin --tags"); 34 + }