Skip to content

Commit 8c8c996

Browse files
committed
Make AST generic over string values
This commit introduces a Text trait that is used for representing text data in the AST. This enables using the ast with &str references only, with Cow, or just as now in an owned fashion (with String). To prevent a wrapper type, the Text trait has an associated type 'Value' that represents the actual value and has to be used in the AST nodes.
1 parent a1fb575 commit 8c8c996

13 files changed

+670
-451
lines changed

src/common.rs

+70-45
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::collections::BTreeMap;
1+
use std::{fmt, collections::BTreeMap};
22

33
use combine::{parser, ParseResult, Parser};
44
use combine::easy::Error;
@@ -9,15 +9,29 @@ use tokenizer::{Kind as T, Token, TokenStream};
99
use helpers::{punct, ident, kind, name};
1010
use position::Pos;
1111

12+
/// Text abstracts over types that hold a string value.
13+
/// It is used to make the AST generic over the string type.
14+
pub trait Text<'a>: 'a {
15+
type Value: 'a + From<&'a str> + AsRef<str> + std::borrow::Borrow<str> + PartialEq + Eq + PartialOrd + Ord + fmt::Debug + Clone;
16+
}
17+
18+
impl<'a> Text<'a> for &'a str {
19+
type Value = Self;
20+
}
1221

13-
/// An alias for string, used where graphql expects a name
14-
pub type Name = String;
22+
impl<'a> Text<'a> for String {
23+
type Value = String;
24+
}
25+
26+
impl<'a> Text<'a> for std::borrow::Cow<'a, str> {
27+
type Value = Self;
28+
}
1529

