Skip to content

Commit 0243abf

Browse files
committed
unilang
1 parent b69f8cd commit 0243abf

File tree

5 files changed

+2169
-0
lines changed

5 files changed

+2169
-0
lines changed
Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
//! # Error Handling and Type Validation
2+
//!
3+
//! This example demonstrates comprehensive error handling scenarios,
4+
//! type validation, and error recovery patterns in Unilang applications.
5+
6+
use unilang::data::{ ArgumentAttributes, ArgumentDefinition, CommandDefinition, Kind, OutputData, ValidationRule };
7+
use unilang::registry::CommandRegistry;
8+
use unilang::semantic::SemanticAnalyzer;
9+
use unilang::error::Error;
10+
use unilang::help::HelpGenerator;
11+
use unilang_parser::Parser;
12+
13+
fn main() -> Result< (), Box< dyn std::error::Error > >
14+
{
15+
println!( "=== Error Handling and Type Validation Demo ===\n" );
16+
17+
let mut registry = CommandRegistry::new();
18+
19+
// Step 1: Command with strict validation rules
20+
let validate_cmd = CommandDefinition::former()
21+
.name( "validate" )
22+
.namespace( ".test" )
23+
.description( "Tests various validation scenarios and error handling".to_string() )
24+
.hint( "Validation testing command" )
25+
.status( "experimental" )
26+
.version( "1.0.0" )
27+
.aliases( vec![] )
28+
.tags( vec![ "validation".to_string(), "testing".to_string() ] )
29+
.permissions( vec![] )
30+
.idempotent( true )
31+
.deprecation_message( String::new() )
32+
.http_method_hint( "POST".to_string() )
33+
.examples( vec!
34+
[
35+
"test.validate age::25 email::[email protected]".to_string(),
36+
"validate age::30 score::95.5 level::advanced".to_string()
37+
])
38+
.arguments( vec!
39+
[
40+
// Integer with range validation
41+
ArgumentDefinition {
42+
name: "age".to_string(),
43+
description: "Age in years (18-120)".to_string(),
44+
kind: Kind::Integer,
45+
hint: "Must be between 18 and 120".to_string(),
46+
attributes: ArgumentAttributes { optional: false, ..Default::default() },
47+
validation_rules: vec![ ValidationRule::Min(18.0), ValidationRule::Max(120.0) ],
48+
aliases: vec![ "a".to_string() ],
49+
tags: vec![ "required".to_string() ],
50+
},
51+
52+
// String with pattern validation
53+
ArgumentDefinition {
54+
name: "email".to_string(),
55+
description: "Valid email address".to_string(),
56+
kind: Kind::String,
57+
hint: "Standard email format".to_string(),
58+
attributes: ArgumentAttributes { optional: false, ..Default::default() },
59+
validation_rules: vec![ ValidationRule::Pattern(r"^[^\s@]+@[^\s@]+\.[^\s@]+$".to_string()) ],
60+
aliases: vec![ "e".to_string() ],
61+
tags: vec![ "required".to_string() ],
62+
},
63+
64+
// Float with precision validation
65+
ArgumentDefinition {
66+
name: "score".to_string(),
67+
description: "Score percentage (0.0-100.0)".to_string(),
68+
kind: Kind::Float,
69+
hint: "Decimal percentage value".to_string(),
70+
attributes: ArgumentAttributes { optional: true, default: Some("0.0".to_string()), ..Default::default() },
71+
validation_rules: vec![ ValidationRule::Min(0.0), ValidationRule::Max(100.0) ],
72+
aliases: vec![ "s".to_string() ],
73+
tags: vec![ "optional".to_string() ],
74+
},
75+
76+
// Enum with restricted choices
77+
ArgumentDefinition {
78+
name: "level".to_string(),
79+
description: "Skill level selection".to_string(),
80+
kind: Kind::Enum( vec![ "beginner".to_string(), "intermediate".to_string(), "advanced".to_string(), "expert".to_string() ] ),
81+
hint: "Choose from predefined levels".to_string(),
82+
attributes: ArgumentAttributes { optional: true, default: Some("beginner".to_string()), ..Default::default() },
83+
validation_rules: vec![],
84+
aliases: vec![ "l".to_string() ],
85+
tags: vec![ "choice".to_string() ],
86+
},
87+
88+
// Interactive argument that triggers special error
89+
ArgumentDefinition {
90+
name: "password".to_string(),
91+
description: "User password (interactive input required)".to_string(),
92+
kind: Kind::String,
93+
hint: "Secure password".to_string(),
94+
attributes: ArgumentAttributes {
95+
optional: true,
96+
interactive: true,
97+
sensitive: true,
98+
..Default::default()
99+
},
100+
validation_rules: vec![ ValidationRule::MinLength(8) ],
101+
aliases: vec![ "p".to_string() ],
102+
tags: vec![ "security".to_string() ],
103+
},
104+
])
105+
.end();
106+
107+
let validate_routine = Box::new( | cmd : unilang::semantic::VerifiedCommand, _ctx |
108+
{
109+
println!( "✓ Validation passed for all arguments!" );
110+
println!( "Processed {} arguments successfully", cmd.arguments.len() );
111+
112+
for ( name, value ) in &cmd.arguments
113+
{
114+
println!( " ✓ {name}: {value}" );
115+
}
116+
117+
Ok( OutputData
118+
{
119+
content : "All validations passed successfully".to_string(),
120+
format : "text".to_string(),
121+
})
122+
});
123+
124+
registry.command_add_runtime( &validate_cmd, validate_routine )?;
125+
println!( "✓ Registered validation test command" );
126+
127+
println!( "\n=== Error Scenarios Demonstration ===\n" );
128+
129+
let options = unilang_parser::UnilangParserOptions::default();
130+
let parser = Parser::new( options );
131+
let help_generator = HelpGenerator::new( &registry );
132+
133+
// Test cases with different error types
134+
let test_cases = vec![
135+
// 1. Type conversion errors
136+
("test.validate age::not_a_number email::[email protected]", "Invalid integer conversion"),
137+
("test.validate age::25 email::[email protected] score::invalid_float", "Invalid float conversion"),
138+
("test.validate age::25 email::[email protected] level::invalid_choice", "Enum choice validation"),
139+
140+
// 2. Range validation errors
141+
("test.validate age::15 email::[email protected]", "Age below minimum (18)"),
142+
("test.validate age::150 email::[email protected]", "Age above maximum (120)"),
143+
("test.validate age::25 email::[email protected] score::-5.0", "Score below minimum (0.0)"),
144+
("test.validate age::25 email::[email protected] score::150.0", "Score above maximum (100.0)"),
145+
146+
// 3. Pattern validation errors
147+
("test.validate age::25 email::invalid_email", "Email pattern validation"),
148+
("test.validate age::25 email::@invalid.com", "Email format validation"),
149+
150+
// 4. Missing required arguments
151+
("test.validate email::[email protected]", "Missing required age argument"),
152+
("test.validate age::25", "Missing required email argument"),
153+
154+
// 5. Interactive argument signaling
155+
("test.validate age::25 email::[email protected] password::secret123", "Interactive argument error"),
156+
157+
// 6. Command not found
158+
("nonexistent.command arg::value", "Command not found"),
159+
160+
// 7. Parsing errors
161+
("test.validate age::25 email::[email protected] invalid::syntax::", "Parsing error"),
162+
];
163+
164+
for ( input, expected_error ) in test_cases
165+
{
166+
println!( "🧪 Testing: {input}" );
167+
println!( " Expected: {expected_error}" );
168+
169+
match parser.parse( input )
170+
{
171+
Ok( instructions ) =>
172+
{
173+
let analyzer = SemanticAnalyzer::new( &instructions, &registry );
174+
match analyzer.analyze()
175+
{
176+
Ok( _verified_commands ) =>
177+
{
178+
println!( " ❌ Unexpectedly succeeded (should have failed)" );
179+
},
180+
Err( error ) =>
181+
{
182+
println!( " ✓ Caught error: {}", format_error( &error ) );
183+
}
184+
}
185+
},
186+
Err( parse_error ) =>
187+
{
188+
println!( " ✓ Parse error: {parse_error}" );
189+
}
190+
}
191+
println!();
192+
}
193+
194+
println!( "=== Error Type Classification ===" );
195+
println!( "• Parse Errors - Syntax issues in command string" );
196+
println!( "• Type Errors - Invalid type conversions (UNILANG_TYPE_MISMATCH)" );
197+
println!( "• Validation Errors - Failed validation rules" );
198+
println!( "• Missing Argument Errors - Required arguments not provided" );
199+
println!( "• Interactive Argument Errors - UNILANG_ARGUMENT_INTERACTIVE_REQUIRED" );
200+
println!( "• Command Not Found - Unknown command or namespace" );
201+
println!( "• Registration Errors - Runtime command registration issues" );
202+
203+
println!( "\n=== Error Recovery Patterns ===\n" );
204+
205+
// Demonstrate error recovery with fallback commands
206+
println!( "🔄 Error Recovery Example:" );
207+
let problematic_input = "test.validate age::invalid email::[email protected]";
208+
209+
match parser.parse( problematic_input )
210+
{
211+
Ok( instructions ) =>
212+
{
213+
let analyzer = SemanticAnalyzer::new( &instructions, &registry );
214+
match analyzer.analyze()
215+
{
216+
Ok( _verified ) => println!( " ✓ Command executed successfully" ),
217+
Err( error ) =>
218+
{
219+
println!( " ❌ Command failed: {}", format_error( &error ) );
220+
println!( " 💡 Recovery suggestion:" );
221+
222+
if let Some( help ) = help_generator.command( "test.validate" )
223+
{
224+
println!( " 📖 Command help:\n{help}" );
225+
}
226+
227+
println!( " 🔧 Corrected command:" );
228+
println!( " test.validate age::25 email::[email protected]" );
229+
}
230+
}
231+
},
232+
Err( parse_error ) =>
233+
{
234+
println!( " ❌ Parse failed: {parse_error}" );
235+
}
236+
}
237+
238+
println!( "\n=== Best Practices for Error Handling ===\n" );
239+
println!( "✨ Error Handling Guidelines:" );
240+
println!( " • Always check command syntax before execution" );
241+
println!( " • Provide clear, actionable error messages" );
242+
println!( " • Use validation rules to prevent invalid input" );
243+
println!( " • Handle interactive arguments appropriately" );
244+
println!( " • Implement graceful degradation for non-critical failures" );
245+
println!( " • Log errors with sufficient context for debugging" );
246+
println!( " • Provide help information when commands fail" );
247+
248+
println!( "\n=== Usage Examples ===" );
249+
println!( "# Valid command:" );
250+
println!( "cargo run --bin unilang_cli test.validate age::25 email::[email protected] score::95.5 level::advanced" );
251+
252+
println!( "\n# Invalid commands (for testing):" );
253+
println!( "cargo run --bin unilang_cli test.validate age::15 email::[email protected] # Age too low" );
254+
println!( "cargo run --bin unilang_cli test.validate age::25 email::invalid_email # Invalid email" );
255+
println!( "cargo run --bin unilang_cli test.validate email::[email protected] # Missing age" );
256+
257+
Ok( () )
258+
}
259+
260+
/// Format error with appropriate styling and context
261+
fn format_error( error : &Error ) -> String
262+
{
263+
match error
264+
{
265+
Error::Execution( error_data ) =>
266+
{
267+
match error_data.code.as_str()
268+
{
269+
"UNILANG_TYPE_MISMATCH" => format!( "🔢 Type Error: {}", error_data.message ),
270+
"UNILANG_ARGUMENT_INTERACTIVE_REQUIRED" => format!( "🔒 Interactive Input: {}", error_data.message ),
271+
_ => format!( "⚠️ Execution Error: {}", error_data.message ),
272+
}
273+
},
274+
Error::Registration( msg ) => format!( "📝 Registration: {msg}" ),
275+
Error::Yaml( err ) => format!( "📄 YAML: {err}" ),
276+
Error::Json( err ) => format!( "📄 JSON: {err}" ),
277+
Error::Parse( err ) => format!( "🔍 Parse: {err}" ),
278+
}
279+
}

0 commit comments

Comments
 (0)