package cjTauri
import std.collection.*
import std.fs.*
import stdx.encoding.json.*
/**
* 官方插件 fs:文件读写(纯仓颉,零 C 桥改动,RFC-001 §7.3)
*
* 命令:fs:readText / fs:writeText / fs:exists
* 权限:**不自动放行**,应用要在 capabilities 里显式声明,两种写法等价:
* "commands": ["fs:readText", "fs:exists"] // 明文逐条(旧写法,一直支持)
* "permissions": ["fs:readonly"] // 命名权限集(v1.1,一次放行一组)
* 前端:window.__CJ_TAURI__.fs.readText({ path: "notes.txt" })
*/
public class FsPlugin <: Plugin {
public init() {}
public func name(): String {
return "fs"
}
public func commands(): HashMap<String, CommandHandler> {
let m = HashMap<String, CommandHandler>()
m.add("readText", FsCommands())
m.add("writeText", FsCommands())
m.add("exists", FsCommands())
return m
}
/**
* 命名权限集(v1.1,对标 Tauri v2 的 fs:default / fs:allow-read-text-file)。
*
* readonly 只含读相关两条:只想读文件的应用引用它即可,**写能力不会被顺带打开**;
* 要写文件必须显式引用 default(或把 fs:writeText 写成明文)——最小权限不打折。
* 集内写短名(readText),框架自动补成 fs:readText。
*/
public func permissions(): HashMap<String, ArrayList<String>> {
let m = HashMap<String, ArrayList<String>>()
let ro = ArrayList<String>()
ro.add("readText")
ro.add("exists")
m.add("readonly", ro)
let all = ArrayList<String>()
all.add("readText")
all.add("writeText")
all.add("exists")
m.add("default", all)
return m
}
/**
* 前端 shim:往桥对象上挂一个 fs 命名空间。
* 只动 __CJ_TAURI__ 的一个命名空间,不碰 invoke / listen / emit,也不注册全局变量。
*/
public func jsShim(): String {
return "(function () { var t = window.__CJ_TAURI__ || (window.__CJ_TAURI__ = {});" +
" t.fs = {" +
" readText: function (args) { return window.__CJ_TAURI__.invoke('fs:readText', args); }," +
" writeText: function (args) { return window.__CJ_TAURI__.invoke('fs:writeText', args); }," +
" exists: function (args) { return window.__CJ_TAURI__.invoke('fs:exists', args); }" +
" }; })();\n"
}
}
/**
* fs 插件的命令处理器(三个命令共用一份,按全名分派,与内置 SystemCommands 同形)
*/
class FsCommands <: CommandHandler {
public init() {}
public func handle(cmd: String, args: JsonObject, ipc: IpcContext): JsonValue {
match (cmd) {
case "fs:readText" => this.readText(args)
case "fs:writeText" => this.writeText(args)
case "fs:exists" => this.exists(args)
case _ => throw CommandException("unknown fs command: ${cmd}")
}
}
/** 取必需的字符串参数;缺失或类型不对时抛业务错误,前端拿到的是可读原文 */
private func requireString(args: JsonObject, key: String): String {
match (args.get(key)) {
case Some(v) => match (v.kind()) {
case JsonKind.JsString => return v.asString().getValue()
case _ => throw CommandException("fs: argument \"${key}\" must be a string")
}
case None => throw CommandException("fs: missing argument \"${key}\"")
}
}
private func readText(args: JsonObject): JsonValue {
let path = this.requireString(args, "path")
try {
return JsonString(String.fromUtf8(File.readFrom(path)))
} catch (e: Exception) {
throw CommandException("fs:readText failed: ${e}")
}
}
private func writeText(args: JsonObject): JsonValue {
let path = this.requireString(args, "path")
let contents = this.requireString(args, "contents")
try {
// File.writeTo 是覆盖写:文件已存在会截断,不存在则创建——不需要先删(与 File.create 不同)
File.writeTo(path, contents.toArray())
return JsonBool(true)
} catch (e: Exception) {
throw CommandException("fs:writeText failed: ${e}")
}
}
private func exists(args: JsonObject): JsonValue {
let path = this.requireString(args, "path")
return JsonBool(exists(path))
}
}