1630
#[derive(Debug, Clone, PartialEq)]
17-
pub struct Directive {
31+
pub struct Directive<'a, T: Text<'a>> {
1832
pub position: Pos,
19-
pub name: Name,
20-
pub arguments: Vec<(Name, Value)>,
33+
pub name: T::Value,
34+
pub arguments: Vec<(T::Value, Value<'a, T>)>,
2135
}
2236

2337
/// This represents integer number
@@ -32,23 +46,23 @@ pub struct Directive {
3246
pub struct Number(pub(crate) i64);
3347

3448
#[derive(Debug, Clone, PartialEq)]
35-
pub enum Value {
36-
Variable(Name),
49+
pub enum Value<'a, T: Text<'a>> {
50+
Variable(T::Value),
3751
Int(Number),
3852
Float(f64),
3953
String(String),
4054
Boolean(bool),
4155
Null,
42-
Enum(Name),
43-
List(Vec<Value>),
44-
Object(BTreeMap<Name, Value>),
56+
Enum(T::Value),
57+
List(Vec<Value<'a, T>>),
58+
Object(BTreeMap<T::Value, Value<'a, T>>),
4559
}
4660

4761
#[derive(Debug, Clone, PartialEq)]
48-
pub enum Type {
49-
NamedType(Name),
50-
ListType(Box<Type>),
51-
NonNullType(Box<Type>),
62+
pub enum Type<'a, T: Text<'a>> {
63+
NamedType(T::Value),
64+
ListType(Box<Type<'a, T>>),
65+
NonNullType(Box<Type<'a, T>>),
5266
}
5367

5468
impl Number {
@@ -64,25 +78,27 @@ impl From<i32> for Number {
6478
}
6579
}
6680

67-
pub fn directives<'a>(input: &mut TokenStream<'a>)
68-
-> ParseResult<Vec<Directive>, TokenStream<'a>>
81+
pub fn directives<'a, T>(input: &mut TokenStream<'a>)
82+
-> ParseResult<Vec<Directive<'a, T>>, TokenStream<'a>>
83+
where T: Text<'a>,
6984
{
7085
many(position()
7186
.skip(punct("@"))
72-
.and(name())
87+
.and(name::<'a, T>())
7388
.and(parser(arguments))
7489
.map(|((position, name), arguments)| {
7590
Directive { position, name, arguments }
7691
}))
7792
.parse_stream(input)
7893
}
7994

80-
pub fn arguments<'a>(input: &mut TokenStream<'a>)
81-
-> ParseResult<Vec<(String, Value)>, TokenStream<'a>>
95+
pub fn arguments<'a, T>(input: &mut TokenStream<'a>)
96+
-> ParseResult<Vec<(T::Value, Value<'a, T>)>, TokenStream<'a>>
97+
where T: Text<'a>,
8298
{
8399
optional(
84100
punct("(")
85-
.with(many1(name()
101+
.with(many1(name::<'a, T>()
86102
.skip(punct(":"))
87103
.and(parser(value))))
88104
.skip(punct(")")))
@@ -92,27 +108,29 @@ pub fn arguments<'a>(input: &mut TokenStream<'a>)
92108
.parse_stream(input)
93109
}
94110

95-
pub fn int_value<'a>(input: &mut TokenStream<'a>)
96-
-> ParseResult<Value, TokenStream<'a>>
111+
pub fn int_value<'a, S>(input: &mut TokenStream<'a>)
112+
-> ParseResult<Value<'a, S>, TokenStream<'a>>
113+
where S: Text<'a>
97114
{
98115
kind(T::IntValue).and_then(|tok| tok.value.parse())
99116
.map(Number).map(Value::Int)
100117
.parse_stream(input)
101118
}
102119

103-
pub fn float_value<'a>(input: &mut TokenStream<'a>)
104-
-> ParseResult<Value, TokenStream<'a>>
120+
pub fn float_value<'a, S>(input: &mut TokenStream<'a>)
121+
-> ParseResult<Value<'a, S>, TokenStream<'a>>
122+
where S: Text<'a>
105123
{
106124
kind(T::FloatValue).and_then(|tok| tok.value.parse())
107125
.map(Value::Float)
108126
.parse_stream(input)
109127
}
110128

111-
fn unquote_block_string(src: &str) -> Result<String, Error<Token, Token>> {
129+
fn unquote_block_string<'a>(src: &'a str) -> Result<String, Error<Token<'a>, Token<'a>>> {
112130
debug_assert!(src.starts_with("\"\"\"") && src.ends_with("\"\"\""));
113131
let indent = src[3..src.len()-3].lines().skip(1)
114132
.filter_map(|line| {
115-
let trimmed = line.trim_left().len();
133+
let trimmed = line.trim_start().len();
116134
if trimmed > 0 {
117135
Some(line.len() - trimmed)
118136
} else {
@@ -144,7 +162,8 @@ fn unquote_block_string(src: &str) -> Result<String, Error<Token, Token>> {
144162
Ok(result)
145163
}
146164

147-
fn unquote_string(s: &str) -> Result<String, Error<Token, Token>> {
165+
fn unquote_string<'a>(s: &'a str) -> Result<String, Error<Token, Token>>
166+
{
148167
let mut res = String::with_capacity(s.len());
149168
debug_assert!(s.starts_with('"') && s.ends_with('"'));
150169
let mut chars = s[1..s.len()-1].chars();
@@ -183,67 +202,73 @@ pub fn string<'a>(input: &mut TokenStream<'a>)
183202
)).parse_stream(input)
184203
}
185204

186-
pub fn string_value<'a>(input: &mut TokenStream<'a>)
187-
-> ParseResult<Value, TokenStream<'a>>
205+
pub fn string_value<'a, S>(input: &mut TokenStream<'a>)
206+
-> ParseResult<Value<'a, S>, TokenStream<'a>>
207+
where S: Text<'a>,
188208
{
189209
kind(T::StringValue).and_then(|tok| unquote_string(tok.value))
190210
.map(Value::String)
191211
.parse_stream(input)
192212
}
193213

194-
pub fn block_string_value<'a>(input: &mut TokenStream<'a>)
195-
-> ParseResult<Value, TokenStream<'a>>
214+
pub fn block_string_value<'a, S>(input: &mut TokenStream<'a>)
215+
-> ParseResult<Value<'a, S>, TokenStream<'a>>
216+
where S: Text<'a>,
196217
{
197218
kind(T::BlockString).and_then(|tok| unquote_block_string(tok.value))
198219
.map(Value::String)
199220
.parse_stream(input)
200221
}
201222

202-
pub fn plain_value<'a>(input: &mut TokenStream<'a>)
203-
-> ParseResult<Value, TokenStream<'a>>
223+
pub fn plain_value<'a, T>(input: &mut TokenStream<'a>)
224+
-> ParseResult<Value<'a, T>, TokenStream<'a>>
225+
where T: Text<'a>,
204226
{
205227
ident("true").map(|_| Value::Boolean(true))
206228
.or(ident("false").map(|_| Value::Boolean(false)))
207229
.or(ident("null").map(|_| Value::Null))
208-
.or(name().map(Value::Enum))
230+
.or(name::<'a, T>().map(Value::Enum))
209231
.or(parser(int_value))
210232
.or(parser(float_value))
211233
.or(parser(string_value))
212234
.or(parser(block_string_value))
213235
.parse_stream(input)
214236
}
215237

216-
pub fn value<'a>(input: &mut TokenStream<'a>)
217-
-> ParseResult<Value, TokenStream<'a>>
238+
pub fn value<'a, T>(input: &mut TokenStream<'a>)
239+
-> ParseResult<Value<'a, T>, TokenStream<'a>>
240+
where T: Text<'a>,
218241
{
219242
parser(plain_value)
220-
.or(punct("$").with(name()).map(Value::Variable))
243+
.or(punct("$").with(name::<'a, T>()).map(Value::Variable))
221244
.or(punct("[").with(many(parser(value))).skip(punct("]"))
222245
.map(Value::List))
223246
.or(punct("{")
224-
.with(many(name().skip(punct(":")).and(parser(value))))
247+
.with(many(name::<'a, T>().skip(punct(":")).and(parser(value))))
225248
.skip(punct("}"))
226249
.map(Value::Object))
227250
.parse_stream(input)
228251
}
229252

230-
pub fn default_value<'a>(input: &mut TokenStream<'a>)
231-
-> ParseResult<Value, TokenStream<'a>>
253+
pub fn default_value<'a, T>(input: &mut TokenStream<'a>)
254+
-> ParseResult<Value<'a, T>, TokenStream<'a>>
255+
where T: Text<'a>,
232256
{
233257
parser(plain_value)
234258
.or(punct("[").with(many(parser(default_value))).skip(punct("]"))
235259
.map(Value::List))
236260
.or(punct("{")
237-
.with(many(name().skip(punct(":")).and(parser(default_value))))
261+
.with(many(name::<'a, T>().skip(punct(":")).and(parser(default_value))))
238262
.skip(punct("}"))
239263
.map(Value::Object))
240264
.parse_stream(input)
241265
}
242266

243-
pub fn parse_type<'a>(input: &mut TokenStream<'a>)
244-
-> ParseResult<Type, TokenStream<'a>>
267+
pub fn parse_type<'a, T>(input: &mut TokenStream<'a>)
268+
-> ParseResult<Type<'a, T>, TokenStream<'a>>
269+
where T: Text<'a>,
245270
{
246-
name().map(Type::NamedType)
271+
name::<'a, T>().map(Type::NamedType)
247272
.or(punct("[")
248273
.with(parser(parse_type))
249274
.skip(punct("]"))

src/format.rs

+15-1
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,9 @@ impl<'a> Formatter<'a> {
130130
}
131131
}
132132

133-
pub(crate) fn format_directives(dirs: &[Directive], f: &mut Formatter) {
133+
pub(crate) fn format_directives<'a, T>(dirs: &[Directive<'a, T>], f: &mut Formatter)
134+
where T: ::common::Text<'a>,
135+
{
134136
for dir in dirs {
135137
f.write(" ");
136138
dir.display(f);
@@ -147,4 +149,16 @@ macro_rules! impl_display {
147149
}
148150
)+
149151
};
152+
153+
('a $($typ: ident, )+) => {
154+
$(
155+
impl<'a, T> fmt::Display for $typ<'a, T>
156+
where T: Text<'a>,
157+
{
158+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
159+
f.write_str(&to_string(self))
160+
}
161+
}
162+
)+
163+
};
150164
}

