term_guard/core/validation_context.rs
1//! Validation context for passing runtime information to constraints.
2//!
3//! This module provides a context object that can be used to pass runtime
4//! information (like table names) to constraints during evaluation.
5
6use std::sync::Arc;
7
8/// Runtime context for validation operations.
9///
10/// This struct holds runtime information that constraints need during evaluation,
11/// such as the name of the table being validated. This allows constraints to
12/// work with any table name rather than being hardcoded to "data".
13#[derive(Debug, Clone)]
14pub struct ValidationContext {
15 /// The name of the table being validated
16 table_name: Arc<str>,
17}
18
19impl ValidationContext {
20 /// Creates a new validation context with the specified table name.
21 ///
22 /// # Arguments
23 ///
24 /// * `table_name` - The name of the table to validate
25 ///
26 /// # Examples
27 ///
28 /// ```rust
29 /// use term_guard::core::ValidationContext;
30 ///
31 /// let ctx = ValidationContext::new("customer_data");
32 /// assert_eq!(ctx.table_name(), "customer_data");
33 /// ```
34 pub fn new(table_name: impl Into<Arc<str>>) -> Self {
35 Self {
36 table_name: table_name.into(),
37 }
38 }
39
40 /// Creates a validation context for the default table name "data".
41 ///
42 /// This is provided for backward compatibility with existing code.
43 ///
44 /// # Examples
45 ///
46 /// ```rust
47 /// use term_guard::core::ValidationContext;
48 ///
49 /// let ctx = ValidationContext::with_default_table();
50 /// assert_eq!(ctx.table_name(), "data");
51 /// ```
52 pub fn with_default_table() -> Self {
53 Self::new("data")
54 }
55
56 /// Returns the name of the table being validated.
57 pub fn table_name(&self) -> &str {
58 &self.table_name
59 }
60}
61
62impl Default for ValidationContext {
63 fn default() -> Self {
64 Self::new("data")
65 }
66}
67
68// Thread-local storage for the current validation context.
69// This allows constraints to access the validation context without
70// requiring changes to the Constraint trait interface.
71tokio::task_local! {
72 pub static CURRENT_CONTEXT: ValidationContext;
73}
74
75/// Gets the current validation context.
76///
77/// Returns the default context if no context has been set.
78pub fn current_validation_context() -> ValidationContext {
79 CURRENT_CONTEXT
80 .try_with(|ctx| ctx.clone())
81 .unwrap_or_else(|_| ValidationContext::with_default_table())
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87
88 #[test]
89 fn test_validation_context_creation() {
90 let ctx = ValidationContext::new("test_table");
91 assert_eq!(ctx.table_name(), "test_table");
92 }
93
94 #[test]
95 fn test_validation_context_default() {
96 let ctx = ValidationContext::with_default_table();
97 assert_eq!(ctx.table_name(), "data");
98 }
99
100 #[tokio::test]
101 async fn test_task_local_context() {
102 // Default context
103 assert_eq!(current_validation_context().table_name(), "data");
104
105 // Set custom context using task local
106 let custom_ctx = ValidationContext::new("custom_table");
107 CURRENT_CONTEXT
108 .scope(custom_ctx, async {
109 assert_eq!(current_validation_context().table_name(), "custom_table");
110 })
111 .await;
112
113 // Back to default
114 assert_eq!(current_validation_context().table_name(), "data");
115 }
116
117 #[tokio::test]
118 async fn test_nested_contexts() {
119 let ctx1 = ValidationContext::new("table1");
120 let ctx2 = ValidationContext::new("table2");
121
122 CURRENT_CONTEXT
123 .scope(ctx1, async {
124 assert_eq!(current_validation_context().table_name(), "table1");
125
126 CURRENT_CONTEXT
127 .scope(ctx2, async {
128 assert_eq!(current_validation_context().table_name(), "table2");
129 })
130 .await;
131
132 assert_eq!(current_validation_context().table_name(), "table1");
133 })
134 .await;
135
136 assert_eq!(current_validation_context().table_name(), "data");
137 }
138}