2025-10-04 22:11:40 -05:00

392 lines
11 KiB
Kotlin

package moe.rosa.planterette.dsl
import moe.rosa.planterette.PlanteretteConfig
import moe.rosa.planterette.dsl.DSLEnablements.*
import moe.rosa.planterette.hakurei.*
@Target(AnnotationTarget.TYPE, AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
@PlanteretteDSL
annotation class HakureiDSL
@Target(AnnotationTarget.TYPE, AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
@HakureiDSL
annotation class DBusDSL
@Target(AnnotationTarget.TYPE, AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
@HakureiDSL
annotation class ExtraPermsDSL
@Target(AnnotationTarget.TYPE, AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
@HakureiDSL
annotation class ContainerDSL
@Target(AnnotationTarget.TYPE, AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
@ContainerDSL
annotation class FilesystemDSL
@Target(AnnotationTarget.TYPE, AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
@FilesystemDSL
annotation class FSBindDSL
@Target(AnnotationTarget.TYPE, AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
@FilesystemDSL
annotation class FSEphemeralDSL
@Target(AnnotationTarget.TYPE, AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
@FilesystemDSL
annotation class FSLinkDSL
@Target(AnnotationTarget.TYPE, AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
@FilesystemDSL
annotation class FSOverlayDSL
@PlanteretteDSL
fun PlanteretteConfig.hakurei(id: String, init: @HakureiDSL HakureiConfig.() -> Unit) {
this.hakurei = HakureiConfig(id).apply(init)
}
@HakureiDSL
fun HakureiConfig.executable(path: String, vararg args: String) {
this.path = AbsolutePath(path)
this.args = args.toList()
}
@HakureiDSL
enum class DSLEnablements {
Wayland,
X11,
DBus,
Pulse
}
@HakureiDSL
fun HakureiConfig.enable(vararg enablements: DSLEnablements) {
val enable = Enablements(wayland = null, x11 = null, dbus = null, pulse = null)
enablements.map {
when(it) {
Wayland -> enable.wayland = true
X11 -> enable.x11 = true
DBus -> enable.dbus = true
Pulse -> enable.pulse = true
}
}
this.enablements = enable
}
@HakureiDSL
fun HakureiConfig.directWayland(directWayland: Boolean = true) {
this.directWayland = directWayland
}
@HakureiDSL
fun HakureiConfig.username(username: String) {
this.username = username
}
@HakureiDSL
fun HakureiConfig.shell(shell: String) {
this.shell = AbsolutePath(shell)
}
@HakureiDSL
fun HakureiConfig.home(home: String) {
this.home = AbsolutePath(home)
}
//TODO(mae) automatic identity?
@HakureiDSL
fun HakureiConfig.identity(identity: Int? = null) {
this.identity = identity
}
@HakureiDSL
fun HakureiConfig.groups(vararg groups: String) {
this.groups = groups.toList()
}
data class DBusConfigs(var session: DBusConfig? = null, var system: DBusConfig? = null)
@HakureiDSL
fun HakureiConfig.dbus(init: @DBusDSL DBusConfigs.() -> Unit) {
val dbus = DBusConfigs().apply(init)
this.sessionBus = dbus.session
this.systemBus = dbus.system
}
@DBusDSL
fun DBusConfigs.session(init: @DBusDSL DBusConfig.() -> Unit) {
this.session = DBusConfig().apply(init)
}
@DBusDSL
fun DBusConfigs.system(init: @DBusDSL DBusConfig.() -> Unit) {
this.system = DBusConfig().apply(init)
}
@DBusDSL
fun DBusConfig.see(vararg see: String) {
this.see = see.toList()
}
@DBusDSL
fun DBusConfig.talk(vararg talk: String) {
this.talk = talk.toList()
}
@DBusDSL
fun DBusConfig.own(vararg own: String) {
this.own = own.toList()
}
@DBusDSL
fun DBusConfig.call(vararg call: Pair<String, String>) {
this.call = call.toMap()
}
@DBusDSL
fun DBusConfig.broadcast(vararg broadcast: Pair<String, String>) {
this.broadcast = broadcast.toMap()
}
@DBusDSL
fun DBusConfig.log(log: Boolean = true) {
this.log = log
}
@DBusDSL
fun DBusConfig.filter(filter: Boolean = true) {
this.filter = filter
}
@HakureiDSL
fun HakureiConfig.extraPerms(vararg extraPerms: ExtraPermsConfig) {
this.extraPerms = extraPerms.toList()
}
@ExtraPermsDSL
fun perm(path: String, init: ExtraPermsConfig.() -> Unit): ExtraPermsConfig {
return ExtraPermsConfig(path = AbsolutePath(path)).apply(init)
}
@ExtraPermsDSL
fun perm(path: String, ensure: Boolean? = null, rwx: String): ExtraPermsConfig {
if(rwx.length != 3) throw IllegalArgumentException()
// TODO(mae): is there a difference between null and false in this case?
val read: Boolean? = when(rwx[0]) {
'r', 'R' -> true
else -> null
}
val write: Boolean? = when(rwx[1]) {
'w', 'W' -> true
else -> null
}
val execute: Boolean? = when(rwx[2]) {
'x', 'X' -> true
else -> null
}
return ExtraPermsConfig(ensure, path = AbsolutePath(path), read, write, execute)
}
@ExtraPermsDSL
fun ExtraPermsConfig.ensure(ensure: Boolean = true) {
this.ensure = ensure
}
@ExtraPermsDSL
fun ExtraPermsConfig.read(read: Boolean = true) {
this.read = read
}
@ExtraPermsDSL
fun ExtraPermsConfig.write(write: Boolean = true) {
this.write = write
}
@ExtraPermsDSL
fun ExtraPermsConfig.execute(execute: Boolean = true) {
this.execute = execute
}
@HakureiDSL
fun HakureiConfig.container(init: @ContainerDSL ContainerConfig.() -> Unit) {
this.container = ContainerConfig().apply(init)
}
@ContainerDSL
fun ContainerConfig.hostname(hostname: String) {
this.hostname = hostname
}
@ContainerDSL
fun ContainerConfig.waitDelay(waitDelay: Long) {
this.waitDelay = waitDelay
}
@ContainerDSL
fun ContainerConfig.noTimeout() {
this.waitDelay = -1
}
@ContainerDSL
fun ContainerConfig.seccompCompat(seccompCompat: Boolean = true) {
this.seccompCompat = seccompCompat
}
@ContainerDSL
fun ContainerConfig.devel(devel: Boolean = true) {
this.devel = devel
}
@ContainerDSL
fun ContainerConfig.userns(userns: Boolean = true) {
this.userns = userns
}
@ContainerDSL
fun ContainerConfig.hostNet(hostNet: Boolean = true) {
this.hostNet = hostNet
}
@ContainerDSL
fun ContainerConfig.hostAbstract(hostAbstract: Boolean = true) {
this.hostAbstract = hostAbstract
}
@ContainerDSL
fun ContainerConfig.tty(tty: Boolean = true) {
this.tty = tty
}
@ContainerDSL
fun ContainerConfig.multiarch(multiarch: Boolean = true) {
this.multiarch = multiarch
}
@ContainerDSL
fun ContainerConfig.env(vararg env: Pair<String, String>) {
this.env = env.toMap()
}
@ContainerDSL
fun ContainerConfig.mapRealUid(mapRealUid: Boolean = true) {
this.mapRealUid = mapRealUid
}
@ContainerDSL
fun ContainerConfig.device(device: Boolean = true) {
this.device = device
}
@FilesystemDSL
data class FilesystemConfigs(val configs: MutableList<FilesystemConfig> = mutableListOf())
@ContainerDSL
fun ContainerConfig.filesystem(init: @FilesystemDSL FilesystemConfigs.() -> Unit) {
val config = FilesystemConfigs().apply(init)
this.filesystem = config.configs
}
@FilesystemDSL
data class DummyFSBind(var target: String? = null,
var source: String? = null,
var write: Boolean? = null,
var device: Boolean? = null,
var ensure: Boolean? = null,
var optional: Boolean? = null,
var special: Boolean? = null) {
fun build(): FSBind {
return FSBind(
target = if(target != null) { AbsolutePath(target!!) } else null,
source = AbsolutePath(source!!),
write = write,
device = device,
ensure = ensure,
optional = optional,
special = special
)
}
}
@FilesystemDSL
fun FilesystemConfigs.bind(src2dst: Pair<String, String>, init: @FSBindDSL DummyFSBind.() -> Unit = {}) {
val fs = DummyFSBind(target = src2dst.second, source = src2dst.first)
fs.apply(init)
this.configs.add(fs.build())
}
@FilesystemDSL
fun FilesystemConfigs.bind(source: String, init: @FSBindDSL DummyFSBind.() -> Unit = {}) {
val fs = DummyFSBind(source = source)
fs.apply(init)
this.configs.add(fs.build())
}
@FSBindDSL
fun DummyFSBind.write(write: Boolean? = true) {
this.write = write
}
@FSBindDSL
fun DummyFSBind.device(device: Boolean? = true) {
this.device = device
}
@FSBindDSL
fun DummyFSBind.ensure(ensure: Boolean? = true) {
this.ensure = ensure
}
@FSBindDSL
fun DummyFSBind.optional(optional: Boolean? = true) {
this.optional = optional
}
@FSBindDSL
fun DummyFSBind.special(special: Boolean? = true) {
this.special = special
}
@FilesystemDSL
data class DummyFSEphemeral(val target: String? = null,
var write: Boolean? = null,
var size: Int? = null,
var perm: Int? = null) {
fun build(): FSEphemeral {
return FSEphemeral(
target = AbsolutePath(target!!),
write = write!!,
size = size,
perm = perm!!
)
}
}
@FSEphemeralDSL
fun DummyFSEphemeral.write(write: Boolean = true) {
this.write = write
}
@FSEphemeralDSL
fun DummyFSEphemeral.size(size: Int) {
this.size = size
}
@FSEphemeralDSL
fun DummyFSEphemeral.perm(perm: Int) {
this.perm = perm
}
@FilesystemDSL
fun FilesystemConfigs.ephemeral(target: String, init: @FSEphemeralDSL DummyFSEphemeral.() -> Unit = {}) {
val fs = DummyFSEphemeral(target = target)
fs.apply(init)
this.configs.add(fs.build())
}
@FilesystemDSL
data class DummyFSLink(val target: String? = null,
val linkname: String? = null,
var dereference: Boolean? = null) {
fun build(): FSLink {
return FSLink(
target = AbsolutePath(target!!),
linkname = linkname!!,
dereference = dereference!!
)
}
}
@FSLinkDSL
fun DummyFSLink.dereference(dereference: Boolean = true) {
this.dereference = dereference
}
@FilesystemDSL
fun FilesystemConfigs.link(lnk2dst: Pair<String, String>, init: @FSLinkDSL DummyFSLink.() -> Unit = {}) {
val fs = DummyFSLink(target = lnk2dst.second, linkname = lnk2dst.first)
fs.apply(init)
this.configs.add(fs.build())
}
@FilesystemDSL
fun FilesystemConfigs.link(target: String, init: @FSLinkDSL DummyFSLink.() -> Unit = {}) {
val fs = DummyFSLink(target = target, linkname = target)
fs.apply(init)
this.configs.add(fs.build())
}
@FilesystemDSL
data class DummyFSOverlay(val target: String? = null,
var lower: MutableList<String>? = mutableListOf(),
var upper: String? = null,
var work: String? = null) {
fun build(): FSOverlay {
return FSOverlay(
target = AbsolutePath(target!!),
lower = lower!!.map { AbsolutePath(it)},
upper = AbsolutePath(upper!!),
work = AbsolutePath(work!!)
)
}
}
@FilesystemDSL
fun FilesystemConfigs.overlay(target: String, init: @FSOverlayDSL DummyFSOverlay.() -> Unit = {}) {
val fs = DummyFSOverlay(target = target)
fs.apply(init)
this.configs.add(fs.build())
}
@FSOverlayDSL
fun DummyFSOverlay.lower(vararg lower: String) {
this.lower!!.addAll(lower.toList())
}
@FSOverlayDSL
fun DummyFSOverlay.upper(upper: String) {
this.upper = upper
}
@FSOverlayDSL
fun DummyFSOverlay.work(work: String) {
this.work = work
}