Add rBroker + wBroker pool, BrokerPayload, NamasteCore trait stub
- src/brokers/: pool manager, r_broker (rec.read), w_broker (rec.write), BrokerPayload struct, BrokerError type - src/core/: NamasteCore trait — fetch/write/update/delete interface, stubs - IPL step 6: spawns rBroker + wBroker pools after exchange declaration - tests/broker_pool_test.rs: integration tests for pool spawn (skip if broker down) - BrokerPayload unit tests + doctest in payload.rs - Added futures-lite, serde_json to Cargo.toml - README.md, CLAUDE.md, wiki updated to reflect new structure and status Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
108
src/brokers/payload.rs
Normal file
108
src/brokers/payload.rs
Normal file
@@ -0,0 +1,108 @@
|
||||
//! # brokers/payload.rs — AMQP Message Payload
|
||||
//!
|
||||
//! Defines the JSON body structure carried in all BEDS broker messages.
|
||||
//! The AMQP envelope handles routing (type header, reply_to, correlation_id);
|
||||
//! this struct is what lives in the message body.
|
||||
//!
|
||||
//! ## Wire Format
|
||||
//!
|
||||
//! ```json
|
||||
//! {
|
||||
//! "template": "usr",
|
||||
//! "data": { "first_name": "joe", "status": "active" }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! `template` names the data object (maps to a NamasteCore implementor).
|
||||
//! `data` carries key/value pairs in user-facing field names — the template
|
||||
//! maps these to actual schema names. Callers never specify primary keys on
|
||||
//! writes; the template generates a GUID and returns it in the reply.
|
||||
//!
|
||||
//! ## Calling Agents
|
||||
//! - `brokers::r_broker` — parsed from message body on fetch events
|
||||
//! - `brokers::w_broker` — parsed from message body on write/update/delete events
|
||||
//!
|
||||
//! **Author:** mks
|
||||
//! **Version:** 1.0
|
||||
//!
|
||||
//! ## History
|
||||
//! * `2026-04-05` - mks - original coding
|
||||
|
||||
use std::collections::HashMap;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
|
||||
/// The JSON body of every BEDS broker message.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use rustybeds::brokers::payload::BrokerPayload;
|
||||
///
|
||||
/// let json = r#"{"template":"usr","data":{"first_name":"joe"}}"#;
|
||||
/// let payload: BrokerPayload = serde_json::from_str(json).unwrap();
|
||||
/// assert_eq!(payload.template, "usr");
|
||||
/// assert!(payload.data.contains_key("first_name"));
|
||||
/// ```
|
||||
///
|
||||
/// Both read and write brokers parse this struct from the raw AMQP delivery
|
||||
/// bytes. The operation type is carried in the AMQP `type` message property —
|
||||
/// this struct carries the object identity and data payload.
|
||||
///
|
||||
/// # History
|
||||
///
|
||||
/// * `2026-04-05` - mks - original coding
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct BrokerPayload {
|
||||
/// Template identifier — names the NamasteCore implementor to dispatch to.
|
||||
/// Matches the TLA convention from the template file (e.g. `"usr"`, `"pst"`).
|
||||
pub template: String,
|
||||
|
||||
/// Key/value data pairs in user-facing field names.
|
||||
/// For writes: the record to store (pkey excluded — generated by template).
|
||||
/// For reads: query discriminants (field → value to match).
|
||||
/// For deletes: discriminants identifying the record(s) to remove.
|
||||
#[serde(default)]
|
||||
pub data: HashMap<String, Value>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn deserializes_full_payload() {
|
||||
let json = r#"{"template":"usr","data":{"first_name":"joe","status":"active"}}"#;
|
||||
let payload: BrokerPayload = serde_json::from_str(json).unwrap();
|
||||
assert_eq!(payload.template, "usr");
|
||||
assert_eq!(payload.data.len(), 2);
|
||||
assert_eq!(payload.data["first_name"], "joe");
|
||||
assert_eq!(payload.data["status"], "active");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserializes_without_data_field() {
|
||||
// data is optional — fetch by template name alone is valid
|
||||
let json = r#"{"template":"usr"}"#;
|
||||
let payload: BrokerPayload = serde_json::from_str(json).unwrap();
|
||||
assert_eq!(payload.template, "usr");
|
||||
assert!(payload.data.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serializes_round_trip() {
|
||||
let json = r#"{"template":"pst","data":{"title":"hello"}}"#;
|
||||
let payload: BrokerPayload = serde_json::from_str(json).unwrap();
|
||||
let serialized = serde_json::to_string(&payload).unwrap();
|
||||
let round_trip: BrokerPayload = serde_json::from_str(&serialized).unwrap();
|
||||
assert_eq!(round_trip.template, payload.template);
|
||||
assert_eq!(round_trip.data["title"], payload.data["title"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_missing_template() {
|
||||
let json = r#"{"data":{"first_name":"joe"}}"#;
|
||||
let result: Result<BrokerPayload, _> = serde_json::from_str(json);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user