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 value_objects;
34
35// Re-export core types
36pub use entities::{Frame, Stream};
37pub use events::{DomainEvent, SessionState};
38pub use value_objects::{JsonData, JsonPath, Priority, Schema, SessionId, StreamId};
39
40/// Domain Result type
41pub type DomainResult<T> = Result<T, DomainError>;
42
43/// Domain-specific errors
44///
45/// All domain errors are value types with no external dependencies.
46/// Uses thiserror for ergonomic error handling.
47#[derive(Debug, thiserror::Error)]
48#[non_exhaustive]
49pub enum DomainError {
50    /// Invalid state transition attempted
51    #[error("Invalid state transition: {0}")]
52    InvalidStateTransition(String),
53
54    /// Invalid stream state
55    #[error("Invalid stream state: {0}")]
56    InvalidStreamState(String),
57
58    /// Invalid session state
59    #[error("Invalid session state: {0}")]
60    InvalidSessionState(String),
61
62    /// Invalid frame structure or content
63    #[error("Invalid frame: {0}")]
64    InvalidFrame(String),
65
66    /// Stream invariant violation
67    #[error("Stream invariant violation: {0}")]
68    InvariantViolation(String),
69
70    /// Invalid priority value (must be 1-255)
71    #[error("Invalid priority value: {0}")]
72    InvalidPriority(String),
73
74    /// Invalid JSON path format
75    #[error("Invalid JSON path: {0}")]
76    InvalidPath(String),
77
78    /// Session not found
79    #[error("Session not found: {0}")]
80    SessionNotFound(String),
81
82    /// Stream not found
83    #[error("Stream not found: {0}")]
84    StreamNotFound(String),
85
86    /// Too many concurrent streams
87    #[error("Too many streams: {0}")]
88    TooManyStreams(String),
89
90    /// General domain logic error
91    #[error("Domain logic error: {0}")]
92    Logic(String),
93
94    /// I/O operation failed
95    #[error("I/O error: {0}")]
96    Io(String),
97
98    /// Resource not found
99    #[error("Resource not found: {0}")]
100    NotFound(String),
101
102    /// Concurrency conflict detected
103    #[error("Concurrency conflict: {0}")]
104    ConcurrencyConflict(String),
105
106    /// Compression operation failed
107    #[error("Compression error: {0}")]
108    CompressionError(String),
109
110    /// Validation failed
111    #[error("Validation error: {0}")]
112    ValidationError(String),
113
114    /// Invalid input provided
115    #[error("Invalid input: {0}")]
116    InvalidInput(String),
117
118    /// Internal error (should not happen)
119    #[error("Internal error: {0}")]
120    InternalError(String),
121
122    /// Security policy violation
123    #[error("Security violation: {0}")]
124    SecurityViolation(String),
125
126    /// Resource exhausted (memory, connections, etc.)
127    #[error("Resource exhausted: {0}")]
128    ResourceExhausted(String),
129}
130
131impl DomainError {
132    /// Create an invariant violation error
133    pub fn invariant_violation(message: impl Into<String>) -> Self {
134        Self::InvariantViolation(message.into())
135    }
136
137    /// Create an invalid state transition error
138    pub fn invalid_transition(from: &str, to: &str) -> Self {
139        Self::InvalidStateTransition(format!("{from} -> {to}"))
140    }
141}
142
143impl From<String> for DomainError {
144    fn from(error: String) -> Self {
145        Self::Logic(error)
146    }
147}
148
149#[cfg(test)]
150mod tests {
151    use super::*;
152
153    #[test]
154    fn test_domain_error_creation() {
155        let err = DomainError::invariant_violation("test");
156        assert!(matches!(err, DomainError::InvariantViolation(_)));
157
158        let err = DomainError::invalid_transition("StateA", "StateB");
159        assert!(matches!(err, DomainError::InvalidStateTransition(_)));
160    }
161
162    #[test]
163    fn test_domain_result() {
164        let result: DomainResult<u32> = Ok(42);
165        assert!(result.is_ok());
166
167        let result: DomainResult<u32> = Err(DomainError::Logic("test".to_string()));
168        assert!(result.is_err());
169    }
170}