graphql_schema_diff/
path.rs1mod 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),
55 InDirective(&'a str, usize),
56}
57
58fn is_valid_graphql_name(s: &str) -> bool {
59 let mut chars = s.chars();
60
61 let Some(first_char) = chars.next() else {
62 return false;
63 };
64
65 if !first_char.is_ascii_alphabetic() && first_char != '_' {
66 return false;
67 }
68
69 for c in chars {
70 if !c.is_ascii_alphanumeric() && c != '_' {
71 return false;
72 }
73 }
74
75 true
76}
77
78#[cfg(test)]
79mod tests {
80 #![allow(clippy::panic)]
81
82 use super::*;
83
84 #[test]
85 fn error_tests() {
86 fn expect_error(path: &str) {
87 match Path::parse(path) {
88 Err(_) => (),
89 Ok(found) => panic!("Expected error for path: {path}, got: {found:?}"),
90 }
91 }
92
93 for case in [
94 "",
95 "s:",
96 "s:meow",
97 ":schema[-1]",
98 ":schema.something",
99 ":s",
100 ":s[1]",
101 ":something",
102 "t:something",
103 "something.:s",
104 "10",
105 "test.",
106 "test[10].",
107 "test.@siblings.",
108 "myObject._abc1.@requires",
110 "_my_object.@key",
111 "@",
113 "_my_object.@",
114 "@test[0]",
116 "myObject.&MyInterface.a",
117 "myObject.&MyInterface.",
118 ] {
119 expect_error(case);
120 }
121 }
122
123 #[test]
124 fn roundtrip_tests() {
125 fn test(path: &str) {
126 let Ok(parsed) = Path::parse(path) else {
127 panic!("Failed to parse path: {path}")
128 };
129 let formatted = parsed.to_string();
130 assert_eq!(path, formatted);
131 }
132
133 for case in [
134 ":schema",
135 ":schema[0]",
136 ":schema[1]",
137 ":schema[100]",
138 "@meow",
139 "@deprecated",
140 "@something__else",
141 "@join__type",
142 "my_union",
143 "__my_input_object[32]",
144 "_my_object.id",
145 "_my_object.@key[0]",
146 "myObject[10].id",
147 "myObject.@join__type[0]",
148 "myObject._abc1",
149 "myObject._abc1.@requires[0]",
150 "myObject._abc1.@requires[100]",
151 "myObject._abc1.arg1",
152 "myObject.&MyInterface",
153 ] {
154 test(case);
155 }
156 }
157}