Skip to main content

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.
git --version

Cargo

Rust’s build system and package manager (included with Rust installation).
cargo --version

Terminal

A modern terminal emulator with Unicode and 256-color support for testing the TUI.

Getting Started

1

Clone the repository

git clone https://github.com/trepidity/loom-ldapbrowser.git
cd loom-ldapbrowser
2

Build the workspace

cargo build --workspace
This compiles all three crates:
  • loom-ldapbrowser (binary)
  • loom-core (library)
  • loom-tui (library)
3

Run the application

cargo run --bin loom-ldapbrowser
Use offline mode for testing without an LDAP server (see Offline Mode).
4

Run tests

cargo test --workspace
This executes unit tests across all crates.

Development Workflow

Building

cargo check --workspace
Use cargo check during development for fast syntax checking without generating binaries.

Testing

cargo test --workspace

Linting and 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.
1

Add action variant

Edit crates/loom-tui/src/action.rs:
pub enum Action {
    // ... existing variants
    MyNewAction(String),  // Add your action
}
2

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;
    }
}
3

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

1

Create component file

touch crates/loom-tui/src/components/my_component.rs
2

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
    }
}
3

Register in mod.rs

Edit crates/loom-tui/src/components/mod.rs:
pub mod my_component;
pub use my_component::MyComponent;
4

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

1

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)
    }
}
2

Create action for the operation

Add to action.rs and handle in app.rs as shown above.
3

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:
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:
RUST_LOG=debug cargo run
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

1

Format code

cargo fmt --all
This is mandatory per CLAUDE.md. Commits without formatting will be rejected.
2

Run linter

cargo clippy --workspace -- -D warnings
Fix all warnings before committing.
3

Run tests

cargo test --workspace
Ensure all tests pass.
4

Commit changes

git add .
git commit -m "feat(component): add my feature"

Pull Request Guidelines

  1. Fork the repository on GitHub
  2. Create a feature branch from main:
    git checkout -b feat/my-feature
    
  3. Make your changes following the guidelines above
  4. Push to your fork:
    git push origin feat/my-feature
    
  5. 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

CratePurposeDocumentation
tokioAsync runtimedocs.rs
ldap3LDAP protocoldocs.rs
ratatuiTUI frameworkratatui.rs
crosstermTerminal backenddocs.rs
serdeSerializationserde.rs

UI Components

CratePurpose
tui-tree-widgetTree view component
tui-textareaMulti-line text input
tui-loggerLog panel integration
nucleoFuzzy search

Data Handling

CratePurpose
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.
1

Update version

Edit Cargo.toml in the workspace root:
[workspace.package]
version = "0.2.0"
2

Update CHANGELOG

Document changes since the last release.
3

Create release commit

cargo fmt --all
git commit -am "chore: release v0.2.0"
git tag v0.2.0
4

Build release binaries

cargo build --workspace --release
Binaries are in target/release/.
5

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:
  1. Check the User Manual: USER_MANUAL.md has detailed feature documentation
  2. Review CLAUDE.md: Contains development workflow notes
  3. Browse existing code: The codebase is well-structured and documented
  4. Open an issue: GitHub Issues
  5. 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.

Build docs developers (and LLMs) love