Skip to content

Latest commit

 

History

History
394 lines (298 loc) · 10.8 KB

File metadata and controls

394 lines (298 loc) · 10.8 KB

Contributing to TP-Lib

Thank you for your interest in contributing to TP-Lib! This document provides guidelines and instructions for contributing.

Code of Conduct

Be respectful and professional in all interactions. We're all here to build better software together.

Development Setup

Prerequisites

  • Rust: 1.91.1 or later (Install rustup)
  • Python: 3.12 or later (for Python bindings)
  • Git: For version control
  • Docker: Optional, for containerized testing

Local Setup

# Clone the repository
git clone https://github.com/matdata-eu/tp-lib.git
cd tp-lib

# Build all workspace crates
cargo build --workspace

# Run tests
cargo test --workspace --all-features

# Build Python bindings
cd tp-py
pip install maturin pytest
maturin develop
pytest python/tests/

Development Workflow

TP-Lib follows Test-Driven Development (TDD):

  1. RED: Write a failing test first
  2. GREEN: Write minimum code to make it pass
  3. REFACTOR: Improve code while keeping tests green

Before Committing

Run these checks locally:

# Format code
cargo fmt --all

# Check formatting
cargo fmt --all --check

# Run linter
cargo clippy --workspace --all-targets --all-features -- -D warnings

# Run security/license check
cargo deny check

# Run all tests
cargo test --workspace --all-features

# Test Python bindings
cd tp-py
maturin develop
pytest

Commit Messages

Use Conventional Commits:

feat: add new CRS transformation
fix: correct haversine distance calculation
docs: update API documentation
test: add integration tests for projection
chore: update dependencies
refactor: simplify spatial indexing

Pull Request Process

  1. Fork the repository
  2. Create a branch from main:
    git checkout -b feature/your-feature-name
  3. Make changes following TDD workflow
  4. Run all checks (see "Before Committing")
  5. Push to your fork
  6. Open a Pull Request against main

PR Requirements

Your PR must:

  • ✅ Pass all CI checks (tests, linting, security)
  • ✅ Include tests for new functionality
  • ✅ Update documentation if needed
  • ✅ Follow code style (enforced by rustfmt)
  • ✅ Have clear commit messages
  • ✅ Not introduce security vulnerabilities

CI Checks:

  • Test Suite (Rust tests)
  • Python Tests
  • Linting (rustfmt + clippy)
  • License & Security Check (cargo-deny)

See docs/WORKFLOWS.md for details on CI/CD automation.

Testing Guidelines

Test Types

  1. Unit Tests: Test individual functions/methods

    #[cfg(test)]
    mod tests {
        use super::*;
        
        #[test]
        fn test_haversine_distance() {
            // Arrange
            let pos1 = Point::new(50.0, 4.0);
            let pos2 = Point::new(50.1, 4.1);
            
            // Act
            let distance = haversine_distance(&pos1, &pos2);
            
            // Assert
            assert!((distance - 13545.0).abs() < 1.0);
        }
    }
  2. Integration Tests: Test component interactions

    • Located in tests/ directory
    • Test complete workflows
  3. Contract Tests: Verify CLI behavior

    • Located in tests/contract/
    • Test exit codes, output formats
  4. Doc Tests: Examples in documentation

    /// Calculate distance between two points
    /// 
    /// ```
    /// use tp_lib_core::haversine_distance;
    /// let dist = haversine_distance(50.0, 4.0, 50.1, 4.1);
    /// assert!(dist > 0.0);
    /// ```
    pub fn haversine_distance(lat1: f64, lon1: f64, lat2: f64, lon2: f64) -> f64 {
        // implementation
    }

Running Tests

# All tests
cargo test --workspace --all-features

# Specific test
cargo test test_haversine_distance

# With output
cargo test -- --nocapture

# Doc tests only
cargo test --doc

# Integration tests only
cargo test --test integration

# Benchmarks
cargo bench --workspace

Documentation

Code Documentation

  • Add rustdoc comments to all public APIs
  • Include examples in doc comments
  • Explain complex algorithms
  • Document panics, errors, and edge cases

Example:

/// Projects a GNSS position onto the nearest railway netelement.
///
/// # Arguments
///
/// * `position` - The GNSS position to project
/// * `network` - The railway network with spatial index
///
/// # Returns
///
/// Returns `Ok(ProjectedPosition)` on success, or `Err(ProjectionError)`
/// if projection fails.
///
/// # Example
///
/// ```
/// use tp_lib_core::{GnssPosition, RailwayNetwork, project_position};
///
/// let position = GnssPosition::new(50.8503, 4.3517, "2024-01-01T12:00:00+00:00");
/// let network = RailwayNetwork::from_geojson("network.geojson")?;
/// let projected = project_position(&position, &network)?;
/// ```
pub fn project_position(
    position: &GnssPosition,
    network: &RailwayNetwork,
) -> Result<ProjectedPosition, ProjectionError> {
    // implementation
}

