Prerequisites
Before you begin development, ensure you have the required tools installed:
Rust 1.80+ The minimum supported Rust version. Check with rustc --version. curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Git Version control for cloning the repository and managing contributions.
Cargo Rust’s build system and package manager (included with Rust installation).
Terminal A modern terminal emulator with Unicode and 256-color support for testing the TUI.
Getting Started
Clone the repository
git clone https://github.com/trepidity/loom-ldapbrowser.git
cd loom-ldapbrowser
Build the workspace
This compiles all three crates:
loom-ldapbrowser (binary)
loom-core (library)
loom-tui (library)
Run the application
cargo run --bin loom-ldapbrowser
Use offline mode for testing without an LDAP server (see Offline Mode ).
Run tests
This executes unit tests across all crates.
Development Workflow
Building
Check all crates
Build debug (faster)
Build release (optimized)
Build specific crate
Use cargo check during development for fast syntax checking without generating binaries.
Testing
Run all tests
Test specific crate
Test specific module
Show test output
Run Clippy (linter)
Format code
Check formatting
cargo clippy --workspace -- -D warnings
Always run cargo fmt --all before committing! This is mentioned in CLAUDE.md and ensures consistent code style.
Workspace Structure
The project is organized as a Cargo workspace. See Architecture Overview for detailed crate responsibilities.
loom-ldapbrowser/
├── Cargo.toml # Workspace configuration
├── crates/
│ ├── loom-ldapbrowser/ # Binary crate
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── main.rs # Entry point
│ ├── loom-core/ # Core LDAP library
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── lib.rs
│ │ ├── connection.rs
│ │ ├── schema.rs
│ │ ├── offline.rs
│ │ └── ...
│ └── loom-tui/ # TUI library
│ ├── Cargo.toml
│ └── src/
│ ├── lib.rs
│ ├── app.rs # Main state machine
│ ├── action.rs # Action dispatch
│ └── components/ # UI components
├── README.md
├── USER_MANUAL.md
├── CLAUDE.md # Development workflow notes
└── LICENSE.txt
Common Development Tasks
Adding a New Action
Actions are the central message type for state changes. See Architecture: Action Dispatch .
Add action variant
Edit crates/loom-tui/src/action.rs: pub enum Action {
// ... existing variants
MyNewAction ( String ), // Add your action
}
Handle action in App
Edit crates/loom-tui/src/app.rs in the App::update() method: match action {
// ... existing handlers
Action :: MyNewAction ( data ) => {
// Update state or spawn async task
self . my_state = data ;
}
}
Dispatch action from component
In your component’s event handler: impl MyComponent {
fn handle_key ( & mut self , key : KeyEvent ) -> Vec < Action > {
match key . code {
KeyCode :: Char ( 'a' ) => vec! [ Action :: MyNewAction ( "hello" . to_string ())],
_ => vec! [],
}
}
}
Adding a New UI Component
Create component file
touch crates/loom-tui/src/components/my_component.rs
Implement Component trait
use ratatui :: prelude ::* ;
use crate :: action :: Action ;
use crate :: component :: Component ;
pub struct MyComponent {
// Component state
}
impl Component for MyComponent {
fn handle_key_event ( & mut self , key : KeyEvent ) -> Vec < Action > {
// Handle keyboard input
vec! []
}
fn render ( & mut self , f : & mut Frame , area : Rect ) {
// Render the component
}
}
Register in mod.rs
Edit crates/loom-tui/src/components/mod.rs: pub mod my_component ;
pub use my_component :: MyComponent ;
Add to App state
Edit crates/loom-tui/src/app.rs: pub struct App {
// ... existing fields
pub my_component : MyComponent ,
}
Adding a New LDAP Operation
Add method to loom-core
Edit crates/loom-core/src/connection.rs: impl LdapConnection {
pub async fn my_operation ( & self , param : & str ) -> Result < String > {
// Use self.ldap to perform LDAP operations
let result = self . ldap . simple_search ( ... ) . await ? ;
Ok ( result )
}
}
Create action for the operation
Add to action.rs and handle in app.rs as shown above.
Spawn async task from UI
In your component or app update handler: let conn = self . current_connection () . clone ();
let tx = self . action_tx . clone ();
tokio :: spawn ( async move {
let result = conn . my_operation ( "param" ) . await ? ;
tx . send ( Action :: MyOperationComplete ( result )) . await ? ;
});
Testing
Unit Tests
Add tests in the same file as your implementation:
#[cfg(test)]
mod tests {
use super ::* ;
#[test]
fn test_my_function () {
let result = my_function ( "input" );
assert_eq! ( result , "expected" );
}
#[tokio :: test]
async fn test_async_function () {
let result = async_function () . await . unwrap ();
assert! ( result . len () > 0 );
}
}
Integration Tests
For testing with a real LDAP server, use the offline mode in tests:
use loom_core :: offline :: OfflineConnection ;
#[tokio :: test]
async fn test_search_operation () {
let conn = OfflineConnection :: new ( "dc=example,dc=org" . to_string ());
let results = conn . search ( "dc=example,dc=org" , "(objectClass=*)" ) . await . unwrap ();
assert! ( results . len () > 0 );
}
Manual Testing
For TUI components, manual testing in a terminal is essential:
Test with offline mode
Test with custom config
Test specific connection
cargo run -- --config /dev/null
# Then press F2 and create an offline connection
Debugging
Logging
Loom LDAP Browser uses tracing for structured logging. Enable debug logs:
Log levels:
RUST_LOG=error - Errors only
RUST_LOG=warn - Warnings and errors
RUST_LOG=info - Info, warnings, errors (default)
RUST_LOG=debug - All debug messages
RUST_LOG=trace - Very verbose, includes LDAP protocol details
Log Panel
While running the application, press F7 to toggle the log panel and see real-time logs.
Debugging Actions
Add logging in the action handler to trace state changes:
match action {
Action :: MyAction ( data ) => {
tracing :: debug! ( "MyAction received: {}" , data );
// ... handle action
}
}
Contributing
Commit Guidelines
Follow conventional commit format as specified in CLAUDE.md:
type(scope): subject
Body (optional)
Types:
feat - New feature
fix - Bug fix
docs - Documentation changes
style - Code style changes (formatting, etc.)
refactor - Code refactoring
test - Adding or updating tests
chore - Maintenance tasks
Examples:
git commit -m "feat(search): add fuzzy search in DN picker"
git commit -m "fix(tls): handle certificate verification errors"
git commit -m "docs(readme): update installation instructions"
Pre-commit Checklist
Format code
This is mandatory per CLAUDE.md. Commits without formatting will be rejected.
Run linter
cargo clippy --workspace -- -D warnings
Fix all warnings before committing.
Commit changes
git add .
git commit -m "feat(component): add my feature"
Pull Request Guidelines
Fork the repository on GitHub
Create a feature branch from main:
git checkout -b feat/my-feature
Make your changes following the guidelines above
Push to your fork :
git push origin feat/my-feature
Open a Pull Request with:
Clear description of changes
Reference to any related issues
Screenshots for UI changes
Test results
Project Dependencies
Key external dependencies used across the workspace:
Core Dependencies
Crate Purpose Documentation tokioAsync runtime docs.rs ldap3LDAP protocol docs.rs ratatuiTUI framework ratatui.rs crosstermTerminal backend docs.rs serdeSerialization serde.rs
UI Components
Crate Purpose tui-tree-widgetTree view component tui-textareaMulti-line text input tui-loggerLog panel integration nucleoFuzzy search
Data Handling
Crate Purpose tomlConfig file parsing csvCSV export/import rust_xlsxwriterXLSX export calamineXLSX import keyringOS keychain access
Building Documentation
The project includes comprehensive documentation:
User Manual : USER_MANUAL.md - Complete user guide
README : README.md - Quick start and overview
API Docs : Generate with cargo doc
# Generate and open API documentation
cargo doc --workspace --open --no-deps
This builds rustdoc documentation for all crates and opens it in your browser.
Release Process
For maintainers only. Release process may vary based on project governance.
Update version
Edit Cargo.toml in the workspace root: [ workspace . package ]
version = "0.2.0"
Update CHANGELOG
Document changes since the last release.
Create release commit
cargo fmt --all
git commit -am "chore: release v0.2.0"
git tag v0.2.0
Build release binaries
cargo build --workspace --release
Binaries are in target/release/.
Push tag
git push origin main --tags
This typically triggers CI/CD to build and publish binaries.
Getting Help
If you encounter issues during development:
Check the User Manual : USER_MANUAL.md has detailed feature documentation
Review CLAUDE.md : Contains development workflow notes
Browse existing code : The codebase is well-structured and documented
Open an issue : GitHub Issues
Read the source : Code comments and module docs explain implementation details
The loom-core and loom-tui crates are libraries that can be explored independently. Use cargo doc to generate detailed API documentation.