grorm 0.1.1

Goroutine-native ORM for Rust with multi-database support
Documentation
# grorm

**GRoutines + ORM** — A goroutine-native async ORM for Rust with multi-database support.

[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)

## Features

- **Multi-database**: PostgreSQL, MySQL, SQLite
- **Goroutine-native**: Built on [gorust]https://crates.io/crates/gorust, no tokio required
- **Chainable API**: `where_eq().limit().offset().order().find()`
- **Transactions**: `Transaction::begin()` with auto-rollback on drop
- **Auto table creation**: `create_table()` generates DDL from model definitions
- **Index & constraints**: `#[index]`, `#[unique]`, `#[unique_index = "name"]`
- **JOIN support**: `left_join()`, `inner_join()`, `right_join()`
- **IN queries**: `where_in("name", vec![...])`
- **Connection pooling**: gorust channel-based connection pool
- **Derive macros**: `#[derive(DeriveModel)]` auto-generates `Model` trait

## Quick Start

Add to your `Cargo.toml`:

```toml
[dependencies]
grorm = "0.1.0"
gorust = "1.5"
```

### SQLite Example

```rust
use grorm::{ConnectionConfig, ConnectionPool, SqliteDriverFactory, QueryBuilder, Value, Error};
use grorm::DeriveModel;
use gorust::runtime;

#[derive(Debug, DeriveModel)]
#[table = "users"]
struct User {
    id: i64,
    #[index]
    name: String,
    #[unique]
    email: String,
    age: i32,
}

#[runtime]
fn main() -> Result<(), Error> {
    let config = ConnectionConfig::sqlite("test.db");
    let pool = ConnectionPool::new(SqliteDriverFactory, config, 4);
    let mut conn = pool.get()?;

    let mut qb = QueryBuilder::<User>::new(conn.driver_mut());

    qb.create_table()?;

    let user = User { id: 0, name: "Alice".into(), email: "alice@x.com".into(), age: 30 };
    qb.insert(&user)?;

    let users = qb.where_eq("name", Value::from("Alice")).find()?;
    println!("{:?}", users);

    Ok(())
}
```

### PostgreSQL Example

```rust
use grorm::{ConnectionConfig, ConnectionPool, PostgresDriverFactory, QueryBuilder, Value, Error};
use grorm::DeriveModel;
use gorust::runtime;

#[derive(Debug, DeriveModel)]
#[table = "users"]
struct User {
    id: i64,
    name: String,
    email: String,
    age: i32,
}

#[runtime]
fn main() -> Result<(), Error> {
    let config = ConnectionConfig::postgres("localhost", 5432, "mydb", "user", "pass");
    let pool = ConnectionPool::new(PostgresDriverFactory, config, 4);
    let mut conn = pool.get()?;

    let mut qb = QueryBuilder::<User>::new(conn.driver_mut());
    qb.create_table()?;

    qb.insert(&User { id: 0, name: "Alice".into(), email: "alice@x.com".into(), age: 30 })?;

    let users = qb.find_all()?;
    println!("{:?}", users);

    Ok(())
}
```

### MySQL Example

```rust
use grorm::{ConnectionConfig, ConnectionPool, MysqlDriverFactory, QueryBuilder, Value, Error};
use grorm::DeriveModel;
use gorust::runtime;

#[derive(Debug, DeriveModel)]
#[table = "users"]
struct User {
    id: i64,
    name: String,
    email: String,
    age: i32,
}

#[runtime]
fn main() -> Result<(), Error> {
    let config = ConnectionConfig::mysql("localhost", 3306, "mydb", "user", "pass");
    let pool = ConnectionPool::new(MysqlDriverFactory, config, 4);
    let mut conn = pool.get()?;

    let mut qb = QueryBuilder::<User>::new(conn.driver_mut());
    qb.create_table()?;

    qb.insert(&User { id: 0, name: "Alice".into(), email: "alice@x.com".into(), age: 30 })?;

    let users = qb.find_all()?;
    println!("{:?}", users);

    Ok(())
}
```

## Model Definition

Use `#[derive(DeriveModel)]` to define your model:

```rust
use grorm::DeriveModel;

#[derive(Debug, DeriveModel)]
#[table = "users"]                    // Override table name (default: snake_case + "s")
#[primary_key = "uuid"]               // Override primary key (default: "id")
struct User {
    id: i64,                          // Auto-increment primary key (when id = 0)
    #[index]                          // Create a regular index
    name: String,
    #[unique]                         // Create a unique constraint
    email: String,
    #[unique_index = "uq_name_age"]   // Composite unique index (same group name)
    first_name: String,
    #[unique_index = "uq_name_age"]
    last_name: String,
    age: i32,
}
```

### Available Attributes

| Attribute | Scope | Description |
|-----------|-------|-------------|
| `#[table = "name"]` | struct | Override table name |
| `#[primary_key = "col"]` | struct | Override primary key column |
| `#[index]` | field | Create a regular index |
| `#[unique]` | field | Create a unique constraint |
| `#[unique_index = "name"]` | field | Group into composite unique index |

## CRUD Operations

### Create Table

```rust
let mut qb = QueryBuilder::<User>::new(conn.driver_mut());
qb.create_table()?;
```

### Insert

