├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── README.md ├── biglog_sort ├── Cargo.toml └── src │ ├── lib.rs │ └── main.rs ├── bitlogin ├── Cargo.toml ├── README.md ├── cli.yml └── src │ ├── errors.rs │ ├── lib.rs │ ├── main.rs │ ├── user.rs │ └── utils.rs ├── brainfuck ├── Cargo.toml ├── README.md └── src │ ├── brainfuck.pest │ ├── lib.rs │ ├── main.rs │ └── parser.rs ├── brainfuck_macro ├── Cargo.toml ├── README.md └── src │ └── main.rs ├── brainfuck_procmacro ├── Cargo.toml ├── README.md └── src │ ├── lib.rs │ └── main.rs ├── huang_li ├── Cargo.toml ├── README.md └── src │ ├── config.toml │ ├── lib.rs │ └── main.rs ├── img2ascii ├── Cargo.toml ├── README.md ├── cli.yml └── src │ ├── lib.rs │ └── main.rs ├── iostream ├── Cargo.toml ├── README.md └── src │ ├── lib.rs │ └── main.rs ├── mirage_tank ├── Cargo.toml ├── README.md ├── cli.yml └── src │ ├── lib.rs │ └── main.rs ├── some_plot ├── Cargo.toml └── src │ ├── lib.rs │ └── main.rs ├── ulogme ├── Cargo.toml ├── README.md ├── build.rs └── src │ ├── X11logger │ ├── X11logger.c │ └── common.h │ ├── keyboard.rs │ ├── lib.rs │ ├── main.rs │ ├── utils.rs │ └── window.rs └── webapi ├── Cargo.toml └── src └── main.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | .directory 4 | .idea -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "biglog_sort", 4 | "bitlogin", 5 | "brainfuck", 6 | "brainfuck_macro", 7 | "brainfuck_procmacro", 8 | "iostream", 9 | "img2ascii", 10 | "huang_li", 11 | "mirage_tank", 12 | "some_plot", 13 | "ulogme", 14 | "webapi", 15 | ] 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rust-toys 2 | 3 | 一堆用 Rust 写的小玩意儿. 4 | 5 | 放在一起节省编译时间 && 减少重复的临时文件 6 | 7 | 8 | 9 | ## 简介 10 | 11 | + [img2ascii](./img2ascii) 图片(jpg/png等)转字符画, 学习图像处理 12 | 13 | + [crc32fix](https://github.com/Aloxaf/crc32fix) 根据损坏图片的 crc32 校验值爆破宽高来修复之, 学习图像处理 14 | 15 | + [mirage_tank](./mirage_tank) 幻影坦克快速发车工具, 学习图像处理 16 | 17 | + [bitlogin](./bitlogin) 登录校园网, 学习 reqwets 18 | 19 | + [brainfuck](./brainfuck) brainfuck 解释器, 学习 pest 20 | 21 | + [iostream](./iostream) 模仿 C++ 的 cin/cout, 学习泛型, Trait 啥的 22 | 23 | + [ulogme](./ulogme) 同名项目的替代脚本, 学习多线程 24 | 25 | + [brainfuck_macro](./brainfuck_macro) 用宏实现的 brainfuck 解释器, 学习宏 26 | 27 | + [brainfuck_procmacro](./brainfuck_procmacro) 用过程宏实现的编译期 brainfuck 解释器, 学习过程宏 28 | 29 | + [huang_li](./huang_li) 程序员老黄历, 仿了一个很老的玩意儿 -------------------------------------------------------------------------------- /biglog_sort/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "biglog_sort" 3 | version = "0.1.0" 4 | authors = ["Aloxaf "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | byteorder = "1.3.1" 9 | extsort = { git = "https://github.com/Aloxaf/extsort-rs", branch = "sort_by" } 10 | fxhash = "0.2" 11 | hashbrown = { version = "0.1", features = ["rayon"] } 12 | jemallocator = "0.3" 13 | rayon = "1.0" 14 | 15 | [profile.release] 16 | lto = true 17 | opt-level = 3 18 | # debug = true 19 | -------------------------------------------------------------------------------- /biglog_sort/src/lib.rs: -------------------------------------------------------------------------------- 1 | use fxhash::hash32; 2 | use hashbrown::HashMap; 3 | use rayon::prelude::*; 4 | use std::fs::File; 5 | use std::io::{BufRead, BufReader}; 6 | 7 | /// 从字符串中提取出"字符串" 8 | pub fn get_keywords(text: &str) -> Vec<&str> { 9 | #[inline] 10 | fn type_of(b: u8) -> u8 { 11 | if b.is_ascii_alphabetic() { 12 | 1 13 | } else if b.is_ascii_digit() { 14 | 2 15 | } else { 16 | 3 17 | } 18 | } 19 | if text.len() == 0 { 20 | return vec![]; 21 | } 22 | 23 | let bytes = text.bytes().map(type_of).collect::>(); 24 | let mut ret = Vec::with_capacity(10); 25 | let mut pos = 0; 26 | 27 | for i in 0..bytes.len() - 1 { 28 | if bytes[i] != 3 && bytes[i] != bytes[i + 1] { 29 | ret.push(&text[pos..=i]); 30 | pos = i + 1; 31 | } else if bytes[i] == 3 { 32 | pos = i + 1; 33 | } 34 | } 35 | if bytes[pos] != 3 && bytes[pos] == bytes[bytes.len() - 1] { 36 | ret.push(&text[pos..]); 37 | } 38 | 39 | ret 40 | } 41 | 42 | /// 读取文件, 确定每个"字符串"出现的次数 43 | pub fn make_keyword_map(path: &str) -> HashMap { 44 | let file = BufReader::new(File::open(path).expect("无法打开文件")); 45 | let mut ret = HashMap::with_capacity(10000); 46 | 47 | for line in file.lines() { 48 | let line = line.unwrap(); 49 | for keyword in get_keywords(&line) { 50 | *ret.entry(hash32(keyword)).or_insert(0) += 1; 51 | } 52 | } 53 | 54 | ret 55 | } 56 | 57 | /// 根据每行的字符串出现次数生成一个"特征值" 58 | pub fn make_line_map( 59 | path: &str, 60 | keyword_map: &HashMap, 61 | ) -> (usize, HashMap>) { 62 | let file = BufReader::new(File::open(path).expect("无法打开文件")); 63 | let mut line_cnt = 0; 64 | let mut ret = HashMap::with_capacity(10000); 65 | 66 | for (idx, line) in file.lines().enumerate() { 67 | let line = line.unwrap(); 68 | let keywords = get_keywords(&line); 69 | 70 | let occurs_cnt = keywords 71 | .iter() 72 | .map(|&s| *keyword_map.get(&hash32(s)).unwrap()) 73 | .max() 74 | .unwrap_or(0); 75 | 76 | ret.entry(occurs_cnt).or_insert(vec![]).push(idx as u32); 77 | line_cnt = idx; 78 | } 79 | 80 | (line_cnt, ret) 81 | } 82 | 83 | pub fn make_line_order(line_cnt: usize, line_map: &HashMap>) -> Vec { 84 | // 排序 85 | let mut keys = line_map.keys().collect::>(); 86 | keys.par_sort_unstable_by(|a, b| b.cmp(a)); 87 | 88 | // 确定每行的顺序 89 | let mut line_order = vec![0; line_cnt + 1]; // TODO: 我没记错的话当 line_cnt 很大时会出问题 90 | let mut order = 0 as u32; 91 | for key in keys { 92 | let idxs = line_map.get(key).unwrap(); 93 | for &idx in idxs { 94 | line_order[idx as usize] = order; 95 | order += 1; 96 | } 97 | } 98 | line_order 99 | } 100 | -------------------------------------------------------------------------------- /biglog_sort/src/main.rs: -------------------------------------------------------------------------------- 1 | use biglog_sort::*; 2 | use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; 3 | use extsort::*; 4 | use std::fs::File; 5 | use std::io::{self, prelude::*, BufReader}; 6 | 7 | #[global_allocator] 8 | static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; 9 | 10 | #[derive(Debug, Ord, PartialOrd, Eq, PartialEq)] 11 | struct Line(u32, String); 12 | 13 | impl Sortable for Line { 14 | #[inline] 15 | fn encode(item: Line, write: &mut Write) { 16 | write.write_u32::(item.0).unwrap(); 17 | write.write(item.1.as_bytes()).unwrap(); 18 | write.write(&[b'\n']).unwrap(); 19 | } 20 | 21 | #[inline] 22 | fn decode(read: &mut Read) -> Option { 23 | let idx = read.read_u32::().ok()?; 24 | let mut bytes = read.bytes(); 25 | let s = String::from_utf8( 26 | bytes 27 | .by_ref() 28 | .map(Result::unwrap) 29 | .take_while(|b| *b != b'\n') 30 | .collect(), 31 | ) 32 | .unwrap(); 33 | Some(Line(idx, s)) 34 | } 35 | } 36 | 37 | fn main() { 38 | let args = std::env::args().collect::>(); 39 | if args.len() == 1 { 40 | println!("Usage: {} FILE", args[0]); 41 | return; 42 | } 43 | let path = &args[1]; 44 | 45 | let keyword_map = make_keyword_map(path); 46 | let (line_cnt, line_map) = make_line_map(path, &keyword_map); 47 | std::mem::drop(keyword_map); // 立即销毁, 节省内存(大概 48 | let line_order = make_line_order(line_cnt, &line_map); 49 | std::mem::drop(line_map); 50 | 51 | // 进行外排序 52 | let file = BufReader::new(File::open(path).expect("无法打开文件")); 53 | let mut sorter = ExternalSorter::new(); 54 | sorter.set_max_size(1 * 1024 * 1024 * 1024 / 128); // 按每行 128 字节算, 1G 内存每次最多载入多少行 55 | let sorted_iter = sorter 56 | .sort_by( 57 | file.lines() 58 | .zip(line_order.iter()) 59 | .map(|(s, idx)| Line(*idx, s.unwrap())), 60 | |a, b| a.0.cmp(&b.0), 61 | ) 62 | .unwrap(); 63 | 64 | // 输出 65 | let stdout = io::stdout(); 66 | let mut handle = stdout.lock(); 67 | for line in sorted_iter { 68 | handle.write(line.1.as_bytes()).unwrap(); 69 | handle.write(&[b'\n']).unwrap(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /bitlogin/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bitlogin" 3 | version = "1.0.0" 4 | authors = ["Aloxaf "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | reqwest = "0.9.1" 9 | json = "0.11.13" 10 | nix = "0.11.0" 11 | failure = "0.1.2" 12 | hmac = "0.6.3" 13 | md-5 = "0.7.0" 14 | base64 = "0.9.3" 15 | byteorder = "1.2.6" 16 | sha-1 = "0.7.0" 17 | clap = {version = "2.32.0", features = ["yaml"]} 18 | regex = "1.0.5" 19 | maplit = "1.0.1" 20 | -------------------------------------------------------------------------------- /bitlogin/README.md: -------------------------------------------------------------------------------- 1 | bitlogin 2 | --- 3 | 4 | 10_0_0_55_login 的 rust 实现, 学习一下网络相关的库. 5 | 6 | 代码写的跟翔一样. -------------------------------------------------------------------------------- /bitlogin/cli.yml: -------------------------------------------------------------------------------- 1 | name: bitlogin 2 | version: "1.0.0" 3 | author: Aloxaf 4 | about: 北理工校园网登录 5 | args: 6 | - action: 7 | help: 动作 (login/logout) 8 | required: true 9 | - username: 10 | short: u 11 | help: 用户名(学号) 12 | required: true 13 | takes_value: true 14 | - password: 15 | short: p 16 | help: 密码 17 | required: true 18 | takes_value: true 19 | - type: 20 | help: 校园网类型, 登录时可选, 注销时必需 (web/mobile/library) -------------------------------------------------------------------------------- /bitlogin/src/errors.rs: -------------------------------------------------------------------------------- 1 | use failure::{Error, Fail}; 2 | use regex; 3 | use reqwest; 4 | use std::fmt; 5 | use std::option::NoneError; 6 | 7 | #[derive(Fail, Debug)] 8 | pub enum MyError { 9 | #[fail(display = "NoneValue: ")] 10 | NoneValue, 11 | #[fail(display = "NornalError: ")] 12 | NormalError, 13 | } 14 | 15 | impl From for MyError { 16 | fn from(_err: NoneError) -> Self { 17 | MyError::NoneValue 18 | } 19 | } 20 | 21 | impl From for MyError { 22 | fn from(_err: Error) -> Self { 23 | MyError::NormalError 24 | } 25 | } 26 | 27 | impl From for MyError { 28 | fn from(_err: regex::Error) -> Self { 29 | MyError::NormalError 30 | } 31 | } 32 | 33 | impl From for MyError { 34 | fn from(_err: reqwest::Error) -> Self { 35 | MyError::NormalError 36 | } 37 | } 38 | 39 | #[derive(Fail, Debug)] 40 | pub enum GetAcidError { 41 | NetWorkError, 42 | CannotFindError, 43 | AlreadyLogin, 44 | } 45 | 46 | impl fmt::Display for GetAcidError { 47 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 48 | match *self { 49 | GetAcidError::NetWorkError => write!(f, "Network error!"), 50 | GetAcidError::CannotFindError => write!(f, "Cannot detect acid!"), 51 | GetAcidError::AlreadyLogin => write!(f, "Already login!"), 52 | } 53 | } 54 | } 55 | 56 | impl From for GetAcidError { 57 | fn from(_err: NoneError) -> Self { 58 | GetAcidError::CannotFindError 59 | } 60 | } 61 | 62 | impl From for GetAcidError { 63 | fn from(_err: regex::Error) -> Self { 64 | GetAcidError::CannotFindError 65 | } 66 | } 67 | 68 | impl From for GetAcidError { 69 | fn from(_err: reqwest::Error) -> Self { 70 | GetAcidError::NetWorkError 71 | } 72 | } 73 | 74 | // 75 | // 76 | //pub enum ErrorKind { 77 | // AuthFailed(String), 78 | // GetAcidError(String), 79 | // NetworkError(String), 80 | //} 81 | // 82 | //impl fmt::Display for ErrorKind { 83 | // fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 84 | // match *self { 85 | // ErrorKind::AuthFailed(ref c) => write!(f, "{}", c), 86 | // ErrorKind::GetAcidError(ref c) => write!(f, "{}", c), 87 | // ErrorKind::NetworkError(ref c) => write!(f, "{}", c), 88 | // } 89 | // } 90 | //} 91 | -------------------------------------------------------------------------------- /bitlogin/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(try_trait)] 2 | mod errors; 3 | mod user; 4 | mod utils; 5 | 6 | pub use crate::errors::{GetAcidError, MyError}; 7 | pub use crate::user::User; 8 | -------------------------------------------------------------------------------- /bitlogin/src/main.rs: -------------------------------------------------------------------------------- 1 | #![feature(try_trait)] 2 | 3 | use bitlogin::{MyError, User}; 4 | use clap::{load_yaml, App}; 5 | use maplit::hashmap; 6 | 7 | fn run() -> Result<(), MyError> { 8 | let yaml = load_yaml!("../cli.yml"); 9 | let matches = App::from_yaml(yaml).get_matches(); 10 | 11 | let map = hashmap!{ 12 | "web" => "8", 13 | "mobile" => "1", 14 | "library" => "1", 15 | }; 16 | 17 | let action = matches.value_of("action")?; 18 | let username = matches.value_of("username")?; 19 | let password = matches.value_of("password")?; 20 | 21 | let user = User::new(username, password)?; 22 | 23 | if action == "login" { 24 | println!("{}", user.login()?.pretty(2)); 25 | } else if action == "logout" { 26 | let wlan_type = matches.value_of("type").unwrap_or("web"); 27 | let acid = map.get(wlan_type)?; 28 | println!("{}", user.logout(acid)?.pretty(2)); 29 | } 30 | 31 | Ok(()) 32 | } 33 | 34 | fn main() { 35 | match run() { 36 | Ok(_t) => (), 37 | Err(e) => eprintln!("{}", e), 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /bitlogin/src/user.rs: -------------------------------------------------------------------------------- 1 | use crate::{utils, GetAcidError}; 2 | use failure::Error; 3 | use json::{object, JsonValue}; 4 | use regex::Regex; 5 | use reqwest; 6 | use sha1::{Digest, Sha1}; 7 | 8 | pub struct User<'a> { 9 | api: &'a str, 10 | client: reqwest::Client, 11 | param_n: &'a str, 12 | param_type: &'a str, 13 | username: &'a str, 14 | password: &'a str, 15 | } 16 | 17 | // FIXME: 乱七八糟的生命周期, clone 什么的 18 | impl<'a> User<'a> { 19 | pub fn new<'b>(username: &'b str, password: &'b str) -> Result, Error> { 20 | Ok(User { 21 | api: "http://10.0.0.55/cgi-bin", 22 | client: reqwest::Client::new(), 23 | param_n: "200", 24 | param_type: "1", 25 | username, 26 | password, 27 | }) 28 | } 29 | 30 | /// 获取 acid 31 | /// 用于判断 wifi 类型 32 | fn get_acid() -> Result { 33 | let client = reqwest::Client::new(); 34 | let re = Regex::new(r"index_(\d)+\.html").unwrap(); 35 | let mut res = client.get("http://detectportal.firefox.com/").send()?; 36 | let text = res.text()?; 37 | 38 | if text == "success" { 39 | return Err(GetAcidError::AlreadyLogin); 40 | } 41 | 42 | let caps = re.captures(&text)?; 43 | Ok(caps.get(1)?.as_str().to_string()) 44 | } 45 | 46 | /// 获取登录 token 47 | fn get_token(&self, ip: &str) -> Result { 48 | let params = [ 49 | ("callback", "jsonp"), 50 | ("username", self.username), 51 | ("ip", ip), 52 | ]; 53 | let mut res = self 54 | .client 55 | .get(&format!("{}/get_challenge", self.api)) 56 | .query(¶ms) 57 | .send()?; 58 | let data = res.text()?; 59 | // raw data: jsonp({xxxx}) 60 | let data = json::parse(&data[6..data.len() - 1])?; 61 | Ok(data["challenge"].to_string()) 62 | } 63 | 64 | /// 计算 sha1 返回 digest 65 | fn sha1(s: &str) -> String { 66 | let mut sh = Sha1::default(); 67 | sh.input(&s.bytes().collect::>()); 68 | let result = sh 69 | .result() 70 | .iter() 71 | .map(|&c| format!("{:02x}", c)) 72 | .collect::>(); 73 | result.join("") 74 | } 75 | 76 | /// 构造登录登出需要的参数 77 | fn make_params(&self, action: &str, acid: &str) -> [(&str, String); 10] { 78 | let ip = utils::get_host_ip().unwrap(); 79 | let token = self.get_token(&ip).unwrap(); 80 | let data = object!{ 81 | "username" => self.username, 82 | "password" => self.password, 83 | "acid" => acid, 84 | "ip" => ip.as_str(), 85 | "enc_ver" => "srun_bx1", 86 | }; 87 | let hmd5 = utils::hmacencode(&token, ""); 88 | let json_data = data.dump(); 89 | let info = String::from("{SRBX1}") + &utils::fkbase64(&utils::xencode(&json_data, &token)); 90 | 91 | let chksum = User::sha1(&format!( 92 | "{0}{1}{0}{2}{0}{3}{0}{4}{0}{5}{0}{6}{0}{7}", 93 | token, self.username, hmd5, acid, ip, self.param_n, self.param_type, info 94 | )); 95 | 96 | let params = [ 97 | ("callback", "jsonp".to_string()), 98 | ("username", self.username.to_string()), 99 | ("action", action.to_string()), 100 | ("ac_id", acid.to_string()), 101 | ("type", self.param_type.to_string()), 102 | ("ip", ip), 103 | ("n", self.param_n.to_string()), 104 | ("password", (String::from("{MD5}") + &hmd5)), 105 | ("chksum", chksum), 106 | ("info", info), 107 | ]; 108 | 109 | params 110 | } 111 | 112 | pub fn login(&self) -> Result { 113 | let acid = User::get_acid()?; 114 | let params = self.make_params("login", &acid); 115 | let mut res = self 116 | .client 117 | .get(&format!("{}/srun_portal", self.api)) 118 | .query(¶ms) 119 | .send() 120 | .unwrap(); 121 | // println!("{}", res.url()); 122 | // println!("-- {}", res.text().unwrap()); 123 | let data = res.text().unwrap(); 124 | // println!("-- {}", data); 125 | Ok(json::parse(&data[6..data.len() - 1])?) 126 | } 127 | 128 | pub fn logout(&self, acid: &str) -> Result { 129 | let params = self.make_params("logout", acid); 130 | let mut res = self 131 | .client 132 | .get(&format!("{}/srun_portal", self.api)) 133 | .query(¶ms) 134 | .send() 135 | .unwrap(); 136 | let data = res.text().unwrap(); 137 | Ok(json::parse(&data[6..data.len() - 1])?) 138 | } 139 | } 140 | 141 | #[cfg(test)] 142 | mod test { 143 | use crate::User; 144 | #[test] 145 | fn test_make_params() { 146 | let user = User::new("1120172179", "Fak3_Pa3sw07d"); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /bitlogin/src/utils.rs: -------------------------------------------------------------------------------- 1 | use byteorder::{ByteOrder, LittleEndian}; 2 | use hmac::{Hmac, Mac}; 3 | use md5::Md5; 4 | use std::str; 5 | 6 | // FIXME: 这方法太TM挫了 7 | pub fn get_host_ip() -> Result { 8 | let addrs = nix::ifaddrs::getifaddrs().unwrap(); 9 | for ifaddr in addrs { 10 | if let Some(address) = ifaddr.address { 11 | let ip = address.to_str(); 12 | if &ip[0..2] == "10" { 13 | return Ok(String::from(&ip[..ip.len() - 2])); 14 | } 15 | } 16 | } 17 | Err(()) 18 | } 19 | 20 | /// 使用 HMAC 算法计算数据的哈希, 这里采用了 MD5 21 | /// 22 | /// Example: 23 | /// 24 | /// ``` 25 | /// assert_eq!("7afed10b1e1bc70b8a428a44754de091", &hmacencode("123", "")); 26 | /// ``` 27 | pub fn hmacencode(key: &str, mes: &str) -> String { 28 | let mut mac = Hmac::::new_varkey(&key.bytes().collect::>()).unwrap(); 29 | mac.input(&mes.bytes().collect::>()); 30 | let result = mac.result().code(); 31 | let bytes = result 32 | .iter() 33 | .map(|&c| format!("{:02x}", c)) 34 | .collect::>(); 35 | bytes.join("") 36 | } 37 | 38 | /// 处理魔改 base64 39 | /// 主要是多字节字符只取低八位 & 自定义字符集 40 | /// 41 | /// Example: 42 | /// 43 | /// ``` 44 | /// assert_eq!("ZaRk", fkbase64("abc")); 45 | /// assert_eq!("o/qi", fkbase64("伊莉雅")); 46 | /// ``` 47 | pub fn fkbase64(s: &str) -> String { 48 | let s = s.chars().map(|c| c as u8).collect::>(); 49 | let digest = base64::encode(&s); 50 | let old = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; 51 | let new = "LVoJPiCN2R8G90yg+hmFHuacZ1OWMnrsSTXkYpUq/3dlbfKwv6xztjI7DeBE45QA=" 52 | .chars() 53 | .collect::>(); 54 | digest 55 | .chars() 56 | .map(|c| new[old.find(c).unwrap()]) 57 | .collect::() 58 | } 59 | 60 | /// 将字符串按小端序转为整数 61 | /// 62 | /// Example 63 | /// ``` 64 | /// assert_eq!(vec![1684234849, 101, 5], unpack("abcde", true)) 65 | /// ``` 66 | fn unpack(msg: &str, key: bool) -> Vec { 67 | let len = msg.len(); 68 | let cnt = if len % 4 == 0 { 0 } else { 4 - len % 4 }; 69 | let msg = String::from(msg) + &"\x00".repeat(cnt); 70 | 71 | let mut pwd: Vec = msg 72 | .bytes() 73 | .collect::>() 74 | .chunks(4) 75 | .map(|c| LittleEndian::read_u32(c)) 76 | .collect(); 77 | 78 | if key { 79 | pwd.push(len as u32) 80 | } 81 | pwd 82 | } 83 | 84 | /// 将整数数组根据小端序转为字符串 85 | /// 86 | /// Example 87 | /// ``` 88 | /// assert_eq!("abcde\x00\x00\x00", &pack(&[0x64636261, 101], false)); 89 | /// ``` 90 | fn pack(msg: &[u32], _key: bool) -> String { 91 | let mut bytes: Vec = Vec::new(); 92 | msg.iter().for_each(|&i| { 93 | let mut buf = [0u8; 4]; 94 | LittleEndian::write_u32(&mut buf, i); 95 | bytes.extend(buf.iter()); 96 | }); 97 | bytes.iter().map(|&c| c as char).collect() 98 | } 99 | 100 | /// 我也不知道这是什么加密方式... 101 | /// 对着 Python 版本翻译的... 102 | /// 而 Python 版本是对着 js 版本翻译的... 103 | /// 104 | /// Example 105 | /// ``` 106 | /// assert_eq!("êÛ\x1d]Ó0c1", xencode("abc", "def")); 107 | /// ``` 108 | pub fn xencode(msg: &str, key: &str) -> String { 109 | if msg == "" { 110 | return String::new(); 111 | } 112 | 113 | let pwd = unpack(msg, true); 114 | let pwdk = unpack(key, false); 115 | 116 | let mut pwd = pwd.iter().map(|&c| c as u64).collect::>(); 117 | let mut pwdk = pwdk.iter().map(|&c| c as u64).collect::>(); 118 | 119 | //println!("{:?}\n{:?}", pwd, pwdk); 120 | 121 | if pwdk.len() < 4 { 122 | for _ in 0..4 - pwdk.len() { 123 | pwdk.push(0); 124 | } 125 | } 126 | let n: u64 = pwd.len() as u64 - 1; 127 | let c: u64 = 0x86014019 | 0x183639A0; 128 | let mut z = pwd[n as usize]; 129 | let (mut y, mut m, mut e, mut p): (u64, u64, u64, u64); 130 | let mut d = 0u64; 131 | let mut q = (6.0 + 52.0 / (n as f32 + 1.0)).floor(); 132 | 133 | while 0.0 < q { 134 | d = d + c & (0x8CE0D9BF | 0x731F2640); 135 | e = (d >> 2) & 3; 136 | p = 0; 137 | 138 | while p < n { 139 | y = pwd[(p + 1) as usize]; 140 | m = (z >> 5) ^ (y << 2); 141 | m += ((y >> 3) ^ (z << 4)) ^ (d ^ y); 142 | m += pwdk[((p & 3) ^ e) as usize] ^ z; 143 | pwd[p as usize] = (pwd[p as usize] + m) & (0xEFB8D130 | 0x10472ECF); 144 | z = pwd[p as usize]; 145 | p = p + 1; 146 | } 147 | 148 | y = pwd[0]; 149 | m = (z >> 5) ^ (y << 2); 150 | // println!(" {}", m); 151 | m += (y >> 3) ^ (z << 4) ^ (d ^ y); 152 | // println!(" {}", m); 153 | m += pwdk[((p & 3) ^ e) as usize] ^ z; 154 | pwd[n as usize] = (pwd[p as usize] + m) & (0xBB390742 | 0x44C6F8BD); 155 | z = pwd[n as usize]; 156 | q -= 1.0; 157 | } 158 | let pwd = pwd.iter().map(|&c| c as u32).collect::>(); 159 | pack(&pwd, false) 160 | } 161 | 162 | #[cfg(test)] 163 | mod test { 164 | use super::{fkbase64, hmacencode, pack, unpack, xencode}; 165 | 166 | #[test] 167 | fn test_hmac() { 168 | assert_eq!("7afed10b1e1bc70b8a428a44754de091", &hmacencode("123", "")); 169 | } 170 | 171 | #[test] 172 | fn test_fkbase64() { 173 | assert_eq!("ZaRk", fkbase64("abc")); 174 | assert_eq!("o/qi", fkbase64("伊莉雅")); 175 | } 176 | 177 | #[test] 178 | fn test_pack() { 179 | assert_eq!("abcde\x00\x00\x00", &pack(&[0x64636261, 101], false)); 180 | } 181 | 182 | #[test] 183 | fn test_unpack() { 184 | assert_eq!(vec![1684234849, 101, 5], unpack("abcde", true)); 185 | } 186 | 187 | #[test] 188 | fn test_xencode() { 189 | assert_eq!("êÛ\x1d]Ó0c1", xencode("abc", "def")); 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /brainfuck/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "brainfuck" 3 | version = "0.1.0" 4 | authors = ["Aloxaf "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | pest = "2.0.1" 9 | pest_derive = "2.0.1" 10 | podio = "0.1.6" 11 | -------------------------------------------------------------------------------- /brainfuck/README.md: -------------------------------------------------------------------------------- 1 | # Brainfuck 解释器 2 | 练习 pest 库. 3 | 4 | 写起来挺简单直观的, 就是第一次用踩坑比较难受, 5 | 比如改 brainfuck.pest 并不会触发更新, 重新 build 后用的还是原来的 pest -------------------------------------------------------------------------------- /brainfuck/src/brainfuck.pest: -------------------------------------------------------------------------------- 1 | WHITESPACE = _{ " " | "\t" } 2 | incp = { ">" } 3 | decp = { "<" } 4 | incd = { "+" } 5 | decd = { "-" } 6 | accept = { "," } 7 | output = { "." } 8 | loop_start = _{ "[" } 9 | loop_end = _{ "]" } 10 | 11 | cmd = _{ incp | decp | incd | decd | accept | output } 12 | loop_body = { loop_start ~ (cmd | loop_body)+ ~ loop_end } 13 | block = { cmd* ~ loop_body* ~ cmd* } 14 | 15 | code = _{SOI ~ block ~ EOI} -------------------------------------------------------------------------------- /brainfuck/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod parser; 2 | 3 | pub use crate::parser::{BrainFKParser, Rule}; 4 | use pest::{iterators::Pair, Parser}; 5 | use podio::ReadPodExt; 6 | use std::{ 7 | error, fmt, 8 | io::{self, Read, Write}, 9 | }; 10 | 11 | #[derive(Debug, Clone)] 12 | pub struct Interpreter { 13 | data: Vec, 14 | size: usize, 15 | istream: R, 16 | ostream: W, 17 | /// 当前指针位置 18 | pub index: usize, 19 | } 20 | 21 | #[derive(Debug)] 22 | pub enum Error { 23 | /// 语法分析错误 24 | ParseError(pest::error::Error), 25 | /// 指针越界 26 | StackPointerOutOfBoundary, 27 | /// IO错误 28 | IOError(io::Error), 29 | } 30 | 31 | impl From for Error { 32 | fn from(e: io::Error) -> Self { 33 | Error::IOError(e) 34 | } 35 | } 36 | 37 | impl fmt::Display for Error { 38 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 39 | match *self { 40 | Error::ParseError(ref err) => write!(f, "Parse Error:\n{}", err), 41 | Error::StackPointerOutOfBoundary => write!(f, "Stack pointer out of boundary."), 42 | Error::IOError(ref err) => write!(f, "IO Error: {}", err), 43 | } 44 | } 45 | } 46 | 47 | impl error::Error for Error { 48 | fn description(&self) -> &str { 49 | match *self { 50 | Error::ParseError(ref err) => err.description(), 51 | Error::StackPointerOutOfBoundary => "Stack pointer out of boundary.", 52 | Error::IOError(ref err) => err.description(), 53 | } 54 | } 55 | 56 | fn cause(&self) -> Option<&error::Error> { 57 | match *self { 58 | Error::IOError(ref err) => Some(err), 59 | _ => None, 60 | } 61 | } 62 | } 63 | 64 | impl Interpreter 65 | where 66 | R: Read, 67 | W: Write, 68 | { 69 | /// 实例化一个解释器\ 70 | /// 71 | /// # 参数 72 | /// 73 | /// `size` - 堆栈大小\ 74 | /// `istream` - 输入流\ 75 | /// `ostream` - 输出流 76 | pub fn new(size: usize, istream: R, ostream: W) -> Interpreter { 77 | Interpreter { 78 | data: vec![0; size], 79 | index: 0, 80 | size, 81 | istream, 82 | ostream, 83 | } 84 | } 85 | 86 | /// 指针指向的cell数值+1 87 | fn incd(&mut self) { 88 | self.data[self.index] = self.data[self.index].wrapping_add(1); 89 | } 90 | 91 | /// 指针指向的cell数值-1 92 | fn decd(&mut self) { 93 | self.data[self.index] = self.data[self.index].wrapping_sub(1); 94 | } 95 | 96 | /// 指针+1 97 | fn incp(&mut self) -> Result<(), Error> { 98 | self.index += 1; 99 | if self.index < self.size { 100 | Ok(()) 101 | } else { 102 | Err(Error::StackPointerOutOfBoundary) 103 | } 104 | } 105 | 106 | /// 指针-1 107 | fn decp(&mut self) -> Result<(), Error> { 108 | if self.index > 0 { 109 | self.index -= 1; 110 | Ok(()) 111 | } else { 112 | Err(Error::StackPointerOutOfBoundary) 113 | } 114 | } 115 | 116 | /// 读取当前cell的数据 117 | fn read(&self) -> u8 { 118 | self.data[self.index] 119 | } 120 | 121 | /// 往当前cell写入数据 122 | fn write(&mut self, n: u8) { 123 | self.data[self.index] = n; 124 | } 125 | 126 | /// 执行一段代码 127 | pub fn eval(&mut self, code: &str) -> Result<(), Error> { 128 | fn parse(code: &str) -> Result, pest::error::Error> { 129 | match BrainFKParser::parse(Rule::code, &code.trim()) { 130 | Ok(mut tokens) => Ok(tokens.next().unwrap()), 131 | Err(e) => Err(e), 132 | } 133 | } 134 | let tokens = parse(code.trim()).map_err(Error::ParseError)?; 135 | self.eval_tokens(&tokens, false) 136 | } 137 | 138 | /// 运行 `Tokens` 139 | pub fn eval_tokens(&mut self, tokens: &Pair, _loop: bool) -> Result<(), Error> { 140 | while !(_loop && self.read() == 0) { 141 | for op in tokens.clone().into_inner() { 142 | match op.as_rule() { 143 | Rule::incp => self.incp()?, 144 | Rule::decp => self.decp()?, 145 | Rule::incd => self.incd(), 146 | Rule::decd => self.decd(), 147 | Rule::accept => { 148 | let input = self.istream.read_u8()?; 149 | self.write(input); 150 | } 151 | Rule::output => { 152 | let output = self.read() as char; 153 | write!(self.ostream, "{}", output)?; 154 | } 155 | Rule::loop_body => self.eval_tokens(&op, true)?, 156 | _ => unreachable!(), 157 | } 158 | self.ostream.flush()?; 159 | } 160 | if !(_loop && self.read() != 0) { 161 | break; 162 | } 163 | } 164 | Ok(()) 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /brainfuck/src/main.rs: -------------------------------------------------------------------------------- 1 | use brainfuck::Interpreter; 2 | use std::io::{self, Read}; 3 | 4 | fn main() { 5 | let mut interpreter = Interpreter::new(256, io::stdin(), io::stdout()); 6 | interpreter.index = 100; 7 | 8 | let mut code = String::new(); 9 | io::stdin().read_to_string(&mut code).unwrap(); 10 | 11 | interpreter.eval(&code.trim()).unwrap_or_else(|e| { 12 | eprintln!("{}", e); 13 | }); 14 | 15 | // // +[-[<<[+[--->]-[<<<]]]>>>-]>-.---.>..>.<<<<-.<+.>>>>>.>.<<.<-. 16 | } 17 | -------------------------------------------------------------------------------- /brainfuck/src/parser.rs: -------------------------------------------------------------------------------- 1 | use pest_derive::Parser; 2 | 3 | #[derive(Parser)] 4 | #[grammar = "brainfuck.pest"] 5 | pub struct BrainFKParser; 6 | -------------------------------------------------------------------------------- /brainfuck_macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "brainfuck_macro" 3 | version = "0.1.0" 4 | authors = ["Aloxaf "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /brainfuck_macro/README.md: -------------------------------------------------------------------------------- 1 | # brainfuck_macro 2 | 学习一下宏 3 | 4 | Rust 的宏真棒 5 | 6 | # 用法 7 | ```rust 8 | fn main() { 9 | brainfuck!(+ [ - [ < < [ + [ - - - > ] - [ < < < ] ] ] > > > - ] > 10 | - . - - -. > . . > . < < < < - . < + . > > > > > . > . < < . < - .); 11 | } 12 | ``` -------------------------------------------------------------------------------- /brainfuck_macro/src/main.rs: -------------------------------------------------------------------------------- 1 | macro_rules! brainfuck { 2 | (@start $($e:tt)*) => { 3 | { 4 | let mut stack = vec![0u8; 256]; 5 | let mut index = 0; 6 | let mut output = Vec::new(); 7 | brainfuck!(@eval (stack, index, output); $($e)*); 8 | String::from_utf8(output.clone()).unwrap() 9 | } 10 | }; 11 | (@eval ($stack:expr, $index:expr, $output:expr); + $($tail:tt)*) => { 12 | $stack[$index] = $stack[$index].wrapping_add(1); 13 | brainfuck!(@eval ($stack, $index, $output); $($tail)*); 14 | }; 15 | (@eval ($stack:expr, $index:expr, $output:expr); - $($tail:tt)*) => { 16 | $stack[$index] = $stack[$index].wrapping_sub(1); 17 | brainfuck!(@eval ($stack, $index, $output); $($tail)*); 18 | }; 19 | (@eval ($stack:expr, $index:expr, $output:expr); > $($tail:tt)*) => { 20 | $index += 1; 21 | brainfuck!(@eval ($stack, $index, $output); $($tail)*); 22 | }; 23 | (@eval ($stack:expr, $index:expr, $output:expr); < $($tail:tt)*) => { 24 | $index -= 1; 25 | brainfuck!(@eval ($stack, $index, $output); $($tail)*); 26 | }; 27 | (@eval ($stack:expr, $index:expr, $output:expr); , $($tail:tt)* ) => { 28 | io::stdin().read(&mut $stack[$index..$index+1]); 29 | brainfuck!(@eval ($stack, $index, $output); $($tail)*); 30 | }; 31 | (@eval ($stack:expr, $index:expr, $output:expr); . $($tail:tt)*) => { 32 | $output.push($stack[$index]); 33 | brainfuck!(@eval ($stack, $index, $output); $($tail)*); 34 | }; 35 | (@eval ($stack:expr, $index:expr, $output:expr); [ $($body:tt)* ] $($tail:tt)* ) => { 36 | while $stack[$index] != 0 { 37 | brainfuck!(@eval ($stack, $index, $output); $($body)*); 38 | } 39 | brainfuck!(@eval ($stack, $index, $output); $($tail)*); 40 | }; 41 | (@eval ($stack:expr, $index:expr, $output:expr);) => { }; 42 | ($($e:tt)*) => { 43 | brainfuck!(@start $($e)*); 44 | }; 45 | } 46 | 47 | fn main() { 48 | let s = brainfuck!(+ [ - [ < < [ + [ - - - > ] - [ < < < ] ] ] > > > - ] > 49 | - . - - -. > . . > . < < < < - . < + . > > > > > . > . < < . < - .); 50 | println!("{}", s); 51 | } 52 | -------------------------------------------------------------------------------- /brainfuck_procmacro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "brainfuck_procmacro" 3 | version = "0.1.0" 4 | authors = ["Aloxaf "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | syn = "0.15.16" 9 | quote = "0.6.9" 10 | 11 | [lib] 12 | proc-macro = true 13 | -------------------------------------------------------------------------------- /brainfuck_procmacro/README.md: -------------------------------------------------------------------------------- 1 | # brainfuck_promacro 2 | 3 | 过程宏 NB! -------------------------------------------------------------------------------- /brainfuck_procmacro/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(extern_crate_item_prelude)] 2 | #![feature(trace_macros)] 3 | extern crate proc_macro; 4 | extern crate syn; 5 | extern crate quote; 6 | 7 | use proc_macro::TokenStream; 8 | 9 | trace_macros!(true); 10 | 11 | #[proc_macro] 12 | pub fn brainfuck(input: TokenStream) -> TokenStream { 13 | fn calc_offset(code: &[u8], rev: bool) -> usize { 14 | let mut code = code.to_owned(); 15 | if rev { 16 | code.reverse(); 17 | } 18 | code.iter().map(|&c| match c { 19 | b'[' => 1, 20 | b']' => -1, 21 | _ => 0, 22 | }).scan(0, |s, c| { 23 | *s += c; 24 | Some(*s) 25 | }).enumerate().skip_while(|&(_i, c)| { 26 | c != 0 27 | }).next().unwrap().0 28 | } 29 | 30 | fn brain_luck(code: &str, input: Vec) -> Vec { 31 | let mut input = input.iter(); 32 | let mut stack: Vec = Vec::new(); 33 | let mut output: Vec = Vec::new(); 34 | let (mut index, mut code_index): (usize, usize) = (0, 0); 35 | let len = code.len(); 36 | let code: Vec = code.chars().map(|x| x as u8).collect(); 37 | 38 | stack.resize(15, 0); 39 | while code_index < len { 40 | let c = code[code_index]; 41 | 42 | if stack.len() <= index { 43 | stack.resize(index + 1, 0); 44 | } 45 | match c { 46 | b'>' => index += 1, 47 | b'<' => index -= 1, 48 | b'+' => stack[index] = stack[index].wrapping_add(1), 49 | b'-' => stack[index] = stack[index].wrapping_sub(1), 50 | b',' => stack[index] = *input.next().unwrap(), 51 | b'.' => output.push(stack[index]), 52 | b'[' => { 53 | if stack[index] == 0 { 54 | code_index += calc_offset(&code[code_index..], false); 55 | } 56 | } 57 | b']' => { 58 | if stack[index] != 0 { 59 | code_index -= calc_offset(&code[..=code_index], true); 60 | } 61 | } 62 | _ => (), 63 | } 64 | code_index += 1; 65 | } 66 | output 67 | } 68 | 69 | let n = input.to_string(); 70 | let b = brain_luck(&n, vec![]); 71 | format!(r#""{}""#, String::from_utf8(b).unwrap()).parse().unwrap() 72 | } 73 | -------------------------------------------------------------------------------- /brainfuck_procmacro/src/main.rs: -------------------------------------------------------------------------------- 1 | #![feature(trace_macros)] 2 | #![feature(proc_macro_hygiene)] 3 | 4 | extern crate brainfuck_procmacro; 5 | 6 | trace_macros!(true); 7 | 8 | use brainfuck_procmacro::brainfuck; 9 | 10 | fn main() { 11 | let s = brainfuck!(+[-[<<[+[--->]-[<<<]]]>>>-]>-.---.>..>.<<<<-.<+.>>>>>.>.<<.<-.); 12 | println!("{}", s); 13 | } 14 | -------------------------------------------------------------------------------- /huang_li/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "huang_li" 3 | version = "0.1.0" 4 | authors = ["Aloxaf "] 5 | description = "程序员老黄历" 6 | documentation = "https://github.com/Aloxaf/huang_li" 7 | repository = "https://github.com/Aloxaf/huang_li" 8 | readme = "README.md" 9 | categories = ["command-line-utilities"] 10 | license = "MIT" 11 | edition = "2018" 12 | 13 | [dependencies] 14 | chrono = "0.4" 15 | colored = "1.7" 16 | serde = { version = "1.0", features = ["derive"] } 17 | toml = "0.4" 18 | term-table = "1.1.0" 19 | -------------------------------------------------------------------------------- /huang_li/README.md: -------------------------------------------------------------------------------- 1 | # 程序员老黄历 2 | 3 | 今天整理收藏夹发现了这个网站: [程序员老黄历beta](https://sandbox.runjs.cn/show/ydp3it7b) 4 | 5 | 惊讶地发现它竟然还活着(好像挺老了, 第一次看到这玩意儿的时候我连 js 都不会 6 | 7 | 于是我就来改成 Rust 练练手 8 | 9 | ```text 10 | ╔═══════════════════════════════════════════════════╗ 11 | ║ 程序员老黄历 ║ 12 | ╠═══════════════════════════════════════════════════╣ 13 | ║ 今天是2019年01月09日 星期三 ║ 14 | ╠═════════════╦═════════════════════════════════════╣ 15 | ║ ║ 写单元测试 ║ 16 | ║ ║ 写单元测试将减少出错 ║ 17 | ║ ║ ║ 18 | ║ ║ 使用IE ║ 19 | ║ 宜 ║ 你看起来更有品位 ║ 20 | ║ ║ ║ 21 | ║ ║ 跳槽 ║ 22 | ║ ║ 该放手时就放手 ║ 23 | ║ ║ ║ 24 | ║ ║ 命名变量"spider" ║ 25 | ╠═════════════╬═════════════════════════════════════╣ 26 | ║ ║ 提交代码 ║ 27 | ║ ║ 你遇到的一大堆冲突会让你觉得自 ║ 28 | ║ ║ 己是不是时间穿越了 ║ 29 | ║ ║ ║ 30 | ║ ║ 设计评审 ║ 31 | ║ 不宜 ║ 人人筋疲力尽,评审就这么过了 ║ 32 | ║ ║ ║ 33 | ║ ║ 上AB站 ║ 34 | ║ ║ 满屏兄贵亮瞎你的眼 ║ 35 | ║ ║ ║ 36 | ║ ║ 玩FlappyBird ║ 37 | ║ ║ 除非你想玩到把手机砸了 ║ 38 | ╠═════════════╩═════════════════════════════════════╣ 39 | ║ 座位朝向: 面向东方写程序,BUG最少。 ║ 40 | ╠═══════════════════════════════════════════════════╣ 41 | ║ 今日宜饮: 水,绿茶 ║ 42 | ╠═══════════════════════════════════════════════════╣ 43 | ║ 女神亲近指数: ★★★★★ ║ 44 | ╚═══════════════════════════════════════════════════╝ 45 | ``` 46 | -------------------------------------------------------------------------------- /huang_li/src/config.toml: -------------------------------------------------------------------------------- 1 | weeks = ["日", "一", "二", "三", "四", "五", "六"] 2 | directions = ["北方", "东北方", "东方", "东南方", "南方", "西南方", "西方", "西北方"] 3 | tools = ["Eclipse写程序", "MSOffice写文档", "记事本写程序", "Windows8", "Linux", "MacOS", "IE", "Android设备", "iOS设备"] 4 | var_names = ["jieguo", "huodong", "pay", "expire", "zhangdan", "every", "free", "i1", "a", "virtual", "ad", "spider", "mima", "pass", "ui"] 5 | drinks = ["水", "茶", "红茶", "绿茶", "咖啡", "奶茶", "可乐", "鲜奶", "豆奶", "果汁", "果味汽水", "苏打水", "运动饮料", "酸奶", "酒"] 6 | 7 | [[specials]] 8 | data = "20140214" 9 | type = "bad" 10 | name = "待在男(女)友身边" 11 | description = "脱团火葬场,入团保平安。" 12 | 13 | [[activities]] 14 | name = "写单元测试" 15 | good = "写单元测试将减少出错" 16 | bad = "写单元测试会降低你的开发效率" 17 | 18 | [[activities]] 19 | name = "洗澡" 20 | good = "你几天没洗澡了?" 21 | bad = "会把设计方面的灵感洗掉" 22 | weekend = true 23 | 24 | [[activities]] 25 | name = "锻炼一下身体" 26 | good = "" 27 | bad = "能量没消耗多少,吃得却更多" 28 | weekend = true 29 | 30 | [[activities]] 31 | name = "抽烟" 32 | good = "抽烟有利于提神,增加思维敏捷" 33 | bad = "除非你活够了,死得早点没关系" 34 | weekend = true 35 | 36 | [[activities]] 37 | name = "白天上线" 38 | good = "今天白天上线是安全的" 39 | bad = "可能导致灾难性后果" 40 | 41 | [[activities]] 42 | name = "重构" 43 | good = "代码质量得到提高" 44 | bad = "你很有可能会陷入泥潭" 45 | 46 | [[activities]] 47 | name = "使用%t" 48 | good = "你看起来更有品位" 49 | bad = "别人会觉得你在装逼" 50 | 51 | [[activities]] 52 | name = "跳槽" 53 | good = "该放手时就放手" 54 | bad = "鉴于当前的经济形势,你的下一份工作未必比现在强" 55 | 56 | [[activities]] 57 | name = "招人" 58 | good = "你面前这位有成为牛人的潜质" 59 | bad = "这人会写程序吗?" 60 | 61 | [[activities]] 62 | name = "面试" 63 | good = "面试官今天心情很好" 64 | bad = "面试官不爽,会拿你出气" 65 | 66 | [[activities]] 67 | name = "提交辞职申请" 68 | good = "公司找到了一个比你更能干更便宜的家伙,巴不得你赶快滚蛋" 69 | bad = "鉴于当前的经济形势,你的下一份工作未必比现在强" 70 | 71 | [[activities]] 72 | name = "申请加薪" 73 | good = "老板今天心情很好" 74 | bad = "公司正在考虑裁员" 75 | 76 | [[activities]] 77 | name = "晚上加班" 78 | good = "晚上是程序员精神最好的时候" 79 | bad = "" 80 | weekend = true 81 | 82 | [[activities]] 83 | name = "在妹子面前吹牛" 84 | good = "改善你矮穷挫的形象" 85 | bad = "会被识破" 86 | weekend = true 87 | 88 | [[activities]] 89 | name = "撸管" 90 | good = "避免缓冲区溢出" 91 | bad = "强撸灰飞烟灭" 92 | weekend = true 93 | 94 | [[activities]] 95 | name = "浏览成人网站" 96 | good = "重拾对生活的信心" 97 | bad = "你会心神不宁" 98 | weekend = true 99 | 100 | [[activities]] 101 | name = "命名变量\"%v\"" 102 | good = "" 103 | bad = "" 104 | 105 | [[activities]] 106 | name = "写超过%l行的方法" 107 | good = "你的代码组织的很好,长一点没关系" 108 | bad = "你的代码将混乱不堪,你自己都看不懂" 109 | 110 | [[activities]] 111 | name = "提交代码" 112 | good = "遇到冲突的几率是最低的" 113 | bad = "你遇到的一大堆冲突会让你觉得自己是不是时间穿越了" 114 | 115 | [[activities]] 116 | name = "代码复审" 117 | good = "发现重要问题的几率大大增加" 118 | bad = "你什么问题都发现不了,白白浪费时间" 119 | 120 | [[activities]] 121 | name = "开会" 122 | good = "写代码之余放松一下打个盹,有益健康" 123 | bad = "小心被扣屎盆子背黑锅" 124 | 125 | [[activities]] 126 | name = "打DOTA" 127 | good = "你将有如神助" 128 | bad = "你会被虐的很惨" 129 | weekend = true 130 | 131 | [[activities]] 132 | name = "晚上上线" 133 | good = "晚上是程序员精神最好的时候" 134 | bad = "你白天已经筋疲力尽了" 135 | 136 | [[activities]] 137 | name = "修复BUG" 138 | good = "你今天对BUG的嗅觉大大提高" 139 | bad = "新产生的BUG将比修复的更多" 140 | 141 | [[activities]] 142 | name = "设计评审" 143 | good = "设计评审会议将变成头脑风暴" 144 | bad = "人人筋疲力尽,评审就这么过了" 145 | 146 | [[activities]] 147 | name = "需求评审" 148 | good = "" 149 | bad = "" 150 | 151 | [[activities]] 152 | name = "上微博" 153 | good = "今天发生的事不能错过" 154 | bad = "今天的微博充满负能量" 155 | weekend = true 156 | 157 | [[activities]] 158 | name = "上AB站" 159 | good = "还需要理由吗?" 160 | bad = "满屏兄贵亮瞎你的眼" 161 | weekend = true 162 | 163 | [[activities]] 164 | name = "玩FlappyBird" 165 | good = "今天破纪录的几率很高" 166 | bad = "除非你想玩到把手机砸了" 167 | weekend = true 168 | -------------------------------------------------------------------------------- /huang_li/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! TODO: 不必要的 clone 2 | use chrono::prelude::*; 3 | use serde::Deserialize; 4 | use std::fs::File; 5 | use std::io::prelude::*; 6 | use std::mem; 7 | use std::path::Path; 8 | 9 | #[derive(Debug, Deserialize)] 10 | pub struct Config { 11 | weeks: Vec, 12 | directions: Vec, 13 | tools: Vec, 14 | var_names: Vec, 15 | drinks: Vec, 16 | activities: Vec, 17 | specials: Vec, 18 | } 19 | 20 | #[derive(Debug, Deserialize)] 21 | struct Special { 22 | data: String, 23 | r#type: String, 24 | name: String, 25 | description: String, 26 | } 27 | 28 | #[derive(Clone, Debug, Deserialize)] 29 | struct Activity { 30 | name: String, 31 | good: String, 32 | bad: String, 33 | weekend: Option, 34 | } 35 | 36 | #[derive(Debug)] 37 | pub struct TodayLuck { 38 | pub time: String, 39 | pub direction: String, 40 | pub drink: String, 41 | pub goddes: String, 42 | pub good: Vec<(String, String)>, 43 | pub bad: Vec<(String, String)>, 44 | } 45 | 46 | pub struct HuangLi { 47 | config: Config, 48 | today: DateTime, 49 | good: Vec<(String, String)>, 50 | bad: Vec<(String, String)>, 51 | } 52 | 53 | impl HuangLi { 54 | fn random(&self, indexseed: u32) -> u32 { 55 | let dayseed = 56 | self.today.year() as u32 * 10000 + self.today.month() * 100 + self.today.day(); 57 | let mut n = dayseed % 11117; 58 | for _ in 0..indexseed + 100 { 59 | n *= n; 60 | n %= 11117; 61 | } 62 | n 63 | } 64 | 65 | fn random_choose(&self, seed: u32, v: &[T]) -> T { 66 | v[self.random(seed) as usize % v.len()].clone() 67 | } 68 | 69 | fn pick_random_activities(&self, activities: Vec, size: u32) -> Vec { 70 | self.pick_random(&activities, size) 71 | .iter() 72 | .map(|&ac| self.parse(ac)) 73 | .collect() 74 | } 75 | 76 | fn pick_random<'a, T>(&self, array: &'a [T], size: u32) -> Vec<&'a T> { 77 | let mut ret: Vec<&T> = array.iter().collect(); 78 | for i in 0..ret.len() as u32 - size { 79 | ret.remove(self.random(i) as usize % ret.len()); 80 | } 81 | ret 82 | } 83 | } 84 | 85 | impl HuangLi { 86 | pub fn load_from>(path: P) -> Self { 87 | let mut file = File::open(path).expect("无法打开配置文件"); 88 | let mut config_str = String::new(); 89 | file.read_to_string(&mut config_str) 90 | .expect("无法读取配置文件"); 91 | 92 | Self { 93 | config: toml::from_str(&config_str).unwrap(), 94 | today: Local::now(), 95 | good: vec![], 96 | bad: vec![], 97 | } 98 | } 99 | 100 | pub fn set_date(&mut self, date: DateTime) { 101 | self.today = date; 102 | } 103 | 104 | fn star(num: u32) -> String { 105 | "★".repeat(num as usize) + &"☆".repeat(5 - num as usize) 106 | } 107 | 108 | pub fn pick_today_luck(&mut self) -> TodayLuck { 109 | let activities = self 110 | .config 111 | .activities 112 | .iter() 113 | .filter(|ac| !self.is_weekend() || ac.weekend.is_some()) 114 | .cloned() 115 | .collect::>(); 116 | let num_good = self.random(98) % 3 + 2; 117 | let num_bad = self.random(87) % 3 + 2; 118 | let event_attr = self.pick_random_activities(activities, num_good + num_bad); 119 | self.pick_specials(); 120 | 121 | let (num_good, num_bad) = (num_good as usize, num_bad as usize); 122 | for event in &event_attr[..num_good] { 123 | self.good.push((event.name.clone(), event.good.clone())); 124 | } 125 | 126 | for event in &event_attr[num_good..num_good + num_bad] { 127 | self.bad.push((event.name.clone(), event.bad.clone())); 128 | } 129 | 130 | TodayLuck { 131 | time: self 132 | .today 133 | .format("今天是%Y年%m月%d日 星期") 134 | .to_string() 135 | + &self.config.weeks[self.today.weekday().num_days_from_sunday() as usize], 136 | direction: self.random_choose(2, &self.config.directions), 137 | drink: { 138 | let mut s = vec![]; 139 | for i in self.pick_random(&self.config.drinks, 2) { 140 | s.push(i as &str); 141 | } 142 | s.join(",") 143 | }, 144 | goddes: Self::star(self.random(6) % 5 + 1), 145 | good: mem::replace(&mut self.good, vec![]), 146 | bad: mem::replace(&mut self.bad, vec![]), 147 | } 148 | } 149 | 150 | fn parse(&self, activity: &Activity) -> Activity { 151 | let mut activity = activity.clone(); 152 | activity.name = activity 153 | .name 154 | .replace("%v", &self.random_choose(12, &self.config.var_names)) 155 | .replace("%t", &self.random_choose(11, &self.config.tools)) 156 | .replace("%l", &(self.random(12) % 247 + 30).to_string()); 157 | activity 158 | } 159 | 160 | fn is_weekend(&self) -> bool { 161 | self.today.weekday().num_days_from_monday() >= 6 162 | } 163 | 164 | fn pick_specials(&mut self) { 165 | let iday = self.today.format("%Y%m%d").to_string(); 166 | for special in &self.config.specials { 167 | if iday == special.data { 168 | if special.r#type == "good" { 169 | self.good 170 | .push((special.name.clone(), special.description.clone())); 171 | } else { 172 | self.bad 173 | .push((special.name.clone(), special.description.clone())); 174 | } 175 | } 176 | } 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /huang_li/src/main.rs: -------------------------------------------------------------------------------- 1 | use colored::{ColoredString, Colorize}; 2 | use huang_li::HuangLi; 3 | use term_table::{ 4 | row::Row, 5 | table_cell::{Alignment, TableCell}, 6 | Table, TableStyle, 7 | }; 8 | 9 | // 提前折行, 这样可以防止 term_table 折行时导致颜色错乱 10 | fn wrap_content(data: T, width: usize, colorfn: F) -> String 11 | where 12 | T: ToString, 13 | F: Fn(&str) -> ColoredString, 14 | { 15 | let cell = TableCell::new(data); 16 | let lines = cell 17 | .wrapped_content(width) 18 | .iter() 19 | .map(|s| colorfn(s).to_string()) 20 | .collect::>(); 21 | lines.join("\n") 22 | } 23 | 24 | fn count_line(s: &str) -> usize { 25 | s.chars().filter(|&c| c == '\n').count() 26 | } 27 | 28 | fn main() { 29 | let mut table = Table::new(); 30 | table.max_column_width = 37; 31 | table.style = TableStyle::extended(); 32 | 33 | let mut huangli = HuangLi::load_from("./src/config.toml"); 34 | let today = huangli.pick_today_luck(); 35 | 36 | let mut good = vec![]; 37 | for g in &today.good { 38 | let mut s = format!("{}", g.0.bold().yellow()); 39 | if !g.1.is_empty() { 40 | s.push_str(&wrap_content( 41 | format!("\n {}", g.1), 42 | table.max_column_width - 4, 43 | |s| s.bright_yellow(), 44 | )); 45 | } 46 | good.push(s); 47 | } 48 | 49 | let mut bad = vec![]; 50 | for g in &today.bad { 51 | let mut s = format!("{}", g.0.bold().red()); 52 | if !g.1.is_empty() { 53 | s.push_str(&wrap_content( 54 | format!("\n {}", g.1), 55 | table.max_column_width - 4, 56 | |s| s.bright_red(), 57 | )); 58 | } 59 | bad.push(s); 60 | } 61 | 62 | let (good, bad) = (good.join("\n\n"), bad.join("\n\n")); 63 | 64 | table.add_row(Row::new(vec![TableCell::new_with_alignment( 65 | "程序员老黄历".bold(), 66 | 3, 67 | Alignment::Center, 68 | )])); 69 | table.add_row(Row::new(vec![TableCell::new_with_alignment( 70 | today.time.bold(), 71 | 3, 72 | Alignment::Center, 73 | )])); 74 | table.add_row(Row::new(vec![ 75 | TableCell::new_with_alignment( 76 | format!( 77 | "{}{}", 78 | "\n".repeat(count_line(&good) / 2), 79 | "宜".bold().yellow() 80 | ), 81 | 1, 82 | Alignment::Center, 83 | ), 84 | TableCell::new_with_col_span(good, 2), 85 | ])); 86 | table.add_row(Row::new(vec![ 87 | TableCell::new_with_alignment( 88 | format!( 89 | "{}{}", 90 | "\n".repeat(count_line(&bad) / 2), 91 | "不宜".bold().red() 92 | ), 93 | 1, 94 | Alignment::Center, 95 | ), 96 | TableCell::new_with_col_span(bad, 2), 97 | ])); 98 | table.add_row(Row::new(vec![TableCell::new_with_alignment( 99 | format!( 100 | "{}: 面向{}写程序,BUG最少。", 101 | "座位朝向".bold(), 102 | today.direction.to_string().bold().green() 103 | ), 104 | 3, 105 | Alignment::Center, 106 | )])); 107 | table.add_row(Row::new(vec![TableCell::new_with_alignment( 108 | format!("{}: {}", "今日宜饮".bold(), today.drink), 109 | 3, 110 | Alignment::Center, 111 | )])); 112 | table.add_row(Row::new(vec![TableCell::new_with_alignment( 113 | format!( 114 | "{}: {}", 115 | "女神亲近指数".bold(), 116 | today.goddes.bright_red() 117 | ), 118 | 3, 119 | Alignment::Center, 120 | )])); 121 | 122 | println!("{}", table.render()); 123 | } 124 | -------------------------------------------------------------------------------- /img2ascii/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "img2ascii" 3 | version = "1.0.0" 4 | authors = ["Aloxaf "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | image = "0.20.1" 9 | clap = {version = "2.32.0", features = ["yaml"]} 10 | -------------------------------------------------------------------------------- /img2ascii/README.md: -------------------------------------------------------------------------------- 1 | img2ascii 2 | -- 3 | 4 | 图片转字符画 5 | 6 | ```text 7 | img2ascii 0.1 8 | Aloxaf 9 | 图片转字符画 10 | 11 | USAGE: 12 | img2ascii [FLAGS] [OPTIONS] 13 | 14 | FLAGS: 15 | -f, --force 当输出文件存在时不询问直接覆盖 16 | --help Prints help information 17 | -V, --version Prints version information 18 | 19 | OPTIONS: 20 | -h, --height 字符画的高 (默认更随宽变化) 21 | -o, --output 输出文件 22 | -w, --width 字符画的宽 (默认80) 23 | 24 | ARGS: 25 | 图片完整路径 26 | 27 | ``` -------------------------------------------------------------------------------- /img2ascii/cli.yml: -------------------------------------------------------------------------------- 1 | name: img2ascii 2 | version: "1.0.0" 3 | author: Aloxaf 4 | about: 图片转字符画 5 | args: 6 | - filename: 7 | help: 图片完整路径 8 | required: true 9 | - width: 10 | short: w 11 | long: width 12 | value_name: width 13 | help: 字符画的宽 (默认80) 14 | takes_value: true 15 | - height: 16 | short: h 17 | long: height 18 | value_name: height 19 | help: 字符画的高 (默认更随宽变化) 20 | takes_value: true 21 | - output: 22 | short: o 23 | long: output 24 | value_name: outputfile 25 | help: 输出文件 26 | takes_value: true 27 | - force: 28 | short: f 29 | long: force 30 | help: 当输出文件存在时不询问直接覆盖 31 | requires: output -------------------------------------------------------------------------------- /img2ascii/src/lib.rs: -------------------------------------------------------------------------------- 1 | use image::{GrayImage, Luma}; 2 | 3 | #[inline] 4 | fn luma2ascii(c: Luma) -> char { 5 | let ascii_char = "$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\\|()1{}[]?-_+~<>i!lI;:,\"^`'. "; 6 | let index = (((ascii_char.len() - 1) * c.data[0] as usize) as f32 / 255.0) as usize; 7 | ascii_char.chars().nth(index).unwrap() 8 | } 9 | 10 | pub fn image2ascii(img: &GrayImage) -> Vec { 11 | let (width, _) = img.dimensions(); 12 | let chars = img.pixels().map(|&c| luma2ascii(c)).collect::>(); 13 | chars 14 | .chunks(width as usize) 15 | .map(|c| c.iter().collect()) 16 | .collect() 17 | } 18 | 19 | #[cfg(test)] 20 | mod test { 21 | use crate::{image2ascii, luma2ascii}; 22 | use image::{GrayImage, Luma}; 23 | 24 | #[test] 25 | fn test_luma2ascii() { 26 | assert_eq!(luma2ascii(Luma { data: [255] }), ' '); 27 | assert_eq!(luma2ascii(Luma { data: [0] }), '$'); 28 | } 29 | 30 | #[test] 31 | fn test_image2ascii() { 32 | let pixels = vec![0, 255, 0, 255]; 33 | let asciis = vec!["$ ", "$ "]; 34 | let image = GrayImage::from_raw(2, 2, pixels).unwrap(); 35 | assert_eq!(image2ascii(&image), asciis); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /img2ascii/src/main.rs: -------------------------------------------------------------------------------- 1 | use clap::{load_yaml, value_t, App}; 2 | use image::{imageops, GenericImageView, ImageError}; 3 | use img2ascii::image2ascii; 4 | use std::fs::OpenOptions; 5 | use std::io::{self, prelude::*}; 6 | 7 | enum Error { 8 | OutputError(io::Error), 9 | InputError(ImageError), 10 | } 11 | 12 | fn main() { 13 | match run() { 14 | Err(Error::OutputError(err)) => eprintln!("output error:\n {}", err), 15 | Err(Error::InputError(err)) => eprintln!("input error:\n {}", err), 16 | Ok(_) => std::process::exit(0), 17 | } 18 | std::process::exit(1); 19 | } 20 | 21 | fn run() -> Result<(), Error> { 22 | let yaml = load_yaml!("../cli.yml"); 23 | let matches = App::from_yaml(yaml).get_matches(); 24 | 25 | let input = matches.value_of("filename").unwrap(); 26 | let width = value_t!(matches.value_of("width"), u32).unwrap_or(0); 27 | let height = value_t!(matches.value_of("height"), u32).unwrap_or(0); 28 | 29 | let image = image::open(input).map_err(Error::InputError)?; 30 | 31 | let (img_w, img_h) = image.dimensions(); 32 | 33 | let (width, height) = match (width, height) { 34 | (0, 0) => (80, 80 * img_h / img_w), 35 | (0, h) => (h * img_w / img_h, h), 36 | (w, 0) => (w, w * img_h / img_w), 37 | (w, h) => (w, h), 38 | }; 39 | 40 | let image = image.resize(width, height, imageops::Nearest).to_luma(); 41 | 42 | let mut output = match matches.value_of("output") { 43 | Some(name) => { 44 | let file = OpenOptions::new() 45 | .write(true) 46 | .create(matches.occurrences_of("force") == 0) 47 | .open(name) 48 | .map_err(Error::OutputError)?; 49 | Ok(Box::new(file) as Box) 50 | } 51 | None => Ok(Box::new(io::stdout()) as Box), 52 | }?; 53 | 54 | for s in image2ascii(&image) { 55 | write!(output, "{}", s).map_err(Error::OutputError)?; 56 | } 57 | Ok(()) 58 | } 59 | -------------------------------------------------------------------------------- /iostream/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "iostream" 3 | version = "0.1.0" 4 | authors = ["Aloxaf "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /iostream/README.md: -------------------------------------------------------------------------------- 1 | # iostream 2 | 3 | ```rust 4 | extern crate iostream; 5 | 6 | use iostream::*; 7 | 8 | #[allow(unused_must_use)] 9 | fn main() { 10 | let (mut a, mut b) = (0, 0); 11 | 12 | while cin >> &mut a >> &mut b != Eof { 13 | cout << (a + b) << endl; 14 | } 15 | } 16 | ``` -------------------------------------------------------------------------------- /iostream/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! C++ like iostream, mainly ">>" and "<<" support 2 | //! # Examples 3 | //! 4 | //! ## basic usage 5 | //! ``` 6 | //! let mut name = String::new(); 7 | //! let mut age = 0; 8 | //! cin >> &mut name >> &mut age; 9 | //! cout << name << " is " << age << " years old." << endl; 10 | //! ``` 11 | //! 12 | //! ## read to eof 13 | //! ``` 14 | //! let mut sum = 0; 15 | //! let mut n = 0 ; 16 | //! while cin >> &mut n != Eof { 17 | //! sum += n; 18 | //! } 19 | //! cout << "sum: " << sum << endl; 20 | //! ``` 21 | use std::fmt::{self, Debug, Display, Formatter}; 22 | use std::io; 23 | use std::io::prelude::*; 24 | use std::ops; 25 | use std::str::FromStr; 26 | 27 | /// end of line 28 | #[allow(non_camel_case_types)] 29 | pub struct endl; 30 | /// console input 31 | #[derive(Clone, Copy, PartialEq)] 32 | pub enum IStream { 33 | Success, 34 | TypeNotMatch, 35 | Eof, 36 | } 37 | /// console output 38 | #[allow(non_camel_case_types)] 39 | pub struct cout; 40 | 41 | #[allow(non_upper_case_globals)] 42 | pub const cin: IStream = IStream::Success; 43 | 44 | pub use crate::IStream::Eof; 45 | 46 | impl Display for endl { 47 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 48 | write!(f, "\n") 49 | } 50 | } 51 | 52 | impl ops::Shl for cout 53 | where 54 | T: Display, 55 | { 56 | type Output = Self; 57 | 58 | fn shl(self, output: T) -> Self::Output { 59 | print!("{}", output); 60 | self 61 | } 62 | } 63 | 64 | impl ops::Shr<&mut T> for IStream 65 | where 66 | T: FromStr, 67 | T::Err: Debug, 68 | { 69 | type Output = Self; 70 | 71 | fn shr(self, input: &mut T) -> Self::Output { 72 | let mut ret = IStream::TypeNotMatch; 73 | while ret == IStream::TypeNotMatch { 74 | ret = match self { 75 | IStream::Success | IStream::TypeNotMatch => { 76 | let str = io::stdin() 77 | .bytes() 78 | .map(|b| b.unwrap() as char) 79 | .take_while(|&c| c != ' ' && c != '\n' && c != '\t') 80 | .collect::(); 81 | match str.trim().parse::() { 82 | Ok(value) => { 83 | *input = value; 84 | IStream::Success 85 | } 86 | Err(_err) => { 87 | if str.trim() == "" { 88 | IStream::Eof 89 | } else { 90 | IStream::TypeNotMatch 91 | } 92 | } 93 | } 94 | } 95 | IStream::Eof => IStream::Eof, 96 | }; 97 | } 98 | ret 99 | } 100 | } 101 | 102 | //#[cfg(test)] 103 | //mod tests { 104 | // use super::{Cin, cout, endl}; 105 | // 106 | // #[test] 107 | // fn it_works() { 108 | // let mut n = 0; 109 | // let mut s = String::new(); 110 | // cin >> &mut n >> &mut s; 111 | // cout << n << " " << s << endl; 112 | // cout << "123" << ' ' << 1 << endl; 113 | // } 114 | //} 115 | -------------------------------------------------------------------------------- /iostream/src/main.rs: -------------------------------------------------------------------------------- 1 | use iostream::*; 2 | 3 | #[allow(unused_must_use)] 4 | fn main() { 5 | let (mut a, mut b) = (0, 0); 6 | 7 | while cin >> &mut a >> &mut b != Eof { 8 | cout << (a + b) << endl; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /mirage_tank/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mirage_tank" 3 | version = "1.0.0" 4 | authors = ["Aloxaf "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | image = "0.19.0" 9 | clap = {version = "2.32.0", features = ["yaml"]} 10 | 11 | [[bin]] 12 | name = "mtank" 13 | path = "src/main.rs" 14 | -------------------------------------------------------------------------------- /mirage_tank/README.md: -------------------------------------------------------------------------------- 1 | 幻影坦克快速发车工具 2 | -- 3 | 4 | ## 原理及介绍 5 | 6 | [幻影坦克架构指南(一)](https://zhuanlan.zhihu.com/p/31164700) 7 | 8 | [幻影坦克架构指南(三)](https://zhuanlan.zhihu.com/p/32532733) 9 | 10 | [棋盘格与幻影坦克](https://zhuanlan.zhihu.com/p/33148445) 11 | 12 | ## 用法 13 | ```text 14 | mirage_tank 1.0 15 | Aloxaf 16 | 幻影坦克快速发车工具 17 | 18 | USAGE: 19 | mtank [FLAGS] [OPTIONS] 20 | 21 | FLAGS: 22 | -c, --colorful 发彩色车 (默认黑白) 23 | -f, --force 不询问直接覆盖文件 24 | -h, --help Prints help information 25 | -s, --sparse 启用棋盘格化渲染 26 | -V, --version Prints version information 27 | 28 | OPTIONS: 29 | --bcolor 黑底图像色彩保留比例 (默认 0.7) 30 | --blight 黑底图像亮度 (默认 0.2) 31 | --bscale 黑底图像缩放比例 (默认 1.0) 32 | -o, --output 输出文件, png 格式 (默认 output.png) 33 | --wcolor 白底图像色彩保留比例 (默认 0.5) 34 | --wlight 白底图像亮度 (默认 1.0) 35 | --wscale 白底图像缩放比例 (默认 1.0) 36 | 37 | ARGS: 38 | 白底下显示的图片(表层) 39 | 黑底下显示的图片(里层) 40 | ``` 41 | -------------------------------------------------------------------------------- /mirage_tank/cli.yml: -------------------------------------------------------------------------------- 1 | name: mirage_tank 2 | version: "1.0.0" 3 | author: Aloxaf 4 | about: 幻影坦克快速发车工具 5 | args: 6 | - wimage: 7 | help: 白底下显示的图片(表层) 8 | required: true 9 | index: 1 10 | - bimage: 11 | help: 黑底下显示的图片(里层) 12 | required: true 13 | index: 2 14 | - output: 15 | short: o 16 | long: output 17 | help: 输出文件, png 格式 (默认 output.png) 18 | takes_value: true 19 | - sparse: 20 | short: s 21 | long: sparse 22 | help: 启用棋盘格化渲染 23 | - colorful: 24 | short: c 25 | long: colorful 26 | help: 发彩色车 (默认黑白) 27 | - wscale: 28 | long: wscale 29 | help: 白底图像缩放比例 (默认 1.0) 30 | takes_value: true 31 | - bscale: 32 | long: bscale 33 | help: 黑底图像缩放比例 (默认 1.0) 34 | takes_value: true 35 | - wlight: 36 | long: wlight 37 | help: 白底图像亮度 (默认 1.0) 38 | takes_value: true 39 | - blight: 40 | long: blight 41 | help: 黑底图像亮度 (默认 0.2) 42 | takes_value: true 43 | - wcolor: 44 | long: wcolor 45 | help: 白底图像色彩保留比例 (默认 0.5) 46 | takes_value: true 47 | requires: colorful 48 | - bcolor: 49 | long: bcolor 50 | help: 黑底图像色彩保留比例 (默认 0.7) 51 | takes_value: true 52 | requires: colorful 53 | - force: 54 | short: f 55 | long: force 56 | help: 不询问直接覆盖文件 57 | -------------------------------------------------------------------------------- /mirage_tank/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! 幻影坦克车库 2 | use image::Rgba; 3 | use image::*; 4 | 5 | pub struct MirageTank { 6 | wimg: ImageBuffer, Vec>, 7 | bimg: ImageBuffer, Vec>, 8 | width: u32, 9 | height: u32, 10 | } 11 | 12 | impl MirageTank { 13 | fn resize(img: &DynamicImage, scale: f32) -> DynamicImage { 14 | // 缩放非常耗时, 1.0 就不缩放了 15 | if (scale - 1.0).abs() < std::f32::EPSILON { 16 | return img.clone(); 17 | } 18 | let (width, height) = img.dimensions(); 19 | let width = (width as f32 * scale).round() as u32; 20 | let height = (height as f32 * scale).round() as u32; 21 | img.resize(width, height, imageops::CatmullRom) 22 | } 23 | 24 | fn greyize(rgba1: Rgba, rgba2: Rgba, wlight: f32, blight: f32) -> Rgba { 25 | let c1 = f32::from(rgba1.to_luma().data[0]) / 255.0 * wlight; 26 | let c2 = f32::from(rgba2.to_luma().data[0]) / 255.0 * blight; 27 | 28 | let a = (1.0 - c1 + c2).min(1.0); 29 | let r = (c2 / a).min(1.0); 30 | 31 | let a = (a * 255.0).round() as u8; 32 | let r = (r * 255.0).round() as u8; 33 | 34 | Rgba { data: [r, r, r, a] } 35 | } 36 | 37 | fn colorize( 38 | rgba1: Rgba, 39 | rgba2: Rgba, 40 | wlight: f32, 41 | blight: f32, 42 | wcolor: f32, 43 | bcolor: f32, 44 | ) -> Rgba { 45 | // turn 0~255 to 0~1 and change light 46 | let rgb1 = rgba1 47 | .to_rgb() 48 | .data 49 | .iter() 50 | .map(|&c| (f32::from(c) / 255.0 * wlight).min(1.0)) 51 | .collect::>(); 52 | let rgb2 = rgba2 53 | .to_rgb() 54 | .data 55 | .iter() 56 | .map(|&c| (f32::from(c) / 255.0 * blight).min(1.0)) 57 | .collect::>(); 58 | 59 | let gray1 = rgb1.iter().fold(0.0, |s, c| s + c / 3.0).min(1.0); 60 | let gray2 = rgb2.iter().fold(0.0, |s, c| s + c / 3.0).min(1.0); 61 | 62 | let rgb1 = rgb1 63 | .iter() 64 | .map(|c| c * wcolor + gray1 * (1.0 - wcolor)) 65 | .collect::>(); 66 | let rgb2 = rgb2 67 | .iter() 68 | .map(|c| c * bcolor + gray2 * (1.0 - bcolor)) 69 | .collect::>(); 70 | 71 | let drgb = rgb1 72 | .iter() 73 | .zip(rgb2.iter()) 74 | .map(|(c1, c2)| 1.0 - c1 + c2) 75 | .collect::>(); 76 | 77 | let maxc = rgb2[0].max(rgb2[1]).max(rgb2[2]); 78 | 79 | let a = (drgb[0] * 0.222 + drgb[1] * 0.707 + drgb[2] * 0.071) 80 | .max(maxc) 81 | .min(1.0); 82 | 83 | let r = ((rgb2[0] / a).min(1.0) * 255.0).round() as u8; 84 | let g = ((rgb2[1] / a).min(1.0) * 255.0).round() as u8; 85 | let b = ((rgb2[2] / a).min(1.0) * 255.0).round() as u8; 86 | 87 | Rgba { 88 | data: [r, g, b, (a * 255.0).round() as u8], 89 | } 90 | } 91 | } 92 | 93 | impl MirageTank { 94 | pub fn open( 95 | wimg: &str, 96 | bimg: &str, 97 | wscale: f32, 98 | bscale: f32, 99 | ) -> Result { 100 | let _wimg = MirageTank::resize(&image::open(wimg)?, wscale); 101 | let _bimg = MirageTank::resize(&image::open(bimg)?, bscale); 102 | 103 | let (wwidth, wheight) = _wimg.dimensions(); 104 | let (bwidth, bheight) = _bimg.dimensions(); 105 | 106 | let width = std::cmp::max(wwidth, bwidth); 107 | let height = std::cmp::max(wheight, bheight); 108 | 109 | let size = (width * height * 4) as usize; 110 | let mut wimg: ImageBuffer, Vec> = 111 | ImageBuffer::from_raw(width, height, vec![255; size]).unwrap(); 112 | let mut bimg: ImageBuffer, Vec> = 113 | ImageBuffer::from_raw(width, height, vec![0; size]).unwrap(); 114 | 115 | wimg.copy_from(&_wimg, (width - wwidth) / 2, (height - wheight) / 2); 116 | bimg.copy_from(&_bimg, (width - bwidth) / 2, (height - bheight) / 2); 117 | 118 | Ok(MirageTank { 119 | wimg, 120 | bimg, 121 | width, 122 | height, 123 | }) 124 | } 125 | 126 | /// 棋盘格化 127 | pub fn checkerboarding(&mut self) { 128 | for w in 0..self.width { 129 | for h in 0..self.height { 130 | if (w + h) % 2 == 0 { 131 | self.bimg.put_pixel( 132 | w, 133 | h, 134 | Rgba { 135 | data: [255, 255, 255, 255], 136 | }, 137 | ); 138 | } else { 139 | self.wimg.put_pixel(w, h, Rgba { data: [0, 0, 0, 0] }); 140 | } 141 | } 142 | } 143 | } 144 | 145 | /// 发黑白车 146 | pub fn greycarize(&mut self, wlight: f32, blight: f32) -> ImageBuffer, Vec> { 147 | let mut outpixels = Vec::new(); 148 | for (&p1, &p2) in self.wimg.pixels().zip(self.bimg.pixels()) { 149 | let c = MirageTank::greyize(p1, p2, wlight, blight); 150 | outpixels.extend(c.data.iter()); 151 | } 152 | ImageBuffer::from_raw(self.width, self.height, outpixels).unwrap() 153 | } 154 | 155 | /// 发彩色车 156 | pub fn colorcarize( 157 | &self, 158 | wlight: f32, 159 | blight: f32, 160 | wcolor: f32, 161 | bcolor: f32, 162 | ) -> ImageBuffer, Vec> { 163 | let mut outpixels = Vec::new(); 164 | for (&p1, &p2) in self.wimg.pixels().zip(self.bimg.pixels()) { 165 | let c = MirageTank::colorize(p1, p2, wlight, blight, wcolor, bcolor); 166 | outpixels.extend(c.data.iter()); 167 | } 168 | ImageBuffer::from_raw(self.width, self.height, outpixels).unwrap() 169 | } 170 | } 171 | 172 | /*#[cfg(test)] 173 | mod tests { 174 | 175 | use super::greycarize; 176 | use super::colorcarize; 177 | use image::Rgba; 178 | 179 | #[test] 180 | fn grey() { 181 | let c1 = Rgba { data: [0xde, 0xad, 0xbe, 0xef] }; 182 | let c2 = Rgba { data: [0xba, 0xaa, 0xaa, 0xad] }; 183 | // TODO: 这玩意儿咋测试.... 184 | greycarize(c1, c2, 1.0, 0.2); 185 | } 186 | 187 | #[test] 188 | fn color() { 189 | let c1 = Rgba { data: [0xde, 0xad, 0xbe, 0xef] }; 190 | let c2 = Rgba { data: [0xba, 0xaa, 0xaa, 0xad] }; 191 | colorcarize(c1, c2, 1.0, 0.2, 0.5, 0.7); 192 | } 193 | }*/ 194 | -------------------------------------------------------------------------------- /mirage_tank/src/main.rs: -------------------------------------------------------------------------------- 1 | use clap::{load_yaml, value_t, App}; 2 | use mirage_tank::MirageTank; 3 | use std::path::PathBuf; 4 | use std::process; 5 | 6 | fn main() { 7 | let yaml = load_yaml!("../cli.yml"); 8 | let matches = App::from_yaml(yaml).get_matches(); 9 | // println!("{:?}", matches); 10 | 11 | let wimage = matches.value_of("wimage").unwrap(); 12 | let bimage = matches.value_of("bimage").unwrap(); 13 | 14 | let wscale = value_t!(matches.value_of("wscale"), f32).unwrap_or(1.0); 15 | let bscale = value_t!(matches.value_of("bscale"), f32).unwrap_or(1.0); 16 | 17 | let mut output = PathBuf::from(matches.value_of("output").unwrap_or("output.png")); 18 | output.set_extension("png"); 19 | 20 | if output.exists() && matches.occurrences_of("force") == 0 { 21 | eprintln!( 22 | "can't write to {}: file already exists!", 23 | output.to_str().unwrap() 24 | ); 25 | process::exit(1); 26 | } 27 | 28 | let mut car = MirageTank::open(wimage, bimage, wscale, bscale).unwrap_or_else(|e| { 29 | eprintln!("fail to load file: {}", e); 30 | process::exit(1); 31 | }); 32 | 33 | let wlight = value_t!(matches.value_of("wlight"), f32).unwrap_or(1.0); 34 | let blight = value_t!(matches.value_of("blight"), f32).unwrap_or(0.2); 35 | 36 | if matches.occurrences_of("sparse") == 1 { 37 | car.checkerboarding(); 38 | } 39 | 40 | let oimage = if matches.occurrences_of("colorful") == 0 { 41 | car.greycarize(wlight, blight) 42 | } else { 43 | let wcolor = value_t!(matches.value_of("wcolor"), f32).unwrap_or(0.5); 44 | let bcolor = value_t!(matches.value_of("bcolor"), f32).unwrap_or(0.7); 45 | car.colorcarize(wlight, blight, wcolor, bcolor) 46 | }; 47 | 48 | oimage.save(output.to_str().unwrap()).unwrap_or_else(|e| { 49 | eprintln!("can't write to {}: {}", output.to_str().unwrap(), e); 50 | }); 51 | } 52 | -------------------------------------------------------------------------------- /some_plot/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "some_plot" 3 | version = "0.1.0" 4 | authors = ["Aloxaf "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | svg = "0.5" 9 | 10 | [lib] 11 | -------------------------------------------------------------------------------- /some_plot/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::f32::consts::PI; 2 | use std::fmt::Debug; 3 | use std::io::{self, prelude::*}; 4 | use std::str::FromStr; 5 | use svg::node::element::{Circle, Line, Path, Text}; 6 | use svg::node::{self, element::path::Data, Node}; 7 | use svg::Document; 8 | 9 | pub fn read() -> T 10 | where 11 | T: FromStr, 12 | T::Err: Debug, 13 | { 14 | let str = io::stdin() 15 | .bytes() 16 | .map(|b| b.unwrap() as char) 17 | .take_while(|&c| c != ' ' && c != '\n') 18 | .collect::(); 19 | str.trim().parse::().unwrap() 20 | } 21 | 22 | /// 统计图 23 | pub struct Plot { 24 | /// svg 文档 25 | document: Document, 26 | /// 中心 x 坐标 27 | x: f32, 28 | /// 中心 y 坐标 29 | y: f32, 30 | /// 统计图半径 31 | radius: f32, 32 | /// 坐标轴个数 (数据项数 33 | axis_cnt: f32, 34 | } 35 | 36 | impl Plot { 37 | pub fn new(x: f32, y: f32, radius: f32, axis_cnt: i32) -> Self { 38 | // 创建外层圆环 39 | let circle = Circle::new() 40 | .set("cx", x) 41 | .set("cy", y) 42 | .set("r", radius) 43 | .set("stroke", "black") 44 | .set("stroke-width", 0.5) 45 | .set("fill", "none"); 46 | 47 | Self { 48 | document: Document::new() 49 | .set("viewBox", (0, 0, x * 2.0, y * 2.0)) 50 | .add(circle), 51 | x, 52 | y, 53 | radius, 54 | axis_cnt: axis_cnt as f32, 55 | } 56 | } 57 | 58 | /// 添加节点 59 | pub fn add(&mut self, node: T) { 60 | let mut document = std::mem::replace(&mut self.document, Document::new()); 61 | document = document.add(node); 62 | std::mem::replace(&mut self.document, document); 63 | } 64 | 65 | /// 在坐标轴上根据阈值增加分隔线 66 | pub fn add_threshold(&mut self, info: &[([f32; 2], [f32; 2], [&str; 3])]) { 67 | for (idx, info) in info.iter().enumerate() { 68 | // 计算坐标轴起点相对原点偏移量 69 | let x = self.radius * (2.0 * PI / self.axis_cnt * idx as f32).sin(); 70 | let y = self.radius * (2.0 * PI / self.axis_cnt * idx as f32).cos(); 71 | 72 | // 添加阈值点 73 | let ([min, max], [threshod1, threshod2], _) = info; 74 | let size1 = (threshod1 - min) / (max - min); 75 | let size2 = (threshod2 - min) / (max - min); 76 | 77 | for size in [size1, size2].iter() { 78 | let flag = Line::new() 79 | .set("x1", self.x + x * size - 2.0) 80 | .set("y1", self.y - y * size) 81 | .set("x2", self.x + x * size + 2.0) 82 | .set("y2", self.y - y * size) 83 | .set("stroke", "black") 84 | .set("stroke-width", 0.7) 85 | .set( 86 | "transform", 87 | format!( 88 | "rotate({}, {}, {})", 89 | 360 / self.axis_cnt as usize * idx, 90 | self.x + x * size, 91 | self.y - y * size 92 | ), 93 | ); 94 | self.add(flag); 95 | } 96 | } 97 | } 98 | 99 | /// 根据给定的标签添加坐标轴 100 | pub fn add_axis(&mut self, labels: &[String]) { 101 | for (idx, label) in labels.iter().enumerate() { 102 | // 计算坐标轴起点相对原点偏移量 103 | let x = self.radius * (2.0 * PI / self.axis_cnt * idx as f32).sin(); 104 | let y = self.radius * (2.0 * PI / self.axis_cnt * idx as f32).cos(); 105 | 106 | // 添加坐标轴 107 | let line = Line::new() 108 | .set("x1", self.x + x) 109 | .set("y1", self.y + y) 110 | .set("x2", self.x) 111 | .set("y2", self.y) 112 | .set("stroke", "black") 113 | .set("stroke-width", 0.5); 114 | self.add(line); 115 | 116 | // 添加标签文字 117 | let text = Text::new() 118 | .set("x", self.x + x * 1.02 - 5.0) 119 | .set("y", self.y - y * 1.02) 120 | .set("font-size", 9) 121 | .add(node::Text::new(label.to_owned())); 122 | self.add(text); 123 | } 124 | } 125 | 126 | /// 添加一个扇形 127 | pub fn add_sector(&mut self, size: f32, nth: i32, color: &str) { 128 | let radius = self.radius * size; 129 | let (delta_x, delta_y) = ( 130 | radius * (PI / self.axis_cnt).sin(), 131 | radius * (PI / self.axis_cnt).cos(), 132 | ); 133 | 134 | let data = Data::new() 135 | .move_to((self.x, self.y)) 136 | .line_to((self.x + delta_x, self.y - delta_y)) 137 | .elliptical_arc_to((radius, radius, 0, 0, 0, self.x - delta_x, self.y - delta_y)) 138 | .close(); 139 | 140 | let path = Path::new() 141 | .set("d", data) 142 | .set( 143 | "transform", 144 | format!( 145 | "rotate({}, {}, {})", 146 | 360 / self.axis_cnt as i32 * nth, 147 | self.x, 148 | self.y 149 | ), 150 | ) 151 | .set("fill", color); 152 | 153 | self.add(path); 154 | } 155 | 156 | pub fn save(&self, path: &str) -> io::Result<()> { 157 | Ok(svg::save(path, &self.document)?) 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /some_plot/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::io::{self, prelude::*}; 3 | use some_plot::{read, Plot}; 4 | 5 | // ([最小值, 最大值], [阈值1, 阈值2], [颜色1, 颜色2, 颜色3) 6 | // (最小值, 阈值1] -> 颜色1 7 | // (阈值1, 阈值2] -> 颜色2 8 | // (阈值2, 最大值] -> 颜色3 9 | const DATA_INFO: [([f32; 2], [f32; 2], [&str; 3]); 10] = [ 10 | // SLR 11 | ([3.0, 13.0], [4.99, 11.01], ["red", "green", "red"]), 12 | // TMOS-S 13 | ([3.0, 4.2], [3.8, 4.0], ["red", "yellow", "green"]), 14 | // RLR 15 | ([-7.0, -1.0], [-5.0, -3.0], ["red", "green", "yellow"]), 16 | // TMOS-RCV 17 | ([2.8, 4.2], [3.4, 3.8], ["red", "yellow", "green"]), 18 | // G-MOS 19 | ([3.6, 4.1], [3.8, 3.95], ["red", "yellow", "green"]), 20 | // S-MOS 21 | ([3.6, 4.1], [3.8, 3.95], ["red", "yellow", "green"]), 22 | // N-MOS 23 | ([3.8, 4.5], [4.0, 4.2], ["red", "yellow", "green"]), 24 | // DT_SEND 25 | ([1.0, 9.0], [2.99, 5.99], ["green", "yellow", "red"]), 26 | // TCLW 27 | ([50.0, 80.0], [65.0, 70.0], ["red", "yellow", "green"]), 28 | // delay 29 | ([0.0, 222.0], [149.99, 179.99], ["green", "yellow", "red"]), 30 | ]; 31 | 32 | fn main() { 33 | let labels = [ 34 | "SLR", "TMOS-S", "RLR", "TMOS-RCV", "G-MOS", "S-MOS", "N-MOS", "DT_SEND", "TCLW", "delay", 35 | ]; 36 | let mut argv = env::args() 37 | .skip(1) 38 | .map(|s| s.parse::().unwrap()) 39 | .collect::>(); 40 | 41 | if argv.len() != 10 { 42 | argv = labels 43 | .iter() 44 | .map(|label| { 45 | print!("请输入 {}: ", label); 46 | io::stdout().flush().unwrap(); 47 | read::() 48 | }) 49 | .collect(); 50 | } 51 | 52 | let mut plot = Plot::new(300.0, 300.0, 280.0, 10); 53 | 54 | for (idx, (value, info)) in argv.iter().zip(DATA_INFO.iter()).enumerate() { 55 | let ([min, max], [threshold1, threshold2], [color1, color2, color3]) = info; 56 | 57 | // 计算扇形大小 58 | assert_eq!( 59 | min <= value && value <= max, 60 | true, 61 | "不满足条件: {} <= {} <= {}", 62 | min, 63 | value, 64 | max 65 | ); 66 | let size = (value - min) / (max - min); 67 | 68 | // 判断颜色 69 | let color = if value <= threshold1 { 70 | color1 71 | } else if value <= threshold2 { 72 | color2 73 | } else { 74 | color3 75 | }; 76 | 77 | /* println!( 78 | "{:10} value:{:7.2} size:{:.2} color:{}", 79 | labels[idx], value, size, color 80 | ); */ 81 | plot.add_sector(size, idx as i32, color); 82 | } 83 | 84 | plot.add_axis( 85 | &labels 86 | .iter() 87 | .zip(argv.iter()) 88 | .map(|(label, value)| format!("{}={}", label, value)) 89 | .collect::>(), 90 | ); 91 | plot.add_threshold(&DATA_INFO); 92 | 93 | plot.save("image.svg").unwrap(); 94 | println!("图片已保存为 image.svg"); 95 | } 96 | -------------------------------------------------------------------------------- /ulogme/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ulogme" 3 | version = "0.1.0" 4 | authors = ["Aloxaf "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | libc = "0.2.45" 9 | chrono = "0.4.6" 10 | dbus = "0.6.3" 11 | regex = "1.1.0" 12 | log = "0.4.6" 13 | env_logger = "0.6.0" 14 | 15 | [build-dependencies] 16 | cc = "1.0.26" 17 | -------------------------------------------------------------------------------- /ulogme/README.md: -------------------------------------------------------------------------------- 1 | # ulogme 2 | ulogme 那玩意儿在后台开了一堆 grep tr 啥的进程, 3 | 虽然似乎没占什么 CPU, 但强迫症看着不爽. 4 | 5 | 试试能不能集成到一个程序中来 6 | 7 | ## 进度 8 | - [x] 记录活动窗口 9 | - [x] 判断是否处于锁屏状态 10 | - [x] 关机时如何处理? 11 | - [x] 记录键盘事件 12 | - [ ] 彩色输出 13 | - [ ] 错误处理 -------------------------------------------------------------------------------- /ulogme/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | cc::Build::new() 3 | .file("src/X11logger/X11logger.c") 4 | .cpp(true) 5 | .opt_level(2) 6 | .compile("X11logger"); 7 | } 8 | -------------------------------------------------------------------------------- /ulogme/src/X11logger/X11logger.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by aloxaf on 18-10-25. 3 | // 4 | // raw: https://github.com/UltimateHackingKeyboard/current-window-linux/blob/master/get-current-window.c 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "common.h" 12 | 13 | #define MAXSTR 1000 14 | 15 | unsigned char *get_string_property(Display *display, Window window, char const *property_name) { 16 | Atom actual_type, filter_atom; 17 | int actual_format, status; 18 | unsigned long nitems, bytes_after; 19 | unsigned char *prop; 20 | 21 | filter_atom = XInternAtom(display, property_name, True); 22 | status = XGetWindowProperty(display, window, filter_atom, 0, MAXSTR, False, AnyPropertyType, 23 | &actual_type, &actual_format, &nitems, &bytes_after, &prop); 24 | return status == Success ? prop : NULL; 25 | } 26 | 27 | unsigned long get_long_property(Display *display, Window window, char const *property_name) { 28 | unsigned char *prop = get_string_property(display, window, property_name); 29 | if (prop == NULL) 30 | return 0; 31 | unsigned long long_property = prop[0] + (prop[1] << 8) + (prop[2] << 16) + (prop[3] << 24); 32 | return long_property; 33 | } 34 | 35 | extern "C" DLL_PUBLIC WindowInfo get_active_window() { 36 | Display *display = XOpenDisplay(NULL); 37 | if (display == NULL) { 38 | return (WindowInfo){0, NULL, NULL}; 39 | } 40 | 41 | int screen = XDefaultScreen(display); 42 | Window window = RootWindow(display, screen); 43 | window = get_long_property(display, window, "_NET_ACTIVE_WINDOW"); 44 | 45 | WindowInfo info; 46 | if (window == 0) { 47 | info.PID = 0; 48 | info.wm_class = NULL; 49 | info.wm_name = NULL; 50 | } else { 51 | info.PID = get_long_property(display, window, "_NET_WM_PID"); 52 | info.wm_class = (char *) get_string_property(display, window, "WM_CLASS"); 53 | info.wm_name = (char *) get_string_property(display, window, "_NET_WM_NAME"); 54 | } 55 | XCloseDisplay(display); 56 | return info; 57 | } 58 | 59 | DLL_PUBLIC KeyEvent get_keyboard_event() { 60 | return (KeyEvent) {0, 0}; 61 | } -------------------------------------------------------------------------------- /ulogme/src/X11logger/common.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by aloxaf on 18-10-25. 3 | // 4 | 5 | #ifndef RUST_TOYS_COMMON_H 6 | #define RUST_TOYS_COMMON_H 7 | 8 | #if defined _WIN32 || defined __CYGWIN__ 9 | #ifdef BUILDING_DLL 10 | #ifdef __GNUC__ 11 | #define DLL_PUBLIC __attribute__ ((dllexport)) 12 | #else 13 | #define DLL_PUBLIC __declspec(dllexport) // Note: actually gcc seems to also supports this syntax. 14 | #endif 15 | #else 16 | #ifdef __GNUC__ 17 | #define DLL_PUBLIC __attribute__ ((dllimport)) 18 | #else 19 | #define DLL_PUBLIC __declspec(dllimport) // Note: actually gcc seems to also supports this syntax. 20 | #endif 21 | #endif 22 | #define DLL_LOCAL 23 | #else 24 | #if __GNUC__ >= 4 25 | #define DLL_PUBLIC __attribute__ ((visibility ("default"))) 26 | #define DLL_LOCAL __attribute__ ((visibility ("hidden"))) 27 | #else 28 | #define DLL_PUBLIC 29 | #define DLL_LOCAL 30 | #endif 31 | #endif 32 | 33 | typedef struct { 34 | unsigned long PID; 35 | char *wm_class; 36 | char *wm_name; 37 | } WindowInfo; 38 | 39 | typedef struct { 40 | int type; 41 | unsigned int keycode; 42 | } KeyEvent; 43 | 44 | #endif //RUST_TOYS_COMMON_H 45 | -------------------------------------------------------------------------------- /ulogme/src/keyboard.rs: -------------------------------------------------------------------------------- 1 | use log::debug; 2 | use regex::RegexBuilder; 3 | use std::io::{prelude::*, BufRead, Cursor}; 4 | use std::process::{Command, Stdio}; 5 | use std::sync::{Arc, Mutex}; 6 | use std::thread; 7 | 8 | /// 获取所有键盘设备的 id 9 | fn get_device_list() -> Vec { 10 | let mut devices = vec![]; 11 | 12 | let xinput = Command::new("xinput").output().unwrap(); 13 | let output = String::from_utf8(xinput.stdout).unwrap(); 14 | let device_list = output.split('\n').collect::>(); 15 | for device in device_list { 16 | if device.find("slave keyboard").is_some() { 17 | let pos = device.find("id=").unwrap(); // return the position of '=' 18 | let id = device 19 | .chars() 20 | .skip(pos + 1) 21 | .take_while(|c| c.is_ascii_digit()) 22 | .collect::(); 23 | let id = id.parse::().unwrap(); 24 | devices.push(id); 25 | } 26 | } 27 | devices 28 | } 29 | 30 | /// 获取修饰键的键码 31 | fn get_modifier_keys() -> Vec { 32 | let cmd_xmodmap = Command::new("xmodmap") 33 | .arg("-pk") 34 | .output() 35 | .expect("WTF? No xmodmap ??"); 36 | let re = RegexBuilder::new(r".*\b(shift_[lr]|alt_[lr]|control_[lr]|caps_lock)\b.*") 37 | .case_insensitive(true) 38 | .build() 39 | .unwrap(); 40 | let cursor = Cursor::new(cmd_xmodmap.stdout); 41 | cursor 42 | .lines() 43 | .filter_map(|s| { 44 | let s = s.unwrap(); 45 | if re.is_match(&s) { 46 | let mut fields = s.split_whitespace(); 47 | Some(fields.next().unwrap().to_string()) 48 | } else { 49 | None 50 | } 51 | }) 52 | .collect() 53 | } 54 | 55 | /// 获取按键次数 56 | /// 先获取所有的 keyboard 设备, 然后开一堆 `xinput test $id` 进程, 监测其输出 57 | #[allow(non_snake_case)] 58 | pub fn GetKeyPressCnt() -> Arc> { 59 | let cnt = Arc::new(Mutex::new(0)); 60 | let modifier_keys = Arc::new(get_modifier_keys()); 61 | let devices = get_device_list(); 62 | debug!("modifier keys: {:?}", modifier_keys); 63 | debug!("keyboard devices: {:?}", devices); 64 | 65 | let mut handles = vec![]; 66 | for device in devices { 67 | let cnt = Arc::clone(&cnt); 68 | let modifier_keys = Arc::clone(&modifier_keys); 69 | 70 | let handle = thread::spawn(move || { 71 | let xinput_child = Command::new("xinput") 72 | .arg("test") 73 | .arg(device.to_string()) 74 | .stdout(Stdio::piped()) 75 | .spawn() 76 | .expect("Failed to run `xinput` command"); 77 | let outout = xinput_child.stdout.unwrap(); 78 | let mut bytes = outout.bytes(); 79 | 80 | loop { 81 | // 读取到换行符 82 | let s = bytes 83 | .by_ref() 84 | .map(|b| b.unwrap() as char) 85 | .take_while(|&c| c != '\n') 86 | .collect::(); 87 | let s = s.split_whitespace().collect::>(); 88 | if s.len() >= 3 && s[1] == "press" && !modifier_keys.contains(&s[2].to_string()) { 89 | let mut cnt = cnt.lock().unwrap(); 90 | *cnt += 1; 91 | } 92 | } 93 | }); 94 | handles.push(handle); 95 | } 96 | 97 | cnt 98 | } 99 | -------------------------------------------------------------------------------- /ulogme/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod keyboard; 2 | mod utils; 3 | mod window; 4 | 5 | pub use self::keyboard::GetKeyPressCnt; 6 | pub use self::utils::*; 7 | pub use self::window::GetActiveWindow; 8 | -------------------------------------------------------------------------------- /ulogme/src/main.rs: -------------------------------------------------------------------------------- 1 | use log::{error, info}; 2 | use std::io::prelude::*; 3 | use std::thread::sleep; 4 | use std::time::{Duration, SystemTime}; 5 | use ulogme::*; 6 | 7 | fn main() { 8 | env_logger::init(); 9 | 10 | let cnt = GetKeyPressCnt(); 11 | 12 | let mut prev_wm_name = ""; 13 | let mut prev_key_time = SystemTime::now(); 14 | let mut prev_log_name = String::new(); 15 | let (mut key_log_file, mut win_log_file) = get_log_file(&get_log_name()); 16 | 17 | // 第一次运行时先写入 __SHUTDOWN, 截断前面的记录和本次记录 18 | writeln!(&mut win_log_file, "{} __SHUTDOWN", get_timestamp()).unwrap(); 19 | 20 | loop { 21 | // 是否更新日志文件名 22 | let log_name = get_log_name(); 23 | if log_name != prev_log_name { 24 | let (f1, f2) = get_log_file(&log_name); 25 | key_log_file = f1; 26 | win_log_file = f2; 27 | prev_log_name = log_name; 28 | } 29 | 30 | // 窗口切换记录 31 | if screen_locked() && prev_wm_name != "__LOCKEDSCREEN" { 32 | prev_wm_name = "__LOCKEDSCREEN"; 33 | info!("{} {}", get_timestamp(), prev_wm_name); 34 | writeln!(&mut win_log_file, "{} {}", get_timestamp(), prev_wm_name).unwrap(); 35 | } else if let Ok(window) = GetActiveWindow() { 36 | if window.wm_name != prev_wm_name { 37 | let log_str = format!( 38 | "{} {} \x00 {}", 39 | get_timestamp(), 40 | window.wm_name, 41 | window.wm_class 42 | ); 43 | info!("{}", log_str); 44 | writeln!(&mut win_log_file, "{}", log_str).unwrap(); 45 | 46 | prev_wm_name = window.wm_name; 47 | } 48 | } else { 49 | error!("No active window"); 50 | } 51 | 52 | // 记录按下的键的次数, 当满10s且按键次数大于0的时候进行一次记录 53 | if prev_key_time.elapsed().unwrap().as_secs() >= 10 { 54 | let mut cnt = cnt.lock().unwrap(); 55 | if *cnt != 0 { 56 | prev_key_time = SystemTime::now(); 57 | info!("{} {}", get_timestamp(), *cnt); 58 | writeln!(&mut key_log_file, "{} {}", get_timestamp(), *cnt).unwrap(); 59 | *cnt = 0; 60 | } 61 | } 62 | 63 | sleep(Duration::from_millis(1000)); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /ulogme/src/utils.rs: -------------------------------------------------------------------------------- 1 | use chrono::prelude::*; 2 | use chrono::Duration; 3 | use dbus::{BusType, Connection, Message}; 4 | use std::fs::{remove_file, File, OpenOptions}; 5 | use std::os::unix::fs::symlink; 6 | use std::path::Path; 7 | use std::time::{SystemTime, UNIX_EPOCH}; 8 | 9 | /// 获取自定义时间戳 10 | /// 早上七点之前算到前一天(肝帝模式 11 | pub fn get_log_name() -> String { 12 | let dt = Local::now(); 13 | let timestr = dt.format("%Y-%m-%d 07:00:00").to_string(); 14 | 15 | let dt = if dt.hour() >= 7 { 16 | Local 17 | .datetime_from_str(×tr, "%Y-%m-%d %H:%M:%S") 18 | .unwrap() 19 | } else { 20 | let mut dt = Local 21 | .datetime_from_str(×tr, "%Y-%m-%d %H:%M:%S") 22 | .unwrap(); 23 | dt = dt - Duration::days(1); 24 | dt 25 | }; 26 | 27 | dt.format("%s").to_string() 28 | } 29 | 30 | /// 判断当前是否是锁屏状态 31 | pub fn screen_locked() -> bool { 32 | let connect = Connection::get_private(BusType::Session).unwrap(); 33 | let mes = Message::new_method_call( 34 | "org.kde.screensaver", 35 | "/ScreenSaver", 36 | "org.freedesktop.ScreenSaver", 37 | "GetActive", 38 | ) 39 | .unwrap(); 40 | let ret = connect.send_with_reply_and_block(mes, 2000).unwrap(); 41 | ret.get1().unwrap() 42 | } 43 | 44 | /// 创建并覆盖符号链接 45 | pub fn create_symlink(src: &str, dst: &str) { 46 | if Path::new(dst).exists() { 47 | remove_file(dst).unwrap(); 48 | } 49 | symlink(src, dst).unwrap(); 50 | } 51 | 52 | /// 以追加模式打开文件, 若不存在则创建 53 | pub fn open_file_to_append(path: &str) -> File { 54 | OpenOptions::new() 55 | .append(true) 56 | .create(true) 57 | .open(path) 58 | .expect("WTF? Can't open log file to write") 59 | } 60 | 61 | pub fn get_timestamp() -> u64 { 62 | SystemTime::now() 63 | .duration_since(UNIX_EPOCH) 64 | .unwrap() 65 | .as_secs() 66 | } 67 | 68 | /// 创建日志文件 69 | pub fn get_log_file(log_name: &str) -> (File, File) { 70 | let keylog_name = format!("keyfreq_{}.txt", log_name); 71 | let winlog_name = format!("window_{}.txt", log_name); 72 | let key_log_file = open_file_to_append(&keylog_name); 73 | let win_log_file = open_file_to_append(&winlog_name); 74 | create_symlink(&keylog_name, "keyfreq_today.txt"); 75 | create_symlink(&winlog_name, "window_today.txt"); 76 | (key_log_file, win_log_file) 77 | } 78 | -------------------------------------------------------------------------------- /ulogme/src/window.rs: -------------------------------------------------------------------------------- 1 | use libc::{c_char, c_ulong}; 2 | use std::ffi::CStr; 3 | 4 | pub enum Error { 5 | NoActiveWindow, 6 | } 7 | 8 | #[repr(C)] 9 | struct CWindowInfo { 10 | pub pid: c_ulong, 11 | pub wm_class: *const c_char, 12 | pub wm_name: *const c_char, 13 | } 14 | 15 | pub struct WindowInfo<'a> { 16 | /// 窗口 PID 17 | pub pid: u64, 18 | /// 进程名称 19 | pub wm_class: &'a str, 20 | /// 窗口标题 21 | pub wm_name: &'a str, 22 | } 23 | 24 | #[link(name = "X11logger")] 25 | #[link(name = "X11")] 26 | extern "C" { 27 | fn get_active_window() -> CWindowInfo; 28 | } 29 | 30 | /// 以 `\0` 为分隔符分割一个以 `\0\0` 结束的 C 字符串 31 | unsafe fn split_by_nul<'a>(ptr: *const c_char) -> Vec<&'a str> { 32 | let mut ret = vec![]; 33 | let mut start = ptr; 34 | for i in 0.. { 35 | if *ptr.offset(i) == 0 { 36 | ret.push(CStr::from_ptr(start).to_str().unwrap()); 37 | start = ptr.offset(i + 1); 38 | if *start == 0 { 39 | break; 40 | } 41 | } 42 | } 43 | ret 44 | } 45 | 46 | /// 获取当前活动窗口 47 | #[allow(non_snake_case)] 48 | pub fn GetActiveWindow<'a>() -> Result, Error> { 49 | let cret = unsafe { get_active_window() }; 50 | // 偶尔会出现找不到激活窗口的情况, 比如打开 telegream 的右键菜单时 51 | if cret.pid == 0 { 52 | Err(Error::NoActiveWindow) 53 | } else { 54 | Ok(WindowInfo { 55 | pid: cret.pid, 56 | // wm_class 含有两项 57 | wm_class: unsafe { split_by_nul(cret.wm_class)[1] }, 58 | wm_name: unsafe { CStr::from_ptr(cret.wm_name) }.to_str().unwrap(), 59 | }) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /webapi/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "webapi" 3 | version = "0.1.0" 4 | authors = ["Aloxaf "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | rocket = "0.3.17" 9 | rocket_codegen = "0.3.17" 10 | serde_derive = "1.0.80" 11 | serde = "1.0.80" 12 | serde_json = "1.0.32" 13 | 14 | [dependencies.rocket_contrib] 15 | version = "*" 16 | default-features = false 17 | features = ["json"] 18 | 19 | [dependencies.brainfuck] 20 | path = "../brainfuck" 21 | -------------------------------------------------------------------------------- /webapi/src/main.rs: -------------------------------------------------------------------------------- 1 | #![feature(proc_macro_hygiene, plugin, decl_macro)] 2 | #![plugin(rocket_codegen)] 3 | 4 | extern crate brainfuck; 5 | #[macro_use] 6 | extern crate rocket; 7 | #[macro_use] 8 | extern crate rocket_contrib; 9 | #[macro_use] 10 | extern crate serde_derive; 11 | 12 | use self::brainfuck::Interpreter; 13 | use rocket_contrib::Json; 14 | use std::io::prelude::*; 15 | use std::io::Cursor; 16 | 17 | #[get("/")] 18 | fn index() -> &'static str { 19 | "Hello, world!" 20 | } 21 | 22 | #[derive(Serialize, Deserialize)] 23 | struct BrainFuck { 24 | stack_size: u16, 25 | input: String, 26 | code: String, 27 | } 28 | 29 | #[derive(Serialize, Deserialize)] 30 | struct BrainFuckResult { 31 | success: bool, 32 | output: String, 33 | } 34 | 35 | #[post("/brainfuck", format = "application/json", data = "")] 36 | fn eval_bf(bf: Json) -> Json { 37 | let bf: BrainFuck = bf.into_inner(); 38 | let input: Cursor = Cursor::new(bf.input); 39 | let mut output: Vec = Vec::new(); 40 | let mut interpreter: Interpreter, &mut Vec> = 41 | Interpreter::new(bf.stack_size as usize, input, output.as_mut()); 42 | 43 | let ret = match interpreter.eval(&bf.code) { 44 | Ok(()) => BrainFuckResult { 45 | success: true, 46 | output: String::from_utf8(output).unwrap(), 47 | }, 48 | Err(err) => BrainFuckResult { 49 | success: false, 50 | output: err.to_string(), 51 | }, 52 | }; 53 | Json(ret) 54 | } 55 | 56 | // Oh no! 57 | // https://stackoverflow.com/questions/43424982/how-to-parse-multipart-forms-using-abonander-multipart-with-rocket/43427509#43427509 58 | //#[post("/miragetank", format = "application/json", data = "")] 59 | //fn build_car() { 60 | // 61 | //} 62 | 63 | fn main() { 64 | rocket::ignite() 65 | .mount("/", routes![index, eval_bf]) 66 | .launch(); 67 | } 68 | --------------------------------------------------------------------------------