<commit_before>/// This list represents the default file types that ripgrep ships with. In
/// general, any file format is fair game, although it should generally be
/// limited to reasonably popular open formats. For other cases, you can add
/// types to each invocation of ripgrep with the '--type-add' flag.
///
/// If you would like to add or improve this list, please file a PR:
/// <https://github.com/BurntSushi/ripgrep>.
///
/// Please try to keep this list sorted lexicographically and wrapped to 79
/// columns (inclusive).
#[rustfmt::skip]
pub const DEFAULT_TYPES: &[(&str, &[&str])] = &[
    ("agda", &["*.agda", "*.lagda"]),
    ("aidl", &["*.aidl"]),
    ("amake", &["*.mk", "*.bp"]),
    ("asciidoc", &["*.adoc", "*.asc", "*.asciidoc"]),
    ("asm", &["*.asm", "*.s", "*.S"]),
    ("asp", &[
        "*.aspx", "*.aspx.cs", "*.aspx.vb", "*.ascx", "*.ascx.cs", "*.ascx.vb",
    ]),
    ("ats", &["*.ats", "*.dats", "*.sats", "*.hats"]),
    ("avro", &["*.avdl", "*.avpr", "*.avsc"]),
    ("awk", &["*.awk"]),
    ("bazel", &["*.bazel", "*.bzl", "*.BUILD", "*.bazelrc", "BUILD", "WORKSPACE"]),
    ("bitbake", &["*.bb", "*.bbappend", "*.bbclass", "*.conf", "*.inc"]),
    ("brotli", &["*.br"]),
    ("buildstream", &["*.bst"]),
    ("bzip2", &["*.bz2", "*.tbz2"]),
    ("c", &["*.[chH]", "*.[chH].in", "*.cats"]),
    ("cabal", &["*.cabal"]),
    ("cbor", &["*.cbor"]),
    ("ceylon", &["*.ceylon"]),
    ("clojure", &["*.clj", "*.cljc", "*.cljs", "*.cljx"]),
    ("cmake", &["*.cmake", "CMakeLists.txt"]),
    ("coffeescript", &["*.coffee"]),
    ("config", &["*.cfg", "*.conf", "*.config", "*.ini"]),
    ("coq", &["*.v"]),
    ("cpp", &[
        "*.[ChH]", "*.cc", "*.[ch]pp", "*.[ch]xx", "*.hh",  "*.inl",
        "*.[ChH].in", "*.cc.in", "*.[ch]pp.in", "*.[ch]xx.in", "*.hh.in",
    ]),
    ("creole", &["*.creole"]),
    ("crystal", &["Projectfile", "*.cr", "*.ecr", "shard.yml"]),
    ("cs", &["*.cs"]),
    ("csharp", &["*.cs"]),
    ("cshtml", &["*.cshtml"]),
    ("css", &["*.css", "*.scss"]),
    ("csv", &["*.csv"]),
    ("cuda", &["*.cu", "*.cuh"]),
    ("cython", &["*.pyx", "*.pxi", "*.pxd"]),
    ("d", &["*.d"]),
    ("dart", &["*.dart"]),
    ("dhall", &["*.dhall"]),
    ("diff", &["*.patch", "*.diff"]),
    ("docker", &["*Dockerfile*"]),
    ("dvc", &["Dvcfile", "*.dvc"]),
    ("ebuild", &["*.ebuild"]),
    ("edn", &["*.edn"]),
    ("elisp", &["*.el"]),
    ("elixir", &["*.ex", "*.eex", "*.exs"]),
    ("elm", &["*.elm"]),
    ("erb", &["*.erb"]),
    ("erlang", &["*.erl", "*.hrl"]),
    ("fidl", &["*.fidl"]),
    ("fish", &["*.fish"]),
    ("flatbuffers", &["*.fbs"]),
    ("fortran", &[
        "*.f", "*.F", "*.f77", "*.F77", "*.pfo",
        "*.f90", "*.F90", "*.f95", "*.F95",
    ]),
    ("fsharp", &["*.fs", "*.fsx", "*.fsi"]),
    ("fut", &[".fut"]),
    ("gap", &["*.g", "*.gap", "*.gi", "*.gd", "*.tst"]),
    ("gn", &["*.gn", "*.gni"]),
    ("go", &["*.go"]),
    ("gradle", &["*.gradle"]),
    ("groovy", &["*.groovy", "*.gradle"]),
    ("gzip", &["*.gz", "*.tgz"]),
    ("h", &["*.h", "*.hpp"]),
    ("haml", &["*.haml"]),
    ("haskell", &["*.hs", "*.lhs", "*.cpphs", "*.c2hs", "*.hsc"]),
    ("hbs", &["*.hbs"]),
    ("hs", &["*.hs", "*.lhs"]),
    ("html", &["*.htm", "*.html", "*.ejs"]),
    ("idris", &["*.idr", "*.lidr"]),
    ("java", &["*.java", "*.jsp", "*.jspx", "*.properties"]),
    ("jinja", &["*.j2", "*.jinja", "*.jinja2"]),
    ("jl", &["*.jl"]),
    ("js", &["*.js", "*.jsx", "*.vue"]),
    ("json", &["*.json", "composer.lock"]),
    ("jsonl", &["*.jsonl"]),
    ("julia", &["*.jl"]),
    ("jupyter", &["*.ipynb", "*.jpynb"]),
    ("k", &["*.k"]),
    ("kotlin", &["*.kt", "*.kts"]),
    ("less", &["*.less"]),
    ("license", &[
        // General
        "COPYING", "COPYING[.-]*",
        "COPYRIGHT", "COPYRIGHT[.-]*",
        "EULA", "EULA[.-]*",
        "licen[cs]e", "licen[cs]e.*",
        "LICEN[CS]E", "LICEN[CS]E[.-]*", "*[.-]LICEN[CS]E*",
        "NOTICE", "NOTICE[.-]*",
        "PATENTS", "PATENTS[.-]*",
        "UNLICEN[CS]E", "UNLICEN[CS]E[.-]*",
        // GPL (gpl.txt, etc.)
        "agpl[.-]*",
        "gpl[.-]*",
        "lgpl[.-]*",
        // Other license-specific (APACHE-2.0.txt, etc.)
        "AGPL-*[0-9]*",
        "APACHE-*[0-9]*",
        "BSD-*[0-9]*",
        "CC-BY-*",
        "GFDL-*[0-9]*",
        "GNU-*[0-9]*",
        "GPL-*[0-9]*",
        "LGPL-*[0-9]*",
        "MIT-*[0-9]*",
        "MPL-*[0-9]*",
        "OFL-*[0-9]*",
    ]),
    ("lisp", &["*.el", "*.jl", "*.lisp", "*.lsp", "*.sc", "*.scm"]),
    ("lock", &["*.lock", "package-lock.json"]),
    ("log", &["*.log"]),
    ("lua", &["*.lua"]),
    ("lz4", &["*.lz4"]),
    ("lzma", &["*.lzma"]),
    ("m4", &["*.ac", "*.m4"]),
    ("make", &[
        "[Gg][Nn][Uu]makefile", "[Mm]akefile",
        "[Gg][Nn][Uu]makefile.am", "[Mm]akefile.am",
        "[Gg][Nn][Uu]makefile.in", "[Mm]akefile.in",
        "*.mk", "*.mak"
    ]),
    ("mako", &["*.mako", "*.mao"]),
    ("man", &["*.[0-9lnpx]", "*.[0-9][cEFMmpSx]"]),
    ("markdown", &["*.markdown", "*.md", "*.mdown", "*.mkdn"]),
    ("matlab", &["*.m"]),
    ("md", &["*.markdown", "*.md", "*.mdown", "*.mkdn"]),
    ("meson", &["meson.build", "meson_options.txt"]),
    ("minified", &["*.min.html", "*.min.css", "*.min.js"]),
    ("mint", &["*.mint"]),
    ("mk", &["mkfile"]),
    ("ml", &["*.ml"]),
    ("msbuild", &[
        "*.csproj", "*.fsproj", "*.vcxproj", "*.proj", "*.props", "*.targets",
    ]),
    ("nim", &["*.nim", "*.nimf", "*.nimble", "*.nims"]),
    ("nix", &["*.nix"]),
    ("objc", &["*.h", "*.m"]),
    ("objcpp", &["*.h", "*.mm"]),
    ("ocaml", &["*.ml", "*.mli", "*.mll", "*.mly"]),
    ("org", &["*.org", "*.org_archive"]),
    ("pascal", &["*.pas", "*.dpr", "*.lpr", "*.pp", "*.inc"]),
    ("pdf", &["*.pdf"]),
    ("perl", &["*.perl", "*.pl", "*.PL", "*.plh", "*.plx", "*.pm", "*.t"]),
    ("php", &["*.php", "*.php3", "*.php4", "*.php5", "*.phtml"]),
    ("po", &["*.po"]),
    ("pod", &["*.pod"]),
    ("postscript", &["*.eps", "*.ps"]),
    ("protobuf", &["*.proto"]),
    ("ps", &["*.cdxml", "*.ps1", "*.ps1xml", "*.psd1", "*.psm1"]),
    ("puppet", &["*.erb", "*.pp", "*.rb"]),
    ("purs", &["*.purs"]),
    ("py", &["*.py"]),
    ("qmake", &["*.pro", "*.pri", "*.prf"]),
    ("qml", &["*.qml"]),
    ("r", &["*.R", "*.r", "*.Rmd", "*.Rnw"]),
    ("racket", &["*.rkt"]),
    ("rdoc", &["*.rdoc"]),
    ("readme", &["README*", "*README"]),
    ("red", &["*.r", "*.red", "*.reds"]),
    ("robot", &["*.robot"]),
    ("rst", &["*.rst"]),
    ("ruby", &[
        // Idiomatic files
        "config.ru", "Gemfile", ".irbrc", "Rakefile",
        // Extensions
        "*.gemspec", "*.rb", "*.rbw"
    ]),
    ("rust", &["*.rs"]),
    ("sass", &["*.sass", "*.scss"]),
    ("scala", &["*.scala", "*.sbt"]),
    ("sh", &[
        // Portable/misc. init files
        ".login", ".logout", ".profile", "profile",
        // bash-specific init files
        ".bash_login", "bash_login",
        ".bash_logout", "bash_logout",
        ".bash_profile", "bash_profile",
        ".bashrc", "bashrc", "*.bashrc",
        // csh-specific init files
        ".cshrc", "*.cshrc",
        // ksh-specific init files
        ".kshrc", "*.kshrc",
        // tcsh-specific init files
        ".tcshrc",
        // zsh-specific init files
        ".zshenv", "zshenv",
        ".zlogin", "zlogin",
        ".zlogout", "zlogout",
        ".zprofile", "zprofile",
        ".zshrc", "zshrc",
        // Extensions
        "*.bash", "*.csh", "*.ksh", "*.sh", "*.tcsh", "*.zsh",
    ]),
    ("slim", &["*.skim", "*.slim", "*.slime"]),
    ("smarty", &["*.tpl"]),
    ("sml", &["*.sml", "*.sig"]),
    ("soy", &["*.soy"]),
    ("spark", &["*.spark"]),
    ("spec", &["*.spec"]),
    ("sql", &["*.sql", "*.psql"]),
    ("stylus", &["*.styl"]),
    ("sv", &["*.v", "*.vg", "*.sv", "*.svh", "*.h"]),
    ("svg", &["*.svg"]),
    ("swift", &["*.swift"]),
    ("swig", &["*.def", "*.i"]),
    ("systemd", &[
        "*.automount", "*.conf", "*.device", "*.link", "*.mount", "*.path",
        "*.scope", "*.service", "*.slice", "*.socket", "*.swap", "*.target",
        "*.timer",
    ]),
    ("taskpaper", &["*.taskpaper"]),
    ("tcl", &["*.tcl"]),
    ("tex", &["*.tex", "*.ltx", "*.cls", "*.sty", "*.bib", "*.dtx", "*.ins"]),
    ("texinfo", &["*.texi"]),
    ("textile", &["*.textile"]),
    ("tf", &["*.tf"]),
    ("thrift", &["*.thrift"]),
    ("toml", &["*.toml", "Cargo.lock"]),
    ("ts", &["*.ts", "*.tsx"]),
    ("twig", &["*.twig"]),
    ("txt", &["*.txt"]),
    ("typoscript", &["*.typoscript", "*.ts"]),
    ("vala", &["*.vala"]),
    ("vb", &["*.vb"]),
    ("vcl", &["*.vcl"]),
    ("verilog", &["*.v", "*.vh", "*.sv", "*.svh"]),
    ("vhdl", &["*.vhd", "*.vhdl"]),
    ("vim", &["*.vim"]),
    ("vimscript", &["*.vim"]),
    ("webidl", &["*.idl", "*.webidl", "*.widl"]),
    ("wiki", &["*.mediawiki", "*.wiki"]),
    ("xml", &[
        "*.xml", "*.xml.dist", "*.dtd", "*.xsl", "*.xslt", "*.xsd", "*.xjb",
        "*.rng", "*.sch", "*.xhtml",
    ]),
    ("xz", &["*.xz", "*.txz"]),
    ("yacc", &["*.y"]),
    ("yaml", &["*.yaml", "*.yml"]),
    ("yang", &["*.yang"]),
    ("z", &["*.Z"]),
    ("zig", &["*.zig"]),
    ("zsh", &[
        ".zshenv", "zshenv",
        ".zlogin", "zlogin",
        ".zlogout", "zlogout",
        ".zprofile", "zprofile",
        ".zshrc", "zshrc",
        "*.zsh",
    ]),
    ("zstd", &["*.zst", "*.zstd"]),
];
<commit_msg>ignore/types: add janet<commit_after>/// This list represents the default file types that ripgrep ships with. In
/// general, any file format is fair game, although it should generally be
/// limited to reasonably popular open formats. For other cases, you can add
/// types to each invocation of ripgrep with the '--type-add' flag.
///
/// If you would like to add or improve this list, please file a PR:
/// <https://github.com/BurntSushi/ripgrep>.
///
/// Please try to keep this list sorted lexicographically and wrapped to 79
/// columns (inclusive).
#[rustfmt::skip]
pub const DEFAULT_TYPES: &[(&str, &[&str])] = &[
    ("agda", &["*.agda", "*.lagda"]),
    ("aidl", &["*.aidl"]),
    ("amake", &["*.mk", "*.bp"]),
    ("asciidoc", &["*.adoc", "*.asc", "*.asciidoc"]),
    ("asm", &["*.asm", "*.s", "*.S"]),
    ("asp", &[
        "*.aspx", "*.aspx.cs", "*.aspx.vb", "*.ascx", "*.ascx.cs", "*.ascx.vb",
    ]),
    ("ats", &["*.ats", "*.dats", "*.sats", "*.hats"]),
    ("avro", &["*.avdl", "*.avpr", "*.avsc"]),
    ("awk", &["*.awk"]),
    ("bazel", &["*.bazel", "*.bzl", "*.BUILD", "*.bazelrc", "BUILD", "WORKSPACE"]),
    ("bitbake", &["*.bb", "*.bbappend", "*.bbclass", "*.conf", "*.inc"]),
    ("brotli", &["*.br"]),
    ("buildstream", &["*.bst"]),
    ("bzip2", &["*.bz2", "*.tbz2"]),
    ("c", &["*.[chH]", "*.[chH].in", "*.cats"]),
    ("cabal", &["*.cabal"]),
    ("cbor", &["*.cbor"]),
    ("ceylon", &["*.ceylon"]),
    ("clojure", &["*.clj", "*.cljc", "*.cljs", "*.cljx"]),
    ("cmake", &["*.cmake", "CMakeLists.txt"]),
    ("coffeescript", &["*.coffee"]),
    ("config", &["*.cfg", "*.conf", "*.config", "*.ini"]),
    ("coq", &["*.v"]),
    ("cpp", &[
        "*.[ChH]", "*.cc", "*.[ch]pp", "*.[ch]xx", "*.hh",  "*.inl",
        "*.[ChH].in", "*.cc.in", "*.[ch]pp.in", "*.[ch]xx.in", "*.hh.in",
    ]),
    ("creole", &["*.creole"]),
    ("crystal", &["Projectfile", "*.cr", "*.ecr", "shard.yml"]),
    ("cs", &["*.cs"]),
    ("csharp", &["*.cs"]),
    ("cshtml", &["*.cshtml"]),
    ("css", &["*.css", "*.scss"]),
    ("csv", &["*.csv"]),
    ("cuda", &["*.cu", "*.cuh"]),
    ("cython", &["*.pyx", "*.pxi", "*.pxd"]),
    ("d", &["*.d"]),
    ("dart", &["*.dart"]),
    ("dhall", &["*.dhall"]),
    ("diff", &["*.patch", "*.diff"]),
    ("docker", &["*Dockerfile*"]),
    ("dvc", &["Dvcfile", "*.dvc"]),
    ("ebuild", &["*.ebuild"]),
    ("edn", &["*.edn"]),
    ("elisp", &["*.el"]),
    ("elixir", &["*.ex", "*.eex", "*.exs"]),
    ("elm", &["*.elm"]),
    ("erb", &["*.erb"]),
    ("erlang", &["*.erl", "*.hrl"]),
    ("fidl", &["*.fidl"]),
    ("fish", &["*.fish"]),
    ("flatbuffers", &["*.fbs"]),
    ("fortran", &[
        "*.f", "*.F", "*.f77", "*.F77", "*.pfo",
        "*.f90", "*.F90", "*.f95", "*.F95",
    ]),
    ("fsharp", &["*.fs", "*.fsx", "*.fsi"]),
    ("fut", &[".fut"]),
    ("gap", &["*.g", "*.gap", "*.gi", "*.gd", "*.tst"]),
    ("gn", &["*.gn", "*.gni"]),
    ("go", &["*.go"]),
    ("gradle", &["*.gradle"]),
    ("groovy", &["*.groovy", "*.gradle"]),
    ("gzip", &["*.gz", "*.tgz"]),
    ("h", &["*.h", "*.hpp"]),
    ("haml", &["*.haml"]),
    ("haskell", &["*.hs", "*.lhs", "*.cpphs", "*.c2hs", "*.hsc"]),
    ("hbs", &["*.hbs"]),
    ("hs", &["*.hs", "*.lhs"]),
    ("html", &["*.htm", "*.html", "*.ejs"]),
    ("idris", &["*.idr", "*.lidr"]),
    ("janet", &["*.janet"]),
    ("java", &["*.java", "*.jsp", "*.jspx", "*.properties"]),
    ("jinja", &["*.j2", "*.jinja", "*.jinja2"]),
    ("jl", &["*.jl"]),
    ("js", &["*.js", "*.jsx", "*.vue"]),
    ("json", &["*.json", "composer.lock"]),
    ("jsonl", &["*.jsonl"]),
    ("julia", &["*.jl"]),
    ("jupyter", &["*.ipynb", "*.jpynb"]),
    ("k", &["*.k"]),
    ("kotlin", &["*.kt", "*.kts"]),
    ("less", &["*.less"]),
    ("license", &[
        // General
        "COPYING", "COPYING[.-]*",
        "COPYRIGHT", "COPYRIGHT[.-]*",
        "EULA", "EULA[.-]*",
        "licen[cs]e", "licen[cs]e.*",
        "LICEN[CS]E", "LICEN[CS]E[.-]*", "*[.-]LICEN[CS]E*",
        "NOTICE", "NOTICE[.-]*",
        "PATENTS", "PATENTS[.-]*",
        "UNLICEN[CS]E", "UNLICEN[CS]E[.-]*",
        // GPL (gpl.txt, etc.)
        "agpl[.-]*",
        "gpl[.-]*",
        "lgpl[.-]*",
        // Other license-specific (APACHE-2.0.txt, etc.)
        "AGPL-*[0-9]*",
        "APACHE-*[0-9]*",
        "BSD-*[0-9]*",
        "CC-BY-*",
        "GFDL-*[0-9]*",
        "GNU-*[0-9]*",
        "GPL-*[0-9]*",
        "LGPL-*[0-9]*",
        "MIT-*[0-9]*",
        "MPL-*[0-9]*",
        "OFL-*[0-9]*",
    ]),
    ("lisp", &["*.el", "*.jl", "*.lisp", "*.lsp", "*.sc", "*.scm"]),
    ("lock", &["*.lock", "package-lock.json"]),
    ("log", &["*.log"]),
    ("lua", &["*.lua"]),
    ("lz4", &["*.lz4"]),
    ("lzma", &["*.lzma"]),
    ("m4", &["*.ac", "*.m4"]),
    ("make", &[
        "[Gg][Nn][Uu]makefile", "[Mm]akefile",
        "[Gg][Nn][Uu]makefile.am", "[Mm]akefile.am",
        "[Gg][Nn][Uu]makefile.in", "[Mm]akefile.in",
        "*.mk", "*.mak"
    ]),
    ("mako", &["*.mako", "*.mao"]),
    ("man", &["*.[0-9lnpx]", "*.[0-9][cEFMmpSx]"]),
    ("markdown", &["*.markdown", "*.md", "*.mdown", "*.mkdn"]),
    ("matlab", &["*.m"]),
    ("md", &["*.markdown", "*.md", "*.mdown", "*.mkdn"]),
    ("meson", &["meson.build", "meson_options.txt"]),
    ("minified", &["*.min.html", "*.min.css", "*.min.js"]),
    ("mint", &["*.mint"]),
    ("mk", &["mkfile"]),
    ("ml", &["*.ml"]),
    ("msbuild", &[
        "*.csproj", "*.fsproj", "*.vcxproj", "*.proj", "*.props", "*.targets",
    ]),
    ("nim", &["*.nim", "*.nimf", "*.nimble", "*.nims"]),
    ("nix", &["*.nix"]),
    ("objc", &["*.h", "*.m"]),
    ("objcpp", &["*.h", "*.mm"]),
    ("ocaml", &["*.ml", "*.mli", "*.mll", "*.mly"]),
    ("org", &["*.org", "*.org_archive"]),
    ("pascal", &["*.pas", "*.dpr", "*.lpr", "*.pp", "*.inc"]),
    ("pdf", &["*.pdf"]),
    ("perl", &["*.perl", "*.pl", "*.PL", "*.plh", "*.plx", "*.pm", "*.t"]),
    ("php", &["*.php", "*.php3", "*.php4", "*.php5", "*.phtml"]),
    ("po", &["*.po"]),
    ("pod", &["*.pod"]),
    ("postscript", &["*.eps", "*.ps"]),
    ("protobuf", &["*.proto"]),
    ("ps", &["*.cdxml", "*.ps1", "*.ps1xml", "*.psd1", "*.psm1"]),
    ("puppet", &["*.erb", "*.pp", "*.rb"]),
    ("purs", &["*.purs"]),
    ("py", &["*.py"]),
    ("qmake", &["*.pro", "*.pri", "*.prf"]),
    ("qml", &["*.qml"]),
    ("r", &["*.R", "*.r", "*.Rmd", "*.Rnw"]),
    ("racket", &["*.rkt"]),
    ("rdoc", &["*.rdoc"]),
    ("readme", &["README*", "*README"]),
    ("red", &["*.r", "*.red", "*.reds"]),
    ("robot", &["*.robot"]),
    ("rst", &["*.rst"]),
    ("ruby", &[
        // Idiomatic files
        "config.ru", "Gemfile", ".irbrc", "Rakefile",
        // Extensions
        "*.gemspec", "*.rb", "*.rbw"
    ]),
    ("rust", &["*.rs"]),
    ("sass", &["*.sass", "*.scss"]),
    ("scala", &["*.scala", "*.sbt"]),
    ("sh", &[
        // Portable/misc. init files
        ".login", ".logout", ".profile", "profile",
        // bash-specific init files
        ".bash_login", "bash_login",
        ".bash_logout", "bash_logout",
        ".bash_profile", "bash_profile",
        ".bashrc", "bashrc", "*.bashrc",
        // csh-specific init files
        ".cshrc", "*.cshrc",
        // ksh-specific init files
        ".kshrc", "*.kshrc",
        // tcsh-specific init files
        ".tcshrc",
        // zsh-specific init files
        ".zshenv", "zshenv",
        ".zlogin", "zlogin",
        ".zlogout", "zlogout",
        ".zprofile", "zprofile",
        ".zshrc", "zshrc",
        // Extensions
        "*.bash", "*.csh", "*.ksh", "*.sh", "*.tcsh", "*.zsh",
    ]),
    ("slim", &["*.skim", "*.slim", "*.slime"]),
    ("smarty", &["*.tpl"]),
    ("sml", &["*.sml", "*.sig"]),
    ("soy", &["*.soy"]),
    ("spark", &["*.spark"]),
    ("spec", &["*.spec"]),
    ("sql", &["*.sql", "*.psql"]),
    ("stylus", &["*.styl"]),
    ("sv", &["*.v", "*.vg", "*.sv", "*.svh", "*.h"]),
    ("svg", &["*.svg"]),
    ("swift", &["*.swift"]),
    ("swig", &["*.def", "*.i"]),
    ("systemd", &[
        "*.automount", "*.conf", "*.device", "*.link", "*.mount", "*.path",
        "*.scope", "*.service", "*.slice", "*.socket", "*.swap", "*.target",
        "*.timer",
    ]),
    ("taskpaper", &["*.taskpaper"]),
    ("tcl", &["*.tcl"]),
    ("tex", &["*.tex", "*.ltx", "*.cls", "*.sty", "*.bib", "*.dtx", "*.ins"]),
    ("texinfo", &["*.texi"]),
    ("textile", &["*.textile"]),
    ("tf", &["*.tf"]),
    ("thrift", &["*.thrift"]),
    ("toml", &["*.toml", "Cargo.lock"]),
    ("ts", &["*.ts", "*.tsx"]),
    ("twig", &["*.twig"]),
    ("txt", &["*.txt"]),
    ("typoscript", &["*.typoscript", "*.ts"]),
    ("vala", &["*.vala"]),
    ("vb", &["*.vb"]),
    ("vcl", &["*.vcl"]),
    ("verilog", &["*.v", "*.vh", "*.sv", "*.svh"]),
    ("vhdl", &["*.vhd", "*.vhdl"]),
    ("vim", &["*.vim"]),
    ("vimscript", &["*.vim"]),
    ("webidl", &["*.idl", "*.webidl", "*.widl"]),
    ("wiki", &["*.mediawiki", "*.wiki"]),
    ("xml", &[
        "*.xml", "*.xml.dist", "*.dtd", "*.xsl", "*.xslt", "*.xsd", "*.xjb",
        "*.rng", "*.sch", "*.xhtml",
    ]),
    ("xz", &["*.xz", "*.txz"]),
    ("yacc", &["*.y"]),
    ("yaml", &["*.yaml", "*.yml"]),
    ("yang", &["*.yang"]),
    ("z", &["*.Z"]),
    ("zig", &["*.zig"]),
    ("zsh", &[
        ".zshenv", "zshenv",
        ".zlogin", "zlogin",
        ".zlogout", "zlogout",
        ".zprofile", "zprofile",
        ".zshrc", "zshrc",
        "*.zsh",
    ]),
    ("zstd", &["*.zst", "*.zstd"]),
];
<|endoftext|>


