1mod display;
4mod parse;
5
6use std::fmt;
7
8type ParseResult<T> = Result<T, ParseError>;
9
10#[derive(Debug)]
11pub struct ParseError;
12
13#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
37pub enum Path<'a> {
38 SchemaDefinition,
39 SchemaExtension(usize),
40 TypeDefinition(&'a str, Option<PathInType<'a>>),
41 TypeExtension(&'a str, usize, Option<PathInType<'a>>),
42 DirectiveDefinition(&'a str),
43}
44
45#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
46pub enum PathInType<'a> {
47 InField(&'a str, Option<PathInField<'a>>),
48 InDirective(&'a str, usize),
49 InterfaceImplementation(&'a str),
50}
51
52#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
53pub enum PathInField<'a> {
54 InArgument(&'a str, Option<PathInArgument<'a>>),
55 InDirective(&'a str, usize),
56}
57
58#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
59pub enum PathInArgument<'a> {
60 InDirective(&'a str, usize),
61}
62
63fn is_valid_graphql_name(s: &str) -> bool {
64 let mut chars = s.chars();
65
66 let Some(first_char) = chars.next() else {
67 return false;
68 };
69
70 if !first_char.is_ascii_alphabetic() && first_char != '_' {
71 return false;
72 }
73
74 for c in chars {
75 if !c.is_ascii_alphanumeric() && c != '_' {
76 return false;
77 }
78 }
79
80 true
81}
82
83#[cfg(test)]
84mod tests {
85 #![allow(clippy::panic)]
86
87 use super::*;
88
89 #[test]
90 fn error_tests() {
91 fn expect_error(path: &str) {
92 match Path::parse(path) {
93 Err(_) => (),
94 Ok(found) => panic!("Expected error for path: {path}, got: {found:?}"),
95 }
96 }
97
98 for case in [
99 "",
100 "s:",
101 "s:meow",
102 ":schema[-1]",
103 ":schema.something",
104 ":s",
105 ":s[1]",
106 ":something",
107 "t:something",
108 "something.:s",
109 "10",
110 "test.",
111 "test[10].",
112 "test.@siblings.",
113 "myObject._abc1.@requires",
115 "_my_object.@key",
116 "@",
118 "_my_object.@",
119 "@test[0]",
121 "myObject.&MyInterface.a",
122 "myObject.&MyInterface.",
123 "myObject._abc1.arg1.",
125 "myObject._abc1.arg1.something",
126 "myObject._abc1.arg1.@requires",
127 ] {
128 expect_error(case);
129 }
130 }
131
132 #[test]
133 fn roundtrip_tests() {
134 fn test(path: &str) {
135 let Ok(parsed) = Path::parse(path) else {
136 panic!("Failed to parse path: {path}")
137 };
138 let formatted = parsed.to_string();
139 assert_eq!(path, formatted);
140 }
141
142 for case in [
143 ":schema",
144 ":schema[0]",
145 ":schema[1]",
146 ":schema[100]",
147 "@meow",
148 "@deprecated",
149 "@something__else",
150 "@join__type",
151 "my_union",
152 "__my_input_object[32]",
153 "_my_object.id",
154 "_my_object.@key[0]",
155 "myObject[10].id",
156 "myObject.@join__type[0]",
157 "myObject._abc1",
158 "myObject._abc1.@requires[0]",
159 "myObject._abc1.@requires[100]",
160 "myObject._abc1.arg1",
161 "myObject._abc1.arg1.@deprecated[0]",
162 "myObject.&MyInterface",
163 ] {
164 test(case);
165 }
166 }
167}