cj-tauri 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +113 -99
- package/bin/cj-tauri.js +287 -287
- package/cjpm.toml +63 -52
- package/cli/cjpm.toml +22 -22
- package/cli/src/doctor.cj +308 -306
- package/cli/src/main.cj +129 -129
- package/cli/src/manifest.cj +632 -558
- package/cli/src/manifest_test.cj +87 -0
- package/cli/src/project.cj +770 -770
- package/cli/src/resolve.cj +348 -348
- package/cli/src/scaffold.cj +369 -369
- package/cli/src/sync.cj +526 -526
- package/cli/src/util.cj +168 -168
- package/cli/templates/app/README.md +51 -48
- package/cli/templates/app/assets/capabilities/default.json +7 -7
- package/cli/templates/app/assets/ui/index.html +120 -118
- package/cli/templates/app/cj-tauri.toml +32 -22
- package/cli/templates/app/cjpm.toml +34 -34
- package/cli/templates/app/src/main.cj +101 -90
- package/cli/templates/app-react/README.md +56 -56
- package/cli/templates/app-react/assets/capabilities/default.json +7 -7
- package/cli/templates/app-react/assets/ui/index.html +25 -25
- package/cli/templates/app-react/assets/ui/package.json +19 -19
- package/cli/templates/app-react/assets/ui/src/App.css +67 -67
- package/cli/templates/app-react/assets/ui/src/App.jsx +124 -124
- package/cli/templates/app-react/assets/ui/src/main.jsx +6 -6
- package/cli/templates/app-react/assets/ui/vite.config.js +24 -24
- package/cli/templates/app-react/cj-tauri.toml +33 -23
- package/cli/templates/app-react/cjpm.toml +34 -34
- package/cli/templates/app-react/src/main.cj +91 -91
- package/cli/templates/app-vue/README.md +56 -56
- package/cli/templates/app-vue/assets/capabilities/default.json +7 -7
- package/cli/templates/app-vue/assets/ui/index.html +25 -25
- package/cli/templates/app-vue/assets/ui/package.json +18 -18
- package/cli/templates/app-vue/assets/ui/src/App.vue +176 -176
- package/cli/templates/app-vue/assets/ui/src/main.js +4 -4
- package/cli/templates/app-vue/assets/ui/vite.config.js +24 -24
- package/cli/templates/app-vue/cj-tauri.toml +33 -23
- package/cli/templates/app-vue/cjpm.toml +34 -34
- package/cli/templates/app-vue/src/main.cj +91 -91
- package/cli/templates/ohos-skeleton/AppScope/app.json5 +13 -13
- package/cli/templates/ohos-skeleton/AppScope/resources/base/element/string.json +8 -8
- package/cli/templates/ohos-skeleton/AppScope/resources/base/media/layered_image.json +6 -6
- package/cli/templates/ohos-skeleton/README.md +84 -79
- package/cli/templates/ohos-skeleton/build-hap.sh +15 -0
- package/cli/templates/ohos-skeleton/build-profile.json5 +46 -46
- package/cli/templates/ohos-skeleton/entry/build-profile.json5 +26 -26
- package/cli/templates/ohos-skeleton/entry/cjpm.toml +51 -43
- package/cli/templates/ohos-skeleton/entry/hvigorfile.ts +6 -6
- package/cli/templates/ohos-skeleton/entry/oh-package.json5 +9 -9
- package/cli/templates/ohos-skeleton/entry/src/main/cangjie/ability_stage.cj +17 -17
- package/cli/templates/ohos-skeleton/entry/src/main/cangjie/app_host.cj +94 -94
- package/cli/templates/ohos-skeleton/entry/src/main/cangjie/dialog_host.cj +217 -0
- package/cli/templates/ohos-skeleton/entry/src/main/cangjie/index.cj +527 -167
- package/cli/templates/ohos-skeleton/entry/src/main/cangjie/main_ability.cj +139 -139
- package/cli/templates/ohos-skeleton/entry/src/main/module.json5 +57 -49
- package/cli/templates/ohos-skeleton/entry/src/main/resources/base/element/color.json +8 -8
- package/cli/templates/ohos-skeleton/entry/src/main/resources/base/element/string.json +16 -16
- package/cli/templates/ohos-skeleton/entry/src/main/resources/base/media/layered_image.json +6 -6
- package/cli/templates/ohos-skeleton/entry/src/main/resources/base/profile/main_pages.json +2 -2
- package/cli/templates/ohos-skeleton/entry/src/main/resources/rawfile/capabilities.json +6 -6
- package/cli/templates/ohos-skeleton/entry/src/main/resources/rawfile/index.html +92 -92
- package/cli/templates/ohos-skeleton/hvigor/hvigor-config.json5 +5 -5
- package/cli/templates/ohos-skeleton/hvigorfile.ts +6 -6
- package/cli/templates/ohos-skeleton/oh-package.json5 +10 -10
- package/native/build_win.bat +62 -62
- package/native/core/bridge_core.c +1681 -1626
- package/native/core/bridge_core.h +675 -618
- package/native/core/bridge_js.h +108 -108
- package/native/platforms/harmonyos/README.md +115 -6
- package/native/platforms/linux/bridge_linux.c +2787 -2485
- package/native/platforms/windows/bridge_win.c +3740 -3248
- package/native/tests/gtk_threading_probe.c +143 -143
- package/native/tests/test_bridge_core.c +1339 -1228
- package/package.json +53 -53
- package/prebuilt/linux-x64/cj-tauri +0 -0
- package/src/api_system.cj +67 -67
- package/src/app.cj +325 -969
- package/src/app_events.cj +166 -0
- package/src/app_host_ops.cj +287 -0
- package/src/app_run.cj +283 -0
- package/src/app_spec.cj +76 -76
- package/src/arkweb_menu.cj +216 -0
- package/src/arkweb_script.cj +142 -142
- package/src/capability.cj +409 -409
- package/src/clipboard_bridge.cj +178 -0
- package/src/dialog_spec.cj +79 -0
- package/src/dragdrop.cj +26 -26
- package/src/embedded_assets.cj +98 -98
- package/src/host.cj +752 -607
- package/src/host_arkweb.cj +924 -778
- package/src/host_webkit.cj +663 -570
- package/src/host_webview2.cj +663 -570
- package/src/hotkey.cj +66 -66
- package/src/ipc_hub.cj +337 -337
- package/src/ipc_message.cj +286 -286
- package/src/launch.cj +62 -62
- package/src/logging.cj +127 -127
- package/src/menu.cj +197 -197
- package/src/platform/platform_app_context.cj +77 -77
- package/src/platform/platform_assets.cj +33 -33
- package/src/platform/platform_discover.cj +176 -176
- package/src/platform/platform_env.cj +29 -29
- package/src/platform/platform_feature.cj +31 -31
- package/src/platform/platform_page_source.cj +87 -87
- package/src/platform/platform_target_info.cj +32 -32
- package/src/platform_host_provider.cj +87 -87
- package/src/plugin.cj +333 -333
- package/src/plugins/clipboard/plugin_clipboard.cj +152 -152
- package/src/plugins/dialog/plugin_dialog.cj +209 -209
- package/src/plugins/fs/plugin_fs.cj +105 -1
- package/src/plugins/http/http_backend.cj +131 -0
- package/src/plugins/http/http_desktop.cj +195 -0
- package/src/plugins/http/http_ohos.cj +204 -0
- package/src/plugins/http/plugin_http.cj +310 -459
- package/src/plugins/log/plugin_log.cj +105 -105
- package/src/plugins/menu/plugin_menu.cj +158 -158
- package/src/plugins/notification/plugin_notification.cj +154 -154
- package/src/plugins/serial/plugin_serial.cj +458 -454
- package/src/plugins/serial/serial_bridge.cj +194 -194
- package/src/plugins/serial/serial_manager_ohos.cj +92 -0
- package/src/plugins/serial/serial_ohos.cj +529 -0
- package/src/plugins/shell/plugin_shell.cj +258 -258
- package/src/plugins/sqlite/plugin_sqlite.cj +705 -710
- package/src/plugins/sqlite/sqlite_ffi.cj +206 -211
- package/src/plugins/ws/plugin_ws.cj +488 -470
- package/src/process_hidden.cj +174 -174
- package/src/scope.cj +391 -391
- package/src/shell_event.cj +69 -69
- package/src/tests/app_spec_test.cj +126 -127
- package/src/tests/capability_test.cj +243 -243
- package/src/tests/dialog_spec_test.cj +73 -0
- package/src/tests/fake_host.cj +201 -201
- package/src/tests/ipc_hub_test.cj +487 -487
- package/src/tests/logging_test.cj +62 -62
- package/src/tests/platform_contract_test.cj +251 -251
- package/src/tests/plugin_clipboard_test.cj +222 -222
- package/src/tests/plugin_dialog_test.cj +237 -237
- package/src/tests/plugin_fs_test.cj +207 -0
- package/src/tests/plugin_http_test.cj +124 -124
- package/src/tests/plugin_notification_test.cj +205 -205
- package/src/tests/plugin_serial_test.cj +48 -0
- package/src/tests/plugin_test.cj +605 -585
- package/src/tests/plugin_ws_test.cj +40 -40
- package/src/tests/process_hidden_test.cj +129 -129
- package/src/tests/scope_test.cj +350 -350
- package/src/tests/system_host_test.cj +99 -81
- package/src/tests/urlutil_test.cj +53 -53
- package/src/tests/version_test.cj +75 -75
- package/src/tests/window_registry_test.cj +123 -123
- package/src/tray.cj +90 -90
- package/src/urlutil.cj +159 -159
- package/src/version.cj +44 -44
- package/src/window.cj +51 -51
- package/src/window_registry.cj +116 -116
- package/prebuilt/win32-x64/cj-tauri.exe +0 -0
- package/src/plugins/serial/serial_ohos_stub.cj +0 -61
- package/src/plugins/sqlite/sqlite_ohos_stub.cj +0 -91
package/src/app_run.cj
ADDED
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
package cjTauri
|
|
2
|
+
|
|
3
|
+
import std.collection.*
|
|
4
|
+
import std.env.*
|
|
5
|
+
import stdx.encoding.json.*
|
|
6
|
+
import cjTauri.platform.*
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* TauriApp 的启动、退出与清单导出(`src/app.cj` 的同包分片)
|
|
10
|
+
*
|
|
11
|
+
* 为什么拆出来:这里是「一次 run 的完整节奏」——prepare 装配 → 插件 shim → 默认窗口 →
|
|
12
|
+
* 附加窗口 → 等事件循环退出 → 收宿主编,外加 CLI 取清单用的 `CJ_TAURI_MANIFEST` 通道。
|
|
13
|
+
* 它与「业务怎么描述应用」(app.cj 的配置 API)和「事件怎么走」(app_events.cj)是两件事。
|
|
14
|
+
*
|
|
15
|
+
* 拆开只是同包换文件:包边界与对外 API 一行未变(`extend` 补齐成员)。
|
|
16
|
+
* 同包拆分的代价(有意接受):`extend` 碰不到 `private` 成员,被跨文件使用的字段与少数内部
|
|
17
|
+
* 方法在 `app.cj` 里放宽成包内可见——对包外(应用 / 插件 / 测试子包)完全不可见。
|
|
18
|
+
*/
|
|
19
|
+
extend TauriApp {
|
|
20
|
+
/**
|
|
21
|
+
* 启动应用:装配 IPC → 启动 WebView 宿主 → 阻塞运行(兼容入口,等价 run(Inline(html)))
|
|
22
|
+
*/
|
|
23
|
+
public func run(html: String): Unit {
|
|
24
|
+
this.run(PageSource.Inline(html))
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 按 URL 启动应用(兼容入口,等价 run(Url(url)))
|
|
29
|
+
*
|
|
30
|
+
* 与 run(html) 平行:同一段装配,只是页面来源换成 URL。有了真实来源,
|
|
31
|
+
* 相对路径资源、动态 import、前端路由才成立(file:// 页面禁止 fetch/XHR)。
|
|
32
|
+
* 开发时可配合 TauriApp.devUrl() 指向前端 dev server(Vite 的 HMR 就这样接进来)。
|
|
33
|
+
*/
|
|
34
|
+
public func runUrl(url: String): Unit {
|
|
35
|
+
this.run(PageSource.Url(url))
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* 统一页面入口(批次 1,RFC-002 §4.3):业务与清单只写 PageSource 一种表达
|
|
40
|
+
*
|
|
41
|
+
* - Inline(html):直接给 HTML(探针、错误页)
|
|
42
|
+
* - Asset(name):逻辑名,经注入的 AssetResolver 解析;未注入时按 cwd 相对路径读盘;
|
|
43
|
+
* devMode(CJ_TAURI_DEV_URL 存在)下自动改写为 DevServer(RFC §4.3)
|
|
44
|
+
* - Url(url) / DevServer(url):直载
|
|
45
|
+
*
|
|
46
|
+
* 装配流程与旧入口完全一致:dumpManifest → 插件 shim → 默认窗口 → 附加窗口 → 等待退出。
|
|
47
|
+
*/
|
|
48
|
+
public func run(source: PageSource): Unit {
|
|
49
|
+
let src = rewriteForDev(source, TauriApp.devUrl())
|
|
50
|
+
this.runWith({ h => h.load(src) })
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** 装配与启动的共同骨架:所有页面入口在 load(source) 收敛,宿主怎么落地是 L0 的事(RFC §4.3/§6.2) */
|
|
54
|
+
private func runWith(start: (WebViewHost) -> Unit): Unit {
|
|
55
|
+
this.prepare()
|
|
56
|
+
if (this.dumpManifestIfRequested()) {
|
|
57
|
+
return
|
|
58
|
+
}
|
|
59
|
+
this.registerPluginScripts()
|
|
60
|
+
this.registerDefaultWindow()
|
|
61
|
+
// 资产解析器随装配进宿主:桌面读盘、鸿蒙 rawfile 材化,各端 load() 自决怎么用
|
|
62
|
+
this.host.setAssetResolver(this.assetResolver)
|
|
63
|
+
start(this.host)
|
|
64
|
+
this.startExtraWindows()
|
|
65
|
+
this.waitForExit()
|
|
66
|
+
// 事件循环已退出:释放全部宿主编(幂等;句柄由 C 侧回收,见 docs/架构演进-多平台与多窗口.md §6.1)
|
|
67
|
+
this.disposeWindows()
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* 清单导出(RFC §8 第 7 条的遗留项:让 `cj-tauri info` 消费 describePluginsJson)
|
|
72
|
+
*
|
|
73
|
+
* 设了 CJ_TAURI_MANIFEST 时,装配一完成就把清单打到 stdout 并返回 true——**不创建窗口**。
|
|
74
|
+
* 于是 CLI / CI 在既没有 X 显示、也没装 WebView2 Runtime 的机器上照样能拿到真实的装配结果。
|
|
75
|
+
* 为什么只能这样拿:CLI 进程里没有应用自己的 TauriApp,插件是应用 main() 里装配的,
|
|
76
|
+
* 所以「不启动窗口,但把 main() 跑到装配完成」是唯一能取到真值的路子(详见 manifestDumpMode)。
|
|
77
|
+
*/
|
|
78
|
+
private func dumpManifestIfRequested(): Bool {
|
|
79
|
+
match (manifestDumpMode()) {
|
|
80
|
+
case Some(mode) =>
|
|
81
|
+
// flush 显式给:CLI 用管道抓 stdout,不能指望进程退出时的缓冲冲刷
|
|
82
|
+
print(renderPluginManifest(this.plugins, mode), flush: true)
|
|
83
|
+
eprintln("[cj-tauri] 已导出插件清单(mode=${mode}):本轮未创建窗口")
|
|
84
|
+
return true
|
|
85
|
+
case None => return false
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* 把插件 jsShim 注册到宿主的「预执行脚本」通道
|
|
91
|
+
*
|
|
92
|
+
* 必须在 load 之前调用:宿主创建 WebView 时一次性交给 C 桥,
|
|
93
|
+
* 桥以 document-start 注入(早于页面脚本,两平台语义一致)。
|
|
94
|
+
* HTML 与 URL 两种来源共用这条通道——v1 那个「runUrl 模式下 shim 注入不进去」的局限就此消掉(RFC §5.4),
|
|
95
|
+
* 所以这里不再需要对使用者的额外提醒。
|
|
96
|
+
*/
|
|
97
|
+
private func registerPluginScripts(): Unit {
|
|
98
|
+
for (js in this.pluginScripts) {
|
|
99
|
+
this.host.addInitScript(js)
|
|
100
|
+
}
|
|
101
|
+
if (this.pluginScripts.size > 0) {
|
|
102
|
+
eprintln("[cj-tauri] 插件 JS 已注册为预执行脚本:${this.pluginScripts.size} 段")
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* 启动前的装配:内置命令、capability、宿主回调与 sink
|
|
108
|
+
*/
|
|
109
|
+
private func prepare(): Unit {
|
|
110
|
+
if (this.started) {
|
|
111
|
+
throw CommandException("app already started")
|
|
112
|
+
}
|
|
113
|
+
this.started = true
|
|
114
|
+
|
|
115
|
+
// 环境变量兜底(RFC-003 第 5 期):应用没显式开日志时,`CJ_TAURI_LOG` 仍能把它开起来——
|
|
116
|
+
// 已经分发出去的 exe 改不了代码,报障时靠这一个变量拿凭证。放在装配的最早处,
|
|
117
|
+
// 让宿主与插件的启动期日志都赶得上;不设这个变量 = 一行不动,行为与之前完全一致。
|
|
118
|
+
if (currentLogFilePath().size == 0) {
|
|
119
|
+
let envPath = logEnvOr("CJ_TAURI_LOG", "")
|
|
120
|
+
if (envPath.size > 0) {
|
|
121
|
+
let _ = this.setLogFile(envPath)
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// 注入内置系统命令
|
|
126
|
+
this.hub.register("system:version", SystemCommands())
|
|
127
|
+
this.hub.register("system:ping", SystemCommands())
|
|
128
|
+
this.hub.register("system:echo", SystemCommands())
|
|
129
|
+
this.hub.register("system:devtools", SystemCommands())
|
|
130
|
+
this.hub.register("system:host", SystemCommands())
|
|
131
|
+
|
|
132
|
+
// 未显式挂载 capability 时,自动扫描 capabilities/ 目录(对标 Tauri)
|
|
133
|
+
autoLoadCapabilities(this.capabilities)
|
|
134
|
+
|
|
135
|
+
this.hub.setCapabilities(this.capabilities)
|
|
136
|
+
|
|
137
|
+
// 插件装配:注册命令(带 "<插件名>:" 前缀)→ 注册命名权限集 → setup → 汇总前端 JS → 未授权提示。
|
|
138
|
+
// 顺序有意放在能力清单加载之后:提示要反映真实的放行情况。
|
|
139
|
+
for (p in this.plugins) {
|
|
140
|
+
registerPluginCommands(this.hub, p)
|
|
141
|
+
registerPluginPermissionSets(this.capabilities, p)
|
|
142
|
+
p.setup(this)
|
|
143
|
+
}
|
|
144
|
+
this.pluginScripts = pluginInitScripts(this.plugins)
|
|
145
|
+
if (this.plugins.size > 0) {
|
|
146
|
+
eprintln("[cj-tauri] 已装配 ${this.plugins.size} 个插件:")
|
|
147
|
+
eprintln(describePlugins(this.plugins))
|
|
148
|
+
let sets = describePermissionSets(this.plugins)
|
|
149
|
+
if (sets.size > 0) {
|
|
150
|
+
eprintln("[cj-tauri] 命名权限集(在 capabilities 的 permissions 里按集名引用):")
|
|
151
|
+
eprintln(sets)
|
|
152
|
+
}
|
|
153
|
+
warnUnauthorizedPlugins(this.plugins, this.capabilities)
|
|
154
|
+
}
|
|
155
|
+
// 集注册完才可能判定「清单引用的集有没有插件提供」,故放在插件装配之后
|
|
156
|
+
warnUnknownPermissionSets(this.capabilities)
|
|
157
|
+
// 路径 scope 的缺省行为提示(RFC-003 第 4 期):没为吃 scope 的插件写规则 = 全量放行,如实告知
|
|
158
|
+
warnUnscopedPlugins(this.plugins, this.capabilities)
|
|
159
|
+
|
|
160
|
+
// 装配:宿主消息 → IPC hub;hub 输出 → 宿主 JS
|
|
161
|
+
this.wireHostCallbacks(this.host)
|
|
162
|
+
this.hub.jsSink = { json, window =>
|
|
163
|
+
this.deliverJs(json, window)
|
|
164
|
+
}
|
|
165
|
+
this.hub.devToolsSink = { =>
|
|
166
|
+
this.host.openDevTools()
|
|
167
|
+
}
|
|
168
|
+
// system:host 的取值通道:按发起命令的窗口 label 取该宿主的能力位
|
|
169
|
+
this.hub.hostCapabilitiesSink = { label => this.windowCapabilities(label) }
|
|
170
|
+
|
|
171
|
+
this.host.setWindowConfig(this.config)
|
|
172
|
+
// 最小化到托盘同属「建窗时定稿」的窗口行为(窗口过程要提前知道这条策略),
|
|
173
|
+
// 所以与窗口配置一起在 start() 之前交给宿主——能力位不含托盘时平台会拒绝并出声
|
|
174
|
+
this.host.setMinimizeToTray(this.config.minimizeToTray)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* 某个窗口的宿主能力位(`system:host` 的取值实现)
|
|
179
|
+
*
|
|
180
|
+
* 未登记的 label 给 0 **并出声**:写错 label 的表现会是「能力位全 false」,看起来像
|
|
181
|
+
* 「这个平台不支持菜单」,比一条日志难查得多。
|
|
182
|
+
*/
|
|
183
|
+
private func windowCapabilities(label: String): Int32 {
|
|
184
|
+
match (this.hostOf(label)) {
|
|
185
|
+
case Some(h) => return h.hostCapabilities()
|
|
186
|
+
case None =>
|
|
187
|
+
eprintln("[cj-tauri] system:host: 未注册的窗口 label \"${label}\"(能力位按 0 返回)")
|
|
188
|
+
return 0
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* 把默认窗口登记进注册表(label = "main")并给它注入 label
|
|
194
|
+
*
|
|
195
|
+
* 必须在 load() 之前:① 页面一加载就可能发 invoke / emit,回执要按 label 找到窗口;
|
|
196
|
+
* ② label 走宿主的预执行脚本通道(document-start)注入,晚于 start 就没机会了。
|
|
197
|
+
* 桥 JS 是**懒读** `window.__CJ_TAURI_LABEL__` 的(首次用到才取、取到即缓存),
|
|
198
|
+
* 所以注入脚本与桥 JS 的先后顺序无所谓——两平台的注入时机本就不同。
|
|
199
|
+
* 多窗口落地前只登记这一条;将来增补窗口走同一个 add() + 注入,路由侧不用改。
|
|
200
|
+
*/
|
|
201
|
+
private func registerDefaultWindow(): Unit {
|
|
202
|
+
let label = DEFAULT_WINDOW_LABEL
|
|
203
|
+
this.windows.add(WindowRecord(label, this.host, this.config))
|
|
204
|
+
// JSON 字符串字面量与 JS 字符串字面量同构(双引号、同样的转义),拼进赋值语句是安全的
|
|
205
|
+
this.host.addInitScript("window.__CJ_TAURI_LABEL__ = ${JsonString(label)};")
|
|
206
|
+
// shell 事件分发要在 start 之前装(菜单点击随时可能来,装晚了就漏)
|
|
207
|
+
this.wireShellEvents(this.host, label)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* 启动 addWindow() 登记过的附加窗口
|
|
212
|
+
*
|
|
213
|
+
* 放在默认窗口 start() 之后:主窗先出现,附加窗口紧随其后(顺序只影响观感与探针取证节奏)。
|
|
214
|
+
* 每个附加窗口在启动前挂上应用级回调——与默认窗口同一只派发器。
|
|
215
|
+
*/
|
|
216
|
+
private func startExtraWindows(): Unit {
|
|
217
|
+
for (w in this.extraWindows) {
|
|
218
|
+
this.wireHostCallbacks(w.host)
|
|
219
|
+
// shell 事件分发要在 load 之前装(菜单点击随时可能来,装晚了就漏)
|
|
220
|
+
this.wireShellEvents(w.host, w.label)
|
|
221
|
+
w.host.load(PageSource.Inline(w.html))
|
|
222
|
+
eprintln("[cj-tauri] 附加窗口已启动:label=\"${w.label}\"")
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* 释放全部宿主编(幂等,见 WebViewHost.dispose 的契约)
|
|
228
|
+
*/
|
|
229
|
+
private func disposeWindows(): Unit {
|
|
230
|
+
for (rec in this.windows.targets("")) {
|
|
231
|
+
rec.host.dispose()
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* 把一个宿主挂上应用级回调
|
|
237
|
+
*
|
|
238
|
+
* 多窗口下每个宿主都挂**同一只**派发器:报文自带发起窗口的 label(架构文档 §7.3),
|
|
239
|
+
* 路由不需要按句柄分表——所以这里传的是同一个闭包,不是每窗口一份。
|
|
240
|
+
*/
|
|
241
|
+
private func wireHostCallbacks(host: WebViewHost): Unit {
|
|
242
|
+
host.setMessageHandler({ raw =>
|
|
243
|
+
this.hub.handleRawMessage(raw)
|
|
244
|
+
})
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* 清单导出模式(环境变量 CJ_TAURI_MANIFEST)
|
|
250
|
+
*
|
|
251
|
+
* 取值:`json` → 机器可读 JSON(`describePluginsJson`);其它非空值 → 人类可读表(`describePlugins`);
|
|
252
|
+
* 未设置(或空串)→ None,即正常启动窗口。
|
|
253
|
+
*
|
|
254
|
+
* 为什么走环境变量,而不是给 TauriApp 加个方法:清单要**装配完成之后**才有值,而装配发生在
|
|
255
|
+
* run() / runUrl() 内部——加方法就得让每个应用的 main() 多写一个分支,既有应用全都要改。
|
|
256
|
+
* 环境变量让既有应用一行不动就能被 CLI 取出清单(就是 `cj-tauri info` 拿插件清单的通道)。
|
|
257
|
+
*/
|
|
258
|
+
func manifestDumpMode(): Option<String> {
|
|
259
|
+
if (let Some(v) <- getVariable("CJ_TAURI_MANIFEST")) {
|
|
260
|
+
if (v.size == 0) {
|
|
261
|
+
return None
|
|
262
|
+
}
|
|
263
|
+
if (v == "json") {
|
|
264
|
+
return Some("json")
|
|
265
|
+
}
|
|
266
|
+
return Some("text")
|
|
267
|
+
}
|
|
268
|
+
return None
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* 渲染清单文本:`json` 模式给机器(工具 / CI 直接解析),其余给人(启动日志里那份表的同款形态)。
|
|
273
|
+
* 抽成纯函数是为了单测能直接断言,不必真去跑一次宿主。
|
|
274
|
+
*/
|
|
275
|
+
func renderPluginManifest(plugins: ArrayList<Plugin>, mode: String): String {
|
|
276
|
+
if (mode == "json") {
|
|
277
|
+
return describePluginsJson(plugins).toJsonString()
|
|
278
|
+
}
|
|
279
|
+
if (plugins.size == 0) {
|
|
280
|
+
return "(未装配插件)\n"
|
|
281
|
+
}
|
|
282
|
+
return describePlugins(plugins)
|
|
283
|
+
}
|
package/src/app_spec.cj
CHANGED
|
@@ -1,76 +1,76 @@
|
|
|
1
|
-
package cjTauri
|
|
2
|
-
|
|
3
|
-
import std.collection.*
|
|
4
|
-
import cjTauri.platform.*
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* 应用装配声明(批次 3,RFC-002 §4.1):`build()` 的返回类型
|
|
8
|
-
*
|
|
9
|
-
* 业务只描述「应用是什么」——页面 / 窗口 / 插件 / 资产 / 命令装配点——
|
|
10
|
-
* 不描述「在哪跑」:`launch(spec)` 在框架侧按目标分派(§4.2),
|
|
11
|
-
* 平台知识只存在于 L0(§5)。
|
|
12
|
-
*
|
|
13
|
-
* 批次 3-PR1 落地形态(§4.1 的补记):
|
|
14
|
-
* - 命令装配点是 `wire` 闭包:拿到装配中的 TauriApp 后 `register`
|
|
15
|
-
* (业务命令多需要 app 自身,如 `music:quit`)。§4.4 的编译期命令表
|
|
16
|
-
* (generated registry)是批次 3 的后续 PR,届时 `wire` 收敛为类型化字段;
|
|
17
|
-
* - 构造器链:RFC §4.1「示意写法;落地用构造器链」。
|
|
18
|
-
*/
|
|
19
|
-
public class AppSpec {
|
|
20
|
-
/** 应用标识(与清单 [app] id 一致;生成器填入,手写装配建议补上) */
|
|
21
|
-
public var appId: String = ""
|
|
22
|
-
public var name: String = ""
|
|
23
|
-
|
|
24
|
-
/** 页面来源:只有一种表达(§4.3) */
|
|
25
|
-
public var pageSource: PageSource = PageSource.Asset("ui/index.html")
|
|
26
|
-
/** 默认窗口配置 */
|
|
27
|
-
public var windowConfig: WindowConfig = WindowConfig()
|
|
28
|
-
/** 插件清单:jsShim 与命令由框架在 run 前统一注册 */
|
|
29
|
-
public var plugins: ArrayList<Plugin> = ArrayList<Plugin>()
|
|
30
|
-
/** 资产解析器:None = TauriApp 按 cwd 相对路径读盘(与批次 1 行为一致) */
|
|
31
|
-
public var assetResolver: Option<AssetResolver> = None
|
|
32
|
-
/** 能力清单目录:显式挂载 = 框架不自动扫描(目录缺失时安静返回 0,不报错) */
|
|
33
|
-
public var capabilitiesDir: String = ""
|
|
34
|
-
/** 能力清单 JSON 原文列表(批次 3-PR3):给没有目录概念的端(鸿蒙 rawfile);与目录可并存 */
|
|
35
|
-
public var capabilityJsons: ArrayList<String> = ArrayList<String>()
|
|
36
|
-
/** 命令装配点:拿到装配中的 TauriApp 后 register(字段名带 Fn:仓颉字段与方法同命名空间,不能与构造器 wire() 重名) */
|
|
37
|
-
public var wireFn: Option<(TauriApp) -> Unit> = None
|
|
38
|
-
|
|
39
|
-
public init() {}
|
|
40
|
-
|
|
41
|
-
public func page(source: PageSource): AppSpec {
|
|
42
|
-
this.pageSource = source
|
|
43
|
-
return this
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
public func window(config: WindowConfig): AppSpec {
|
|
47
|
-
this.windowConfig = config
|
|
48
|
-
return this
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
public func plugin(p: Plugin): AppSpec {
|
|
52
|
-
this.plugins.add(p)
|
|
53
|
-
return this
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
public func assets(resolver: AssetResolver): AppSpec {
|
|
57
|
-
this.assetResolver = Some(resolver)
|
|
58
|
-
return this
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
public func capabilities(dir: String): AppSpec {
|
|
62
|
-
this.capabilitiesDir = dir
|
|
63
|
-
return this
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/** 挂载一份能力清单 JSON(多份多次调用;与 capabilities 目录不互斥) */
|
|
67
|
-
public func capabilityJson(json: String): AppSpec {
|
|
68
|
-
this.capabilityJsons.add(json)
|
|
69
|
-
return this
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
public func wire(f: (TauriApp) -> Unit): AppSpec {
|
|
73
|
-
this.wireFn = Some(f)
|
|
74
|
-
return this
|
|
75
|
-
}
|
|
76
|
-
}
|
|
1
|
+
package cjTauri
|
|
2
|
+
|
|
3
|
+
import std.collection.*
|
|
4
|
+
import cjTauri.platform.*
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 应用装配声明(批次 3,RFC-002 §4.1):`build()` 的返回类型
|
|
8
|
+
*
|
|
9
|
+
* 业务只描述「应用是什么」——页面 / 窗口 / 插件 / 资产 / 命令装配点——
|
|
10
|
+
* 不描述「在哪跑」:`launch(spec)` 在框架侧按目标分派(§4.2),
|
|
11
|
+
* 平台知识只存在于 L0(§5)。
|
|
12
|
+
*
|
|
13
|
+
* 批次 3-PR1 落地形态(§4.1 的补记):
|
|
14
|
+
* - 命令装配点是 `wire` 闭包:拿到装配中的 TauriApp 后 `register`
|
|
15
|
+
* (业务命令多需要 app 自身,如 `music:quit`)。§4.4 的编译期命令表
|
|
16
|
+
* (generated registry)是批次 3 的后续 PR,届时 `wire` 收敛为类型化字段;
|
|
17
|
+
* - 构造器链:RFC §4.1「示意写法;落地用构造器链」。
|
|
18
|
+
*/
|
|
19
|
+
public class AppSpec {
|
|
20
|
+
/** 应用标识(与清单 [app] id 一致;生成器填入,手写装配建议补上) */
|
|
21
|
+
public var appId: String = ""
|
|
22
|
+
public var name: String = ""
|
|
23
|
+
|
|
24
|
+
/** 页面来源:只有一种表达(§4.3) */
|
|
25
|
+
public var pageSource: PageSource = PageSource.Asset("ui/index.html")
|
|
26
|
+
/** 默认窗口配置 */
|
|
27
|
+
public var windowConfig: WindowConfig = WindowConfig()
|
|
28
|
+
/** 插件清单:jsShim 与命令由框架在 run 前统一注册 */
|
|
29
|
+
public var plugins: ArrayList<Plugin> = ArrayList<Plugin>()
|
|
30
|
+
/** 资产解析器:None = TauriApp 按 cwd 相对路径读盘(与批次 1 行为一致) */
|
|
31
|
+
public var assetResolver: Option<AssetResolver> = None
|
|
32
|
+
/** 能力清单目录:显式挂载 = 框架不自动扫描(目录缺失时安静返回 0,不报错) */
|
|
33
|
+
public var capabilitiesDir: String = ""
|
|
34
|
+
/** 能力清单 JSON 原文列表(批次 3-PR3):给没有目录概念的端(鸿蒙 rawfile);与目录可并存 */
|
|
35
|
+
public var capabilityJsons: ArrayList<String> = ArrayList<String>()
|
|
36
|
+
/** 命令装配点:拿到装配中的 TauriApp 后 register(字段名带 Fn:仓颉字段与方法同命名空间,不能与构造器 wire() 重名) */
|
|
37
|
+
public var wireFn: Option<(TauriApp) -> Unit> = None
|
|
38
|
+
|
|
39
|
+
public init() {}
|
|
40
|
+
|
|
41
|
+
public func page(source: PageSource): AppSpec {
|
|
42
|
+
this.pageSource = source
|
|
43
|
+
return this
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
public func window(config: WindowConfig): AppSpec {
|
|
47
|
+
this.windowConfig = config
|
|
48
|
+
return this
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
public func plugin(p: Plugin): AppSpec {
|
|
52
|
+
this.plugins.add(p)
|
|
53
|
+
return this
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
public func assets(resolver: AssetResolver): AppSpec {
|
|
57
|
+
this.assetResolver = Some(resolver)
|
|
58
|
+
return this
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
public func capabilities(dir: String): AppSpec {
|
|
62
|
+
this.capabilitiesDir = dir
|
|
63
|
+
return this
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** 挂载一份能力清单 JSON(多份多次调用;与 capabilities 目录不互斥) */
|
|
67
|
+
public func capabilityJson(json: String): AppSpec {
|
|
68
|
+
this.capabilityJsons.add(json)
|
|
69
|
+
return this
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
public func wire(f: (TauriApp) -> Unit): AppSpec {
|
|
73
|
+
this.wireFn = Some(f)
|
|
74
|
+
return this
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
package cjTauri
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ArkWeb 宿主的菜单模型与线路格式解析(`@When[env == "ohos"]` 门禁)
|
|
5
|
+
*
|
|
6
|
+
* ## 为什么鸿蒙的菜单要「框架存模型、应用侧画」
|
|
7
|
+
*
|
|
8
|
+
* 桌面两平台的菜单由**宿主自己**画(Win32 `CreateMenu` / GTK `GtkMenuBar`),线路格式的解析在
|
|
9
|
+
* C 桥的 `cj_core_menu_walk` 里。鸿蒙没有「宿主菜单栏」这种东西:ArkWeb 的仓颉绑定只给了
|
|
10
|
+
* **组件级**的 `bindMenu`,而**框架包不许引用 ArkUI**(AGENTS §4:框架作为独立编译单元引用
|
|
11
|
+
* `kit.ArkUI` 会让鸿蒙包初始化链跳坏地址,启动即 SIGSEGV)。于是分工只能是:
|
|
12
|
+
*
|
|
13
|
+
* - 框架侧(本文件 + [ArkWebHost]):把应用给的线路格式解析成**快照**,并留一个「某项被点了」
|
|
14
|
+
* 的回投入口;
|
|
15
|
+
* - 应用侧(`ArkWebView` 样板):用 `bindMenu` 把快照画出来,点击时调 `noteMenuClick`。
|
|
16
|
+
*
|
|
17
|
+
* 这与 dialog 的落法同源(框架入队 / 应用侧声明式渲染),也是**唯一**同时满足「框架不碰 ArkUI」
|
|
18
|
+
* 与「菜单真的能点」的形状。判据在 `docs/鸿蒙ArkWeb接入踩坑.md` §10.11(声明式 overlay 那轮)。
|
|
19
|
+
*
|
|
20
|
+
* ## 线路格式
|
|
21
|
+
*
|
|
22
|
+
* 与 `src/menu.cj` 的 `MenuModel.toWire()` 逐字对应(同一份格式,别在鸿蒙侧另发明一种):
|
|
23
|
+
*
|
|
24
|
+
* <tabs><kind>\t<id>\t<label>\t<flags>\t<accel>\n
|
|
25
|
+
*
|
|
26
|
+
* `kind`:`n` 普通 / `c` 勾选 / `s` 分隔线 / `m` 子菜单;`flags` 十进制,bit0 = 可用、bit1 = 勾选。
|
|
27
|
+
* 容错口径照 C 侧那遍解析([cj_core_menu_walk]):缺字段按空串 / 0,kind 为空的行整行跳过。
|
|
28
|
+
*/
|
|
29
|
+
@When[env == "ohos"]
|
|
30
|
+
import std.collection.ArrayList
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* 一项菜单(快照)。
|
|
34
|
+
*
|
|
35
|
+
* 字段全是快照值:UI 线程拿到的是 [ArkWebHost.menuModel] 给的副本,改它不会影响宿主里的模型
|
|
36
|
+
* (两边共享同一批可变对象会变成跨线程数据竞争)。
|
|
37
|
+
*/
|
|
38
|
+
@When[env == "ohos"]
|
|
39
|
+
public class ArkWebMenuItem {
|
|
40
|
+
/** 线路格式的 kind 码:`n` / `c` / `s` / `m` */
|
|
41
|
+
public let kind: String
|
|
42
|
+
|
|
43
|
+
/** 应用给的稳定 id(回投与 `setMenuItemState` 的查找键);分隔线为空串 */
|
|
44
|
+
public let id: String
|
|
45
|
+
|
|
46
|
+
public let label: String
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* 快捷键词法原文(如 `Ctrl+O`),应用没写就是空串。
|
|
50
|
+
*
|
|
51
|
+
* 桌面两平台由宿主把它翻成真热键(Windows `RegisterHotKey` / Linux X11 grab),
|
|
52
|
+
* 鸿蒙宿主**没有**这层,所以应用侧要自己认键:按 accel 匹配按键再回投 `menu:click`
|
|
53
|
+
* (骨架 `index.cj` 的 `menuAccelKey` + `onKeyEvent`)。别把 accel 只当展示字段丢掉。
|
|
54
|
+
*/
|
|
55
|
+
public let accel: String
|
|
56
|
+
|
|
57
|
+
public var enabled: Bool
|
|
58
|
+
|
|
59
|
+
public var checked: Bool
|
|
60
|
+
|
|
61
|
+
/** 仅子菜单(kind = `m`)会填;其它 kind 恒为空表 */
|
|
62
|
+
public let children: ArrayList<ArkWebMenuItem>
|
|
63
|
+
|
|
64
|
+
public init(kind: String, id: String, label: String, enabled: Bool, checked: Bool,
|
|
65
|
+
children: ArrayList<ArkWebMenuItem>, accel!: String = "") {
|
|
66
|
+
this.kind = kind
|
|
67
|
+
this.id = id
|
|
68
|
+
this.label = label
|
|
69
|
+
this.enabled = enabled
|
|
70
|
+
this.checked = checked
|
|
71
|
+
this.children = children
|
|
72
|
+
this.accel = accel
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
public func isSubmenu(): Bool {
|
|
76
|
+
return this.kind == "m"
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
public func isCheckbox(): Bool {
|
|
80
|
+
return this.kind == "c"
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
public func isSeparator(): Bool {
|
|
84
|
+
return this.kind == "s"
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** 深拷贝:交给 UI 线程的快照(见类注释里的跨线程口径) */
|
|
88
|
+
public func snapshot(): ArkWebMenuItem {
|
|
89
|
+
let copyChildren = ArrayList<ArkWebMenuItem>()
|
|
90
|
+
for (child in this.children) {
|
|
91
|
+
copyChildren.add(child.snapshot())
|
|
92
|
+
}
|
|
93
|
+
return ArkWebMenuItem(this.kind, this.id, this.label, this.enabled, this.checked, copyChildren,
|
|
94
|
+
accel: this.accel)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** 按 id 找一项(含子孙),找不到回 None——`setMenuItemState` 的查找键 |
|
|
98
|
+
*/
|
|
99
|
+
public func find(id: String): Option<ArkWebMenuItem> {
|
|
100
|
+
if (this.id == id && !this.isSeparator()) {
|
|
101
|
+
return Some(this)
|
|
102
|
+
}
|
|
103
|
+
for (child in this.children) {
|
|
104
|
+
match (child.find(id)) {
|
|
105
|
+
case Some(hit) => return Some(hit)
|
|
106
|
+
case None => ()
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return None
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* 解析菜单线路格式 → 根列表(depth 0 的项)。
|
|
115
|
+
*
|
|
116
|
+
* 逐字节扫:`\n` 断行、`\t` 断字段、行首 `\t` 数即层级。跳级的行按「挂到当前最深可用层」容错
|
|
117
|
+
* (线路来自应用,坏了只该少画一项,不该让宿主抛异常)。
|
|
118
|
+
*/
|
|
119
|
+
@When[env == "ohos"]
|
|
120
|
+
public func parseArkWebMenuWire(wire: String): ArrayList<ArkWebMenuItem> {
|
|
121
|
+
let root = ArrayList<ArkWebMenuItem>()
|
|
122
|
+
if (wire.size == 0) {
|
|
123
|
+
return root
|
|
124
|
+
}
|
|
125
|
+
/* stack[d] = 第 d 层的挂载容器:stack[0] 是根,stack[d+1] 是上一个子菜单项的子表 */
|
|
126
|
+
let stack = ArrayList<ArrayList<ArkWebMenuItem>>()
|
|
127
|
+
stack.add(root)
|
|
128
|
+
let bytes = wire.toArray()
|
|
129
|
+
var i: Int64 = 0
|
|
130
|
+
while (i < bytes.size) {
|
|
131
|
+
var end = i
|
|
132
|
+
while (end < bytes.size && bytes[end] != 10u8) {
|
|
133
|
+
end++
|
|
134
|
+
}
|
|
135
|
+
var depth: Int64 = 0
|
|
136
|
+
var p = i
|
|
137
|
+
while (p < end && bytes[p] == 9u8) {
|
|
138
|
+
depth++
|
|
139
|
+
p++
|
|
140
|
+
}
|
|
141
|
+
if (p < end) {
|
|
142
|
+
let fields = splitWireFields(wire, bytes, p, end)
|
|
143
|
+
let kind = fields[0]
|
|
144
|
+
if (kind.size > 0) {
|
|
145
|
+
let id = wireField(fields, 1)
|
|
146
|
+
let label = wireField(fields, 2)
|
|
147
|
+
let flags = parseWireFlags(wireField(fields, 3))
|
|
148
|
+
let item = ArkWebMenuItem(kind, id, label, (flags & 1) != 0, (flags & 2) != 0,
|
|
149
|
+
ArrayList<ArkWebMenuItem>(), accel: wireField(fields, 4))
|
|
150
|
+
/* 跳级(depth 大于当前栈深)按最深层挂,少画一层但不错位 */
|
|
151
|
+
var slot = depth
|
|
152
|
+
if (slot >= stack.size) {
|
|
153
|
+
slot = stack.size - 1
|
|
154
|
+
}
|
|
155
|
+
stack[slot].add(item)
|
|
156
|
+
while (stack.size > slot + 1) {
|
|
157
|
+
stack.remove(at: stack.size - 1)
|
|
158
|
+
}
|
|
159
|
+
if (item.isSubmenu()) {
|
|
160
|
+
stack.add(item.children)
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
i = end + 1
|
|
165
|
+
}
|
|
166
|
+
return root
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/* 一行内的字段(最多 5 个):kind / id / label / flags / accel,缺的按空串(与 C 侧的容错同口径) */
|
|
170
|
+
@When[env == "ohos"]
|
|
171
|
+
private func splitWireFields(wire: String, bytes: Array<UInt8>, start: Int64, end: Int64): ArrayList<String> {
|
|
172
|
+
let fields = ArrayList<String>()
|
|
173
|
+
var seg = start
|
|
174
|
+
var k = start
|
|
175
|
+
while (k <= end && fields.size < 5) {
|
|
176
|
+
if (k == end || bytes[k] == 9u8) {
|
|
177
|
+
fields.add(wire[seg..k])
|
|
178
|
+
seg = k + 1
|
|
179
|
+
}
|
|
180
|
+
k++
|
|
181
|
+
}
|
|
182
|
+
if (fields.size == 0) {
|
|
183
|
+
fields.add("")
|
|
184
|
+
}
|
|
185
|
+
return fields
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
@When[env == "ohos"]
|
|
189
|
+
private func wireField(fields: ArrayList<String>, index: Int64): String {
|
|
190
|
+
if (index < fields.size) {
|
|
191
|
+
return fields[index]
|
|
192
|
+
}
|
|
193
|
+
return ""
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/* flags 的十进制解析:不引 std.convert(`Int64.parse` 要那个扩展),几个字节自己算,顺带无异常 */
|
|
197
|
+
@When[env == "ohos"]
|
|
198
|
+
private func parseWireFlags(text: String): Int64 {
|
|
199
|
+
let bytes = text.toArray()
|
|
200
|
+
var value: Int64 = 0
|
|
201
|
+
var seen = false
|
|
202
|
+
for (b in bytes) {
|
|
203
|
+
if (b < 48u8 || b > 57u8) {
|
|
204
|
+
break
|
|
205
|
+
}
|
|
206
|
+
value = value * 10 + Int64(b - 48u8)
|
|
207
|
+
seen = true
|
|
208
|
+
if (value > 65535) {
|
|
209
|
+
return 65535
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
if (!seen) {
|
|
213
|
+
return 0
|
|
214
|
+
}
|
|
215
|
+
return value
|
|
216
|
+
}
|