<commit_before>/// This list represents the default file types that ripgrep ships with. In
/// general, any file format is fair game, although it should generally be
/// limited to reasonably popular open formats. For other cases, you can add
/// types to each invocation of ripgrep with the '--type-add' flag.
///
/// If you would like to add or improve this list, please file a PR:
/// <https://github.com/BurntSushi/ripgrep>.
///
/// Please try to keep this list sorted lexicographically and wrapped to 79
/// columns (inclusive).
#[rustfmt::skip]
pub const DEFAULT_TYPES: &[(&str, &[&str])] = &[
    ("agda", &["*.agda", "*.lagda"]),
    ("aidl", &["*.aidl"]),
    ("amake", &["*.mk", "*.bp"]),
    ("asciidoc", &["*.adoc", "*.asc", "*.asciidoc"]),
    ("asm", &["*.asm", "*.s", "*.S"]),
    ("asp", &[
        "*.aspx", "*.aspx.cs", "*.aspx.vb", "*.ascx", "*.ascx.cs", "*.ascx.vb",
    ]),
    ("ats", &["*.ats", "*.dats", "*.sats", "*.hats"]),
    ("avro", &["*.avdl", "*.avpr", "*.avsc"]),
    ("awk", &["*.awk"]),
    ("bazel", &["*.bazel", "*.bzl", "*.BUILD", "*.bazelrc", "BUILD", "WORKSPACE"]),
    ("bitbake", &["*.bb", "*.bbappend", "*.bbclass", "*.conf", "*.inc"]),
    ("brotli", &["*.br"]),
    ("buildstream", &["*.bst"]),
    ("bzip2", &["*.bz2", "*.tbz2"]),
    ("c", &["*.[chH]", "*.[chH].in", "*.cats"]),
    ("cabal", &["*.cabal"]),
    ("cbor", &["*.cbor"]),
    ("ceylon", &["*.ceylon"]),
    ("clojure", &["*.clj", "*.cljc", "*.cljs", "*.cljx"]),
    ("cmake", &["*.cmake", "CMakeLists.txt"]),
    ("coffeescript", &["*.coffee"]),
    ("config", &["*.cfg", "*.conf", "*.config", "*.ini"]),
    ("coq", &["*.v"]),
    ("cpp", &[
        "*.[ChH]", "*.cc", "*.[ch]pp", "*.[ch]xx", "*.hh",  "*.inl",
        "*.[ChH].in", "*.cc.in", "*.[ch]pp.in", "*.[ch]xx.in", "*.hh.in",
    ]),
    ("creole", &["*.creole"]),
    ("crystal", &["Projectfile", "*.cr", "*.ecr", "shard.yml"]),
    ("cs", &["*.cs"]),
    ("csharp", &["*.cs"]),
    ("cshtml", &["*.cshtml"]),
    ("css", &["*.css", "*.scss"]),
    ("csv", &["*.csv"]),
    ("cuda", &["*.cu", "*.cuh"]),
    ("cython", &["*.pyx", "*.pxi", "*.pxd"]),
    ("d", &["*.d"]),
    ("dart", &["*.dart"]),
    ("dhall", &["*.dhall"]),
    ("diff", &["*.patch", "*.diff"]),
    ("docker", &["*Dockerfile*"]),
    ("dvc", &["Dvcfile", "*.dvc"]),
    ("ebuild", &["*.ebuild"]),
    ("edn", &["*.edn"]),
    ("elisp", &["*.el"]),
    ("elixir", &["*.ex", "*.eex", "*.exs"]),
    ("elm", &["*.elm"]),
    ("erb", &["*.erb"]),
    ("erlang", &["*.erl", "*.hrl"]),
    ("fidl", &["*.fidl"]),
    ("fish", &["*.fish"]),
    ("flatbuffers", &["*.fbs"]),
    ("fortran", &[
        "*.f", "*.F", "*.f77", "*.F77", "*.pfo",
        "*.f90", "*.F90", "*.f95", "*.F95",
    ]),
    ("fsharp", &["*.fs", "*.fsx", "*.fsi"]),
    ("fut", &[".fut"]),
    ("gap", &["*.g", "*.gap", "*.gi", "*.gd", "*.tst"]),
    ("gn", &["*.gn", "*.gni"]),
    ("go", &["*.go"]),
    ("gradle", &["*.gradle"]),
    ("groovy", &["*.groovy", "*.gradle"]),
    ("gzip", &["*.gz", "*.tgz"]),
    ("h", &["*.h", "*.hpp"]),
    ("haml", &["*.haml"]),
    ("haskell", &["*.hs", "*.lhs", "*.cpphs", "*.c2hs", "*.hsc"]),
    ("hbs", &["*.hbs"]),
    ("hs", &["*.hs", "*.lhs"]),
    ("html", &["*.htm", "*.html", "*.ejs"]),
    ("idris", &["*.idr", "*.lidr"]),
    ("java", &["*.java", "*.jsp", "*.jspx", "*.properties"]),
    ("jinja", &["*.j2", "*.jinja", "*.jinja2"]),
    ("jl", &["*.jl"]),
    ("js", &["*.js", "*.jsx", "*.vue"]),
    ("json", &["*.json", "composer.lock"]),
    ("jsonl", &["*.jsonl"]),
    ("julia", &["*.jl"]),
    ("jupyter", &["*.ipynb", "*.jpynb"]),
    ("k", &["*.k"]),
    ("kotlin", &["*.kt", "*.kts"]),
    ("less", &["*.less"]),
    ("license", &[
        // General
        "COPYING", "COPYING[.-]*",
        "COPYRIGHT", "COPYRIGHT[.-]*",
        "EULA", "EULA[.-]*",
        "licen[cs]e", "licen[cs]e.*",
        "LICEN[CS]E", "LICEN[CS]E[.-]*", "*[.-]LICEN[CS]E*",
        "NOTICE", "NOTICE[.-]*",
        "PATENTS", "PATENTS[.-]*",
        "UNLICEN[CS]E", "UNLICEN[CS]E[.-]*",
        // GPL (gpl.txt, etc.)
        "agpl[.-]*",
        "gpl[.-]*",
        "lgpl[.-]*",
        // Other license-specific (APACHE-2.0.txt, etc.)
        "AGPL-*[0-9]*",
        "APACHE-*[0-9]*",
        "BSD-*[0-9]*",
        "CC-BY-*",
        "GFDL-*[0-9]*",
        "GNU-*[0-9]*",
        "GPL-*[0-9]*",
        "LGPL-*[0-9]*",
        "MIT-*[0-9]*",
        "MPL-*[0-9]*",
        "OFL-*[0-9]*",
    ]),
    ("lisp", &["*.el", "*.jl", "*.lisp", "*.lsp", "*.sc", "*.scm"]),
    ("lock", &["*.lock", "package-lock.json"]),
    ("log", &["*.log"]),
    ("lua", &["*.lua"]),
    ("lz4", &["*.lz4"]),
    ("lzma", &["*.lzma"]),
    ("m4", &["*.ac", "*.m4"]),
    ("make", &[
        "[Gg][Nn][Uu]makefile", "[Mm]akefile",
        "[Gg][Nn][Uu]makefile.am", "[Mm]akefile.am",
        "[Gg][Nn][Uu]makefile.in", "[Mm]akefile.in",
        "*.mk", "*.mak"
    ]),
    ("mako", &["*.mako", "*.mao"]),
    ("man", &["*.[0-9lnpx]", "*.[0-9][cEFMmpSx]"]),
    ("markdown", &["*.markdown", "*.md", "*.mdown", "*.mkdn"]),
    ("matlab", &["*.m"]),
    ("md", &["*.markdown", "*.md", "*.mdown", "*.mkdn"]),
    ("meson", &["meson.build", "meson_options.txt"]),
    ("minified", &["*.min.html", "*.min.css", "*.min.js"]),
    ("mint", &["*.mint"]),
    ("mk", &["mkfile"]),
    ("ml", &["*.ml"]),
    ("msbuild", &[
        "*.csproj", "*.fsproj", "*.vcxproj", "*.proj", "*.props", "*.targets",
    ]),
    ("nim", &["*.nim", "*.nimf", "*.nimble", "*.nims"]),
    ("nix", &["*.nix"]),
    ("objc", &["*.h", "*.m"]),
    ("objcpp", &["*.h", "*.mm"]),
    ("ocaml", &["*.ml", "*.mli", "*.mll", "*.mly"]),
    ("org", &["*.org", "*.org_archive"]),
    ("pascal", &["*.pas", "*.dpr", "*.lpr", "*.pp", "*.inc"]),
    ("pdf", &["*.pdf"]),
    ("perl", &["*.perl", "*.pl", "*.PL", "*.plh", "*.plx", "*.pm", "*.t"]),
    ("php", &["*.php", "*.php3", "*.php4", "*.php5", "*.phtml"]),
    ("po", &["*.po"]),
    ("pod", &["*.pod"]),
    ("postscript", &["*.eps", "*.ps"]),
    ("protobuf", &["*.proto"]),
    ("ps", &["*.cdxml", "*.ps1", "*.ps1xml", "*.psd1", "*.psm1"]),
    ("puppet", &["*.erb", "*.pp", "*.rb"]),
    ("purs", &["*.purs"]),
    ("py", &["*.py"]),
    ("qmake", &["*.pro", "*.pri", "*.prf"]),
    ("qml", &["*.qml"]),
    ("r", &["*.R", "*.r", "*.Rmd", "*.Rnw"]),
    ("racket", &["*.rkt"]),
    ("rdoc", &["*.rdoc"]),
    ("readme", &["README*", "*README"]),
    ("red", &["*.r", "*.red", "*.reds"]),
    ("robot", &["*.robot"]),
    ("rst", &["*.rst"]),
    ("ruby", &[
        // Idiomatic files
        "config.ru", "Gemfile", ".irbrc", "Rakefile",
        // Extensions
        "*.gemspec", "*.rb", "*.rbw"
    ]),
    ("rust", &["*.rs"]),
    ("sass", &["*.sass", "*.scss"]),
    ("scala", &["*.scala", "*.sbt"]),
    ("sh", &[
        // Portable/misc. init files
        ".login", ".logout", ".profile", "profile",
        // bash-specific init files
        ".bash_login", "bash_login",
        ".bash_logout", "bash_logout",
        ".bash_profile", "bash_profile",
        ".bashrc", "bashrc", "*.bashrc",
        // csh-specific init files
        ".cshrc", "*.cshrc",
        // ksh-specific init files
        ".kshrc", "*.kshrc",
        // tcsh-specific init files
        ".tcshrc",
        // zsh-specific init files
        ".zshenv", "zshenv",
        ".zlogin", "zlogin",
        ".zlogout", "zlogout",
        ".zprofile", "zprofile",
        ".zshrc", "zshrc",
        // Extensions
        "*.bash", "*.csh", "*.ksh", "*.sh", "*.tcsh", "*.zsh",
    ]),
    ("slim", &["*.skim", "*.slim", "*.slime"]),
    ("smarty", &["*.tpl"]),
    ("sml", &["*.sml", "*.sig"]),
    ("soy", &["*.soy"]),
    ("spark", &["*.spark"]),
    ("spec", &["*.spec"]),
    ("sql", &["*.sql", "*.psql"]),
    ("stylus", &["*.styl"]),
    ("sv", &["*.v", "*.vg", "*.sv", "*.svh", "*.h"]),
    ("svg", &["*.svg"]),
    ("swift", &["*.swift"]),
    ("swig", &["*.def", "*.i"]),
    ("systemd", &[
        "*.automount", "*.conf", "*.device", "*.link", "*.mount", "*.path",
        "*.scope", "*.service", "*.slice", "*.socket", "*.swap", "*.target",
        "*.timer",
    ]),
    ("taskpaper", &["*.taskpaper"]),
    ("tcl", &["*.tcl"]),
    ("tex", &["*.tex", "*.ltx", "*.cls", "*.sty", "*.bib", "*.dtx", "*.ins"]),
    ("textile", &["*.textile"]),
    ("tf", &["*.tf"]),
    ("thrift", &["*.thrift"]),
    ("toml", &["*.toml", "Cargo.lock"]),
    ("ts", &["*.ts", "*.tsx"]),
    ("twig", &["*.twig"]),
    ("txt", &["*.txt"]),
    ("typoscript", &["*.typoscript", "*.ts"]),
    ("vala", &["*.vala"]),
    ("vb", &["*.vb"]),
    ("vcl", &["*.vcl"]),
    ("verilog", &["*.v", "*.vh", "*.sv", "*.svh"]),
    ("vhdl", &["*.vhd", "*.vhdl"]),
    ("vim", &["*.vim"]),
    ("vimscript", &["*.vim"]),
    ("webidl", &["*.idl", "*.webidl", "*.widl"]),
    ("wiki", &["*.mediawiki", "*.wiki"]),
    ("xml", &[
        "*.xml", "*.xml.dist", "*.dtd", "*.xsl", "*.xslt", "*.xsd", "*.xjb",
        "*.rng", "*.sch", "*.xhtml",
    ]),
    ("xz", &["*.xz", "*.txz"]),
    ("yacc", &["*.y"]),
    ("yaml", &["*.yaml", "*.yml"]),
    ("yang", &["*.yang"]),
    ("z", &["*.Z"]),
    ("zig", &["*.zig"]),
    ("zsh", &[
        ".zshenv", "zshenv",
        ".zlogin", "zlogin",
        ".zlogout", "zlogout",
        ".zprofile", "zprofile",
        ".zshrc", "zshrc",
        "*.zsh",
    ]),
    ("zstd", &["*.zst", "*.zstd"]),
];
<commit_msg>ignore/types: add texinfo<commit_after>/// This list represents the default file types that ripgrep ships with. In
/// general, any file format is fair game, although it should generally be
/// limited to reasonably popular open formats. For other cases, you can add
/// types to each invocation of ripgrep with the '--type-add' flag.
///
/// If you would like to add or improve this list, please file a PR:
/// <https://github.com/BurntSushi/ripgrep>.
///
/// Please try to keep this list sorted lexicographically and wrapped to 79
/// columns (inclusive).
#[rustfmt::skip]
pub const DEFAULT_TYPES: &[(&str, &[&str])] = &[
    ("agda", &["*.agda", "*.lagda"]),
    ("aidl", &["*.aidl"]),
    ("amake", &["*.mk", "*.bp"]),
    ("asciidoc", &["*.adoc", "*.asc", "*.asciidoc"]),
    ("asm", &["*.asm", "*.s", "*.S"]),
    ("asp", &[
        "*.aspx", "*.aspx.cs", "*.aspx.vb", "*.ascx", "*.ascx.cs", "*.ascx.vb",
    ]),
    ("ats", &["*.ats", "*.dats", "*.sats", "*.hats"]),
    ("avro", &["*.avdl", "*.avpr", "*.avsc"]),
    ("awk", &["*.awk"]),
    ("bazel", &["*.bazel", "*.bzl", "*.BUILD", "*.bazelrc", "BUILD", "WORKSPACE"]),
    ("bitbake", &["*.bb", "*.bbappend", "*.bbclass", "*.conf", "*.inc"]),
    ("brotli", &["*.br"]),
    ("buildstream", &["*.bst"]),
    ("bzip2", &["*.bz2", "*.tbz2"]),
    ("c", &["*.[chH]", "*.[chH].in", "*.cats"]),
    ("cabal", &["*.cabal"]),
    ("cbor", &["*.cbor"]),
    ("ceylon", &["*.ceylon"]),
    ("clojure", &["*.clj", "*.cljc", "*.cljs", "*.cljx"]),
    ("cmake", &["*.cmake", "CMakeLists.txt"]),
    ("coffeescript", &["*.coffee"]),
    ("config", &["*.cfg", "*.conf", "*.config", "*.ini"]),
    ("coq", &["*.v"]),
    ("cpp", &[
        "*.[ChH]", "*.cc", "*.[ch]pp", "*.[ch]xx", "*.hh",  "*.inl",
        "*.[ChH].in", "*.cc.in", "*.[ch]pp.in", "*.[ch]xx.in", "*.hh.in",
    ]),
    ("creole", &["*.creole"]),
    ("crystal", &["Projectfile", "*.cr", "*.ecr", "shard.yml"]),
    ("cs", &["*.cs"]),
    ("csharp", &["*.cs"]),
    ("cshtml", &["*.cshtml"]),
    ("css", &["*.css", "*.scss"]),
    ("csv", &["*.csv"]),
    ("cuda", &["*.cu", "*.cuh"]),
    ("cython", &["*.pyx", "*.pxi", "*.pxd"]),
    ("d", &["*.d"]),
    ("dart", &["*.dart"]),
    ("dhall", &["*.dhall"]),
    ("diff", &["*.patch", "*.diff"]),
    ("docker", &["*Dockerfile*"]),
    ("dvc", &["Dvcfile", "*.dvc"]),
    ("ebuild", &["*.ebuild"]),
    ("edn", &["*.edn"]),
    ("elisp", &["*.el"]),
    ("elixir", &["*.ex", "*.eex", "*.exs"]),
    ("elm", &["*.elm"]),
    ("erb", &["*.erb"]),
    ("erlang", &["*.erl", "*.hrl"]),
    ("fidl", &["*.fidl"]),
    ("fish", &["*.fish"]),
    ("flatbuffers", &["*.fbs"]),
    ("fortran", &[
        "*.f", "*.F", "*.f77", "*.F77", "*.pfo",
        "*.f90", "*.F90", "*.f95", "*.F95",
    ]),
    ("fsharp", &["*.fs", "*.fsx", "*.fsi"]),
    ("fut", &[".fut"]),
    ("gap", &["*.g", "*.gap", "*.gi", "*.gd", "*.tst"]),
    ("gn", &["*.gn", "*.gni"]),
    ("go", &["*.go"]),
    ("gradle", &["*.gradle"]),
    ("groovy", &["*.groovy", "*.gradle"]),
    ("gzip", &["*.gz", "*.tgz"]),
    ("h", &["*.h", "*.hpp"]),
    ("haml", &["*.haml"]),
    ("haskell", &["*.hs", "*.lhs", "*.cpphs", "*.c2hs", "*.hsc"]),
    ("hbs", &["*.hbs"]),
    ("hs", &["*.hs", "*.lhs"]),
    ("html", &["*.htm", "*.html", "*.ejs"]),
    ("idris", &["*.idr", "*.lidr"]),
    ("java", &["*.java", "*.jsp", "*.jspx", "*.properties"]),
    ("jinja", &["*.j2", "*.jinja", "*.jinja2"]),
    ("jl", &["*.jl"]),
    ("js", &["*.js", "*.jsx", "*.vue"]),
    ("json", &["*.json", "composer.lock"]),
    ("jsonl", &["*.jsonl"]),
    ("julia", &["*.jl"]),
    ("jupyter", &["*.ipynb", "*.jpynb"]),
    ("k", &["*.k"]),
    ("kotlin", &["*.kt", "*.kts"]),
    ("less", &["*.less"]),
    ("license", &[
        // General
        "COPYING", "COPYING[.-]*",
        "COPYRIGHT", "COPYRIGHT[.-]*",
        "EULA", "EULA[.-]*",
        "licen[cs]e", "licen[cs]e.*",
        "LICEN[CS]E", "LICEN[CS]E[.-]*", "*[.-]LICEN[CS]E*",
        "NOTICE", "NOTICE[.-]*",
        "PATENTS", "PATENTS[.-]*",
        "UNLICEN[CS]E", "UNLICEN[CS]E[.-]*",
        // GPL (gpl.txt, etc.)
        "agpl[.-]*",
        "gpl[.-]*",
        "lgpl[.-]*",
        // Other license-specific (APACHE-2.0.txt, etc.)
        "AGPL-*[0-9]*",
        "APACHE-*[0-9]*",
        "BSD-*[0-9]*",
        "CC-BY-*",
        "GFDL-*[0-9]*",
        "GNU-*[0-9]*",
        "GPL-*[0-9]*",
        "LGPL-*[0-9]*",
        "MIT-*[0-9]*",
        "MPL-*[0-9]*",
        "OFL-*[0-9]*",
    ]),
    ("lisp", &["*.el", "*.jl", "*.lisp", "*.lsp", "*.sc", "*.scm"]),
    ("lock", &["*.lock", "package-lock.json"]),
    ("log", &["*.log"]),
    ("lua", &["*.lua"]),
    ("lz4", &["*.lz4"]),
    ("lzma", &["*.lzma"]),
    ("m4", &["*.ac", "*.m4"]),
    ("make", &[
        "[Gg][Nn][Uu]makefile", "[Mm]akefile",
        "[Gg][Nn][Uu]makefile.am", "[Mm]akefile.am",
        "[Gg][Nn][Uu]makefile.in", "[Mm]akefile.in",
        "*.mk", "*.mak"
    ]),
    ("mako", &["*.mako", "*.mao"]),
    ("man", &["*.[0-9lnpx]", "*.[0-9][cEFMmpSx]"]),
    ("markdown", &["*.markdown", "*.md", "*.mdown", "*.mkdn"]),
    ("matlab", &["*.m"]),
    ("md", &["*.markdown", "*.md", "*.mdown", "*.mkdn"]),
    ("meson", &["meson.build", "meson_options.txt"]),
    ("minified", &["*.min.html", "*.min.css", "*.min.js"]),
    ("mint", &["*.mint"]),
    ("mk", &["mkfile"]),
    ("ml", &["*.ml"]),
    ("msbuild", &[
        "*.csproj", "*.fsproj", "*.vcxproj", "*.proj", "*.props", "*.targets",
    ]),
    ("nim", &["*.nim", "*.nimf", "*.nimble", "*.nims"]),
    ("nix", &["*.nix"]),
    ("objc", &["*.h", "*.m"]),
    ("objcpp", &["*.h", "*.mm"]),
    ("ocaml", &["*.ml", "*.mli", "*.mll", "*.mly"]),
    ("org", &["*.org", "*.org_archive"]),
    ("pascal", &["*.pas", "*.dpr", "*.lpr", "*.pp", "*.inc"]),
    ("pdf", &["*.pdf"]),
    ("perl", &["*.perl", "*.pl", "*.PL", "*.plh", "*.plx", "*.pm", "*.t"]),
    ("php", &["*.php", "*.php3", "*.php4", "*.php5", "*.phtml"]),
    ("po", &["*.po"]),
    ("pod", &["*.pod"]),
    ("postscript", &["*.eps", "*.ps"]),
    ("protobuf", &["*.proto"]),
    ("ps", &["*.cdxml", "*.ps1", "*.ps1xml", "*.psd1", "*.psm1"]),
    ("puppet", &["*.erb", "*.pp", "*.rb"]),
    ("purs", &["*.purs"]),
    ("py", &["*.py"]),
    ("qmake", &["*.pro", "*.pri", "*.prf"]),
    ("qml", &["*.qml"]),
    ("r", &["*.R", "*.r", "*.Rmd", "*.Rnw"]),
    ("racket", &["*.rkt"]),
    ("rdoc", &["*.rdoc"]),
    ("readme", &["README*", "*README"]),
    ("red", &["*.r", "*.red", "*.reds"]),
    ("robot", &["*.robot"]),
    ("rst", &["*.rst"]),
    ("ruby", &[
        // Idiomatic files
        "config.ru", "Gemfile", ".irbrc", "Rakefile",
        // Extensions
        "*.gemspec", "*.rb", "*.rbw"
    ]),
    ("rust", &["*.rs"]),
    ("sass", &["*.sass", "*.scss"]),
    ("scala", &["*.scala", "*.sbt"]),
    ("sh", &[
        // Portable/misc. init files
        ".login", ".logout", ".profile", "profile",
        // bash-specific init files
        ".bash_login", "bash_login",
        ".bash_logout", "bash_logout",
        ".bash_profile", "bash_profile",
        ".bashrc", "bashrc", "*.bashrc",
        // csh-specific init files
        ".cshrc", "*.cshrc",
        // ksh-specific init files
        ".kshrc", "*.kshrc",
        // tcsh-specific init files
        ".tcshrc",
        // zsh-specific init files
        ".zshenv", "zshenv",
        ".zlogin", "zlogin",
        ".zlogout", "zlogout",
        ".zprofile", "zprofile",
        ".zshrc", "zshrc",
        // Extensions
        "*.bash", "*.csh", "*.ksh", "*.sh", "*.tcsh", "*.zsh",
    ]),
    ("slim", &["*.skim", "*.slim", "*.slime"]),
    ("smarty", &["*.tpl"]),
    ("sml", &["*.sml", "*.sig"]),
    ("soy", &["*.soy"]),
    ("spark", &["*.spark"]),
    ("spec", &["*.spec"]),
    ("sql", &["*.sql", "*.psql"]),
    ("stylus", &["*.styl"]),
    ("sv", &["*.v", "*.vg", "*.sv", "*.svh", "*.h"]),
    ("svg", &["*.svg"]),
    ("swift", &["*.swift"]),
    ("swig", &["*.def", "*.i"]),
    ("systemd", &[
        "*.automount", "*.conf", "*.device", "*.link", "*.mount", "*.path",
        "*.scope", "*.service", "*.slice", "*.socket", "*.swap", "*.target",
        "*.timer",
    ]),
    ("taskpaper", &["*.taskpaper"]),
    ("tcl", &["*.tcl"]),
    ("tex", &["*.tex", "*.ltx", "*.cls", "*.sty", "*.bib", "*.dtx", "*.ins"]),
    ("texinfo", &["*.texi"]),
    ("textile", &["*.textile"]),
    ("tf", &["*.tf"]),
    ("thrift", &["*.thrift"]),
    ("toml", &["*.toml", "Cargo.lock"]),
    ("ts", &["*.ts", "*.tsx"]),
    ("twig", &["*.twig"]),
    ("txt", &["*.txt"]),
    ("typoscript", &["*.typoscript", "*.ts"]),
    ("vala", &["*.vala"]),
    ("vb", &["*.vb"]),
    ("vcl", &["*.vcl"]),
    ("verilog", &["*.v", "*.vh", "*.sv", "*.svh"]),
    ("vhdl", &["*.vhd", "*.vhdl"]),
    ("vim", &["*.vim"]),
    ("vimscript", &["*.vim"]),
    ("webidl", &["*.idl", "*.webidl", "*.widl"]),
    ("wiki", &["*.mediawiki", "*.wiki"]),
    ("xml", &[
        "*.xml", "*.xml.dist", "*.dtd", "*.xsl", "*.xslt", "*.xsd", "*.xjb",
        "*.rng", "*.sch", "*.xhtml",
    ]),
    ("xz", &["*.xz", "*.txz"]),
    ("yacc", &["*.y"]),
    ("yaml", &["*.yaml", "*.yml"]),
    ("yang", &["*.yang"]),
    ("z", &["*.Z"]),
    ("zig", &["*.zig"]),
    ("zsh", &[
        ".zshenv", "zshenv",
        ".zlogin", "zlogin",
        ".zlogout", "zlogout",
        ".zprofile", "zprofile",
        ".zshrc", "zshrc",
        "*.zsh",
    ]),
    ("zstd", &["*.zst", "*.zstd"]),
];
<|endoftext|>


