core.lua 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. local dir = require "pl.dir"
  2. local path = require "pl.path"
  3. local store = require "volksdata.store"
  4. local term = require "volksdata.term"
  5. local nsm = require "volksdata.namespace"
  6. -- Read only module properties.
  7. local PROTECTED = {
  8. store = true,
  9. reset_store = true,
  10. }
  11. local fpath = debug.getinfo(1, "S").source:sub(2)
  12. local root_path = path.dirname(path.dirname(fpath))
  13. local config_path = os.getenv("PKAR_CONFIG_DIR") or
  14. path.join(root_path .. "/config")
  15. local config = dofile(path.join(config_path, "app.lua"))
  16. local store_id = "file://" .. (os.getenv("PKAR_BASE") or config.fs.dres_path)
  17. -- Fill namespace map from config.
  18. for pfx, ns in pairs(config.namespace) do nsm.add(pfx, ns) end
  19. -- Initialize the store. `clear` wipes all data. YOU HAVE BEEN WARNED!
  20. local store_init = function(clear)
  21. return store.new(store.MDB, store_id, clear)
  22. end
  23. local M = {
  24. -- Project root path.
  25. root = root_path,
  26. config = config,
  27. default_title = "Pocket Archive",
  28. store_id = store_id,
  29. --Logger config.
  30. logger = require "sllog":init{
  31. {"err", "%T PKAR %-5L ", "%n", io.stderr},
  32. {"warn", "%T PKAR %-5L ", "%n", io.stderr},
  33. {"info", "%T PKAR %-5L ", "%n", io.stderr},
  34. {"debug", "%T PKAR %-5L%f (%S) ", "%n", io.stderr},
  35. timefn=(socket or {}).gettime, -- use socket.gettime if available
  36. report="debug", -- to which level should internal log events be passed?
  37. hookrequire=true, -- also report calls to require()
  38. level="debug", -- output levels up to and including "dbg"
  39. },
  40. -- Commonly used terms.
  41. RDF_TYPE = term.new_iriref_ns("rdf:type"),
  42. DC_TITLE_P = term.new_iriref_ns("dc:title"),
  43. DC_CREATED_P = term.new_iriref_ns("dc:created"),
  44. TN_P = term.new_iriref_ns("pas:thumbnail"),
  45. FIRST_P = term.new_iriref_ns("pas:first"),
  46. NEXT_P = term.new_iriref_ns("pas:next"),
  47. PATH_P = term.new_iriref_ns("pas:sourcePath"),
  48. CONTENT_TYPE_P = term.new_iriref_ns("pas:contentType"),
  49. ART_T = term.new_iriref_ns("pas:Artifact"),
  50. PART_T = term.new_iriref_ns("pas:Part"),
  51. FILE_T = term.new_iriref_ns("pas:File"),
  52. -- Common namespaces
  53. PAR_NS = nsm.get_ns("par"),
  54. PAS_NS = nsm.get_ns("pas"),
  55. -- Methods.
  56. -- Escape strings for use in gsub patterns.
  57. escape_ptn = function(src)
  58. return src:gsub("[%(%)%.%%%+%-%*%?%[%]%^%$]", "%%%0")
  59. end,
  60. }
  61. --[[
  62. Generate pairtree directory and file path from an ID string and prefix.
  63. The directory is created if not existing.
  64. The ID string can include the `par:` namespace prefix or the fully qualified
  65. namespace.
  66. ext is optional, and is appended to the raw path.
  67. if no_create is not nil or false, the directory will not be checked for
  68. existence or created.
  69. return: full file path, with the optional extension if provided.
  70. --]]
  71. M.gen_pairtree = function (pfx, id_str, ext, no_create)
  72. local bare_id = id_str:gsub(M.PAR_NS, ""):gsub("^par:", "")
  73. local res_dir = path.join(pfx, bare_id:sub(1,2), bare_id:sub(3,4))
  74. local created, err
  75. if (path.isdir(res_dir)) or no_create then created = false
  76. else
  77. created, err = dir.makepath(res_dir)
  78. if not created then error(err) end
  79. end
  80. local fpath
  81. if (res_dir) then fpath = path.join(res_dir, bare_id .. (ext or "")) end
  82. return fpath, created
  83. end
  84. -- No more assignments to the module afte here.
  85. local mt = {
  86. __index = function(t, k)
  87. if (k == "store") then
  88. if not rawget(t, "_store") then
  89. rawset(t, "_store", store.new(store.MDB, store_id)) end
  90. return rawget(t, "_store")
  91. -- WIPE ALL DATA from the store and returns the new empty store handle.
  92. elseif (k == "reset_store") then
  93. rawset(t, "_store", store.new(store.MDB, store_id, true))
  94. return rawget(t, "_store")
  95. end
  96. end,
  97. __newindex = function () return nil, "Module is read only." end
  98. }
  99. setmetatable (M, mt)
  100. -- Initialize random ID generator.
  101. math.randomseed(M.config.id.seed[1], M.config.id.seed[2])
  102. return M