Skip to main content
This guide walks you through adding a new validator type to envcheck. We’ll use real examples from the codebase to illustrate the process.

Overview

Validators in envcheck are modular components that check specific aspects of your development environment. Each validator implements the Validator trait and returns validation results.

Understanding the Validator Trait

All validators implement this trait (defined in src/validators/mod.rs:52):

ValidationResult Structure

Validators return ValidationResult objects (defined in src/validators/mod.rs:20):
With three status types:
  • ValidationStatus::Success - Check passed
  • ValidationStatus::Warning - Optional check failed
  • ValidationStatus::Error - Required check failed

Helper Methods

Use these helper methods to create results:

Step-by-Step Guide

Step 1: Create the Validator File

Create a new file in src/validators/ for your validator. For example, to add a database validator:

Step 2: Define the Configuration Structure

First, add a configuration struct in src/config.rs. This defines what users can configure:
Then add it to the main Config struct:

Step 3: Implement the Validator

Here’s a complete example based on the EnvValidator pattern (src/validators/env.rs:1):

Step 4: Register the Validator Module

Add your validator to src/validators/mod.rs:5:

Step 5: Integrate Into Validation Runner

Add your validator to the run_all_validations function in src/validators/mod.rs:56:

Real-World Example: EnvValidator

Let’s examine the EnvValidator (src/validators/env.rs:1) as a complete example:
Key patterns to note:
  1. Required vs Optional: Check self.check.required to return errors vs warnings
  2. Helpful suggestions: Always provide actionable suggestions in error/warning messages
  3. Multiple results: You can push multiple ValidationResult objects for complex checks

Real-World Example: FileValidator

The FileValidator (src/validators/file.rs:1) shows more complex validation:
This example demonstrates:
  • Multiple validation steps: First check existence, then type, then permissions
  • Conditional checks: Only check permissions if the file exists and is the right type
  • Platform-specific code: Using #[cfg(unix)] for Unix-only features
  • Specific error messages: Different messages for different failure modes

Adding Tests

Always add tests for your validator. Here’s an example pattern:

Best Practices

1. Clear Error Messages

Always provide clear, actionable error messages:

2. Respect the Required Flag

Always check the required field and return appropriate status:

3. Return Multiple Results When Appropriate

Don’t hesitate to return multiple validation results for complex checks:

4. Use Rust Idioms

  • Use pattern matching instead of if-else chains
  • Leverage the ? operator for error propagation
  • Use descriptive variable names
  • Keep functions focused and small

5. Handle Edge Cases

Consider edge cases in your validation:
  • Missing dependencies
  • Permission issues
  • Platform differences (Windows vs Unix)
  • Network timeouts
  • Malformed input

Documentation

After implementing your validator, update the documentation:
  1. Add examples to README.md showing how to use your validator
  2. Document the configuration options
  3. Provide real-world use cases

Checklist

Before submitting your validator:
  • Created validator file in src/validators/
  • Added configuration struct to src/config.rs
  • Implemented the Validator trait
  • Registered module in src/validators/mod.rs
  • Integrated into run_all_validations
  • Added unit tests
  • Tested with real configuration
  • Ran cargo fmt and cargo clippy
  • Updated documentation
  • All tests pass (cargo test)

Getting Help

If you’re stuck:
  • Look at existing validators for patterns
  • Open a draft PR and ask for feedback
  • Create an issue with your questions
  • Check for good first issue labels for simpler starting points