use crate::execution::engine::PropagateNull;
use crate::execution::JsonMap;
use crate::node::NodeLocation;
use crate::SourceMap;
use serde::Deserialize;
use serde::Serialize;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Response {
#[serde(skip_serializing_if = "Vec::is_empty")]
#[serde(default)]
pub errors: Vec<GraphQLError>,
#[serde(skip_serializing_if = "ResponseData::is_absent")]
#[serde(default = "ResponseData::absent")]
pub data: ResponseData,
#[serde(skip_serializing_if = "JsonMap::is_empty")]
#[serde(default)]
pub extensions: JsonMap,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(from = "Option<JsonMap>")]
pub enum ResponseData {
Object(JsonMap),
Null,
Absent,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct GraphQLError {
pub message: String,
#[serde(skip_serializing_if = "Vec::is_empty")]
#[serde(default)]
pub locations: Vec<GraphQLLocation>,
#[serde(skip_serializing_if = "Vec::is_empty")]
#[serde(default)]
pub path: Vec<ResponseDataPathElement>,
#[serde(skip_serializing_if = "JsonMap::is_empty")]
#[serde(default)]
pub extensions: JsonMap,
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct GraphQLLocation {
pub line: usize,
pub column: usize,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ResponseDataPathElement {
Field(crate::ast::Name),
ListIndex(usize),
}
impl Response {
pub fn from_request_error(error: impl Into<GraphQLError>) -> Self {
Self {
errors: vec![error.into()],
data: ResponseData::Absent,
extensions: JsonMap::new(),
}
}
pub fn merge(mut self, mut other: Self) -> Self {
match (&mut self.data, other.data) {
(ResponseData::Absent, _) | (_, ResponseData::Absent) => {
self.data = ResponseData::Absent
}
(ResponseData::Null, _) | (_, ResponseData::Null) => {
self.data = ResponseData::Null
}
(ResponseData::Object(self_data), ResponseData::Object(other_data)) => {
self_data.extend(other_data)
}
}
self.errors.append(&mut other.errors);
self.extensions.extend(other.extensions);
self
}
}
impl GraphQLError {
pub fn new(
message: impl Into<String>,
location: Option<NodeLocation>,
sources: &SourceMap,
) -> Self {
Self {
message: message.into(),
locations: GraphQLLocation::from_node(sources, location)
.into_iter()
.collect(),
path: Default::default(),
extensions: Default::default(),
}
}
}
impl GraphQLLocation {
pub fn from_node(sources: &SourceMap, location: Option<NodeLocation>) -> Option<Self> {
let loc = location?;
let source = sources.get(&loc.file_id)?;
source
.get_line_column(loc.offset())
.map(|(line, column)| GraphQLLocation {
line: line + 1,
column: column + 1,
})
}
}
impl ResponseData {
fn is_absent(&self) -> bool {
matches!(self, Self::Absent)
}
fn absent() -> Self {
Self::Absent
}
}
impl Serialize for ResponseData {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self {
ResponseData::Absent => panic!("ResponseData::None should not be serialized"),
ResponseData::Null => serializer.serialize_unit(),
ResponseData::Object(map) => map.serialize(serializer),
}
}
}
impl From<Option<JsonMap>> for ResponseData {
fn from(value: Option<JsonMap>) -> Self {
match value {
Some(data) => Self::Object(data),
None => Self::Null,
}
}
}
impl From<Result<JsonMap, PropagateNull>> for ResponseData {
fn from(result: Result<JsonMap, PropagateNull>) -> Self {
match result {
Ok(data) => Self::Object(data),
Err(PropagateNull) => Self::Null,
}
}
}