///|
pub impl ToJson for FileStat with fn to_json(self) {
{
"file_type": self.file_type,
"size": Json::number(self.size.to_double(), repr=self.size.to_string()),
"mtime": Json::number(self.mtime.to_double(), repr=self.mtime.to_string()),
"ctime": Json::number(self.ctime.to_double(), repr=self.ctime.to_string()),
}
}
///|
fn get_int64_field(
m : Map[String, Json],
key : String,
path : @json.JsonPath,
) -> Int64 raise @json.JsonDecodeError {
match m.get(key) {
Some(Number(n, ..)) => n.to_int64()
Some(String(s)) =>
@string.parse_int64(s) catch {
_ => raise JsonDecodeError((path, "invalid int64 for '\{key}'"))
}
_ => raise JsonDecodeError((path, "missing field '\{key}'"))
}
}
///|
pub impl @json.FromJson for FileStat with fn from_json(json, path) {
guard json is Object(m) else {
raise JsonDecodeError((path, "expected object for FileStat"))
}
guard m.get("file_type") is Some(Number(ft, ..)) else {
raise JsonDecodeError((path, "missing field 'file_type'"))
}
{
file_type: ft.to_int(),
size: get_int64_field(m, "size", path),
mtime: get_int64_field(m, "mtime", path),
ctime: get_int64_field(m, "ctime", path),
}
}
///|
pub impl ToJson for FsMessage with fn to_json(self) {
match self {
WatchFile(req_id~, client_id~, path~) =>
{
"ch": "fs",
"type": "watch_file",
"req_id": req_id,
"client_id": client_id,
"path": path,
}
UnwatchFile(req_id~, client_id~) =>
{
"ch": "fs",
"type": "unwatch_file",
"req_id": req_id,
"client_id": client_id,
}
WatchFileReady(req_id~, client_id~, path~, resolved_path~) =>
{
"ch": "fs",
"type": "watch_file_ready",
"req_id": req_id,
"client_id": client_id,
"path": path,
"resolved_path": resolved_path,
}
FileChanged(req_id~, client_id~, path~, resolved_path~) =>
{
"ch": "fs",
"type": "file_changed",
"req_id": req_id,
"client_id": client_id,
"path": path,
"resolved_path": resolved_path,
}
Stat(req_id~, client_id~, path~) =>
{
"ch": "fs",
"type": "stat",
"req_id": req_id,
"client_id": client_id,
"path": path,
}
StatResult(req_id~, client_id~, stat~) =>
{
"ch": "fs",
"type": "stat_result",
"req_id": req_id,
"client_id": client_id,
"stat": stat.to_json(),
}
Readdir(req_id~, client_id~, path~) =>
{
"ch": "fs",
"type": "readdir",
"req_id": req_id,
"client_id": client_id,
"path": path,
}
ReaddirResult(req_id~, client_id~, entries~) => {
let arr : Json = entries
.map(fn(e) {
let j : Json = [e.0, e.1]
j
})
.to_json()
{
"ch": "fs",
"type": "readdir_result",
"req_id": req_id,
"client_id": client_id,
"entries": arr,
}
}
ReadFile(req_id~, client_id~, path~) =>
{
"ch": "fs",
"type": "read_file",
"req_id": req_id,
"client_id": client_id,
"path": path,
}
ReadFileResult(req_id~, client_id~, data~) =>
{
"ch": "fs",
"type": "read_file_result",
"req_id": req_id,
"client_id": client_id,
"data": data,
}
WriteFile(req_id~, client_id~, path~, data~, create~, overwrite~) =>
{
"ch": "fs",
"type": "write_file",
"req_id": req_id,
"client_id": client_id,
"path": path,
"data": data,
"create": create,
"overwrite": overwrite,
}
Mkdir(req_id~, client_id~, path~) =>
{
"ch": "fs",
"type": "mkdir",
"req_id": req_id,
"client_id": client_id,
"path": path,
}
FsDelete(req_id~, client_id~, path~, recursive~) =>
{
"ch": "fs",
"type": "delete",
"req_id": req_id,
"client_id": client_id,
"path": path,
"recursive": recursive,
}
FsRename(req_id~, client_id~, from~, to~, overwrite~) =>
{
"ch": "fs",
"type": "rename",
"req_id": req_id,
"client_id": client_id,
"from": from,
"to": to,
"overwrite": overwrite,
}
FsOk(req_id~, client_id~) =>
{ "ch": "fs", "type": "ok", "req_id": req_id, "client_id": client_id }
FsError(req_id~, client_id~, code~, message~) =>
{
"ch": "fs",
"type": "error",
"req_id": req_id,
"client_id": client_id,
"code": code,
"message": message,
}
}
}
///|
pub impl @json.FromJson for FsMessage with fn from_json(json, path) {
guard json is Object(fields) else {
raise JsonDecodeError((path, "expected JSON object"))
}
guard fields.get("type") is Some(String(t)) else {
raise JsonDecodeError((path, "missing field 'type'"))
}
guard fields.get("req_id") is Some(String(req_id)) else {
raise JsonDecodeError((path, "missing field 'req_id'"))
}
let client_id = match fields.get("client_id") {
Some(String(s)) => s
_ => ""
}
match t {
"watch_file" => {
guard fields.get("path") is Some(String(p)) else {
raise JsonDecodeError((path, "missing field 'path'"))
}
WatchFile(req_id~, client_id~, path=p)
}
"unwatch_file" => UnwatchFile(req_id~, client_id~)
"watch_file_ready" => {
guard fields.get("path") is Some(String(p)) else {
raise JsonDecodeError((path, "missing field 'path'"))
}
// Older protocol-v4 peers sent only `path`. Treat it as the effective
// path so the additive canonical identity stays wire-compatible.
let resolved_path = match fields.get("resolved_path") {
Some(String(value)) => value
_ => p
}
WatchFileReady(req_id~, client_id~, path=p, resolved_path~)
}
"file_changed" => {
guard fields.get("path") is Some(String(p)) else {
raise JsonDecodeError((path, "missing field 'path'"))
}
let resolved_path = match fields.get("resolved_path") {
Some(String(value)) => value
_ => p
}
FileChanged(req_id~, client_id~, path=p, resolved_path~)
}
"stat" => {
guard fields.get("path") is Some(String(p)) else {
raise JsonDecodeError((path, "missing field 'path'"))
}
Stat(req_id~, client_id~, path=p)
}
"stat_result" => {
guard fields.get("stat") is Some(stat_json) else {
raise JsonDecodeError((path, "missing field 'stat'"))
}
let stat : FileStat = @json.from_json(
stat_json,
path=path.add_key("stat"),
)
StatResult(req_id~, client_id~, stat~)
}
"readdir" => {
guard fields.get("path") is Some(String(p)) else {
raise JsonDecodeError((path, "missing field 'path'"))
}
Readdir(req_id~, client_id~, path=p)
}
"readdir_result" => {
guard fields.get("entries") is Some(Array(arr)) else {
raise JsonDecodeError((path, "missing field 'entries'"))
}
let entries : Array[(String, Int)] = []
for item in arr {
guard item is Array([String(name), Number(ft, ..)]) else {
raise JsonDecodeError((path, "invalid entry in entries"))
}
entries.push((name, ft.to_int()))
}
ReaddirResult(req_id~, client_id~, entries~)
}
"read_file" => {
guard fields.get("path") is Some(String(p)) else {
raise JsonDecodeError((path, "missing field 'path'"))
}
ReadFile(req_id~, client_id~, path=p)
}
"read_file_result" => {
guard fields.get("data") is Some(String(data)) else {
raise JsonDecodeError((path, "missing field 'data'"))
}
ReadFileResult(req_id~, client_id~, data~)
}
"write_file" => {
guard fields.get("path") is Some(String(p)) else {
raise JsonDecodeError((path, "missing field 'path'"))
}
guard fields.get("data") is Some(String(data)) else {
raise JsonDecodeError((path, "missing field 'data'"))
}
let create = match fields.get("create") {
Some(True) => true
_ => false
}
let overwrite = match fields.get("overwrite") {
Some(True) => true
_ => false
}
WriteFile(req_id~, client_id~, path=p, data~, create~, overwrite~)
}
"mkdir" => {
guard fields.get("path") is Some(String(p)) else {
raise JsonDecodeError((path, "missing field 'path'"))
}
Mkdir(req_id~, client_id~, path=p)
}
"delete" => {
guard fields.get("path") is Some(String(p)) else {
raise JsonDecodeError((path, "missing field 'path'"))
}
let recursive = match fields.get("recursive") {
Some(True) => true
_ => false
}
FsDelete(req_id~, client_id~, path=p, recursive~)
}
"rename" => {
guard fields.get("from") is Some(String(from)) else {
raise JsonDecodeError((path, "missing field 'from'"))
}
guard fields.get("to") is Some(String(to)) else {
raise JsonDecodeError((path, "missing field 'to'"))
}
let overwrite = match fields.get("overwrite") {
Some(True) => true
_ => false
}
FsRename(req_id~, client_id~, from~, to~, overwrite~)
}
"ok" => FsOk(req_id~, client_id~)
"error" => {
guard fields.get("code") is Some(String(code)) else {
raise JsonDecodeError((path, "missing field 'code'"))
}
guard fields.get("message") is Some(String(message)) else {
raise JsonDecodeError((path, "missing field 'message'"))
}
FsError(req_id~, client_id~, code~, message~)
}
_ => raise JsonDecodeError((path, "unknown FsMessage type: \{t}"))
}
}