weavatrix-parse 0.3.3

Dependency-free source tokenizer and structural extractor for repository intelligence
Documentation
use super::{BTreeMap, ContractTokens, Facts, GraphqlOperation, Language, Parser, operation};

impl<'source> Parser<'source> {
    pub(super) fn new(source: &'source str) -> Self {
        Self {
            tokens: ContractTokens::new(source, Language::Graphql),
            roots: BTreeMap::new(),
            facts: Facts::default(),
        }
    }

    pub(super) fn parse(mut self) -> Facts {
        if let Some(error) = self.tokens.delimiter_error("graphql.syntax_error") {
            self.facts.diagnostics.push(error);
            return self.facts;
        }
        self.discover_roots();
        let mut index = 0;
        while index < self.tokens.len() {
            let extended = self.text(index) == "extend";
            let keyword = index + usize::from(extended);
            let value = self.text(keyword).to_owned();
            let required = extended
                || matches!(
                    value.as_str(),
                    "schema"
                        | "type"
                        | "interface"
                        | "input"
                        | "enum"
                        | "scalar"
                        | "union"
                        | "query"
                        | "mutation"
                        | "subscription"
                        | "fragment"
                        | "{"
                );
            let next = match value.as_str() {
                "schema" => self.skip_body(keyword),
                "type" | "interface" | "input" => self.parse_object(keyword, &value),
                "enum" | "scalar" | "union" => self.parse_named_type(keyword, &value),
                "query" => self.parse_operation(keyword, GraphqlOperation::Query),
                "mutation" => self.parse_operation(keyword, GraphqlOperation::Mutation),
                "subscription" => self.parse_operation(keyword, GraphqlOperation::Subscription),
                "fragment" => self.parse_fragment(keyword),
                "{" if !extended => self.parse_anonymous(keyword),
                _ => None,
            };
            if required && next.is_none() {
                return self.fail(keyword, "incomplete or unsupported GraphQL declaration");
            }
            index = next.filter(|next| *next > index).unwrap_or(index + 1);
        }
        self.facts.contracts.sort_by_key(|fact| fact.span.start);
        self.facts
    }

    pub(super) fn text(&self, index: usize) -> &str {
        self.tokens.text(index)
    }

    pub(super) fn discover_roots(&mut self) {
        for (name, operation) in [
            ("Query", GraphqlOperation::Query),
            ("Mutation", GraphqlOperation::Mutation),
            ("Subscription", GraphqlOperation::Subscription),
        ] {
            self.roots.insert(name.to_owned(), operation);
        }
        let mut index = 0;
        while index < self.tokens.len() {
            if self.text(index) != "schema" {
                index += 1;
                continue;
            }
            let Some(open) = self.body_open(index + 1) else {
                return;
            };
            let Some(close) = self.tokens.matching(open, "{", "}") else {
                return;
            };
            let mut item = open + 1;
            while item < close {
                if let Some(root) = operation(self.text(item))
                    && self.text(item + 1) == ":"
                    && self.identifier(item + 2)
                {
                    self.roots.insert(self.text(item + 2).to_owned(), root);
                    item += 3;
                } else {
                    item += 1;
                }
            }
            index = close + 1;
        }
    }
}