apollo-language-server 0.6.0

A GraphQL language server with first-class support for Apollo Federation
Documentation
use crate::{
    diagnostics::LspDiagnostics,
    federation::SpecBuiltins,
    graph::{
        parse_as_subgraph_or_monolith, supergraph::KnownSubgraphs, Graph, GraphConfig,
        SchemaWithMetadata,
    },
};
use apollo_compiler::validation::DiagnosticList;
use strip_ansi_escapes::strip_str;

pub fn diagnostics_from_subgraph_or_monolith(source_text: &str, uri: &str) -> LspDiagnostics {
    let uri = lsp::Url::parse(uri).unwrap();
    let graph = Graph::new(
        uri.clone(),
        source_text.to_string(),
        0,
        KnownSubgraphs::default(),
        GraphConfig::default(),
    );
    LspDiagnostics::new(graph.diagnostics_for_uri(&uri).0, source_text.to_string())
}

pub fn merge_diagnostic_lists(
    parse_errors: Option<DiagnosticList>,
    build_errors: Option<DiagnosticList>,
    validation_errors: Option<DiagnosticList>,
) -> Option<DiagnosticList> {
    [parse_errors, build_errors, validation_errors]
        .into_iter()
        .flatten()
        .reduce(|mut a, b| {
            a.merge(b);
            a
        })
}

pub fn get_diagnostics(source_text: &str) -> Option<(DiagnosticList, LspDiagnostics)> {
    let SchemaWithMetadata {
        parse_errors,
        build_errors,
        validation_errors,
        ..
    } = parse_as_subgraph_or_monolith(source_text, "test.graphql", GraphConfig::default());

    Some((
        merge_diagnostic_lists(parse_errors, build_errors, validation_errors)?,
        diagnostics_from_subgraph_or_monolith(source_text, "file://test.graphql"),
    ))
}

pub fn pretty_print_diagnostic_comparison(
    source_text: &str,
    apollo_diagnostics: &DiagnosticList,
    lsp_diagnostics: &LspDiagnostics,
) -> String {
    format!(
            "\n\n---- Input Text ----\n\n{}\n\n---- Editor ----\n\n{:?}\n---- apollo-compiler ----\n\n{}",
            source_text,
            lsp_diagnostics,
            strip_str(format!("{}", apollo_diagnostics))
        )
}

pub fn collect_diagnostic_comparisons(
    expected_errors: &[&str],
    source_texts: &[&str],
    base_text: Option<String>,
) -> String {
    let base_text = base_text.unwrap_or_default();
    source_texts
        .iter()
        .filter_map(|source_text| {
            let source_text = format!("{}{}", base_text, source_text);
            let (diagnostic_list, lsp_diagnostics) = get_diagnostics(source_text.as_str())?;

            assert_eq!(
                lsp_diagnostics
                    .clone()
                    .into_iter()
                    .map(|d| d.message.clone())
                    .collect::<Vec<String>>(),
                expected_errors.to_vec()
            );
            Some(pretty_print_diagnostic_comparison(
                source_text.as_str(),
                &diagnostic_list,
                &lsp_diagnostics,
            ))
        })
        .collect::<Vec<String>>()
        .join("")
}

pub fn pretty_print_spec_builtins(spec_builtins: SpecBuiltins) -> String {
    let (definitions, alias_map, parsed_link) = spec_builtins.unwrap();

    // Hashmaps are not guaranteed to be in the same order across runs, so we should sort them beforehand
    let spec_alias_map = alias_map.unwrap();
    let mut sorted_map = spec_alias_map.iter().collect::<Vec<_>>();
    sorted_map.sort_by(|a, b| a.0.cmp(b.0));

    let link_location = parsed_link.unwrap().node.location().unwrap();

    format!(
        "\n\n---- Spec Definitions ----\n\n{}\n\n---- Spec Alias Map ----\n\n{:#?}\n\n---- Link Location ----\n\n{}..{}",
        definitions, sorted_map, link_location.offset(), link_location.end_offset()
    )
}