# Decide
---
**A tiny and fast permission engine for Rust.**
Define roles and permissions with simple logic like `user_id == resource_owner`. Done.
## Features
- 🦀 Written in pure Rust (zero dependencies for the core logic)
- 💡 Declarative permission checks using embedded expressions (via [Rhai](https://rhai.rs))
- 🧱 Define your roles + permissions in JSON
- 📦 Also powers a JS/TS SDK via NAPI — cross-platform ready
## Table of Contents
- [Installation](#installation)
- [Quickstart](#quickstart)
- [File-Based Config (Optional, but Recommended)](#file-based-config-(optional,-but-recommended))
- [Expressions](#expressions)
- [Error Handling](#error-handling)
- [Structs](#structs)
## Installation
Add to your `Cargo.toml`:
```toml
[dependencies]
decide-core = "0.1.1"
```
## Quickstart
```rust
use decide_core::types::{User, Resource, RoleMap};
use decide_core::evaluator::Decide;
fn main() {
let roles_json = r#"
{
"editor": {
"name": "editor",
"permissions": [
{ "action": "edit_post", "condition": "user_id == resource_owner" },
{ "action": "like_post", "condition": null }
]
}
}
"#;
let user_json = r#"
{
"id": "123",
"roles": ["editor"]
}
"#;
let resource_json = r#"
{
"owner_id": "123",
"resource_name": "hello-world",
"resource_type": "post"
}
"#;
let role_map: RoleMap = serde_json::from_str(roles_json).unwrap();
let user: User = serde_json::from_str(user_json).unwrap();
let resource: Resource = serde_json::from_str(resource_json).unwrap();
let decide = Decide::new(role_map);
let allowed = decide.can(&user, "edit_post", &resource).unwrap();
println!("Can edit? {}", allowed); // true
}
```
## File-Based Config (Optional, but Recommended)
```rust
let decide = Decide::from_file("config_file.json").unwrap();
// or use default "decide.config.json" from root:
let decide = Decide::default().unwrap();
```
## Expressions
Conditions are written in a simple expression format using [Rhai](https://rhai.rs), a safe and embeddable scripting language.
Available fields:
- `user_id` => `user.id`
- `user_roles` => `user.roles`
- `resource_owner` => `resource.owner_id`
- `resource_name` => `resource.resource_name`
- `resource_type` => `resource.resource_type`
You can write things like:
```
condition: 'user_id == resource_owner'
condition: 'user_roles.contains("admin")'
condition: 'user_roles.contains("moderator") || user_id == resource_owner'
```
## Error Handling
`Decide::can()` returns a `Result<bool, DecideError>`. So you can check and handle errors as follows:
```rust
match decide.can(&user, "edit_post", &resource) {
Ok(true) => println!("Access granted"),
Ok(false) => println!("Access denied"),
Err(e) => eprintln!("Error: {}", e),
}
```
## Structs
```rust
pub struct User {
pub id: String,
pub roles: Vec<String>,
}
pub struct Resource {
pub owner_id: String,
pub resource_name: String,
pub resource_type: String,
}
pub struct Permission {
pub action: String,
pub condition: Option<String>,
}
pub struct Role {
pub name: String,
pub permissions: Vec<Permission>,
}
pub type RoleMap = HashMap<String, Role>;
```
## License
MIT