- Move development details to DEVELOPMENT.md - Move contributing guidelines to CONTRIBUTING.md - Add LICENSE file with MIT license - Add CONTRIBUTORS.md for attribution - Update README.md to focus on user-facing information - Improve documentation structure and navigation
416 lines
11 KiB
Markdown
416 lines
11 KiB
Markdown
# Development Guide
|
|
|
|
This document provides comprehensive information about the design, architecture, and development of the CalDAV Calendar Synchronizer.
|
|
|
|
## Architecture Overview
|
|
|
|
The application is built with a modular architecture using Rust's strong type system and async capabilities.
|
|
|
|
### Core Components
|
|
|
|
#### 1. **Configuration System** (`src/config.rs`)
|
|
- **Purpose**: Manage configuration from files, environment variables, and CLI arguments
|
|
- **Features**:
|
|
- TOML-based configuration files
|
|
- Environment variable support
|
|
- Command-line argument overrides
|
|
- Configuration validation
|
|
- **Key Types**: `Config`, `ServerConfig`, `CalendarConfig`, `SyncConfig`
|
|
|
|
#### 2. **CalDAV Client** (`src/caldav_client.rs`)
|
|
- **Purpose**: Handle CalDAV protocol operations with Zoho and Nextcloud
|
|
- **Features**:
|
|
- HTTP client with authentication
|
|
- Calendar discovery via PROPFIND
|
|
- Event retrieval via REPORT requests
|
|
- Event creation via PUT requests
|
|
- **Key Types**: `CalDavClient`, `CalendarInfo`, `CalDavEventInfo`
|
|
|
|
#### 3. **Event Model** (`src/event.rs`)
|
|
- **Purpose**: Represent calendar events and handle parsing
|
|
- **Features**:
|
|
- iCalendar data parsing
|
|
- Timezone-aware datetime handling
|
|
- Event filtering and validation
|
|
- **Key Types**: `Event`, `EventBuilder`, `EventFilter`
|
|
|
|
#### 4. **Timezone Handler** (`src/timezone.rs`)
|
|
- **Purpose**: Manage timezone conversions and datetime operations
|
|
- **Features**:
|
|
- Convert between different timezones
|
|
- Parse timezone information from iCalendar data
|
|
- Handle DST transitions
|
|
- **Key Types**: `TimezoneHandler`, `TimeZoneInfo`
|
|
|
|
#### 5. **Calendar Filter** (`src/calendar_filter.rs`)
|
|
- **Purpose**: Filter calendars and events based on user criteria
|
|
- **Features**:
|
|
- Calendar name filtering
|
|
- Regex pattern matching
|
|
- Event date range filtering
|
|
- **Key Types**: `CalendarFilter`, `FilterRule`, `EventFilter`
|
|
|
|
#### 6. **Sync Engine** (`src/sync.rs`)
|
|
- **Purpose**: Coordinate the synchronization process
|
|
- **Features**:
|
|
- Pull events from Zoho
|
|
- Push events to Nextcloud
|
|
- Conflict resolution
|
|
- Progress tracking
|
|
- **Key Types**: `SyncEngine`, `SyncResult`, `SyncStats`
|
|
|
|
#### 7. **Error Handling** (`src/error.rs`)
|
|
- **Purpose**: Comprehensive error management
|
|
- **Features**:
|
|
- Custom error types
|
|
- Error context and chaining
|
|
- User-friendly error messages
|
|
- **Key Types**: `CalDavError`, `CalDavResult`
|
|
|
|
## Design Decisions
|
|
|
|
### 1. **Selective Calendar Import**
|
|
The application allows users to select specific Zoho calendars to import from, consolidating all events into a single Nextcloud calendar. This design choice:
|
|
|
|
- **Reduces complexity** compared to bidirectional sync
|
|
- **Provides clear data flow** (Zoho → Nextcloud)
|
|
- **Minimizes sync conflicts**
|
|
- **Matches user requirements** exactly
|
|
|
|
### 2. **Timezone Handling**
|
|
All events are converted to UTC internally for consistency, while preserving original timezone information:
|
|
|
|
```rust
|
|
pub struct Event {
|
|
pub id: String,
|
|
pub summary: String,
|
|
pub start: DateTime<Utc>,
|
|
pub end: DateTime<Utc>,
|
|
pub original_timezone: Option<String>,
|
|
pub source_calendar: String,
|
|
}
|
|
```
|
|
|
|
### 3. **Configuration Hierarchy**
|
|
Configuration is loaded in priority order:
|
|
|
|
1. **Command line arguments** (highest priority)
|
|
2. **User config file** (`config/config.toml`)
|
|
3. **Default config file** (`config/default.toml`)
|
|
4. **Environment variables**
|
|
5. **Hardcoded defaults** (lowest priority)
|
|
|
|
### 4. **Error Handling Strategy**
|
|
Uses `thiserror` for custom error types and `anyhow` for error propagation:
|
|
|
|
```rust
|
|
#[derive(Error, Debug)]
|
|
pub enum CalDavError {
|
|
#[error("HTTP error: {0}")]
|
|
Http(#[from] reqwest::Error),
|
|
|
|
#[error("Configuration error: {0}")]
|
|
Config(String),
|
|
|
|
#[error("Calendar not found: {0}")]
|
|
CalendarNotFound(String),
|
|
|
|
// ... more error variants
|
|
}
|
|
```
|
|
|
|
## Data Flow
|
|
|
|
### 1. **Application Startup**
|
|
```
|
|
1. Load CLI arguments
|
|
2. Load configuration files
|
|
3. Apply environment variables
|
|
4. Validate configuration
|
|
5. Initialize logging
|
|
6. Create CalDAV clients
|
|
```
|
|
|
|
### 2. **Calendar Discovery**
|
|
```
|
|
1. Connect to Zoho CalDAV server
|
|
2. Authenticate with app password
|
|
3. Send PROPFIND request to discover calendars
|
|
4. Parse calendar list and metadata
|
|
5. Apply user filters to select calendars
|
|
```
|
|
|
|
### 3. **Event Synchronization**
|
|
```
|
|
1. Query selected Zoho calendars for events (next week)
|
|
2. Parse iCalendar data into Event objects
|
|
3. Convert timestamps to UTC with timezone preservation
|
|
4. Apply event filters (duration, status, patterns)
|
|
5. Connect to Nextcloud CalDAV server
|
|
6. Create target calendar if needed
|
|
7. Upload events to Nextcloud calendar
|
|
8. Report sync statistics
|
|
```
|
|
|
|
## Key Algorithms
|
|
|
|
### 1. **Calendar Filtering**
|
|
```rust
|
|
impl CalendarFilter {
|
|
pub fn should_import_calendar(&self, calendar_name: &str) -> bool {
|
|
// Check exact matches
|
|
if self.selected_names.contains(&calendar_name.to_string()) {
|
|
return true;
|
|
}
|
|
|
|
// Check regex patterns
|
|
for pattern in &self.regex_patterns {
|
|
if pattern.is_match(calendar_name) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
false
|
|
}
|
|
}
|
|
```
|
|
|
|
### 2. **Timezone Conversion**
|
|
```rust
|
|
impl TimezoneHandler {
|
|
pub fn convert_to_utc(&self, dt: DateTime<FixedOffset>, timezone: &str) -> CalDavResult<DateTime<Utc>> {
|
|
let tz = self.get_timezone(timezone)?;
|
|
let local_dt = dt.with_timezone(&tz);
|
|
Ok(local_dt.with_timezone(&Utc))
|
|
}
|
|
}
|
|
```
|
|
|
|
### 3. **Event Processing**
|
|
```rust
|
|
impl SyncEngine {
|
|
pub async fn sync_calendar(&mut self, calendar: &CalendarInfo) -> CalDavResult<SyncResult> {
|
|
// 1. Fetch events from Zoho
|
|
let zoho_events = self.fetch_zoho_events(calendar).await?;
|
|
|
|
// 2. Filter and process events
|
|
let processed_events = self.process_events(zoho_events)?;
|
|
|
|
// 3. Upload to Nextcloud
|
|
let upload_results = self.upload_to_nextcloud(processed_events).await?;
|
|
|
|
// 4. Return sync statistics
|
|
Ok(SyncResult::from_upload_results(upload_results))
|
|
}
|
|
}
|
|
```
|
|
|
|
## Configuration Schema
|
|
|
|
### Complete Configuration Structure
|
|
```toml
|
|
# Zoho Configuration (Source)
|
|
[zoho]
|
|
server_url = "https://caldav.zoho.com/caldav"
|
|
username = "your-zoho-email@domain.com"
|
|
password = "your-zoho-app-password"
|
|
selected_calendars = ["Work Calendar", "Personal Calendar"]
|
|
|
|
# Nextcloud Configuration (Target)
|
|
[nextcloud]
|
|
server_url = "https://your-nextcloud-domain.com"
|
|
username = "your-nextcloud-username"
|
|
password = "your-nextcloud-app-password"
|
|
target_calendar = "Imported-Zoho-Events"
|
|
create_if_missing = true
|
|
|
|
# General Settings
|
|
[server]
|
|
timeout = 30
|
|
|
|
[calendar]
|
|
color = "#3174ad"
|
|
timezone = "UTC"
|
|
|
|
[sync]
|
|
interval = 300
|
|
sync_on_startup = true
|
|
weeks_ahead = 1
|
|
dry_run = false
|
|
|
|
# Optional Filtering
|
|
[filters]
|
|
min_duration_minutes = 5
|
|
max_duration_hours = 24
|
|
exclude_patterns = ["Cancelled:", "BLOCKED"]
|
|
include_status = ["confirmed", "tentative"]
|
|
exclude_status = ["cancelled"]
|
|
```
|
|
|
|
## Dependencies and External Libraries
|
|
|
|
### Core Dependencies
|
|
```toml
|
|
[dependencies]
|
|
tokio = { version = "1.0", features = ["full"] } # Async runtime
|
|
reqwest = { version = "0.11", features = ["json", "xml"] } # HTTP client
|
|
serde = { version = "1.0", features = ["derive"] } # Serialization
|
|
chrono = { version = "0.4", features = ["serde"] } # Date/time
|
|
chrono-tz = "0.8" # Timezone support
|
|
quick-xml = "0.28" # XML parsing
|
|
thiserror = "1.0" # Error handling
|
|
anyhow = "1.0" # Error propagation
|
|
config = "0.13" # Configuration
|
|
clap = { version = "4.0", features = ["derive"] } # CLI
|
|
tracing = "0.1" # Logging
|
|
tracing-subscriber = "0.3" # Log formatting
|
|
toml = "0.8" # TOML parsing
|
|
```
|
|
|
|
### Optional Dependencies for Future Features
|
|
```toml
|
|
# For enhanced XML handling
|
|
serde_xml_rs = "0.6"
|
|
|
|
# For better HTTP client customization
|
|
http = "0.2"
|
|
|
|
# For async file operations
|
|
tokio-util = "0.7"
|
|
|
|
# For better error formatting
|
|
color-eyre = "0.6"
|
|
```
|
|
|
|
## Testing Strategy
|
|
|
|
### 1. **Unit Tests**
|
|
- Individual module functionality
|
|
- Configuration parsing and validation
|
|
- Event parsing and timezone conversion
|
|
- Error handling paths
|
|
|
|
### 2. **Integration Tests**
|
|
- End-to-end CalDAV operations
|
|
- Configuration loading from files
|
|
- CLI argument processing
|
|
- HTTP client behavior
|
|
|
|
### 3. **Mock Testing**
|
|
- Mock CalDAV server responses
|
|
- Test error conditions without real servers
|
|
- Validate retry logic and timeout handling
|
|
|
|
## Performance Considerations
|
|
|
|
### 1. **Async Operations**
|
|
All network operations are async to prevent blocking:
|
|
```rust
|
|
pub async fn fetch_events(&self, calendar: &CalendarInfo) -> CalDavResult<Vec<Event>> {
|
|
let response = self.client
|
|
.request(reqwest::Method::REPORT, &calendar.url)
|
|
.body(report_body)
|
|
.send()
|
|
.await?;
|
|
|
|
// Process response...
|
|
}
|
|
```
|
|
|
|
### 2. **Memory Management**
|
|
- Stream processing for large calendar responses
|
|
- Efficient string handling with `Cow<str>` where appropriate
|
|
- Clear lifecycle management for HTTP connections
|
|
|
|
### 3. **Configuration Caching**
|
|
- Cache parsed timezone information
|
|
- Reuse HTTP connections where possible
|
|
- Validate configuration once at startup
|
|
|
|
## Security Considerations
|
|
|
|
### 1. **Authentication**
|
|
- Support for app-specific passwords only
|
|
- Never log authentication credentials
|
|
- Secure storage of sensitive configuration
|
|
|
|
### 2. **Network Security**
|
|
- Enforce HTTPS by default
|
|
- SSL certificate validation
|
|
- Custom CA certificate support
|
|
|
|
### 3. **Data Privacy**
|
|
- Minimal data collection (only required event fields)
|
|
- Optional debug logging with sensitive data filtering
|
|
- Clear data retention policies
|
|
|
|
## Future Enhancements
|
|
|
|
### 1. **Enhanced Filtering**
|
|
- Advanced regex patterns
|
|
- Calendar color-based filtering
|
|
- Attendee-based filtering
|
|
|
|
### 2. **Bidirectional Sync**
|
|
- Two-way synchronization with conflict resolution
|
|
- Event modification tracking
|
|
- Deletion synchronization
|
|
|
|
### 3. **Performance Optimizations**
|
|
- Parallel calendar processing
|
|
- Incremental sync with change detection
|
|
- Local caching and offline mode
|
|
|
|
### 4. **User Experience**
|
|
- Interactive configuration wizard
|
|
- Web-based status dashboard
|
|
- Real-time sync notifications
|
|
|
|
## Build and Development
|
|
|
|
### 1. **Development Setup**
|
|
```bash
|
|
# Clone repository
|
|
git clone ssh://git@gitea.soliverez.com.ar/alvaro/caldavpuller.git
|
|
cd caldavpuller
|
|
|
|
# Install Rust toolchain
|
|
rustup update stable
|
|
rustup component add rustfmt clippy
|
|
|
|
# Build in development mode
|
|
cargo build
|
|
|
|
# Run tests
|
|
cargo test
|
|
|
|
# Check formatting
|
|
cargo fmt --check
|
|
|
|
# Run linter
|
|
cargo clippy -- -D warnings
|
|
```
|
|
|
|
### 2. **Release Build**
|
|
```bash
|
|
# Build optimized release
|
|
cargo build --release
|
|
|
|
# Create distribution archive
|
|
tar -czf caldav-sync-${VERSION}-${TARGET}.tar.gz \
|
|
-C target/release caldav-sync \
|
|
-C ../config example.toml \
|
|
-C .. README.md LICENSE
|
|
```
|
|
|
|
### 3. **Testing with Mock Servers**
|
|
For testing without real CalDAV servers, use the mock server setup:
|
|
```bash
|
|
# Start mock CalDAV server
|
|
cargo run --bin mock-server
|
|
|
|
# Run integration tests against mock server
|
|
cargo test --test integration_tests
|
|
```
|
|
|
|
This architecture provides a solid foundation for the CalDAV synchronization tool while maintaining flexibility for future enhancements.
|