axor

Crates.ioaxor
lib.rsaxor
version0.1.2
created_at2025-06-11 13:37:54.371035+00
updated_at2025-06-13 14:56:23.258201+00
descriptionCore of the Axor framework: agent-based business logic with typed operations and injection.
homepage
repositoryhttps://github.com/kinai-io/axor
max_upload_size
id1708598
size25,790
(riana)

documentation

README

Axor

Axor is a zero-overhead, modular backend framework based on agents.

It helps you structure your backend logic into isolated, injectable components — called agents — that expose typed operations and can be invoked either directly or dynamically via RPC.

“Give me six hours to chop down a tree and I will spend the first four sharpening the axe.”
— Abraham Lincoln

Axor is your sharpened axe: focus on business logic, not boilerplate.

✅ Clean design
🧪 No ceremony
🚀 Zero runtime cost when calling directly
🌐 RPC-style invocation when needed
🔌 Inject and share any service, with no trait or macro required

Agents are exposed to the runtime. Services, in contrast, can be injected and shared without being registered as public operations — keeping your context clean and focused.


✨ Features

  • Strongly-typed agents with auto-injection
  • 🔌 Service/Agent DI via Inject<T>
  • ⚙️ Operations with #[operation], exposed automatically
  • 🧪 Business logic is testable directly (no runtime required)
  • 🚀 Zero overhead in direct mode, thanks to static dispatch
  • 🌐 Optional RPC-style interface via Payload

Use axor::prelude::* for a complete, developer-friendly import.


📦 Installation

[dependencies]
axor = "0.1"
axor-macros = "0.1" # Required for attribute macros

⚠️ You must declare axor-macros in your Cargo.toml, but you don't need to import it in your source code — just use the prelude:

use axor::prelude::*;

🛠️ Basic usage

1. Define a service and an agent

use std::sync::Arc;
use axor::prelude::*;

trait Logger: Send + Sync {
    fn log(&self, message: &str);
}

struct ConsoleLogger;
impl Logger for ConsoleLogger {
    fn log(&self, message: &str) {
        println!("[LOG] {}", message);
    }
}

#[agent]
struct HelloAgent {
    logger: Inject<Arc<dyn Logger>>,
}

#[agent_impl]
impl HelloAgent {
    #[operation]
    fn hello(&self) -> &'static str {
        self.logger.resolve().log("Saying hello");
        "Hello, world!"
    }
}

2. Register and run

let mut context = AxorContext::new();
context.register(HelloAgent::default());
context.register_service::<Arc<dyn Logger>>(Arc::new(ConsoleLogger));
context.init();

// Direct call (zero overhead)
let agent = context.resolve::<HelloAgent>();
assert_eq!(agent.hello(), "Hello, world!");

// RPC-style used by web, cli and tauri runtimes
let payload = Payload::new("HelloAgent.hello");
let result = context.invoke(payload);
assert!(result.success);

📜 Manifest support

Introspect all registered agents and operations:

let manifest = context.manifest();
println!("{}", serde_json::to_string_pretty(&manifest).unwrap());

⚠️ Limitations

❌ No reflection

Rust doesn’t provide reflection. You must:

  • Add #[agent] to your struct
  • Add #[agent_impl] to your impl block
  • Use #[operation] to expose methods

🎯 Operation constraints

Each operation may take zero or one input, which must:

  • Be DeserializeOwned
  • Not be a reference (e.g., use String, not &str)

Return values must be Serialize, but they are optional.

🔄 Runtime cost only when using Payload

In direct mode, everything is statically dispatched and compiled away. RPC-style invocation uses serde_json::Value, which involves serialization overhead — by design.


🧭 Roadmap

  • Core agent system and macros
  • Dependency injection with Inject<T>
  • Operation exposure
  • Manifest generation
  • Type metadata for operations
  • axor-web (RPC over HTTP)
  • axor-cli (invoke agents from CLI)
  • axor-tauri (bindings for desktop apps)
  • TypeScript client generator
  • axor-doc for endpoint documentation

🔗 License

MIT © 2025 — Made with ❤️ in Rust

Commit count: 20

cargo fmt