cape 0.0.2

🦸 An unintrusive ORM for Rust
Documentation
# 🦸 Cape  

> **⚠️ Work in Progress**: This project is currently under active development and not ready for production use.

**Cape** is an unintrusive and lightweight wrapper ORM for Rust.

[![Build Status]][actions] [![Docs]][docs.rs] [![Latest Version]][crates.io]

[Docs]: https://img.shields.io/docsrs/cape?style=for-the-badge&logo=docsdotrs&color=informational
[docs.rs]: https://docs.rs/cape
[Build Status]: https://img.shields.io/github/actions/workflow/status/mcaneris/cape/ci.yml?branch=main&style=for-the-badge&logo=github
[actions]: https://github.com/mcaneris/cape/actions
[Latest Version]: https://img.shields.io/crates/v/cape?style=for-the-badge&logo=rust&color=blueviolet
[crates.io]: https://crates.io/crates/cape

---

## ✨ Why Cape?

Most ORMs force core domain structs to mirror database schemas — IDs, foreign keys, and boilerplate that sullies the core domain model.

Cape flips this around by wrapping the domain model, just like a superhero’s cape. Core types stay clean and focused, while persistence details live in the wrapper.

---

## 🧩 Core Concepts

Cape centers on a single generic type:

```rust
Record<T, R, S, K>
```

* `T`**inner entity** (core struct)
* `R`**relations** (another `Record`, a tuple, or a `Vec<Record>`)
* `S`**persistence state** (`N`, `S`)
* `K`**key type** (`i64`, `Uuid`, etc.)

### Example

```rust
use cape::prelude::*;
use uuid::Uuid;

#[derive(Debug, Clone)]
pub struct User {
    pub username: String,
    pub email: String,
}

#[derive(Debug, Clone)]
pub struct Post {
    pub title: String,
    pub body: String,
}

fn main() {
    // New user, not yet persisted
    let new_user: Record<User, (), N, Uuid> = Record::new(User {
        username: "alice".into(),
        email: "alice@example.com".into(),
    });

    assert!(new_user.is_dirty(), true);

    // Stored user, persisted with a UUID
    let stored_user = new_user.store(Uuid::new_v4(), chrono::Utc::now().naive_utc());

    // New post with a stored author relation
    let new_post: Record<Post, Record<User, (), S, Uuid>, N, i64> =
        Record::with_relations(
            Post { title: "Hello".into(), body: "Cape world!" },
            stored_user,
        );

    // Move post into stored state
    let stored_post = new_post.store(42, chrono::Utc::now().naive_utc());

    assert!(new_user.is_dirty(), false);

    println!("Post '{}' by {}", 
        stored_post.inner.title,
        stored_post.relations.inner.username
    );
}
```

---

## 📜 License

<sup>
  Licensed under either of <a href="LICENSE-APACHE">Apache License, Version 2.0</a> or <a href="LICENSE-MIT">MIT license</a> at your option.
</sup>

<br>

<sub>
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
</sub>