///|
/// 实时数据库 CLI。
/// - 无参数:运行内置演示。
/// - `moon run src/cmd/main -- "cmd1" "cmd2" ...`:逐条执行命令。
///
/// 命令:
/// set [ttl] setnx [ttl]
/// get del incr [delta] decr [delta]
/// keys size clock entries snapshot clear
/// touch persist
/// watch watchp watchall unwatch
/// advance expire help quit
fn main {
let args = @env.args()
let cmds : Array[String] = []
for i, a in args {
if i > 0 {
cmds.push(a)
}
}
if cmds.length() == 0 {
run_demo()
return
}
let db = @db.DB::new()
println("=== MoonBit 实时数据库 REPL ===")
for cmd in cmds {
println("> \{cmd}")
let cont = run_command(db, cmd)
if !cont {
break
}
}
println("=== 结束 ===")
}
///|
/// 执行单条命令。返回 false 表示 quit。
fn run_command(db : @db.DB, line : String) -> Bool {
let tokens = tokenize(line)
if tokens.length() == 0 {
return true
}
match tokens[0] {
"set" => {
guard tokens.length() >= 3 else {
println(" usage: set [ttl]")
return true
}
let k = tokens[1]
let v = parse_value(tokens[2])
if tokens.length() >= 4 {
db.set(k, v, ttl=parse_int_or(tokens[3], 0))
} else {
db.set(k, v)
}
println(" ok")
true
}
"get" => {
guard tokens.length() >= 2 else {
println(" usage: get ")
return true
}
match db.get(tokens[1]) {
Some(v) => println(" \{v.to_string()}")
None => println(" (nil)")
}
true
}
"del" => {
guard tokens.length() >= 2 else {
println(" usage: del ")
return true
}
let _ = db.delete(tokens[1])
println(" ok")
true
}
"keys" => {
for k in db.keys() {
println(" \{k}")
}
true
}
"size" => {
println(" \{db.size()}")
true
}
"clock" => {
println(" \{db.clock()}")
true
}
"watch" => {
guard tokens.length() >= 2 else {
println(" usage: watch ")
return true
}
let id = db.watch(tokens[1], fn(e) {
println(" >> [watch] \{e.to_string()}")
})
println(" watcher#\{id} watching \{tokens[1]}")
true
}
"watchp" => {
guard tokens.length() >= 2 else {
println(" usage: watchp ")
return true
}
let id = db.watch_prefix(tokens[1], fn(e) {
println(" >> [prefix] \{e.to_string()}")
})
println(" watcher#\{id} watching prefix \{tokens[1]}")
true
}
"watchall" => {
let id = db.watch_all(fn(e) { println(" >> [global] \{e.to_string()}") })
println(" watcher#\{id} watching all")
true
}
"unwatch" => {
guard tokens.length() >= 2 else {
println(" usage: unwatch ")
return true
}
let id = parse_int_or(tokens[1], -1)
println(" unwatch: \{db.unwatch(id)}")
true
}
"advance" => {
guard tokens.length() >= 2 else {
println(" usage: advance ")
return true
}
let expired = db.advance(parse_int_or(tokens[1], 0))
println(" clock=\{db.clock()}, expired=\{expired}")
true
}
"expire" => {
println(" expired=\{db.expire_now()}")
true
}
"snapshot" => {
println(
" size=\{db.size()}, watchers=\{db.watcher_count()}, clock=\{db.clock()}",
)
for k in db.keys() {
match db.get(k) {
Some(v) => println(" \{k} = \{v.to_string()}")
None => ()
}
}
true
}
"entries" => {
for (k, v) in db.entries() {
println(" \{k} = \{v.to_string()}")
}
true
}
"setnx" => {
guard tokens.length() >= 3 else {
println(" usage: setnx [ttl]")
return true
}
let k = tokens[1]
let v = parse_value(tokens[2])
let ok = if tokens.length() >= 4 {
db.setnx(k, v, ttl=parse_int_or(tokens[3], 0))
} else {
db.setnx(k, v)
}
println(" setnx=\{ok}")
true
}
"incr" => {
guard tokens.length() >= 2 else {
println(" usage: incr [delta]")
return true
}
let ok = if tokens.length() >= 3 {
db.incr(tokens[1], delta=parse_int_or(tokens[2], 1))
} else {
db.incr(tokens[1])
}
println(" incr=\{ok}")
true
}
"decr" => {
guard tokens.length() >= 2 else {
println(" usage: decr [delta]")
return true
}
let ok = if tokens.length() >= 3 {
db.decr(tokens[1], delta=parse_int_or(tokens[2], 1))
} else {
db.decr(tokens[1])
}
println(" decr=\{ok}")
true
}
"touch" => {
guard tokens.length() >= 3 else {
println(" usage: touch ")
return true
}
println(" touch=\{db.touch(tokens[1], parse_int_or(tokens[2], 0))}")
true
}
"persist" => {
guard tokens.length() >= 2 else {
println(" usage: persist ")
return true
}
println(" persist=\{db.persist(tokens[1])}")
true
}
"clear" => {
db.clear()
println(" ok")
true
}
"help" => {
print_help()
true
}
"quit" => {
println(" bye")
false
}
cmd => {
println(" unknown command: \{cmd} (try help)")
true
}
}
}
///|
/// 按空格切分命令行,去空 token。
fn tokenize(line : String) -> Array[String] {
let parts : Array[String] = []
for p in line.split(" ") {
let t = p.trim().to_owned()
if t.length() > 0 {
parts.push(t)
}
}
parts
}
///|
/// 解析字符串为 Value:Int > Float > Bool > Null > Str。
fn parse_value(s : String) -> @db.Value {
let sv = s.to_string_view()
try {
let i = @string.parse_int(sv)
return @db.Int(i)
} catch {
_ => ()
}
try {
let f = @string.parse_double(sv)
return @db.Float(f)
} catch {
_ => ()
}
if s == "true" {
return @db.Bool(true)
}
if s == "false" {
return @db.Bool(false)
}
if s == "null" {
return @db.Null
}
@db.Str(s)
}
///|
/// 解析 Int,失败返回 default。
fn parse_int_or(s : String, default : Int32) -> Int32 {
@string.parse_int(s.to_string_view()) catch {
_ => default
}
}
///|
fn print_help() -> Unit {
println(" commands:")
println(" set [ttl] setnx [ttl]")
println(" get del incr [delta] decr [delta]")
println(" keys size clock entries snapshot clear")
println(" touch persist ")
println(" watch watchp watchall unwatch ")
println(" advance expire quit help")
println(
" value: 42->Int 3.14->Float true/false->Bool null->Null else->Str",
)
}
///|
/// 内置演示:展示 watch/watch_all/watch_prefix/unwatch/TTL 的实时通知。
fn run_demo() -> Unit {
let db = @db.DB::new()
println("=== MoonBit 实时数据库演示 ===")
println("")
println("[1] watch(\"counter\")")
let w_counter = db.watch("counter", fn(e) {
println(" >> [counter] \{e.to_string()}")
})
let _ = w_counter
println("[2] set counter = 1")
db.set("counter", @db.Int(1))
println("[3] set counter = 2")
db.set("counter", @db.Int(2))
println("[4] watch_prefix(\"user:\")")
let _ = db.watch_prefix("user:", fn(e) {
println(" >> [prefix] \{e.to_string()}")
})
println("[5] set user:1 = 99")
db.set("user:1", @db.Int(99))
println("[6] watch_all()")
let _ = db.watch_all(fn(e) { println(" >> [global] \{e.to_string()}") })
println("[7] set temp = 3.14 (Float)")
db.set("temp", @db.Float(3.14))
println("[8] set sess = x with ttl=3")
db.set("sess", @db.Str("x"), ttl=3)
println("[9] advance(3) —— sess 过期")
let expired = db.advance(3)
println(" expired=\{expired}")
println("[10] delete counter")
let _ = db.delete("counter")
println("[11] unwatch(w_counter)")
let _ = db.unwatch(w_counter)
println("[12] set counter = 3 —— 仅 prefix(不匹配)/global 收到")
db.set("counter", @db.Int(3))
println("[13] snapshot")
println(
" size=\{db.size()}, watchers=\{db.watcher_count()}, clock=\{db.clock()}",
)
for k in db.keys() {
match db.get(k) {
Some(v) => println(" \{k} = \{v.to_string()}")
None => ()
}
}
println("")
println("=== 演示结束 ===")
}