difftastic-lib 0.0.2

A structural diff that understands syntax.
Documentation
use rayon::prelude::*;
use std::path::PathBuf;
use version_check as rustc;

struct TreeSitterParser {
    name: &'static str,
    src_dir: &'static str,
    extra_files: Vec<&'static str>,
}

/// Emit linking flags for this library, but specifcy `+whole-archive`.
///
/// This should be possible in the cc crate directly after
/// https://github.com/rust-lang/cc-rs/pull/671
#[allow(dead_code)]
fn emit_whole_archive_link_flags(build: &mut cc::Build, lib_name: &str, is_cpp: bool) {
    if rustc::is_max_version("1.60.0").unwrap_or(false) {
        // whole-archive was only stabilised in 1.61, and we don't
        // need it in earlier versions.
        return;
    }

    build.cargo_metadata(false);

    println!("cargo:rustc-link-lib=static:+whole-archive={}", lib_name);
    println!(
        "cargo:rustc-link-search=native={}",
        std::env::var("OUT_DIR").expect("did not set OUT_DIR")
    );

    if is_cpp {
        let cpp_stdlib = if let Ok(stdlib) = std::env::var("CXXSTDLIB") {
            if stdlib.is_empty() {
                None
            } else {
                Some(stdlib)
            }
        } else {
            let target = std::env::var("TARGET").expect("TARGET environment should be set");

            // Equivalent to https://github.com/rust-lang/cc-rs/blob/53fb72c87e5769a299f1886ead831901b9c775d6/src/lib.rs#L2528
            if target.contains("msvc") {
                None
            } else if target.contains("apple") {
                Some("c++".to_string())
            } else if target.contains("freebsd") {
                Some("c++".to_string())
            } else if target.contains("openbsd") {
                Some("c++".to_string())
            } else if target.contains("android") {
                Some("c++_shared".to_string())
            } else {
                Some("stdc++".to_string())
            }
        };

        if let Some(cpp_stdlib) = cpp_stdlib {
            println!("cargo:rustc-link-lib={}", cpp_stdlib);
        }
    }
}

impl TreeSitterParser {
    fn build(&self) {
        let dir = PathBuf::from(&self.src_dir);

        let mut c_files = vec!["parser.c"];
        let mut cpp_files = vec![];

        for file in &self.extra_files {
            if file.ends_with(".c") {
                c_files.push(file);
            } else {
                cpp_files.push(file);
            }
        }

        if !cpp_files.is_empty() {
            let mut cpp_build = cc::Build::new();
            cpp_build
                .include(&dir)
                .cpp(true)
                .flag_if_supported("-Wno-implicit-fallthrough")
                .flag_if_supported("-Wno-unused-parameter")
                .flag_if_supported("-Wno-ignored-qualifiers")
                // Ignore warning from tree-sitter-html.
                .flag_if_supported("-Wno-sign-compare")
                // Ignore warning from tree-sitter-ruby.
                .flag_if_supported("-Wno-parentheses")
                // Ignore warning from tree-sitter-ruby.
                .flag_if_supported("-Wno-unused-but-set-variable")
                // Workaround for: https://github.com/ganezdragon/tree-sitter-perl/issues/16
                // should be removed after fixed.
                .flag_if_supported("-Wno-return-type");

            if cfg!(windows) {
                cpp_build.flag("/std:c++14");
            } else {
                cpp_build.flag("--std=c++14");
            }

            for file in cpp_files {
                cpp_build.file(dir.join(file));
            }

            // emit_whole_archive_link_flags(&mut cpp_build, &format!("{}-cpp", self.name), true);
            cpp_build.compile(&format!("{}-cpp", self.name));
        }

        let mut build = cc::Build::new();
        if cfg!(windows) {
            build.flag("/utf-8");
        }
        build.include(&dir).warnings(false); // ignore unused parameter warnings
        for file in c_files {
            build.file(dir.join(file));
        }

        // emit_whole_archive_link_flags(&mut build, self.name, false);
        build.compile(self.name);
    }
}

