# bluetape-rs

[English](README.md) | [한국어](README.ko.md)

bluetape 생태계를 위한 Rust 백엔드 primitive 라이브러리입니다.

![bluetape-rs workspace overview](docs/images/readme-diagrams/bluetape-rs-workspace-overview.png)

`bluetape-rs`는 새 WIP repository입니다. Kotlin/JVM 기반 `bluetape4k`의
포팅도 아니고, `bluetape-go`의 재작성도 아닙니다. 목표는 컴파일타임 계약, 작은
native binary, 명시적 오류 처리, 재현 가능한 integration test가 중요한 backend
서비스를 위해 Rust다운 building block을 제공하는 것입니다.

## 현재 상태

현재 release-prep package scope는 `0.4.0` workspace release입니다. Root facade와
foundation, collections, async, codec, compression focused crate 전체를 같은
version으로 배포합니다. `0.4.0` line은 opt-in compression surface를 시작합니다.

완료된 foundation, `0.2.0`, `0.3.0`, `0.4.0` 작업 범위는 작게 유지합니다.

- workspace layout과 release policy 정의
- typed validation error, string, 작은 numeric check를 위한 일반 helper 함수 추가
- library code에서 global subscriber를 강제하지 않는 logging/tracing 지원과 scoped
  test capture helper 추가
- async assertion, `MultithreadingTester`, `SuspendedJobTester`, temporary resource
  cleanup을 위한 재사용 test helper 추가
- `0.2.0` line을 위한 focused collection helper와 Tokio-first bounded task helper 추가
- `0.3.0` codec line을 위한 strict hex, Base64, Base58, Base62, UTF-8 text
  boundary helper 추가
- `0.4.0` line에서 gzip, zlib, deflate, zstd, lz4, snappy를 위한 첫 opt-in
  compression crate 추가
- Kotlin extension API나 Go package shape를 복사하지 않는 Rust-native API 유지

`0.4.0` line은 최신 stable workspace를 배포합니다. Codec scope는 hex, Base64,
Base58, Base62 helper를 위한 명시적 `bluetape-rs-codec` boundary를 유지합니다.
URL-safe encoding과 UTF-8 text/byte boundary helper도 이 범위에 포함합니다.
Compression scope는 algorithm dependency를 명시적 feature flag 뒤에 두고, release
publication 전에 동일 조건 benchmark 결과를 기록합니다.

## 계획 패키지군

| 영역 | 작업명 | 목적 |
|---|---|---|
| Core | `bluetape-rs-core` | Typed validation error, validation helper, string helper, 작은 numeric check. |
| Logging | `bluetape-rs-logging` | Tracing setup helper, structured field, bounded correlation ID, scoped test capture. |
| Testing | `bluetape-rs-test` | Async assertion, `MultithreadingTester`, `SuspendedJobTester`, temporary resource, 향후 Testcontainers 경계. |
| Collections | `bluetape-rs-collections` | Iterator, slice, map, grouping, chunking, error-aware transform helper. |
| Async | `bluetape-rs-async` | Tokio-first bounded task execution, timeout/deadline, cancellation, shutdown helper. |
| Encoding | `bluetape-rs-codec` | Base encoder, hex, URL-safe codec, 작은 binary/text codec helper. |
| Compression | `bluetape-rs-compression` | Opt-in gzip, zlib, deflate, zstd, lz4, snappy helper와 registry-style selection. |
| Serialization | `bluetape-rs-serde` | serde-compatible format을 위한 안전한 serializer/deserializer interface와 test helper. |
| Testcontainers | `bluetape-rs-testcontainers` | PostgreSQL, Redis, MySQL, NATS, Kafka, emulator fixture helper를 명시적 feature 뒤에 둡니다. |
| Leader | `bluetape-rs-leader` | Redis, SQL, etcd, Kubernetes Lease 기반 leader election. |
| SQL | `bluetape-rs-sql` | SQL AST, dialect rendering, bind collection, typed query construction. |
| SQLx | `bluetape-rs-sqlx` | SQLx executor, pool, transaction, migration, repository adapter. |
| Resilience | `bluetape-rs-resilience` | Retry, timeout, circuit breaker, bulkhead, backoff, service policy. |
| AWS | `bluetape-rs-aws` | 공식 AWS SDK for Rust 위의 얇은 helper. |
| Audit | `bluetape-rs-audit` | Audit workload를 위한 snapshot, diff, outbox, event-stream primitive. |
| Graph | `bluetape-rs-graph` | Graph model, bulk I/O, Rust driver가 성숙한 backend adapter. |
| Text | `bluetape-rs-text` | Aho-Corasick search, blockword masking, tokenizer wrapper, language detection. |
| Workshop | `bluetape-rs-workshop` | axum, Tokio, SQLx, Redis, AWS, graph, text runnable example. |

