juniper 0.11.1

GraphQL server library
Documentation
use ast::InputValue;

use parser::{ParseError, ParseResult, Parser, ScalarToken, SourcePosition, Spanning, Token};
use schema::meta::{InputObjectMeta, MetaType};
use schema::model::SchemaType;
use value::ScalarValue;

pub fn parse_value_literal<'a, 'b, S>(
    parser: &mut Parser<'a>,
    is_const: bool,
    schema: &'b SchemaType<'b, S>,
    tpe: Option<&MetaType<'b, S>>,
) -> ParseResult<'a, InputValue<S>>
where
    S: ScalarValue,
{
    match (parser.peek(), tpe) {
        (
            &Spanning {
                item: Token::BracketOpen,
                ..
            },
            _,
        ) => parse_list_literal(parser, is_const, schema, tpe),
        (
            &Spanning {
                item: Token::CurlyOpen,
                ..
            },
            None,
        ) => parse_object_literal(parser, is_const, schema, None),
        (
            &Spanning {
                item: Token::CurlyOpen,
                ..
            },
            Some(&MetaType::InputObject(ref o)),
        ) => parse_object_literal(parser, is_const, schema, Some(o)),
        (
            &Spanning {
                item: Token::Dollar,
                ..
            },
            _,
        )
            if !is_const =>
        {
            parse_variable_literal(parser)
        }
        (
            &Spanning {
                item: Token::Scalar(_),
                ..
            },
            Some(&MetaType::Scalar(ref s)),
        ) => {
            if let Spanning {
                item: Token::Scalar(scalar),
                start,
                end,
            } = parser.next()?
            {
                (s.parse_fn)(scalar)
                    .map(|s| Spanning::start_end(&start, &end, InputValue::Scalar(s)))
                    .or_else(|_| parse_scalar_literal_by_infered_type(scalar, &start, &end, schema))
            } else {
                unreachable!()
            }
        }
        (
            &Spanning {
                item: Token::Scalar(_),
                ..
            },
            _,
        ) => {
            if let Spanning {
                item: Token::Scalar(token),
                start,
                end,
            } = parser.next()?
            {
                parse_scalar_literal_by_infered_type(token, &start, &end, schema)
            } else {
                unreachable!()
            }
        }
        (
            &Spanning {
                item: Token::Name("true"),
                ..
            },
            _,
        ) => Ok(parser.next()?.map(|_| InputValue::scalar(true))),
        (
            &Spanning {
                item: Token::Name("false"),
                ..
            },
            _,
        ) => Ok(parser.next()?.map(|_| InputValue::scalar(false))),
        (
            &Spanning {
                item: Token::Name("null"),
                ..
            },
            _,
        ) => Ok(parser.next()?.map(|_| InputValue::null())),
        (
            &Spanning {
                item: Token::Name(name),
                ..
            },
            _,
        ) => Ok(parser
            .next()?
            .map(|_| InputValue::enum_value(name.to_owned()))),
        _ => Err(parser.next()?.map(ParseError::UnexpectedToken)),
    }
}

fn parse_list_literal<'a, 'b, S>(
    parser: &mut Parser<'a>,
    is_const: bool,
    schema: &'b SchemaType<'b, S>,
    tpe: Option<&MetaType<'b, S>>,
) -> ParseResult<'a, InputValue<S>>
where
    S: ScalarValue,
{
    Ok(parser
        .delimited_list(
            &Token::BracketOpen,
            |p| parse_value_literal(p, is_const, schema, tpe),
            &Token::BracketClose,
        )?.map(InputValue::parsed_list))
}

fn parse_object_literal<'a, 'b, S>(
    parser: &mut Parser<'a>,
    is_const: bool,
    schema: &'b SchemaType<'b, S>,
    object_tpe: Option<&InputObjectMeta<'b, S>>,
) -> ParseResult<'a, InputValue<S>>
where
    S: ScalarValue,
{
    Ok(parser
        .delimited_list(
            &Token::CurlyOpen,
            |p| parse_object_field(p, is_const, schema, object_tpe),
            &Token::CurlyClose,
        )?.map(|items| InputValue::parsed_object(items.into_iter().map(|s| s.item).collect())))
}

fn parse_object_field<'a, 'b, S>(
    parser: &mut Parser<'a>,
    is_const: bool,
    schema: &'b SchemaType<'b, S>,
    object_meta: Option<&InputObjectMeta<'b, S>>,
) -> ParseResult<'a, (Spanning<String>, Spanning<InputValue<S>>)>
where
    S: ScalarValue,
{
    let key = parser.expect_name()?;

    let tpe = object_meta
        .and_then(|o| o.input_fields.iter().find(|f| f.name == key.item))
        .and_then(|f| schema.lookup_type(&f.arg_type));

    parser.expect(&Token::Colon)?;

    let value = parse_value_literal(parser, is_const, schema, tpe)?;

    Ok(Spanning::start_end(
        &key.start.clone(),
        &value.end.clone(),
        (key.map(|s| s.to_owned()), value),
    ))
}

fn parse_variable_literal<'a, S>(parser: &mut Parser<'a>) -> ParseResult<'a, InputValue<S>>
where
    S: ScalarValue,
{
    let Spanning {
        start: start_pos, ..
    } = parser.expect(&Token::Dollar)?;
    let Spanning {
        item: name,
        end: end_pos,
        ..
    } = parser.expect_name()?;

    Ok(Spanning::start_end(
        &start_pos,
        &end_pos,
        InputValue::variable(name),
    ))
}

fn parse_scalar_literal_by_infered_type<'a, 'b, S>(
    token: ScalarToken<'a>,
    start: &SourcePosition,
    end: &SourcePosition,
    schema: &'b SchemaType<'b, S>,
) -> ParseResult<'a, InputValue<S>>
where
    S: ScalarValue,
{
    match token {
        ScalarToken::String(_) => {
            if let Some(&MetaType::Scalar(ref s)) = schema.concrete_type_by_name("String") {
                (s.parse_fn)(token)
                    .map(|s| Spanning::start_end(start, end, InputValue::Scalar(s)))
                    .map_err(|e| Spanning::start_end(start, end, e))
            } else {
                panic!("There needs to be a String type")
            }
        }
        ScalarToken::Int(_) => {
            if let Some(&MetaType::Scalar(ref s)) = schema.concrete_type_by_name("Int") {
                (s.parse_fn)(token)
                    .map(|s| Spanning::start_end(start, end, InputValue::Scalar(s)))
                    .map_err(|e| Spanning::start_end(start, end, e))
            } else {
                panic!("There needs to be a Int type")
            }
        }
        ScalarToken::Float(_) => {
            if let Some(&MetaType::Scalar(ref s)) = schema.concrete_type_by_name("Float") {
                (s.parse_fn)(token)
                    .map(|s| Spanning::start_end(start, end, InputValue::Scalar(s)))
                    .map_err(|e| Spanning::start_end(start, end, e))
            } else {
                panic!("There needs to be a Float type")
            }
        }
    }
}