Last active
December 28, 2024 15:54
-
-
Save ccampores-n26/8e2edcb5d6e74d1cdb1d5c5f0f40423e to your computer and use it in GitHub Desktop.
Demo FileHostingService
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.time.Instant | |
data class File( | |
val filename: String, | |
val size: Int, | |
val createdAt: Long? = null, | |
val ttlSec: Long? = null | |
) { | |
fun isAlive(timestamp: Long): Boolean { | |
if (ttlSec == null || createdAt == null) | |
return true | |
val expiryDate = createdAt + (ttlSec * 1000) | |
return expiryDate > timestamp | |
} | |
} | |
class FileAlreadyExists(filename: String) : RuntimeException("File $filename already exists") | |
class FileNotFoundException(filename: String) : RuntimeException("File $filename doesn't exist") | |
class FileHostingService { | |
val files = mutableMapOf<String, File>() | |
val history = mutableListOf<Pair<Long, Map<String, File>>>() | |
fun upload(filename: String, size: Int) { | |
if (filename in files) { | |
throw FileAlreadyExists(filename) | |
} | |
files[filename] = File(filename, size) | |
backup() | |
} | |
fun uploadAt(filename: String, size: Int, timestamp: Long, ttlSec: Long? = null) { | |
if (filename in files) { | |
throw FileAlreadyExists(filename) | |
} | |
files[filename] = File(filename, size, createdAt = timestamp, ttlSec) | |
backup(timestamp) | |
} | |
fun get(name: String): Int? = files[name]?.size | |
fun getAt(name: String, timestamp: Long): Int? = | |
files[name] | |
?.takeIf { it.isAlive(timestamp) } | |
?.size | |
fun copy(source: String, dest: String) { | |
val oldFile = files[source] ?: throw FileNotFoundException(source) | |
files[dest] = oldFile | |
backup() | |
} | |
fun copyAt(source: String, dest: String, timestamp: Long) { | |
val oldFile = files[source] | |
?.takeIf { it.isAlive(timestamp) } | |
?: throw FileNotFoundException(source) | |
files[dest] = oldFile | |
backup(timestamp) | |
} | |
fun search(prefix: String): List<File> { | |
return files | |
.filter { it.key.startsWith(prefix) } | |
.map { it.value } | |
.sortedWith( | |
compareByDescending<File> { it.size } | |
.thenBy { it.filename } | |
) | |
.take(10) | |
} | |
fun searchAt(prefix: String, timestamp: Long): List<File> { | |
return files | |
.filter { it.key.startsWith(prefix) } | |
.map { it.value } | |
.filter { it.isAlive(timestamp) } | |
.sortedWith( | |
compareByDescending<File> { it.size } | |
.thenBy { it.filename } | |
) | |
.take(10) | |
} | |
fun rollback(timestamp: Long) { | |
val snapshot = history | |
.filter { it.first <= timestamp } | |
.maxByOrNull { it.first } | |
?.second ?: return | |
files.clear() | |
val modified = snapshot | |
.mapValues { (_, file) -> file.copy(createdAt = timestamp) } | |
files.putAll(modified) | |
} | |
private fun backup(timestamp: Long = Instant.now().toEpochMilli()) { | |
history.add(Pair(timestamp, files.toMap())) | |
} | |
} | |
fun main() { | |
val now = Instant.now() | |
val fh = FileHostingService() | |
for (i in 1..15) { | |
fh.uploadAt( | |
"f$i", 100, | |
now.minusSeconds(i.toLong()).toEpochMilli(), | |
5 | |
) | |
} | |
// fh.searchAt("f", Instant.now().toEpochMilli()) | |
// .forEach { println(it) } | |
val cutTime = now.plusSeconds(2) | |
fh.uploadAt("after", 100, cutTime.plusSeconds(2).toEpochMilli(), 3) | |
fh.files.forEach { println(it) } | |
println("------") | |
fh.history.forEach { println(it) } | |
println("------") | |
fh.rollback(cutTime.minusSeconds(6).toEpochMilli()) | |
fh.files.forEach { println(it) } | |
val words = arrayOf("apple", "tisane") | |
words | |
.mapIndexed { index, w -> index to w } | |
.filter { (_, w) -> | |
w.startsWith("a") && w.endsWith("e") | |
} | |
.maxByOrNull { (index, _) -> index } ?: -1 | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment