rust · no unsafe · crates.io
Detect what kind of project a directory is.
A pure-Rust crate that answers what a directory is, finds every project root under a tree, and resolves which project a file belongs to — a faithful 1:1 port of the Go projectdetect.
MIT licensed · no unsafe · CEL support is an opt-in feature
// what project type(s) is this directory? for m in projectdetect::detect(".") { println!("{} via {}", m.r#type, m.indicator); } // go via go.mod // docker-compose via docker-compose.yml
what you get
Filesystem-first project detection, in Rust.
Types match on indicators — an exact filename, a basename glob, a subdirectory marker, or an optional CEL expression over the directory's files and subdirs.
Detect & find
detect(dir) reports every type this directory matches — a directory can be several at once. Registry::find walks a tree and returns every project root under it.
Resolve a file's project
resolve_for_path(file) walks up to the nearest ancestor project root. The Resolver keeps an interior-mutable cache so repeated look-ups over a tree stay cheap.
28 built-in types
Go, Node, Rust, Python, Ruby, Maven/Gradle, .NET, Terraform, Compose — plus Swift, PHP, Scala, CMake, Zig and eight static-site generators. Each declares its build-artefact dirs for collect_build_excludes.
Custom types in YAML
Load extra types from YAML — has_file, has_glob, has_subdir_glob, or cel. Configs are auto-discovered across a user-wide layer and a per-project .file-search-on/.
CEL is opt-in
The crate has no CEL dependency by default. Enable the cel cargo feature for cel: indicators — otherwise the interpreter and its transitive deps stay out of your build.
Pure Rust, no unsafe
Idiomatic and safe: Indicator is an enum, timeouts are Option<Duration> with an Arc<AtomicBool> cancel flag, and the root .gitignore is respected. Builds on Rust 1.85+.
coverage
Built-in project types.
28 types out of the box — languages, build tools, and static-site generators — with the same indicator semantics as the Go library. Extend the set with your own via YAML.
- go
- node
- rust
- python
- ruby
- java-maven
- java-gradle
- dotnet
- terraform
- docker-compose
- swift
- php
- scala-sbt
- scala-mill
- cmake
- autotools
- r
- zig
- perl
- matlab
- hugo
- jekyll
- eleventy
- astro
- gatsby
- mkdocs
- docusaurus
- pelican
usage
Walk a tree, or resolve a single file.
Find every project root under a directory, or ask which project a given file belongs to.
// Every project root under a tree. use projectdetect::{Registry, FindOptions}; let reg = Registry::with_builtins(); let result = reg.find("/path/to/code", &FindOptions::default())?; for p in result.projects { println!("{}: {:?}", p.path.display(), p.types); }
// Which project does this file belong to? let reg = Registry::with_builtins(); if let Some((root, types)) = reg.resolve_for_path("src/main.rs") { println!("part of {:?}", types); }
# Custom types in YAML. project_types: - name: my-stack indicators: - has_file: "my.config" - has_glob: "*.mytool" - cel: '"services" in subdirs'
// Load them into a registry. let mut reg = Registry::with_builtins(); let n = reg.load_from_file("types.yaml")?; // …or auto-discover user + per-project configs reg.load_discovered()?;
install
Add it to your crate.
One dependency-light crate. CEL support is a cargo feature you enable only if you need it.
cargo add projectdetect
cargo add projectdetect --features cel
https://docs.rs/projectdetect
Builds on Rust 1.85+ (the cel feature needs 1.86+). Published on crates.io.