package com.rippr.data import android.content.Context import androidx.room.Database import androidx.room.Room import androidx.room.RoomDatabase import androidx.room.TypeConverter import androidx.room.TypeConverters class Converters { @TypeConverter fun toTripState(value: String): TripState = TripState.valueOf(value) @TypeConverter fun fromTripState(state: TripState): String = state.name } @Database( entities = [Trip::class, Segment::class, TrackPoint::class], version = 2, exportSchema = true, ) @TypeConverters(Converters::class) abstract class AppDatabase : RoomDatabase() { abstract fun tripDao(): TripDao abstract fun segmentDao(): SegmentDao abstract fun trackPointDao(): TrackPointDao companion object { @Volatile private var INSTANCE: AppDatabase? = null fun getDatabase(context: Context): AppDatabase = INSTANCE ?: synchronized(this) { INSTANCE ?: Room.databaseBuilder( context.applicationContext, AppDatabase::class.java, "rippr_db" ) // A ride is unrecoverable if a write is lost to a crash mid-flush, // but full sync on every insert at 2 Hz burns battery. WAL with // NORMAL sync is the standard compromise and survives app crashes; // only an OS-level crash can lose the last few points. .setJournalMode(JournalMode.WRITE_AHEAD_LOGGING) // No destructive fallback. Rides recorded from v2 onward are real // data, so any future schema change MUST ship a Migration against the // committed schemas/com.rippr.data.AppDatabase/2.json baseline. // Reinstating fallbackToDestructiveMigration() here would silently // delete every stored ride on the next version bump. .build() .also { INSTANCE = it } } /** Test seam — lets instrumented tests substitute an in-memory database. */ fun overrideForTest(db: AppDatabase?) { INSTANCE = db } } }