<commit_before>//! Handles dynamic library loading for proc macro

use std::{
    convert::{TryFrom, TryInto},
    fmt,
    fs::File,
    io,
    path::{Path, PathBuf},
};

use libloading::Library;
use memmap2::Mmap;
use object::Object;
use proc_macro_api::{read_dylib_info, ProcMacroKind, RustCInfo};

use crate::{
    proc_macro::bridge::{self as stable_bridge, client::ProcMacro as StableProcMacro},
    rustc_server::TokenStream as StableTokenStream,
};
use crate::{
    proc_macro_nightly::bridge::{self as nightly_bridge, client::ProcMacro as NightlyProcMacro},
    rustc_server_nightly::TokenStream as NightlyTokenStream,
};

const NEW_REGISTRAR_SYMBOL: &str = "_rustc_proc_macro_decls_";

fn invalid_data_err(e: impl Into<Box<dyn std::error::Error + Send + Sync>>) -> io::Error {
    io::Error::new(io::ErrorKind::InvalidData, e)
}

fn is_derive_registrar_symbol(symbol: &str) -> bool {
    symbol.contains(NEW_REGISTRAR_SYMBOL)
}

fn find_registrar_symbol(file: &Path) -> io::Result<Option<String>> {
    let file = File::open(file)?;
    let buffer = unsafe { Mmap::map(&file)? };

    Ok(object::File::parse(&*buffer)
        .map_err(invalid_data_err)?
        .exports()
        .map_err(invalid_data_err)?
        .into_iter()
        .map(|export| export.name())
        .filter_map(|sym| String::from_utf8(sym.into()).ok())
        .find(|sym| is_derive_registrar_symbol(sym))
        .map(|sym| {
            // From MacOS docs:
            // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/dlsym.3.html
            // Unlike other dyld API's, the symbol name passed to dlsym() must NOT be
            // prepended with an underscore.
            if cfg!(target_os = "macos") && sym.starts_with('_') {
                sym[1..].to_owned()
            } else {
                sym
            }
        }))
}

