# Comprehensive Usage Examples
This document provides detailed examples for using `rok-utils`.
## Table of Contents
1. [String Utilities](#string-utilities)
2. [Fluent Builder](#fluent-builder)
3. [Array/Collection Utilities](#arraycollection-utilities)
4. [Error Handling](#error-handling)
5. [Functional Patterns](#functional-patterns)
6. [Data Utilities](#data-utilities)
7. [File System Helpers](#file-system-helpers)
8. [Type Guards](#type-guards)
---
## String Utilities
### Case Conversion
```rust
use rok_utils::{to_snake_case, to_camel_case, to_kebab_case, to_pascal_case};
let snake = to_snake_case("HelloWorld"); // "hello_world"
let camel = to_camel_case("hello_world"); // "helloWorld"
let kebab = to_kebab_case("HelloWorld"); // "hello-world"
let pascal = to_pascal_case("hello_world"); // "HelloWorld"
```
### Truncation
```rust
use rok_utils::truncate;
let short = truncate("Hello World", 5); // "Hello..."
let complete = truncate("Hello World", 5); // Already short, unchanged
```
### Slug Generation
```rust
use rok_utils::slug;
let slug = slug("Hello World!"); // "hello-world"
let custom = slug("Hello World!", '_'); // "hello_world"
```
### Pluralization (feature: "unicode")
```rust
use rok_utils::pluralize;
assert_eq!(pluralize("cat", 1), "cat");
assert_eq!(pluralize("cat", 2), "cats");
assert_eq!(pluralize("box", 3), "boxes");
```
### Base64 Encoding
```rust
use rok_utils::to_base64;
let encoded = to_base64("Hello"); // "SGVsbG8="
```
### String Inspection
```rust
use rok_utils::{
is_empty, is_ascii, is_url, starts_with, ends_with, contains,
word_count, length
};
assert!(!is_empty("hello"));
assert!(is_ascii("hello"));
assert!(is_url("https://example.com"));
assert!(starts_with("hello world", "hello"));
assert!(ends_with("hello world", "world"));
assert!(contains("hello world", "lo"));
assert_eq!(word_count("hello world"), 2);
assert_eq!(length("hello"), 5);
```
---
## Fluent Builder
The `Str::of()` builder provides a chainable API for complex string transformations.
### Basic Chaining
```rust
use rok_utils::str::Str;
let result = Str::of(" Hello World ")
.trim()
.to_snake_case()
.value();
assert_eq!(result, "hello_world");
```
### Conditional Transformations
```rust
use rok_utils::str::Str;
let result = Str::of("hello")
.when(true, |s| s.append(" world"))
.when(false, |s| s.append(" hidden"))
.value();
assert_eq!(result, "hello world");
```
### Empty Handling
```rust
use rok_utils::str::Str;
let result = Str::of("")
.when_empty(|s| s.append("default"))
.value();
assert_eq!(result, "default");
```
### Content-Based Conditionals
```rust
use rok_utils::str::Str;
let result = Str::of("user@example.com")
.when_contains("@", |s| s.replace("@", " [at] "))
.value();
assert_eq!(result, "user [at] example.com");
```
### Prefix/Suffix Operations
```rust
use rok_utils::str::Str;
let result = Str::of("world")
.ensure_start("hello ")
.finish("!")
.value();
assert_eq!(result, "hello world!");
```
### Text Wrapping
```rust
use rok_utils::str::Str;
let result = Str::of("content")
.wrap("[", "]")
.value();
assert_eq!(result, "[content]");
```
### Side Effects with Tap
```rust
use rok_utils::str::Str;
let mut collected = Vec::new();
let _ = Str::of("hello")
.tap(|s| collected.push(s.len()))
.tap(|s| collected.push(s.chars().count()))
.value();
// collected: [5, 5]
```
### Custom Transformations with Pipe
```rust
use rok_utils::str::Str;
let result = Str::of("hello")
.pipe(|s| s.to_uppercase())
.pipe(|s| format!("{s}!"))
.value();
assert_eq!(result, "HELLO!");
```
### Complete Real-World Example
```rust
use rok_utils::str::Str;
fn format_username(name: &str) -> String {
Str::of(name)
.trim()
.lower()
.when_empty(|s| s.append("anonymous"))
.replace(" ", "_")
.prepend("@")
.value()
}
assert_eq!(format_username(" John Doe "), "@john_doe");
assert_eq!(format_username(" "), "@anonymous");
```
---
## Array/Collection Utilities
### Mapping and Filtering
```rust
use rok_utils::arr::{map, filter};
let numbers = [1, 2, 3, 4, 5];
```
### Finding Elements
```rust
use rok_utils::arr::{first, last, find};
let items = ["a", "b", "c"];
assert_eq!(first(&items), Some(&"a"));
assert_eq!(last(&items), Some(&"c"));
```rust
use rok_utils::arr::{unique, diff, intersect, without};
let a = [1, 2, 3, 2, 1];
let uniq = unique(&a);
assert_eq!(uniq, vec![1, 2, 3]);
let difference = diff(&[1, 2, 3], &[2]);
assert_eq!(difference, vec![1, 3]);
let intersection = intersect(&[1, 2, 3], &[2, 3, 4]);
assert_eq!(intersection, vec![2, 3]);
let filtered = without(&[1, 2, 3, 4], &[2, 4]);
assert_eq!(filtered, vec![1, 3]);
```
### Membership Testing
```rust
use rok_utils::arr::contains;
let items = ["a", "b", "c"];
assert!(contains(&items, &"b"));
assert!(!contains(&items, &"d"));
```
### Reducing
```rust
use rok_utils::arr::reduce;
let numbers = [1, 2, 3, 4];
```
---
## Error Handling
### Basic Error Creation
```rust
use rok_utils::RokError;
let err = RokError::NotFound("User #42".into());
assert_eq!(err.code(), "E_NOT_FOUND");
assert_eq!(err.status(), 404);
assert!(err.is_self_handled());
```
### Result Extension
```rust
use rok_utils::{RokResultExt, RokError};
fn find_user(id: u64) -> Result<String, RokError> {
if id == 42 {
Ok("Alice".to_string())
} else {
Err(RokError::NotFound(format!("User #{id}")))
}
}
let result = find_user(42).context("Database query failed");
assert!(result.is_ok());
let result = find_user(0).or_not_found("User");
assert!(matches!(result, Err(RokError::NotFound(_))));
```
### Contextual Errors
```rust
use rok_utils::{RokResultExt, RokError};
let data = vec![1, 2, 3];
let result = data.get(10)
.ok_or(RokError::NotFound("Element not found".into()))
.context("Array access failed");
```
### Validation Errors
```rust
use rok_utils::RokError;
fn validate_email(email: &str) -> Result<(), RokError> {
if email.contains('@') {
Ok(())
} else {
Err(RokError::ValidationFailure {
field: "email".to_string(),
reason: "Must contain @".to_string(),
})
}
}
let result = validate_email("invalid");
assert!(matches!(result, Err(RokError::ValidationFailure { .. })));
```
### Handling Different Error Types
```rust
use rok_utils::RokError;
fn handle_error(err: &RokError) -> String {
match err {
RokError::NotFound(msg) => format!("404: {}", msg),
RokError::ValidationFailure { field, reason } => {
format!("Validation error on {}: {}", field, reason)
}
RokError::Unauthorized(msg) => format!("401: {}", msg),
_ => format!("Error: {}", err.code()),
}
}
```
---
## Functional Patterns
### Pipe
```rust
use rok_utils::fp::pipe;
let result = pipe(5, vec![
|x| x + 1,
|x| x * 2,
|x| x - 3,
]);
assert_eq!(result, 9); // (5 + 1) * 2 - 3 = 9
```
### Compose
```rust
use rok_utils::fp::compose;
let add_one = |x: i32| x + 1;
assert_eq!(add_then_double(5), 12); // (5 + 1) * 2
```
### Tap (Side Effects)
```rust
use rok_utils::fp::tap;
let mut log = Vec::new();
```rust
use rok_utils::fp::memoize;
let call_count = std::cell::Cell::new(0);
x * x
});
assert_eq!(expensive(5), 25);
assert_eq!(expensive(5), 25); // Uses cached result
assert_eq!(call_count.get(), 1);
```
### Lazy Initialization
```rust
use rok_utils::fp::Lazy;
"config_value".to_string()
});
println!("Before access");
let value = config.get();
println!("After access: {}", value);
let value2 = config.get(); // No re-initialization
```
---
## Data Utilities
### Number Formatting (feature: default)
```rust
use rok_utils::data::numbers::{format_number, format_currency, format_percentage};
assert_eq!(format_number(1234567.89, 2, ','), "1,234,567.89");
assert_eq!(format_currency(1234.50, "USD"), "$1,234.50");
assert_eq!(format_percentage(0.1234, 1), "12.3%");
```
### Rounding
```rust
use rok_utils::data::numbers::{round, ceil, floor};
assert_eq!(round(3.14159, 2), 3.14);
assert_eq!(ceil(3.001, 0), 4.0);
assert_eq!(floor(3.999, 0), 3.0);
```
### Date/Time (feature: "dates")
```rust
use rok_utils::{now, today, format_date, add_days};
let today = today();
let tomorrow = add_days(&today, 1);
let now = now();
let formatted = format_date(&now, "%Y-%m-%d %H:%M");
```
### UUID/ULID Generation (feature: "ids")
```rust
use rok_utils::{uuid_v4, uuid_v7, ulid, is_uuid, is_ulid};
let uuid = uuid_v4();
let uuid7 = uuid_v7();
let id = ulid();
assert!(is_uuid(&uuid));
assert!(is_ulid(&id));
```
### Hashing (feature: "crypto")
```rust
use rok_utils::{hash_sha256, verify_sha256, generate_token, secure_compare};
let hash = hash_sha256("password");
assert!(verify_sha256("password", &hash));
let token = generate_token(32);
assert_eq!(token.len(), 32); // 32 bytes as hex
assert!(secure_compare("secret", "secret"));
assert!(!secure_compare("secret", "Secret"));
```
### Random Strings (feature: "random")
```rust
use rok_utils::{random, password};
let rand_str = random(16); // 16 character random string
let pass = password(16, true); // with symbols
```
---
## File System Helpers
### Directory Operations
```rust
use rok_utils::fs::{ensure_dir, is_dir, is_file, find_files};
ensure_dir("./data/output").expect("Failed to create directory");
if is_dir("./data") {
let files = find_files("./data", "*.txt").unwrap();
}
```
### File Operations
```rust
use rok_utils::fs::{read_to_string, write_atomic, copy_dir_all};
let content = read_to_string("config.toml").unwrap();
write_atomic("output.txt", "Hello, World!").unwrap();
```
### Directory Copying
```rust
use rok_utils::fs::copy_dir_all;
copy_dir_all("source", "destination").expect("Copy failed");
```
---
## Type Guards (feature: "json")
```rust
use rok_utils::types::{is_string, is_number, is_array, is_object};
use serde_json::json;
let value = json!({
"name": "Alice",
"age": 30,
"scores": [95, 87, 92]
});
assert!(is_string(&value["name"]));
assert!(is_number(&value["age"]));
assert!(is_array(&value["scores"]));
assert!(is_object(&value));
```
### Deep Path Access
```rust
use rok_utils::types::get_path;
use serde_json::json;
let data = json!({
"user": {
"address": {
"city": "New York"
}
}
});
assert_eq!(get_path(&data, "user.address.city"), Some(&json!("New York")));
assert_eq!(get_path(&data, "user.missing"), None);
```
### Deep Path Setting
```rust
use rok_utils::types::set_path;
use serde_json::json;
let mut data = json!({"a": 1});
data = set_path(data, "b", json!(2));
assert_eq!(data.get("b"), Some(&json!(2)));
```
### Deep Equality
```rust
use rok_utils::types::deep_equal;
use serde_json::json;
assert!(deep_equal(&json!({"a": 1}), &json!({"a": 1})));
assert!(!deep_equal(&json!({"a": 1}), &json!({"a": 2})));
```
---
## Complete Examples
### Web API Input Validation
```rust
use rok_utils::{RokError, RokResultExt, Str, truncate};
fn validate_username(username: &str) -> Result<String, RokError> {
let normalized = Str::of(username)
.trim()
.lower()
.when_empty(|_| panic!("Username required"))
.value();
if normalized.len() < 3 {
return Err(RokError::ValidationFailure {
field: "username".to_string(),
reason: "Must be at least 3 characters".to_string(),
});
}
if normalized.len() > 20 {
return Err(RokError::ValidationFailure {
field: "username".to_string(),
reason: "Must be at most 20 characters".to_string(),
});
}
Ok(normalized)
}
```
### Slug Generation for URLs
```rust
use rok_utils::{Str, slug, to_snake_case};
fn generate_url_slug(title: &str) -> String {
Str::of(title)
.trim()
.lower()
.replace("'", "")
.squish()
.value()
}
assert_eq!(generate_url_slug(" Hello World !"), "hello world");
```
### API Response Formatting
```rust
use rok_utils::{Str, truncate};
fn format_error_message(error: &str) -> String {
Str::of(error)
.trim()
.truncate(100)
.finish("...")
.wrap("[Error] ", "")
.value()
}
assert_eq!(format_error_message(" Something went wrong "), "[Error] Something went wrong...");
```
---
## Recipes
Common patterns and ready-to-use solutions.
### URL Slug Generation
Generate URL-friendly slugs from titles:
```rust
use rok_utils::str::Str;
fn generate_slug(title: &str) -> String {
Str::of(title)
.trim()
.lower()
.replace("'", "")
.replace("&", " and ")
.squish()
.pipe(|s| slug(&s, '-'))
.value()
}
assert_eq!(generate_slug(" Hello World! "), "hello-world");
assert_eq!(generate_slug("O'Reilly's Guide"), "oreillys-guide");
```
### Username Normalization
Normalize usernames for consistent storage:
```rust
use rok_utils::str::Str;
fn normalize_username(username: &str) -> String {
Str::of(username)
.trim()
.lower()
.replace(" ", "_")
.replace("@", "")
.when_empty(|s| s.append("anonymous"))
.value()
}
assert_eq!(normalize_username(" JohnDoe "), "johndoe");
assert_eq!(normalize_username("@admin"), "admin");
assert_eq!(normalize_username(" "), "anonymous");
```
### Email Masking
Mask email addresses for privacy:
```rust
use rok_utils::str::Str;
fn mask_email(email: &str) -> String {
let parts: Vec<&str> = email.split('@').collect();
if parts.len() != 2 {
return email.to_string();
}
let local = parts[0];
let domain = parts[1];
if local.len() <= 2 {
Str::of(email)
.replace(local, "*".repeat(local.len()).as_str())
.value()
} else {
Str::of(email)
.replace(local, &format!("{}***", &local[..1]))
.value()
}
}
assert_eq!(mask_email("john@example.com"), "j***@example.com");
assert_eq!(mask_email("ab@example.com"), "**@example.com");
```
### Credit Card Formatting
Format credit card numbers with spaces:
```rust
use rok_utils::str::Str;
fn format_card_number(card: &str) -> String {
let digits: String = card.chars().filter(|c| c.is_ascii_digit()).collect();
Str::of(&digits)
.pipe(|s| {
s.chars()
.collect::<Vec<_>>()
.chunks(4)
.map(|chunk| chunk.iter().collect::<String>())
.collect::<Vec<_>>()
.join(" ")
})
.value()
}
assert_eq!(format_card_number("1234567890123456"), "1234 5678 9012 3456");
```
### Title Case with Exceptions
Apply title case while preserving acronyms and small words:
```rust
use rok_utils::str::Str;
fn smart_title(title: &str) -> String {
let small_words = ["a", "an", "the", "and", "but", "or", "for", "nor", "on", "at", "to", "by"];
Str::of(title)
.title()
.pipe(|s: String| {
let words: Vec<String> = s.split_whitespace()
.map(|word| {
let lower = word.to_lowercase();
if small_words.contains(&lower.as_str()) && word != s.split_whitespace().next().unwrap() {
lower
} else if word.chars().all(|c| c.is_uppercase()) {
word.to_string()
} else {
word.to_string()
}
})
.collect();
words.join(" ")
})
.value()
}
assert_eq!(smart_title("the quick brown fox"), "The Quick Brown Fox");
```
### Dynamic Error Messages
Build user-friendly error messages:
```rust
use rok_utils::{RokError, Str};
fn format_error(error: &RokError, context: &str) -> String {
let code = error.code();
let message = match error {
RokError::NotFound(msg) => format!("{} not found", msg),
RokError::ValidationFailure { field, reason } => format!("Invalid {}: {}", field, reason),
RokError::Unauthorized(msg) => msg.clone(),
_ => "An unexpected error occurred".to_string(),
};
Str::of(&message)
.wrap("[", "]")
.prepend(&format!("{}: ", context))
.value()
}
let err = RokError::NotFound("User".to_string());
assert!(format_error(&err, "Lookup").contains("User not found"));
```
### Batch Processing with Retry
Process items with retry logic:
```rust
use rok_utils::fp::{retry, pipe};
fn process_with_retry<T, U>(
items: Vec<T>,
processor: impl Fn(T) -> Result<U, &'static str>,
max_retries: usize,
) -> Vec<Result<U, &'static str>> {
items.into_iter()
.map(|item| {
retry(max_retries, || processor(item.clone()))
})
.collect()
}
```
### Config File Loading
Load and validate configuration:
```rust
use rok_utils::{RokError, RokResultExt, Str};
#[derive(Debug)]
struct Config {
host: String,
port: u16,
database: String,
}
fn load_config(path: &str) -> Result<Config, RokError> {
let content = std::fs::read_to_string(path)
.context("Failed to read config file")?;
let json: serde_json::Value = serde_json::from_str(&content)
.context("Failed to parse config JSON")?;
let host = json["host"].as_str()
.ok_or_else(|| RokError::ValidationFailure {
field: "host".to_string(),
reason: "Missing or invalid".to_string(),
})?;
let port = json["port"].as_u64()
.ok_or_else(|| RokError::ValidationFailure {
field: "port".to_string(),
reason: "Missing or invalid".to_string(),
})? as u16;
let database = json["database"].as_str()
.ok_or_else(|| RokError::ValidationFailure {
field: "database".to_string(),
reason: "Missing or invalid".to_string(),
})?;
Ok(Config {
host: host.to_string(),
port,
database: database.to_string(),
})
}
```
### API Response Building
Build consistent API responses:
```rust
use rok_utils::str::Str;
#[derive(Debug, serde::Serialize)]
struct ApiResponse<T> {
success: bool,
data: Option<T>,
error: Option<String>,
}
impl<T> ApiResponse<T> {
fn ok(data: T) -> Self {
Self {
success: true,
data: Some(data),
error: None,
}
}
fn err(message: &str) -> Self {
Self {
success: false,
data: None,
error: Some(Str::of(message)
.trim()
.lower()
.to_string()),
}
}
}
```
### Form Input Sanitization
Sanitize user input for safe storage:
```rust
use rok_utils::str::Str;
fn sanitize_input(input: &str) -> String {
Str::of(input)
.trim()
.replace("<", "<")
.replace(">", ">")
.replace("\"", """)
.replace("'", "'")
.replace("&", "&")
.squish()
.value()
}
assert_eq!(sanitize_input(" <script>alert('xss')</script> "), "<script>alert('xss')</script>");
```
### Path Manipulation
Safe file path operations:
```rust
use rok_utils::{path::normalize, path::with_extension, path::stem_ext};
fn safe_file_rename(old_path: &str, new_name: &str) -> Result<String, String> {
let (stem, ext) = stem_ext(old_path)
.ok_or("Invalid path")?;
let new_ext = ext.unwrap_or("txt");
let parent = Str::of(old_path)
.pipe(|s: String| {
std::path::Path::new(&s)
.parent()
.map(|p| p.to_string_lossy().to_string())
.unwrap_or_default()
});
let new_path = std::path::Path::new(&parent)
.join(new_name)
.with_extension(new_ext)
.to_string_lossy()
.to_string();
Ok(normalize(&new_path))
}
```