docs(rust-style): order declarations by dependency

This commit is contained in:
2026-09-19 17:31:50 -04:00
parent 87dc93513b
commit 6102b1b8c8
3 changed files with 109 additions and 71 deletions

View File

@@ -0,0 +1,38 @@
# Declaration rules
## Order declarations by dependency
### Rule
Order Rust declarations in depth-first dependency order.
Place each function, struct, and enum before private declarations that support it.
For structs and enums, order supporting declarations by field or variant order.
For functions, order helper declarations by first use in the function body.
Apply the same rule recursively to each supporting declaration.
### Rationale
Dependency order lets readers start at the public operation or type.
Readers can then continue downward into the declarations that explain each part.
### Examples
```rust
pub struct Report {
files: FileStore,
diagnostics: Vec<Diagnostic>,
}
struct FileStore {
files: Vec<SourceFile>,
}
struct SourceFile {
path: PathBuf,
source: String,
}
struct Diagnostic {
message: String,
}
```

View File

@@ -38,20 +38,6 @@ pub trait NodeRule {
fn check(node: &Self::Node) -> Option<&'static str>;
}
fn run_node_rule<R>(node: &ast::Node) -> Option<Finding>
where
R: NodeRule,
{
let typed_node = R::select(&node.data)?;
let message = R::check(typed_node)?;
Some(Finding {
rule_id: R::ID,
range: node.range,
message,
})
}
pub fn run_rules(file: &ast::ParsedFile) -> Vec<Diagnostic> {
file.nodes
.iter()
@@ -69,3 +55,17 @@ pub fn run_rules(file: &ast::ParsedFile) -> Vec<Diagnostic> {
})
.collect()
}
fn run_node_rule<R>(node: &ast::Node) -> Option<Finding>
where
R: NodeRule,
{
let typed_node = R::select(&node.data)?;
let message = R::check(typed_node)?;
Some(Finding {
rule_id: R::ID,
range: node.range,
message,
})
}

View File

@@ -4,28 +4,45 @@ use std::path::{Path, PathBuf};
use std::process::ExitCode;
use xatu::{Report, SourceFile};
fn is_nix_file(path: &Path) -> bool {
path.extension().is_some_and(|extension| extension == "nix")
fn main() -> ExitCode {
match analyze(Args::parse()) {
Ok(report) if report.is_clean() => {
println!("No findings.");
ExitCode::SUCCESS
}
Ok(report) => {
print!("{report}");
ExitCode::from(1)
}
Err(error) => {
eprintln!("{error}");
ExitCode::from(2)
}
}
}
// TODO: Add configurable exclusions.
fn is_excluded_dir(path: &Path) -> bool {
path.file_name()
.and_then(|name| name.to_str())
.is_some_and(|name| {
matches!(
name,
".git"
| "bazel-bin"
| "bazel-dev"
| "bazel-out"
| "bazel-testlogs"
| "target"
| "testdata"
| "third_party"
) || name == "result"
|| name.starts_with("result-")
})
fn analyze(args: Args) -> status::Result<Report> {
let paths: Vec<PathBuf> = args
.paths
.into_iter()
.map(monorepo::workspace::resolve_workspace_path)
.collect();
let files: Vec<SourceFile> = paths
.iter()
.map(|path| collect_source_files(path))
.collect::<status::Result<Vec<_>>>()?
.into_iter()
.flatten()
.collect();
xatu::future_sight(files)
}
#[derive(Debug, Parser)]
struct Args {
#[arg(value_name = "PATH", default_value = ".")]
paths: Vec<PathBuf>,
}
fn collect_source_files(path: &Path) -> status::Result<Vec<SourceFile>> {
@@ -61,43 +78,26 @@ fn collect_source_files(path: &Path) -> status::Result<Vec<SourceFile>> {
Ok(Vec::new())
}
#[derive(Debug, Parser)]
struct Args {
#[arg(value_name = "PATH", default_value = ".")]
paths: Vec<PathBuf>,
// TODO: Add configurable exclusions.
fn is_excluded_dir(path: &Path) -> bool {
path.file_name()
.and_then(|name| name.to_str())
.is_some_and(|name| {
matches!(
name,
".git"
| "bazel-bin"
| "bazel-dev"
| "bazel-out"
| "bazel-testlogs"
| "target"
| "testdata"
| "third_party"
) || name == "result"
|| name.starts_with("result-")
})
}
fn analyze(args: Args) -> status::Result<Report> {
let paths: Vec<PathBuf> = args
.paths
.into_iter()
.map(monorepo::workspace::resolve_workspace_path)
.collect();
let files: Vec<SourceFile> = paths
.iter()
.map(|path| collect_source_files(path))
.collect::<status::Result<Vec<_>>>()?
.into_iter()
.flatten()
.collect();
xatu::future_sight(files)
}
fn main() -> ExitCode {
match analyze(Args::parse()) {
Ok(report) if report.is_clean() => {
println!("No findings.");
ExitCode::SUCCESS
}
Ok(report) => {
print!("{report}");
ExitCode::from(1)
}
Err(error) => {
eprintln!("{error}");
ExitCode::from(2)
}
}
fn is_nix_file(path: &Path) -> bool {
path.extension().is_some_and(|extension| extension == "nix")
}