/// Loads dynamic library in platform dependent manner.
///
/// For unix, you have to use RTLD_DEEPBIND flag to escape problems described
/// [here](https://github.com/fedochet/rust-proc-macro-panic-inside-panic-expample)
/// and [here](https://github.com/rust-lang/rust/issues/60593).
///
/// Usage of RTLD_DEEPBIND
/// [here](https://github.com/fedochet/rust-proc-macro-panic-inside-panic-expample/issues/1)
///
/// It seems that on Windows that behaviour is default, so we do nothing in that case.
#[cfg(windows)]
fn load_library(file: &Path) -> Result<Library, libloading::Error> {
    unsafe { Library::new(file) }
}

#[cfg(unix)]
fn load_library(file: &Path) -> Result<Library, libloading::Error> {
    use libloading::os::unix::Library as UnixLibrary;
    use std::os::raw::c_int;

    const RTLD_NOW: c_int = 0x00002;
    const RTLD_DEEPBIND: c_int = 0x00008;

    unsafe { UnixLibrary::open(Some(file), RTLD_NOW | RTLD_DEEPBIND).map(|lib| lib.into()) }
}

enum ProcMacroABI {
    Stable,
    Nightly,
}

impl TryFrom<RustCInfo> for ProcMacroABI {
    type Error = LoadProcMacroDylibError;

