use std::borrow::Cow;
use crate::ByteSpan;
use crate::ast;
pub(super) fn pos_from_span(
span: ByteSpan,
source_map: &crate::SourceMap<'_>,
) -> graphql_parser::Pos {
source_map
.resolve_offset(span.start)
.map(|p| graphql_parser::Pos {
line: p.line() + 1,
column: p.col_utf8() + 1,
})
.unwrap_or(graphql_parser::Pos {
line: 1,
column: 1,
})
}
pub(super) fn end_pos_from_span(
span: ByteSpan,
source_map: &crate::SourceMap<'_>,
) -> graphql_parser::Pos {
let end_inclusive =
span.end.saturating_sub(1);
source_map
.resolve_offset(end_inclusive)
.map(|p| graphql_parser::Pos {
line: p.line() + 1,
column: p.col_utf8() + 1,
})
.unwrap_or(graphql_parser::Pos {
line: 1,
column: 1,
})
}
pub(super) fn type_ext_pos_from_span(
span: ByteSpan,
source_map: &crate::SourceMap<'_>,
) -> graphql_parser::Pos {
let keyword_offset = source_map.source()
.map(|source| {
let start = (span.start as usize) + 6;
let rest = &source[start..];
let ws_len = rest.len() - rest.trim_start().len();
(start + ws_len) as u32
})
.unwrap_or(span.start + 7);
source_map
.resolve_offset(keyword_offset)
.map(|p| graphql_parser::Pos {
line: p.line() + 1,
column: p.col_utf8() + 1,
})
.unwrap_or(graphql_parser::Pos {
line: 1,
column: 1,
})
}
pub(crate) fn value_to_gp(
val: &ast::Value<'_>,
) -> graphql_parser::query::Value<'static, String> {
use graphql_parser::query::Value as GpValue;
match val {
ast::Value::Boolean(b) => {
GpValue::Boolean(b.value)
},
ast::Value::Enum(e) => {
GpValue::Enum(e.value.to_string())
},
ast::Value::Float(f) => {
GpValue::Float(f.value)
},
ast::Value::Int(i) => {
GpValue::Int(i.value.into())
},
ast::Value::List(l) => GpValue::List(
l.values.iter().map(value_to_gp).collect(),
),
ast::Value::Null(_) => GpValue::Null,
ast::Value::Object(o) => {
let mut map =
std::collections::BTreeMap::new();
for field in &o.fields {
map.insert(
field.name.value.to_string(),
value_to_gp(&field.value),
);
}
GpValue::Object(map)
},
ast::Value::String(s) => {
GpValue::String(s.value.to_string())
},
ast::Value::Variable(var) => {
GpValue::Variable(
var.name.value.to_string(),
)
},
}
}
pub(crate) fn type_annotation_to_gp(
ta: &ast::TypeAnnotation<'_>,
) -> graphql_parser::schema::Type<'static, String> {
use graphql_parser::schema::Type as GpType;
match ta {
ast::TypeAnnotation::Named(n) => {
let inner = GpType::NamedType(
n.name.value.to_string(),
);
match &n.nullability {
ast::Nullability::NonNull { .. } => {
GpType::NonNullType(Box::new(inner))
},
ast::Nullability::Nullable => inner,
}
},
ast::TypeAnnotation::List(l) => {
let inner = GpType::ListType(Box::new(
type_annotation_to_gp(&l.element_type),
));
match &l.nullability {
ast::Nullability::NonNull { .. } => {
GpType::NonNullType(Box::new(inner))
},
ast::Nullability::Nullable => inner,
}
},
}
}
pub(crate) fn directive_to_gp(
dir: &ast::DirectiveAnnotation<'_>,
source_map: &crate::SourceMap<'_>,
) -> graphql_parser::query::Directive<'static, String> {
graphql_parser::query::Directive {
position: pos_from_span(dir.span, source_map),
name: dir.name.value.to_string(),
arguments: dir
.arguments
.iter()
.map(|arg| {
(
arg.name.value.to_string(),
value_to_gp(&arg.value),
)
})
.collect(),
}
}
pub(super) fn directives_to_gp(
dirs: &[ast::DirectiveAnnotation<'_>],
source_map: &crate::SourceMap<'_>,
) -> Vec<
graphql_parser::query::Directive<'static, String>,
> {
dirs.iter()
.map(|d| directive_to_gp(d, source_map))
.collect()
}
pub(crate) fn description_to_gp(
desc: &Option<ast::StringValue<'_>>,
) -> Option<String> {
desc.as_ref().map(|s| s.value.to_string())
}
pub(crate) fn input_value_def_to_gp(
ivd: &ast::InputValueDefinition<'_>,
source_map: &crate::SourceMap<'_>,
) -> graphql_parser::schema::InputValue<'static, String>
{
graphql_parser::schema::InputValue {
position: match &ivd.description {
Some(desc) => {
pos_from_span(desc.span, source_map)
},
None => {
pos_from_span(ivd.span, source_map)
},
},
description: description_to_gp(
&ivd.description,
),
name: ivd.name.value.to_string(),
value_type: type_annotation_to_gp(
&ivd.value_type,
),
default_value: ivd
.default_value
.as_ref()
.map(value_to_gp),
directives: directives_to_gp(
&ivd.directives, source_map,
),
}
}
pub(crate) fn field_def_to_gp(
fd: &ast::FieldDefinition<'_>,
source_map: &crate::SourceMap<'_>,
) -> graphql_parser::schema::Field<'static, String> {
graphql_parser::schema::Field {
position: match &fd.description {
Some(desc) => {
pos_from_span(desc.span, source_map)
},
None => {
pos_from_span(fd.span, source_map)
},
},
description: description_to_gp(&fd.description),
name: fd.name.value.to_string(),
arguments: fd
.parameters
.iter()
.map(|ivd| {
input_value_def_to_gp(ivd, source_map)
})
.collect(),
field_type: type_annotation_to_gp(&fd.field_type),
directives: directives_to_gp(
&fd.directives, source_map,
),
}
}
pub(crate) fn enum_value_def_to_gp(
evd: &ast::EnumValueDefinition<'_>,
source_map: &crate::SourceMap<'_>,
) -> graphql_parser::schema::EnumValue<'static, String> {
graphql_parser::schema::EnumValue {
position: match &evd.description {
Some(desc) => {
pos_from_span(desc.span, source_map)
},
None => {
pos_from_span(evd.span, source_map)
},
},
description: description_to_gp(
&evd.description,
),
name: evd.name.value.to_string(),
directives: directives_to_gp(
&evd.directives, source_map,
),
}
}
pub(super) fn directive_location_to_gp(
kind: &ast::DirectiveLocationKind,
) -> graphql_parser::schema::DirectiveLocation {
use graphql_parser::schema::DirectiveLocation as Gp;
match kind {
ast::DirectiveLocationKind::ArgumentDefinition => {
Gp::ArgumentDefinition
},
ast::DirectiveLocationKind::Enum => Gp::Enum,
ast::DirectiveLocationKind::EnumValue => {
Gp::EnumValue
},
ast::DirectiveLocationKind::Field => Gp::Field,
ast::DirectiveLocationKind::FieldDefinition => {
Gp::FieldDefinition
},
ast::DirectiveLocationKind::FragmentDefinition => {
Gp::FragmentDefinition
},
ast::DirectiveLocationKind::FragmentSpread => {
Gp::FragmentSpread
},
ast::DirectiveLocationKind::InlineFragment => {
Gp::InlineFragment
},
ast::DirectiveLocationKind::InputFieldDefinition => {
Gp::InputFieldDefinition
},
ast::DirectiveLocationKind::InputObject => {
Gp::InputObject
},
ast::DirectiveLocationKind::Interface => {
Gp::Interface
},
ast::DirectiveLocationKind::Mutation => {
Gp::Mutation
},
ast::DirectiveLocationKind::Object => Gp::Object,
ast::DirectiveLocationKind::Query => Gp::Query,
ast::DirectiveLocationKind::Scalar => Gp::Scalar,
ast::DirectiveLocationKind::Schema => Gp::Schema,
ast::DirectiveLocationKind::Subscription => {
Gp::Subscription
},
ast::DirectiveLocationKind::Union => Gp::Union,
ast::DirectiveLocationKind::VariableDefinition => {
Gp::VariableDefinition
},
}
}
pub(super) struct FromGpContext<'src> {
source: Option<&'src str>,
line_starts: Vec<usize>,
}
impl<'src> FromGpContext<'src> {
pub(super) fn without_source() -> Self {
Self {
source: None,
line_starts: vec![],
}
}
pub(super) fn with_source(
source: &'src str,
) -> Self {
let bytes = source.as_bytes();
let len = bytes.len();
let mut line_starts = vec![0usize];
let mut i = 0;
while i < len {
match bytes[i] {
b'\n' => {
line_starts.push(i + 1);
},
b'\r' => {
if i + 1 < len
&& bytes[i + 1] == b'\n'
{
line_starts.push(i + 2);
i += 1; } else {
line_starts.push(i + 1);
}
},
_ => {},
}
i += 1;
}
Self {
source: Some(source),
line_starts,
}
}
fn byte_offset_for(
&self,
line: usize,
col_utf8: usize,
) -> usize {
match self.source {
Some(src)
if line < self.line_starts.len() =>
{
let line_start =
self.line_starts[line];
src[line_start..]
.char_indices()
.nth(col_utf8)
.map(|(off, _)| line_start + off)
.unwrap_or(line_start)
},
_ => 0,
}
}
pub(super) fn span_from_pos(
&self,
pos: graphql_parser::Pos,
) -> ByteSpan {
let byte_off = self.byte_offset_from_gp(pos);
ByteSpan::new(byte_off as u32, byte_off as u32)
}
pub(super) fn span_from_pos_pair(
&self,
start: graphql_parser::Pos,
end: graphql_parser::Pos,
) -> ByteSpan {
let end_line = end.line.saturating_sub(1);
let end_col_exclusive = end.column;
let end_byte_off = self
.byte_offset_for(end_line, end_col_exclusive);
let start_byte_off =
self.byte_offset_from_gp(start);
ByteSpan::new(
start_byte_off as u32,
end_byte_off as u32,
)
}
fn byte_offset_from_gp(
&self,
pos: graphql_parser::Pos,
) -> usize {
let line = pos.line.saturating_sub(1);
let col = pos.column.saturating_sub(1);
self.byte_offset_for(line, col)
}
pub(super) fn zero_span(
&self,
) -> ByteSpan {
ByteSpan::new(0, 0)
}
pub(super) fn string_to_name(
&self,
value: &str,
) -> ast::Name<'static> {
ast::Name {
span: self.zero_span(),
syntax: None,
value: Cow::Owned(value.to_owned()),
}
}
pub(super) fn string_to_name_at(
&self,
value: &str,
pos: graphql_parser::Pos,
) -> ast::Name<'static> {
ast::Name {
span: self.span_from_pos(pos),
syntax: None,
value: Cow::Owned(value.to_owned()),
}
}
}
pub(super) fn gp_description_to_ast(
desc: &Option<String>,
ctx: &FromGpContext<'_>,
) -> Option<ast::StringValue<'static>> {
desc.as_ref().map(|s| ast::StringValue {
is_block: false,
span: ctx.zero_span(),
syntax: None,
value: Cow::Owned(s.clone()),
})
}
pub(super) fn gp_value_to_ast(
val: &graphql_parser::query::Value<'static, String>,
ctx: &FromGpContext<'_>,
) -> ast::Value<'static> {
use graphql_parser::query::Value as GpValue;
let zs = ctx.zero_span();
match val {
GpValue::Boolean(b) => {
ast::Value::Boolean(ast::BooleanValue {
span: zs,
syntax: None,
value: *b,
})
},
GpValue::Enum(e) => {
ast::Value::Enum(ast::EnumValue {
span: zs,
syntax: None,
value: Cow::Owned(e.clone()),
})
},
GpValue::Float(f) => {
ast::Value::Float(ast::FloatValue {
span: zs,
syntax: None,
value: *f,
})
},
GpValue::Int(n) => {
let i64_val =
n.as_i64().unwrap_or(0);
ast::Value::Int(ast::IntValue {
span: zs,
syntax: None,
value: i64_val as i32,
})
},
GpValue::List(items) => {
ast::Value::List(ast::ListValue {
span: zs,
syntax: None,
values: items
.iter()
.map(|v| gp_value_to_ast(v, ctx))
.collect(),
})
},
GpValue::Null => {
ast::Value::Null(ast::NullValue {
span: zs,
syntax: None,
})
},
GpValue::Object(map) => {
ast::Value::Object(ast::ObjectValue {
fields: map
.iter()
.map(|(key, val)| {
ast::ObjectField {
name: ctx
.string_to_name(key),
span: ctx.zero_span(),
syntax: None,
value: gp_value_to_ast(
val, ctx,
),
}
})
.collect(),
span: zs,
syntax: None,
})
},
GpValue::String(s) => {
ast::Value::String(ast::StringValue {
is_block: false,
span: zs,
syntax: None,
value: Cow::Owned(s.clone()),
})
},
GpValue::Variable(name) => {
ast::Value::Variable(ast::VariableReference {
name: ctx.string_to_name(name),
span: zs,
syntax: None,
})
},
}
}
pub(super) fn gp_type_to_ast(
ty: &graphql_parser::schema::Type<'static, String>,
ctx: &FromGpContext<'_>,
) -> ast::TypeAnnotation<'static> {
use graphql_parser::schema::Type as GpType;
let zs = ctx.zero_span();
match ty {
GpType::NamedType(name) => {
ast::TypeAnnotation::Named(
ast::NamedTypeAnnotation {
name: ctx.string_to_name(name),
nullability:
ast::Nullability::Nullable,
span: zs,
},
)
},
GpType::ListType(inner) => {
ast::TypeAnnotation::List(
ast::ListTypeAnnotation {
element_type: Box::new(
gp_type_to_ast(inner, ctx),
),
nullability:
ast::Nullability::Nullable,
span: zs,
syntax: None,
},
)
},
GpType::NonNullType(inner) => {
match inner.as_ref() {
GpType::NamedType(name) => {
ast::TypeAnnotation::Named(
ast::NamedTypeAnnotation {
name: ctx
.string_to_name(name),
nullability:
ast::Nullability::NonNull {
syntax: None,
},
span: zs,
},
)
},
GpType::ListType(elem) => {
ast::TypeAnnotation::List(
ast::ListTypeAnnotation {
element_type: Box::new(
gp_type_to_ast(
elem, ctx,
),
),
nullability:
ast::Nullability::NonNull {
syntax: None,
},
span: zs,
syntax: None,
},
)
},
GpType::NonNullType(_) => {
gp_type_to_ast(inner, ctx)
},
}
},
}
}
fn gp_directive_to_ast(
dir: &graphql_parser::query::Directive<
'static,
String,
>,
ctx: &FromGpContext<'_>,
) -> ast::DirectiveAnnotation<'static> {
ast::DirectiveAnnotation {
name: ctx
.string_to_name_at(&dir.name, dir.position),
span: ctx.span_from_pos(dir.position),
syntax: None,
arguments: dir
.arguments
.iter()
.map(|(name, val)| ast::Argument {
name: ctx.string_to_name(name),
span: ctx.zero_span(),
syntax: None,
value: gp_value_to_ast(val, ctx),
})
.collect(),
}
}
pub(super) fn gp_directives_to_ast(
dirs: &[graphql_parser::query::Directive<
'static,
String,
>],
ctx: &FromGpContext<'_>,
) -> Vec<ast::DirectiveAnnotation<'static>> {
dirs.iter()
.map(|d| gp_directive_to_ast(d, ctx))
.collect()
}
pub(super) fn gp_input_value_to_ast(
iv: &graphql_parser::schema::InputValue<
'static,
String,
>,
ctx: &FromGpContext<'_>,
) -> ast::InputValueDefinition<'static> {
ast::InputValueDefinition {
default_value: iv
.default_value
.as_ref()
.map(|v| gp_value_to_ast(v, ctx)),
description: gp_description_to_ast(
&iv.description,
ctx,
),
directives: gp_directives_to_ast(
&iv.directives,
ctx,
),
name: ctx
.string_to_name_at(&iv.name, iv.position),
span: ctx.span_from_pos(iv.position),
syntax: None,
value_type: gp_type_to_ast(
&iv.value_type,
ctx,
),
}
}
pub(super) fn gp_field_def_to_ast(
field: &graphql_parser::schema::Field<
'static,
String,
>,
ctx: &FromGpContext<'_>,
) -> ast::FieldDefinition<'static> {
ast::FieldDefinition {
parameters: field
.arguments
.iter()
.map(|iv| gp_input_value_to_ast(iv, ctx))
.collect(),
description: gp_description_to_ast(
&field.description,
ctx,
),
directives: gp_directives_to_ast(
&field.directives,
ctx,
),
field_type: gp_type_to_ast(
&field.field_type,
ctx,
),
name: ctx.string_to_name_at(
&field.name,
field.position,
),
span: ctx.span_from_pos(field.position),
syntax: None,
}
}
pub(super) fn gp_enum_value_to_ast(
ev: &graphql_parser::schema::EnumValue<
'static,
String,
>,
ctx: &FromGpContext<'_>,
) -> ast::EnumValueDefinition<'static> {
ast::EnumValueDefinition {
description: gp_description_to_ast(
&ev.description,
ctx,
),
directives: gp_directives_to_ast(
&ev.directives,
ctx,
),
name: ctx
.string_to_name_at(&ev.name, ev.position),
span: ctx.span_from_pos(ev.position),
}
}
pub(super) fn gp_directive_location_to_ast(
loc: &graphql_parser::schema::DirectiveLocation,
) -> ast::DirectiveLocationKind {
use graphql_parser::schema::DirectiveLocation as Gp;
match loc {
Gp::ArgumentDefinition => {
ast::DirectiveLocationKind::ArgumentDefinition
},
Gp::Enum => {
ast::DirectiveLocationKind::Enum
},
Gp::EnumValue => {
ast::DirectiveLocationKind::EnumValue
},
Gp::Field => {
ast::DirectiveLocationKind::Field
},
Gp::FieldDefinition => {
ast::DirectiveLocationKind::FieldDefinition
},
Gp::FragmentDefinition => {
ast::DirectiveLocationKind::FragmentDefinition
},
Gp::FragmentSpread => {
ast::DirectiveLocationKind::FragmentSpread
},
Gp::InlineFragment => {
ast::DirectiveLocationKind::InlineFragment
},
Gp::InputFieldDefinition => {
ast::DirectiveLocationKind::InputFieldDefinition
},
Gp::InputObject => {
ast::DirectiveLocationKind::InputObject
},
Gp::Interface => {
ast::DirectiveLocationKind::Interface
},
Gp::Mutation => {
ast::DirectiveLocationKind::Mutation
},
Gp::Object => {
ast::DirectiveLocationKind::Object
},
Gp::Query => {
ast::DirectiveLocationKind::Query
},
Gp::Scalar => {
ast::DirectiveLocationKind::Scalar
},
Gp::Schema => {
ast::DirectiveLocationKind::Schema
},
Gp::Subscription => {
ast::DirectiveLocationKind::Subscription
},
Gp::Union => {
ast::DirectiveLocationKind::Union
},
Gp::VariableDefinition => {
ast::DirectiveLocationKind::VariableDefinition
},
}
}