1use std::collections::{HashMap, HashSet};
2use std::iter;
3use std::sync::LazyLock;
4
5use crate::assemble::{ASM, ASMSchema, ASMState, build_asm_graph};
6use crate::build::{SymbolGraph, SymbolVar, build_symbol_graph};
7use crate::guide::{Trail, TrailState};
8use crate::helpers::{TrailError, is_escaped};
9
10const KEYWORDS: LazyLock<HashSet<&str>> = LazyLock::new(|| {
11 HashSet::from([
12 "type",
13 "enum",
14 "const",
15 "properties",
16 "required",
17 "items",
18 "prefixItems",
19 "oneOf",
20 ])
21});
22
23#[derive(Clone)]
24pub struct InputContext {
25 content: String,
26 line: usize,
27 path: String,
28}
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq)]
31pub enum InputKind {
32 Block,
33 Entry,
34 Array,
35 String,
36 Number,
37 Integer,
38}
39
40#[derive(Clone)]
41pub struct JSONInput {
42 kind: InputKind,
43 context: InputContext,
44}
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq)]
47pub enum LabelKind {
48 Property,
49 Keyword,
50}
51
52#[derive(Clone)]
53pub struct JSONLabel {
54 kind: LabelKind,
55 context: InputContext,
56}
57
58type JSONSpecs = HashMap<String, JSONInput>;
59
60fn split_json_input(input: JSONInput) -> Result<Vec<JSONInput>, TrailError> {
61 let (context, kind) = (&input.context, &input.kind);
62 let (content, path) = (&context.content, &context.path);
63
64 assert!(
65 matches!(kind, InputKind::Block | InputKind::Array),
66 "Expected `Block` or `Array`, got `{:#?}` instead.",
67 { kind }
68 );
69
70 let mut brackets = 0;
71 let mut braces = 0;
72 let mut inputs: Vec<JSONInput> = Vec::new();
73 let mut i = 0;
74 let mut accumulate: Vec<char> = Vec::new();
75 let mut line = context.line;
76
77 let chars: Vec<char> = content[1..content.len() - 1].chars().collect();
78
79 while i < chars.len() {
80 let curr = chars[i];
81
82 if curr == '{' {
83 braces += 1;
84 } else if curr == '}' {
85 braces -= 1;
86 } else if curr == '[' {
87 brackets += 1;
88 } else if curr == ']' {
89 brackets -= 1;
90 } else if curr == '\n' {
91 line += 1;
92 } else if curr == ',' && braces == 0 && brackets == 0 {
93 consume_input(&mut inputs, &mut accumulate, line, path.clone())?;
94 i += 1;
95 continue;
96 }
97
98 accumulate.push(curr);
99 i += 1;
100 }
101
102 if !is_valid_whitespace(&accumulate) {
103 consume_input(&mut inputs, &mut accumulate, line, path.clone())?;
104 }
105
106 return Ok(inputs);
107}
108
109pub fn build_entry_from_input(input: JSONInput) -> Result<(JSONLabel, JSONInput), TrailError> {
110 let (context, kind) = (&input.context, &input.kind);
111
112 assert!(
113 matches!(kind, InputKind::Entry),
114 "Expected `Entry`, got `{:#?}` instead.",
115 { kind }
116 );
117
118 let (line, path) = (context.line, &context.path);
119 let (key, value) = context
120 .content
121 .split_once(':')
122 .map(|(l, r)| (l.trim(), r.trim()))
123 .expect("Failed to split input of kind `Entry`.");
124
125 if is_valid_string(key) {
126 let label = build_json_label(&key[1..key.len() - 1], line, path.clone());
127 let value = build_json_input(value, line, format!("{}/{}", &path, &key[1..key.len() - 1]))?;
128
129 return Ok((label, value));
130 } else {
131 return Err(TrailError(format_json_error(
132 JSONError::InvalidLabel,
133 input.context,
134 )));
135 }
136}
137
138pub fn build_specs_from_input(input: JSONInput) -> Result<JSONSpecs, TrailError> {
139 let mut specs: JSONSpecs = HashMap::new();
140
141 if input.kind != InputKind::Block {
142 return Err(TrailError(format_json_error(
143 JSONError::InvalidBlock,
144 input.context,
145 )));
146 }
147
148 let entries = split_json_input(input)?;
149
150 for entry in entries {
151 if entry.kind != InputKind::Entry {
152 return Err(TrailError(format_json_error(
153 JSONError::InvalidEntry,
154 entry.context,
155 )));
156 }
157
158 let (label, input) = build_entry_from_input(entry)?;
159 let context = label.context;
160
161 if label.kind != LabelKind::Keyword {
162 return Err(TrailError(format_json_error(
163 JSONError::InvalidKeyword,
164 context,
165 )));
166 }
167
168 specs.insert(context.content, input);
169 }
170
171 return Ok(specs);
172}
173
174#[derive(Clone)]
177pub struct JSONBlock {
178 id: SymbolVar,
179 entity: Box<JSONEntity>,
180}
181
182impl Default for JSONBlock {
183 fn default() -> Self {
184 Self {
185 id: SymbolVar::rand(),
186 entity: Box::new(JSONEntity::default()),
187 }
188 }
189}
190
191#[derive(Clone)]
192pub struct JSONProperty {
193 label: String,
194 block: JSONBlock,
195 required: bool,
196}
197
198#[derive(Clone)]
199pub enum JSONEntity {
200 Object { properties: Vec<JSONProperty> },
201 Union { blocks: Vec<JSONBlock> },
202 Array { blocks: Vec<JSONBlock> },
203 Tuple { blocks: Vec<JSONBlock> },
204 Literal { value: String },
205}
206
207impl Default for JSONEntity {
208 fn default() -> Self {
209 Self::Literal {
210 value: String::from("\"null\""),
211 }
212 }
213}
214
215pub fn build_entity_from_input(input: JSONInput) -> Result<JSONEntity, TrailError> {
216 let specs = build_specs_from_input(input)?;
217
218 if let Some(input) = specs.get("type") {
219 let label = input.clone();
220 let (context, kind) = (label.context, label.kind);
221
222 if kind != InputKind::String {
223 return Err(TrailError(format_json_error(
224 JSONError::InvalidLabel,
225 context,
226 )));
227 }
228
229 let content = &context.content[1..context.content.len() - 1];
230
231 let entity = if content == "object" {
232 build_object_from_specs(&specs)?
233 } else if content == "array" {
234 build_array_from_specs(&specs)?
235 } else if content == "string" {
236 build_string_from_specs(&specs)?
237 } else if content == "number" {
238 build_number_from_specs(&specs)?
239 } else if content == "integer" {
240 build_integer_from_specs(&specs)?
241 } else if content == "boolean" {
242 JSONEntity::Literal {
243 value: String::from("\"true\" | \"false\""),
244 }
245 } else if content == "null" {
246 JSONEntity::Literal {
247 value: String::from("\"null\""),
248 }
249 } else {
250 return Err(TrailError(format_json_error(
251 JSONError::InvalidType,
252 context,
253 )));
254 };
255
256 return Ok(entity);
257 }
258
259 if let Some(input) = specs.get("oneOf") {
260 let array = input.clone();
261
262 if input.kind != InputKind::Array {
263 return Err(TrailError(format_json_error(
264 JSONError::InvalidArray,
265 array.context,
266 )));
267 }
268
269 let inputs: Vec<JSONInput> = split_json_input(array)?
270 .into_iter()
271 .map(|block| {
272 if block.kind != InputKind::Block {
273 Err(TrailError(format_json_error(
274 JSONError::InvalidBlock,
275 block.context,
276 )))
277 } else {
278 Ok(block)
279 }
280 })
281 .collect::<Result<Vec<JSONInput>, TrailError>>()?;
282
283 let mut blocks: Vec<JSONBlock> = Vec::new();
284
285 for input in inputs {
286 let entity = build_entity_from_input(input)?;
287 let block = JSONBlock {
288 id: SymbolVar::rand(),
289 entity: Box::new(entity),
290 };
291
292 blocks.push(block)
293 }
294
295 return Ok(JSONEntity::Union { blocks: blocks });
296 }
297
298 return Ok(JSONEntity::default());
299}
300
301pub fn build_object_from_specs(specs: &JSONSpecs) -> Result<JSONEntity, TrailError> {
302 let mut properties: Vec<JSONProperty> = Vec::new();
303
304 if let Some(input) = specs.get("properties") {
305 let block = input.clone();
306
307 if block.kind != InputKind::Block {
308 return Err(TrailError(format_json_error(
309 JSONError::InvalidBlock,
310 block.context,
311 )));
312 }
313
314 let entries = split_json_input(block)?;
315
316 for entry in entries {
317 if entry.kind != InputKind::Entry {
318 return Err(TrailError(format_json_error(
319 JSONError::InvalidEntry,
320 entry.context,
321 )));
322 }
323
324 let (label, input) = build_entry_from_input(entry)?;
325 let context = label.context;
326 let entity = build_entity_from_input(input)?;
327
328 let block = JSONBlock {
329 id: SymbolVar::rand(),
330 entity: Box::new(entity),
331 };
332
333 properties.push(JSONProperty {
334 label: context.content,
335 block: block,
336 required: false,
337 })
338 }
339 }
340
341 if let Some(input) = specs.get("required") {
342 let array = input.clone();
343
344 if array.kind != InputKind::Array {
345 return Err(TrailError(format_json_error(
346 JSONError::InvalidInput,
347 array.context,
348 )));
349 }
350
351 let labels: Vec<String> = split_json_input(array)?
352 .into_iter()
353 .map(|label| {
354 let context = label.context;
355
356 if label.kind != InputKind::String {
357 Err(TrailError(format_json_error(
358 JSONError::InvalidString,
359 context,
360 )))
361 } else {
362 Ok(context.content[1..context.content.len() - 1].to_string())
363 }
364 })
365 .collect::<Result<Vec<String>, TrailError>>()?;
366
367 for property in &mut properties {
368 if labels.contains(&property.label) {
369 property.required = true;
370 }
371 }
372 }
373
374 return Ok(JSONEntity::Object {
375 properties: properties,
376 });
377}
378
379pub fn build_array_from_specs(specs: &JSONSpecs) -> Result<JSONEntity, TrailError> {
380 if let Some(input) = specs.get("items") {
381 let (block, kind) = (input.clone(), input.kind);
382
383 if kind != InputKind::Block {
384 return Err(TrailError(format_json_error(
385 JSONError::InvalidBlock,
386 block.context,
387 )));
388 }
389
390 let entity = build_entity_from_input(block)?;
391 let block = JSONBlock {
392 id: SymbolVar::rand(),
393 entity: Box::new(entity),
394 };
395
396 return Ok(JSONEntity::Array {
397 blocks: vec![block],
398 });
399 }
400
401 if let Some(input) = specs.get("prefixItems") {
402 let (array, kind) = (input.clone(), input.kind);
403
404 if kind != InputKind::Array {
405 return Err(TrailError(format_json_error(
406 JSONError::InvalidArray,
407 array.context,
408 )));
409 }
410
411 let inputs: Vec<JSONInput> = split_json_input(array)?
412 .into_iter()
413 .map(|block| {
414 if block.kind != InputKind::Block {
415 Err(TrailError(format_json_error(
416 JSONError::InvalidBlock,
417 block.context,
418 )))
419 } else {
420 Ok(block)
421 }
422 })
423 .collect::<Result<Vec<JSONInput>, TrailError>>()?;
424
425 if inputs.is_empty() {
426 return Ok(JSONEntity::Tuple {
427 blocks: vec![JSONBlock::default()],
428 });
429 }
430
431 let mut blocks: Vec<JSONBlock> = Vec::new();
432
433 for input in inputs {
434 let entity = build_entity_from_input(input)?;
435 let block = JSONBlock {
436 id: SymbolVar::rand(),
437 entity: Box::new(entity),
438 };
439
440 blocks.push(block)
441 }
442
443 return Ok(JSONEntity::Tuple { blocks: blocks });
444 }
445
446 return Ok(JSONEntity::Array {
447 blocks: vec![JSONBlock::default()],
448 });
449}
450
451pub fn build_number_from_specs(specs: &JSONSpecs) -> Result<JSONEntity, TrailError> {
452 if let Some(input) = specs.get("const") {
453 let number = input.clone();
454 let (context, kind) = (number.context, number.kind);
455
456 if !matches!(kind, InputKind::Integer | InputKind::Number) {
457 return Err(TrailError(format_json_error(
458 JSONError::InvalidNumber,
459 context,
460 )));
461 }
462
463 return Ok(JSONEntity::Literal {
464 value: format!("\"{}\"", context.content),
465 });
466 }
467
468 if let Some(input) = specs.get("enum") {
469 let array = input.clone();
470
471 if array.kind != InputKind::Array {
472 return Err(TrailError(format_json_error(
473 JSONError::InvalidArray,
474 array.context,
475 )));
476 }
477
478 let value: String = split_json_input(array)?
479 .into_iter()
480 .map(|label| {
481 let context = label.context;
482
483 if !matches!(label.kind, InputKind::Integer | InputKind::Number) {
484 Err(TrailError(format_json_error(
485 JSONError::InvalidNumber,
486 context,
487 )))
488 } else {
489 Ok(format!("\"{}\"", context.content))
490 }
491 })
492 .collect::<Result<Vec<String>, TrailError>>()?
493 .join(" | ");
494
495 return Ok(JSONEntity::Literal { value: value });
496 }
497
498 return Ok(JSONEntity::Literal {
499 value: String::from("/^[0-9]\\.[0-9]$/"),
500 });
501}
502
503pub fn build_integer_from_specs(specs: &JSONSpecs) -> Result<JSONEntity, TrailError> {
504 if let Some(input) = specs.get("const") {
505 let number = input.clone();
506 let (context, kind) = (number.context, number.kind);
507
508 if kind != InputKind::Integer {
509 return Err(TrailError(format_json_error(
510 JSONError::InvalidInteger,
511 context,
512 )));
513 }
514
515 return Ok(JSONEntity::Literal {
516 value: format!("\"{}\"", context.content),
517 });
518 }
519
520 if let Some(input) = specs.get("enum") {
521 let array = input.clone();
522
523 if array.kind != InputKind::Array {
524 return Err(TrailError(format_json_error(
525 JSONError::InvalidArray,
526 array.context,
527 )));
528 }
529
530 let value: String = split_json_input(array)?
531 .into_iter()
532 .map(|label| {
533 let context = label.context;
534
535 if label.kind != InputKind::Integer {
536 Err(TrailError(format_json_error(
537 JSONError::InvalidInteger,
538 context,
539 )))
540 } else {
541 Ok(format!("\"{}\"", context.content))
542 }
543 })
544 .collect::<Result<Vec<String>, TrailError>>()?
545 .join(" | ");
546
547 return Ok(JSONEntity::Literal { value: value });
548 }
549
550 return Ok(JSONEntity::Literal {
551 value: String::from("/^[0-9]$/"),
552 });
553}
554
555pub fn build_string_from_specs(specs: &JSONSpecs) -> Result<JSONEntity, TrailError> {
556 if let Some(input) = specs.get("const") {
557 let string = input.clone();
558 let (context, kind) = (string.context, string.kind);
559
560 if kind != InputKind::String {
561 return Err(TrailError(format_json_error(
562 JSONError::InvalidString,
563 context,
564 )));
565 }
566
567 return Ok(JSONEntity::Literal {
568 value: format!(
569 "\"\\\"{}\\\"\"",
570 &context.content[1..context.content.len() - 1]
571 ),
572 });
573 }
574
575 if let Some(input) = specs.get("enum") {
576 let array = input.clone();
577
578 if array.kind != InputKind::Array {
579 return Err(TrailError(format_json_error(
580 JSONError::InvalidArray,
581 array.context,
582 )));
583 }
584
585 let value: String = split_json_input(array)?
586 .into_iter()
587 .map(|label| {
588 let context = label.context;
589
590 if label.kind != InputKind::String {
591 Err(TrailError(format_json_error(
592 JSONError::InvalidString,
593 context,
594 )))
595 } else {
596 let content = &context.content[1..context.content.len() - 1];
597
598 Ok(format!("\"\\\"{}\\\"\"", content))
599 }
600 })
601 .collect::<Result<Vec<String>, TrailError>>()?
602 .join(" | ");
603
604 return Ok(JSONEntity::Literal { value: value });
605 }
606
607 return Ok(JSONEntity::Literal {
608 value: String::from("/\"PLACEHOLDER\"/"),
609 });
610}
611
612type CFGGraph = HashMap<SymbolVar, SymbolGraph>;
615
616pub fn build_cfg_from_entity(entity: JSONEntity) -> CFGGraph {
617 let mut graphs: CFGGraph = HashMap::new();
618 let mut items: Vec<JSONBlock> = Vec::new();
619
620 items.push(JSONBlock {
621 id: SymbolVar::new("start"),
622 entity: Box::new(entity),
623 });
624
625 while !items.is_empty() {
626 let item = items.pop().unwrap();
627
628 match *item.entity {
629 JSONEntity::Object { properties } => {
630 let mut production = format!("\"{{\"");
631
632 let mut unions: Vec<String> = Vec::new();
633 let mut anchor = false;
634
635 for property in &properties {
636 let block = &property.block;
637
638 for union in &mut unions {
639 union.push_str(&format!(
640 "( \",\" \"\\\"{}\\\":\" {} )",
641 property.label, block.id.0,
642 ));
643 }
644
645 if property.required {
646 if unions.is_empty() {
647 unions.push(format!(
648 "( \"\\\"{}\\\":\" {} )",
649 property.label, block.id.0
650 ));
651 }
652
653 anchor = true;
654 } else {
655 for union in &mut unions {
656 union.push_str("?");
657 }
658
659 if !anchor {
660 unions.push(format!(
661 "( \"\\\"{}\\\":\" {} )",
662 property.label, block.id.0
663 ));
664 }
665 }
666
667 items.push(block.clone());
668 }
669
670 if !anchor {
672 unions.push(String::from("\"\""));
673 }
674
675 production.push_str(&format!("({})", unions.join("|")));
676
677 production.push_str(&format!("\"}}\""));
678
679 let graph = build_symbol_graph(&production)
680 .expect("Expected `SymbolGraph`, but got a `TrailError`.");
681
682 graphs.insert(item.id, graph);
683 }
684 JSONEntity::Array { blocks } => {
685 let mut production = String::from(" \"[\" ( ");
686
687 for block in &blocks[..blocks.len() - 1] {
688 production.push_str(&format!(" {} \",\" | ", block.id.0));
689 items.push(block.clone());
690 }
691
692 if let Some(last_block) = blocks.last() {
693 production.push_str(&format!(
694 " {} \",\" )* {} \"]\" ",
695 last_block.id.0, last_block.id.0
696 ));
697 items.push(last_block.clone());
698 }
699
700 let graph = build_symbol_graph(&production)
701 .expect("Expected `SymbolGraph`, but got a `TrailError`.");
702
703 graphs.insert(item.id, graph);
704 }
705 JSONEntity::Tuple { blocks } => {
706 let mut production = String::from(" \"[\" ");
707
708 for block in &blocks[0..blocks.len() - 1] {
709 production.push_str(&format!(" {} \",\" ", block.id.0));
710 items.push(block.clone());
711 }
712
713 if let Some(last_block) = blocks.last() {
714 production.push_str(&format!(" {} \"]\" ", last_block.id.0));
715 items.push(last_block.clone());
716 }
717
718 let graph = build_symbol_graph(&production)
719 .expect("Expected `SymbolGraph`, but got a `TrailError`.");
720
721 graphs.insert(item.id, graph);
722 }
723 JSONEntity::Union { blocks } => {
724 let mut production = String::from("(");
725
726 for block in &blocks[0..blocks.len() - 1] {
727 production.push_str(&format!(" {} | ", block.id.0));
728 items.push(block.clone());
729 }
730
731 if let Some(last_block) = blocks.last() {
732 production.push_str(&format!(" {} ) ", last_block.id.0));
733 items.push(last_block.clone());
734 }
735
736 let graph = build_symbol_graph(&production)
737 .expect("Expected `SymbolGraph`, but got a `TrailError`.");
738
739 graphs.insert(item.id, graph);
740 }
741 JSONEntity::Literal { value } => {
742 let graph = build_symbol_graph(&format!("{}", value))
743 .expect("Expected `SymbolGraph`, but got a `TrailError`.");
744
745 graphs.insert(item.id.clone(), graph);
746 }
747 }
748 }
749
750 return graphs;
751}
752
753pub fn trail_json<'a>(schema: &str) -> Result<Trail<'a>, TrailError> {
754 let input = build_json_input(schema.trim(), 0, String::from("~"))?;
755 let entity = build_entity_from_input(input)?;
756
757 return Ok((build_cfg_from_entity(entity), TrailState::default()));
758}
759
760pub fn asm_json<'a>(schema: &str, alphabet: Vec<String>) -> Result<ASM<'a>, TrailError> {
761 let input = build_json_input(schema.trim(), 0, String::new())?;
762 let entity = build_entity_from_input(input)?;
763
764 return Ok((
765 ASMSchema {
766 cfg: build_cfg_from_entity(entity),
767 asm: build_asm_graph(alphabet),
768 },
769 ASMState::default(),
770 ));
771}
772
773pub enum JSONError {
776 InvalidInput,
777 InvalidLabel,
778 InvalidEntry,
779 InvalidBlock,
780 InvalidKeyword,
781 InvalidNumber,
782 InvalidArray,
783 InvalidString,
784 InvalidInteger,
785 InvalidType,
786}
787
788impl JSONError {
789 pub const fn message(&self) -> &'static str {
790 match self {
791 Self::InvalidInput => "Invalid JSON input.",
792 Self::InvalidLabel => "Invalid JSON label.",
793 Self::InvalidKeyword => "Invalid JSON keyword.",
794 Self::InvalidBlock => "Invalid JSON block.",
795 Self::InvalidArray => "Invalid JSON array.",
796 Self::InvalidEntry => "Invalid JSON entry.",
797 Self::InvalidString => "Invalid JSON string.",
798 Self::InvalidNumber => "Invalid JSON number.",
799 Self::InvalidInteger => "Invalid JSON integer.",
800 Self::InvalidType => "Invalid JSON type.",
801 }
802 }
803
804 pub const fn help(&self) -> &'static str {
805 match self {
806 Self::InvalidInput => {
807 "Expected format: value ({}, [], \"string\", number) or \"key\" : value pair."
808 }
809 Self::InvalidLabel => "Expected format: \"name\" (alphanumeric + underscore only).",
810 Self::InvalidKeyword => {
811 "Valid keywords include: type, const, enum, properties, items, required, etc."
812 }
813 Self::InvalidBlock => {
814 "Expected format: {value}, where value is {object}, [array], \"string\", or number."
815 }
816 Self::InvalidArray => {
817 "Expected format: [value], where value is {object}, [array], \"string\", or number."
818 }
819 Self::InvalidEntry => {
820 "Expected format: \"key\" : value, where value is {object}, [array], \"string\", or number."
821 }
822 Self::InvalidString => {
823 "Expected format: \"name\". Inner quotes must be escaped - use \\\" instead of \"."
824 }
825 Self::InvalidNumber => "Expected a float number.",
826 Self::InvalidInteger => "Expected an integer number.",
827 Self::InvalidType => "Valid types include: object, array, number, string, ect.",
828 }
829 }
830}
831
832pub fn consume_input(
835 inputs: &mut Vec<JSONInput>,
836 accumulate: &mut Vec<char>,
837 line: usize,
838 path: String,
839) -> Result<(), TrailError> {
840 let content: String = accumulate.iter().collect();
841 let input = build_json_input(content.trim(), line.clone(), path.clone())?;
842
843 inputs.push(input);
844 accumulate.clear();
845
846 return Ok(());
847}
848
849pub fn build_json_input(content: &str, line: usize, path: String) -> Result<JSONInput, TrailError> {
850 let context = InputContext {
851 content: content.to_string(),
852 line: line,
853 path: path,
854 };
855
856 let kind = if is_valid_block(&content) {
857 InputKind::Block
858 } else if is_valid_array(&content) {
859 InputKind::Array
860 } else if is_valid_entry(&content) {
861 InputKind::Entry
862 } else if is_valid_string(&content) {
863 InputKind::String
864 } else if is_valid_integer(&content) {
865 InputKind::Integer
866 } else if is_valid_number(&content) {
867 InputKind::Number
868 } else {
869 return Err(TrailError(format_json_error(
870 JSONError::InvalidInput,
871 context,
872 )));
873 };
874
875 return Ok(JSONInput {
876 kind: kind,
877 context: context,
878 });
879}
880
881pub fn build_json_label(content: &str, line: usize, path: String) -> JSONLabel {
882 let context = InputContext {
883 content: content.to_string(),
884 line: line,
885 path: path,
886 };
887
888 let kind = if KEYWORDS.contains(content) {
889 LabelKind::Keyword
890 } else {
891 LabelKind::Property
892 };
893
894 return JSONLabel {
895 kind: kind,
896 context: context,
897 };
898}
899
900pub fn is_valid_block(input: &str) -> bool {
901 return input.starts_with('{') && input.ends_with('}');
902}
903pub fn is_valid_entry(input: &str) -> bool {
904 return input.contains(':');
905}
906
907pub fn is_valid_array(input: &str) -> bool {
908 return input.starts_with('[') && input.ends_with(']');
909}
910
911pub fn is_valid_string(input: &str) -> bool {
912 return input.starts_with('"')
913 && input.ends_with('"')
914 && input[1..input.len() - 1]
915 .split("\\\"")
916 .all(|part| !part.contains('"'));
917}
918
919pub fn is_valid_number(input: &str) -> bool {
920 return input.parse::<f64>().is_ok();
921}
922
923pub fn is_valid_integer(input: &str) -> bool {
924 return input.parse::<u64>().is_ok();
925}
926
927pub fn is_valid_whitespace(chars: &[char]) -> bool {
928 chars.iter().all(|c| c.is_whitespace())
929}
930
931pub fn format_json_instance(instance: &str) -> String {
932 let mut chunks: Vec<char> = Vec::new();
933 let mut depth = 0;
934 let mut in_quote = false;
935 let mut i = 0;
936
937 let chars: Vec<char> = instance.chars().collect();
938
939 while i < chars.len() {
940 let curr = chars[i];
941
942 assert!(
943 ![' ', '\n', '\t', '\r'].contains(&curr),
944 "There should be no whitespace characters."
945 );
946
947 if curr == '"' {
948 if !is_escaped(&chars, i) {
949 in_quote = !in_quote;
950 }
951
952 chunks.push(curr)
953 } else if matches!(curr, '{' | '[') {
954 chunks.extend([curr, '\n']);
955 depth += 1;
956 chunks.extend(iter::repeat('\t').take(depth))
957 } else if matches!(curr, '}' | ']') {
958 depth -= 1;
959 chunks.extend(
960 iter::once('\n')
961 .chain(iter::repeat('\t').take(depth))
962 .chain(iter::once(curr)),
963 );
964 } else if curr == ',' {
965 chunks.extend(
966 iter::once(curr)
967 .chain(iter::once('\n'))
968 .chain(iter::repeat('\t').take(depth)),
969 );
970 } else if curr == ':' {
971 chunks.extend([curr, ' '])
972 } else {
973 chunks.push(curr);
974 }
975
976 i += 1
977 }
978
979 return chunks.into_iter().collect();
980}
981
982pub fn format_json_error(error: JSONError, context: InputContext) -> String {
983 const RED: &str = "\x1b[31m";
984 const BLUE: &str = "\x1b[34m";
985 const CYAN: &str = "\x1b[36m";
986 const BOLD: &str = "\x1b[1m";
987 const RESET: &str = "\x1b[0m";
988
989 let (path, content, line) = (&context.path, &context.content, &context.line);
990 let (help, message) = (error.help(), error.message());
991
992 let context = content
993 .lines()
994 .enumerate()
995 .map(|(i, content)| format!("{BOLD}{BLUE}{:5} |{RESET} {}", line + i, content))
996 .collect::<Vec<_>>()
997 .join("\n");
998
999 format!(
1000 "{BOLD}{RED}error{RESET}: {message}\n\
1001 {BOLD}{BLUE} --> {RESET}{path}\n\
1002 {BOLD}{BLUE} |{RESET}\n\
1003 {context}\n\
1004 {BOLD}{BLUE} |{RESET}\n\
1005 {BOLD}{BLUE} = {BOLD}{CYAN}help{RESET}: {help}"
1006 )
1007}