graphql-idl-parser 0.1.1

A parser for the GraphQL IDL format.
Documentation
use std::fs::File;
use std::io::Read;

use gqlidl;

#[test]
#[allow(unused)]
fn sanity_check() {
    let mut array = [
        "scalars",
        "objects",
        "enums",
        "interfaces",
        "unions",
        "input_objects",
    ];
    let mut contents = String::new();
    for (_, f) in array.iter_mut().enumerate() {
        let mut file = File::open(format!("test/{}.graphql", f)).expect("Unable to open file");
        file.read_to_string(&mut contents);
    }
    let definitions = gqlidl::parse_schema(contents.as_str()).unwrap();
}

#[test]
#[allow(unused)]
fn github_sanity_check() {
    let mut file = File::open("test/github.graphql").expect("Unable to open file");
    let mut contents = String::new();
    file.read_to_string(&mut contents);

    let definitions = gqlidl::parse_schema(contents.as_str()).unwrap();
}

#[test]
fn scalar_no_description() {
    let def = gqlidl::parse_schema("scalar DateTime")
        .unwrap()
        .pop()
        .unwrap();

    assert_eq!(None, def.description());
    assert_eq!("scalar", def.typename());
    assert_eq!("DateTime", def.name());
}

#[test]
fn scalar_with_description() {
    let def = gqlidl::parse_schema("# An ISO-8601 encoded UTC date string.\nscalar DateTime")
        .unwrap()
        .pop()
        .unwrap();

    assert_eq!(
        "An ISO-8601 encoded UTC date string.",
        def.description().unwrap()
    );
    assert_eq!("scalar", def.typename());
    assert_eq!("DateTime", def.name());
}

#[test]
fn scalar_with_tricky_description() {
    let def = gqlidl::parse_schema(
        "# An ISO-8601 encoded UTC, scalar, date string.\nscalar DateTime",
    ).unwrap()
        .pop()
        .unwrap();

    assert_eq!(
        "An ISO-8601 encoded UTC, scalar, date string.",
        def.description().unwrap()
    );
    assert_eq!("scalar", def.typename());
    assert_eq!("DateTime", def.name());
}

#[test]
fn type_no_description() {
    let def = gqlidl::parse_schema("type CodeOfConduct {}")
        .unwrap()
        .pop()
        .unwrap();

    assert_eq!(None, def.description());
    assert_eq!("object", def.typename());
    assert_eq!("CodeOfConduct", def.name());
}

#[test]
fn type_with_description() {
    let def = gqlidl::parse_schema(
        "# The Code of Conduct for a repository\ntype CodeOfConduct {}",
    ).unwrap()
        .pop()
        .unwrap();

    assert_eq!(
        "The Code of Conduct for a repository",
        def.description().unwrap()
    );
    assert_eq!("object", def.typename());
    assert_eq!("CodeOfConduct", def.name());
    assert_eq!(None, def.implements());
}

#[test]
fn type_with_one_implements() {
    let def = gqlidl::parse_schema("type PushAllowance implements Node {}")
        .unwrap()
        .pop()
        .unwrap();

    assert_eq!(None, def.description());
    assert_eq!("object", def.typename());
    assert_eq!("PushAllowance", def.name());

    let implement = def.implements().unwrap().pop().unwrap();

    assert_eq!("Node", implement);
}

#[test]
fn type_with_multiple_implements() {
    let def = gqlidl::parse_schema("type Release implements Node, UniformResourceLocatable {}")
        .unwrap()
        .pop()
        .unwrap();

    assert_eq!(None, def.description());
    assert_eq!("object", def.typename());
    assert_eq!("Release", def.name());

    let mut implement = def.implements().unwrap().remove(0);

    assert_eq!("Node", implement);
    implement = def.implements().unwrap().remove(1);
    assert_eq!("UniformResourceLocatable", implement);
}