    fn try_from(info: RustCInfo) -> Result<Self, Self::Error> {
        if info.version.0 < 1 {
            Err(LoadProcMacroDylibError::UnsupportedABI)
        } else if info.version.1 < 47 {
            Err(LoadProcMacroDylibError::UnsupportedABI)
        } else if info.version.1 < 54 {
            Ok(ProcMacroABI::Stable)
        } else {
            Ok(ProcMacroABI::Nightly)
        }
    }
}

#[derive(Debug)]
pub enum LoadProcMacroDylibError {
    Io(io::Error),
    UnsupportedABI,
}

impl fmt::Display for LoadProcMacroDylibError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Io(e) => e.fmt(f),
            Self::UnsupportedABI => write!(f, "unsupported ABI version"),
        }
    }
}

impl From<io::Error> for LoadProcMacroDylibError {
    fn from(e: io::Error) -> Self {
        LoadProcMacroDylibError::Io(e)
    }
}

enum ProcMacroLibraryLibloading {
    StableProcMacroLibrary {
        _lib: Library,
        exported_macros: Vec<crate::proc_macro::bridge::client::ProcMacro>,
    },
    NightlyProcMacroLibrary {
        _lib: Library,
        exported_macros: Vec<crate::proc_macro_nightly::bridge::client::ProcMacro>,
    },
}

impl ProcMacroLibraryLibloading {
    fn open(file: &Path) -> Result<Self, LoadProcMacroDylibError> {
        let symbol_name = find_registrar_symbol(file)?.ok_or_else(|| {
            invalid_data_err(format!("Cannot find registrar symbol in file {}", file.display()))
        })?;

        let version_info = read_dylib_info(file)?;
        let macro_abi: ProcMacroABI = version_info.try_into()?;

        let lib = load_library(file).map_err(invalid_data_err)?;
        match macro_abi {
            ProcMacroABI::Stable => {
                let macros: libloading::Symbol<&&[crate::proc_macro::bridge::client::ProcMacro]> =
                    unsafe { lib.get(symbol_name.as_bytes()) }.map_err(invalid_data_err)?;
                let macros_vec = macros.to_vec();
                Ok(ProcMacroLibraryLibloading::StableProcMacroLibrary {
                    _lib: lib,
                    exported_macros: macros_vec,
                })
            }
            ProcMacroABI::Nightly => {
                let macros: libloading::Symbol<
                    &&[crate::proc_macro_nightly::bridge::client::ProcMacro],
                > = unsafe { lib.get(symbol_name.as_bytes()) }.map_err(invalid_data_err)?;
                let macros_vec = macros.to_vec();
                Ok(ProcMacroLibraryLibloading::NightlyProcMacroLibrary {
                    _lib: lib,
                    exported_macros: macros_vec,
                })
            }
        }
    }
}

#[derive(Debug)]
pub enum PanicMessage {
    Stable(stable_bridge::PanicMessage),
    Nightly(nightly_bridge::PanicMessage),
}

impl From<stable_bridge::PanicMessage> for PanicMessage {
    fn from(p: stable_bridge::PanicMessage) -> Self {
        PanicMessage::Stable(p)
    }
}

impl From<nightly_bridge::PanicMessage> for PanicMessage {
    fn from(p: nightly_bridge::PanicMessage) -> Self {
        PanicMessage::Nightly(p)
    }
}

impl PanicMessage {
    pub fn as_str(&self) -> Option<&str> {
        match self {
            Self::Stable(p) => p.as_str(),
            Self::Nightly(p) => p.as_str(),
        }
    }
}

pub struct Expander {
    inner: ProcMacroLibraryLibloading,
}

impl Expander {
    pub fn new(lib: &Path) -> Result<Expander, LoadProcMacroDylibError> {
        // Some libraries for dynamic loading require canonicalized path even when it is
        // already absolute
        let lib = lib.canonicalize()?;

        let lib = ensure_file_with_lock_free_access(&lib)?;

        let library = ProcMacroLibraryLibloading::open(&lib)?;

        Ok(Expander { inner: library })
    }

    pub fn expand(
        &self,
        macro_name: &str,
        macro_body: &tt::Subtree,
        attributes: Option<&tt::Subtree>,
    ) -> Result<tt::Subtree, PanicMessage> {
        match &self.inner {
            ProcMacroLibraryLibloading::StableProcMacroLibrary { exported_macros, .. } => {
                expand_stable(macro_name, macro_body, attributes, &exported_macros[..])
                    .map_err(PanicMessage::from)
            }
            ProcMacroLibraryLibloading::NightlyProcMacroLibrary { exported_macros, .. } => {
                expand_nightly(macro_name, macro_body, attributes, &exported_macros[..])
                    .map_err(PanicMessage::from)
            }
        }
    }

    pub fn list_macros(&self) -> Vec<(String, ProcMacroKind)> {
        match &self.inner {
            ProcMacroLibraryLibloading::StableProcMacroLibrary { exported_macros, .. } => {
                list_macros_stable(&exported_macros[..])
            }
            ProcMacroLibraryLibloading::NightlyProcMacroLibrary { exported_macros, .. } => {
                list_macros_nightly(&exported_macros[..])
            }
        }
    }
}

/// Copy the dylib to temp directory to prevent locking in Windows
#[cfg(windows)]
fn ensure_file_with_lock_free_access(path: &Path) -> io::Result<PathBuf> {
    use std::collections::hash_map::RandomState;
    use std::ffi::OsString;
    use std::hash::{BuildHasher, Hasher};

    let mut to = std::env::temp_dir();

    let file_name = path.file_name().ok_or_else(|| {
        io::Error::new(
            io::ErrorKind::InvalidInput,
            format!("File path is invalid: {}", path.display()),
        )
    })?;

    // Generate a unique number by abusing `HashMap`'s hasher.
    // Maybe this will also "inspire" a libs team member to finally put `rand` in libstd.
    let t = RandomState::new().build_hasher().finish();

    let mut unique_name = OsString::from(t.to_string());
    unique_name.push(file_name);

    to.push(unique_name);
    std::fs::copy(path, &to).unwrap();
    Ok(to)
}

#[cfg(unix)]
fn ensure_file_with_lock_free_access(path: &Path) -> io::Result<PathBuf> {
    Ok(path.to_path_buf())
}

fn expand_nightly(
    macro_name: &str,
    macro_body: &tt::Subtree,
    attributes: Option<&tt::Subtree>,
    macros: &[NightlyProcMacro],
) -> Result<tt::Subtree, crate::proc_macro_nightly::bridge::PanicMessage> {
    let parsed_body = NightlyTokenStream::with_subtree(macro_body.clone());

    let parsed_attributes = attributes
        .map_or(crate::rustc_server_nightly::TokenStream::new(), |attr| {
            NightlyTokenStream::with_subtree(attr.clone())
        });

    for proc_macro in macros {
        match proc_macro {
            crate::proc_macro_nightly::bridge::client::ProcMacro::CustomDerive {
                trait_name,
                client,
                ..
            } if *trait_name == macro_name => {
                let res = client.run(
                    &crate::proc_macro_nightly::bridge::server::SameThread,
                    crate::rustc_server_nightly::Rustc::default(),
                    parsed_body,
                    false,
                );
                return res.map(|it| it.into_subtree());
            }
            crate::proc_macro_nightly::bridge::client::ProcMacro::Bang { name, client }
                if *name == macro_name =>
            {
                let res = client.run(
                    &crate::proc_macro_nightly::bridge::server::SameThread,
                    crate::rustc_server_nightly::Rustc::default(),
                    parsed_body,
                    false,
                );
                return res.map(|it| it.into_subtree());
            }
            crate::proc_macro_nightly::bridge::client::ProcMacro::Attr { name, client }
                if *name == macro_name =>
            {
                let res = client.run(
                    &crate::proc_macro_nightly::bridge::server::SameThread,
                    crate::rustc_server_nightly::Rustc::default(),
                    parsed_attributes,
                    parsed_body,
                    false,
                );
                return res.map(|it| it.into_subtree());
            }
            _ => continue,
        }
    }

    Err(crate::proc_macro_nightly::bridge::PanicMessage::String("Nothing to expand".to_string()))
}

