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