decide-core

Crates.iodecide-core
lib.rsdecide-core
version0.1.1
created_at2025-06-25 20:48:59.315857+00
updated_at2025-06-30 06:45:22.733946+00
descriptionA fast, lightweight permission engine for Rust and TypeScript with condition support.
homepagehttps://github.com/aether-flux/decide
repositoryhttps://github.com/aether-flux/decide
max_upload_size
id1726386
size18,943
Amartya Chowdhury (aether-flux)

documentation

https://github.com/aether-flux/decide#readme

README

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)
  • 🧱 Define your roles + permissions in JSON
  • 📦 Also powers a JS/TS SDK via NAPI — cross-platform ready

Table of Contents

Installation

Add to your Cargo.toml:

[dependencies]
decide-core = "0.1.1"

Quickstart

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)

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, 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:

match decide.can(&user, "edit_post", &resource) {
    Ok(true) => println!("Access granted"),
    Ok(false) => println!("Access denied"),
    Err(e) => eprintln!("Error: {}", e),
}

Structs

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

Commit count: 0

cargo fmt