Skip to content

Bookkeeper

interface Bookkeeper<Key : Any> {
    companion object {
        fun <Key : Any> by(
            maxLastFailedSync: suspend (key: Key) -> Long?,
            setLastFailedSync: suspend (key: Key, timestamp: Long) -> Boolean,
            clear: suspend (key: Key) -> Boolean,
            clearAll: suspend () -> Boolean
        ): Bookkeeper<Key>
    }
}

Example

fun provide(
    db: NotesDatabase,
    bookkeeping: BookkeepingDatabase
): Bookkeeper<NotesKey> = Bookkeeper.by(
    getLastFailedSync = { key: NotesKey ->
        require(key is NotesKey.Read)
        when (key) {
            is NotesKey.Read.ByNoteId -> {
                bookkeeping.getByNoteId(key.noteId)
            }
            is NotesKey.Read.ByAuthorId -> {
                val notes = db.getByAuthorId(key.authorId)
                bookkeeping.maxLastFailedSync(notes)
            }
            is NotesKey.Read.Paginated -> {
                val notes = db.getNotes(key.start, key.size)
                bookkeeping.maxLastFailedSync(notes)
            }
        }
    },
    setLastFailedSync = { key: NotesKey, timestamp: Long ->
        require(key !is NotesKey.Clear)
        try {
            when (key) {
                is NotesKey.Read.ByNoteId -> {
                    bookkeeping.upsert(key.noteId, timestamp)
                    true
                }
                is NotesKey.Read.ByAuthorId -> {
                    val notes = db.getByAuthorId(key.authorId)
                    notes.forEach { note: Note ->
                        bookkeeping.upsert(note.id, timestamp)
                    }
                    true
                }
                is NotesKey.Read.Paginated -> {
                    val notes = db.getNotes(key.start, key.size)
                    notes.forEach { note: Note ->
                        bookkeeping.upsert(note.id, timestamp)
                    }
                    true
                }
                is NotesKey.Write.ById -> {
                    bookkeeping.upsert(key.noteId, timestamp)
                    true
                }
                is NotesKey.Write.Create {
                    false
                }
            }
        } catch (_: Throwable) {
            false
        }
    },
    clear = { key: NotesKey ->
        require(key is NotesKey.Clear.ById)
        try {
            bookkeeping.deleteById(key.noteId)
        } catch (_: Throwable) {
            false
        }
    },
    clearAll = {
        require(key is NotesKey.Clear.All)
        try {
            bookkeeping.delete()
        } catch (_: Throwable) {
            false
        }
    },
)


private fun BookkeepingDatabase.maxLastFailedSync(notes: List<Note>): Long? {
    var maxLastFailedSync: Long? = null
    notes.forEach { note: Note ->
        val lastFailedSync = getByNoteId(note.id)
        if (maxLastFailedSync == null) {
            maxLastFailedSync = lastFailedSync
        } else if (lastFailedSync != null) {
            maxLastFailedSync = max(maxLastFailedSync, lastFailedSync)
        }
    }
    return maxLastFailedSync
}