Building Documentation

# Generate docs
cargo doc --workspace --no-deps

# Open in browser
cargo doc --workspace --no-deps --open

Documentation is automatically deployed to GitHub Pages on every push to main.

Release Process

Releases are automated via GitHub Actions. See docs/WORKFLOWS.md for details.

Creating a Release

For Maintainers Only:

  1. Update versions in all Cargo.toml and pyproject.toml files
  2. Update CHANGELOG.md with release notes
  3. Commit and tag:
    git commit -am "chore: release v1.0.0"
    git tag v1.0.0
    git push origin main
    git push origin v1.0.0
  4. Create GitHub Release at: https://github.com/matdata-eu/tp-lib/releases/new
    • Tag: v1.0.0
    • Title: Release 1.0.0
    • Description: Copy from CHANGELOG.md
    • Click "Publish release"

Automated Actions:

  • ✅ Publishes tp-core, tp-cli, tp-py to crates.io
  • ✅ Publishes tp-lib Python package to PyPI
  • ✅ Builds wheels for Linux, Windows, macOS
  • ✅ Updates documentation on GitHub Pages

Project Structure

tp-lib/
├── .github/workflows/     # CI/CD automation
├── tp-core/               # Core library
│   ├── src/
│   │   ├── models/        # Data models
│   │   ├── projection/    # Projection algorithms
│   │   ├── path/          # Train path calculation
│   │   ├── io/            # Input/output parsers
│   │   ├── crs/           # Coordinate transformations
│   │   └── temporal/      # Timezone utilities
│   ├── tests/             # Integration tests
│   └── benches/           # Performance benchmarks
├── tp-cli/                # Command-line interface
└── tp-py/                 # Python bindings
    └── python/tests/      # Python tests

Train Path Calculation Development

When working on path calculation features (tp-core/src/path/):

Architecture Overview

The path calculation module consists of several submodules:

  • candidate.rs: Find candidate netelements for each GNSS position
  • probability.rs: Calculate HMM-related probabilities (e.g., emission/transition) using distance, heading, and network context
  • viterbi.rs: Run the HMM/Viterbi algorithm to compute the most likely train path from the candidate sequences
  • graph.rs: Network topology graph operations
  • spacing.rs: GNSS resampling for consistent spacing

Key Functions

// Main entry point
calculate_train_path(gnss_positions, netelements, netrelations, config) -> PathResult

// Project onto pre-calculated path
project_onto_path(gnss_positions, path, netelements, config) -> Vec<ProjectedPosition>

Algorithm Flow

  1. Candidate Selection (find_candidate_netelements)

    • Uses R-tree spatial index for O(log n) lookup
    • Filters by cutoff_distance and max_candidates
  2. Probability Calculation (calculate_combined_probability)

    • Distance probability: exp(-distance / distance_scale)
    • Heading probability: exp(-heading_diff / heading_scale)
    • Combined: distance_prob * heading_prob
  3. Path Construction (construct_forward_path, construct_backward_path)

    • Traverses network using netrelations (topology)
    • Builds path in both directions for validation
  4. Path Selection (select_best_path)

    • Validates bidirectional agreement
    • Returns highest probability path

Testing Path Calculation

# Run path calculation tests
cargo test --workspace path

# Run integration tests
cargo test --test tests path_calculation

# Run benchmarks
cargo bench --bench projection_bench
cargo bench --bench naive_baseline_bench

Creating coverage report

cargo llvm-cov --lib -p tp-lib-core --html --output-dir target/coverage

Performance Targets

  • SC-001: 1000 positions × 50 netelements in <10 seconds
  • Current: ~900μs (11,000× faster than target)
  • Memory: <500MB for 10k+ positions

Configuration Parameters

Parameter Default Purpose
distance_scale 10.0 Distance decay rate (meters)
heading_scale 2.0 Heading decay rate (degrees)
cutoff_distance 500.0 Max search distance (meters)
heading_cutoff 10.0 Max heading difference (degrees)
probability_threshold 0.02 Min probability for inclusion
max_candidates 3 Max candidates per position
resampling_distance None Optional GNSS resampling

Constitution Principles

TP-Lib follows strict architectural principles (see Constitution v1.1.0):

  1. Library-First: Core functionality in library, not CLI
  2. CLI Mandatory: All features accessible via command-line
  3. High Performance: Use efficient data structures (R-tree, Arrow)
  4. TDD: Test-driven development, write tests first
  5. Full Coverage: Comprehensive test suite
  6. Timezone Awareness: All timestamps with explicit timezone
  7. CRS Explicit: All coordinates include CRS specification
  8. Error Handling: Typed errors, fail-fast validation
  9. Data Provenance: Preserve original data, enable auditing
  10. Integration Flexibility: Rust API + CLI + Python bindings

Getting Help

License

By contributing, you agree that your contributions will be licensed under the Apache License 2.0.


Thank you for contributing to TP-Lib! 🚄