## 설계 포지션

Rust는 기존 라이브러리와 다른 가치를 제공해야 합니다.

- SQL, nullability, transaction scope, dialect capability를 더 강하게 타입으로 표현
- `Result`/`Option` 기반의 명시적 실패/없음 처리
- `Send`/`Sync`를 고려한 동시성 경계
- 낮은 runtime overhead와 작은 배포 binary
- infrastructure package는 Testcontainers 기반 실제 smoke test로 검증

첫 release는 의도적으로 지루하고 넓게 재사용 가능한 helper, logging, test
support에 집중합니다. Codec, compression, serialization, Testcontainers, leader
election은 별도 milestone으로 나누어 진행합니다. Relational SQL은 Testcontainers 뒤,
resilience 앞에 구현합니다. Leader election은 Redis, RDB, etcd, Kubernetes Lease
등 backend matrix가 큰 작업이므로 SQL과 resilience 뒤에 배치합니다. SQL을
시작할 때도 초기 형태는 full ORM이 아니라 inspect 가능한 SQL AST와 SQLx
execution adapter가 되어야 합니다.

## 사용 방법

Root facade는 기본으로 `core`만 활성화합니다. Optional facade module은 feature를
명시적으로 켜거나, 필요한 focused crate에 직접 의존합니다.

```toml
[dependencies]
bluetape-rs = { version = "0.4.0", features = ["logging", "codec"] }

[dev-dependencies]
bluetape-rs = { version = "0.4.0", features = ["test"] }
```

```rust
use bluetape_rs::{codec, core, logging};
```

Focused crate는 import name에서 hyphen 대신 underscore를 사용합니다.

```toml
[dependencies]
bluetape-rs-core = "0.4.0"
bluetape-rs-logging = "0.4.0"
bluetape-rs-collections = "0.4.0"
bluetape-rs-async = "0.4.0"
bluetape-rs-codec = "0.4.0"
bluetape-rs-compression = { version = "0.4.0", default-features = false, features = ["gzip"] }

[dev-dependencies]
bluetape-rs-test = "0.4.0"
```

```rust
use bluetape_rs_core::require_not_blank;
use bluetape_rs_logging::CorrelationId;
use bluetape_rs_collections::{Page, iter};
use bluetape_rs_codec::{decode_hex, encode_hex_lower};
use bluetape_rs_codec::{decode_base64_url_unpadded, encode_base64_url_unpadded};
use bluetape_rs_codec::{decode_base58, encode_base58};
use bluetape_rs_compression::{CompressionAlgorithm, Compressor};
use bluetape_rs_test::TempDir;
```

Collection helper를 사용할 때:

```rust
use bluetape_rs_collections::{Page, iter};

let page = Page::with_meta(vec!["a", "b"], 0, 2, 5).unwrap();
assert_eq!(page.total_pages(), 3);

let chunks: Vec<_> = iter::chunks(1..=5, 2).unwrap().collect();
assert_eq!(chunks, vec![vec![1, 2], vec![3, 4], vec![5]]);
```

Tokio task 및 control helper를 사용할 때:

```toml
[dependencies]
bluetape-rs-async = "0.4.0"
```

