toon/shared/
validation.rs1use crate::shared::constants::{DEFAULT_DELIMITER, LIST_ITEM_MARKER};
2use crate::shared::literal_utils::{is_boolean_or_null_literal, is_numeric_like};
3
4#[must_use]
5pub fn is_valid_unquoted_key(key: &str) -> bool {
6 let mut chars = key.chars();
7 let Some(first) = chars.next() else {
8 return false;
9 };
10
11 if !(first.is_ascii_alphabetic() || first == '_') {
12 return false;
13 }
14
15 for ch in chars {
16 if ch.is_ascii_alphanumeric() || ch == '_' || ch == '.' {
17 continue;
18 }
19 return false;
20 }
21
22 true
23}
24
25#[must_use]
26pub fn is_identifier_segment(segment: &str) -> bool {
27 let mut chars = segment.chars();
28 let Some(first) = chars.next() else {
29 return false;
30 };
31
32 if !(first.is_ascii_alphabetic() || first == '_') {
33 return false;
34 }
35
36 for ch in chars {
37 if ch.is_ascii_alphanumeric() || ch == '_' {
38 continue;
39 }
40 return false;
41 }
42
43 true
44}
45
46#[must_use]
47pub fn is_safe_unquoted(value: &str, delimiter: char) -> bool {
48 if value.is_empty() {
49 return false;
50 }
51
52 if value.trim() != value {
53 return false;
54 }
55
56 if is_boolean_or_null_literal(value) || is_numeric_like(value) {
57 return false;
58 }
59
60 if value.contains(':') {
61 return false;
62 }
63
64 if value.contains('"') || value.contains('\\') {
65 return false;
66 }
67
68 if value.contains('[') || value.contains(']') || value.contains('{') || value.contains('}') {
69 return false;
70 }
71
72 if value.contains('\n') || value.contains('\r') || value.contains('\t') {
73 return false;
74 }
75
76 if value.contains(delimiter) {
77 return false;
78 }
79
80 if value.starts_with(LIST_ITEM_MARKER) {
81 return false;
82 }
83
84 true
85}
86
87#[must_use]
88pub const fn default_delimiter() -> char {
89 DEFAULT_DELIMITER
90}