src/helpers.rs

+16-8
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ use combine::stream::easy::{Error, Errors, Info};
77
use tokenizer::{TokenStream, Kind, Token};
88
use position::Pos;
99

10+
use super::common::{Text};
11+
1012

1113
#[derive(Debug, Clone)]
1214
pub struct TokenMatch<'a> {
@@ -15,8 +17,10 @@ pub struct TokenMatch<'a> {
1517
}
1618

1719
#[derive(Debug, Clone)]
18-
pub struct NameMatch<'a> {
19-
phantom: PhantomData<&'a u8>,
20+
pub struct NameMatch<'a, T>
21+
where T: Text<'a>
22+
{
23+
phantom: PhantomData<(&'a T)>,
2024
}
2125

2226
#[derive(Debug, Clone)]
@@ -34,7 +38,9 @@ pub fn kind<'x>(kind: Kind) -> TokenMatch<'x> {
3438
}
3539
}
3640

37-
pub fn name<'x>() -> NameMatch<'x> {
41+
pub fn name<'a, T>() -> NameMatch<'a, T>
42+
where T: Text<'a>
43+
{
3844
NameMatch {
3945
phantom: PhantomData,
4046
}
@@ -60,15 +66,15 @@ impl<'a> Parser for TokenMatch<'a> {
6066
}
6167
}
6268

63-
pub fn punct<'x>(value: &'static str) -> Value<'x> {
69+
pub fn punct<'s>(value: &'static str) -> Value<'s> {
6470
Value {
6571
kind: Kind::Punctuator,
6672
value: value,
6773
phantom: PhantomData,
6874
}
6975
}
7076