#[test]
fn type_with_field() {
    let def = gqlidl::parse_schema(
        "# The Code of Conduct for a repository\ntype CodeOfConduct { body: String }",
    ).unwrap()
        .pop()
        .unwrap();

    assert_eq!(
        "The Code of Conduct for a repository",
        def.description().unwrap()
    );
    assert_eq!("object", def.typename());
    assert_eq!("CodeOfConduct", def.name());

    let field = def.fields().unwrap().pop().unwrap();

    assert_eq!(None, field.description());
    assert_eq!("body", field.name());
    assert_eq!("String", field.typeinfo().name());
    assert_eq!("", field.typeinfo().info());
    assert_eq!(None, field.arguments());
}

#[test]
fn type_with_field_and_description() {
    let def = gqlidl::parse_schema(
        "# The Code of Conduct for a repository\ntype CodeOfConduct { \n# The body of the CoC\n body: String }",
    ).unwrap()
        .pop()
        .unwrap();

    assert_eq!(
        "The Code of Conduct for a repository",
        def.description().unwrap()
    );
    assert_eq!("object", def.typename());
    assert_eq!("CodeOfConduct", def.name());

    let field = def.fields().unwrap().pop().unwrap();

    assert_eq!("The body of the CoC", field.description().unwrap());
    assert_eq!("body", field.name());
    assert_eq!("String", field.typeinfo().name());
    assert_eq!("", field.typeinfo().info());
}

#[test]
fn type_with_required_field() {
    let def = gqlidl::parse_schema(
        "# The Code of Conduct for a repository\ntype CodeOfConduct { key: String! }",
    ).unwrap()
        .pop()
        .unwrap();

    assert_eq!(
        "The Code of Conduct for a repository",
        def.description().unwrap()
    );
    assert_eq!("object", def.typename());
    assert_eq!("CodeOfConduct", def.name());

    let field = def.fields().unwrap().pop().unwrap();

    assert_eq!(None, field.description());
    assert_eq!("key", field.name());
    assert_eq!("String", field.typeinfo().name());
    assert_eq!("!", field.typeinfo().info());
}

#[test]
fn type_with_nullable_field_list() {
    let def = gqlidl::parse_schema(
        "type CommitCommentConnection { edges: [CommitCommentEdge] }",
    ).unwrap()
        .pop()
        .unwrap();

    assert_eq!(None, def.description());
    assert_eq!("object", def.typename());
    assert_eq!("CommitCommentConnection", def.name());

    let field = def.fields().unwrap().pop().unwrap();

    assert_eq!(None, field.description());
    assert_eq!("edges", field.name());
    assert_eq!("CommitCommentEdge", field.typeinfo().name());
    assert_eq!("[]", field.typeinfo().info());
}

#[test]
fn type_with_non_nullable_field_non_nullable_list() {
    let def = gqlidl::parse_schema(
        "type CommitComment { viewerCannotUpdateReasons: [CommentCannotUpdateReason!]! }",
    ).unwrap()
        .pop()
        .unwrap();

    assert_eq!(None, def.description());
    assert_eq!("object", def.typename());
    assert_eq!("CommitComment", def.name());

    let field = def.fields().unwrap().pop().unwrap();

    assert_eq!(None, field.description());
    assert_eq!("viewerCannotUpdateReasons", field.name());
    assert_eq!("CommentCannotUpdateReason", field.typeinfo().name());
    assert_eq!("[!]!", field.typeinfo().info());
}

#[test]
fn type_with_nullable_field_non_nullable_list() {
    let def = gqlidl::parse_schema(
        "type PullRequest { suggestedReviewers: [SuggestedReviewer]! }",
    ).unwrap()
        .pop()
        .unwrap();

    assert_eq!(None, def.description());
    assert_eq!("object", def.typename());
    assert_eq!("PullRequest", def.name());

    let field = def.fields().unwrap().pop().unwrap();

    assert_eq!(None, field.description());
    assert_eq!("suggestedReviewers", field.name());
    assert_eq!("SuggestedReviewer", field.typeinfo().name());
    assert_eq!("[]!", field.typeinfo().info());
}

