Skip to main content

graphql_schema_diff/
change.rs

1/// A change that represents a meaningful difference between the two schemas. Changes have a
2/// direction: from source to target. For example, if `kind` is `AddField`, it means the field does
3/// not exist in the `source` schema but it does exist in the `target` schema.
4#[derive(Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Debug)]
5#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
6pub struct Change {
7    // /!\ The order of fields matters for the PartialOrd derive /!\
8    /// Where the change happened in the schema. It is dot separated where relevant. For example if
9    /// the change happened in a field argument, the path will be something like
10    /// `ParentTypeName.fieldName.argumentName`.
11    ///
12    /// See [crate::path::Path] for the full documentation.
13    pub path: String,
14    /// The nature of the change.
15    pub kind: ChangeKind,
16    /// Contents depend on the change kind:
17    ///
18    /// - [ChangeQueryType]/[ChangeMutationType]/[ChangeSubscriptionType]: the new type
19    /// - [AddObjectType]/[AddUnion]/[AddEnum]/[AddScalar]/[AddInterface]/[AddInputObject]/[AddSchemaDefinition]/[AddDirectiveDefinition]: the whole definition.
20    /// - [AddInterfaceImplementation]/[RemoveInterfaceImplementation]: empty
21    /// - [ChangeFieldType]/[ChangeFieldArgumentType]: the new type
22    /// - [RemoveObjectType]/[RemoveField]/[RemoveUnion]/[RemoveUnionMember]/[RemoveEnum]/[RemoveScalar]/[RemoveInterface]/[RemoveDirectiveDefinition]/[RemoveSchemaDefinition]/[RemoveInputObject]/[RemoveFieldArgument]/[RemoveFieldArgumentDefault]: empty
23    /// - [AddField]: the whole field definition
24    /// - [AddUnionMember]: the union member added
25    /// - [AddEnumValue]/[RemoveEnumValue]: empty
26    /// - [AddFieldArgument]: the value of the argument, potentially with the default
27    /// - [AddFieldArgumentDefault]/[ChangeFieldArgumentDefault]: the default value of the argument
28    /// - [ChangeDescription]: the new description, or empty if the description was removed
29    pub span: Span,
30
31    /// The span of the corresponding content in the `source` schema, when there is meaningful
32    /// content there to point to. `None` otherwise, in particular for change kinds where the item
33    /// only exists in the `target` schema (e.g. [AddField]) or carries no content on either side
34    /// (e.g. [AddInterfaceImplementation]/[RemoveInterfaceImplementation]).
35    ///
36    /// Currently populated for:
37    ///
38    /// - [ChangeFieldType]/[ChangeFieldArgumentType]: the old type
39    /// - [ChangeFieldArgumentDefault]: the old default value of the argument
40    /// - [ChangeDescription]: the old description, or `None` if the description was added
41    /// - [RemoveField]/[RemoveEnumValue]/[RemoveUnionMember]: the removed field/value/member
42    /// - [RemoveFieldArgument]: the removed argument
43    /// - [RemoveFieldArgumentDefault]: the removed default value of the argument
44    /// - [RemoveSchemaDefinition]: the removed schema definition
45    /// - [RemoveDirective]: the removed directive usage
46    pub source_span: Option<Span>,
47}
48
49#[derive(Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Debug)]
50#[allow(missing_docs)]
51#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
52#[repr(u8)]
53pub enum ChangeKind {
54    // /!\ The order of variants matters for the PartialOrd derive /!\
55    ChangeQueryType,
56    ChangeMutationType,
57    ChangeSubscriptionType,
58    RemoveObjectType,
59    AddObjectType,
60    AddInterfaceImplementation,
61    RemoveInterfaceImplementation,
62    ChangeFieldType,
63    RemoveField,
64    AddField,
65    AddUnion,
66    RemoveUnion,
67    AddUnionMember,
68    RemoveUnionMember,
69    AddEnum,
70    RemoveEnum,
71    AddEnumValue,
72    RemoveEnumValue,
73    AddScalar,
74    RemoveScalar,
75    AddInterface,
76    RemoveInterface,
77    AddDirectiveDefinition,
78    RemoveDirectiveDefinition,
79    AddSchemaDefinition,
80    RemoveSchemaDefinition,
81    AddSchemaExtension,
82    RemoveSchemaExtension,
83    AddInputObject,
84    RemoveInputObject,
85    AddFieldArgument,
86    RemoveFieldArgument,
87    AddFieldArgumentDefault,
88    RemoveFieldArgumentDefault,
89    ChangeFieldArgumentDefault,
90    ChangeFieldArgumentType,
91    AddDirective,
92    RemoveDirective,
93    ChangeDescription,
94}
95
96impl ChangeKind {
97    /// The change kind as a string.
98    pub fn as_str(&self) -> &'static str {
99        use ChangeKind::*;
100
101        match self {
102            ChangeQueryType => "ChangeQueryType",
103            ChangeMutationType => "ChangeMutationType",
104            ChangeSubscriptionType => "ChangeSubscriptionType",
105            RemoveObjectType => "RemoveObjectType",
106            AddObjectType => "AddObjectType",
107            AddInterfaceImplementation => "AddInterfaceImplementation",
108            RemoveInterfaceImplementation => "RemoveInterfaceImplementation",
109            ChangeFieldType => "ChangeFieldType",
110            RemoveField => "RemoveField",
111            AddField => "AddField",
112            AddUnion => "AddUnion",
113            RemoveUnion => "RemoveUnion",
114            AddUnionMember => "AddUnionMember",
115            RemoveUnionMember => "RemoveUnionMember",
116            AddEnum => "AddEnum",
117            RemoveEnum => "RemoveEnum",
118            AddEnumValue => "AddEnumValue",
119            RemoveEnumValue => "RemoveEnumValue",
120            AddScalar => "AddScalar",
121            RemoveScalar => "RemoveScalar",
122            AddInterface => "AddInterface",
123            RemoveInterface => "RemoveInterface",
124            AddDirectiveDefinition => "AddDirectiveDefinition",
125            RemoveDirectiveDefinition => "RemoveDirectiveDefinition",
126            AddSchemaDefinition => "AddSchemaDefinition",
127            AddSchemaExtension => "AddSchemaExtension",
128            RemoveSchemaDefinition => "RemoveSchemaDefinition",
129            RemoveSchemaExtension => "RemoveSchemaExtension",
130            AddInputObject => "AddInputObject",
131            RemoveInputObject => "RemoveInputObject",
132            AddFieldArgument => "AddFieldArgument",
133            RemoveFieldArgument => "RemoveFieldArgument",
134            AddFieldArgumentDefault => "AddFieldArgumentDefault",
135            RemoveFieldArgumentDefault => "RemoveFieldArgumentDefault",
136            ChangeFieldArgumentDefault => "ChangeFieldArgumentDefault",
137            ChangeFieldArgumentType => "ChangeFieldArgumentType",
138            AddDirective => "AddDirective",
139            RemoveDirective => "RemoveDirective",
140            ChangeDescription => "ChangeDescription",
141        }
142    }
143}
144
145impl std::str::FromStr for ChangeKind {
146    type Err = ();
147
148    fn from_str(s: &str) -> Result<Self, Self::Err> {
149        Ok(match s {
150            "ChangeQueryType" => Self::ChangeQueryType,
151            "ChangeMutationType" => Self::ChangeMutationType,
152            "ChangeSubscriptionType" => Self::ChangeSubscriptionType,
153            "RemoveObjectType" => Self::RemoveObjectType,
154            "AddObjectType" => Self::AddObjectType,
155            "AddInterfaceImplementation" => Self::AddInterfaceImplementation,
156            "RemoveInterfaceImplementation" => Self::RemoveInterfaceImplementation,
157            "ChangeFieldType" => Self::ChangeFieldType,
158            "RemoveField" => Self::RemoveField,
159            "AddField" => Self::AddField,
160            "AddUnion" => Self::AddUnion,
161            "RemoveUnion" => Self::RemoveUnion,
162            "AddUnionMember" => Self::AddUnionMember,
163            "RemoveUnionMember" => Self::RemoveUnionMember,
164            "AddEnum" => Self::AddEnum,
165            "RemoveEnum" => Self::RemoveEnum,
166            "AddEnumValue" => Self::AddEnumValue,
167            "RemoveEnumValue" => Self::RemoveEnumValue,
168            "AddScalar" => Self::AddScalar,
169            "RemoveScalar" => Self::RemoveScalar,
170            "AddInterface" => Self::AddInterface,
171            "RemoveInterface" => Self::RemoveInterface,
172            "AddDirectiveDefinition" => Self::AddDirectiveDefinition,
173            "RemoveDirectiveDefinition" => Self::RemoveDirectiveDefinition,
174            "AddSchemaDefinition" => Self::AddSchemaDefinition,
175            "RemoveSchemaDefinition" => Self::RemoveSchemaDefinition,
176            "AddSchemaExtension" => Self::AddSchemaExtension,
177            "RemoveSchemaExtension" => Self::RemoveSchemaExtension,
178            "AddInputObject" => Self::AddInputObject,
179            "RemoveInputObject" => Self::RemoveInputObject,
180            "AddFieldArgument" => Self::AddFieldArgument,
181            "RemoveFieldArgument" => Self::RemoveFieldArgument,
182            "AddFieldArgumentDefault" => Self::AddFieldArgumentDefault,
183            "RemoveFieldArgumentDefault" => Self::RemoveFieldArgumentDefault,
184            "ChangeFieldArgumentDefault" => Self::ChangeFieldArgumentDefault,
185            "ChangeFieldArgumentType" => Self::ChangeFieldArgumentType,
186            "AddDirective" => Self::AddDirective,
187            "RemoveDirective" => Self::RemoveDirective,
188            "ChangeDescription" => Self::ChangeDescription,
189            _ => return Err(()),
190        })
191    }
192}
193
194/// A span in a source file.
195#[derive(Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Debug, Copy)]
196#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
197pub struct Span {
198    /// The byte offset where the span starts.
199    pub start: usize,
200    /// The byte offset where the span stops (exclusive).
201    pub end: usize,
202}
203
204impl Span {
205    /// Create a span from start and end.
206    pub fn new(start: usize, end: usize) -> Self {
207        Span { start, end }
208    }
209
210    pub(crate) const fn empty() -> Span {
211        Span { start: 0, end: 0 }
212    }
213}
214
215impl From<cynic_parser::Span> for Span {
216    fn from(cynic_parser::Span { start, end }: cynic_parser::Span) -> Self {
217        Span { start, end }
218    }
219}
220
221impl std::ops::Index<Span> for str {
222    type Output = str;
223
224    fn index(&self, Span { start, end }: Span) -> &Self::Output {
225        &self[start..end]
226    }
227}