# doe — Versatile Rust Utility Crate
[](https://crates.io/crates/doe)
[](https://docs.rs/doe)
[](LICENSE)
**doe** bundles encoding helpers, cryptographic primitives, clipboard,
keyboard & mouse simulation, screenshot capture, HTTP & JSON utilities,
date/time, Excel & DOCX manipulation, structured logging, an async
runtime, and more — every feature opt-in, minimal dependencies.
## Installation
```toml
[dependencies]
doe = { version = "1", features = ["ctf", "crypto", "date"] }
```
## Feature flags
| `ctf` | Hex, base64, URL, hashing, regex, Chinese text, HTML parsing |
| `clip` | Clipboard read & write |
| `keyboard` | Keyboard input simulation |
| `mouse` | Mouse movement, click, drag, scroll |
| `kcm` | Keyboard + clipboard + mouse combined |
| `crypto` | AES, RSA, SHA-1/2/3, Blake3, SM2/3/4, MD5, RIPEMD-160 |
| `screenshot` | Screen capture — Windows GDI / macOS CoreGraphics / Linux X11+Wayland |
| `images` | Image resize, padding, ICO & ICNS conversion |
| `date` | Date/time formatting, Excel serial-date conversion |
| `xlsx` | Read & write `.xlsx` files |
| `docx` | Read & write `.docx` files |
| `exiftool` | EXIF metadata extraction |
| `httprs` | Simplified reqwest HTTP client (sync & async) |
| `json` | `json!` macro, `to_json_value`, `as_bytes` |
| `asyncrs` | Tokio runtime + `block_on` + async `command` |
| `logger` | Tracing-subscriber with file appender & env-filter |
| `sqlserver` | Deadpool Tiberius SQL Server pool |
| `axumserver` | Axum HTTP server (CORS, TLS, rate-limit) |
| `ip_addr` | Local IP / interface discovery |
| `process` | Enumerate & kill processes |
| `sys_random` | Cryptographically secure random bytes & UUID v4 |
| `full` | All of the above |
**Always available** (no feature needed): `utils` (hex/base64/percent/temp),
`color`, `fs`, `system`/`command`, `rand`, `macros`, `traits`, `timer`,
`zoml`, `structs`, `DynError<T>`.
## Quick start
```rust
use doe::utils; // always available
assert_eq!(utils::hex::encode(b"hi"), "6869");
assert_eq!(utils::base64::encode_standard(b"hi"), "aGk=");
```
---
## Examples
### utils — encoding & temp (no feature)
```rust
use doe::utils;
// Hex
assert_eq!(utils::hex::encode(b"hello"), "68656c6c6f");
assert_eq!(utils::hex::decode("68656C6C6F").unwrap(), b"hello");
// Base64 (standard, no-pad, URL-safe)
assert_eq!(utils::base64::encode_standard(b"hello"), "aGVsbG8=");
assert_eq!(utils::base64::encode_standard_no_pad(b"hello"), "aGVsbG8");
assert_eq!(utils::base64::encode_url_safe(b">?"), "Pj8=");
assert_eq!(utils::base64::decode_standard("aGVsbG8=").unwrap(), b"hello");
// Percent-encoding
let enc = utils::percent::encode("a b", utils::percent::EncodeSet::NonAlphanumeric);
assert_eq!(enc, "a%20b");
assert_eq!(utils::percent::decode(&enc).unwrap(), "a b");
// RAII temporary directory
{
let tmp = utils::temp::TempDir::new().unwrap();
std::fs::write(tmp.path().join("x.txt"), b"data").unwrap();
} // cleaned up automatically
```
### color — RGB / hex (no feature)
```rust
use doe::color;
assert_eq!(color::rgb_to_hex((255, 87, 51)), "#FF5733");
assert_eq!(color::hex_to_rgb("#FF5733").unwrap(), (255, 87, 51));
```
### fs — file-system (no feature)
```rust
use doe::fs;
use std::path::Path;
// Walk directories
let files = fs::walk_dir_get_files(Path::new(".")).unwrap();
let folders = fs::walk_dir_get_folders(Path::new(".")).unwrap();
// Copy / move single files
fs::copy_file(Path::new("a.txt"), Path::new("b.txt")).unwrap();
fs::move_file(Path::new("b.txt"), Path::new("c.txt")).unwrap();
// Copy / move entire folders
fs::copy_folder(Path::new("src"), Path::new("backup")).unwrap();
fs::move_folder(Path::new("old"), Path::new("new")).unwrap();
// Append & remove read-only
fs::append_data_to_file("log.txt", b"new line\n".to_vec()).unwrap();
fs::set_not_readonly("readonly.txt").unwrap();
```
### system / command — shell (no feature)
```rust
use doe::{system, command};
system("echo hello").unwrap(); // fire & forget
let out = command("echo hello").unwrap(); // capture output
println!("stdout: {}", out.stdout);
println!("stderr: {}", out.stderr);
assert!(out.status.success());
```
### rand — LCG PRNG (no feature)
```rust
use doe::rand::LCG;
let mut rng = LCG::new();
println!("random u32: {}", rng.random());
println!("random f32: {}", rng.random_f32());
```
### macros — utility macros (no feature)
```rust
use doe::*;
vec![1, 2, 3].dprintln(); // debug-print with file:line prefix
"hello".dprintln();
if has_powershell!() {
println!("running on Windows with PowerShell");
}
```
### traits — common traits (no feature)
```rust
use doe::*;
use doe::traits::*;
// DebugPrint
"hello".dprintln(); // [src/main.rs:5] "hello"
vec![1,2,3].dprintln();
// Sleep — call .sleep(secs) on any value
let x = 42.sleep(0.5); // waits 500 ms, then x == 42
// Spawn — .spawn() convenience
```
### timer — scheduled tasks (no feature)
```rust
use doe::run_timer;
use std::time::{Duration, Instant};
run_timer(
Instant::now(),
Duration::from_secs(1), // run every 1s
Duration::from_secs(5), // stop after 5s
Box::new(|| println!("tick")),
);
```
### zoml — ZOML / CSV (no feature)
```rust
use doe::zoml;
let csv = "name,age\nAlice,30\nBob,25";
let zoml = zoml::csv_to_zoml(csv).unwrap();
let back = zoml::zoml_to_csv(&zoml).unwrap();
assert_eq!(csv, back);
```
### structs — Bfn / Bts (no feature)
```rust
use doe::structs::*;
// Bfn: buffered file read/write
let bfn = Bfn::new();
bfn.write("data.txt", "hello world").unwrap();
assert_eq!(bfn.read("data.txt").unwrap(), "hello world");
```
### crypto (`-F crypto`)
```rust
use doe::crypto;
// SHA family
assert_eq!(crypto::md5(b"hello"), "5d41402abc4b2a76b9719d911017c592");
println!("sha1: {}", crypto::sha1::sha1(b"hello"));
println!("sha256: {}", crypto::sha2::sha_256(b"hello"));
println!("sha512: {}", crypto::sha2::sha_512(b"hello"));
println!("blake3: {}", crypto::blake3(b"hello"));
println!("blake3_xof: {}", crypto::blake3_xof(b"hello", 64));
// AES-256-CBC
let key = b"12345678123456781234567812345678";
let iv = b"1234567812345678";
let enc = crypto::aes::aes_128::aes_cbc_encrypt(key, iv, b"secret").unwrap();
let dec = crypto::aes::aes_128::aes_cbc_decrypt(key, iv, &enc).unwrap();
assert_eq!(dec, b"secret");
// AES-GCM (authenticated encryption)
let key = b"0123456789abcdef0123456789abcdef";
let nonce = b"0123456789abcdef";
let aad = b"additional data";
let enc = crypto::aes::aes_128::aes_gcm_encrypt(key, nonce, aad, b"secret").unwrap();
let dec = crypto::aes::aes_128::aes_gcm_decrypt(key, nonce, aad, &enc).unwrap();
assert_eq!(dec, b"secret");
// RSA
let (priv_key, pub_key) = crypto::rsa::generate_keypair(2048);
let enc = crypto::rsa::encrypt(&pub_key, b"message").unwrap();
let dec = crypto::rsa::decrypt(&priv_key, &enc).unwrap();
assert_eq!(dec, b"message");
// SM2 sign & verify
let sig = crypto::sm2::sm2_sign(
b"private_key_32_bytes____",
b"message to sign",
).unwrap();
let ok = crypto::sm2::sm2_verify(
b"public_key_64_bytes_____",
b"message to sign",
&sig,
).unwrap();
assert!(ok);
```
### ctf — encoding & text (`-F ctf`)
```rust
use doe::ctf::*;
// Hex
assert_eq!(hex_encode(b"hi"), "6869");
assert_eq!(hex_decode("6869").unwrap(), b"hi");
// Base64
assert_eq!(base64_encode(b"hi"), "aGk");
assert_eq!(base64_decode("aGk"), b"hi");
// URL encode / decode
assert_eq!(url_encode("a b"), "a%20b");
assert_eq!(url_decode("a%20b"), "a b");
// Chinese detection & conversion
assert!(contains_chinese("hello 测试"));
assert!(!contains_chinese("hello"));
assert_eq!(chinese_to_unicode("学习").unwrap(), "\\u5b66\\u4e60");
// Pinyin conversion
let py = convert_to_pinyin_with_non_chinese("测试 test");
println!("pinyin: {py}"); // ce_shi_test
// HTML parsing
let html = r#"<html><body><a href="/x">link</a></body></html>"#;
let links = parse_html(html, "a", Some("href"));
assert_eq!(links, vec!["/x"]);
// Find URLs / PNG URLs in text
let urls = find_urls("visit https://example.com now");
assert!(urls.contains(&"https://example.com".to_string()));
// CtfParty trait (on &str and String)
use doe::ctf::{CtfParty, Base64Mode};
assert_eq!("hello".to_b64(Base64Mode::Strict), "aGVsbG8=");
assert_eq!("hello".md5(), "5d41402abc4b2a76b9719d911017c592");
assert_eq!("hello".sha1(), "aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d");
assert_eq!("HELLO".rot(13), "URYYB");
assert_eq!("hello".leet(), "h3ll0");
// Validation
assert!("192.168.1.1".is_ipv4());
assert!("test@example.com".is_email(doe::ctf::EmailMode::Light));
assert!("example.com".is_domain());
```
### clipboard (`-F clip`)
```rust
use doe::clipboard;
clipboard::set_clipboard("copied text").unwrap();
let text = clipboard::get_clipboard().unwrap();
assert_eq!(text, "copied text");
```
### keyboard (`-F keyboard`)
```rust
use doe::keyboard::{key_click, key_press, key_release, KeyCode};
key_click("a"); // press + release
key_press("ctrl+c"); // hold Ctrl, tap C
key_release("ctrl+c"); // release
// Virtual-key codes
key_press(KeyCode::A);
key_press(KeyCode::ENTER);
key_press(KeyCode::F1);
```
### mouse (`-F mouse`)
```rust
use doe::mouse;
// Move & click
mouse::move_to(500, 300);
mouse::press("left");
// Right-click at position
mouse::move_and_press_right(800, 900);
// Drag
mouse::mouse_drag((100, 100).into(), (200, 200).into());
// Scroll
mouse::scrool(2); // scroll up
mouse::scrool(-2); // scroll down
```
### screenshot (`-F screenshot`)
```rust
use doe::screenshot;
// Capture all screens to PNG files
screenshot::capture_all(); // → screenshot_0.png, screenshot_1.png, …
// Crop a region from the primary screen
if let Some(img) = screenshot::capture_area(10, 10, 200, 100) {
img.save("cropped.png").unwrap();
}
// Read a pixel from an image
if let Some((r, g, b)) = screenshot::get_rgb_from_position("screenshot_0.png", 0, 0) {
println!("#{r:02X}{g:02X}{b:02X}");
}
// Crop an existing image
use image::{ImageBuffer, Rgba};
let img = ImageBuffer::new(100, 100);
let cropped = screenshot::cut_area(img, 10, 10, 50, 50).unwrap();
```
### images (`-F images`)
```rust
use doe::images;
// Add white padding
images::image_add_padding(
"in.png", [255u8, 255, 255, 255], 10, 10, "out.png",
);
// Resize
images::image_resize("big.png", 64, 64, "thumb.png");
// Convert formats
images::convert_to_ico("icon.png", "icon.ico");
images::images_to_icns(
&["16.png", "32.png", "64.png", "128.png"],
"app.icns",
).unwrap();
```
### date (`-F date`)
```rust
use doe::date;
println!("now: {}", date::now_str());
println!("year: {}", date::year());
println!("month: {}", date::month());
println!("day: {}", date::day());
println!("hours: {}", date::hours());
println!("minutes: {}", date::minutes());
println!("seconds: {}", date::seconds());
// Recent 7 days
let days = date::get_recent_seven_days();
for d in &days { println!("{d}"); }
// Excel serial date conversions
let excel_val = date::normal_date_to_excel_date("2024-01-01").unwrap();
assert_eq!(excel_val, 45292);
let iso = date::excel_date_to_normal_date(45292).unwrap();
assert_eq!(iso, "2024-01-01");
```
### xlsx (`-F xlsx`)
```rust
use doe::xlsx;
use doe::cellvalue;
// Set cell values
xlsx::xlsx_set_values("demo.xlsx", "Sheet1", &[
cellvalue!("A1", "Name", "string"),
cellvalue!("B1", "42", "number"),
cellvalue!("C1", "=A1&B1", "formula"),
]).unwrap();
// Read a cell value
let val = xlsx::xlsx_get_cell_value_str("demo.xlsx", "Sheet1", "A1").unwrap();
println!("A1 = {val}");
// Read sheet names
let names = xlsx::xlsx_get_sheet_names("demo.xlsx").unwrap();
println!("sheets: {names:?}");
// Replace values & save
xlsx::xlsx_replace_values_save(
"template.xlsx", "Sheet1",
&[("{{name}}".to_string(), "Alice".to_string())],
"output.xlsx",
).unwrap();
// Column name helpers
assert_eq!(xlsx::num_to_col(0), "A");
assert_eq!(xlsx::col_to_num("D"), 3);
```
### docx (`-F docx`)
```rust
use doe::docx;
// Replace placeholder text
docx::docx_replace("template.docx", "{{name}}", "Alice").unwrap();
// Extract all text content
let content = docx::docx_get_content("doc.docx").unwrap();
println!("{content}");
// Remove read-only protection
docx::docx_remove_read_only("protected.docx").unwrap();
```
### exiftool (`-F exiftool`)
```rust
use doe::exiftool;
// Read all metadata
let meta = exiftool::run("photo.jpg").unwrap();
if let Some(date) = meta.get("EXIF:DateTimeOriginal") {
println!("taken on: {date}");
}
// Filter by keyword
let camera = exiftool::run_filtered("photo.jpg", "camera").unwrap();
for (k, v) in camera {
println!("{k} = {v}");
}
// Get a single tag
let model = exiftool::get_tag("photo.jpg", "EXIF:Model").unwrap();
println!("camera: {model:?}");
```
### httprs (`-F httprs`)
```rust
use doe::httprs;
// Sync client with custom headers
let headers = httprs::headers![
"Content-Type" => "application/json",
"Authorization" => "Bearer my-token",
];
let client = httprs::sync_client_with_headers(headers);
let body = client.sync_get_bytes("https://httpbin.org/get").unwrap();
println!("{}", String::from_utf8_lossy(&body));
// Async client
use doe::httprs::{async_client_with_headers, ClientAsyncSend};
// #[tokio::main]
// async fn main() {
// let client = async_client_with_headers(headers).await;
// let body = client.async_get_bytes("https://httpbin.org/get").await;
// }
```
### json (`-F json`)
```rust
use doe::json::{json, ToJsonValue, ValueToBytes};
// Build JSON
let val = json!({
"name": "Alice",
"age": 30,
"active": true,
"tags": ["rust", "developer"],
});
println!("{}", val);
// Serialise to bytes
let bytes: Vec<u8> = val.as_bytes();
// Deserialise from a string
let parsed = r#"{"key":"value"}"#.to_json_value();
```
### asyncrs (`-F asyncrs`)
```rust
use doe::asyncrs;
asyncrs::block_on(async {
// Run shell command asynchronously
let output = asyncrs::command("echo hello").await;
println!("{}", String::from_utf8_lossy(&output.stdout));
});
// Custom multi-thread runtime
let rt = doe::asyncrs::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap();
rt.block_on(async {
println!("running on a custom runtime");
});
```
### logger (`-F logger`)
```rust
use doe::logger;
// Initialise (terminal + file appender + env filter)
logger::init();
tracing::info!("server started on port 8080");
tracing::warn!(user_id = 42, "rate limit approaching");
tracing::error!(error = "timeout", "database connection failed");
```
### ip_addr (`-F ip_addr`)
```rust
use doe::ip_addr;
let addrs = ip_addr::get_addrs().unwrap();
for addr in addrs {
println!("{addr:?}");
}
```
### process (`-F process`)
```rust
use doe::process;
// List all running processes
let procs = process::get_all_process_name_and_pid();
for p in &procs {
println!("{:>6} {}", p.pid, p.name);
}
// Kill by name or PID
process::kill_process_by_name("notepad.exe");
process::kill_process_by_pid(1234);
```
### sys_random (`-F sys_random`)
```rust
use doe::sys_random;
// Cryptographically secure random bytes
let mut buf = [0u8; 32];
sys_random::get_system_random_bytes(&mut buf).unwrap();
println!("random bytes: {buf:02X?}");
// UUID v4
let id = sys_random::uuid();
println!("UUID: {id}"); // e.g. "550e8400-e29b-41d4-a716-446655440000"
```
### axumserver (`-F axumserver`)
```rust
use doe::axumserver;
use axum::{routing::get, Router};
#[tokio::main]
async fn main() {
// Build routes
let app = Router::new()
.route("/", get(|| async { "Hello from doe!" }))
.route("/health", get(|| async { "OK" }));
// Serve on 0.0.0.0:3000 with CORS & TLS
axumserver::server_app_default_ip(app).await.unwrap();
}
```
### DynError — error alias (always available)
```rust
use doe::DynError;
fn read_config(path: &str) -> DynError<String> {
let content = std::fs::read_to_string(path)?;
Ok(content)
}
let config = read_config("config.toml");
```
---
## MSRV
**1.73** — required for `OnceLock` and `div_ceil` used in `utils`.
## License
MIT © Andrew (dnrops@outlook.com)