graphql_federated_graph/federated_graph/directives/
link.rs1use std::collections::HashMap;
2
3use cynic_parser_deser::{ConstDeserializer, ValueDeserialize};
4use serde::Deserialize;
5
6#[derive(Debug)]
15pub struct LinkDirective<'a> {
16 pub url: &'a str,
17 pub r#as: Option<&'a str>,
18 pub import: Option<Vec<Import<'a>>>,
19 pub r#for: Option<Purpose>,
20}
21
22impl<'a> ValueDeserialize<'a> for LinkDirective<'a> {
23 fn deserialize(input: cynic_parser_deser::DeserValue<'a>) -> Result<Self, cynic_parser_deser::Error> {
24 let fields = input
25 .as_object()
26 .ok_or_else(|| cynic_parser_deser::Error::custom("Bad link directive", input.span()))?;
27
28 let mut url = None;
29 let mut r#as = None;
30 let mut import = None;
31 let mut r#for = None;
32
33 for field in fields {
34 match field.name() {
35 "url" => {
36 url = Some(field.value().as_str().ok_or_else(|| {
37 cynic_parser_deser::Error::custom("Bad `url` argument in `@link` directive", field.name_span())
38 })?)
39 }
40 "as" => {
41 r#as = Some(field.value().as_str().ok_or_else(|| {
42 cynic_parser_deser::Error::custom("Bad `as` argument in `@link` directive", field.name_span())
43 })?)
44 }
45 "for" => r#for = Some(field.value().deserialize()?),
46 "import" => import = Some(field.value().deserialize()?),
47 other => {
48 return Err(cynic_parser_deser::Error::custom(
49 format!("Unknown argument `{}` in `@link` directive", other),
50 field.name_span(),
51 ));
52 }
53 }
54 }
55
56 let Some(url) = url else {
57 return Err(cynic_parser_deser::Error::custom(
58 "Missing `url` argument in `@link` directive",
59 input.span(),
60 ));
61 };
62
63 Ok(LinkDirective {
64 url,
65 r#as,
66 import,
67 r#for,
68 })
69 }
70}
71
72#[derive(Debug, Deserialize)]
73pub enum Purpose {
74 Security,
75 Execution,
76}
77
78impl<'a> ValueDeserialize<'a> for Purpose {
79 fn deserialize(input: cynic_parser_deser::DeserValue<'a>) -> Result<Self, cynic_parser_deser::Error> {
80 let str: &str = input.deserialize()?;
81
82 match str {
83 "SECURITY" => Ok(Purpose::Security),
84 "EXECUTION" => Ok(Purpose::Execution),
85 _ => Err(cynic_parser_deser::Error::custom("Bad purpose", input.span())),
86 }
87 }
88}
89
90#[derive(Debug)]
91pub enum Import<'a> {
92 String(&'a str),
93 Qualified(QualifiedImport<'a>),
94}
95
96#[derive(Debug)]
97pub struct QualifiedImport<'a> {
98 pub name: &'a str,
99 pub r#as: Option<&'a str>,
100}
101
102impl<'a> ValueDeserialize<'a> for QualifiedImport<'a> {
103 fn deserialize(input: cynic_parser_deser::DeserValue<'a>) -> Result<Self, cynic_parser_deser::Error> {
104 let Some(object) = input.as_object() else {
105 return Err(cynic_parser_deser::Error::Custom {
106 text: "Bad import".to_owned(),
107 span: input.span(),
108 });
109 };
110
111 let mut fields: HashMap<&str, _> = object.fields().map(|field| (field.name(), field)).collect();
112
113 if fields.len() > 2 {
114 return Err(cynic_parser_deser::Error::Custom {
115 text: "Bad import".to_owned(),
116 span: input.span(),
117 });
118 }
119
120 let Some(name) = fields.remove("name").and_then(|field| field.value().as_str()) else {
121 return Err(cynic_parser_deser::Error::Custom {
122 text: "Bad import".to_owned(),
123 span: input.span(),
124 });
125 };
126
127 let r#as = fields
128 .remove("as")
129 .map(|alias| {
130 alias
131 .value()
132 .as_str()
133 .ok_or_else(|| cynic_parser_deser::Error::custom("Bad import", input.span()))
134 })
135 .transpose()?;
136
137 Ok(QualifiedImport { name, r#as })
138 }
139}
140
141impl<'a> ValueDeserialize<'a> for Import<'a> {
142 fn deserialize(input: cynic_parser_deser::DeserValue<'a>) -> Result<Self, cynic_parser_deser::Error> {
143 if let Some(string) = input.as_str() {
144 return Ok(Import::String(string));
145 }
146
147 if input.as_object().is_some() {
148 return Ok(Import::Qualified(input.deserialize()?));
149 }
150
151 Err(cynic_parser_deser::Error::custom("Bad import", input.span()))
152 }
153}