core.lua 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  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. local M = {
  20. -- Project root path.
  21. root = root_path,
  22. config_path = config_path,
  23. config = config,
  24. default_title = "Pocket Archive",
  25. store_id = store_id,
  26. --Logger config.
  27. logger = require "sllog":init{
  28. {"err", "%T PKAR %-5L ", "%n", io.stderr},
  29. {"warn", "%T PKAR %-5L ", "%n", io.stderr},
  30. {"info", "%T PKAR %-5L ", "%n", io.stderr},
  31. {"debug", "%T PKAR %-5L%f (%S) ", "%n", io.stderr},
  32. timefn=(socket or {}).gettime, -- use socket.gettime if available
  33. report="debug", -- to which level should internal log events be passed?
  34. hookrequire=true, -- also report calls to require()
  35. level="debug", -- output levels up to and including "dbg"
  36. },
  37. -- Commonly used terms.
  38. RDF_TYPE = term.new_iriref_ns("rdf:type"),
  39. DC_TITLE_P = term.new_iriref_ns("dc:title"),
  40. SUBMITTED_P = term.new_iriref_ns("dc:dateSubmitted"),
  41. TN_P = term.new_iriref_ns("pas:thumbnail"),
  42. FIRST_P = term.new_iriref_ns("pas:first"),
  43. NEXT_P = term.new_iriref_ns("pas:next"),
  44. PATH_P = term.new_iriref_ns("pas:sourcePath"),
  45. CONTENT_TYPE_P = term.new_iriref_ns("pas:contentType"),
  46. ART_T = term.new_iriref_ns("pas:Artifact"),
  47. PART_T = term.new_iriref_ns("pas:Part"),
  48. FILE_T = term.new_iriref_ns("pas:File"),
  49. -- Common namespaces
  50. PAR_NS = nsm.get_ns("par"),
  51. PAS_NS = nsm.get_ns("pas"),
  52. -- Methods.
  53. -- Escape strings for use in gsub patterns.
  54. escape_ptn = function(src)
  55. return src:gsub("[%(%)%.%%%+%-%*%?%[%]%^%$]", "%%%0")
  56. end,
  57. }
  58. -- Adapted from lua-núcleo
  59. M.escape_pattern = function(s)
  60. local matches = {
  61. ["^"] = "%^";
  62. ["$"] = "%$";
  63. ["("] = "%(";
  64. [")"] = "%)";
  65. ["%"] = "%%";
  66. ["."] = "%.";
  67. ["["] = "%[";
  68. ["]"] = "%]";
  69. ["*"] = "%*";
  70. ["+"] = "%+";
  71. ["-"] = "%-";
  72. ["?"] = "%?";
  73. ["\0"] = "%z";
  74. }
  75. return (s:gsub(".", matches))
  76. end
  77. --[[
  78. Generate pairtree directory and file path from an ID string and prefix.
  79. The directory is created if not existing.
  80. The ID string can include the `par:` namespace prefix or the fully qualified
  81. namespace.
  82. ext is optional, and is appended to the raw path.
  83. if no_create is not nil or false, the directory will not be checked for
  84. existence or created.
  85. return: full file path, with the optional extension if provided.
  86. --]]
  87. M.gen_pairtree = function (pfx, id_str, ext, no_create)
  88. local bare_id = id_str:gsub(M.PAR_NS, ""):gsub("^par:", "")
  89. local res_dir = path.join(pfx, bare_id:sub(1,2), bare_id:sub(3,4))
  90. local created, err
  91. if (path.isdir(res_dir)) or no_create then created = false
  92. else
  93. created, err = dir.makepath(res_dir)
  94. if not created then error(err) end
  95. end
  96. local fpath
  97. if (res_dir) then fpath = path.join(res_dir, bare_id .. (ext or "")) end
  98. return fpath, created
  99. end
  100. -- No more assignments to the module afte here.
  101. local mt = {
  102. __index = function(t, k)
  103. if (k == "store") then
  104. if not rawget(t, "_store") then
  105. rawset(t, "_store", store.new(store.MDB, store_id)) end
  106. return rawget(t, "_store")
  107. -- WIPE ALL DATA from the store and returns the new empty store handle.
  108. elseif (k == "reset_store") then
  109. rawset(t, "_store", store.new(store.MDB, store_id, true))
  110. return rawget(t, "_store")
  111. end
  112. end,
  113. __newindex = function () return nil, "Module is read only." end
  114. }
  115. setmetatable (M, mt)
  116. -- Initialize random ID generator.
  117. -- TODO This must be set to completely random or use another random generator
  118. -- so that resource IDs are not repeated for each process. For testing it's
  119. -- convenient that resource IDs have always the same pattern.
  120. math.randomseed(M.config.id.seed[1], M.config.id.seed[2])
  121. return M