allora_core/service.rs
1//! ServiceActivator trait: metadata & channel binding intent for a service (activator).
2//! Service trait: async-only business logic component.
3//!
4//! # Example
5//! ```rust
6//! use allora_core::{error::Result, service::Service, Exchange, Message};
7//! #[derive(Debug)] struct Echo;
8//! impl Echo { pub fn new() -> Self { Self } }
9//! #[async_trait::async_trait]
10//! impl Service for Echo {
11//! async fn process(&self, exchange: &mut Exchange) -> Result<()> {
12//! if let Some(b) = exchange.in_msg.body_text() { exchange.out_msg = Some(Message::from_text(format!("echo:{b}"))); }
13//! Ok(())
14//! }
15//! }
16//! let svc = Echo::new();
17//! let mut ex = Exchange::new(Message::from_text("hi"));
18//! tokio::runtime::Runtime::new().unwrap().block_on(async { svc.process(&mut ex).await.unwrap(); });
19//! assert_eq!(ex.out_msg.unwrap().body_text(), Some("echo:hi"));
20//! ```
21//!
22//! # Metadata Accessors
23//! * `id()` – stable identifier (auto-generated or user supplied).
24//! * `from()` / `to()` – declared channel ids for wiring.
25//! * `ref_name()` – reference name matching YAML `ref-name` and the `#[service(name=..)]` macro argument (not a file path).
26//!
27//! # Implementing
28//! ```ignore
29//! use allora::{service::Service, Exchange, error::Result};
30//! struct UppercaseService;
31//! impl UppercaseService { pub fn new() -> Self { Self } }
32//! #[cfg_attr(feature = "async", async_trait::async_trait)]
33//! impl Service for UppercaseService {
34//! async fn process(&self, exchange: &mut Exchange) -> Result<()> {
35//! if let Some(body) = exchange.in_msg.body_text() { exchange.in_msg.set_body_text(body.to_uppercase()); }
36//! Ok(())
37//! }
38//! }
39//! ```
40//! (Single impl with conditional methods; avoids duplication.)
41//!
42//! # Future Extensions
43//! * Error classification (e.g. routing vs transformation).
44//! * Lifecycle hooks (`init`, `shutdown`).
45//! * Metrics instrumentation via feature flag.
46//!
47//! This file intentionally provides only the trait; concrete implementations are user-defined
48//! or generated by a future service builder.
49
50use crate::{error::Result, Exchange};
51use std::fmt::Debug;
52
53/// Internal metadata describing how a service is wired. Users do NOT implement this directly; it is derived from specs.
54pub trait ServiceActivator: Send + Sync + Debug {
55 fn id(&self) -> &str;
56 fn from(&self) -> &str;
57 fn to(&self) -> &str;
58 fn ref_name(&self) -> &str;
59}
60
61#[async_trait::async_trait]
62pub trait Service: Send + Sync + Debug {
63 async fn process(&self, exchange: &mut Exchange) -> Result<()>;
64}