#[test]
fn type_with_non_nullable_connection() {
    let def = gqlidl::parse_schema(
        "type User {
        # A list of users the given user is followed by.
        followers(
          # Returns the elements in the list that come after the specified global ID.
          after: String

          # Returns the first _n_ elements from the list.
          first: Int!
        ): FollowerConnection!
    }
    ",
    ).unwrap()
        .pop()
        .unwrap();

    assert_eq!(None, def.description());
    assert_eq!("object", def.typename());
    assert_eq!("User", def.name());

    let connection = def.fields().unwrap().pop().unwrap();

    assert_eq!(
        "A list of users the given user is followed by.",
        connection.description().unwrap()
    );
    assert_eq!("followers", connection.name());
    assert_eq!("FollowerConnection", connection.typeinfo().name());
    assert_eq!("!", connection.typeinfo().info());

    let mut argument = connection.arguments().unwrap().remove(0);

    assert_eq!(
        "Returns the elements in the list that come after the specified global ID.",
        argument.description().unwrap()
    );
    assert_eq!("String", argument.typeinfo().name());
    assert_eq!("", argument.typeinfo().info());

    argument = connection.arguments().unwrap().remove(1);

    assert_eq!(
        "Returns the first _n_ elements from the list.",
        argument.description().unwrap()
    );
    assert_eq!("Int", argument.typeinfo().name());
    assert_eq!("!", argument.typeinfo().info());
}

#[test]
fn type_with_deprecated_field() {
    let def = gqlidl::parse_schema(
        "type User {
        databaseId: Int @deprecated
    }
    ",
    ).unwrap()
        .pop()
        .unwrap();

    assert_eq!(None, def.description());
    assert_eq!("object", def.typename());
    assert_eq!("User", def.name());

    let field = def.fields().unwrap().pop().unwrap();

    assert_eq!("databaseId", field.name());
    assert_eq!("Int", field.typeinfo().name());
    assert_eq!("", field.typeinfo().info());
    let directive = field.directives().unwrap().pop().unwrap();
    assert_eq!("deprecated", directive.name());
    assert_eq!(None, directive.arguments());
}

#[test]
fn type_with_deprecated_field_and_reason() {
    let def = gqlidl::parse_schema(
        "type User {
        databaseId: Int @deprecated(reason: \"Exposed database IDs will eventually be removed in favor of global Relay IDs.\")
    }
    ",
    ).unwrap()
        .pop()
        .unwrap();

    assert_eq!(None, def.description());
    assert_eq!("object", def.typename());
    assert_eq!("User", def.name());

    let field = def.fields().unwrap().pop().unwrap();

    assert_eq!("databaseId", field.name());
    assert_eq!("Int", field.typeinfo().name());
    assert_eq!("", field.typeinfo().info());
    let directive = field.directives().unwrap().pop().unwrap();
    assert_eq!("deprecated", directive.name());
    let arg = directive.arguments().unwrap().pop().unwrap();
    assert_eq!("reason", arg.name());
    assert_eq!(
        "Exposed database IDs will eventually be removed in favor of global Relay IDs.",
        arg.value().unwrap()
    );
}

#[test]
fn type_with_multiline_field_description() {
    let def = gqlidl::parse_schema(
        "
    # Represents a range of information from a Git blame.
    type BlameRange {
      # Identifies the recency of the change, from 1 (new) to 10 (old). This is
      # calculated as a 2-quantile and determines the length of distance between the
      # median age of all the changes in the file and the recency of the current
      # range's change.
      age: Int!
    }
    ",
    ).unwrap()
        .pop()
        .unwrap();

    let field = def.fields().unwrap().remove(0);

    assert_eq!(
        "Identifies the recency of the change, from 1 (new) to 10 (old). This is calculated as a 2-quantile and determines the length of distance between the median age of all the changes in the file and the recency of the current range's change.",
        field.description().unwrap()
    );
    assert_eq!("age", field.name());
    assert_eq!("Int", field.typeinfo().name());
    assert_eq!("!", field.typeinfo().info());
}

