reworking file store...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2022-07-30 14:20:19 +03:00
parent a0a18b52be
commit e8ea046652

224
pwiki2.js
View File

@ -712,6 +712,149 @@ module.localStorageNestedStore = {
var fs = require('fs') var fs = require('fs')
var glob = require('glob') var glob = require('glob')
// exists(base[, options])
// -> true/false
//
// exists(base, path[, options])
// -> true/false
//
var exists =
module.exists =
async function(base, sub, options={index: '.index'}){
if(typeof(sub) != 'string'){
options = sub
sub = base
base = '' }
var {index} = options
var target = module.path.join(base, sub)
if(!fs.existsSync(target)){
return false }
var stat = await fs.promises.stat(target)
if(stat.isDirectory()){
return fs.existsSync(module.path.join(target, index)) }
return true }
var read =
module.read =
async function(base, sub, options={index: '.index'}){
if(typeof(sub) != 'string'){
options = sub
sub = base
base = '' }
var {index} = options
var target = module.path.join(base, sub)
if(!fs.existsSync(target)){
return undefined }
// handle dir text...
var stat = await fs.promises.stat(target)
if(stat.isDirectory()){
var target = module.path.join(target, index)
fs.existsSync(target)
|| (target = false) }
return target ?
fs.promises.readFile(target, {encoding: 'utf-8'})
: undefined }
var mkdir =
module.mkdir =
async function(base, sub, options={index: '.index'}){
if(typeof(sub) != 'string'){
options = sub
sub = base
base = '' }
var {index} = options
var levels = module.path.split(sub)
for(var level of levels){
base = module.path.join(base, level)
// nothing exists -- create dir and continue...
if(!fs.existsSync(base)){
await fs.promises.mkdir(base, {recursive: true})
continue }
// directory -- continue...
var stat = await fs.promises.stat(base)
if(stat.isDirectory()){
continue }
// file -- convert to dir...
await fs.promises.rename(base, base+'.pwiki-bak')
await fs.promises.mkdir(base, {recursive: true})
await fs.promises.rename(base +'.pwiki-bak', base +'/'+ index) }
return base }
// XXX error checking???
// XXX metadata???
// XXX modes???
// XXX should this transform <dir>/.index into a file if nothing else exists in it???
var update =
module.update =
async function(base, sub, data, options={index: '.index'}){
if(typeof(sub) != 'string'){
options = sub
sub = base
base = '' }
var {index} = options
// path already exists...
if(fs.existsSync(module.path.join(base, sub))){
var stat = await fs.promises.stat(base)
if(stat.isDirectory()){
sub = module.path.join(sub, index) }
// create path / parts of path...
} else {
var levels = module.path.split(sub)
var basename = levels.pop()
// ensure the parent dir exists...
await module.mkdir(base, levels, index) }
// write the data...
var target = module.path.join(base, sub)
var f = await fs.promises.open(target, 'w')
await f.writeFile(data)
f.close()
return target }
var clear =
module.clear =
async function(base, sub, options={index: '.index'}){
if(typeof(sub) != 'string'){
options = sub
sub = base
base = '' }
var {index} = options
// remove leaf...
var target = module.path.join(base, sub)
// dir...
if(fs.existsSync(target)){
var stat = await fs.promises.stat(target)
if(stat.isDirectory()){
var files = await fs.promises.readdir(target)
// remove index...
if(files.includes(index)){
await fs.promises.rm(module.path.join(target, index))
// NOTE: we do not care what we pop as long as the .length
// is correct as we'll not be using the content after
// this point...
files.pop() }
// remove dir if empty...
if(files.length == 0){
fs.promises.rmdir(target) }
// simple file...
} else {
await fs.promises.rm(target) } }
// cleanup path -- remove empty dirs... (XXX ???)
var levels = module.path.split(sub)
.slice(0, -1)
while(levels.length > 0){
var cur = module.path.join(base, ...levels)
if(fs.existsSync(cur)){
var stat = await fs.promises.stat(base)
if(stat.isDirectory()){
// stop cleanup if non-empty dir...
if((await fs.promises.readdir(cur)).length != 0){
break }
fs.promises.rmdir(cur) } }
levels.pop() }
return target }
// XXX add monitor API... // XXX add monitor API...
// XXX backup files on write/delete... // XXX backup files on write/delete...
// XXX do a r/o version... // XXX do a r/o version...
@ -726,6 +869,7 @@ module.FileStore = {
__directory_text__: '.index', __directory_text__: '.index',
// XXX do we remove the extension??? // XXX do we remove the extension???
// XXX BUG? is this recursive???
// XXX cache??? // XXX cache???
__paths__: async function(){ __paths__: async function(){
var that = this var that = this
@ -738,85 +882,27 @@ module.FileStore = {
return path return path
.slice(that.__path__.length) })) }) }) }, .slice(that.__path__.length) })) }) }) },
__exists__: async function(path){ __exists__: async function(path){
var p = module.path.join(this.__path__, path) return module.exists(this.__path__, path, {index: this.__directory_text__})
try { && path },
var stat = await fs.promises.stat(p)
// NOTE: we consider a directory as "existing" iff we can
// produce text for it...
return stat.isDirectory() ?
(!!fs.existsSync(p +'/'+ this.__directory_text__)
&& path)
: !!fs.existsSync(p) ?
path
: false
} catch(err){
return false } },
__get__: async function(path){ __get__: async function(path){
var p = module.path.join(this.__path__, path) var p = module.path.join(this.__path__, path)
var stat = await fs.promises.stat(p) var {atimeMs, mtimeMs, ctimeMs, birthtimeMs} = await fs.promises.stat(p)
var {atimeMs, mtimeMs, ctimeMs, birthtimeMs} = stat
return { return {
atime: atimeMs, atime: atimeMs,
mtime: mtimeMs, mtime: mtimeMs,
ctime: ctimeMs, ctime: ctimeMs,
text: stat.isDirectory() ? text: module.read(p, {index: this.__directory_text__})
fs.readFileSync(p +'/'+ this.__directory_text__).toString()
: fs.readFileSync(p).toString(),
} }, } },
//
// Add data to path...
// .__update_path__(<path>, <data>)
//
// Clear data from path...
// .__update_path__(<path>, undefined)
//
// XXX this is FileStore-specific...
// XXX create a file path and set data...
// - convert files in path to dirs:
// a/b/c -> a/b/c/.text
// - move "c" -> "c.bak"
// - mkdir "c"
// - move "c.bak" -> "c/.text"
// - convert last dir to file if directory empty...
// a/b/c/.text -> a/b/c
// (same as above)
// - remove empty dirs from path...
// XXX might be a good idea to have a store-specific backup/tmp dir...
// XXX might be a good idea to move this functionality to
// .__update__(..) and use it from .__delete__(..)...
__update_path__: async function(path, data, mode='update'){
var p = module.path.join(this.__path__, path)
// write/update...
if(data != null){
// XXX create/update basedir...
// clear/remove...
} else {
// XXX remove file...
// XXX recursively check/remove dirs...
}
},
// XXX handle writing to directories...
// i.e. write to "./"+ this.__directory_text__
// XXX would need to convert a file to a dir if writing to sub-path...
// XXX recursively create all dirs...
// XXX do we write all the data or only the .text??? // XXX do we write all the data or only the .text???
__update__: async function(path, data, mode='update'){ __update__: async function(path, data, mode='update'){
var p = module.path.join(this.__path__, path) return module.update(
this.__path__, path,
var f = await fs.promises.open(p, 'w') data.text,
var size = await f.writeFile(data.text) {index: this.__directory_text__}) },
f.close()
// XXX check size...
// XXX
},
// XXX remove empty dirs (???)
// XXX convert a dir to a file if removing last sub-file/dir...
__delete__: async function(path){ __delete__: async function(path){
var p = module.path.join(this.__path__, path) return module.clear(
// XXX this.__path__, path,
}, {index: this.__directory_text__}) },
load: function(data){ load: function(data){
// XXX // XXX
}, },