Pith - varnish
package com.vgmlr.varnish

import android.content.Context
import androidx.room.Dao
import androidx.room.Database
import androidx.room.Entity
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.Room
import androidx.room.RoomDatabase
import kotlinx.coroutines.flow.Flow

object MarkKind {
    const val FOLDER_BLUR = 0
    const val FILE_BLUR = 1
    const val FAVORITE = 2
    const val PINNED = 3
    const val LIST_VIEW = 4
}

@Entity(tableName = "marks", primaryKeys = ["path", "kind"])
data class Mark(val path: String, val kind: Int)

@Dao
interface MarkDao {
    @Query("SELECT path FROM marks WHERE kind = :kind")
    fun paths(kind: Int): Flow<List<String>>

    @Query("SELECT path FROM marks WHERE kind = :kind")
    suspend fun pathsNow(kind: Int): List<String>

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    suspend fun add(marks: List<Mark>)

    @Query("DELETE FROM marks WHERE kind = :kind AND path IN (:paths)")
    suspend fun remove(kind: Int, paths: List<String>)

    @Query("DELETE FROM marks WHERE path = :path OR path LIKE :prefix ESCAPE '\\'")
    suspend fun purge(path: String, prefix: String)

    @Query("UPDATE OR REPLACE marks SET path = :to WHERE path = :from")
    suspend fun move(from: String, to: String)

    @Query(
        "UPDATE OR REPLACE marks SET path = :to || substr(path, :cut) " +
            "WHERE path LIKE :prefix ESCAPE '\\'"
    )
    suspend fun moveTree(to: String, cut: Int, prefix: String)
}

@Database(entities = [Mark::class], version = 1, exportSchema = false)
abstract class VarnishDb : RoomDatabase() {
    abstract fun marks(): MarkDao

    companion object {
        @Volatile
        private var inst: VarnishDb? = null

        fun get(context: Context): VarnishDb = inst ?: synchronized(this) {
            inst ?: Room.databaseBuilder(
                context.applicationContext,
                VarnishDb::class.java,
                "varnish.db"
            ).build().also { inst = it }
        }
    }
}

internal fun likeEscape(s: String): String =
    s.replace("\\", "\\\\").replace("%", "\\%").replace("_", "\\_")

internal fun treeLike(path: String): String = likeEscape("$path/") + "%"