Skip to main content

pjson_rs_domain/
lib.rs

1//! PJS Domain Layer - Pure Business Logic
2//!
3//! This crate contains the pure domain logic for PJS (Priority JSON Streaming Protocol)
4//! with ZERO external dependencies (except thiserror for error handling).
5//!
6//! The domain layer is WASM-compatible and can be used in both native and
7//! WebAssembly environments.
8//!
9//! ## Architecture
10//!
11//! Following Clean Architecture principles:
12//! - **Value Objects**: Immutable, validated domain concepts (Priority, JsonPath, etc.)
13//! - **Entities**: Domain objects with identity (Frame, Stream)
14//! - **Domain Events**: State change notifications
15//!
16//! ## Features
17//!
18//! - `std` (default): Standard library support
19//! - `serde`: Serialization support for WASM interop
20//! - `wasm`: Enables WASM-specific optimizations
21
22#![cfg_attr(not(feature = "std"), no_std)]
23#![warn(missing_docs)]
24
25#[cfg(not(feature = "std"))]
26extern crate alloc;
27
28#[cfg(not(feature = "std"))]
29use alloc::{format, string::String, vec::Vec};
30
31pub mod entities;
32pub mod events;
33pub mod services;
34pub mod value_objects;
35
36// Re-export core types
37pub use entities::{Frame, Stream};
38pub use events::{DomainEvent, SessionState};
39pub use services::{PriorityHeuristicConfig, compute_priority};
40pub use value_objects::{JsonData, JsonPath, Priority, Schema, SessionId, StreamId};
41
42/// Domain Result type
43pub type DomainResult<T> = Result<T, DomainError>;
44
45/// Domain-specific errors
46///
47/// All domain errors are value types with no external dependencies.
48/// Uses thiserror for ergonomic error handling.
49#[derive(Debug, thiserror::Error)]
50#[non_exhaustive]
51pub enum DomainError {
52    /// Invalid state transition attempted
53    #[error("Invalid state transition: {0}")]
54    InvalidStateTransition(String),
55
56    /// Invalid stream state
57    #[error("Invalid stream state: {0}")]
58    InvalidStreamState(String),
59
60    /// Invalid session state
61    #[error("Invalid session state: {0}")]
62    InvalidSessionState(String),
63
64    /// Invalid frame structure or content
65    #[error("Invalid frame: {0}")]
66    InvalidFrame(String),
67
68    /// Stream invariant violation
69    #[error("Stream invariant violation: {0}")]
70    InvariantViolation(String),
71
72    /// Invalid priority value (must be 1-255)
73    #[error("Invalid priority value: {0}")]
74    InvalidPriority(String),
75
76    /// Invalid JSON path format
77    #[error("Invalid JSON path: {0}")]
78    InvalidPath(String),
79
80    /// Session not found
81    #[error("Session not found: {0}")]
82    SessionNotFound(String),
83
84    /// Stream not found
85    #[error("Stream not found: {0}")]
86    StreamNotFound(String),
87
88    /// Too many concurrent streams
89    #[error("Too many streams: {0}")]
90    TooManyStreams(String),
91
92    /// General domain logic error
93    #[error("Domain logic error: {0}")]
94    Logic(String),
95
96    /// I/O operation failed
97    #[error("I/O error: {0}")]
98    Io(String),
99
100    /// Resource not found
101    #[error("Resource not found: {0}")]
102    NotFound(String),
103
104    /// Concurrency conflict detected
105    #[error("Concurrency conflict: {0}")]
106    ConcurrencyConflict(String),
107
108    /// Compression operation failed
109    #[error("Compression error: {0}")]
110    CompressionError(String),
111
112    /// Validation failed
113    #[error("Validation error: {0}")]
114    ValidationError(String),
115
116    /// Invalid input provided
117    #[error("Invalid input: {0}")]
118    InvalidInput(String),
119
120    /// Internal error (should not happen)
121    #[error("Internal error: {0}")]
122    InternalError(String),
123
124    /// Security policy violation
125    #[error("Security violation: {0}")]
126    SecurityViolation(String),
127
128    /// Resource exhausted (memory, connections, etc.)
129    #[error("Resource exhausted: {0}")]
130    ResourceExhausted(String),
131}
132
133impl DomainError {
134    /// Create an invariant violation error
135    pub fn invariant_violation(message: impl Into<String>) -> Self {
136        Self::InvariantViolation(message.into())
137    }
138
139    /// Create an invalid state transition error
140    pub fn invalid_transition(from: &str, to: &str) -> Self {
141        Self::InvalidStateTransition(format!("{from} -> {to}"))
142    }
143}
144
145impl From<String> for DomainError {
146    fn from(error: String) -> Self {
147        Self::Logic(error)
148    }
149}
150
151#[cfg(test)]
152mod tests {
153    use super::*;
154
155    #[test]
156    fn test_domain_error_creation() {
157        let err = DomainError::invariant_violation("test");
158        assert!(matches!(err, DomainError::InvariantViolation(_)));
159
160        let err = DomainError::invalid_transition("StateA", "StateB");
161        assert!(matches!(err, DomainError::InvalidStateTransition(_)));
162    }
163
164    #[test]
165    fn test_domain_result() {
166        let result: DomainResult<u32> = Ok(42);
167        assert!(result.is_ok());
168
169        let result: DomainResult<u32> = Err(DomainError::Logic("test".to_string()));
170        assert!(result.is_err());
171    }
172}