Major additions: - New NextcloudImportEngine with import behaviors (SkipDuplicates, Overwrite, Merge) - Complete import workflow with result tracking and conflict resolution - Support for dry-run mode and detailed progress reporting - Import command integration in CLI with --import-events flag Configuration improvements: - Added ImportConfig struct for structured import settings - Backward compatibility with legacy ImportTargetConfig - Enhanced get_import_config() method supporting both formats CalDAV client enhancements: - Improved XML parsing for multiple calendar display name formats - Better fallback handling for calendar discovery - Enhanced error handling and debugging capabilities Bug fixes: - Fixed test compilation errors in error.rs (reqwest::Error type conversion) - Resolved unused variable warning in main.rs - All tests now pass (16/16) Documentation: - Added comprehensive NEXTCLOUD_IMPORT_PLAN.md with implementation roadmap - Updated library exports to include new modules Files changed: - src/nextcloud_import.rs: New import engine implementation - src/config.rs: Enhanced configuration with import support - src/main.rs: Added import command and CLI integration - src/minicaldav_client.rs: Improved calendar discovery and XML parsing - src/error.rs: Fixed test compilation issues - src/lib.rs: Updated module exports - Deleted: src/real_caldav_client.rs (removed unused file)
12 KiB
12 KiB
Nextcloud CalDAV Import Implementation Plan
Current State Analysis
Current Code Overview
The caldavpuller project is a Rust-based CalDAV synchronization tool that currently:
- Reads events from Zoho calendars using multiple approaches (zoho-export, zoho-events-list, zoho-events-direct)
- Supports basic CalDAV operations like listing calendars and events
- Has a solid event model in
src/event.rswith support for datetime, timezone, title, and other properties - Implements CalDAV client functionality in
src/caldav_client.rsand related files - Can already generate iCalendar format using the
to_ical()method
Current Capabilities
- ✅ Event listing: Can read and display events from external sources
- ✅ iCalendar generation: Has basic iCalendar export functionality
- ✅ CalDAV client: Basic WebDAV operations implemented
- ✅ Configuration: Flexible configuration system for different CalDAV servers
Missing Functionality for Nextcloud Import
- ❌ PUT/POST operations: No ability to write events to CalDAV servers
- ❌ Calendar creation: Cannot create new calendars on Nextcloud
- ❌ Nextcloud-specific optimizations: No handling for Nextcloud's CalDAV implementation specifics
- ❌ Import workflow: No dedicated import command or process
Nextcloud CalDAV Architecture
Based on research of Nextcloud's CalDAV implementation (built on SabreDAV):
Key Requirements
- Standard CalDAV Compliance: Nextcloud follows RFC 4791 CalDAV specification
- iCalendar Format: Requires RFC 5545 compliant iCalendar data
- Authentication: Basic auth or app password authentication
- URL Structure: Typically
/remote.php/dav/calendars/{user}/{calendar-name}/
Nextcloud-Specific Features
- SabreDAV Backend: Nextcloud uses SabreDAV as its CalDAV server
- WebDAV Extensions: Supports standard WebDAV sync operations
- Calendar Discovery: Can auto-discover user calendars via PROPFIND
- ETag Support: Proper ETag handling for synchronization
- Multi-Get Operations: Supports calendar-multiget for efficiency
Implementation Plan
Phase 1: Core CalDAV Write Operations
1.1 Extend CalDAV Client for Write Operations
File: src/caldav_client.rs
Required Methods:
// Create or update an event
pub async fn put_event(&self, calendar_url: &str, event_path: &str, ical_data: &str) -> CalDavResult<()>
// Create a new calendar
pub async fn create_calendar(&self, calendar_name: &str, display_name: Option<&str>) -> CalDavResult<String>
// Upload multiple events efficiently
pub async fn import_events_batch(&self, calendar_url: &str, events: &[Event]) -> CalDavResult<Vec<CalDavResult<()>>>
Implementation Details:
- Use HTTP PUT method for individual events
- Handle ETag conflicts with If-Match headers
- Use proper content-type:
text/calendar; charset=utf-8 - Support both creating new events and updating existing ones
1.2 Enhanced Event to iCalendar Conversion
File: src/event.rs
Current Issues:
- Timezone handling is incomplete
- Missing proper DTSTAMP and LAST-MODIFIED
- Limited property support
Required Enhancements:
impl Event {
pub fn to_ical_for_nextcloud(&self) -> CalDavResult<String> {
// Enhanced iCalendar generation with:
// - Proper timezone handling
// - Nextcloud-specific properties
// - Better datetime formatting
// - Required properties for Nextcloud compatibility
}
pub fn generate_unique_path(&self) -> String {
// Generate filename/path for CalDAV storage
format!("{}.ics", self.uid)
}
}
Phase 2: Nextcloud Integration
2.1 Nextcloud Client Extension
New File: src/nextcloud_client.rs
pub struct NextcloudClient {
client: CalDavClient,
base_url: String,
username: String,
}
impl NextcloudClient {
pub fn new(config: NextcloudConfig) -> CalDavResult<Self>
// Auto-discover calendars
pub async fn discover_calendars(&self) -> CalDavResult<Vec<CalendarInfo>>
// Create calendar if it doesn't exist
pub async fn ensure_calendar_exists(&self, name: &str, display_name: Option<&str>) -> CalDavResult<String>
// Import events with conflict resolution
pub async fn import_events(&self, calendar_name: &str, events: Vec<Event>) -> CalDavResult<ImportResult>
// Check if event already exists
pub async fn event_exists(&self, calendar_name: &str, event_uid: &str) -> CalDavResult<bool>
// Get existing event ETag
pub async fn get_event_etag(&self, calendar_name: &str, event_uid: &str) -> CalDavResult<Option<String>>
}
2.2 Nextcloud Configuration
File: src/config.rs
Add Nextcloud-specific configuration:
[nextcloud]
# Nextcloud server URL (e.g., https://cloud.example.com)
server_url = "https://cloud.example.com"
# Username
username = "your_username"
# App password (recommended) or regular password
password = "your_app_password"
# Default calendar for imports
default_calendar = "imported-events"
# Import behavior
import_behavior = "skip_duplicates" # or "overwrite" or "merge"
# Conflict resolution
conflict_resolution = "keep_existing" # or "overwrite_remote" or "merge"
Phase 3: Import Workflow Implementation
3.1 Import Command Line Interface
File: src/main.rs
Add new CLI options:
/// Import events into Nextcloud calendar
#[arg(long)]
import_nextcloud: bool,
/// Target calendar name for Nextcloud import
#[arg(long)]
nextcloud_calendar: Option<String>,
/// Import behavior (skip_duplicates, overwrite, merge)
#[arg(long, default_value = "skip_duplicates")]
import_behavior: String,
/// Dry run - show what would be imported without actually doing it
#[arg(long)]
dry_run: bool,
3.2 Import Engine
New File: src/nextcloud_import.rs
pub struct ImportEngine {
nextcloud_client: NextcloudClient,
config: ImportConfig,
}
pub struct ImportResult {
pub total_events: usize,
pub imported: usize,
pub skipped: usize,
pub errors: Vec<ImportError>,
pub conflicts: Vec<ConflictInfo>,
}
impl ImportEngine {
pub async fn import_events(&self, events: Vec<Event>) -> CalDavResult<ImportResult> {
// 1. Validate events
// 2. Check for existing events
// 3. Resolve conflicts based on configuration
// 4. Batch upload events
// 5. Report results
}
fn validate_event(&self, event: &Event) -> CalDavResult<()> {
// Ensure required fields are present
// Validate datetime and timezone
// Check for Nextcloud compatibility
}
async fn check_existing_event(&self, event: &Event) -> CalDavResult<Option<String>> {
// Return ETag if event exists, None otherwise
}
async fn resolve_conflict(&self, existing_event: &str, new_event: &Event) -> CalDavResult<ConflictResolution> {
// Based on configuration: skip, overwrite, or merge
}
}
Phase 4: Error Handling and Validation
4.1 Enhanced Error Types
File: src/error.rs
#[derive(Debug, thiserror::Error)]
pub enum ImportError {
#[error("Event validation failed: {message}")]
ValidationFailed { message: String },
#[error("Event already exists: {uid}")]
EventExists { uid: String },
#[error("Calendar creation failed: {message}")]
CalendarCreationFailed { message: String },
#[error("Import conflict: {event_uid} - {message}")]
ImportConflict { event_uid: String, message: String },
#[error("Nextcloud API error: {status} - {message}")]
NextcloudError { status: u16, message: String },
}
4.2 Event Validation
impl Event {
pub fn validate_for_nextcloud(&self) -> CalDavResult<()> {
// Check required fields
if self.summary.trim().is_empty() {
return Err(CalDavError::EventProcessing("Event summary cannot be empty".to_string()));
}
// Validate timezone
if let Some(ref tz) = self.timezone {
if !is_valid_timezone(tz) {
return Err(CalDavError::EventProcessing(format!("Invalid timezone: {}", tz)));
}
}
// Check date ranges
if self.start > self.end {
return Err(CalDavError::EventProcessing("Event start must be before end".to_string()));
}
Ok(())
}
}
Phase 5: Testing and Integration
5.1 Unit Tests
File: tests/nextcloud_import_tests.rs
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_event_validation() {
// Test valid and invalid events
}
#[tokio::test]
async fn test_ical_generation() {
// Test iCalendar output format
}
#[tokio::test]
async fn test_conflict_resolution() {
// Test different conflict strategies
}
#[tokio::test]
async fn test_calendar_creation() {
// Test Nextcloud calendar creation
}
}
5.2 Integration Tests
File: tests/nextcloud_integration_tests.rs
// These tests require a real Nextcloud instance
// Use environment variables for test credentials
#[tokio::test]
#[ignore] // Run manually with real instance
async fn test_full_import_workflow() {
// Test complete import process
}
#[tokio::test]
#[ignore]
async fn test_duplicate_handling() {
// Test duplicate event handling
}
Implementation Priorities
Priority 1: Core Import Functionality
- Enhanced CalDAV client with PUT support - Essential for writing events
- Basic Nextcloud client - Discovery and calendar operations
- Import command - CLI interface for importing events
- Event validation - Ensure data quality
Priority 2: Advanced Features
- Conflict resolution - Handle existing events gracefully
- Batch operations - Improve performance for many events
- Error handling - Comprehensive error management
- Testing suite - Ensure reliability
Priority 3: Optimization and Polish
- Progress reporting - User feedback during import
- Dry run mode - Preview imports before execution
- Configuration validation - Better error messages
- Documentation - User guides and API docs
Technical Considerations
Nextcloud URL Structure
Base URL: https://cloud.example.com
Principal: /remote.php/dav/principals/users/{username}/
Calendar Home: /remote.php/dav/calendars/{username}/
Calendar URL: /remote.php/dav/calendars/{username}/{calendar-name}/
Event URL: /remote.php/dav/calendars/{username}/{calendar-name}/{event-uid}.ics
Authentication
- App Passwords: Recommended over regular passwords
- Basic Auth: Standard HTTP Basic authentication
- Two-Factor: Must use app passwords if 2FA enabled
iCalendar Compliance
- RFC 5545: Strict compliance required
- Required Properties: UID, DTSTAMP, SUMMARY, DTSTART, DTEND
- Timezone Support: Proper TZID usage
- Line Folding: Handle long lines properly
Performance Considerations
- Batch Operations: Use calendar-multiget where possible
- Concurrency: Import multiple events in parallel
- Memory Management: Process large event lists in chunks
- Network Efficiency: Minimize HTTP requests
Success Criteria
Minimum Viable Product
- ✅ Can import events with title, datetime, and timezone into Nextcloud
- ✅ Handles duplicate events gracefully
- ✅ Provides clear error messages and progress feedback
- ✅ Works with common Nextcloud configurations
Complete Implementation
- ✅ Full conflict resolution strategies
- ✅ Batch import with performance optimization
- ✅ Comprehensive error handling and recovery
- ✅ Test suite with >90% coverage
- ✅ Documentation and examples
Next Steps
- Week 1: Implement CalDAV PUT operations and basic Nextcloud client
- Week 2: Add import command and basic workflow
- Week 3: Implement validation and error handling
- Week 4: Add conflict resolution and batch operations
- Week 5: Testing, optimization, and documentation
This plan provides a structured approach to implementing robust Nextcloud CalDAV import functionality while maintaining compatibility with the existing codebase architecture.