fn expand_stable(
    macro_name: &str,
    macro_body: &tt::Subtree,
    attributes: Option<&tt::Subtree>,
    macros: &[StableProcMacro],
) -> Result<tt::Subtree, crate::proc_macro::bridge::PanicMessage> {
    let parsed_body = StableTokenStream::with_subtree(macro_body.clone());

    let parsed_attributes = attributes.map_or(crate::rustc_server::TokenStream::new(), |attr| {
        StableTokenStream::with_subtree(attr.clone())
    });

    for proc_macro in macros {
        match proc_macro {
            crate::proc_macro::bridge::client::ProcMacro::CustomDerive {
                trait_name,
                client,
                ..
            } if *trait_name == macro_name => {
                let res = client.run(
                    &crate::proc_macro::bridge::server::SameThread,
                    crate::rustc_server::Rustc::default(),
                    parsed_body,
                    false,
                );
                return res.map(|it| it.into_subtree());
            }
            crate::proc_macro::bridge::client::ProcMacro::Bang { name, client }
                if *name == macro_name =>
            {
                let res = client.run(
                    &crate::proc_macro::bridge::server::SameThread,
                    crate::rustc_server::Rustc::default(),
                    parsed_body,
                    false,
                );
                return res.map(|it| it.into_subtree());
            }
            crate::proc_macro::bridge::client::ProcMacro::Attr { name, client }
                if *name == macro_name =>
            {
                let res = client.run(
                    &crate::proc_macro::bridge::server::SameThread,
                    crate::rustc_server::Rustc::default(),
                    parsed_attributes,
                    parsed_body,
                    false,
                );
                return res.map(|it| it.into_subtree());
            }
            _ => continue,
        }
    }

    Err(crate::proc_macro::bridge::PanicMessage::String("Nothing to expand".to_string()))
}

pub fn list_macros_stable(macros: &[StableProcMacro]) -> Vec<(String, ProcMacroKind)> {
    macros
        .iter()
        .map(|proc_macro| match proc_macro {
            crate::proc_macro::bridge::client::ProcMacro::CustomDerive { trait_name, .. } => {
                (trait_name.to_string(), ProcMacroKind::CustomDerive)
            }
            crate::proc_macro::bridge::client::ProcMacro::Bang { name, .. } => {
                (name.to_string(), ProcMacroKind::FuncLike)
            }
            crate::proc_macro::bridge::client::ProcMacro::Attr { name, .. } => {
                (name.to_string(), ProcMacroKind::Attr)
            }
        })
        .collect()
}

pub fn list_macros_nightly(macros: &[NightlyProcMacro]) -> Vec<(String, ProcMacroKind)> {
    macros
        .iter()
        .map(|proc_macro| match proc_macro {
            crate::proc_macro_nightly::bridge::client::ProcMacro::CustomDerive {
                trait_name,
                ..
            } => (trait_name.to_string(), ProcMacroKind::CustomDerive),
            crate::proc_macro_nightly::bridge::client::ProcMacro::Bang { name, .. } => {
                (name.to_string(), ProcMacroKind::FuncLike)
            }
            crate::proc_macro_nightly::bridge::client::ProcMacro::Attr { name, .. } => {
                (name.to_string(), ProcMacroKind::Attr)
            }
        })
        .collect()
}
<commit_msg>check rustc major version == 1 not < 1<commit_after>//! Handles dynamic library loading for proc macro

use std::{
    convert::{TryFrom, TryInto},
    fmt,
    fs::File,
    io,
    path::{Path, PathBuf},
};

use libloading::Library;
use memmap2::Mmap;
use object::Object;
use proc_macro_api::{read_dylib_info, ProcMacroKind, RustCInfo};

use crate::{
    proc_macro::bridge::{self as stable_bridge, client::ProcMacro as StableProcMacro},
    rustc_server::TokenStream as StableTokenStream,
};
use crate::{
    proc_macro_nightly::bridge::{self as nightly_bridge, client::ProcMacro as NightlyProcMacro},
    rustc_server_nightly::TokenStream as NightlyTokenStream,
};

const NEW_REGISTRAR_SYMBOL: &str = "_rustc_proc_macro_decls_";

fn invalid_data_err(e: impl Into<Box<dyn std::error::Error + Send + Sync>>) -> io::Error {
    io::Error::new(io::ErrorKind::InvalidData, e)
}

fn is_derive_registrar_symbol(symbol: &str) -> bool {
    symbol.contains(NEW_REGISTRAR_SYMBOL)
}

fn find_registrar_symbol(file: &Path) -> io::Result<Option<String>> {
    let file = File::open(file)?;
    let buffer = unsafe { Mmap::map(&file)? };

    Ok(object::File::parse(&*buffer)
        .map_err(invalid_data_err)?
        .exports()
        .map_err(invalid_data_err)?
        .into_iter()
        .map(|export| export.name())
        .filter_map(|sym| String::from_utf8(sym.into()).ok())
        .find(|sym| is_derive_registrar_symbol(sym))
        .map(|sym| {
            // From MacOS docs:
            // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/dlsym.3.html
            // Unlike other dyld API's, the symbol name passed to dlsym() must NOT be
            // prepended with an underscore.
            if cfg!(target_os = "macos") && sym.starts_with('_') {
                sym[1..].to_owned()
            } else {
                sym
            }
        }))
}

/// Loads dynamic library in platform dependent manner.
///
/// For unix, you have to use RTLD_DEEPBIND flag to escape problems described
/// [here](https://github.com/fedochet/rust-proc-macro-panic-inside-panic-expample)
/// and [here](https://github.com/rust-lang/rust/issues/60593).
///
/// Usage of RTLD_DEEPBIND
/// [here](https://github.com/fedochet/rust-proc-macro-panic-inside-panic-expample/issues/1)
///
/// It seems that on Windows that behaviour is default, so we do nothing in that case.
#[cfg(windows)]
fn load_library(file: &Path) -> Result<Library, libloading::Error> {
    unsafe { Library::new(file) }
}

#[cfg(unix)]
fn load_library(file: &Path) -> Result<Library, libloading::Error> {
    use libloading::os::unix::Library as UnixLibrary;
    use std::os::raw::c_int;

    const RTLD_NOW: c_int = 0x00002;
    const RTLD_DEEPBIND: c_int = 0x00008;

    unsafe { UnixLibrary::open(Some(file), RTLD_NOW | RTLD_DEEPBIND).map(|lib| lib.into()) }
}

enum ProcMacroABI {
    Stable,
    Nightly,
}

impl TryFrom<RustCInfo> for ProcMacroABI {
    type Error = LoadProcMacroDylibError;

    fn try_from(info: RustCInfo) -> Result<Self, Self::Error> {
        if info.version.0 != 1 {
            Err(LoadProcMacroDylibError::UnsupportedABI)
        } else if info.version.1 < 47 {
            Err(LoadProcMacroDylibError::UnsupportedABI)
        } else if info.version.1 < 54 {
            Ok(ProcMacroABI::Stable)
        } else {
            Ok(ProcMacroABI::Nightly)
        }
    }
}

#[derive(Debug)]
pub enum LoadProcMacroDylibError {
    Io(io::Error),
    UnsupportedABI,
}

impl fmt::Display for LoadProcMacroDylibError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Io(e) => e.fmt(f),
            Self::UnsupportedABI => write!(f, "unsupported ABI version"),
        }
    }
}

impl From<io::Error> for LoadProcMacroDylibError {
    fn from(e: io::Error) -> Self {
        LoadProcMacroDylibError::Io(e)
    }
}

enum ProcMacroLibraryLibloading {
    StableProcMacroLibrary {
        _lib: Library,
        exported_macros: Vec<crate::proc_macro::bridge::client::ProcMacro>,
    },
    NightlyProcMacroLibrary {
        _lib: Library,
        exported_macros: Vec<crate::proc_macro_nightly::bridge::client::ProcMacro>,
    },
}

impl ProcMacroLibraryLibloading {
    fn open(file: &Path) -> Result<Self, LoadProcMacroDylibError> {
        let symbol_name = find_registrar_symbol(file)?.ok_or_else(|| {
            invalid_data_err(format!("Cannot find registrar symbol in file {}", file.display()))
        })?;

        let version_info = read_dylib_info(file)?;
        let macro_abi: ProcMacroABI = version_info.try_into()?;

        let lib = load_library(file).map_err(invalid_data_err)?;
        match macro_abi {
            ProcMacroABI::Stable => {
                let macros: libloading::Symbol<&&[crate::proc_macro::bridge::client::ProcMacro]> =
                    unsafe { lib.get(symbol_name.as_bytes()) }.map_err(invalid_data_err)?;
                let macros_vec = macros.to_vec();
                Ok(ProcMacroLibraryLibloading::StableProcMacroLibrary {
                    _lib: lib,
                    exported_macros: macros_vec,
                })
            }
            ProcMacroABI::Nightly => {
                let macros: libloading::Symbol<
                    &&[crate::proc_macro_nightly::bridge::client::ProcMacro],
                > = unsafe { lib.get(symbol_name.as_bytes()) }.map_err(invalid_data_err)?;
                let macros_vec = macros.to_vec();
                Ok(ProcMacroLibraryLibloading::NightlyProcMacroLibrary {
                    _lib: lib,
                    exported_macros: macros_vec,
                })
            }
        }
    }
}

#[derive(Debug)]
pub enum PanicMessage {
    Stable(stable_bridge::PanicMessage),
    Nightly(nightly_bridge::PanicMessage),
}

impl From<stable_bridge::PanicMessage> for PanicMessage {
    fn from(p: stable_bridge::PanicMessage) -> Self {
        PanicMessage::Stable(p)
    }
}

impl From<nightly_bridge::PanicMessage> for PanicMessage {
    fn from(p: nightly_bridge::PanicMessage) -> Self {
        PanicMessage::Nightly(p)
    }
}

impl PanicMessage {
    pub fn as_str(&self) -> Option<&str> {
        match self {
            Self::Stable(p) => p.as_str(),
            Self::Nightly(p) => p.as_str(),
        }
    }
}

pub struct Expander {
    inner: ProcMacroLibraryLibloading,
}

impl Expander {
    pub fn new(lib: &Path) -> Result<Expander, LoadProcMacroDylibError> {
        // Some libraries for dynamic loading require canonicalized path even when it is
        // already absolute
        let lib = lib.canonicalize()?;

        let lib = ensure_file_with_lock_free_access(&lib)?;

        let library = ProcMacroLibraryLibloading::open(&lib)?;

        Ok(Expander { inner: library })
    }

    pub fn expand(
        &self,
        macro_name: &str,
        macro_body: &tt::Subtree,
        attributes: Option<&tt::Subtree>,
    ) -> Result<tt::Subtree, PanicMessage> {
        match &self.inner {
            ProcMacroLibraryLibloading::StableProcMacroLibrary { exported_macros, .. } => {
                expand_stable(macro_name, macro_body, attributes, &exported_macros[..])
                    .map_err(PanicMessage::from)
            }
            ProcMacroLibraryLibloading::NightlyProcMacroLibrary { exported_macros, .. } => {
                expand_nightly(macro_name, macro_body, attributes, &exported_macros[..])
                    .map_err(PanicMessage::from)
            }
        }
    }

    pub fn list_macros(&self) -> Vec<(String, ProcMacroKind)> {
        match &self.inner {
            ProcMacroLibraryLibloading::StableProcMacroLibrary { exported_macros, .. } => {
                list_macros_stable(&exported_macros[..])
            }
            ProcMacroLibraryLibloading::NightlyProcMacroLibrary { exported_macros, .. } => {
                list_macros_nightly(&exported_macros[..])
            }
        }
    }
}

/// Copy the dylib to temp directory to prevent locking in Windows
#[cfg(windows)]
fn ensure_file_with_lock_free_access(path: &Path) -> io::Result<PathBuf> {
    use std::collections::hash_map::RandomState;
    use std::ffi::OsString;
    use std::hash::{BuildHasher, Hasher};

    let mut to = std::env::temp_dir();

    let file_name = path.file_name().ok_or_else(|| {
        io::Error::new(
            io::ErrorKind::InvalidInput,
            format!("File path is invalid: {}", path.display()),
        )
    })?;

    // Generate a unique number by abusing `HashMap`'s hasher.
    // Maybe this will also "inspire" a libs team member to finally put `rand` in libstd.
    let t = RandomState::new().build_hasher().finish();

    let mut unique_name = OsString::from(t.to_string());
    unique_name.push(file_name);

    to.push(unique_name);
    std::fs::copy(path, &to).unwrap();
    Ok(to)
}

#[cfg(unix)]
fn ensure_file_with_lock_free_access(path: &Path) -> io::Result<PathBuf> {
    Ok(path.to_path_buf())
}

fn expand_nightly(
    macro_name: &str,
    macro_body: &tt::Subtree,
    attributes: Option<&tt::Subtree>,
    macros: &[NightlyProcMacro],
) -> Result<tt::Subtree, crate::proc_macro_nightly::bridge::PanicMessage> {
    let parsed_body = NightlyTokenStream::with_subtree(macro_body.clone());

    let parsed_attributes = attributes
        .map_or(crate::rustc_server_nightly::TokenStream::new(), |attr| {
            NightlyTokenStream::with_subtree(attr.clone())
        });

    for proc_macro in macros {
        match proc_macro {
            crate::proc_macro_nightly::bridge::client::ProcMacro::CustomDerive {
                trait_name,
                client,
                ..
            } if *trait_name == macro_name => {
                let res = client.run(
                    &crate::proc_macro_nightly::bridge::server::SameThread,
                    crate::rustc_server_nightly::Rustc::default(),
                    parsed_body,
                    false,
                );
                return res.map(|it| it.into_subtree());
            }
            crate::proc_macro_nightly::bridge::client::ProcMacro::Bang { name, client }
                if *name == macro_name =>
            {
                let res = client.run(
                    &crate::proc_macro_nightly::bridge::server::SameThread,
                    crate::rustc_server_nightly::Rustc::default(),
                    parsed_body,
                    false,
                );
                return res.map(|it| it.into_subtree());
            }
            crate::proc_macro_nightly::bridge::client::ProcMacro::Attr { name, client }
                if *name == macro_name =>
            {
                let res = client.run(
                    &crate::proc_macro_nightly::bridge::server::SameThread,
                    crate::rustc_server_nightly::Rustc::default(),
                    parsed_attributes,
                    parsed_body,
                    false,
                );
                return res.map(|it| it.into_subtree());
            }
            _ => continue,
        }
    }

    Err(crate::proc_macro_nightly::bridge::PanicMessage::String("Nothing to expand".to_string()))
}