#[test]
fn enum_with_fields() {
    let def = gqlidl::parse_schema(
        "
    # State of the project; either 'open' or 'closed'
    enum ProjectState {
      # The project is closed.
      CLOSED

      # The project is open.
      OPEN
    }
    ",
    ).unwrap()
        .pop()
        .unwrap();

    assert_eq!(
        "State of the project; either 'open' or 'closed'",
        def.description().unwrap()
    );
    assert_eq!("enum", def.typename());
    assert_eq!("ProjectState", def.name());

    let mut value = def.values().unwrap().remove(0);

    assert_eq!("The project is closed.", value.description().unwrap());
    assert_eq!("CLOSED", value.name());

    value = def.values().unwrap().remove(1);

    assert_eq!("The project is open.", value.description().unwrap());
    assert_eq!("OPEN", value.name());
}

#[test]
fn interface_with_field() {
    let def = gqlidl::parse_schema(
        "
    # An object that can be closed
    interface Closable {
      # `true` if the object is closed (definition of closed may depend on type)
      closed: Boolean!
    }
    ",
    ).unwrap()
        .pop()
        .unwrap();

    assert_eq!("An object that can be closed", def.description().unwrap());
    assert_eq!("interface", def.typename());
    assert_eq!("Closable", def.name());

    let field = def.fields().unwrap().pop().unwrap();

    assert_eq!(
        "`true` if the object is closed (definition of closed may depend on type)",
        field.description().unwrap()
    );
    assert_eq!("closed", field.name());
    assert_eq!("Boolean", field.typeinfo().name());
    assert_eq!("!", field.typeinfo().info());
}

#[test]
fn union_with_descriptions() {
    let def = gqlidl::parse_schema(
        "
    # Any referencable object
    union ReferencedSubject = Issue | PullRequest
    ",
    ).unwrap()
        .pop()
        .unwrap();

    assert_eq!("Any referencable object", def.description().unwrap());
    assert_eq!("union", def.typename());
    assert_eq!("ReferencedSubject", def.name());

    let mut _type = def.types().unwrap().remove(0);

    assert_eq!("Issue", _type);

    _type = def.types().unwrap().remove(1);

    assert_eq!("PullRequest", _type);
}

#[test]
fn input_object_with_descriptions() {
    let def = gqlidl::parse_schema(
        "
    # Autogenerated input type of UpdateTopics
    input UpdateTopicsInput {
      # A unique identifier for the client performing the mutation.
      clientMutationId: String

      # An array of topic names.
      topicNames: [String!]!
    }
    ",
    ).unwrap()
        .pop()
        .unwrap();

    assert_eq!(
        "Autogenerated input type of UpdateTopics",
        def.description().unwrap()
    );
    assert_eq!("input_object", def.typename());
    assert_eq!("UpdateTopicsInput", def.name());

    let mut field = def.fields().unwrap().remove(0);

    assert_eq!(
        "A unique identifier for the client performing the mutation.",
        field.description().unwrap()
    );
    assert_eq!("clientMutationId", field.name());
    assert_eq!("String", field.typeinfo().name());
    assert_eq!("", field.typeinfo().info());

    field = def.fields().unwrap().remove(1);

    assert_eq!("An array of topic names.", field.description().unwrap());
    assert_eq!("topicNames", field.name());
    assert_eq!("String", field.typeinfo().name());
    assert_eq!("[!]!", field.typeinfo().info());
}

#[test]
fn input_object_with_special_field_name() {
    let def = gqlidl::parse_schema(
        "
    input RequestReviewsInput {
      # Add users to the set rather than replace.
      union: Boolean
    }
    ",
    ).unwrap()
        .pop()
        .unwrap();

    assert_eq!(None, def.description());
    assert_eq!("input_object", def.typename());
    assert_eq!("RequestReviewsInput", def.name());

    let field = def.fields().unwrap().remove(0);

    assert_eq!(
        "Add users to the set rather than replace.",
        field.description().unwrap()
    );
    assert_eq!("union", field.name());
    assert_eq!("Boolean", field.typeinfo().name());
    assert_eq!("", field.typeinfo().info());
}