1#[macro_export]
67macro_rules! schema_string {
68 ($description:expr) => {
69 serde_json::json!({
70 "type": "string",
71 "description": $description
72 })
73 };
74 ($description:expr, pattern: $pattern:expr) => {
75 serde_json::json!({
76 "type": "string",
77 "description": $description,
78 "pattern": $pattern
79 })
80 };
81}
82
83#[macro_export]
92macro_rules! schema_bool {
93 ($description:expr) => {
94 serde_json::json!({
95 "type": "boolean",
96 "description": $description
97 })
98 };
99 ($description:expr, default: $default:expr) => {
100 serde_json::json!({
101 "type": "boolean",
102 "description": $description,
103 "default": $default
104 })
105 };
106}
107
108#[macro_export]
117macro_rules! schema_integer {
118 ($description:expr) => {
119 serde_json::json!({
120 "type": "integer",
121 "description": $description
122 })
123 };
124 ($description:expr, default: $default:expr) => {
125 serde_json::json!({
126 "type": "integer",
127 "description": $description,
128 "default": $default
129 })
130 };
131 ($description:expr, minimum: $min:expr, maximum: $max:expr) => {
132 serde_json::json!({
133 "type": "integer",
134 "description": $description,
135 "minimum": $min,
136 "maximum": $max
137 })
138 };
139}
140
141#[macro_export]
149macro_rules! schema_enum {
150 ([$($variant:expr),+ $(,)?], $description:expr) => {
151 serde_json::json!({
152 "type": "string",
153 "enum": [$($variant),+],
154 "description": $description
155 })
156 };
157 ([$($variant:expr),+ $(,)?], $description:expr, default: $default:expr) => {
158 serde_json::json!({
159 "type": "string",
160 "enum": [$($variant),+],
161 "description": $description,
162 "default": $default
163 })
164 };
165}
166
167#[macro_export]
177macro_rules! schema_options {
178 ($($field:ident: $schema:expr),+ $(,)?) => {
179 serde_json::json!({
180 "type": "object",
181 "description": "Format-specific options",
182 "properties": {
183 $(stringify!($field): $schema),+
184 }
185 })
186 };
187}
188
189#[macro_export]
197macro_rules! schema_string_array {
198 ($description:expr) => {
199 serde_json::json!({
200 "type": "array",
201 "items": { "type": "string" },
202 "description": $description
203 })
204 };
205 ($description:expr, items_pattern: $pattern:expr) => {
206 serde_json::json!({
207 "type": "array",
208 "items": {
209 "type": "string",
210 "pattern": $pattern
211 },
212 "description": $description
213 })
214 };
215}
216
217#[macro_export]
224macro_rules! schema_array {
225 ($description:expr, items: $items:tt) => {
226 serde_json::json!({
227 "type": "array",
228 "items": $items,
229 "description": $description
230 })
231 };
232}
233
234#[macro_export]
250macro_rules! tool_schema {
251 (
252 required: [$($req:expr),* $(,)?],
253 properties: {
254 $($field:ident: $schema:expr),+ $(,)?
255 }
256 ) => {
257 serde_json::json!({
258 "type": "object",
259 "properties": {
260 $(stringify!($field): $schema),+
261 },
262 "required": [$($req),*]
263 })
264 };
265}
266
267#[macro_export]
277macro_rules! hedl_content_arg {
278 () => {
279 $crate::schema_string!("HEDL document content")
280 };
281 ($description:expr) => {
282 $crate::schema_string!($description)
283 };
284}
285
286#[macro_export]
294macro_rules! path_arg {
295 () => {
296 $crate::schema_string!("File or directory path")
297 };
298 ($description:expr) => {
299 $crate::schema_string!($description)
300 };
301}
302
303#[macro_export]
311macro_rules! format_arg {
312 ([$($variant:expr),+ $(,)?]) => {
313 $crate::schema_enum!([$($variant),+], "Format type")
314 };
315 ([$($variant:expr),+ $(,)?], $description:expr) => {
316 $crate::schema_enum!([$($variant),+], $description)
317 };
318}
319
320#[macro_export]
327macro_rules! validation_args {
328 () => {
329 (
330 $crate::schema_bool!(
331 "Enable strict validation mode: treat lint warnings as errors",
332 default: true
333 ),
334 $crate::schema_bool!(
335 "Run linting rules in addition to parsing",
336 default: true
337 )
338 )
339 };
340}
341
342#[macro_export]
350macro_rules! pagination_args {
351 () => {
352 (
353 $crate::schema_integer!(
354 "Maximum number of entities to return",
355 default: 100
356 ),
357 $crate::schema_integer!(
358 "Number of entities to skip",
359 default: 0
360 )
361 )
362 };
363 (default_limit: $limit:expr) => {
364 (
365 $crate::schema_integer!(
366 "Maximum number of entities to return",
367 default: $limit
368 ),
369 $crate::schema_integer!(
370 "Number of entities to skip",
371 default: 0
372 )
373 )
374 };
375}
376
377#[macro_export]
384macro_rules! file_write_args {
385 () => {
386 (
387 $crate::schema_bool!(
388 "Validate HEDL before writing",
389 default: true
390 ),
391 $crate::schema_bool!(
392 "Format/canonicalize before writing",
393 default: false
394 ),
395 $crate::schema_bool!(
396 "Create backup of existing file if it exists",
397 default: true
398 )
399 )
400 };
401}
402
403#[macro_export]
411macro_rules! ditto_arg {
412 () => {
413 $crate::schema_bool!(
414 "Enable ditto optimization for repeated values",
415 default: true
416 )
417 };
418 ($description:expr) => {
419 $crate::schema_bool!($description, default: true)
420 };
421}
422
423#[macro_export]
430macro_rules! convert_to_options {
431 () => {
432 $crate::schema_options! {
433 pretty: $crate::schema_bool!("Pretty-print output (json)"),
434 include_headers: $crate::schema_bool!("Include headers (csv)"),
435 use_merge: $crate::schema_bool!("Use MERGE vs CREATE (cypher)"),
436 include_constraints: $crate::schema_bool!("Include constraints (cypher)")
437 }
438 };
439}
440
441#[macro_export]
448macro_rules! convert_from_options {
449 () => {
450 $crate::schema_options! {
451 type_name: $crate::schema_string!("Type name for entities (csv)"),
452 schema: $crate::schema_string_array!("Column names (csv)"),
453 delimiter: $crate::schema_string!("Field delimiter (csv)")
454 }
455 };
456}
457
458#[cfg(test)]
459mod tests {
460 use serde_json::Value as JsonValue;
461
462 #[test]
463 fn test_schema_string() {
464 let schema = schema_string!("Test description");
465 assert_eq!(schema["type"], "string");
466 assert_eq!(schema["description"], "Test description");
467 assert!(schema.get("pattern").is_none());
468 }
469
470 #[test]
471 fn test_schema_string_with_pattern() {
472 let schema = schema_string!("HEDL file", pattern: r"\.hedl$");
473 assert_eq!(schema["type"], "string");
474 assert_eq!(schema["description"], "HEDL file");
475 assert_eq!(schema["pattern"], r"\.hedl$");
476 }
477
478 #[test]
479 fn test_schema_bool() {
480 let schema = schema_bool!("Enable feature");
481 assert_eq!(schema["type"], "boolean");
482 assert_eq!(schema["description"], "Enable feature");
483 assert!(schema.get("default").is_none());
484 }
485
486 #[test]
487 fn test_schema_bool_with_default() {
488 let schema = schema_bool!("Enable strict mode", default: true);
489 assert_eq!(schema["type"], "boolean");
490 assert_eq!(schema["description"], "Enable strict mode");
491 assert_eq!(schema["default"], true);
492 }
493
494 #[test]
495 fn test_schema_integer() {
496 let schema = schema_integer!("Row count");
497 assert_eq!(schema["type"], "integer");
498 assert_eq!(schema["description"], "Row count");
499 }
500
501 #[test]
502 fn test_schema_integer_with_default() {
503 let schema = schema_integer!("Page size", default: 50);
504 assert_eq!(schema["type"], "integer");
505 assert_eq!(schema["default"], 50);
506 }
507
508 #[test]
509 fn test_schema_integer_with_range() {
510 let schema = schema_integer!("Port number", minimum: 1, maximum: 65535);
511 assert_eq!(schema["type"], "integer");
512 assert_eq!(schema["minimum"], 1);
513 assert_eq!(schema["maximum"], 65535);
514 }
515
516 #[test]
517 fn test_schema_enum() {
518 let schema = schema_enum!(["json", "yaml", "csv"], "Output format");
519 assert_eq!(schema["type"], "string");
520 assert_eq!(schema["description"], "Output format");
521 let enum_vals = schema["enum"].as_array().unwrap();
522 assert_eq!(enum_vals.len(), 3);
523 assert!(enum_vals.contains(&JsonValue::String("json".to_string())));
524 }
525
526 #[test]
527 fn test_schema_enum_with_default() {
528 let schema = schema_enum!(["simple", "cl100k"], "Tokenizer", default: "simple");
529 assert_eq!(schema["type"], "string");
530 assert_eq!(schema["default"], "simple");
531 }
532
533 #[test]
534 fn test_schema_options() {
535 let schema = schema_options! {
536 pretty: schema_bool!("Pretty output"),
537 indent: schema_integer!("Indent size")
538 };
539 assert_eq!(schema["type"], "object");
540 assert_eq!(schema["description"], "Format-specific options");
541 assert!(schema["properties"].get("pretty").is_some());
542 assert!(schema["properties"].get("indent").is_some());
543 }
544
545 #[test]
546 fn test_schema_string_array() {
547 let schema = schema_string_array!("Column names");
548 assert_eq!(schema["type"], "array");
549 assert_eq!(schema["items"]["type"], "string");
550 assert_eq!(schema["description"], "Column names");
551 }
552
553 #[test]
554 fn test_tool_schema() {
555 let schema = tool_schema! {
556 required: ["hedl", "format"],
557 properties: {
558 hedl: schema_string!("HEDL content"),
559 format: schema_enum!(["json", "yaml"], "Format"),
560 ditto: schema_bool!("Use ditto", default: true)
561 }
562 };
563 assert_eq!(schema["type"], "object");
564 let required = schema["required"].as_array().unwrap();
565 assert_eq!(required.len(), 2);
566 assert!(schema["properties"].get("hedl").is_some());
567 assert!(schema["properties"].get("format").is_some());
568 assert!(schema["properties"].get("ditto").is_some());
569 }
570
571 #[test]
572 fn test_hedl_content_arg() {
573 let schema = hedl_content_arg!();
574 assert_eq!(schema["type"], "string");
575 assert_eq!(schema["description"], "HEDL document content");
576
577 let custom = hedl_content_arg!("Custom HEDL description");
578 assert_eq!(custom["description"], "Custom HEDL description");
579 }
580
581 #[test]
582 fn test_path_arg() {
583 let schema = path_arg!();
584 assert_eq!(schema["type"], "string");
585 assert_eq!(schema["description"], "File or directory path");
586
587 let custom = path_arg!("Output directory");
588 assert_eq!(custom["description"], "Output directory");
589 }
590
591 #[test]
592 fn test_format_arg() {
593 let schema = format_arg!(["json", "yaml", "csv"]);
594 assert_eq!(schema["type"], "string");
595 assert_eq!(schema["description"], "Format type");
596 let enum_vals = schema["enum"].as_array().unwrap();
597 assert_eq!(enum_vals.len(), 3);
598 }
599
600 #[test]
601 fn test_validation_args() {
602 let (strict, lint) = validation_args!();
603 assert_eq!(strict["type"], "boolean");
604 assert_eq!(strict["default"], true);
605 assert_eq!(lint["type"], "boolean");
606 assert_eq!(lint["default"], true);
607 }
608
609 #[test]
610 fn test_pagination_args() {
611 let (limit, offset) = pagination_args!();
612 assert_eq!(limit["type"], "integer");
613 assert_eq!(limit["default"], 100);
614 assert_eq!(offset["type"], "integer");
615 assert_eq!(offset["default"], 0);
616
617 let (custom_limit, _) = pagination_args!(default_limit: 50);
618 assert_eq!(custom_limit["default"], 50);
619 }
620
621 #[test]
622 fn test_file_write_args() {
623 let (validate, format, backup) = file_write_args!();
624 assert_eq!(validate["default"], true);
625 assert_eq!(format["default"], false);
626 assert_eq!(backup["default"], true);
627 }
628
629 #[test]
630 fn test_ditto_arg() {
631 let schema = ditto_arg!();
632 assert_eq!(schema["type"], "boolean");
633 assert_eq!(schema["default"], true);
634 assert!(schema["description"].as_str().unwrap().contains("ditto"));
635 }
636
637 #[test]
638 fn test_convert_to_options() {
639 let schema = convert_to_options!();
640 assert_eq!(schema["type"], "object");
641 assert!(schema["properties"].get("pretty").is_some());
642 assert!(schema["properties"].get("include_headers").is_some());
643 assert!(schema["properties"].get("use_merge").is_some());
644 assert!(schema["properties"].get("include_constraints").is_some());
645 }
646
647 #[test]
648 fn test_convert_from_options() {
649 let schema = convert_from_options!();
650 assert_eq!(schema["type"], "object");
651 assert!(schema["properties"].get("type_name").is_some());
652 assert!(schema["properties"].get("schema").is_some());
653 assert!(schema["properties"].get("delimiter").is_some());
654 }
655}