doe 1.1.90

doe is a powerful Rust crate designed to enhance development workflow by providing an extensive collection of useful macros and utility functions. It not only simplifies common tasks but also offers convenient features for clipboard management,robust cryptographic functions,keyboard input, and mouse interaction.
Documentation

doe — Versatile Rust Utility Crate

Crates.io docs.rs MIT

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

[dependencies]

doe = { version = "1", features = ["ctf", "crypto", "date"] }

Feature flags

Feature What it enables
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

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)

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)

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)

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)

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)

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)

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)

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
let handle = (|| println!("in thread")).spawn();
handle.join().unwrap();

timer — scheduled tasks (no feature)

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)

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)

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)

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)

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)

use doe::clipboard;

clipboard::set_clipboard("copied text").unwrap();
let text = clipboard::get_clipboard().unwrap();
assert_eq!(text, "copied text");

keyboard (-F keyboard)

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)

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)

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)

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)

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)

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)

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)

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)

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)

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)

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)

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)

use doe::ip_addr;

let addrs = ip_addr::get_addrs().unwrap();
for addr in addrs {
    println!("{addr:?}");
}

process (-F process)

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)

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)

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)

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)