```rust
let user = User { id: 0, name: "Alice".into(), email: "alice@x.com".into(), age: 30 };
let id = qb.insert(&user)?;  // Returns Option<i64> (auto-generated id)
```

### Query

```rust
// Find all
let all = qb.find_all()?;

// Find by id
let user = qb.find_by_id(1)?;

// Find one with conditions
let user = qb.where_eq("age", Value::from(30)).find_one()?;

// Find with conditions
let users = qb.where_eq("age", Value::from(30)).find()?;

// Find with column name
let users = qb.find_where("name", Value::from("Alice"))?;

// Find with model (non-zero fields become conditions)
let filter = User { id: 0, name: "".into(), email: "".into(), age: 30 };
let users = qb.where_model(&filter).find()?;

// IN query
let users = qb.where_in("name", vec![Value::from("Alice"), Value::from("Bob")]).find()?;

// Pagination
let users = qb.order("age", true).limit(10).offset(0).find()?;

// Count
let total = qb.count()?;
```

### Update

```rust
// Update single column
let rows = qb.where_eq("name", Value::from("Alice"))
    .update_one("age", Value::from(31))?;

// Update from model (non-zero/non-empty fields)
let update = User { id: 0, name: "".into(), email: "".into(), age: 31 };
let rows = qb.where_eq("name", Value::from("Alice"))
    .update_model(&update)?;
```

### Delete

```rust
// Delete with conditions
let rows = qb.where_eq("name", Value::from("Alice")).delete()?;

// Delete all
let rows = qb.delete_all()?;
```

## Transactions

```rust
let mut tx = Transaction::<User>::begin(conn.driver_mut())?;

tx.insert(&User { id: 0, name: "Alice".into(), email: "alice@x.com".into(), age: 30 })?;
tx.where_eq("name", Value::from("Bob")).update_one("age", Value::from(26))?;

tx.commit()?;
// If tx goes out of scope without commit, it auto-rolls back
```

All `QueryBuilder` methods are available on `Transaction`:
- `insert`, `find`, `find_all`, `find_one`, `find_by_id`, `find_where`
- `where_eq`, `where_in`, `where_model`
- `update_one`, `update_model`
- `delete`, `delete_all`
- `count`, `limit`, `offset`, `order`

## JOIN Support

```rust
let mut qb = QueryBuilder::<User>::new(conn.driver_mut());

qb.left_join("orders", "users.id = orders.user_id")
  .inner_join("profiles", "users.id = profiles.user_id")
  .right_join("scores", "users.id = scores.user_id");

let results = qb.find()?;
```

## Connection Pool

```rust
// Create pool
let config = ConnectionConfig::sqlite("test.db");
let pool = ConnectionPool::new(SqliteDriverFactory, config, 4);

// Get connection (blocks if pool exhausted)
let mut conn = pool.get()?;

// Use driver directly
let mut qb = QueryBuilder::<User>::new(conn.driver_mut());
```

## Error Handling

All public APIs return `Result<T, grorm::Error>`. The `Error` enum covers:

| Variant | Description |
|---------|-------------|
| `Connection` | Auth, network errors |
| `Query` | SQL syntax, constraint violations |
| `Execute` | Write operation errors |
| `Protocol` | Wire format, parsing errors |
| `Model` | Serialization/deserialization errors |
| `Pool` | Pool exhausted, closed |
| `Config` | Invalid DSN, configuration |
| `Io` | Wrapped I/O errors |
| `NotFound` | Entity not found |
| `Transaction` | Begin, commit, rollback errors |

## Project Structure

```
grorm/
├── Cargo.toml
├── README.md
├── src/
│   ├── lib.rs              # Library root, re-exports
│   ├── error.rs            # Unified error types
│   ├── driver/             # Database driver abstraction
│   │   ├── mod.rs          # ConnectionConfig, DatabaseDriver trait
│   │   ├── postgres.rs     # PostgreSQL driver
│   │   ├── mysql.rs        # MySQL driver
│   │   └── sqlite.rs       # SQLite driver
│   ├── protocol/           # Database wire protocols
│   │   ├── mod.rs
│   │   ├── pg.rs           # PostgreSQL protocol
│   │   ├── myproto.rs      # MySQL protocol
│   │   └── sqlite_proto.rs # SQLite protocol (mock)
│   ├── query/              # Low-level SQL builders
│   │   ├── mod.rs
│   │   ├── select.rs
│   │   ├── insert.rs
│   │   ├── update.rs
│   │   └── delete.rs
│   ├── types/              # Type mapping (Rust ↔ SQL)
│   │   ├── mod.rs
│   │   ├── value.rs        # Value enum
│   │   ├── from_sql.rs     # FromSql trait
│   │   └── to_sql.rs       # ToSql trait
│   ├── orm/                # ORM core
│   │   ├── mod.rs
│   │   ├── model.rs        # Model trait, ColumnInfo
│   │   ├── query.rs        # QueryBuilder (chainable API)
│   │   └── transaction.rs  # Transaction support
│   └── pool/               # Connection pool (gorust channels)
│       └── mod.rs
├── grorm-macros/           # Procedural macros
│   ├── Cargo.toml
│   └── src/
│       └── lib.rs          # #[derive(DeriveModel)]
└── examples/
    ├── sqlite_demo.rs
    ├── postgres_demo.rs
    └── mysql_demo.rs
```

## License

MIT