fn main() {
    let parsers = vec![
        // TreeSitterParser {
        //     name: "tree-sitter-bash",
        //     src_dir: "vendor/tree-sitter-bash-src",
        //     extra_files: vec!["scanner.cc"],
        // },
        // TreeSitterParser {
        //     name: "tree-sitter-c",
        //     src_dir: "vendor/tree-sitter-c-src",
        //     extra_files: vec![],
        // },
        // TreeSitterParser {
        //     name: "tree-sitter-cpp",
        //     src_dir: "vendor/tree-sitter-cpp-src",
        //     extra_files: vec!["scanner.cc"],
        // },
        // TreeSitterParser {
        //     name: "tree-sitter-c-sharp",
        //     src_dir: "vendor/tree-sitter-c-sharp-src",
        //     extra_files: vec!["scanner.c"],
        // },
        // TreeSitterParser {
        //     name: "tree-sitter-clojure",
        //     src_dir: "vendor/tree-sitter-clojure-src",
        //     extra_files: vec![],
        // },
        // TreeSitterParser {
        //     name: "tree-sitter-cmake",
        //     src_dir: "vendor/tree-sitter-cmake-src",
        //     extra_files: vec!["scanner.cc"],
        // },
        // TreeSitterParser {
        //     name: "tree-sitter-commonlisp",
        //     src_dir: "vendor/tree-sitter-commonlisp-src",
        //     extra_files: vec![],
        // },
        // TreeSitterParser {
        //     name: "tree-sitter-css",
        //     src_dir: "vendor/tree-sitter-css-src",
        //     extra_files: vec!["scanner.c"],
        // },
        // TreeSitterParser {
        //     name: "tree-sitter-dart",
        //     src_dir: "vendor/tree-sitter-dart-src",
        //     extra_files: vec!["scanner.c"],
        // },
        // TreeSitterParser {
        //     name: "tree-sitter-elisp",
        //     src_dir: "vendor/tree-sitter-elisp-src",
        //     extra_files: vec![],
        // },
        // TreeSitterParser {
        //     name: "tree-sitter-elixir",
        //     src_dir: "vendor/tree-sitter-elixir-src",
        //     extra_files: vec!["scanner.cc"],
        // },
        // TreeSitterParser {
        //     name: "tree-sitter-elm",
        //     src_dir: "vendor/tree-sitter-elm-src",
        //     extra_files: vec!["scanner.cc"],
        // },
        // TreeSitterParser {
        //     name: "tree-sitter-elvish",
        //     src_dir: "vendor/tree-sitter-elvish-src",
        //     extra_files: vec![],
        // },
        // TreeSitterParser {
        //     name: "tree-sitter-gleam",
        //     src_dir: "vendor/tree-sitter-gleam-src",
        //     extra_files: vec!["scanner.c"],
        // },
        // TreeSitterParser {
        //     name: "tree-sitter-go",
        //     src_dir: "vendor/tree-sitter-go-src",
        //     extra_files: vec![],
        // },
        // TreeSitterParser {
        //     name: "tree-sitter-hack",
        //     src_dir: "vendor/tree-sitter-hack-src",
        //     extra_files: vec!["scanner.cc"],
        // },
        // TreeSitterParser {
        //     name: "tree-sitter-haskell",
        //     src_dir: "vendor/tree-sitter-haskell-src",
        //     extra_files: vec!["scanner.c"],
        // },
        // TreeSitterParser {
        //     name: "tree-sitter-hcl",
        //     src_dir: "vendor/tree-sitter-hcl-src",
        //     extra_files: vec!["scanner.cc"],
        // },
        // TreeSitterParser {
        //     name: "tree-sitter-html",
        //     src_dir: "vendor/tree-sitter-html-src",
        //     extra_files: vec!["scanner.cc"],
        // },
        // TreeSitterParser {
        //     name: "tree-sitter-janet-simple",
        //     src_dir: "vendor/tree-sitter-janet-simple-src",
        //     extra_files: vec!["scanner.c"],
        // },
        // TreeSitterParser {
        //     name: "tree-sitter-java",
        //     src_dir: "vendor/tree-sitter-java-src",
        //     extra_files: vec![],
        // },
        // TreeSitterParser {
        //     name: "tree-sitter-javascript",
        //     src_dir: "vendor/tree-sitter-javascript-src",
        //     extra_files: vec!["scanner.c"],
        // },
        // TreeSitterParser {
        //     name: "tree-sitter-json",
        //     src_dir: "vendor/tree-sitter-json-src",
        //     extra_files: vec![],
        // },
        // TreeSitterParser {
        //     name: "tree-sitter-julia",
        //     src_dir: "vendor/tree-sitter-julia-src",
        //     extra_files: vec!["scanner.c"],
        // },
        // TreeSitterParser {
        //     name: "tree-sitter-kotlin",
        //     src_dir: "vendor/tree-sitter-kotlin-src",
        //     extra_files: vec!["scanner.c"],
        // },
        // TreeSitterParser {
        //     name: "tree-sitter-lua",
        //     src_dir: "vendor/tree-sitter-lua-src",
        //     extra_files: vec!["scanner.cc"],
        // },
        // TreeSitterParser {
        //     name: "tree-sitter-make",
        //     src_dir: "vendor/tree-sitter-make-src",
        //     extra_files: vec![],
        // },
        // TreeSitterParser {
        //     name: "tree-sitter-nix",
        //     src_dir: "vendor/tree-sitter-nix-src",
        //     extra_files: vec!["scanner.c"],
        // },
        // TreeSitterParser {
        //     name: "tree-sitter-ocaml",
        //     src_dir: "vendor/tree-sitter-ocaml-src/ocaml/src",
        //     extra_files: vec!["scanner.cc"],
        // },
        // TreeSitterParser {
        //     name: "tree-sitter-ocaml-interface",
        //     src_dir: "vendor/tree-sitter-ocaml-src/interface/src",
        //     extra_files: vec!["scanner.cc"],
        // },
        // TreeSitterParser {
        //     name: "tree-sitter-php",
        //     src_dir: "vendor/tree-sitter-php-src",
        //     extra_files: vec!["scanner.cc"],
        // },
        // TreeSitterParser {
        //     name: "tree-sitter-perl",
        //     src_dir: "vendor/tree-sitter-perl-src",
        //     extra_files: vec!["scanner.cc"],
        // },
        // TreeSitterParser {
        //     name: "tree-sitter-python",
        //     src_dir: "vendor/tree-sitter-python-src",
        //     extra_files: vec!["scanner.cc"],
        // },
        // TreeSitterParser {
        //     name: "tree-sitter-qmljs",
        //     src_dir: "vendor/tree-sitter-qmljs-src",
        //     extra_files: vec!["scanner.c"],
        // },
        // TreeSitterParser {
        //     name: "tree-sitter-ruby",
        //     src_dir: "vendor/tree-sitter-ruby-src",
        //     extra_files: vec!["scanner.cc"],
        // },
        // TreeSitterParser {
        //     name: "tree-sitter-rust",
        //     src_dir: "vendor/tree-sitter-rust-src",
        //     extra_files: vec!["scanner.c"],
        // },
        // TreeSitterParser {
        //     name: "tree-sitter-scala",
        //     src_dir: "vendor/tree-sitter-scala-src",
        //     extra_files: vec!["scanner.c"],
        // },
        // TreeSitterParser {
        //     name: "tree-sitter-sql",
        //     src_dir: "vendor/tree-sitter-sql-src",
        //     extra_files: vec!["scanner.cc"],
        // },
        // TreeSitterParser {
        //     name: "tree-sitter-swift",
        //     src_dir: "vendor/tree-sitter-swift-src",
        //     extra_files: vec!["scanner.c"],
        // },
        // TreeSitterParser {
        //     name: "tree-sitter-toml",
        //     src_dir: "vendor/tree-sitter-toml-src",
        //     extra_files: vec!["scanner.c"],
        // },
        // TreeSitterParser {
        //     name: "tree-sitter-tsx",
        //     src_dir: "vendor/tree-sitter-typescript-src/tsx/src",
        //     extra_files: vec!["scanner.c"],
        // },
        // TreeSitterParser {
        //     name: "tree-sitter-typescript",
        //     src_dir: "vendor/tree-sitter-typescript-src/typescript/src",
        //     extra_files: vec!["scanner.c"],
        // },
        TreeSitterParser {
            name: "tree-sitter-yaml",
            src_dir: "vendor/tree-sitter-yaml-src",
            extra_files: vec!["scanner.cc"],
        },
        // TreeSitterParser {
        //     name: "tree-sitter-zig",
        //     src_dir: "vendor/tree-sitter-zig-src",
        //     extra_files: vec![],
        // },
    ];

    // Only rerun if relevant files in the vendor/ directory change.
    println!("cargo:rerun-if-changed=vendor/highlights");
    for parser in &parsers {
        println!("cargo:rerun-if-changed={}", parser.src_dir);
    }

    parsers.par_iter().for_each(|p| p.build());
}