Skip to main content

Development Environment Setup

Prerequisites

  • Rust 1.56+: Latest stable Rust version
  • Cargo: Rust package manager
  • SQLx CLI: For database migrations
  • Git: Version control

Clone Repository

git clone https://github.com/nihalxkumar/zingat
cd zingat

Install Dependencies

# Install SQLx CLI
cargo install sqlx-cli

# Install development tools
cargo install cargo-watch  # For hot reloading
cargo install cargo-audit  # For security audits
Confirm tools with cargo --version, sqlx --version, and cargo audit --version.

Database Setup

# Setup development database
sqlx database setup

# Run migrations
sqlx migrate run

Development Database

Zingat uses SQLite for development by default. The database file is created at data.db in the project root (default connection string: sqlite:data.db).

Codebase Structure

zingat/
├── src/
│   ├── bin/
│   │   ├── httpd.rs         # Web server binary
│   │   └── clipclient.rs    # CLI client binary
│   └── lib/
│       ├── data/             # Data layer
│       │   ├── mod.rs        # Database connection and configuration
│       │   ├── model.rs      # Database models and entities
│       │   └── query.rs      # Database queries and operations
│       ├── domain/           # Domain layer
│       │   ├── clip/         # Core business logic for clips
│       │   │   ├── field/    # Newtype wrappers for type safety
│       │   │   │   ├── clip_id.rs
│       │   │   │   ├── content.rs
│       │   │   │   ├── expires.rs
│       │   │   │   ├── hits.rs
│       │   │   │   ├── password.rs
│       │   │   │   ├── posted.rs
│       │   │   │   ├── shortcode.rs
│       │   │   │   └── title.rs
│       │   │   └── mod.rs    # Main Clip struct and ClipError enum
│       │   ├── maintenance.rs  # Background maintenance tasks
│       │   └── time.rs       # Time-related utilities
│       ├── service/          # Service layer
│       │   ├── action.rs     # Business logic actions
│       │   └── ask.rs        # Request/response DTOs
│       └── web/              # Web layer
│           ├── api.rs        # REST API endpoints and authentication
│           ├── ctx.rs        # Template context structures
│           ├── form.rs       # Form handling and validation
│           ├── hitcounter.rs # Asynchronous hit counting system
│           ├── http.rs       # Web UI routes and handlers
│           ├── mod.rs        # Web module exports
│           └── renderer.rs   # Template rendering engine
├── migrations/               # Database migrations
├── templates/               # Handlebars templates
│   ├── base.hbs
│   ├── home.hbs
│   ├── clip.hbs
│   ├── clip_need_password.hbs
│   ├── error_box.hbs
│   ├── header.hbs
│   └── footer.hbs
├── static/                  # Static assets
│   ├── zingat.css
│   ├── tiny-date-picker.min.js
│   └── logo.svg
├── tests/                   # Integration tests
└── Cargo.toml              # Project dependencies

Key Modules

Data Layer (src/lib/data/)

  • mod.rs: Database connection pooling and configuration
  • model.rs: Database models matching the clips and api_keys tables
  • query.rs: SQL queries using SQLx with prepared statements

Domain Layer (src/lib/domain/)

  • clip/mod.rs: Core Clip struct and ClipError enum with all error variants
  • clip/field/: Newtype wrappers for type safety (ClipId, Content, Expires, Hits, Password, Posted, Shortcode, Title)
  • maintenance.rs: Background tasks for expired clip cleanup
  • time.rs: Time utilities for date parsing and expiration checks

Service Layer (src/lib/service/)

  • action.rs: Business logic for clip operations (create, read, update)
  • ask.rs: Request/response DTOs for API operations

