ngvfs_docs.typ 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. #import "../docs_common/template/template.typ" : ZRYGenericDocs
  2. #import "@preview/tablex:0.0.8": tablex, colspanx, rowspanx
  3. #import "@preview/oxifmt:0.2.0": strfmt
  4. #let pageCounting = locate(loc=>counter(page).final(loc).at(0))
  5. #let title = "openNGVFS 文档"
  6. #let version = (
  7. major: 1,
  8. minor: 1,
  9. patch: 0,
  10. )
  11. #let semVerStr = strfmt("{}.{}.{}", version.major, version.minor, version.patch)
  12. #show: body => ZRYGenericDocs(
  13. title: title,
  14. subTitle: "",
  15. titleDesc: "",
  16. docVer: "API Ver. " + semVerStr,
  17. headerSettings: (
  18. enable: true,
  19. ascent: 20%,
  20. content: locate(loc => {
  21. let elems = query(selector(heading).before(loc), loc)
  22. if counter(page).at(loc).first() > 1 [
  23. #text(
  24. weight: "bold",
  25. size: 0.9em,
  26. )[
  27. #title
  28. ]
  29. #h(1fr)
  30. #text(
  31. weight: "thin",
  32. size: 0.7em,
  33. )[
  34. #counter(page).display() / #pageCounting
  35. ]
  36. #line(length: 100%)
  37. ]
  38. }),
  39. ),
  40. footerSettings: (
  41. enable: true,
  42. descent: 30%,
  43. content: locate(loc => {
  44. if counter(page).at(loc).first() > 1 [
  45. #line(length: 100%)
  46. #text(
  47. weight: "thin",
  48. size: 0.6em,
  49. )[
  50. 公开文档
  51. ]
  52. ]
  53. }),
  54. ),
  55. body
  56. )
  57. = 项目说明
  58. 该项目基于商业项目ProjectNagae下属的NagaeVFS V1.1.0,为NagaeVFS的开源版本。该版本使用MIT License开源。注意,NagaeVFS后续版本将不再与openNGVFS兼容。
  59. = 版本说明
  60. 当前版本从NagaeVFS V1.1.0衍生,经过一定程度的测试,但使用前请自行进行必要的功能测试。
  61. 当前版本提供以下功能。
  62. == VFS
  63. 一个可挂载的虚拟文件系统。其主机接口兼容afero.Fs。
  64. == INIT WASM
  65. 用于初始化文件系统挂载的WASM虚拟机。
  66. 该虚拟机提供以下接口:
  67. + 在主机上创建和释放afero.Fs对象的接口。
  68. + 挂载主机上被创建的afero.Fs对象的接口。
  69. + 在主机VFS上创建目录的接口。
  70. + 读取配置文件中ExKV键值对内容的接口。
  71. === 可创建的afs对象
  72. ==== 来自afero.Fs的类型
  73. 当前版本可创建的afero.Fs对象,由afero包本身提供的有:
  74. - afero.OsFs
  75. - afero.MemMapFs
  76. - afero.BasePathFs
  77. - afero.RegexpFs
  78. - afero.ReadOnlyFs
  79. - afero.CopyOnWriteFs
  80. - afero.CacheOnReadFs
  81. 其中经过简单测试并通过的有:
  82. - afero.OsFs
  83. - afero.MemMapFs
  84. - afero.BasePathFs
  85. - afero.ReadOnlyFs
  86. 其中经过测试有问题的:
  87. - afero.RegexpFs
  88. - Readdir功能在遇到无法匹配的文件时产生error
  89. 该问题来自afero.RegexpFs本身,故不提供其修正。
  90. 谨慎考虑使用RegexpFs。
  91. ==== 来自第三方的
  92. - `"github.com/bep/overlayfs" overlayfs.OverlayFs`
  93. 经过测试,但有如下问题:
  94. - 不支持在子目录下创建文件,会产生错误
  95. `The system cannot find the path specified.`
  96. = 主机接口
  97. 这部分是关于主机golang库的文档。
  98. 该库位于 `openngvfs` 目录,包名 `git.swzry.com/zry/openNGVFS/openngvfs`。
  99. 以下为主机部分的示例代码,更完整的示例代码可以参考
  100. `testing/standalone-test-0001/go`。
  101. ```go
  102. func main() {
  103. // Init logger.
  104. logger := hiedalog.NewHiedaLogger()
  105. consoleBke := hiedabke_console.NewConsoleBackend(os.Stderr)
  106. logger.AddBackend(
  107. consoleBke,
  108. logger.LevelFilter.NameToID(hiedalog.DLN_DEBUG),
  109. )
  110. // Create openNGVFS object.
  111. fs := ngvfs.NewOpenNagaeVFS(logger)
  112. // Create Context.
  113. ctx := context.Background()
  114. // root is the root path of this program.
  115. root := "/home/satori/openngvfs_test_root/"
  116. // Init openNGVFS.
  117. // "test" is app name of this program.
  118. err := fs.Init(ctx, root, "test")
  119. if err != nil {
  120. fmt.Println("error initializing fs: ", err)
  121. return
  122. }
  123. // start init-wasm to mount filesystems.
  124. err = fs.RunMount()
  125. if err != nil {
  126. fmt.Println("error in run auto-mount: ", err)
  127. return
  128. }
  129. // ...
  130. // deinitializing before quit.
  131. err = fs.DeInit()
  132. if err != nil {
  133. fmt.Println("error deinitializing fs: ", err)
  134. }
  135. }
  136. ```
  137. 在上述示例代码中,App名称为test,
  138. openNGVFS相关的根目录为`/home/satori/openngvfs_test_root`,
  139. 那么我们应该像这样准备根目录的内容:
  140. ```bash
  141. cd /home/satori/openngvfs_test_root
  142. mkdir -p ./syscfg/openngvfs/wasm
  143. ```
  144. 然后将init-wasm文件test-init.wasm放入
  145. `/home/satori/openngvfs_test_root/syscfg/openngvfs/wasm`
  146. 目录中。
  147. 接下来在`/home/satori/openngvfs_test_root/syscfg/openngvfs/`下创建配置文件
  148. `fstab.toml`并写入如下内容
  149. ```toml
  150. [wasm]
  151. # 用于未单独定义的app的默认值
  152. default = "default-init"
  153. # 定义app `test` 的设置
  154. [test.app]
  155. wpms = "test-init"
  156. # 在这里定义全局的ExKV,这些键值可以在wasm中获取到
  157. [exkv.default]
  158. # 为每个app定义ExKV,会和全局ExKV合并,如key相同,则取每个app单独定义的值。
  159. [exkv.app.wpms]
  160. ```
  161. = INIT WASI接口
  162. == WASM导出函数
  163. #{
  164. let data = yaml("init_wasi_export.yaml")
  165. for funcPair in data {
  166. {
  167. let funcName = funcPair.at(0)
  168. [==== 函数 #raw(funcName)]
  169. let funcPart = funcPair.at(1)
  170. let funcParams = funcPart.at("params", default: none)
  171. let funcResults = funcPart.at("results", default: none)
  172. let prototypeParams = if funcParams != none and funcParams.len() > 0 {
  173. let prototypeParamsList = ()
  174. for iParam in funcParams {
  175. prototypeParamsList.push(iParam.name + ": " + iParam.type)
  176. }
  177. prototypeParamsList.join(",")
  178. }else{""}
  179. let prototypeCode = "fn " + funcName + "(" + prototypeParams + ")"
  180. let prototypeResult = if funcResults != none and funcResults.len() > 0 {
  181. let prototypeResultList = ()
  182. for iResult in funcResults {
  183. prototypeResultList.push(iResult.name + ": " + iResult.type)
  184. }
  185. " -> (" + prototypeResultList.join(",") + ")"
  186. }else{""}
  187. prototypeCode = prototypeCode + prototypeResult + ";"
  188. let prototype = raw(prototypeCode, lang:"rust")
  189. let tableContent = (
  190. [*名称*], colspanx(4)[#funcPair.at(0)],
  191. [*说明*], colspanx(4)[#funcPart.desc],
  192. [*原型*], colspanx(4)[#prototype],
  193. )
  194. if funcParams != none and funcParams.len() > 0 {
  195. let lenParams = funcParams.len()
  196. tableContent.push(rowspanx(lenParams+1)[*参数*])
  197. tableContent.push([*名称*])
  198. tableContent.push([*WASM类型*])
  199. tableContent.push([*语义类型*])
  200. tableContent.push([*说明*])
  201. for param in funcParams {
  202. let paramEnum = param.at("enum", default: none)
  203. tableContent.push(param.name)
  204. tableContent.push(param.type)
  205. tableContent.push(if param.xtyp != none {param.xtyp} else {param.type})
  206. tableContent.push(
  207. if paramEnum != none [
  208. #param.desc
  209. #let paramEnumTable = ()
  210. #for peti in paramEnum {
  211. paramEnumTable.push(peti.at(0))
  212. paramEnumTable.push(peti.at(1))
  213. }
  214. #tablex(
  215. columns: 2,
  216. [*值*], [*说明*],
  217. ..paramEnumTable
  218. )
  219. ]else[#param.desc]
  220. )
  221. }
  222. }
  223. if funcResults != none and funcResults.len() > 0 {
  224. let lenResults = funcResults.len()
  225. tableContent.push(rowspanx(lenResults+1)[*返回值*])
  226. tableContent.push([*名称*])
  227. tableContent.push([*WASM类型*])
  228. tableContent.push([*语义类型*])
  229. tableContent.push([*说明*])
  230. for param in funcResults {
  231. let paramEnum = param.at("enum", default: none)
  232. let paramXtyp = param.at("xtyp", default: none)
  233. tableContent.push(param.name)
  234. tableContent.push(param.type)
  235. tableContent.push(if paramXtyp != none {paramXtyp} else {param.type})
  236. tableContent.push(
  237. if paramEnum != none [
  238. #param.desc
  239. #let paramEnumTable = ()
  240. #for peti in paramEnum {
  241. paramEnumTable.push(peti.at(0))
  242. paramEnumTable.push(peti.at(1))
  243. }
  244. #tablex(
  245. columns: 2,
  246. [*值*], [*说明*],
  247. ..paramEnumTable
  248. )
  249. ]else[#param.desc]
  250. )
  251. }
  252. }
  253. tablex(
  254. columns: 5,
  255. ..tableContent
  256. )
  257. }
  258. }
  259. }
  260. == 主机导出模块
  261. #{
  262. let data = yaml("init_wasi_import.yaml")
  263. for modPair in data {
  264. [=== 模块 #raw(modPair.at(0))]
  265. for funcPair in modPair.at(1) {
  266. {
  267. let funcName = funcPair.at(0)
  268. [==== 函数 #raw(funcName)]
  269. let funcPart = funcPair.at(1)
  270. let funcParams = funcPart.at("params", default: none)
  271. let funcResults = funcPart.at("results", default: none)
  272. let prototypeParams = if funcParams != none and funcParams.len() > 0 {
  273. let prototypeParamsList = ()
  274. for iParam in funcParams {
  275. prototypeParamsList.push(iParam.name + ": " + iParam.type)
  276. }
  277. prototypeParamsList.join(",")
  278. }else{""}
  279. let prototypeCode = "fn " + funcName + "(" + prototypeParams + ")"
  280. let prototypeResult = if funcResults != none and funcResults.len() > 0 {
  281. let prototypeResultList = ()
  282. for iResult in funcResults {
  283. prototypeResultList.push(iResult.name + ": " + iResult.type)
  284. }
  285. " -> (" + prototypeResultList.join(",") + ")"
  286. }else{""}
  287. prototypeCode = prototypeCode + prototypeResult + ";"
  288. let prototype = raw(prototypeCode, lang:"rust")
  289. let tableContent = (
  290. [*名称*], colspanx(4)[#funcPair.at(0)],
  291. [*说明*], colspanx(4)[#funcPart.desc],
  292. [*原型*], colspanx(4)[#prototype],
  293. )
  294. if funcParams != none and funcParams.len() > 0 {
  295. let lenParams = funcParams.len()
  296. tableContent.push(rowspanx(lenParams+1)[*参数*])
  297. tableContent.push([*名称*])
  298. tableContent.push([*WASM类型*])
  299. tableContent.push([*语义类型*])
  300. tableContent.push([*说明*])
  301. for param in funcParams {
  302. let paramEnum = param.at("enum", default: none)
  303. let paramXtyp = param.at("xtyp", default: none)
  304. tableContent.push(param.name)
  305. tableContent.push(param.type)
  306. tableContent.push(if paramXtyp != none {paramXtyp} else {param.type})
  307. tableContent.push(
  308. if paramEnum != none [
  309. #param.desc
  310. #let paramEnumTable = ()
  311. #for peti in paramEnum {
  312. paramEnumTable.push(peti.at(0))
  313. paramEnumTable.push(peti.at(1))
  314. }
  315. #tablex(
  316. columns: 2,
  317. [*值*], [*说明*],
  318. ..paramEnumTable
  319. )
  320. ]else[#param.desc]
  321. )
  322. }
  323. }
  324. if funcResults != none and funcResults.len() > 0 {
  325. let lenResults = funcResults.len()
  326. tableContent.push(rowspanx(lenResults+1)[*返回值*])
  327. tableContent.push([*名称*])
  328. tableContent.push([*WASM类型*])
  329. tableContent.push([*语义类型*])
  330. tableContent.push([*说明*])
  331. for param in funcResults {
  332. let paramEnum = param.at("enum", default: none)
  333. tableContent.push(param.name)
  334. tableContent.push(param.type)
  335. tableContent.push(if param.xtyp != none {param.xtyp} else {param.type})
  336. tableContent.push(
  337. if paramEnum != none [
  338. #param.desc
  339. #let paramEnumTable = ()
  340. #for peti in paramEnum {
  341. paramEnumTable.push(peti.at(0))
  342. paramEnumTable.push(peti.at(1))
  343. }
  344. #tablex(
  345. columns: 2,
  346. [*值*], [*说明*],
  347. ..paramEnumTable
  348. )
  349. ]else[#param.desc]
  350. )
  351. }
  352. }
  353. tablex(
  354. columns: 5,
  355. ..tableContent
  356. )
  357. }
  358. }
  359. }
  360. }
  361. == WASM侧Rust Crate
  362. 提供有用于WASM侧的接口包装Rust Crate。
  363. 示例程序在`testing\standalone-test-0001\openngvfs_init_wasm_test`,
  364. 编译该版本建议安装cargo-wasi。
  365. ```bash
  366. cargo install cargo-wasi
  367. ```
  368. 可使用如下命令编译init-wasm示例:
  369. ```bash
  370. just wasm
  371. ```
  372. 若未安装cargo-wasi请修改justfile内的编译命令。
  373. 目前本文档内暂不提供该Crate的文档。
  374. 该包的交互式文档可使用rustdoc生成:
  375. ```bash
  376. cd init-wasm
  377. just doc_html
  378. ```
  379. 生成的文档在`init-wasm/ngvfs_init_wasm_lib/target/doc/ngvfs_init_wasm_lib`
  380. 若安装有`SimpleHttpTestServer`,可继续使用如下命令:
  381. ```bash
  382. # 端口号可换成其他值
  383. just serv_doc "localhost:8088"
  384. ```
  385. 然后使用浏览器打开 #link("http://localhsot:8088/")。