# File Structure Validator (fsvalidator)
A Rust library for validating real filesystem directories against a declarative, strongly-typed schema. Useful for enforcing project structure, data ingestion layout, or configuration rules.
[](https://crates.io/crates/fsvalidator)
[](https://docs.rs/fsvalidator)
## Features
- **Flexible Matching**: Validate files and directories using both literal names and regex patterns
- **Nested Hierarchies**: Support for complex directory trees with arbitrary depth
- **Template System**: Reuse common structures via a template reference system
- **Strictness Controls**: Fine-grained control over validation strictness (required files, restricted directories)
- **Format Options**: Define validation rules in TOML or JSON formats
- **Descriptive Errors**: Clear, informative error messages for validation failures
## Installation
Add to your `Cargo.toml`:
```toml
[dependencies]
fsvalidator = { version = "0.1.3", features = ["toml"] } # or "json" feature
```
## Quick Start
```rust
use anyhow::Result;
use fsvalidator::from_toml;
fn main() -> Result<()> {
// Load structure definition from TOML
let root = from_toml("path/to/fsvalidator.toml")?;
// Display the parsed structure (optional)
println!("{root}");
// Validate a directory against the defined structure
root.validate("./path/to/validate")?;
Ok(())
}
```
## Definition Format
### TOML Example
```toml
# Root directory definition
[root]
type = "dir"
name = "project"
required = true
allow_defined_only = true
# Define children of the root directory
[[root.children]]
type = "file"
name = "README.md"
required = true
[[root.children]]
type = "dir"
name = "src"
required = true
# Define children of the src directory
[[root.children.children]]
type = "file"
pattern = ".*\.rs"
required = true
# Reference to a template
[[root.children]]
type = "ref"
name = "tests"
ref = "test_directory"
# Template definition
[template.test_directory]
type = "dir"
name = "tests"
required = false
[[template.test_directory.children]]
type = "file"
pattern = "test_.*\.rs"
required = false
# Global settings (applied to all nodes unless overridden)
[global]
required = false
allow_defined_only = false
```
### JSON Example
```json
{
"root": {
"type": "dir",
"name": "project",
"required": true,
"allow_defined_only": true,
"children": [
{
"type": "file",
"name": "README.md",
"required": true
},
{
"type": "dir",
"name": "src",
"required": true,
"children": [
{
"type": "file",
"pattern": ".*\.rs",
"required": true
}
]
},
{
"type": "ref",
"name": "tests",
"ref": "test_directory"
}
]
},
"template": {
"test_directory": {
"type": "dir",
"name": "tests",
"required": false,
"children": [
{
"type": "file",
"pattern": "test_.*\.rs",
"required": false
}
]
}
},
"global": {
"required": false,
"allow_defined_only": false
}
}
```
## Definition Structure
### Node Types
- `dir`: A directory node
- `file`: A file node
- `ref`: A reference to a template
### Node Properties
- `name`: Literal name of the file or directory
- `pattern`: Regex pattern to match against file or directory name (use either `name` or `pattern`, not both)
- `required`: Whether the node must exist (default: false)
- `allow_defined_only`: For directories, whether only defined children are allowed (default: false)
- `children`: List of child nodes (only valid for `dir` type)
- `ref`: Template reference name (only valid for `ref` type)
### Special Sections
- `root`: The root node of the validation tree
- `template`: Dictionary of reusable node templates
- `global`: Global settings applied to all nodes unless overridden
- `config`: Configuration options for the validator
## Model Structure
```rust
// Main node types
enum Node {
Dir(Rc<RefCell<DirNode>>),
File(Rc<RefCell<FileNode>>),
}
// Directory node with children
struct DirNode {
name: NodeName,
children: Vec<Node>,
required: bool,
allow_defined_only: bool,
}
// File node
struct FileNode {
name: NodeName,
required: bool,
}
// Node name (literal or pattern)
enum NodeName {
Literal(String),
Pattern(String), // Regex pattern
}
```
## Use Cases
- Enforcing consistent project layouts
- Validating data pipeline inputs/outputs
- Ensuring configuration directories are correctly structured
- Verifying deployment artifacts
- Testing file-based APIs
## Advanced Usage
### Programmatic Structure Creation
You can create validation structures programmatically:
```rust
use fsvalidator::model::{DirNode, FileNode, Node, NodeName};
// Create a file node
let readme = FileNode::new(NodeName::Literal("README.md".to_string()), true);
// Create a directory with pattern-matched files
let src_files = FileNode::new(NodeName::Pattern(".*\.rs".to_string()), true);
let src_dir = DirNode::new(
NodeName::Literal("src".to_string()),
vec![src_files],
true,
false
);
// Create the project root
let project = DirNode::new(
NodeName::Literal("project".to_string()),
vec![readme, src_dir],
true,
true
);
// Validate a directory
project.validate("path/to/project").expect("Validation failed");
```
## License
MIT OR Apache-2.0