Web Layer (src/lib/web/)

  • api.rs: REST API endpoints (/api/clip/*) with API key authentication
  • http.rs: Web UI routes (/, /clip/<shortcode>, /clip/raw/<shortcode>)
  • hitcounter.rs: Asynchronous hit counting with batching via crossbeam channels
  • form.rs: Form handling and validation for web UI
  • renderer.rs: Handlebars template rendering
  • ctx.rs: Template context structures

Development Workflow

Running Development Server

# Standard run
cargo run --bin httpd

# With hot reloading
cargo watch -x "run --bin httpd"

# With debug logging
RUST_LOG=debug cargo run --bin httpd

Running CLI Client

cargo run --bin clipclient

Code Formatting

# Format code
cargo fmt

# Check formatting
cargo fmt -- --check

Linting

# Run clippy linter
cargo clippy

# Run clippy with warnings
cargo clippy -- -W clippy::pedantic

Testing

Unit Tests

Run unit tests:
cargo test

Integration Tests

Integration tests are in the tests/ directory:
cargo test --test integration

Test Coverage

Install tarpaulin for test coverage:
cargo install cargo-tarpaulin
cargo tarpaulin --ignore-tests

Templates and Static Assets

Templates

Zingat uses Handlebars templates located in the templates/ directory:
  • base.hbs: Base template layout with common HTML structure
  • home.hbs: Home page with clip creation form
  • clip.hbs: Clip display page showing content and metadata
  • clip_need_password.hbs: Password prompt for protected clips
  • error_box.hbs: Error display component
  • header.hbs: Page header component
  • footer.hbs: Page footer component
Templates are rendered via the renderer.rs module which provides template context and error handling.

Static Assets

Static files are served from the static/ directory:
  • zingat.css: Custom CSS styling for the web interface
  • tiny-date-picker.min.js: JavaScript library for date picker functionality
  • logo.svg: Zingat logo (available in light and dark variants)
Static assets are served directly by Rocket and don’t require template rendering.

Dependencies Overview

Key dependencies used in Zingat: Core:
  • serde, serde_json: Serialization
  • thiserror: Error handling
  • chrono: Date/time handling
  • uuid: UUID generation
Web:
  • rocket: Web framework (0.5.0-rc.1)
  • handlebars: Template engine (4.x)
  • tokio: Async runtime (1.8.0)
Database:
  • sqlx: Database toolkit (0.8.2)
CLI:
  • structopt: Command-line argument parsing
  • dotenv: Environment variable loading
Utilities:
  • derive_more: Additional derive macros
  • rand: Random number generation
  • base64: Base64 encoding/decoding
  • reqwest: HTTP client for CLI
  • strum: String enum conversions
Concurrency:
  • crossbeam-channel: Multi-producer, multi-consumer channels for hit counter batching
  • parking_lot: Faster mutex implementation

Code Quality

Security Audits

# Check for vulnerable dependencies
cargo audit

# Update dependencies
cargo update

Documentation

Generate documentation:
# Generate docs
cargo doc --open

# Check documentation coverage
cargo doc --no-deps --document-private-items

Benchmarking

Create benchmarks for performance-critical code:
#[bench]
fn bench_paste_creation(b: &mut Bencher) {
    b.iter(|| create_test_paste());
}

Code Standards

Rust Conventions

  • Follow Rust naming conventions
  • Use rustfmt for consistent formatting
  • Apply Clippy recommendations
  • Document public APIs with /// comments

Error Handling

Zingat uses comprehensive error types with the thiserror crate: ClipError Variants (defined in src/lib/domain/clip/mod.rs):
  • NotFound: Clip doesn’t exist
  • AlreadyExists: Shortcode collision
  • Expired: Clip has passed expiration date
  • InvalidPassword: Wrong password provided
  • InvalidTitle: Title validation failed
  • EmptyContent: Content cannot be empty
  • InvalidDate: Date parsing error
API Errors:
  • NotFound: Resource not found (404)
  • Server: Internal server error (500)
  • User: Client error (401)
  • KeyError: API key issues (400)
All errors implement std::error::Error and can be converted to appropriate HTTP responses.

Database Operations

  • Use prepared statements with SQLx
  • Handle database errors appropriately
  • Implement proper connection pooling

Security

  • Hash passwords with Argon2
  • Validate all user input
  • Implement rate limiting
  • Use HTTPS in production

Performance Considerations

Database Optimization

  • Use batch operations for view tracking
  • Implement proper indexing
  • Monitor query performance

Memory Management

  • Use Rust’s ownership system effectively
  • Avoid unnecessary allocations
  • Implement proper caching

Async Programming

  • Use Tokio for async operations
  • Avoid blocking calls in async context
  • Implement proper error handling in async code

Debugging

Logging

Enable different log levels:
RUST_LOG=debug cargo run --bin httpd
RUST_LOG=zingat=debug cargo run --bin httpd
1

Start the API server

Run cargo run --bin httpd and ensure the server listens on the expected port.
2

Smoke test the API

xh POST http://localhost:8000/api/paste content='dev test' expires_in:=60
3

Run CLI client

cargo run --bin clipclient

Debugging Tools

  • println! debugging: Simple output debugging
  • dbg! macro: Quick value inspection
  • Rust debugger: Use GDB or LLDB
  • Tracing: Structured logging

Database Inspection

# SQLite inspection
sqlite3 zingat.db
.tables
.schema pastes