fn expand_stable(
    macro_name: &str,
    macro_body: &tt::Subtree,
    attributes: Option<&tt::Subtree>,
    macros: &[StableProcMacro],
) -> Result<tt::Subtree, crate::proc_macro::bridge::PanicMessage> {
    let parsed_body = StableTokenStream::with_subtree(macro_body.clone());

    let parsed_attributes = attributes.map_or(crate::rustc_server::TokenStream::new(), |attr| {
        StableTokenStream::with_subtree(attr.clone())
    });

    for proc_macro in macros {
        match proc_macro {
            crate::proc_macro::bridge::client::ProcMacro::CustomDerive {
                trait_name,
                client,
                ..
            } if *trait_name == macro_name => {
                let res = client.run(
                    &crate::proc_macro::bridge::server::SameThread,
                    crate::rustc_server::Rustc::default(),
                    parsed_body,
                    false,
                );
                return res.map(|it| it.into_subtree());
            }
            crate::proc_macro::bridge::client::ProcMacro::Bang { name, client }
                if *name == macro_name =>
            {
                let res = client.run(
                    &crate::proc_macro::bridge::server::SameThread,
                    crate::rustc_server::Rustc::default(),
                    parsed_body,
                    false,
                );
                return res.map(|it| it.into_subtree());
            }
            crate::proc_macro::bridge::client::ProcMacro::Attr { name, client }
                if *name == macro_name =>
            {
                let res = client.run(
                    &crate::proc_macro::bridge::server::SameThread,
                    crate::rustc_server::Rustc::default(),
                    parsed_attributes,
                    parsed_body,
                    false,
                );
                return res.map(|it| it.into_subtree());
            }
            _ => continue,
        }
    }

    Err(crate::proc_macro::bridge::PanicMessage::String("Nothing to expand".to_string()))
}

pub fn list_macros_stable(macros: &[StableProcMacro]) -> Vec<(String, ProcMacroKind)> {
    macros
        .iter()
        .map(|proc_macro| match proc_macro {
            crate::proc_macro::bridge::client::ProcMacro::CustomDerive { trait_name, .. } => {
                (trait_name.to_string(), ProcMacroKind::CustomDerive)
            }
            crate::proc_macro::bridge::client::ProcMacro::Bang { name, .. } => {
                (name.to_string(), ProcMacroKind::FuncLike)
            }
            crate::proc_macro::bridge::client::ProcMacro::Attr { name, .. } => {
                (name.to_string(), ProcMacroKind::Attr)
            }
        })
        .collect()
}

pub fn list_macros_nightly(macros: &[NightlyProcMacro]) -> Vec<(String, ProcMacroKind)> {
    macros
        .iter()
        .map(|proc_macro| match proc_macro {
            crate::proc_macro_nightly::bridge::client::ProcMacro::CustomDerive {
                trait_name,
                ..
            } => (trait_name.to_string(), ProcMacroKind::CustomDerive),
            crate::proc_macro_nightly::bridge::client::ProcMacro::Bang { name, .. } => {
                (name.to_string(), ProcMacroKind::FuncLike)
            }
            crate::proc_macro_nightly::bridge::client::ProcMacro::Attr { name, .. } => {
                (name.to_string(), ProcMacroKind::Attr)
            }
        })
        .collect()
}
<|endoftext|>


<commit_before>use std;

export token;
export compile;

enum token {
    text(str),
    etag(str),
    utag(str),
    section({ name: str, inverted: bool, children: @[token] }),
    incomplete_section(str, bool),
    partial(str),
}

enum state { TEXT, OTAG, TAG, CTAG }

type parser = {
    mutable state: state,
    mutable otag: str,
    mutable ctag: str,
    mutable content: str,
    mutable tag_position: uint,
    mutable tokens: [token],
};

fn compile(src: str) -> [token] {
    let parser : parser = {
        mutable state: TEXT,
        mutable otag: "{{",
        mutable ctag: "}}",
        mutable content: "",
        mutable tag_position: 0u,
        mutable tokens: [],
    };

    let curly_brace_tag = false;

    str::bytes_iter(src) { |c|
        alt parser.state {
          TEXT {
            if c == parser.otag[0] {
                if str::byte_len(parser.otag) > 1u {
                    parser.tag_position = 1u;
                    parser.state = OTAG;
                } else {
                    add_text(parser);
                    parser.state = TAG;
                }
            } else {
                str::push_byte(parser.content, c);
            }
          }
          OTAG {
            if c == parser.otag[parser.tag_position] {
                if parser.tag_position == str::byte_len(parser.otag) - 1u {
                    add_text(parser);
                    curly_brace_tag = false;
                    parser.state = TAG;
                } else {
                    parser.tag_position += 1u;
                }
            } else {
                // We don't have a tag, so add all the tag parts we've seen
                // so far to the string.
                parser.state = TEXT;
                not_otag(parser);
                str::push_byte(parser.content, c);
            }
          }
          TAG {
              if parser.content == "" && c == '{' as u8 {
                  curly_brace_tag = true;
                  str::push_byte(parser.content, c);
              } else if curly_brace_tag && c == '}' as u8 {
                  curly_brace_tag = false;
                  str::push_byte(parser.content, c);
              } else if c == parser.ctag[0u] {
                  if str::byte_len(parser.ctag) > 1u {
                      parser.tag_position = 1u;
                      parser.state = CTAG;
                  } else {
                      add_tag(parser);
                      parser.state = TEXT;
                  }
              } else {
                  str::push_byte(parser.content, c);
              }
          }
          CTAG {
              if c == parser.ctag[parser.tag_position] {
                  if parser.tag_position == str::byte_len(parser.ctag) - 1u {
                      add_tag(parser);
                      parser.state = TEXT;
                  } else {
                      parser.state = TAG;
                      not_ctag(parser);
                      str::push_byte(parser.content, c);
                  }
              }
          }
      }
    };

    alt parser.state {
      TEXT { add_text(parser) }
      OTAG { not_otag(parser); add_text(parser) }
      TAG { fail "unclosed tag" }
      CTAG { not_ctag(parser); add_text(parser) }
    }

    // Check that we don't have any incomplete sections.
    vec::iter(copy parser.tokens) { |token|
        alt token {
          incomplete_section(name, _) {
              fail #fmt("Unclosed mustache section %s", name);
          }
          _ {}
        }
    };

    parser.tokens
}

fn add_text(parser: parser) {
    if parser.content != "" {
        vec::push(parser.tokens, text(parser.content));
        parser.content = "";
    }
}

fn add_tag(parser: parser) {
    let content = parser.content;
    let content_len = str::byte_len(content);

    alt content[0] as char {
      '!' {} // ignore comments
      '&' {
          let name = check_content(str::slice(content, 1u, content_len));
          vec::push(parser.tokens, utag(name)); }
      '{' {
        if str::ends_with(content, "}") {
            let name = check_content(str::slice(content, 1u, content_len - 1u));
            vec::push(parser.tokens, utag(name));
        } else {
            log(error, parser.content);
            log(error, content);
            fail "unbalanced \"{\" in tag";
        }
      }
      '#' {
          let name = check_content(str::slice(content, 1u, content_len));
          vec::push(parser.tokens, incomplete_section(name, false));
      }
      '^' {
          let name = check_content(str::slice(content, 1u, content_len));
          vec::push(parser.tokens, incomplete_section(name, true));
      }
      '/' {
          let name = check_content(str::slice(content, 1u, content_len));
          let children = [];

          while true {
              if vec::len(parser.tokens) == 0u {
                  fail "closing unopened section";
              }

              let last = vec::pop(parser.tokens);

              alt last {
                incomplete_section(section_name, inverted) {
                    if section_name == name {
                        vec::push(parser.tokens,
                            section({
                                name: name,
                                inverted: inverted,
                                children: @vec::reversed(children),
                            }));
                        break;
                    } else {
                        fail "Unclosed section";
                    }
                }
                _ { vec::push(children, last); }
              }
          }
      }
      '>' {
          let name = check_content(str::slice(content, 1u, content_len));
          vec::push(parser.tokens, partial(name));
      }
      '=' {
          if (content_len > 2u && str::ends_with(content, "=")) {
              let s = check_content(str::slice(content, 1u, content_len - 1u));
              let tags = str::splitn(s, ' ' as u8, 2u);

              if vec::len(tags) == 2u {
                  parser.otag = tags[0];
                  parser.ctag = tags[1];
              } else {
                  fail "invalid change delimiter tag content";
              }
          } else {
              fail "invalid change delimiter tag content";
          }
      }
      _ {
          vec::push(parser.tokens, etag(check_content(content)));
      }
    }

    parser.content = "";
}

fn not_otag(parser: parser) {
    let i = 0u;
    while i < parser.tag_position {
        str::push_byte(parser.content, parser.otag[i]);
        i += 1u;
    }
}

fn not_ctag(parser: parser) {
    let i = 0u;
    while i < parser.tag_position {
        str::push_byte(parser.content, parser.ctag[i]);
        i += 1u;
    }
}

fn check_content(content: str) -> str {
    let trimmed = str::trim(content);
    if str::byte_len(trimmed) == 0u {
        fail "empty tag";
    }
    trimmed
}

#[cfg(test)]
mod tests {
    #[test]
    fn test_compile_texts() {
        assert compile("hello world") == [text("hello world")];
        assert compile("hello {world") == [text("hello {world")];
        assert compile("hello world}") == [text("hello world}")];
        assert compile("hello world}}") == [text("hello world}}")];
    }

    #[test]
    fn test_compile_etags() {
        assert compile("{{ name }}") == [etag("name")];

        assert compile("before {{name}} after") == [
            text("before "),
            etag("name"),
            text(" after")
        ];

        assert compile("before {{name}}") == [
            text("before "),
            etag("name")
        ];

        assert compile("{{name}} after") == [
            etag("name"),
            text(" after")
        ];
    }

    #[test]
    fn test_compile_utags() {
        assert compile("{{{name}}}") == [utag("name")];

        assert compile("before {{{name}}} after") == [
            text("before "),
            utag("name"),
            text(" after")
        ];

        assert compile("before {{{name}}}") == [
            text("before "),
            utag("name")
        ];

        assert compile("{{{name}}} after") == [
            utag("name"),
            text(" after")
        ];
    }

    #[test]
    fn test_compile_sections() {
        assert compile("{{# name}}{{/name}}") == [
            section({name: "name", inverted: false, children: @[]})
        ];

        assert compile("before {{^name}}{{/name}} after") == [
            text("before "),
            section({name: "name", inverted: true, children: @[]}),
            text(" after")
        ];

        assert compile("before {{#name}}{{/name}}") == [
            text("before "),
            section({name: "name", inverted: false, children: @[]})
        ];

        assert compile("{{#name}}{{/name}} after") == [
            section({name: "name", inverted: false, children: @[]}),
            text(" after")
        ];

        assert compile("before {{#a}} 1 {{^b}} 2 {{/b}} {{/a}} after") == [
            text("before "),
            section({name: "a", inverted: false, children: @[
                text(" 1 "),
                section({name: "b", inverted: true, children: @[
                    text(" 2 ")
                ]}),
                text(" ")
            ]}),
            text(" after")
        ];
    }

    #[test]
    fn test_compile_partials() {
        assert compile("{{> name}}") == [partial("name")];

        assert compile("before {{>name}} after") == [
            text("before "),
            partial("name"),
            text(" after")
        ];

        assert compile("before {{> name}}") == [
            text("before "),
            partial("name")
        ];

        assert compile("{{>name}} after") == [
            partial("name"),
            text(" after")
        ];
    }

    #[test]
    fn test_compile_delimiters() {
        assert compile("before {{=<% %>=}}<%name%> after") == [
            text("before "),
            etag("name"),
            text(" after")
        ];
    }
}
<commit_msg>Partially implement rendering.<commit_after>use std;

import result::{ok, err};
import std::io;
import std::map::{map, new_str_hash};

export token;
export compile;

enum token {
    text(str),
    etag(str),
    utag(str),
    section({ name: str, inverted: bool, children: [token] }),
    incomplete_section(str, bool),
    partial(str),
}

enum data {
    str(str),
    bool(bool),
    vec([context]),
    fun(fn@(str) -> str),
}

type context = map<str, data>;

mod parser {
    enum state { TEXT, OTAG, TAG, CTAG }

    type t = {
        mutable state: state,
        mutable otag: str,
        mutable ctag: str,
        mutable content: str,
        mutable tag_position: uint,
        mutable tokens: [token],
    };
}

