diff --git a/build.yaml b/build.yaml new file mode 100644 index 0000000..72b188c --- /dev/null +++ b/build.yaml @@ -0,0 +1,13 @@ +targets: + $default: + builders: + drift_dev: + options: + # Preserve Dart field names as SQL column names instead of snake_casing them. + # + # This keeps the schema column-for-column identical to the native app's Room + # schema (app/schemas/com.rippr.data.AppDatabase/2.json), which means the raw + # SQL in database.dart can stay byte-identical to the Kotlin DAO queries it was + # ported from -- much easier to review -- and leaves the door open to reading an + # old Room database file directly if an importer is ever wanted. + case_from_dart_to_sql: preserve diff --git a/docs/port/PROGRESS.md b/docs/port/PROGRESS.md index 1cfe9a0..f558f68 100644 --- a/docs/port/PROGRESS.md +++ b/docs/port/PROGRESS.md @@ -277,3 +277,43 @@ comment in `run.sh` says so explicitly. its ~965 lines of tests, and proven equivalent rather than assumed equivalent. Next: **T08 (Drift schema)**, the first task that touches persistence. + +--- + +## T08 — Drift schema · **complete** + +`lib/src/data/database.dart` (+ generated `database.g.dart`). **16 tests.** + +Mirrors `app/schemas/com.rippr.data.AppDatabase/2.json`: three tables, CASCADE foreign +keys, indices on `tripId` / `segmentId` / `synced`, WAL with `synchronous = NORMAL`. +Starts at Dart schema version 1 with a real `MigrationStrategy` — the destructive +fallback never comes back. + +### Foreign keys are OFF by default in SQLite + +Room switched them on for us. **Drift does not.** Without `PRAGMA foreign_keys = ON` in +`beforeOpen`, every `CASCADE` in the schema is decorative and deleting a trip silently +orphans all of its points. There is now a test that reads the pragma back and asserts it +is `1`, because this is invisible until data is already wrong. + +### Two collisions worth recording + +**Drift generates row classes named after the table.** `Trips` → `Trip`, colliding with +the domain model of the same name and producing 21 confusing analyzer errors of the form +*"Trip can't be assigned to Trip"*. Fixed with `@DataClassName('TripRow')` etc. The +mapping functions `_toTrip` / `_toSegment` / `_toPoint` convert row → domain, so the +domain layer stays unaware Drift exists. + +**Drift snake_cases column names.** `speedKmh` became `speed_kmh`, which broke the one +raw-SQL query (the live stats aggregate) with `no such column: speedKmh`. Rather than 30 +`.named()` annotations, `build.yaml` sets `case_from_dart_to_sql: preserve`. That keeps +the schema column-for-column identical to Room's, lets the raw SQL stay byte-identical to +the Kotlin DAO query it was ported from, and leaves a Room-file importer possible later. + +### The instrumented-to-unit win, realised + +`SchemaTest` needed a device and an emulator. The Drift equivalent runs on the Dart VM in +well under a second with nothing booted. `TripRepositoryTest` and `MergeTest` (441 more +lines) should convert the same way in T09. + +**95 tests passing, analyze clean.** diff --git a/lib/src/data/database.dart b/lib/src/data/database.dart new file mode 100644 index 0000000..4c6b82d --- /dev/null +++ b/lib/src/data/database.dart @@ -0,0 +1,429 @@ +/// Drift schema, mirroring the Room schema at +/// `app/schemas/com.rippr.data.AppDatabase/2.json` in the native repo. +/// +/// Ported from `com.rippr.data.AppDatabase` plus the three DAOs. +/// +/// ## Two invariants carried over verbatim +/// +/// **No destructive migration, ever.** v2 removed `fallbackToDestructiveMigration()` +/// because rides are real data. This starts at Dart schema version 1 with a real +/// [MigrationStrategy] from day one; reinstating a destructive fallback would silently +/// delete every stored ride on the next version bump. +/// +/// **Foreign keys must be switched on explicitly.** SQLite defaults `foreign_keys` to +/// OFF. Room turned it on for us; Drift does not. Without the pragma in [beforeOpen] the +/// `CASCADE` deletes below are decorative, and deleting a trip would silently orphan +/// every one of its points. There is a test for exactly this. +library; + +import 'package:drift/drift.dart'; + +import '../domain/models.dart' as domain; + +part 'database.g.dart'; + +/// One ride, from pressing Start to pressing Stop. +/// +/// The aggregate columns are denormalised on purpose — accumulated as points arrive and +/// recomputed authoritatively on completion, so the trips list never touches the point +/// table. +@DataClassName('TripRow') +@TableIndex(name: 'idx_trips_ended', columns: {#endedAt}) +class Trips extends Table { + @override + String get tableName => 'trips'; + + IntColumn get id => integer().autoIncrement()(); + IntColumn get startedAt => integer()(); + + /// Null while the ride is still active. This column *is* the fact of an in-progress + /// ride, which is why recording state survives process death. + IntColumn get endedAt => integer().nullable()(); + + /// Null means the UI derives a label from [startedAt]. Never store an empty string. + TextColumn get name => text().nullable()(); + + TextColumn get state => textEnum()(); + + RealColumn get distanceM => real().withDefault(const Constant(0))(); + IntColumn get movingMillis => integer().withDefault(const Constant(0))(); + RealColumn get maxSpeedKmh => real().withDefault(const Constant(0))(); + RealColumn get elevationGainM => real().withDefault(const Constant(0))(); + IntColumn get pointCount => integer().withDefault(const Constant(0))(); +} + +/// One pause-free stretch of recording within a trip. +/// +/// This layer is what makes pause correct rather than cosmetic. +@DataClassName('SegmentRow') +@TableIndex(name: 'idx_segments_trip', columns: {#tripId}) +class Segments extends Table { + @override + String get tableName => 'segments'; + + IntColumn get id => integer().autoIncrement()(); + IntColumn get tripId => + integer().references(Trips, #id, onDelete: KeyAction.cascade)(); + IntColumn get startedAt => integer()(); + + /// Null while this segment is still being recorded into. + IntColumn get endedAt => integer().nullable()(); +} + +/// A single GPS fix. +@DataClassName('TrackPointRow') +@TableIndex(name: 'idx_points_trip', columns: {#tripId}) +@TableIndex(name: 'idx_points_segment', columns: {#segmentId}) +@TableIndex(name: 'idx_points_synced', columns: {#synced}) +class TrackPoints extends Table { + @override + String get tableName => 'track_points'; + + IntColumn get id => integer().autoIncrement()(); + IntColumn get tripId => + integer().references(Trips, #id, onDelete: KeyAction.cascade)(); + IntColumn get segmentId => + integer().references(Segments, #id, onDelete: KeyAction.cascade)(); + IntColumn get timestamp => integer()(); + RealColumn get latitude => real()(); + RealColumn get longitude => real()(); + RealColumn get speedKmh => real()(); + RealColumn get altitudeM => real()(); + RealColumn get accuracyM => real().withDefault(const Constant(0))(); + RealColumn get bearingDeg => real().withDefault(const Constant(0))(); + + /// Set once the point has been accepted by the remote endpoint. + BoolColumn get synced => boolean().withDefault(const Constant(false))(); +} + +@DriftDatabase(tables: [Trips, Segments, TrackPoints]) +class AppDatabase extends _$AppDatabase { + AppDatabase(super.e); + + @override + int get schemaVersion => 1; + + @override + MigrationStrategy get migration => MigrationStrategy( + onCreate: (m) => m.createAll(), + beforeOpen: (details) async { + // Non-negotiable: without this the CASCADE relationships above do nothing. + await customStatement('PRAGMA foreign_keys = ON'); + // 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. + await customStatement('PRAGMA journal_mode = WAL'); + await customStatement('PRAGMA synchronous = NORMAL'); + }, + ); + + // --- Trips --------------------------------------------------------------- + + /// The in-progress ride, or null. This is the source of truth for "are we recording" — + /// it survives process death, which an in-memory flag cannot. + Stream watchActiveTrip() => (select(trips) + ..where((t) => t.endedAt.isNull()) + ..orderBy([(t) => OrderingTerm.desc(t.id)]) + ..limit(1)) + .watchSingleOrNull() + .map((r) => r == null ? null : _toTrip(r)); + + Future getActiveTrip() async { + final row = await (select(trips) + ..where((t) => t.endedAt.isNull()) + ..orderBy([(t) => OrderingTerm.desc(t.id)]) + ..limit(1)) + .getSingleOrNull(); + return row == null ? null : _toTrip(row); + } + + Stream> watchCompletedTrips() => (select(trips) + ..where((t) => t.endedAt.isNotNull()) + ..orderBy([(t) => OrderingTerm.desc(t.startedAt)])) + .watch() + .map((rows) => rows.map(_toTrip).toList()); + + Stream watchTrip(int id) => + (select(trips)..where((t) => t.id.equals(id))) + .watchSingleOrNull() + .map((r) => r == null ? null : _toTrip(r)); + + Future getTrip(int id) async { + final row = + await (select(trips)..where((t) => t.id.equals(id))).getSingleOrNull(); + return row == null ? null : _toTrip(row); + } + + Future insertTrip(domain.Trip trip) => into(trips).insert( + TripsCompanion.insert( + startedAt: trip.startedAt, + endedAt: Value(trip.endedAt), + name: Value(trip.name), + state: trip.state, + distanceM: Value(trip.distanceM), + movingMillis: Value(trip.movingMillis), + maxSpeedKmh: Value(trip.maxSpeedKmh), + elevationGainM: Value(trip.elevationGainM), + pointCount: Value(trip.pointCount), + ), + ); + + Future renameTrip(int id, String? name) => + (update(trips)..where((t) => t.id.equals(id))) + .write(TripsCompanion(name: Value(name))); + + Future setTripState(int id, domain.TripState state) => + (update(trips)..where((t) => t.id.equals(id))) + .write(TripsCompanion(state: Value(state))); + + Future closeTrip(int id, int endedAt, + {domain.TripState state = domain.TripState.completed}) => + (update(trips)..where((t) => t.id.equals(id))).write( + TripsCompanion(endedAt: Value(endedAt), state: Value(state))); + + /// Persists the running totals. Called once per writer flush (~every 2 s), so it stays + /// a narrow targeted update rather than a full row rewrite. + Future updateAggregates({ + required int id, + required double distanceM, + required int movingMillis, + required double maxSpeedKmh, + required double elevationGainM, + required int pointCount, + }) => + (update(trips)..where((t) => t.id.equals(id))).write(TripsCompanion( + distanceM: Value(distanceM), + movingMillis: Value(movingMillis), + maxSpeedKmh: Value(maxSpeedKmh), + elevationGainM: Value(elevationGainM), + pointCount: Value(pointCount), + )); + + /// Segments and points go with it via CASCADE. + Future deleteTrip(int id) => + (delete(trips)..where((t) => t.id.equals(id))).go(); + + Future countTrips() async => + (await select(trips).get()).length; + + /// Test/maintenance helper. Segments and points follow via CASCADE. + Future deleteAllTrips() => delete(trips).go(); + + // --- Segments ------------------------------------------------------------ + + Future insertSegment(int tripId, int startedAt) => + into(segments).insert( + SegmentsCompanion.insert(tripId: tripId, startedAt: startedAt), + ); + + Future> segmentsForTrip(int tripId) async { + final rows = await (select(segments) + ..where((s) => s.tripId.equals(tripId)) + ..orderBy([(s) => OrderingTerm.asc(s.id)])) + .get(); + return rows.map(_toSegment).toList(); + } + + /// The segment currently being recorded into, if any. + Future openSegment(int tripId) async { + final row = await (select(segments) + ..where((s) => s.tripId.equals(tripId) & s.endedAt.isNull()) + ..orderBy([(s) => OrderingTerm.desc(s.id)]) + ..limit(1)) + .getSingleOrNull(); + return row == null ? null : _toSegment(row); + } + + Future closeSegment(int id, int endedAt) => + (update(segments)..where((s) => s.id.equals(id))) + .write(SegmentsCompanion(endedAt: Value(endedAt))); + + /// Used by merge: re-parents a trip's segments onto the surviving trip. + Future reparentSegments(int oldTripId, int newTripId) => + (update(segments)..where((s) => s.tripId.equals(oldTripId))) + .write(SegmentsCompanion(tripId: Value(newTripId))); + + Future countSegmentsForTrip(int tripId) async => + (await (select(segments)..where((s) => s.tripId.equals(tripId))).get()) + .length; + + // --- Track points -------------------------------------------------------- + + /// Batched insert — the recorder buffers points and flushes them in groups. + Future insertPoints(List points) async { + if (points.isEmpty) return; + await batch((b) { + b.insertAll( + trackPoints, + points.map((p) => TrackPointsCompanion.insert( + tripId: p.tripId, + segmentId: p.segmentId, + timestamp: p.timestamp, + latitude: p.latitude, + longitude: p.longitude, + speedKmh: p.speedKmh, + altitudeM: p.altitudeM, + accuracyM: Value(p.accuracyM), + bearingDeg: Value(p.bearingDeg), + synced: Value(p.synced), + )), + ); + }); + } + + Future insertPoint(domain.TrackPoint p) => + into(trackPoints).insert(TrackPointsCompanion.insert( + tripId: p.tripId, + segmentId: p.segmentId, + timestamp: p.timestamp, + latitude: p.latitude, + longitude: p.longitude, + speedKmh: p.speedKmh, + altitudeM: p.altitudeM, + accuracyM: Value(p.accuracyM), + bearingDeg: Value(p.bearingDeg), + synced: Value(p.synced), + )); + + /// Ordered by segment then id so consumers walk the ride in recording order with pause + /// boundaries intact. **Not** ordered by timestamp: that value is GPS-derived and can + /// jump, whereas id is monotonic in write order. + Future> pointsForTrip(int tripId) async { + final rows = await (select(trackPoints) + ..where((p) => p.tripId.equals(tripId)) + ..orderBy([ + (p) => OrderingTerm.asc(p.segmentId), + (p) => OrderingTerm.asc(p.id), + ])) + .get(); + return rows.map(_toPoint).toList(); + } + + Future> pointsForSegment(int segmentId) async { + final rows = await (select(trackPoints) + ..where((p) => p.segmentId.equals(segmentId)) + ..orderBy([(p) => OrderingTerm.asc(p.id)])) + .get(); + return rows.map(_toPoint).toList(); + } + + /// The anchor a restarted recorder needs to continue accumulating distance. + Future lastInSegment(int segmentId) async { + final row = await (select(trackPoints) + ..where((p) => p.segmentId.equals(segmentId)) + ..orderBy([(p) => OrderingTerm.desc(p.id)]) + ..limit(1)) + .getSingleOrNull(); + return row == null ? null : _toPoint(row); + } + + Future countPointsForTrip(int tripId) async => + (await (select(trackPoints)..where((p) => p.tripId.equals(tripId))).get()) + .length; + + /// Live stats for one trip. + /// + /// The COALESCE guards in the Kotlin original existed because Room throws on null for + /// non-null fields; they are kept because the semantics are what matter — a trip with + /// no points yet must read as zeros, not as an error. + Stream watchTripStats(int tripId) { + final q = customSelect( + ''' + SELECT COUNT(*) AS pointCount, + COALESCE(MAX(speedKmh), 0.0) AS maxSpeedKmh, + COALESCE(AVG(speedKmh), 0.0) AS avgSpeedKmh, + COALESCE(MIN(timestamp), 0) AS firstTimestamp, + COALESCE(MAX(timestamp), 0) AS lastTimestamp, + COALESCE(SUM(CASE WHEN synced = 0 THEN 1 ELSE 0 END), 0) AS pendingUpload + FROM track_points + WHERE tripId = ? + ''', + variables: [Variable.withInt(tripId)], + readsFrom: {trackPoints}, + ); + return q.watchSingle().map((row) => domain.RideStats( + pointCount: row.read('pointCount'), + maxSpeedKmh: row.read('maxSpeedKmh'), + avgSpeedKmh: row.read('avgSpeedKmh'), + firstTimestamp: row.read('firstTimestamp'), + lastTimestamp: row.read('lastTimestamp'), + pendingUpload: row.read('pendingUpload'), + )); + } + + // --- Upload backlog ------------------------------------------------------ + // Batches are drawn by id and can straddle a segment or trip boundary, which is why + // the payload carries trip/segment identity per point rather than per batch. + + Future> unsyncedPoints(int limit) async { + final rows = await (select(trackPoints) + ..where((p) => p.synced.equals(false)) + ..orderBy([(p) => OrderingTerm.asc(p.id)]) + ..limit(limit)) + .get(); + return rows.map(_toPoint).toList(); + } + + Future markSynced(List ids) async { + if (ids.isEmpty) return; + await (update(trackPoints)..where((p) => p.id.isIn(ids))) + .write(const TrackPointsCompanion(synced: Value(true))); + } + + Future countUnsynced() async => + (await (select(trackPoints)..where((p) => p.synced.equals(false))).get()) + .length; + + // --- Maintenance --------------------------------------------------------- + + /// Used by merge: points carry tripId directly, so they re-parent alongside segments. + Future reparentPoints(int oldTripId, int newTripId) => + (update(trackPoints)..where((p) => p.tripId.equals(oldTripId))) + .write(TrackPointsCompanion(tripId: Value(newTripId))); + + Future> allPoints() async { + final rows = await (select(trackPoints) + ..orderBy([(p) => OrderingTerm.asc(p.id)])) + .get(); + return rows.map(_toPoint).toList(); + } +} + +// --- Row → domain mapping --------------------------------------------------- +// Kept as free functions rather than extension getters so the domain layer stays +// entirely unaware that Drift exists. + +domain.Trip _toTrip(TripRow r) => domain.Trip( + id: r.id, + startedAt: r.startedAt, + endedAt: r.endedAt, + name: r.name, + state: r.state, + distanceM: r.distanceM, + movingMillis: r.movingMillis, + maxSpeedKmh: r.maxSpeedKmh, + elevationGainM: r.elevationGainM, + pointCount: r.pointCount, + ); + +domain.Segment _toSegment(SegmentRow r) => domain.Segment( + id: r.id, + tripId: r.tripId, + startedAt: r.startedAt, + endedAt: r.endedAt, + ); + +domain.TrackPoint _toPoint(TrackPointRow r) => domain.TrackPoint( + id: r.id, + tripId: r.tripId, + segmentId: r.segmentId, + timestamp: r.timestamp, + latitude: r.latitude, + longitude: r.longitude, + speedKmh: r.speedKmh, + altitudeM: r.altitudeM, + accuracyM: r.accuracyM, + bearingDeg: r.bearingDeg, + synced: r.synced, + ); diff --git a/lib/src/data/database.g.dart b/lib/src/data/database.g.dart new file mode 100644 index 0000000..c7e8ee7 --- /dev/null +++ b/lib/src/data/database.g.dart @@ -0,0 +1,3019 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'database.dart'; + +// ignore_for_file: type=lint +class $TripsTable extends Trips with TableInfo<$TripsTable, TripRow> { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + $TripsTable(this.attachedDatabase, [this._alias]); + static const VerificationMeta _idMeta = const VerificationMeta('id'); + @override + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'PRIMARY KEY AUTOINCREMENT', + ), + ); + static const VerificationMeta _startedAtMeta = const VerificationMeta( + 'startedAt', + ); + @override + late final GeneratedColumn startedAt = GeneratedColumn( + 'startedAt', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + static const VerificationMeta _endedAtMeta = const VerificationMeta( + 'endedAt', + ); + @override + late final GeneratedColumn endedAt = GeneratedColumn( + 'endedAt', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + static const VerificationMeta _nameMeta = const VerificationMeta('name'); + @override + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + late final GeneratedColumnWithTypeConverter state = + GeneratedColumn( + 'state', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ).withConverter($TripsTable.$converterstate); + static const VerificationMeta _distanceMMeta = const VerificationMeta( + 'distanceM', + ); + @override + late final GeneratedColumn distanceM = GeneratedColumn( + 'distanceM', + aliasedName, + false, + type: DriftSqlType.double, + requiredDuringInsert: false, + defaultValue: const Constant(0), + ); + static const VerificationMeta _movingMillisMeta = const VerificationMeta( + 'movingMillis', + ); + @override + late final GeneratedColumn movingMillis = GeneratedColumn( + 'movingMillis', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const Constant(0), + ); + static const VerificationMeta _maxSpeedKmhMeta = const VerificationMeta( + 'maxSpeedKmh', + ); + @override + late final GeneratedColumn maxSpeedKmh = GeneratedColumn( + 'maxSpeedKmh', + aliasedName, + false, + type: DriftSqlType.double, + requiredDuringInsert: false, + defaultValue: const Constant(0), + ); + static const VerificationMeta _elevationGainMMeta = const VerificationMeta( + 'elevationGainM', + ); + @override + late final GeneratedColumn elevationGainM = GeneratedColumn( + 'elevationGainM', + aliasedName, + false, + type: DriftSqlType.double, + requiredDuringInsert: false, + defaultValue: const Constant(0), + ); + static const VerificationMeta _pointCountMeta = const VerificationMeta( + 'pointCount', + ); + @override + late final GeneratedColumn pointCount = GeneratedColumn( + 'pointCount', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const Constant(0), + ); + @override + List get $columns => [ + id, + startedAt, + endedAt, + name, + state, + distanceM, + movingMillis, + maxSpeedKmh, + elevationGainM, + pointCount, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'trips'; + @override + VerificationContext validateIntegrity( + Insertable instance, { + bool isInserting = false, + }) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('id')) { + context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); + } + if (data.containsKey('startedAt')) { + context.handle( + _startedAtMeta, + startedAt.isAcceptableOrUnknown(data['startedAt']!, _startedAtMeta), + ); + } else if (isInserting) { + context.missing(_startedAtMeta); + } + if (data.containsKey('endedAt')) { + context.handle( + _endedAtMeta, + endedAt.isAcceptableOrUnknown(data['endedAt']!, _endedAtMeta), + ); + } + if (data.containsKey('name')) { + context.handle( + _nameMeta, + name.isAcceptableOrUnknown(data['name']!, _nameMeta), + ); + } + if (data.containsKey('distanceM')) { + context.handle( + _distanceMMeta, + distanceM.isAcceptableOrUnknown(data['distanceM']!, _distanceMMeta), + ); + } + if (data.containsKey('movingMillis')) { + context.handle( + _movingMillisMeta, + movingMillis.isAcceptableOrUnknown( + data['movingMillis']!, + _movingMillisMeta, + ), + ); + } + if (data.containsKey('maxSpeedKmh')) { + context.handle( + _maxSpeedKmhMeta, + maxSpeedKmh.isAcceptableOrUnknown( + data['maxSpeedKmh']!, + _maxSpeedKmhMeta, + ), + ); + } + if (data.containsKey('elevationGainM')) { + context.handle( + _elevationGainMMeta, + elevationGainM.isAcceptableOrUnknown( + data['elevationGainM']!, + _elevationGainMMeta, + ), + ); + } + if (data.containsKey('pointCount')) { + context.handle( + _pointCountMeta, + pointCount.isAcceptableOrUnknown(data['pointCount']!, _pointCountMeta), + ); + } + return context; + } + + @override + Set get $primaryKey => {id}; + @override + TripRow map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return TripRow( + id: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}id'], + )!, + startedAt: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}startedAt'], + )!, + endedAt: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}endedAt'], + ), + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + ), + state: $TripsTable.$converterstate.fromSql( + attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}state'], + )!, + ), + distanceM: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}distanceM'], + )!, + movingMillis: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}movingMillis'], + )!, + maxSpeedKmh: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}maxSpeedKmh'], + )!, + elevationGainM: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}elevationGainM'], + )!, + pointCount: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}pointCount'], + )!, + ); + } + + @override + $TripsTable createAlias(String alias) { + return $TripsTable(attachedDatabase, alias); + } + + static JsonTypeConverter2 $converterstate = + const EnumNameConverter(domain.TripState.values); +} + +class TripRow extends DataClass implements Insertable { + final int id; + final int startedAt; + + /// Null while the ride is still active. This column *is* the fact of an in-progress + /// ride, which is why recording state survives process death. + final int? endedAt; + + /// Null means the UI derives a label from [startedAt]. Never store an empty string. + final String? name; + final domain.TripState state; + final double distanceM; + final int movingMillis; + final double maxSpeedKmh; + final double elevationGainM; + final int pointCount; + const TripRow({ + required this.id, + required this.startedAt, + this.endedAt, + this.name, + required this.state, + required this.distanceM, + required this.movingMillis, + required this.maxSpeedKmh, + required this.elevationGainM, + required this.pointCount, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['startedAt'] = Variable(startedAt); + if (!nullToAbsent || endedAt != null) { + map['endedAt'] = Variable(endedAt); + } + if (!nullToAbsent || name != null) { + map['name'] = Variable(name); + } + { + map['state'] = Variable($TripsTable.$converterstate.toSql(state)); + } + map['distanceM'] = Variable(distanceM); + map['movingMillis'] = Variable(movingMillis); + map['maxSpeedKmh'] = Variable(maxSpeedKmh); + map['elevationGainM'] = Variable(elevationGainM); + map['pointCount'] = Variable(pointCount); + return map; + } + + TripsCompanion toCompanion(bool nullToAbsent) { + return TripsCompanion( + id: Value(id), + startedAt: Value(startedAt), + endedAt: endedAt == null && nullToAbsent + ? const Value.absent() + : Value(endedAt), + name: name == null && nullToAbsent ? const Value.absent() : Value(name), + state: Value(state), + distanceM: Value(distanceM), + movingMillis: Value(movingMillis), + maxSpeedKmh: Value(maxSpeedKmh), + elevationGainM: Value(elevationGainM), + pointCount: Value(pointCount), + ); + } + + factory TripRow.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return TripRow( + id: serializer.fromJson(json['id']), + startedAt: serializer.fromJson(json['startedAt']), + endedAt: serializer.fromJson(json['endedAt']), + name: serializer.fromJson(json['name']), + state: $TripsTable.$converterstate.fromJson( + serializer.fromJson(json['state']), + ), + distanceM: serializer.fromJson(json['distanceM']), + movingMillis: serializer.fromJson(json['movingMillis']), + maxSpeedKmh: serializer.fromJson(json['maxSpeedKmh']), + elevationGainM: serializer.fromJson(json['elevationGainM']), + pointCount: serializer.fromJson(json['pointCount']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'startedAt': serializer.toJson(startedAt), + 'endedAt': serializer.toJson(endedAt), + 'name': serializer.toJson(name), + 'state': serializer.toJson( + $TripsTable.$converterstate.toJson(state), + ), + 'distanceM': serializer.toJson(distanceM), + 'movingMillis': serializer.toJson(movingMillis), + 'maxSpeedKmh': serializer.toJson(maxSpeedKmh), + 'elevationGainM': serializer.toJson(elevationGainM), + 'pointCount': serializer.toJson(pointCount), + }; + } + + TripRow copyWith({ + int? id, + int? startedAt, + Value endedAt = const Value.absent(), + Value name = const Value.absent(), + domain.TripState? state, + double? distanceM, + int? movingMillis, + double? maxSpeedKmh, + double? elevationGainM, + int? pointCount, + }) => TripRow( + id: id ?? this.id, + startedAt: startedAt ?? this.startedAt, + endedAt: endedAt.present ? endedAt.value : this.endedAt, + name: name.present ? name.value : this.name, + state: state ?? this.state, + distanceM: distanceM ?? this.distanceM, + movingMillis: movingMillis ?? this.movingMillis, + maxSpeedKmh: maxSpeedKmh ?? this.maxSpeedKmh, + elevationGainM: elevationGainM ?? this.elevationGainM, + pointCount: pointCount ?? this.pointCount, + ); + TripRow copyWithCompanion(TripsCompanion data) { + return TripRow( + id: data.id.present ? data.id.value : this.id, + startedAt: data.startedAt.present ? data.startedAt.value : this.startedAt, + endedAt: data.endedAt.present ? data.endedAt.value : this.endedAt, + name: data.name.present ? data.name.value : this.name, + state: data.state.present ? data.state.value : this.state, + distanceM: data.distanceM.present ? data.distanceM.value : this.distanceM, + movingMillis: data.movingMillis.present + ? data.movingMillis.value + : this.movingMillis, + maxSpeedKmh: data.maxSpeedKmh.present + ? data.maxSpeedKmh.value + : this.maxSpeedKmh, + elevationGainM: data.elevationGainM.present + ? data.elevationGainM.value + : this.elevationGainM, + pointCount: data.pointCount.present + ? data.pointCount.value + : this.pointCount, + ); + } + + @override + String toString() { + return (StringBuffer('TripRow(') + ..write('id: $id, ') + ..write('startedAt: $startedAt, ') + ..write('endedAt: $endedAt, ') + ..write('name: $name, ') + ..write('state: $state, ') + ..write('distanceM: $distanceM, ') + ..write('movingMillis: $movingMillis, ') + ..write('maxSpeedKmh: $maxSpeedKmh, ') + ..write('elevationGainM: $elevationGainM, ') + ..write('pointCount: $pointCount') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + startedAt, + endedAt, + name, + state, + distanceM, + movingMillis, + maxSpeedKmh, + elevationGainM, + pointCount, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is TripRow && + other.id == this.id && + other.startedAt == this.startedAt && + other.endedAt == this.endedAt && + other.name == this.name && + other.state == this.state && + other.distanceM == this.distanceM && + other.movingMillis == this.movingMillis && + other.maxSpeedKmh == this.maxSpeedKmh && + other.elevationGainM == this.elevationGainM && + other.pointCount == this.pointCount); +} + +class TripsCompanion extends UpdateCompanion { + final Value id; + final Value startedAt; + final Value endedAt; + final Value name; + final Value state; + final Value distanceM; + final Value movingMillis; + final Value maxSpeedKmh; + final Value elevationGainM; + final Value pointCount; + const TripsCompanion({ + this.id = const Value.absent(), + this.startedAt = const Value.absent(), + this.endedAt = const Value.absent(), + this.name = const Value.absent(), + this.state = const Value.absent(), + this.distanceM = const Value.absent(), + this.movingMillis = const Value.absent(), + this.maxSpeedKmh = const Value.absent(), + this.elevationGainM = const Value.absent(), + this.pointCount = const Value.absent(), + }); + TripsCompanion.insert({ + this.id = const Value.absent(), + required int startedAt, + this.endedAt = const Value.absent(), + this.name = const Value.absent(), + required domain.TripState state, + this.distanceM = const Value.absent(), + this.movingMillis = const Value.absent(), + this.maxSpeedKmh = const Value.absent(), + this.elevationGainM = const Value.absent(), + this.pointCount = const Value.absent(), + }) : startedAt = Value(startedAt), + state = Value(state); + static Insertable custom({ + Expression? id, + Expression? startedAt, + Expression? endedAt, + Expression? name, + Expression? state, + Expression? distanceM, + Expression? movingMillis, + Expression? maxSpeedKmh, + Expression? elevationGainM, + Expression? pointCount, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (startedAt != null) 'startedAt': startedAt, + if (endedAt != null) 'endedAt': endedAt, + if (name != null) 'name': name, + if (state != null) 'state': state, + if (distanceM != null) 'distanceM': distanceM, + if (movingMillis != null) 'movingMillis': movingMillis, + if (maxSpeedKmh != null) 'maxSpeedKmh': maxSpeedKmh, + if (elevationGainM != null) 'elevationGainM': elevationGainM, + if (pointCount != null) 'pointCount': pointCount, + }); + } + + TripsCompanion copyWith({ + Value? id, + Value? startedAt, + Value? endedAt, + Value? name, + Value? state, + Value? distanceM, + Value? movingMillis, + Value? maxSpeedKmh, + Value? elevationGainM, + Value? pointCount, + }) { + return TripsCompanion( + id: id ?? this.id, + startedAt: startedAt ?? this.startedAt, + endedAt: endedAt ?? this.endedAt, + name: name ?? this.name, + state: state ?? this.state, + distanceM: distanceM ?? this.distanceM, + movingMillis: movingMillis ?? this.movingMillis, + maxSpeedKmh: maxSpeedKmh ?? this.maxSpeedKmh, + elevationGainM: elevationGainM ?? this.elevationGainM, + pointCount: pointCount ?? this.pointCount, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (startedAt.present) { + map['startedAt'] = Variable(startedAt.value); + } + if (endedAt.present) { + map['endedAt'] = Variable(endedAt.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (state.present) { + map['state'] = Variable( + $TripsTable.$converterstate.toSql(state.value), + ); + } + if (distanceM.present) { + map['distanceM'] = Variable(distanceM.value); + } + if (movingMillis.present) { + map['movingMillis'] = Variable(movingMillis.value); + } + if (maxSpeedKmh.present) { + map['maxSpeedKmh'] = Variable(maxSpeedKmh.value); + } + if (elevationGainM.present) { + map['elevationGainM'] = Variable(elevationGainM.value); + } + if (pointCount.present) { + map['pointCount'] = Variable(pointCount.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('TripsCompanion(') + ..write('id: $id, ') + ..write('startedAt: $startedAt, ') + ..write('endedAt: $endedAt, ') + ..write('name: $name, ') + ..write('state: $state, ') + ..write('distanceM: $distanceM, ') + ..write('movingMillis: $movingMillis, ') + ..write('maxSpeedKmh: $maxSpeedKmh, ') + ..write('elevationGainM: $elevationGainM, ') + ..write('pointCount: $pointCount') + ..write(')')) + .toString(); + } +} + +class $SegmentsTable extends Segments + with TableInfo<$SegmentsTable, SegmentRow> { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + $SegmentsTable(this.attachedDatabase, [this._alias]); + static const VerificationMeta _idMeta = const VerificationMeta('id'); + @override + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'PRIMARY KEY AUTOINCREMENT', + ), + ); + static const VerificationMeta _tripIdMeta = const VerificationMeta('tripId'); + @override + late final GeneratedColumn tripId = GeneratedColumn( + 'tripId', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES trips (id) ON DELETE CASCADE', + ), + ); + static const VerificationMeta _startedAtMeta = const VerificationMeta( + 'startedAt', + ); + @override + late final GeneratedColumn startedAt = GeneratedColumn( + 'startedAt', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + static const VerificationMeta _endedAtMeta = const VerificationMeta( + 'endedAt', + ); + @override + late final GeneratedColumn endedAt = GeneratedColumn( + 'endedAt', + aliasedName, + true, + type: DriftSqlType.int, + requiredDuringInsert: false, + ); + @override + List get $columns => [id, tripId, startedAt, endedAt]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'segments'; + @override + VerificationContext validateIntegrity( + Insertable instance, { + bool isInserting = false, + }) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('id')) { + context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); + } + if (data.containsKey('tripId')) { + context.handle( + _tripIdMeta, + tripId.isAcceptableOrUnknown(data['tripId']!, _tripIdMeta), + ); + } else if (isInserting) { + context.missing(_tripIdMeta); + } + if (data.containsKey('startedAt')) { + context.handle( + _startedAtMeta, + startedAt.isAcceptableOrUnknown(data['startedAt']!, _startedAtMeta), + ); + } else if (isInserting) { + context.missing(_startedAtMeta); + } + if (data.containsKey('endedAt')) { + context.handle( + _endedAtMeta, + endedAt.isAcceptableOrUnknown(data['endedAt']!, _endedAtMeta), + ); + } + return context; + } + + @override + Set get $primaryKey => {id}; + @override + SegmentRow map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return SegmentRow( + id: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}id'], + )!, + tripId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}tripId'], + )!, + startedAt: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}startedAt'], + )!, + endedAt: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}endedAt'], + ), + ); + } + + @override + $SegmentsTable createAlias(String alias) { + return $SegmentsTable(attachedDatabase, alias); + } +} + +class SegmentRow extends DataClass implements Insertable { + final int id; + final int tripId; + final int startedAt; + + /// Null while this segment is still being recorded into. + final int? endedAt; + const SegmentRow({ + required this.id, + required this.tripId, + required this.startedAt, + this.endedAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['tripId'] = Variable(tripId); + map['startedAt'] = Variable(startedAt); + if (!nullToAbsent || endedAt != null) { + map['endedAt'] = Variable(endedAt); + } + return map; + } + + SegmentsCompanion toCompanion(bool nullToAbsent) { + return SegmentsCompanion( + id: Value(id), + tripId: Value(tripId), + startedAt: Value(startedAt), + endedAt: endedAt == null && nullToAbsent + ? const Value.absent() + : Value(endedAt), + ); + } + + factory SegmentRow.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return SegmentRow( + id: serializer.fromJson(json['id']), + tripId: serializer.fromJson(json['tripId']), + startedAt: serializer.fromJson(json['startedAt']), + endedAt: serializer.fromJson(json['endedAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'tripId': serializer.toJson(tripId), + 'startedAt': serializer.toJson(startedAt), + 'endedAt': serializer.toJson(endedAt), + }; + } + + SegmentRow copyWith({ + int? id, + int? tripId, + int? startedAt, + Value endedAt = const Value.absent(), + }) => SegmentRow( + id: id ?? this.id, + tripId: tripId ?? this.tripId, + startedAt: startedAt ?? this.startedAt, + endedAt: endedAt.present ? endedAt.value : this.endedAt, + ); + SegmentRow copyWithCompanion(SegmentsCompanion data) { + return SegmentRow( + id: data.id.present ? data.id.value : this.id, + tripId: data.tripId.present ? data.tripId.value : this.tripId, + startedAt: data.startedAt.present ? data.startedAt.value : this.startedAt, + endedAt: data.endedAt.present ? data.endedAt.value : this.endedAt, + ); + } + + @override + String toString() { + return (StringBuffer('SegmentRow(') + ..write('id: $id, ') + ..write('tripId: $tripId, ') + ..write('startedAt: $startedAt, ') + ..write('endedAt: $endedAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, tripId, startedAt, endedAt); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is SegmentRow && + other.id == this.id && + other.tripId == this.tripId && + other.startedAt == this.startedAt && + other.endedAt == this.endedAt); +} + +class SegmentsCompanion extends UpdateCompanion { + final Value id; + final Value tripId; + final Value startedAt; + final Value endedAt; + const SegmentsCompanion({ + this.id = const Value.absent(), + this.tripId = const Value.absent(), + this.startedAt = const Value.absent(), + this.endedAt = const Value.absent(), + }); + SegmentsCompanion.insert({ + this.id = const Value.absent(), + required int tripId, + required int startedAt, + this.endedAt = const Value.absent(), + }) : tripId = Value(tripId), + startedAt = Value(startedAt); + static Insertable custom({ + Expression? id, + Expression? tripId, + Expression? startedAt, + Expression? endedAt, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (tripId != null) 'tripId': tripId, + if (startedAt != null) 'startedAt': startedAt, + if (endedAt != null) 'endedAt': endedAt, + }); + } + + SegmentsCompanion copyWith({ + Value? id, + Value? tripId, + Value? startedAt, + Value? endedAt, + }) { + return SegmentsCompanion( + id: id ?? this.id, + tripId: tripId ?? this.tripId, + startedAt: startedAt ?? this.startedAt, + endedAt: endedAt ?? this.endedAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (tripId.present) { + map['tripId'] = Variable(tripId.value); + } + if (startedAt.present) { + map['startedAt'] = Variable(startedAt.value); + } + if (endedAt.present) { + map['endedAt'] = Variable(endedAt.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('SegmentsCompanion(') + ..write('id: $id, ') + ..write('tripId: $tripId, ') + ..write('startedAt: $startedAt, ') + ..write('endedAt: $endedAt') + ..write(')')) + .toString(); + } +} + +class $TrackPointsTable extends TrackPoints + with TableInfo<$TrackPointsTable, TrackPointRow> { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + $TrackPointsTable(this.attachedDatabase, [this._alias]); + static const VerificationMeta _idMeta = const VerificationMeta('id'); + @override + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + hasAutoIncrement: true, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'PRIMARY KEY AUTOINCREMENT', + ), + ); + static const VerificationMeta _tripIdMeta = const VerificationMeta('tripId'); + @override + late final GeneratedColumn tripId = GeneratedColumn( + 'tripId', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES trips (id) ON DELETE CASCADE', + ), + ); + static const VerificationMeta _segmentIdMeta = const VerificationMeta( + 'segmentId', + ); + @override + late final GeneratedColumn segmentId = GeneratedColumn( + 'segmentId', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES segments (id) ON DELETE CASCADE', + ), + ); + static const VerificationMeta _timestampMeta = const VerificationMeta( + 'timestamp', + ); + @override + late final GeneratedColumn timestamp = GeneratedColumn( + 'timestamp', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + static const VerificationMeta _latitudeMeta = const VerificationMeta( + 'latitude', + ); + @override + late final GeneratedColumn latitude = GeneratedColumn( + 'latitude', + aliasedName, + false, + type: DriftSqlType.double, + requiredDuringInsert: true, + ); + static const VerificationMeta _longitudeMeta = const VerificationMeta( + 'longitude', + ); + @override + late final GeneratedColumn longitude = GeneratedColumn( + 'longitude', + aliasedName, + false, + type: DriftSqlType.double, + requiredDuringInsert: true, + ); + static const VerificationMeta _speedKmhMeta = const VerificationMeta( + 'speedKmh', + ); + @override + late final GeneratedColumn speedKmh = GeneratedColumn( + 'speedKmh', + aliasedName, + false, + type: DriftSqlType.double, + requiredDuringInsert: true, + ); + static const VerificationMeta _altitudeMMeta = const VerificationMeta( + 'altitudeM', + ); + @override + late final GeneratedColumn altitudeM = GeneratedColumn( + 'altitudeM', + aliasedName, + false, + type: DriftSqlType.double, + requiredDuringInsert: true, + ); + static const VerificationMeta _accuracyMMeta = const VerificationMeta( + 'accuracyM', + ); + @override + late final GeneratedColumn accuracyM = GeneratedColumn( + 'accuracyM', + aliasedName, + false, + type: DriftSqlType.double, + requiredDuringInsert: false, + defaultValue: const Constant(0), + ); + static const VerificationMeta _bearingDegMeta = const VerificationMeta( + 'bearingDeg', + ); + @override + late final GeneratedColumn bearingDeg = GeneratedColumn( + 'bearingDeg', + aliasedName, + false, + type: DriftSqlType.double, + requiredDuringInsert: false, + defaultValue: const Constant(0), + ); + static const VerificationMeta _syncedMeta = const VerificationMeta('synced'); + @override + late final GeneratedColumn synced = GeneratedColumn( + 'synced', + aliasedName, + false, + type: DriftSqlType.bool, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'CHECK ("synced" IN (0, 1))', + ), + defaultValue: const Constant(false), + ); + @override + List get $columns => [ + id, + tripId, + segmentId, + timestamp, + latitude, + longitude, + speedKmh, + altitudeM, + accuracyM, + bearingDeg, + synced, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'track_points'; + @override + VerificationContext validateIntegrity( + Insertable instance, { + bool isInserting = false, + }) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('id')) { + context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); + } + if (data.containsKey('tripId')) { + context.handle( + _tripIdMeta, + tripId.isAcceptableOrUnknown(data['tripId']!, _tripIdMeta), + ); + } else if (isInserting) { + context.missing(_tripIdMeta); + } + if (data.containsKey('segmentId')) { + context.handle( + _segmentIdMeta, + segmentId.isAcceptableOrUnknown(data['segmentId']!, _segmentIdMeta), + ); + } else if (isInserting) { + context.missing(_segmentIdMeta); + } + if (data.containsKey('timestamp')) { + context.handle( + _timestampMeta, + timestamp.isAcceptableOrUnknown(data['timestamp']!, _timestampMeta), + ); + } else if (isInserting) { + context.missing(_timestampMeta); + } + if (data.containsKey('latitude')) { + context.handle( + _latitudeMeta, + latitude.isAcceptableOrUnknown(data['latitude']!, _latitudeMeta), + ); + } else if (isInserting) { + context.missing(_latitudeMeta); + } + if (data.containsKey('longitude')) { + context.handle( + _longitudeMeta, + longitude.isAcceptableOrUnknown(data['longitude']!, _longitudeMeta), + ); + } else if (isInserting) { + context.missing(_longitudeMeta); + } + if (data.containsKey('speedKmh')) { + context.handle( + _speedKmhMeta, + speedKmh.isAcceptableOrUnknown(data['speedKmh']!, _speedKmhMeta), + ); + } else if (isInserting) { + context.missing(_speedKmhMeta); + } + if (data.containsKey('altitudeM')) { + context.handle( + _altitudeMMeta, + altitudeM.isAcceptableOrUnknown(data['altitudeM']!, _altitudeMMeta), + ); + } else if (isInserting) { + context.missing(_altitudeMMeta); + } + if (data.containsKey('accuracyM')) { + context.handle( + _accuracyMMeta, + accuracyM.isAcceptableOrUnknown(data['accuracyM']!, _accuracyMMeta), + ); + } + if (data.containsKey('bearingDeg')) { + context.handle( + _bearingDegMeta, + bearingDeg.isAcceptableOrUnknown(data['bearingDeg']!, _bearingDegMeta), + ); + } + if (data.containsKey('synced')) { + context.handle( + _syncedMeta, + synced.isAcceptableOrUnknown(data['synced']!, _syncedMeta), + ); + } + return context; + } + + @override + Set get $primaryKey => {id}; + @override + TrackPointRow map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return TrackPointRow( + id: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}id'], + )!, + tripId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}tripId'], + )!, + segmentId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}segmentId'], + )!, + timestamp: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}timestamp'], + )!, + latitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}latitude'], + )!, + longitude: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}longitude'], + )!, + speedKmh: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}speedKmh'], + )!, + altitudeM: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}altitudeM'], + )!, + accuracyM: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}accuracyM'], + )!, + bearingDeg: attachedDatabase.typeMapping.read( + DriftSqlType.double, + data['${effectivePrefix}bearingDeg'], + )!, + synced: attachedDatabase.typeMapping.read( + DriftSqlType.bool, + data['${effectivePrefix}synced'], + )!, + ); + } + + @override + $TrackPointsTable createAlias(String alias) { + return $TrackPointsTable(attachedDatabase, alias); + } +} + +class TrackPointRow extends DataClass implements Insertable { + final int id; + final int tripId; + final int segmentId; + final int timestamp; + final double latitude; + final double longitude; + final double speedKmh; + final double altitudeM; + final double accuracyM; + final double bearingDeg; + + /// Set once the point has been accepted by the remote endpoint. + final bool synced; + const TrackPointRow({ + required this.id, + required this.tripId, + required this.segmentId, + required this.timestamp, + required this.latitude, + required this.longitude, + required this.speedKmh, + required this.altitudeM, + required this.accuracyM, + required this.bearingDeg, + required this.synced, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['tripId'] = Variable(tripId); + map['segmentId'] = Variable(segmentId); + map['timestamp'] = Variable(timestamp); + map['latitude'] = Variable(latitude); + map['longitude'] = Variable(longitude); + map['speedKmh'] = Variable(speedKmh); + map['altitudeM'] = Variable(altitudeM); + map['accuracyM'] = Variable(accuracyM); + map['bearingDeg'] = Variable(bearingDeg); + map['synced'] = Variable(synced); + return map; + } + + TrackPointsCompanion toCompanion(bool nullToAbsent) { + return TrackPointsCompanion( + id: Value(id), + tripId: Value(tripId), + segmentId: Value(segmentId), + timestamp: Value(timestamp), + latitude: Value(latitude), + longitude: Value(longitude), + speedKmh: Value(speedKmh), + altitudeM: Value(altitudeM), + accuracyM: Value(accuracyM), + bearingDeg: Value(bearingDeg), + synced: Value(synced), + ); + } + + factory TrackPointRow.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return TrackPointRow( + id: serializer.fromJson(json['id']), + tripId: serializer.fromJson(json['tripId']), + segmentId: serializer.fromJson(json['segmentId']), + timestamp: serializer.fromJson(json['timestamp']), + latitude: serializer.fromJson(json['latitude']), + longitude: serializer.fromJson(json['longitude']), + speedKmh: serializer.fromJson(json['speedKmh']), + altitudeM: serializer.fromJson(json['altitudeM']), + accuracyM: serializer.fromJson(json['accuracyM']), + bearingDeg: serializer.fromJson(json['bearingDeg']), + synced: serializer.fromJson(json['synced']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'tripId': serializer.toJson(tripId), + 'segmentId': serializer.toJson(segmentId), + 'timestamp': serializer.toJson(timestamp), + 'latitude': serializer.toJson(latitude), + 'longitude': serializer.toJson(longitude), + 'speedKmh': serializer.toJson(speedKmh), + 'altitudeM': serializer.toJson(altitudeM), + 'accuracyM': serializer.toJson(accuracyM), + 'bearingDeg': serializer.toJson(bearingDeg), + 'synced': serializer.toJson(synced), + }; + } + + TrackPointRow copyWith({ + int? id, + int? tripId, + int? segmentId, + int? timestamp, + double? latitude, + double? longitude, + double? speedKmh, + double? altitudeM, + double? accuracyM, + double? bearingDeg, + bool? synced, + }) => TrackPointRow( + id: id ?? this.id, + tripId: tripId ?? this.tripId, + segmentId: segmentId ?? this.segmentId, + timestamp: timestamp ?? this.timestamp, + latitude: latitude ?? this.latitude, + longitude: longitude ?? this.longitude, + speedKmh: speedKmh ?? this.speedKmh, + altitudeM: altitudeM ?? this.altitudeM, + accuracyM: accuracyM ?? this.accuracyM, + bearingDeg: bearingDeg ?? this.bearingDeg, + synced: synced ?? this.synced, + ); + TrackPointRow copyWithCompanion(TrackPointsCompanion data) { + return TrackPointRow( + id: data.id.present ? data.id.value : this.id, + tripId: data.tripId.present ? data.tripId.value : this.tripId, + segmentId: data.segmentId.present ? data.segmentId.value : this.segmentId, + timestamp: data.timestamp.present ? data.timestamp.value : this.timestamp, + latitude: data.latitude.present ? data.latitude.value : this.latitude, + longitude: data.longitude.present ? data.longitude.value : this.longitude, + speedKmh: data.speedKmh.present ? data.speedKmh.value : this.speedKmh, + altitudeM: data.altitudeM.present ? data.altitudeM.value : this.altitudeM, + accuracyM: data.accuracyM.present ? data.accuracyM.value : this.accuracyM, + bearingDeg: data.bearingDeg.present + ? data.bearingDeg.value + : this.bearingDeg, + synced: data.synced.present ? data.synced.value : this.synced, + ); + } + + @override + String toString() { + return (StringBuffer('TrackPointRow(') + ..write('id: $id, ') + ..write('tripId: $tripId, ') + ..write('segmentId: $segmentId, ') + ..write('timestamp: $timestamp, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude, ') + ..write('speedKmh: $speedKmh, ') + ..write('altitudeM: $altitudeM, ') + ..write('accuracyM: $accuracyM, ') + ..write('bearingDeg: $bearingDeg, ') + ..write('synced: $synced') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash( + id, + tripId, + segmentId, + timestamp, + latitude, + longitude, + speedKmh, + altitudeM, + accuracyM, + bearingDeg, + synced, + ); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is TrackPointRow && + other.id == this.id && + other.tripId == this.tripId && + other.segmentId == this.segmentId && + other.timestamp == this.timestamp && + other.latitude == this.latitude && + other.longitude == this.longitude && + other.speedKmh == this.speedKmh && + other.altitudeM == this.altitudeM && + other.accuracyM == this.accuracyM && + other.bearingDeg == this.bearingDeg && + other.synced == this.synced); +} + +class TrackPointsCompanion extends UpdateCompanion { + final Value id; + final Value tripId; + final Value segmentId; + final Value timestamp; + final Value latitude; + final Value longitude; + final Value speedKmh; + final Value altitudeM; + final Value accuracyM; + final Value bearingDeg; + final Value synced; + const TrackPointsCompanion({ + this.id = const Value.absent(), + this.tripId = const Value.absent(), + this.segmentId = const Value.absent(), + this.timestamp = const Value.absent(), + this.latitude = const Value.absent(), + this.longitude = const Value.absent(), + this.speedKmh = const Value.absent(), + this.altitudeM = const Value.absent(), + this.accuracyM = const Value.absent(), + this.bearingDeg = const Value.absent(), + this.synced = const Value.absent(), + }); + TrackPointsCompanion.insert({ + this.id = const Value.absent(), + required int tripId, + required int segmentId, + required int timestamp, + required double latitude, + required double longitude, + required double speedKmh, + required double altitudeM, + this.accuracyM = const Value.absent(), + this.bearingDeg = const Value.absent(), + this.synced = const Value.absent(), + }) : tripId = Value(tripId), + segmentId = Value(segmentId), + timestamp = Value(timestamp), + latitude = Value(latitude), + longitude = Value(longitude), + speedKmh = Value(speedKmh), + altitudeM = Value(altitudeM); + static Insertable custom({ + Expression? id, + Expression? tripId, + Expression? segmentId, + Expression? timestamp, + Expression? latitude, + Expression? longitude, + Expression? speedKmh, + Expression? altitudeM, + Expression? accuracyM, + Expression? bearingDeg, + Expression? synced, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (tripId != null) 'tripId': tripId, + if (segmentId != null) 'segmentId': segmentId, + if (timestamp != null) 'timestamp': timestamp, + if (latitude != null) 'latitude': latitude, + if (longitude != null) 'longitude': longitude, + if (speedKmh != null) 'speedKmh': speedKmh, + if (altitudeM != null) 'altitudeM': altitudeM, + if (accuracyM != null) 'accuracyM': accuracyM, + if (bearingDeg != null) 'bearingDeg': bearingDeg, + if (synced != null) 'synced': synced, + }); + } + + TrackPointsCompanion copyWith({ + Value? id, + Value? tripId, + Value? segmentId, + Value? timestamp, + Value? latitude, + Value? longitude, + Value? speedKmh, + Value? altitudeM, + Value? accuracyM, + Value? bearingDeg, + Value? synced, + }) { + return TrackPointsCompanion( + id: id ?? this.id, + tripId: tripId ?? this.tripId, + segmentId: segmentId ?? this.segmentId, + timestamp: timestamp ?? this.timestamp, + latitude: latitude ?? this.latitude, + longitude: longitude ?? this.longitude, + speedKmh: speedKmh ?? this.speedKmh, + altitudeM: altitudeM ?? this.altitudeM, + accuracyM: accuracyM ?? this.accuracyM, + bearingDeg: bearingDeg ?? this.bearingDeg, + synced: synced ?? this.synced, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (tripId.present) { + map['tripId'] = Variable(tripId.value); + } + if (segmentId.present) { + map['segmentId'] = Variable(segmentId.value); + } + if (timestamp.present) { + map['timestamp'] = Variable(timestamp.value); + } + if (latitude.present) { + map['latitude'] = Variable(latitude.value); + } + if (longitude.present) { + map['longitude'] = Variable(longitude.value); + } + if (speedKmh.present) { + map['speedKmh'] = Variable(speedKmh.value); + } + if (altitudeM.present) { + map['altitudeM'] = Variable(altitudeM.value); + } + if (accuracyM.present) { + map['accuracyM'] = Variable(accuracyM.value); + } + if (bearingDeg.present) { + map['bearingDeg'] = Variable(bearingDeg.value); + } + if (synced.present) { + map['synced'] = Variable(synced.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('TrackPointsCompanion(') + ..write('id: $id, ') + ..write('tripId: $tripId, ') + ..write('segmentId: $segmentId, ') + ..write('timestamp: $timestamp, ') + ..write('latitude: $latitude, ') + ..write('longitude: $longitude, ') + ..write('speedKmh: $speedKmh, ') + ..write('altitudeM: $altitudeM, ') + ..write('accuracyM: $accuracyM, ') + ..write('bearingDeg: $bearingDeg, ') + ..write('synced: $synced') + ..write(')')) + .toString(); + } +} + +abstract class _$AppDatabase extends GeneratedDatabase { + _$AppDatabase(QueryExecutor e) : super(e); + $AppDatabaseManager get managers => $AppDatabaseManager(this); + late final $TripsTable trips = $TripsTable(this); + late final $SegmentsTable segments = $SegmentsTable(this); + late final $TrackPointsTable trackPoints = $TrackPointsTable(this); + late final Index idxTripsEnded = Index( + 'idx_trips_ended', + 'CREATE INDEX idx_trips_ended ON trips (endedAt)', + ); + late final Index idxSegmentsTrip = Index( + 'idx_segments_trip', + 'CREATE INDEX idx_segments_trip ON segments (tripId)', + ); + late final Index idxPointsTrip = Index( + 'idx_points_trip', + 'CREATE INDEX idx_points_trip ON track_points (tripId)', + ); + late final Index idxPointsSegment = Index( + 'idx_points_segment', + 'CREATE INDEX idx_points_segment ON track_points (segmentId)', + ); + late final Index idxPointsSynced = Index( + 'idx_points_synced', + 'CREATE INDEX idx_points_synced ON track_points (synced)', + ); + @override + Iterable> get allTables => + allSchemaEntities.whereType>(); + @override + List get allSchemaEntities => [ + trips, + segments, + trackPoints, + idxTripsEnded, + idxSegmentsTrip, + idxPointsTrip, + idxPointsSegment, + idxPointsSynced, + ]; + @override + StreamQueryUpdateRules get streamUpdateRules => const StreamQueryUpdateRules([ + WritePropagation( + on: TableUpdateQuery.onTableName( + 'trips', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('segments', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'trips', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('track_points', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'segments', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('track_points', kind: UpdateKind.delete)], + ), + ]); +} + +typedef $$TripsTableCreateCompanionBuilder = TripsCompanion Function({ + Value id, + required int startedAt, + Value endedAt, + Value name, + required domain.TripState state, + Value distanceM, + Value movingMillis, + Value maxSpeedKmh, + Value elevationGainM, + Value pointCount, +}); +typedef $$TripsTableUpdateCompanionBuilder = TripsCompanion Function({ + Value id, + Value startedAt, + Value endedAt, + Value name, + Value state, + Value distanceM, + Value movingMillis, + Value maxSpeedKmh, + Value elevationGainM, + Value pointCount, +}); + +final class $$TripsTableReferences + extends BaseReferences<_$AppDatabase, $TripsTable, TripRow> { + $$TripsTableReferences(super.$_db, super.$_table, super.$_typedResult); + + static MultiTypedResultKey<$SegmentsTable, List> + _segmentsRefsTable(_$AppDatabase db) => MultiTypedResultKey.fromTable( + db.segments, + aliasName: 'trips__id__segments__tripId', + ); + + $$SegmentsTableProcessedTableManager get segmentsRefs { + final manager = $$SegmentsTableTableManager( + $_db, + $_db.segments, + ).filter((f) => f.tripId.id.sqlEquals($_itemColumn('id')!)); + + final cache = $_typedResult.readTableOrNull(_segmentsRefsTable($_db)); + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: cache), + ); + } + + static MultiTypedResultKey<$TrackPointsTable, List> + _trackPointsRefsTable(_$AppDatabase db) => MultiTypedResultKey.fromTable( + db.trackPoints, + aliasName: 'trips__id__track_points__tripId', + ); + + $$TrackPointsTableProcessedTableManager get trackPointsRefs { + final manager = $$TrackPointsTableTableManager( + $_db, + $_db.trackPoints, + ).filter((f) => f.tripId.id.sqlEquals($_itemColumn('id')!)); + + final cache = $_typedResult.readTableOrNull(_trackPointsRefsTable($_db)); + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: cache), + ); + } +} + +class $$TripsTableFilterComposer extends Composer<_$AppDatabase, $TripsTable> { + $$TripsTableFilterComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnFilters get id => $composableBuilder( + column: $table.id, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get startedAt => $composableBuilder( + column: $table.startedAt, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get endedAt => $composableBuilder( + column: $table.endedAt, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get name => $composableBuilder( + column: $table.name, + builder: (column) => ColumnFilters(column), + ); + + ColumnWithTypeConverterFilters + get state => $composableBuilder( + column: $table.state, + builder: (column) => ColumnWithTypeConverterFilters(column), + ); + + ColumnFilters get distanceM => $composableBuilder( + column: $table.distanceM, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get movingMillis => $composableBuilder( + column: $table.movingMillis, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get maxSpeedKmh => $composableBuilder( + column: $table.maxSpeedKmh, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get elevationGainM => $composableBuilder( + column: $table.elevationGainM, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get pointCount => $composableBuilder( + column: $table.pointCount, + builder: (column) => ColumnFilters(column), + ); + + Expression segmentsRefs( + Expression Function($$SegmentsTableFilterComposer f) f, + ) { + final $$SegmentsTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.segments, + getReferencedColumn: (t) => t.tripId, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$SegmentsTableFilterComposer( + $db: $db, + $table: $db.segments, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return f(composer); + } + + Expression trackPointsRefs( + Expression Function($$TrackPointsTableFilterComposer f) f, + ) { + final $$TrackPointsTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.trackPoints, + getReferencedColumn: (t) => t.tripId, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$TrackPointsTableFilterComposer( + $db: $db, + $table: $db.trackPoints, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return f(composer); + } +} + +class $$TripsTableOrderingComposer + extends Composer<_$AppDatabase, $TripsTable> { + $$TripsTableOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnOrderings get id => $composableBuilder( + column: $table.id, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get startedAt => $composableBuilder( + column: $table.startedAt, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get endedAt => $composableBuilder( + column: $table.endedAt, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get name => $composableBuilder( + column: $table.name, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get state => $composableBuilder( + column: $table.state, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get distanceM => $composableBuilder( + column: $table.distanceM, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get movingMillis => $composableBuilder( + column: $table.movingMillis, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get maxSpeedKmh => $composableBuilder( + column: $table.maxSpeedKmh, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get elevationGainM => $composableBuilder( + column: $table.elevationGainM, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get pointCount => $composableBuilder( + column: $table.pointCount, + builder: (column) => ColumnOrderings(column), + ); +} + +class $$TripsTableAnnotationComposer + extends Composer<_$AppDatabase, $TripsTable> { + $$TripsTableAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + GeneratedColumn get id => + $composableBuilder(column: $table.id, builder: (column) => column); + + GeneratedColumn get startedAt => + $composableBuilder(column: $table.startedAt, builder: (column) => column); + + GeneratedColumn get endedAt => + $composableBuilder(column: $table.endedAt, builder: (column) => column); + + GeneratedColumn get name => + $composableBuilder(column: $table.name, builder: (column) => column); + + GeneratedColumnWithTypeConverter get state => + $composableBuilder(column: $table.state, builder: (column) => column); + + GeneratedColumn get distanceM => + $composableBuilder(column: $table.distanceM, builder: (column) => column); + + GeneratedColumn get movingMillis => $composableBuilder( + column: $table.movingMillis, + builder: (column) => column, + ); + + GeneratedColumn get maxSpeedKmh => $composableBuilder( + column: $table.maxSpeedKmh, + builder: (column) => column, + ); + + GeneratedColumn get elevationGainM => $composableBuilder( + column: $table.elevationGainM, + builder: (column) => column, + ); + + GeneratedColumn get pointCount => $composableBuilder( + column: $table.pointCount, + builder: (column) => column, + ); + + Expression segmentsRefs( + Expression Function($$SegmentsTableAnnotationComposer a) f, + ) { + final $$SegmentsTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.segments, + getReferencedColumn: (t) => t.tripId, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$SegmentsTableAnnotationComposer( + $db: $db, + $table: $db.segments, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return f(composer); + } + + Expression trackPointsRefs( + Expression Function($$TrackPointsTableAnnotationComposer a) f, + ) { + final $$TrackPointsTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.trackPoints, + getReferencedColumn: (t) => t.tripId, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$TrackPointsTableAnnotationComposer( + $db: $db, + $table: $db.trackPoints, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return f(composer); + } +} + +class $$TripsTableTableManager + extends + RootTableManager< + _$AppDatabase, + $TripsTable, + TripRow, + $$TripsTableFilterComposer, + $$TripsTableOrderingComposer, + $$TripsTableAnnotationComposer, + $$TripsTableCreateCompanionBuilder, + $$TripsTableUpdateCompanionBuilder, + (TripRow, $$TripsTableReferences), + TripRow, + PrefetchHooks Function({bool segmentsRefs, bool trackPointsRefs}) + > { + $$TripsTableTableManager(_$AppDatabase db, $TripsTable table) + : super( + TableManagerState( + db: db, + table: table, + createFilteringComposer: () => + $$TripsTableFilterComposer($db: db, $table: table), + createOrderingComposer: () => + $$TripsTableOrderingComposer($db: db, $table: table), + createComputedFieldComposer: () => + $$TripsTableAnnotationComposer($db: db, $table: table), + updateCompanionCallback: + ({ + Value id = const Value.absent(), + Value startedAt = const Value.absent(), + Value endedAt = const Value.absent(), + Value name = const Value.absent(), + Value state = const Value.absent(), + Value distanceM = const Value.absent(), + Value movingMillis = const Value.absent(), + Value maxSpeedKmh = const Value.absent(), + Value elevationGainM = const Value.absent(), + Value pointCount = const Value.absent(), + }) => TripsCompanion( + id: id, + startedAt: startedAt, + endedAt: endedAt, + name: name, + state: state, + distanceM: distanceM, + movingMillis: movingMillis, + maxSpeedKmh: maxSpeedKmh, + elevationGainM: elevationGainM, + pointCount: pointCount, + ), + createCompanionCallback: + ({ + Value id = const Value.absent(), + required int startedAt, + Value endedAt = const Value.absent(), + Value name = const Value.absent(), + required domain.TripState state, + Value distanceM = const Value.absent(), + Value movingMillis = const Value.absent(), + Value maxSpeedKmh = const Value.absent(), + Value elevationGainM = const Value.absent(), + Value pointCount = const Value.absent(), + }) => TripsCompanion.insert( + id: id, + startedAt: startedAt, + endedAt: endedAt, + name: name, + state: state, + distanceM: distanceM, + movingMillis: movingMillis, + maxSpeedKmh: maxSpeedKmh, + elevationGainM: elevationGainM, + pointCount: pointCount, + ), + withReferenceMapper: (p0) => p0 + .map( + (e) => + (e.readTable(table), $$TripsTableReferences(db, table, e)), + ) + .toList(), + prefetchHooksCallback: + ({segmentsRefs = false, trackPointsRefs = false}) { + return PrefetchHooks( + db: db, + explicitlyWatchedTables: [ + if (segmentsRefs) db.segments, + if (trackPointsRefs) db.trackPoints, + ], + addJoins: null, + getPrefetchedDataCallback: (items) async { + return [ + if (segmentsRefs) + await $_getPrefetchedData< + TripRow, + $TripsTable, + SegmentRow + >( + currentTable: table, + referencedTable: $$TripsTableReferences + ._segmentsRefsTable(db), + managerFromTypedResult: (p0) => + $$TripsTableReferences( + db, + table, + p0, + ).segmentsRefs, + referencedItemsForCurrentItem: + (item, referencedItems) => referencedItems.where( + (e) => e.tripId == item.id, + ), + typedResults: items, + ), + if (trackPointsRefs) + await $_getPrefetchedData< + TripRow, + $TripsTable, + TrackPointRow + >( + currentTable: table, + referencedTable: $$TripsTableReferences + ._trackPointsRefsTable(db), + managerFromTypedResult: (p0) => + $$TripsTableReferences( + db, + table, + p0, + ).trackPointsRefs, + referencedItemsForCurrentItem: + (item, referencedItems) => referencedItems.where( + (e) => e.tripId == item.id, + ), + typedResults: items, + ), + ]; + }, + ); + }, + ), + ); +} + +typedef $$TripsTableProcessedTableManager = + ProcessedTableManager< + _$AppDatabase, + $TripsTable, + TripRow, + $$TripsTableFilterComposer, + $$TripsTableOrderingComposer, + $$TripsTableAnnotationComposer, + $$TripsTableCreateCompanionBuilder, + $$TripsTableUpdateCompanionBuilder, + (TripRow, $$TripsTableReferences), + TripRow, + PrefetchHooks Function({bool segmentsRefs, bool trackPointsRefs}) + >; +typedef $$SegmentsTableCreateCompanionBuilder = SegmentsCompanion Function({ + Value id, + required int tripId, + required int startedAt, + Value endedAt, +}); +typedef $$SegmentsTableUpdateCompanionBuilder = SegmentsCompanion Function({ + Value id, + Value tripId, + Value startedAt, + Value endedAt, +}); + +final class $$SegmentsTableReferences + extends BaseReferences<_$AppDatabase, $SegmentsTable, SegmentRow> { + $$SegmentsTableReferences(super.$_db, super.$_table, super.$_typedResult); + + static $TripsTable _tripIdTable(_$AppDatabase db) => + db.trips.createAlias('segments__tripId__trips__id'); + + $$TripsTableProcessedTableManager get tripId { + final $_column = $_itemColumn('tripId')!; + + final manager = $$TripsTableTableManager( + $_db, + $_db.trips, + ).filter((f) => f.id.sqlEquals($_column)); + final item = $_typedResult.readTableOrNull(_tripIdTable($_db)); + if (item == null) return manager; + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: [item]), + ); + } + + static MultiTypedResultKey<$TrackPointsTable, List> + _trackPointsRefsTable(_$AppDatabase db) => MultiTypedResultKey.fromTable( + db.trackPoints, + aliasName: 'segments__id__track_points__segmentId', + ); + + $$TrackPointsTableProcessedTableManager get trackPointsRefs { + final manager = $$TrackPointsTableTableManager( + $_db, + $_db.trackPoints, + ).filter((f) => f.segmentId.id.sqlEquals($_itemColumn('id')!)); + + final cache = $_typedResult.readTableOrNull(_trackPointsRefsTable($_db)); + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: cache), + ); + } +} + +class $$SegmentsTableFilterComposer + extends Composer<_$AppDatabase, $SegmentsTable> { + $$SegmentsTableFilterComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnFilters get id => $composableBuilder( + column: $table.id, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get startedAt => $composableBuilder( + column: $table.startedAt, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get endedAt => $composableBuilder( + column: $table.endedAt, + builder: (column) => ColumnFilters(column), + ); + + $$TripsTableFilterComposer get tripId { + final $$TripsTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.tripId, + referencedTable: $db.trips, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$TripsTableFilterComposer( + $db: $db, + $table: $db.trips, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } + + Expression trackPointsRefs( + Expression Function($$TrackPointsTableFilterComposer f) f, + ) { + final $$TrackPointsTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.trackPoints, + getReferencedColumn: (t) => t.segmentId, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$TrackPointsTableFilterComposer( + $db: $db, + $table: $db.trackPoints, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return f(composer); + } +} + +class $$SegmentsTableOrderingComposer + extends Composer<_$AppDatabase, $SegmentsTable> { + $$SegmentsTableOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnOrderings get id => $composableBuilder( + column: $table.id, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get startedAt => $composableBuilder( + column: $table.startedAt, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get endedAt => $composableBuilder( + column: $table.endedAt, + builder: (column) => ColumnOrderings(column), + ); + + $$TripsTableOrderingComposer get tripId { + final $$TripsTableOrderingComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.tripId, + referencedTable: $db.trips, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$TripsTableOrderingComposer( + $db: $db, + $table: $db.trips, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } +} + +class $$SegmentsTableAnnotationComposer + extends Composer<_$AppDatabase, $SegmentsTable> { + $$SegmentsTableAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + GeneratedColumn get id => + $composableBuilder(column: $table.id, builder: (column) => column); + + GeneratedColumn get startedAt => + $composableBuilder(column: $table.startedAt, builder: (column) => column); + + GeneratedColumn get endedAt => + $composableBuilder(column: $table.endedAt, builder: (column) => column); + + $$TripsTableAnnotationComposer get tripId { + final $$TripsTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.tripId, + referencedTable: $db.trips, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$TripsTableAnnotationComposer( + $db: $db, + $table: $db.trips, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } + + Expression trackPointsRefs( + Expression Function($$TrackPointsTableAnnotationComposer a) f, + ) { + final $$TrackPointsTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.trackPoints, + getReferencedColumn: (t) => t.segmentId, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$TrackPointsTableAnnotationComposer( + $db: $db, + $table: $db.trackPoints, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return f(composer); + } +} + +class $$SegmentsTableTableManager + extends + RootTableManager< + _$AppDatabase, + $SegmentsTable, + SegmentRow, + $$SegmentsTableFilterComposer, + $$SegmentsTableOrderingComposer, + $$SegmentsTableAnnotationComposer, + $$SegmentsTableCreateCompanionBuilder, + $$SegmentsTableUpdateCompanionBuilder, + (SegmentRow, $$SegmentsTableReferences), + SegmentRow, + PrefetchHooks Function({bool tripId, bool trackPointsRefs}) + > { + $$SegmentsTableTableManager(_$AppDatabase db, $SegmentsTable table) + : super( + TableManagerState( + db: db, + table: table, + createFilteringComposer: () => + $$SegmentsTableFilterComposer($db: db, $table: table), + createOrderingComposer: () => + $$SegmentsTableOrderingComposer($db: db, $table: table), + createComputedFieldComposer: () => + $$SegmentsTableAnnotationComposer($db: db, $table: table), + updateCompanionCallback: + ({ + Value id = const Value.absent(), + Value tripId = const Value.absent(), + Value startedAt = const Value.absent(), + Value endedAt = const Value.absent(), + }) => SegmentsCompanion( + id: id, + tripId: tripId, + startedAt: startedAt, + endedAt: endedAt, + ), + createCompanionCallback: + ({ + Value id = const Value.absent(), + required int tripId, + required int startedAt, + Value endedAt = const Value.absent(), + }) => SegmentsCompanion.insert( + id: id, + tripId: tripId, + startedAt: startedAt, + endedAt: endedAt, + ), + withReferenceMapper: (p0) => p0 + .map( + (e) => ( + e.readTable(table), + $$SegmentsTableReferences(db, table, e), + ), + ) + .toList(), + prefetchHooksCallback: ({tripId = false, trackPointsRefs = false}) { + return PrefetchHooks( + db: db, + explicitlyWatchedTables: [if (trackPointsRefs) db.trackPoints], + addJoins: + < + T extends TableManagerState< + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic + > + >(state) { + if (tripId) { + state = state.withJoin( + currentTable: table, + currentColumn: table.tripId, + referencedTable: $$SegmentsTableReferences._tripIdTable( + db, + ), + referencedColumn: $$SegmentsTableReferences + ._tripIdTable(db) + .id, + ) as T; + } + + return state; + }, + getPrefetchedDataCallback: (items) async { + return [ + if (trackPointsRefs) + await $_getPrefetchedData< + SegmentRow, + $SegmentsTable, + TrackPointRow + >( + currentTable: table, + referencedTable: $$SegmentsTableReferences + ._trackPointsRefsTable(db), + managerFromTypedResult: (p0) => $$SegmentsTableReferences( + db, + table, + p0, + ).trackPointsRefs, + referencedItemsForCurrentItem: (item, referencedItems) => + referencedItems.where((e) => e.segmentId == item.id), + typedResults: items, + ), + ]; + }, + ); + }, + ), + ); +} + +typedef $$SegmentsTableProcessedTableManager = + ProcessedTableManager< + _$AppDatabase, + $SegmentsTable, + SegmentRow, + $$SegmentsTableFilterComposer, + $$SegmentsTableOrderingComposer, + $$SegmentsTableAnnotationComposer, + $$SegmentsTableCreateCompanionBuilder, + $$SegmentsTableUpdateCompanionBuilder, + (SegmentRow, $$SegmentsTableReferences), + SegmentRow, + PrefetchHooks Function({bool tripId, bool trackPointsRefs}) + >; +typedef $$TrackPointsTableCreateCompanionBuilder = + TrackPointsCompanion Function({ + Value id, + required int tripId, + required int segmentId, + required int timestamp, + required double latitude, + required double longitude, + required double speedKmh, + required double altitudeM, + Value accuracyM, + Value bearingDeg, + Value synced, + }); +typedef $$TrackPointsTableUpdateCompanionBuilder = + TrackPointsCompanion Function({ + Value id, + Value tripId, + Value segmentId, + Value timestamp, + Value latitude, + Value longitude, + Value speedKmh, + Value altitudeM, + Value accuracyM, + Value bearingDeg, + Value synced, + }); + +final class $$TrackPointsTableReferences + extends BaseReferences<_$AppDatabase, $TrackPointsTable, TrackPointRow> { + $$TrackPointsTableReferences(super.$_db, super.$_table, super.$_typedResult); + + static $TripsTable _tripIdTable(_$AppDatabase db) => + db.trips.createAlias('track_points__tripId__trips__id'); + + $$TripsTableProcessedTableManager get tripId { + final $_column = $_itemColumn('tripId')!; + + final manager = $$TripsTableTableManager( + $_db, + $_db.trips, + ).filter((f) => f.id.sqlEquals($_column)); + final item = $_typedResult.readTableOrNull(_tripIdTable($_db)); + if (item == null) return manager; + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: [item]), + ); + } + + static $SegmentsTable _segmentIdTable(_$AppDatabase db) => + db.segments.createAlias('track_points__segmentId__segments__id'); + + $$SegmentsTableProcessedTableManager get segmentId { + final $_column = $_itemColumn('segmentId')!; + + final manager = $$SegmentsTableTableManager( + $_db, + $_db.segments, + ).filter((f) => f.id.sqlEquals($_column)); + final item = $_typedResult.readTableOrNull(_segmentIdTable($_db)); + if (item == null) return manager; + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: [item]), + ); + } +} + +class $$TrackPointsTableFilterComposer + extends Composer<_$AppDatabase, $TrackPointsTable> { + $$TrackPointsTableFilterComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnFilters get id => $composableBuilder( + column: $table.id, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get timestamp => $composableBuilder( + column: $table.timestamp, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get latitude => $composableBuilder( + column: $table.latitude, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get longitude => $composableBuilder( + column: $table.longitude, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get speedKmh => $composableBuilder( + column: $table.speedKmh, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get altitudeM => $composableBuilder( + column: $table.altitudeM, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get accuracyM => $composableBuilder( + column: $table.accuracyM, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get bearingDeg => $composableBuilder( + column: $table.bearingDeg, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get synced => $composableBuilder( + column: $table.synced, + builder: (column) => ColumnFilters(column), + ); + + $$TripsTableFilterComposer get tripId { + final $$TripsTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.tripId, + referencedTable: $db.trips, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$TripsTableFilterComposer( + $db: $db, + $table: $db.trips, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } + + $$SegmentsTableFilterComposer get segmentId { + final $$SegmentsTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.segmentId, + referencedTable: $db.segments, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$SegmentsTableFilterComposer( + $db: $db, + $table: $db.segments, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } +} + +class $$TrackPointsTableOrderingComposer + extends Composer<_$AppDatabase, $TrackPointsTable> { + $$TrackPointsTableOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnOrderings get id => $composableBuilder( + column: $table.id, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get timestamp => $composableBuilder( + column: $table.timestamp, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get latitude => $composableBuilder( + column: $table.latitude, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get longitude => $composableBuilder( + column: $table.longitude, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get speedKmh => $composableBuilder( + column: $table.speedKmh, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get altitudeM => $composableBuilder( + column: $table.altitudeM, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get accuracyM => $composableBuilder( + column: $table.accuracyM, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get bearingDeg => $composableBuilder( + column: $table.bearingDeg, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get synced => $composableBuilder( + column: $table.synced, + builder: (column) => ColumnOrderings(column), + ); + + $$TripsTableOrderingComposer get tripId { + final $$TripsTableOrderingComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.tripId, + referencedTable: $db.trips, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$TripsTableOrderingComposer( + $db: $db, + $table: $db.trips, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } + + $$SegmentsTableOrderingComposer get segmentId { + final $$SegmentsTableOrderingComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.segmentId, + referencedTable: $db.segments, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$SegmentsTableOrderingComposer( + $db: $db, + $table: $db.segments, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } +} + +class $$TrackPointsTableAnnotationComposer + extends Composer<_$AppDatabase, $TrackPointsTable> { + $$TrackPointsTableAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + GeneratedColumn get id => + $composableBuilder(column: $table.id, builder: (column) => column); + + GeneratedColumn get timestamp => + $composableBuilder(column: $table.timestamp, builder: (column) => column); + + GeneratedColumn get latitude => + $composableBuilder(column: $table.latitude, builder: (column) => column); + + GeneratedColumn get longitude => + $composableBuilder(column: $table.longitude, builder: (column) => column); + + GeneratedColumn get speedKmh => + $composableBuilder(column: $table.speedKmh, builder: (column) => column); + + GeneratedColumn get altitudeM => + $composableBuilder(column: $table.altitudeM, builder: (column) => column); + + GeneratedColumn get accuracyM => + $composableBuilder(column: $table.accuracyM, builder: (column) => column); + + GeneratedColumn get bearingDeg => $composableBuilder( + column: $table.bearingDeg, + builder: (column) => column, + ); + + GeneratedColumn get synced => + $composableBuilder(column: $table.synced, builder: (column) => column); + + $$TripsTableAnnotationComposer get tripId { + final $$TripsTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.tripId, + referencedTable: $db.trips, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$TripsTableAnnotationComposer( + $db: $db, + $table: $db.trips, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } + + $$SegmentsTableAnnotationComposer get segmentId { + final $$SegmentsTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.segmentId, + referencedTable: $db.segments, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$SegmentsTableAnnotationComposer( + $db: $db, + $table: $db.segments, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } +} + +class $$TrackPointsTableTableManager + extends + RootTableManager< + _$AppDatabase, + $TrackPointsTable, + TrackPointRow, + $$TrackPointsTableFilterComposer, + $$TrackPointsTableOrderingComposer, + $$TrackPointsTableAnnotationComposer, + $$TrackPointsTableCreateCompanionBuilder, + $$TrackPointsTableUpdateCompanionBuilder, + (TrackPointRow, $$TrackPointsTableReferences), + TrackPointRow, + PrefetchHooks Function({bool tripId, bool segmentId}) + > { + $$TrackPointsTableTableManager(_$AppDatabase db, $TrackPointsTable table) + : super( + TableManagerState( + db: db, + table: table, + createFilteringComposer: () => + $$TrackPointsTableFilterComposer($db: db, $table: table), + createOrderingComposer: () => + $$TrackPointsTableOrderingComposer($db: db, $table: table), + createComputedFieldComposer: () => + $$TrackPointsTableAnnotationComposer($db: db, $table: table), + updateCompanionCallback: + ({ + Value id = const Value.absent(), + Value tripId = const Value.absent(), + Value segmentId = const Value.absent(), + Value timestamp = const Value.absent(), + Value latitude = const Value.absent(), + Value longitude = const Value.absent(), + Value speedKmh = const Value.absent(), + Value altitudeM = const Value.absent(), + Value accuracyM = const Value.absent(), + Value bearingDeg = const Value.absent(), + Value synced = const Value.absent(), + }) => TrackPointsCompanion( + id: id, + tripId: tripId, + segmentId: segmentId, + timestamp: timestamp, + latitude: latitude, + longitude: longitude, + speedKmh: speedKmh, + altitudeM: altitudeM, + accuracyM: accuracyM, + bearingDeg: bearingDeg, + synced: synced, + ), + createCompanionCallback: + ({ + Value id = const Value.absent(), + required int tripId, + required int segmentId, + required int timestamp, + required double latitude, + required double longitude, + required double speedKmh, + required double altitudeM, + Value accuracyM = const Value.absent(), + Value bearingDeg = const Value.absent(), + Value synced = const Value.absent(), + }) => TrackPointsCompanion.insert( + id: id, + tripId: tripId, + segmentId: segmentId, + timestamp: timestamp, + latitude: latitude, + longitude: longitude, + speedKmh: speedKmh, + altitudeM: altitudeM, + accuracyM: accuracyM, + bearingDeg: bearingDeg, + synced: synced, + ), + withReferenceMapper: (p0) => p0 + .map( + (e) => ( + e.readTable(table), + $$TrackPointsTableReferences(db, table, e), + ), + ) + .toList(), + prefetchHooksCallback: ({tripId = false, segmentId = false}) { + return PrefetchHooks( + db: db, + explicitlyWatchedTables: [], + addJoins: + < + T extends TableManagerState< + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic + > + >(state) { + if (tripId) { + state = state.withJoin( + currentTable: table, + currentColumn: table.tripId, + referencedTable: $$TrackPointsTableReferences + ._tripIdTable(db), + referencedColumn: $$TrackPointsTableReferences + ._tripIdTable(db) + .id, + ) as T; + } + if (segmentId) { + state = state.withJoin( + currentTable: table, + currentColumn: table.segmentId, + referencedTable: $$TrackPointsTableReferences + ._segmentIdTable(db), + referencedColumn: $$TrackPointsTableReferences + ._segmentIdTable(db) + .id, + ) as T; + } + + return state; + }, + getPrefetchedDataCallback: (items) async { + return []; + }, + ); + }, + ), + ); +} + +typedef $$TrackPointsTableProcessedTableManager = + ProcessedTableManager< + _$AppDatabase, + $TrackPointsTable, + TrackPointRow, + $$TrackPointsTableFilterComposer, + $$TrackPointsTableOrderingComposer, + $$TrackPointsTableAnnotationComposer, + $$TrackPointsTableCreateCompanionBuilder, + $$TrackPointsTableUpdateCompanionBuilder, + (TrackPointRow, $$TrackPointsTableReferences), + TrackPointRow, + PrefetchHooks Function({bool tripId, bool segmentId}) + >; + +class $AppDatabaseManager { + final _$AppDatabase _db; + $AppDatabaseManager(this._db); + $$TripsTableTableManager get trips => + $$TripsTableTableManager(_db, _db.trips); + $$SegmentsTableTableManager get segments => + $$SegmentsTableTableManager(_db, _db.segments); + $$TrackPointsTableTableManager get trackPoints => + $$TrackPointsTableTableManager(_db, _db.trackPoints); +} diff --git a/test/database_test.dart b/test/database_test.dart new file mode 100644 index 0000000..8304605 --- /dev/null +++ b/test/database_test.dart @@ -0,0 +1,248 @@ +import 'package:drift/drift.dart' show driftRuntimeOptions; +import 'package:drift/native.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:rippr/src/data/database.dart'; +import 'package:rippr/src/domain/models.dart'; + +/// Ported from `com.rippr.data.SchemaTest`. +/// +/// **These were instrumented tests requiring a device.** Against Drift on the Dart VM +/// they are plain unit tests: faster, and runnable with no emulator booted. That is one +/// of the concrete wins of the port. +/// +/// Every test uses an in-memory database. The native suite once wiped a real device's +/// rides by running against the production singleton in `setUp`, so this is not +/// negotiable — see `docs/TESTING.md` in the native repo. +void main() { + late AppDatabase db; + + setUp(() { + driftRuntimeOptions.dontWarnAboutMultipleDatabases = true; + db = AppDatabase(NativeDatabase.memory()); + }); + + tearDown(() async => db.close()); + + Future<(int tripId, int segmentId)> seedTrip({int startedAt = 1000}) async { + final tripId = + await db.insertTrip(Trip(startedAt: startedAt, state: TripState.recording)); + final segmentId = await db.insertSegment(tripId, startedAt); + return (tripId, segmentId); + } + + TrackPoint point(int tripId, int segmentId, int ts, {double speed = 50.0}) => + TrackPoint( + tripId: tripId, + segmentId: segmentId, + timestamp: ts, + latitude: 51.0447, + longitude: -114.0719, + speedKmh: speed, + altitudeM: 1045.0, + ); + + group('cascade', () { + test('deleting a trip cascades to segments and points', () async { + final (tripId, segmentId) = await seedTrip(); + await db.insertPoints( + List.generate(5, (i) => point(tripId, segmentId, 1000 + i))); + + expect(await db.countPointsForTrip(tripId), 5); + expect(await db.countSegmentsForTrip(tripId), 1); + + await db.deleteTrip(tripId); + + expect(await db.countPointsForTrip(tripId), 0); + expect(await db.countSegmentsForTrip(tripId), 0); + expect(await db.getTrip(tripId), isNull); + }); + + test('foreign keys are actually enforced', () async { + // SQLite defaults foreign_keys to OFF. Room enabled it; Drift does not, so the + // pragma in beforeOpen is load-bearing. Without it the CASCADE above is + // decorative and every delete silently orphans its points. + final result = + await db.customSelect('PRAGMA foreign_keys').getSingle(); + expect(result.data.values.first, 1, + reason: 'foreign_keys pragma is off — CASCADE would do nothing'); + }); + + test('a point cannot reference a trip that does not exist', () async { + await expectLater( + db.insertPoint(point(9999, 9999, 1000)), + throwsA(anything), + ); + }); + }); + + group('active trip', () { + test('an open trip is the active one and a closed trip is not', () async { + final (tripId, _) = await seedTrip(); + + expect((await db.getActiveTrip())?.id, tripId); + + await db.closeTrip(tripId, 5000); + + expect(await db.getActiveTrip(), isNull); + }); + + test('watchActiveTrip emits the open trip', () async { + final (tripId, _) = await seedTrip(); + expect((await db.watchActiveTrip().first)?.id, tripId); + }); + + test('completed trips are ordered newest first', () async { + final a = await db.insertTrip( + const Trip(startedAt: 1000, state: TripState.recording)); + final b = await db.insertTrip( + const Trip(startedAt: 3000, state: TripState.recording)); + final c = await db.insertTrip( + const Trip(startedAt: 2000, state: TripState.recording)); + for (final id in [a, b, c]) { + await db.closeTrip(id, 9000); + } + + final listed = await db.watchCompletedTrips().first; + expect(listed.map((t) => t.startedAt).toList(), [3000, 2000, 1000]); + }); + }); + + group('point ordering', () { + test('points come back by segment then id, not by timestamp', () async { + // Timestamps are GPS-derived and can jump; id is monotonic in write order. The + // ordering contract is what keeps a ride walkable with pauses intact. + final (tripId, seg1) = await seedTrip(); + final seg2 = await db.insertSegment(tripId, 2000); + + await db.insertPoints([ + point(tripId, seg2, 500), // deliberately the earliest timestamp + point(tripId, seg1, 9000), + point(tripId, seg1, 1000), + ]); + + final ordered = await db.pointsForTrip(tripId); + expect(ordered.map((p) => p.segmentId).toList(), [seg1, seg1, seg2]); + expect(ordered.first.timestamp, 9000, + reason: 'must not be re-sorted by timestamp'); + }); + + test('lastInSegment returns the highest id, the recorder anchor', () async { + final (tripId, segmentId) = await seedTrip(); + await db.insertPoints( + List.generate(5, (i) => point(tripId, segmentId, 1000 + i))); + + final last = await db.lastInSegment(segmentId); + expect(last!.timestamp, 1004); + }); + + test('openSegment finds the one still being recorded into', () async { + final (tripId, seg1) = await seedTrip(); + await db.closeSegment(seg1, 2000); + final seg2 = await db.insertSegment(tripId, 3000); + + expect((await db.openSegment(tripId))?.id, seg2); + + await db.closeSegment(seg2, 4000); + expect(await db.openSegment(tripId), isNull); + }); + }); + + group('stats guards', () { + test('a trip with no points reports zeros rather than failing', () async { + final (tripId, _) = await seedTrip(); + + final stats = await db.watchTripStats(tripId).first; + + expect(stats.pointCount, 0); + expect(stats.maxSpeedKmh, 0.0); + expect(stats.avgSpeedKmh, 0.0); + expect(stats.firstTimestamp, 0); + expect(stats.durationMillis, 0); + }); + + test('stats aggregate over the trip', () async { + final (tripId, segmentId) = await seedTrip(); + await db.insertPoints([ + point(tripId, segmentId, 1000, speed: 10), + point(tripId, segmentId, 2000, speed: 50), + point(tripId, segmentId, 3000, speed: 30), + ]); + + final stats = await db.watchTripStats(tripId).first; + + expect(stats.pointCount, 3); + expect(stats.maxSpeedKmh, 50.0); + expect(stats.avgSpeedKmh, closeTo(30.0, 1e-9)); + expect(stats.durationMillis, 2000); + expect(stats.pendingUpload, 3); + }); + }); + + group('upload backlog', () { + test('unsynced points come back oldest first and mark cleanly', () async { + final (tripId, segmentId) = await seedTrip(); + await db.insertPoints( + List.generate(10, (i) => point(tripId, segmentId, 1000 + i))); + + expect(await db.countUnsynced(), 10); + + final batch = await db.unsyncedPoints(4); + expect(batch.length, 4); + expect(batch.first.timestamp, 1000); + + await db.markSynced(batch.map((p) => p.id).toList()); + + expect(await db.countUnsynced(), 6); + expect((await db.unsyncedPoints(1)).first.timestamp, 1004); + }); + + test('marking an empty list is a no-op', () async { + final (tripId, segmentId) = await seedTrip(); + await db.insertPoints([point(tripId, segmentId, 1000)]); + await db.markSynced(const []); + expect(await db.countUnsynced(), 1); + }); + }); + + group('aggregates', () { + test('updateAggregates writes only the running totals', () async { + final (tripId, _) = await seedTrip(); + await db.renameTrip(tripId, 'Morning loop'); + + await db.updateAggregates( + id: tripId, + distanceM: 1234.5, + movingMillis: 60000, + maxSpeedKmh: 88.5, + elevationGainM: 42.0, + pointCount: 99, + ); + + final trip = (await db.getTrip(tripId))!; + expect(trip.distanceM, 1234.5); + expect(trip.movingMillis, 60000); + expect(trip.maxSpeedKmh, 88.5); + expect(trip.elevationGainM, 42.0); + expect(trip.pointCount, 99); + expect(trip.name, 'Morning loop', reason: 'unrelated columns must survive'); + expect(trip.endedAt, isNull, reason: 'must not close the trip'); + }); + + test('state round-trips through the enum column', () async { + final (tripId, _) = await seedTrip(); + + await db.setTripState(tripId, TripState.paused); + expect((await db.getTrip(tripId))!.state, TripState.paused); + + await db.closeTrip(tripId, 5000); + expect((await db.getTrip(tripId))!.state, TripState.completed); + }); + + test('renaming to null clears the name rather than storing empty', () async { + final (tripId, _) = await seedTrip(); + await db.renameTrip(tripId, 'Something'); + await db.renameTrip(tripId, null); + expect((await db.getTrip(tripId))!.name, isNull); + }); + }); +}