neocrates 0.1.44

A comprehensive Rust library for various utilities and helpers
Documentation
# Helper Module

The `helper` module is the always-available toolbox in Neocrates. It gathers reusable utilities for IDs, retries, pagination, serde normalization, config loading, text chunking, lightweight validation, and a few web-only Axum extractors.

See also: [root README](../../README.md)

---

## What this module contains

Top-level areas in `helper::core`:

- **IDs**: Snowflake, Sonyflake, and Crockford-style hashid encode/decode helpers
- **Request/data normalization**: serde deserialize/serialize helpers, page-size normalization, string/number coercion
- **Validation helpers**: mobile/landline/email checks and masking utilities
- **Retries**: reusable async retry helpers with exponential backoff
- **Config loading**: upward YAML file search based on `ENV`
- **Pagination**: `PageParams`, `PageResponse`, and offset/limit conversion
- **Text tooling**: chunk parsed text by length while preserving metadata
- **Web-only extras**: `LoggedJson<T>` and `DetailedJson<T>` Axum extractors

---

## Feature and compatibility notes

- `helper` itself is **always compiled**.
- `helper::core::axum_extractor` is only available with `web` or `full`.
- Most helpers are framework-agnostic and can be used without Axum.

---

## Quick start

```rust
use neocrates::helper::core::{
    page::to_offset_limit,
    retry::{RetryPolicy, retry_async},
    snowflake::generate_snowflake_id,
    utils::Utils,
};

async fn demo() -> neocrates::anyhow::Result<()> {
    let token = Utils::generate_token();
    let id = generate_snowflake_id();
    let (_current, _size, offset, limit) = to_offset_limit(2, 20);

    let policy = RetryPolicy::default();
    let value = retry_async(&policy, "demo", || async {
        Ok::<_, neocrates::anyhow::Error>(42)
    })
    .await?;

    println!("{token} {id} {offset} {limit} {value}");
    Ok(())
}
```

---

## Step-by-step tutorial

## 1. Load YAML config with automatic environment-aware lookup

`helper::core::loader::load_config()` searches upward from the current directory and checks files in this order:

1. `application.{ENV}.yml|yaml`
2. `config.{ENV}.yml|yaml`
3. `application.yml|yaml`
4. `config.yml|yaml`

Example:

```rust
use neocrates::helper::core::loader::load_config;
use neocrates::serde::Deserialize;

#[derive(Debug, Deserialize)]
struct AppConfig {
    server_port: u16,
    debug_mode: bool,
}

fn main() {
    std::env::set_var("ENV", "development");
    let config = load_config::<AppConfig>();
    println!("{config:?}");
}
```

## 2. Normalize IDs and pagination in request DTOs

`serde_helpers` lets you accept string-or-number inputs and normalize them at deserialize time.

```rust
use neocrates::helper::core::serde_helpers::{
    deserialize_option_i64,
    normalize_current,
    normalize_page_size,
};
use neocrates::serde::Deserialize;

#[derive(Debug, Deserialize)]
struct QueryDto {
    #[serde(default, deserialize_with = "normalize_current")]
    current: Option<i64>,
    #[serde(default, deserialize_with = "normalize_page_size")]
    size: Option<i64>,
    #[serde(default, deserialize_with = "deserialize_option_i64")]
    user_id: Option<i64>,
}
```

## 3. Use retries around transient storage or network failures

```rust
use neocrates::helper::core::retry::{RetryPolicy, retry_async};

async fn fetch_with_retry() -> neocrates::anyhow::Result<Vec<u8>> {
    let policy = RetryPolicy::storage_io();

    retry_async(&policy, "download-avatar", || async {
        // Replace this with your real HTTP or storage operation.
        Ok::<_, neocrates::anyhow::Error>(b"ok".to_vec())
    })
    .await
}
```

## 4. Use the Axum JSON extractors when you want structured JSON parse errors

```rust
#[cfg(feature = "web")]
use neocrates::helper::core::axum_extractor::DetailedJson;
#[cfg(feature = "web")]
use neocrates::serde::Deserialize;

#[cfg(feature = "web")]
#[derive(Debug, Deserialize)]
struct CreateUser {
    name: String,
    email: String,
}

#[cfg(feature = "web")]
async fn create_user(DetailedJson(payload): DetailedJson<CreateUser>) -> String {
    format!("created {}", payload.email)
}
```

See the runnable example: [`../../examples/axum_extractor_example.rs`](../../examples/axum_extractor_example.rs)

---

## Key points and gotchas

- `snowflake.rs` contains both a custom Snowflake generator and a Sonyflake wrapper.
- `hashid.rs` uses Crockford Base32-style encoding to present numeric IDs as compact strings.
- `Utils::is_cn_mobile()` and related helpers are pragmatic validations, not telecom-spec validators.
- `retry_async()` decides retryability from error-message text; use `retry_async_with()` when you need a custom predicate.
- `LoggedJson<T>` and `DetailedJson<T>` are helpful drop-in replacements for `axum::Json<T>` when you want structured parse failures.

---

## Roadmap

Possible future directions for `helper`:

1. Split large serde helpers into smaller, more focused submodules.
2. Add docs.rs examples for common DTO patterns.
3. Expose more framework-neutral utility traits instead of mostly static helpers.
4. Expand retry classification beyond string matching.
5. Add stronger typed helpers around loader search paths and config errors.