fn compile(src: str) -> [token] {
    let parser : parser::t = {
        mutable state: parser::TEXT,
        mutable otag: "{{",
        mutable ctag: "}}",
        mutable content: "",
        mutable tag_position: 0u,
        mutable tokens: [],
    };

    let curly_brace_tag = false;

    str::bytes_iter(src) { |c|
        alt parser.state {
          parser::TEXT {
            if c == parser.otag[0] {
                if str::len_bytes(parser.otag) > 1u {
                    parser.tag_position = 1u;
                    parser.state = parser::OTAG;
                } else {
                    add_text(parser);
                    parser.state = parser::TAG;
                }
            } else {
                unsafe { str::unsafe::push_byte(parser.content, c) };
            }
          }
          parser::OTAG {
            if c == parser.otag[parser.tag_position] {
                if parser.tag_position == str::len_bytes(parser.otag) - 1u {
                    add_text(parser);
                    curly_brace_tag = false;
                    parser.state = parser::TAG;
                } else {
                    parser.tag_position += 1u;
                }
            } else {
                // We don't have a tag, so add all the tag parts we've seen
                // so far to the string.
                parser.state = parser::TEXT;
                not_otag(parser);
                unsafe { str::unsafe::push_byte(parser.content, c) };
            }
          }
          parser::TAG {
              if parser.content == "" && c == '{' as u8 {
                  curly_brace_tag = true;
                  unsafe { str::unsafe::push_byte(parser.content, c) };
              } else if curly_brace_tag && c == '}' as u8 {
                  curly_brace_tag = false;
                  unsafe { str::unsafe::push_byte(parser.content, c) };
              } else if c == parser.ctag[0u] {
                  if str::len_bytes(parser.ctag) > 1u {
                      parser.tag_position = 1u;
                      parser.state = parser::CTAG;
                  } else {
                      add_tag(parser);
                      parser.state = parser::TEXT;
                  }
              } else {
                  unsafe { str::unsafe::push_byte(parser.content, c) };
              }
          }
          parser::CTAG {
              if c == parser.ctag[parser.tag_position] {
                  if parser.tag_position == str::len_bytes(parser.ctag) - 1u {
                      add_tag(parser);
                      parser.state = parser::TEXT;
                  } else {
                      parser.state = parser::TAG;
                      not_ctag(parser);
                      unsafe { str::unsafe::push_byte(parser.content, c) };
                  }
              }
          }
      }
    };

    alt parser.state {
      parser::TEXT { add_text(parser) }
      parser::OTAG { not_otag(parser); add_text(parser) }
      parser::TAG { fail "unclosed tag" }
      parser::CTAG { not_ctag(parser); add_text(parser) }
    }

    // Check that we don't have any incomplete sections.
    vec::iter(copy parser.tokens) { |token|
        alt token {
          incomplete_section(name, _) {
              fail #fmt("Unclosed mustache section %s", name);
          }
          _ {}
        }
    };

    parser.tokens
}

fn add_text(parser: parser::t) {
    if parser.content != "" {
        vec::push(parser.tokens, text(parser.content));
        parser.content = "";
    }
}

fn add_tag(parser: parser::t) {
    let content = parser.content;
    let content_len = str::len_bytes(content);

    alt content[0] as char {
      '!' {} // ignore comments
      '&' {
          let name = check_content(str::slice(content, 1u, content_len));
          vec::push(parser.tokens, utag(name)); }
      '{' {
        if str::ends_with(content, "}") {
            let name = check_content(str::slice(content, 1u, content_len - 1u));
            vec::push(parser.tokens, utag(name));
        } else {
            log(error, parser.content);
            log(error, content);
            fail "unbalanced \"{\" in tag";
        }
      }
      '#' {
          let name = check_content(str::slice(content, 1u, content_len));
          vec::push(parser.tokens, incomplete_section(name, false));
      }
      '^' {
          let name = check_content(str::slice(content, 1u, content_len));
          vec::push(parser.tokens, incomplete_section(name, true));
      }
      '/' {
          let name = check_content(str::slice(content, 1u, content_len));
          let children = [];

          while true {
              if vec::len(parser.tokens) == 0u {
                  fail "closing unopened section";
              }

              let last = vec::pop(parser.tokens);

              alt last {
                incomplete_section(section_name, inverted) {
                    if section_name == name {
                        vec::push(parser.tokens,
                            section({
                                name: name,
                                inverted: inverted,
                                children: vec::reversed(children),
                            }));
                        break;
                    } else {
                        fail "Unclosed section";
                    }
                }
                _ { vec::push(children, last); }
              }
          }
      }
      '>' {
          let name = check_content(str::slice(content, 1u, content_len));
          vec::push(parser.tokens, partial(name));
      }
      '=' {
          if (content_len > 2u && str::ends_with(content, "=")) {
              let s = check_content(str::slice(content, 1u, content_len - 1u));
              let tags = str::splitn_char(s, ' ', 2u);

              if vec::len(tags) == 2u {
                  parser.otag = tags[0];
                  parser.ctag = tags[1];
              } else {
                  fail "invalid change delimiter tag content";
              }
          } else {
              fail "invalid change delimiter tag content";
          }
      }
      _ {
          vec::push(parser.tokens, etag(check_content(content)));
      }
    }

    parser.content = "";
}

fn not_otag(parser: parser::t) {
    let i = 0u;
    while i < parser.tag_position {
        unsafe { str::unsafe::push_byte(parser.content, parser.otag[i]) };
        i += 1u;
    }
}

fn not_ctag(parser: parser::t) {
    let i = 0u;
    while i < parser.tag_position {
        unsafe { str::unsafe::push_byte(parser.content, parser.ctag[i]) };
        i += 1u;
    }
}

fn check_content(content: str) -> str {
    let trimmed = str::trim(content);
    if str::len_bytes(trimmed) == 0u {
        fail "empty tag";
    }
    trimmed
}

fn from_str(template: str, context: context) -> str {
    render(compile(template), context)
}

fn from_file(path: str, context: context) -> str {
    alt io::read_whole_file_str(path) {
      ok(template) { from_str(template, context) }
      err(e) { fail }
    }
}

fn render(tokens: [token], context: context) -> str {
    let output = vec::map(tokens) { |token|
        alt token {
          text(s) { s }
          etag(name) {
            alt context.find(name) {
              none { "" }
              some(tag) { render_etag(tag) }
            }
          }
          utag(name) {
            alt context.find(name) {
              none { "" }
              some(tag) { render_utag(tag) }
            }
          }
          section({name, inverted, children}) {
            alt context.find(name) {
              none {
                if inverted {
                    render(children, new_str_hash())
                } else { "" }
              }
              some(tag) {
                render_section(tag, inverted, children) }
            }
          }
          partial(name) { from_file(name + ".mustache", context) }
          _ { fail }
        }
    };

    str::concat(output)
}

fn render_etag(data: data) -> str {
    let escaped = "";
    str::chars_iter(render_utag(data)) { |c|
        alt c {
          '<' { escaped += "<" }
          '>' { escaped += ">" }
          '&' { escaped += "&" }
          '"' { escaped += """ }
          '\'' { escaped += "'" }
          _ { str::push_char(escaped, c); }
        }
    }
    escaped
}

fn render_utag(data: data) -> str {
    alt data {
      str(s) { s }
      _ { fail }
    }
}

fn render_section(data: data, inverted: bool, children: [token]) -> str {
    alt data {
      bool(b) {
        if inverted {
            render(children, new_str_hash())
        } else { "" }
      }
      vec(ctxs) {
        if inverted {
            if vec::is_empty(ctxs) {
                render(children, new_str_hash())
            } else { "" }
        } else {
            str::concat(vec::map(ctxs) { |ctx| render(children, ctx) })
        }
      }
      fun(f) { fail }
      _ { fail }
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn test_compile_texts() {
        assert compile("hello world") == [text("hello world")];
        assert compile("hello {world") == [text("hello {world")];
        assert compile("hello world}") == [text("hello world}")];
        assert compile("hello world}}") == [text("hello world}}")];
    }

    #[test]
    fn test_compile_etags() {
        assert compile("{{ name }}") == [etag("name")];

        assert compile("before {{name}} after") == [
            text("before "),
            etag("name"),
            text(" after")
        ];

        assert compile("before {{name}}") == [
            text("before "),
            etag("name")
        ];

        assert compile("{{name}} after") == [
            etag("name"),
            text(" after")
        ];
    }

    #[test]
    fn test_compile_utags() {
        assert compile("{{{name}}}") == [utag("name")];

        assert compile("before {{{name}}} after") == [
            text("before "),
            utag("name"),
            text(" after")
        ];

        assert compile("before {{{name}}}") == [
            text("before "),
            utag("name")
        ];

        assert compile("{{{name}}} after") == [
            utag("name"),
            text(" after")
        ];
    }

    #[test]
    fn test_compile_sections() {
        assert compile("{{# name}}{{/name}}") == [
            section({name: "name", inverted: false, children: []})
        ];

        assert compile("before {{^name}}{{/name}} after") == [
            text("before "),
            section({name: "name", inverted: true, children: []}),
            text(" after")
        ];

        assert compile("before {{#name}}{{/name}}") == [
            text("before "),
            section({name: "name", inverted: false, children: []})
        ];

        assert compile("{{#name}}{{/name}} after") == [
            section({name: "name", inverted: false, children: []}),
            text(" after")
        ];

        assert compile("before {{#a}} 1 {{^b}} 2 {{/b}} {{/a}} after") == [
            text("before "),
            section({name: "a", inverted: false, children: [
                text(" 1 "),
                section({name: "b", inverted: true, children: [
                    text(" 2 ")
                ]}),
                text(" ")
            ]}),
            text(" after")
        ];
    }

    #[test]
    fn test_compile_partials() {
        assert compile("{{> name}}") == [partial("name")];

        assert compile("before {{>name}} after") == [
            text("before "),
            partial("name"),
            text(" after")
        ];

        assert compile("before {{> name}}") == [
            text("before "),
            partial("name")
        ];

        assert compile("{{>name}} after") == [
            partial("name"),
            text(" after")
        ];
    }

    #[test]
    fn test_compile_delimiters() {
        assert compile("before {{=<% %>=}}<%name%> after") == [
            text("before "),
            etag("name"),
            text(" after")
        ];
    }

    #[test]
    fn test_render_texts() {
        let ctx = new_str_hash();
        ctx.insert("name", str("world"));

        assert render(compile("hello world"), ctx) == "hello world";
        assert render(compile("hello {world"), ctx) == "hello {world";
        assert render(compile("hello world}"), ctx) == "hello world}";
        assert render(compile("hello {world}"), ctx) == "hello {world}";
        assert render(compile("hello world}}"), ctx) == "hello world}}";
    }

    #[test]
    fn test_render_etags() {
        let ctx = new_str_hash();
        ctx.insert("name", str("world"));

        assert render(compile("hello {{name}}"), ctx) == "hello world";
    }

    #[test]
    fn test_render_utags() {
        let ctx = new_str_hash();
        ctx.insert("name", str("world"));

        assert render(compile("hello {{{name}}}"), ctx) == "hello world";
    }

    #[test]
    fn test_render_sections() {
        let ctx0 = new_str_hash();
        let template = compile("0{{#a}}1 {{n}} 3{{/a}}5");

        assert render(template, ctx0) == "05";

        ctx0.insert("a", vec([]));
        assert render(template, ctx0) == "05";

        let ctx1 = new_str_hash();
        ctx0.insert("a", vec([ctx1]));

        assert render(template, ctx0) == "01  35";

        ctx1.insert("n", str("a"));
        assert render(template, ctx0) == "01 a 35";
    }

    #[test]
    fn test_render_inverted_sections() {
        let template = "0{{^a}}1 3{{/a}}5";

        let ctx0 = new_str_hash();
        assert from_str(template, ctx0) == "01 35";

        ctx0.insert("a", vec([]));
        assert from_str(template, ctx0) == "01 35";

        let ctx1 = new_str_hash();
        ctx0.insert("a", vec([ctx1]));
        assert from_str(template, ctx0) == "05";

        ctx1.insert("n", str("a"));
        assert from_str(template, ctx0) == "05";
    }

    #[test]
    fn test_render_partial() {
        let path = "base.mustache";

        let ctx0 = new_str_hash();
        assert from_file(path, ctx0) == "<h2>Names</h2>\n\n";

        ctx0.insert("names", vec([]));
        assert from_file(path, ctx0) == "<h2>Names</h2>\n\n";

        let ctx1 = new_str_hash();
        ctx0.insert("names", vec([ctx1]));
        assert from_file(path, ctx0) ==
            "<h2>Names</h2>\n\n" +
            "  <strong></strong>\n\n\n";

        ctx1.insert("name", str("a"));
        assert from_file(path, ctx0) ==
            "<h2>Names</h2>\n\n" +
            "  <strong>a</strong>\n\n\n";

        let ctx2 = new_str_hash();
        ctx2.insert("name", str("<b>"));
        ctx0.insert("names", vec([ctx1, ctx2]));
        assert from_file(path, ctx0) ==
            "<h2>Names</h2>\n\n" +
            "  <strong>a</strong>\n\n\n" +
            "  <strong><b></strong>\n\n\n";
    }
}
<|endoftext|>