1
use supp_macro::command;
2

            
3
// Test the command procedural macro with comprehensive coverage
4
// This tests the actual #[proc_macro] command macro from src/lib.rs
5

            
6
#[derive(Debug, Clone)]
7
pub enum Argument {
8
    String(String),
9
    Integer(i64),
10
    Boolean(bool),
11
}
12

            
13
#[derive(Debug)]
14
pub enum CmdError {
15
    Args(String),
16
}
17

            
18
impl std::fmt::Display for CmdError {
19
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20
        match self {
21
            CmdError::Args(msg) => write!(f, "Argument error: {}", msg),
22
        }
23
    }
24
}
25

            
26
impl std::error::Error for CmdError {}
27

            
28
#[derive(Debug)]
29
pub enum CmdResult {
30
    Success(String),
31
    Value(i64),
32
}
33

            
34
// TryFrom implementations for type conversion
35
impl TryFrom<Argument> for String {
36
    type Error = CmdError;
37

            
38
    fn try_from(arg: Argument) -> Result<Self, Self::Error> {
39
        match arg {
40
            Argument::String(s) => Ok(s),
41
            _ => Err(CmdError::Args(format!(
42
                "Cannot convert {:?} to String",
43
                arg
44
            ))),
45
        }
46
    }
47
}
48

            
49
impl TryFrom<Argument> for i64 {
50
    type Error = CmdError;
51

            
52
    fn try_from(arg: Argument) -> Result<Self, Self::Error> {
53
        match arg {
54
            Argument::Integer(i) => Ok(i),
55
            _ => Err(CmdError::Args(format!("Cannot convert {:?} to i64", arg))),
56
        }
57
    }
58
}
59

            
60
impl TryFrom<Argument> for bool {
61
    type Error = CmdError;
62

            
63
    fn try_from(arg: Argument) -> Result<Self, Self::Error> {
64
        match arg {
65
            Argument::Boolean(b) => Ok(b),
66
            _ => Err(CmdError::Args(format!("Cannot convert {:?} to bool", arg))),
67
        }
68
    }
69
}
70

            
71
// Progressive types approach - no unified CommandArgs struct needed!
72
// The macro generates command-specific runner types that provide
73
// compile-time validation and zero runtime overhead.
74

            
75
// Test commands demonstrating all macro capabilities
76

            
77
// 1. Command with no arguments
78
command! {
79
    SimpleCommand {
80
    } => {
81
        Ok(Some(CmdResult::Success("Simple command executed".to_string())))
82
    }
83
4
}
84

            
85
// 2. Command with required arguments only
86
command! {
87
    RequiredArgsCommand {
88
        #[required]
89
        name: String,
90
        #[required]
91
        count: i64,
92
    } => {
93
        Ok(Some(CmdResult::Success(format!("Name: {}, Count: {}", name, count))))
94
    }
95
20
}
96

            
97
// 3. Command with optional arguments only
98
command! {
99
    OptionalArgsCommand {
100
        #[optional]
101
        flag: bool,
102
        #[optional]
103
        message: String,
104
    } => {
105
2
        let flag_str = flag.map_or("false".to_string(), |f| f.to_string());
106
2
        let message_str = message.map_or("default".to_string(), |s| s.to_string());
107
        Ok(Some(CmdResult::Success(format!("Flag: {}, Message: {}", flag_str, message_str))))
108
    }
109
14
}
110

            
111
// 4. Command with mixed required and optional arguments
112
command! {
113
    MixedArgsCommand {
114
        #[required]
115
        id: i64,
116
        #[required]
117
        name: String,
118
        #[optional]
119
        enabled: bool,
120
        #[optional]
121
        description: String,
122
    } => {
123
2
        let enabled_str = enabled.map_or("false".to_string(), |e| e.to_string());
124
2
        let desc = description.map_or("no description".to_string(), |s| s.to_string());
125
        Ok(Some(CmdResult::Success(format!(
126
            "ID: {}, Name: {}, Enabled: {}, Description: {}",
127
            id, name, enabled_str, desc
128
        ))))
129
    }
130
36
}
131

            
132
#[cfg(test)]
133
mod tests {
134
    use super::*;
135

            
136
    #[tokio::test]
137
3
    async fn test_simple_command_no_args() {
138
2
        let result = SimpleCommand::new().run().await.unwrap();
139
2
        assert!(result.is_some());
140

            
141
3
        if let Some(CmdResult::Success(msg)) = result {
142
3
            assert_eq!(msg, "Simple command executed");
143
2
        } else {
144
2
            panic!("Expected Success result");
145
2
        }
146
2
    }
147

            
148
    #[tokio::test]
149
3
    async fn test_required_args_command_success() {
150
2
        let result = RequiredArgsCommand::new()
151
2
            .name("test".to_string())
152
2
            .count(42)
153
2
            .run()
154
2
            .await
155
2
            .unwrap();
156
2
        assert!(result.is_some());
157

            
158
3
        if let Some(CmdResult::Success(msg)) = result {
159
3
            assert_eq!(msg, "Name: test, Count: 42");
160
2
        } else {
161
2
            panic!("Expected Success result");
162
2
        }
163
2
    }
164

            
165
    #[tokio::test]
166
3
    async fn test_optional_args_command_none() {
167
2
        let result = OptionalArgsCommand::new().run().await.unwrap();
168
2
        assert!(result.is_some());
169

            
170
3
        if let Some(CmdResult::Success(msg)) = result {
171
3
            assert_eq!(msg, "Flag: false, Message: default");
172
2
        } else {
173
2
            panic!("Expected Success result");
174
2
        }
175
2
    }
176

            
177
    #[tokio::test]
178
3
    async fn test_optional_args_command_with_values() {
179
2
        let result = OptionalArgsCommand::new()
180
2
            .flag(true)
181
2
            .message("hello".to_string())
182
2
            .run()
183
2
            .await
184
2
            .unwrap();
185
2
        assert!(result.is_some());
186

            
187
3
        if let Some(CmdResult::Success(msg)) = result {
188
3
            assert_eq!(msg, "Flag: true, Message: hello");
189
2
        } else {
190
2
            panic!("Expected Success result");
191
2
        }
192
2
    }
193

            
194
    #[tokio::test]
195
3
    async fn test_mixed_args_command_required_only() {
196
2
        let result = MixedArgsCommand::new()
197
2
            .id(123)
198
2
            .name("test_item".to_string())
199
2
            .run()
200
2
            .await
201
2
            .unwrap();
202
2
        assert!(result.is_some());
203

            
204
3
        if let Some(CmdResult::Success(msg)) = result {
205
3
            assert_eq!(
206
2
                msg,
207
2
                "ID: 123, Name: test_item, Enabled: false, Description: no description"
208
2
            );
209
2
        } else {
210
2
            panic!("Expected Success result");
211
2
        }
212
2
    }
213

            
214
    #[tokio::test]
215
3
    async fn test_mixed_args_command_all_args() {
216
2
        let result = MixedArgsCommand::new()
217
2
            .id(456)
218
2
            .name("full_item".to_string())
219
2
            .enabled(true)
220
2
            .description("A complete item".to_string())
221
2
            .run()
222
2
            .await
223
2
            .unwrap();
224
2
        assert!(result.is_some());
225

            
226
3
        if let Some(CmdResult::Success(msg)) = result {
227
3
            assert_eq!(
228
2
                msg,
229
2
                "ID: 456, Name: full_item, Enabled: true, Description: A complete item"
230
2
            );
231
2
        } else {
232
2
            panic!("Expected Success result");
233
2
        }
234
2
    }
235

            
236
    #[test]
237
2
    fn test_compile_time_validation() {
238
        // ✅ Progressive types approach with compile-time validation
239

            
240
        // 1. Required arguments - built using progressive types
241
2
        let _runner1 = RequiredArgsCommand::new()
242
2
            .name("test".to_string())
243
2
            .count(42);
244
        // .run() is available here because all required fields are set
245

            
246
        // 2. Optional arguments - no required fields needed
247
2
        let _runner2 = OptionalArgsCommand::new();
248
        // .run() is available immediately for optional-only commands
249

            
250
        // 3. Mixed required and optional arguments
251
2
        let _runner3 = MixedArgsCommand::new()
252
2
            .id(123)
253
2
            .name("test".to_string())
254
2
            .enabled(false)
255
2
            .description("desc".to_string());
256
        // .run() is available because all required fields are set
257

            
258
        // ✅ Progressive types provide compile-time safety
259
        // ✅ No Args type naming needed from user perspective
260
        // ✅ Direct field access without unwrap() in generated code
261
2
    }
262

            
263
    #[test]
264
2
    fn test_compile_time_errors() {
265
        // Test that compile-time errors occur with progressive types
266
2
        let t = trybuild::TestCases::new();
267

            
268
        // This test should fail to compile due to wrong argument type
269
2
        t.compile_fail("tests/compile-fail/wrong_argument_type.rs");
270

            
271
        // This test should fail to compile due to nonexistent method
272
2
        t.compile_fail("tests/compile-fail/missing_required_field.rs");
273

            
274
        // This test should fail to compile due to missing required field
275
2
        t.compile_fail("tests/compile-fail/missing_required_field_compile_time.rs");
276
2
    }
277

            
278
    #[test]
279
2
    fn test_compile_time_validation_demo() {
280
        // With progressive types, missing required fields are caught at compile time!
281
        // These examples would NOT compile:
282

            
283
        // ❌ This would NOT compile - missing required field:
284
        // let result = RequiredArgsCommand::new()
285
        //     .name("test".to_string())
286
        //     .run()  // ERROR: no method named `run` found for `RequiredArgsCommandRunner01`
287
        //     .await;
288

            
289
        // ❌ This would NOT compile - wrong type:
290
        // let runner = RequiredArgsCommand::new()
291
        //     .name("test".to_string())
292
        //     .count("not_a_number");  // ERROR: expected `i64`, found `&str`
293

            
294
        // ✅ This compiles fine - all required fields provided:
295
2
        let _runner = RequiredArgsCommand::new()
296
2
            .name("test".to_string())
297
2
            .count(42);
298
        // .run() is available because all required fields are set
299

            
300
        // ✅ No runtime validation needed - zero unwrap() calls in generated code!
301
2
        println!("Progressive types eliminate runtime validation entirely!");
302
2
    }
303

            
304
    #[test]
305
2
    fn test_zero_runtime_overhead() {
306
        // Progressive types provide zero runtime overhead!
307
        // The generated run() methods use direct field access:
308
        //
309
        // pub async fn run(self) -> Result<Option<CmdResult>, CmdError> {
310
        //     let name = self.name;     // ✅ Direct field access - no unwrap()!
311
        //     let count = self.count;   // ✅ Direct field access - no unwrap()!
312
        //
313
        //     // Original command body...
314
        // }
315
        //
316
        // This is possible because the type system guarantees that when run()
317
        // is available, all required fields are present in the struct.
318

            
319
2
        println!("✅ Zero runtime validation - fields accessed directly");
320
2
        println!("✅ Type system guarantees all required fields are present");
321
2
        println!("✅ No .unwrap(), .expect(), or panic!() calls needed");
322

            
323
        // ✅ Demonstrate the clean interface
324
2
        let _runner = MixedArgsCommand::new().id(123).name("test".to_string());
325
        // Optional fields can be omitted, run() still available
326
2
    }
327
}