71-
pub fn ident<'x>(value: &'static str) -> Value<'x> {
77+
pub fn ident<'s>(value: &'static str) -> Value<'s> {
7278
Value {
7379
kind: Kind::Name,
7480
value: value,
@@ -97,17 +103,19 @@ impl<'a> Parser for Value<'a> {
97103
}
98104
}
99105

100-
impl<'a> Parser for NameMatch<'a> {
106+
impl<'a, S> Parser for NameMatch<'a, S>
107+
where S: Text<'a>,
108+
{
101109
type Input = TokenStream<'a>;
102-
type Output = String;
110+
type Output = S::Value;
103111
type PartialState = ();
104112

105113
#[inline]
106114
fn parse_lazy(&mut self, input: &mut Self::Input)
107115
-> ConsumedResult<Self::Output, Self::Input>
108116
{
109117
satisfy(|c: Token<'a>| c.kind == Kind::Name)
110-
.map(|t: Token<'a>| t.value.to_string())
118+
.map(|t: Token<'a>| -> S::Value { S::Value::from(t.value) } )
111119
.parse_lazy(input)
112120
}
113121

src/lib.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
//! use graphql_parser::parse_query;
2626
//!
2727
//! # fn parse() -> Result<(), failure::Error> {
28-
//! let ast = parse_query("query MyQuery { field1, field2 }")?;
28+
//! let ast = parse_query::<&str>("query MyQuery { field1, field2 }")?;
2929
//! // Format canonical representation
3030
//! assert_eq!(format!("{}", ast), "\
3131
//! query MyQuery {
@@ -49,7 +49,7 @@
4949
//! use graphql_parser::parse_schema;
5050
//!
5151
//! # fn parse() -> Result<(), failure::Error> {
52-
//! let ast = parse_schema(r#"
52+
//! let ast = parse_schema::<String>(r#"
5353
//! schema {
5454
//! query: Query
5555
//! }
@@ -64,7 +64,7 @@
6464
//! type User {
6565
//! name: String!,
6666
//! }
67-
//! "#)?;
67+
//! "#)?.to_owned();
6868
//! // Format canonical representation
6969
//! assert_eq!(format!("{}", ast), "\
7070
//! schema {

0 commit comments

Comments
 (0)