Skip to main content

lextrail_test/
json.rs

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// ============================ ENTITIES ============================
175
176#[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
612// ============================ CFG ============================
613
614type 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                // All fields are optional.
671                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
773// ============================ ERRORS ============================
774
775pub 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
832// ============================ HELPERS ============================
833
834pub 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}