```rust
use std::time::Duration;

use bluetape_rs_async::{try_map_bounded, with_timeout};

async fn run() -> Result<(), Box<dyn std::error::Error>> {
    let doubled = try_map_bounded([1, 2, 3], 2, |value| async move {
        Ok::<_, &'static str>(value * 2)
    })
    .await?;

    assert_eq!(doubled, vec![2, 4, 6]);

    let value = with_timeout(Duration::from_millis(50), async { 42 }).await?;
    assert_eq!(value, 42);
    Ok(())
}
```

Codec helper를 사용할 때:

```toml
[dependencies]
bluetape-rs-codec = "0.4.0"
```

`bluetape-rs-codec`는 strict hex, Base64, Base58, Base62, URL-safe encoding,
UTF-8 text/byte boundary helper를 위한 `0.4.0` crate boundary입니다.
Compression은 `0.4.0` milestone에서 시작하고, serde-oriented serialization은
`0.5.0`으로 계속 분리합니다.

```rust
use bluetape_rs_codec::{decode_hex, encode_hex_lower, encode_hex_upper};

let bytes = [0x00, 0xab, 0xff];

assert_eq!(encode_hex_lower(bytes), "00abff");
assert_eq!(encode_hex_upper(bytes), "00ABFF");
assert_eq!(
    decode_hex("00abFF").expect("valid hex"),
    vec![0x00, 0xab, 0xff]
);
```

```rust
use bluetape_rs_codec::{
    decode_base64, decode_base64_url_unpadded, encode_base64, encode_base64_url_unpadded,
};

assert_eq!(encode_base64(b"fo"), "Zm8=");
assert_eq!(decode_base64("Zm8=").expect("valid Base64"), b"fo");

let token = encode_base64_url_unpadded([0xfb, 0xff]);
assert_eq!(token, "-_8");
assert_eq!(
    decode_base64_url_unpadded(token).expect("valid URL-safe Base64"),
    vec![0xfb, 0xff]
);
```

Standard helper는 `+`와 `/` alphabet을 사용하고, URL-safe helper는 `-`와
`_`를 사용합니다. 이름이 `_unpadded`로 끝나는 함수는 decode 시 `=` padding을
거부합니다.

```rust
use bluetape_rs_codec::{decode_base58, decode_base62, encode_base58, encode_base62};

assert_eq!(encode_base58(b"Hello, World!"), "72k1xXWG59fYdzSNoA");
assert_eq!(
    decode_base58("72k1xXWG59fYdzSNoA").expect("valid Base58"),
    b"Hello, World!"
);

assert_eq!(encode_base62(b"Hello, World!"), "1wJfrzvdbtXUOlUjUf");
assert_eq!(
    decode_base62("1wJfrzvdbtXUOlUjUf").expect("valid Base62"),
    b"Hello, World!"
);
```

Base58은 Bitcoin alphabet을 사용하고 leading zero byte를 `1`로 보존합니다.
Base62는 `0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz`를
사용하고 leading zero byte를 `0`으로 보존합니다. 이번 Base62 API는
byte-oriented primitive이며, integer/UUID rendering은 이후 상위 API에서
다룰 수 있습니다.

```rust
use bluetape_rs_codec::{
    decode_base64_url_unpadded, decode_utf8_text, decode_utf8_text_lossy,
    encode_base64_url_unpadded, encode_utf8_text,
};

let token = encode_base64_url_unpadded(encode_utf8_text("blue테이프"));
assert_eq!(token, "Ymx1Ze2FjOydtO2UhA");

let bytes = decode_base64_url_unpadded(token).expect("valid URL-safe Base64");
assert_eq!(decode_utf8_text(bytes).expect("valid UTF-8"), "blue테이프");

assert_eq!(decode_utf8_text_lossy([b'a', 0xff, b'z']), "a\u{fffd}z");
```

`decode_utf8_text`는 invalid UTF-8을 typed byte-position diagnostic으로
거부합니다. 손실 대체는 이름에 `lossy`가 들어간 `decode_utf8_text_lossy`를
명시적으로 호출할 때만 사용합니다. 일반 string utility, normalization,
compression registry, serde-oriented serialization은 codec crate 밖에 둡니다.

