Pure MoonBit D-Bus protocol implementation — connect, authenticate, call methods, and export objects on the session bus. Only ~40 lines of C for the unix socket.
moon add conglinyizhi/moondbusimport {
"conglinyizhi/moondbus/src" @dbus,
}
supported_targets = "+native"///|
test "build and parse a method call" {
// 构造一条方法调用消息
let body = @dbus.build_variant_string("hello")
let msg = @dbus.build_method_call(
"com.example.Service", "/com/example/Object", "com.example.Interface", "Ping",
body, "v", 1,
)
assert_true(msg.length() > 0)
// 解析它的 header 字段
let fields = @dbus.parse_header_fields(msg)
match fields.interface {
Some(i) => assert_eq(i, "com.example.Interface")
None => fail("no interface parsed")
}
match fields.member {
Some(m) => assert_eq(m, "Ping")
None => fail("no member parsed")
}
}///|
fn main {
match @dbus.connect_bus() {
Err(e) => println("connect failed: \{e}")
Ok(conn) => {
match @dbus.hello(conn) {
Err(e) => println("hello failed: \{e}")
Ok(name) => println("our unique name: \{name}") // e.g. ":1.448"
}
// Ask the bus who owns a well-known name
match
@dbus.call_with_string(
conn, "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus",
"GetNameOwner", "org.kde.StatusNotifierWatcher", 2,
) {
Err(e) => println("call failed: \{e}")
Ok(m) =>
match @dbus.read_dbus_string(m.body, 0) {
Some(owner) => println("owner: \{owner}")
None => println("no owner")
}
}
@dbus.close(conn)
}
}
}| Function | Purpose |
|---|---|
| connect_bus() | Connect to the session bus and complete SASL authentication |
| close(conn) | Close the connection |
| send_raw(conn, bytes) | Write raw bytes |
| recv_raw(conn, len) | Read up to len bytes |
| recv_message(conn) | Read exactly one complete D-Bus message |
| getpid() | Current process id (useful for well-known names) |
| Function | Purpose |
|---|---|
| hello(conn) | The mandatory Hello handshake; returns your unique name |
| call(conn, dest, path, iface, member, body, sig, serial) | Full method call |
| call_simple(...) | Method call with no arguments |
| call_with_string(...) | Method call with a single string argument |
| request_name(conn, name, flags, serial) | Claim a well-known bus name |
| Function | Purpose |
|---|---|
| build_method_call(...) | Encode a METHOD_CALL |
| build_method_return(...) | Encode a METHOD_RETURN reply |
| parse_message(bytes) | Decode type / serial / body |
| parse_header_fields(bytes) | Decode path / interface / member / sender / … |
| read_dbus_string(body, off) | Read a string out of a message body |
| build_variant_string(value) | Encode a v holding a string |
| build_dict_sv(entries) | Encode an a{sv} dictionary |
| Method | Type |
|---|---|
| Encoder::new() / new_at(base) | Create; new_at declares the absolute offset |
| byte / u32 / i32 / bool | y / u / i / b |
| string / signature | s,o / g (single-byte length!) |
| variant_string / variant_bool / variant_i32 / variant_object_path | v |
| array_with(align, f) | Array; the callback writes elements at the right offset |
| struct_with(f) | Struct (8-byte aligned) |
| align(n) / pos() / finish() | Manual control |
///|
test "encode a{sv} dictionary" {
let e = @dbus.Encoder::new()
e.array_with(8, fn(dict) {
dict.struct_with(fn(kv) {
kv.string("Title")
kv.variant_string("My App")
})
dict.struct_with(fn(kv) {
kv.string("Menu")
kv.variant_object_path("/MenuBar")
})
})
let body = e.finish()
assert_true(body.length() > 0)
}///|
test "build an a{sv} body for a signal" {
// encode_layout 需要的 body (u ui) 示例:用 build_body_uu 构造
let body = @dbus.build_body_uu(2, 0)
assert_true(body.length() > 0)
}///|
test "server dispatch" {
let r = dispatch_ping("com.example", "Ping")
if r is Some(reply) {
assert_eq(reply.signature, "v")
} else {
fail("expected reply")
}
}
///|
/// 模拟服务分发:Ping 返回 pong,其它不回。
fn dispatch_ping(iface : String, member : String) -> @dbus.Reply? {
if iface == "com.example" && member == "Ping" {
Some(@dbus.Reply::make(@dbus.build_variant_string("pong"), "v"))
} else {
None // unknown method: no reply
}
}Install
Download zipPure MoonBit D-Bus protocol implementation — connect, authenticate, call methods, and export objects on the session bus. Only ~40 lines of C for the unix socket.