154 lines
5.9 KiB
Markdown
154 lines
5.9 KiB
Markdown
# Feature 0: Foundation
|
|
|
|
## Context
|
|
Before implementing any user-facing features, we need shared infrastructure: package structure, platform abstraction, config persistence, and logging. Every subsequent feature depends on this.
|
|
|
|
## Dependencies
|
|
- None (this is the base layer)
|
|
- **Depended on by**: All other features (1-5)
|
|
|
|
## New Dependencies to Add
|
|
|
|
### gradle/libs.versions.toml
|
|
| Library | Coordinate | Version | Purpose |
|
|
|---------|-----------|---------|---------|
|
|
| kotlinx-serialization-json | org.jetbrains.kotlinx:kotlinx-serialization-json | 1.10.0 | JSON config persistence |
|
|
| kotlin-logging-jvm | io.github.oshai:kotlin-logging-jvm | 7.0.3 | Kotlin-idiomatic logging |
|
|
| logback-classic | ch.qos.logback:logback-classic | 1.5.18 | Logging backend with file rotation |
|
|
|
|
### Plugins
|
|
| Plugin | ID | Version |
|
|
|--------|----|---------|
|
|
| Kotlin Serialization | org.jetbrains.kotlin.plugin.serialization | (uses kotlin version) |
|
|
|
|
## Package Structure
|
|
|
|
```
|
|
com.rukira.wowbackup/
|
|
├── main.kt # Entry point (existing, to be updated)
|
|
├── App.kt # Root composable (existing, to be gutted)
|
|
├── platform/
|
|
│ ├── Platform.kt # OS enum + detection
|
|
│ ├── AppDirectories.kt # Platform-specific app data paths
|
|
│ └── WoWLocations.kt # Default WoW install detection
|
|
├── config/
|
|
│ ├── AppConfig.kt # @Serializable data class
|
|
│ └── ConfigManager.kt # Read/write JSON config file
|
|
└── logging/
|
|
└── LoggingSetup.kt # Logback initialization
|
|
```
|
|
|
|
## Implementation Steps
|
|
|
|
### Step 1: Add dependencies
|
|
**Files to modify:**
|
|
- `gradle/libs.versions.toml` — add versions, libraries, and serialization plugin
|
|
- `build.gradle.kts` (root) — register serialization plugin
|
|
- `composeApp/build.gradle.kts` — apply serialization plugin, add new deps to jvmMain
|
|
- `settings.gradle.kts` — rename root project from "MyApplication" to "WoWBackup"
|
|
|
|
### Step 2: Platform abstraction
|
|
**Files to create:**
|
|
- `composeApp/src/jvmMain/kotlin/com/rukira/wowbackup/platform/Platform.kt`
|
|
|
|
```kotlin
|
|
enum class OS { Windows, Mac, Linux, Other }
|
|
|
|
object Platform {
|
|
val current: OS = System.getProperty("os.name").lowercase().let { name ->
|
|
when {
|
|
"mac" in name -> OS.Mac
|
|
"win" in name -> OS.Windows
|
|
"nux" in name || "nix" in name -> OS.Linux
|
|
else -> OS.Other
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
**Files to delete:**
|
|
- `composeApp/src/jvmMain/kotlin/com/rukira/wowbackup/Platform.kt` (old template file)
|
|
- `composeApp/src/jvmMain/kotlin/com/rukira/wowbackup/Greeting.kt` (template boilerplate)
|
|
|
|
### Step 3: App directories
|
|
**Files to create:**
|
|
- `composeApp/src/jvmMain/kotlin/com/rukira/wowbackup/platform/AppDirectories.kt`
|
|
|
|
Resolves platform-specific app data directory:
|
|
- macOS: `~/Library/Application Support/WoWBackup/`
|
|
- Windows: `%APPDATA%/WoWBackup/`
|
|
- Linux: `$XDG_CONFIG_HOME/WoWBackup/` (fallback `~/.config/WoWBackup/`)
|
|
|
|
Creates directory on first access if it doesn't exist.
|
|
|
|
### Step 4: WoW install detection
|
|
**Files to create:**
|
|
- `composeApp/src/jvmMain/kotlin/com/rukira/wowbackup/platform/WoWLocations.kt`
|
|
|
|
Scans known default install paths:
|
|
- macOS: `/Applications/World of Warcraft/`, `~/Applications/World of Warcraft/`
|
|
- Windows: `C:\Program Files (x86)\World of Warcraft\`, `C:\Program Files\World of Warcraft\`
|
|
|
|
Validates by checking for WTF folder, WoW executable, or .app bundle.
|
|
Returns `File?` — null if not found.
|
|
|
|
### Step 5: Config data model + persistence
|
|
**Files to create:**
|
|
- `composeApp/src/jvmMain/kotlin/com/rukira/wowbackup/config/AppConfig.kt`
|
|
|
|
```kotlin
|
|
@Serializable
|
|
data class AppConfig(
|
|
val wowInstallPath: String? = null,
|
|
val backupPath: String? = null,
|
|
val backupTimeOfDay: LocalTime = LocalTime(hour = 3, minute = 0), // daily at 3:00 AM
|
|
val backupHistoryCount: Int = 5,
|
|
val forceBackupWhileRunning: Boolean = false,
|
|
val backupWtf: Boolean = true,
|
|
val backupInterface: Boolean = true,
|
|
val compressionEnabled: Boolean = false,
|
|
val notificationsEnabled: Boolean = true,
|
|
val runAtStartup: Boolean = false,
|
|
)
|
|
```
|
|
|
|
- `composeApp/src/jvmMain/kotlin/com/rukira/wowbackup/config/ConfigManager.kt`
|
|
|
|
Responsibilities:
|
|
- Load config from `<appDataDir>/config.json` (returns defaults if file missing)
|
|
- Save config to `<appDataDir>/config.json` (pretty-printed JSON)
|
|
- Expose config as `StateFlow<AppConfig>` for reactive UI updates
|
|
- `isConfigured: Boolean` — true when wowInstallPath and backupPath are set
|
|
- Thread-safe reads/writes
|
|
|
|
### Step 6: Logging setup
|
|
**Files to create:**
|
|
- `composeApp/src/jvmMain/resources/logback.xml`
|
|
|
|
Configuration:
|
|
- Console appender (for development)
|
|
- Rolling file appender to `<appDataDir>/logs/wowbackup.log`
|
|
- Daily rotation + 10MB max size per file
|
|
- 30 days retention / 100MB total cap
|
|
- Pattern: `%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n`
|
|
|
|
- `composeApp/src/jvmMain/kotlin/com/rukira/wowbackup/logging/LoggingSetup.kt`
|
|
|
|
Programmatically sets the log directory to the platform-specific app data path at startup (since logback.xml can't reference runtime-resolved paths directly).
|
|
|
|
### Step 7: Update entry point
|
|
**Files to modify:**
|
|
- `composeApp/src/jvmMain/kotlin/com/rukira/wowbackup/main.kt`
|
|
- Initialize logging on startup
|
|
- Load config via ConfigManager
|
|
- Update window title to "WoW Backup"
|
|
- `composeApp/src/jvmMain/kotlin/com/rukira/wowbackup/App.kt`
|
|
- Strip template boilerplate
|
|
- Simple placeholder screen showing config status ("Configured" / "Not configured")
|
|
|
|
## Verification
|
|
1. `./gradlew composeApp:run` — app launches, shows placeholder UI
|
|
2. Check `~/Library/Application Support/WoWBackup/` (macOS) exists after launch
|
|
3. Check `config.json` is created with defaults after first run
|
|
4. Check `logs/wowbackup.log` exists and contains startup log entries
|
|
5. If WoW is installed in default location, verify auto-detection logs the path
|