Codec public API test는 `crates/codec/tests/` 아래에 두어 downstream user가
호출하는 crate boundary와 같은 경로로 검증합니다. Source-local test는 private
implementation detail에만 남깁니다.

Compression helper를 사용할 때:

```toml
[dependencies]
bluetape-rs-compression = { version = "0.4.0", default-features = false, features = ["gzip"] }
```

`bluetape-rs-compression`은 gzip, zlib, deflate, zstd, lz4, snappy compressor를
additive feature flag 뒤에 제공합니다. Default feature set은 비어 있으므로
필요한 algorithm feature를 명시적으로 선택합니다. `features = ["all"]`은
benchmark 또는 local comparison 용도로만 사용합니다. `zstd` feature는 native
`zstd-sys` build path를 사용합니다. Decode helper는 기본 64 MiB
decompressed-size safety limit를 적용하며, caller는 더 작은 limit를 지정하거나
trusted input에서 명시적으로 해제할 수 있습니다. Stream helper는 `Read`에서
`Write`로 복사하고 encoder를 finish한 뒤 반환합니다. 직접 stream 소유권이
필요하면 `compression_writer` / `decompression_reader` constructor를 사용할 수
있습니다. `lz4`와 snappy one-shot helper는 block/raw payload format을 유지하고,
stream helper는 framed stream format을 사용하므로 matching stream API로
round-trip해야 합니다.

```rust
use bluetape_rs_compression::{CompressionAlgorithm, CompressionConfig};

let algorithm = CompressionAlgorithm::Gzip;
let compressed = algorithm
    .compress_with_config(
        b"{\"service\":\"blue\"}",
        CompressionConfig::new().with_level(bluetape_rs_compression::CompressionLevel::Fast),
    )
    .expect("compress");
let restored = algorithm.decompress(&compressed).expect("decompress");

assert_eq!(restored, br#"{"service":"blue"}"#);
```

```rust
use bluetape_rs_compression::{CompressionAlgorithm, CompressionConfig};

let algorithm = CompressionAlgorithm::Gzip;
let mut compressed = Vec::new();
algorithm
    .compress_stream(
        b"{\"service\":\"blue\"}".as_slice(),
        &mut compressed,
        CompressionConfig::new(),
    )
    .expect("compress stream");

let mut restored = Vec::new();
algorithm
    .decompress_stream(
        &compressed[..],
        &mut restored,
        CompressionConfig::new().with_max_decompressed_size(1024),
    )
    .expect("decompress stream");

assert_eq!(restored, br#"{"service":"blue"}"#);
```

동일 조건 benchmark 결과는 공유 JSON, text, binary, random payload fixture로
`bluetape-rs`, `bluetape-go`, `bluetape4k-io`를 비교합니다. 이 결과는 local
same-condition snapshot이며, production ranking 또는 regression threshold가
아닙니다.

![Compression throughput](docs/images/readme-charts/compression-throughput-large-payloads.png)

![Compression ratio](docs/images/readme-charts/compression-ratio-large-payloads.png)

Raw CSV, 실행 command, large-payload 비교표, 전체 compress/decompress matrix는
[compression benchmark report](docs/benchmark/compression-same-condition-benchmark.md)와
[benchmark metadata](docs/benchmark/compression-same-condition-metadata.md)를
참고하세요.

## 개발

```bash
cargo fmt --all
cargo test --workspace
cargo test --workspace --all-features
cargo clippy --workspace --all-targets --all-features -- -D warnings
```

## 프로젝트 관리

- [Current WIP](WIP.md)
- [Release guide](docs/release/release-guide.md)
- [Research index](docs/research/README.md)
- [Backend library feasibility](docs/research/2026-06-08-backend-library-feasibility.md)

## 프로젝트 원칙

- Rust에 자연스러운 API를 우선합니다.
- Kotlin/JVM 또는 Go API를 기계적으로 옮기지 않습니다.
- catch-all utility package보다 목적이 분명한 작은 crate를 선호합니다.
- 인프라 지원을 주장하기 전에 실제 container-backed smoke test를 추가합니다.
- 공개 문서는 영어를 기본으로 하고 Korean README parity를 유지합니다.
