Hi all,
I'm struggling to get a piece of code working which involves graphql_parser and stubborn lifetime issues. Graphql_parser takes a str reference and returns a document with references to the input &str, thus the corresponding document struct needs a lifetime.
It also supports using String copies so there's no requirement for the input string reference to live as long as the resulting document struct and the crate provides a function to convert the lifetime of such a document to 'static, into_static().
This caused all kinds of issues because the document is an element in a stuct of my own and 'static means it lives as long as the process so I replaced that bit with a similar call to hack the lifetime to the one of the struct, 'a.
I got most of it working but can't seem to get the final issue resolved in line 72 of the sample code. You'll need graphql-parser = "0.4.0"
in you Cargo dependencies section.
Thanks in advance for any insight!
--Christina
use std::io;
use std::fs;
use std::sync::Mutex;
use graphql_parser::schema;
use graphql_parser::query;
#[derive(Debug)]
struct Generator<'a, D> {
schema: Mutex<schema::Document<'a, String>>,
data: D
}
pub trait GeneratorTrait<D> {
fn new() -> D;
fn query<'b>(self: &Self, gen: &'b Generator<'b, D>, query: &query::Query<'b, &'b str>);
fn mutate<'b>(self: &Self, gen: &'b Generator<'b, D>, mutate: &query::Mutation<'b, &'b str>);
fn subscribe<'b>(self: &Self, gen: &'b Generator<'b, D>, subsribe: &query::Subscription<'b, &'b str>);
}
#[derive(Debug)]
struct GeneratorTestData {
}
pub type GeneratorTest<'a> = Generator<'a, GeneratorTestData>;
impl GeneratorTrait<GeneratorTestData> for GeneratorTestData {
fn new() -> Self {
GeneratorTestData {}
}
fn query<'b>(self: &Self, gen: &'b GeneratorTest, query: &query::Query<'b, &'b str>) { }
fn mutate<'b>(self: &Self, gen: &'b GeneratorTest, query: &query::Mutation<'b, &'b str>) { }
fn subscribe<'b>(self: &Self, gen: &'b GeneratorTest, query: &query::Subscription<'b, &'b str>) { }
}
impl<'a, D> Generator<'a, D> where D: GeneratorTrait<D>{
pub fn new(fname: &str) -> io::Result<Self> {
/* parse schema file into a document */
let s = fs::read_to_string(fname)?;
let schema: schema::Document<String> = match schema::parse_schema(&s) {
Ok(doc) => doc,
Err(why) => return io::Result::Err(io::Error::new(io::ErrorKind::InvalidData, why.to_string()))
};
/* Since we're using 'String' copies for the parser's AST elements, we can remove the lifetime
* dependency to the &str passed to 'parse_schema()'. The author of graphql_parser provides the
* function 'into_static()' for this purpose but we don't really need 'static, just living as
* long as the struct would be ebough for our purposes, so we'll hack this manually and avoid
* issues with rustc complaining that a struct member variable can't really be 'static further
* down.
*/
let schema = unsafe { std::mem::transmute::<_, schema::Document<'a, String>>(schema) };
/* return completed struct */
let this = Self {
schema: Mutex::new(schema),
data: D::new()
};
io::Result::Ok(this)
}
pub fn parse<'b>(self: &'b Self, query: &'b str) ->
Result<query::Document<'b, &'b str>, query::ParseError> {
query::parse_query(query)
}
pub fn query<'b>(self: &Self, query: &query::Document<'b, &'b str>) {
/* go through top-level operations in the given query */
for d in &query.definitions {
match d {
query::Definition::Operation(op) => {
match op {
query::OperationDefinition::SelectionSet(s) => (),
query::OperationDefinition::Query(q) => self.data.query(self, q),
query::OperationDefinition::Mutation(m) => self.data.mutate(self, m),
query::OperationDefinition::Subscription(s) => self.data.subscribe(self, s),
};
},
query::Definition::Fragment(f) => (), /* ignored for now */
}
}
}
}
fn main() -> std::io::Result<()> {
/* load schema */
let gen: GeneratorTest = match GeneratorTest::new("../assets/test_schema.graphql") {
Ok(g) => g,
Err(err) => {
println!("Failed to read GraphQL schema file: {}", err.to_string());
return Err(err);
}
};
println!("schema: {:#?}", gen);
println!("\n\n-------------------------------------------------------------\n");
/* load query */
let qs = fs::read_to_string("../assets/test_query.graphql")?;
let query = match gen.parse(&qs) {
Ok(doc) => doc,
Err(why) => {
println!("Failed to read GraphQL query file: {}", why.to_string());
return std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::InvalidData,
why.to_string()));
}
};
println!("query: {:#?}", query);
println!("\n\n-------------------------------------------------------------\n");
/* execute GraphQL query */
gen.query(&query);
Ok(())
}