rename and cleanup fs store

This commit is contained in:
Dmitriy Ryajov 2022-09-16 21:13:25 -06:00
parent f0f979539f
commit 2390839406
No known key found for this signature in database
GPG Key ID: DA8C680CE7C657A4
2 changed files with 168 additions and 159 deletions

View File

@ -1,159 +0,0 @@
import std/os
import std/sequtils
import std/options
import pkg/chronos
import pkg/questionable
import pkg/questionable/results
from pkg/stew/results as stewResults import get, isErr
import pkg/upraises
import ./datastore
export datastore
push: {.upraises: [].}
type
FileSystemDatastore* = ref object of Datastore
root*: string
const
objExt* = ".obj"
proc path*(
self: FileSystemDatastore,
key: Key): string =
var
segments: seq[string]
for ns in key:
without field =? ns.field:
segments.add ns.value
continue
segments.add(field / ns.value)
# is it problematic that per this logic Key(/a:b) evaluates to the same path
# as Key(/a/b)? may need to check if/how other Datastore implementations
# distinguish them
self.root / joinPath(segments) & objExt
method contains*(
self: FileSystemDatastore,
key: Key): Future[?!bool] {.async, locks: "unknown".} =
return success fileExists(self.path(key))
method delete*(
self: FileSystemDatastore,
key: Key): Future[?!void] {.async, locks: "unknown".} =
let
path = self.path(key)
try:
removeFile(path)
return success()
# removing an empty directory might lead to surprising behavior depending
# on what the user specified as the `root` of the FileSystemDatastore, so
# until further consideration, empty directories will be left in place
except OSError as e:
return failure e
method get*(
self: FileSystemDatastore,
key: Key): Future[?!(?seq[byte])] {.async, locks: "unknown".} =
# to support finer control of memory allocation, maybe could/should change
# the signature of `get` so that it has a 3rd parameter
# `bytes: var openArray[byte]` and return type `?!bool`; this variant with
# return type `?!(?seq[byte])` would be a special case (convenience method)
# calling the former after allocating a seq with size automatically
# determined via `getFileSize`
let
path = self.path(key)
containsRes = await self.contains(key)
if containsRes.isErr: return failure containsRes.error.msg
if containsRes.get:
var
file: File
if not file.open(path):
return failure "unable to open file: " & path
else:
try:
let
size = file.getFileSize
var
bytes: seq[byte]
if size > 0:
newSeq(bytes, size)
let
bytesRead = file.readBytes(bytes, 0, size)
if bytesRead < size:
return failure $bytesRead & " bytes were read from " & path &
" but " & $size & " bytes were expected"
return success bytes.some
except IOError as e:
return failure e
finally:
file.close
else:
return success seq[byte].none
method put*(
self: FileSystemDatastore,
key: Key,
data: seq[byte]): Future[?!void] {.async, locks: "unknown".} =
let
path = self.path(key)
try:
createDir(parentDir(path))
if data.len > 0: writeFile(path, data)
else: writeFile(path, "")
return success()
except IOError as e:
return failure e
except OSError as e:
return failure e
# method query*(
# self: FileSystemDatastore,
# query: ...): Future[?!(?...)] {.async, locks: "unknown".} =
#
# return success ....some
proc new*(
T: type FileSystemDatastore,
root: string,
caseSensitive = true): ?!T =
let root = ? (
block:
if root.isAbsolute: root
else: getCurrentDir() / root).catch
if not dirExists(root):
failure "directory does not exist: " & root
else:
success T(root: root)

168
datastore/fsstore.nim Normal file
View File

@ -0,0 +1,168 @@
import std/os
import std/sequtils
import std/options
import pkg/chronos
import pkg/questionable
import pkg/questionable/results
from pkg/stew/results as stewResults import get, isErr
import pkg/upraises
import ./datastore
export datastore
push: {.upraises: [].}
const
# TODO: Add more dirs from relevant OSs
# Paths should be matched exactly, i.e.
# we're forbidding this dirs from being
# touched directly, but subdirectories
# can still be touched
ProtectedPaths = [
"/",
"/usr",
"/etc",
"/home",
"/Users"]
type
FSDatastore* = ref object of Datastore
root*: string
ignoreProtected: bool
func checkProtected(dir: string): bool =
dir in ProtectedPaths
proc path*(self: FSDatastore, key: Key): string =
var
segments: seq[string]
for ns in key:
if ns.field == "":
segments.add ns.value
continue
segments.add(ns.field / ns.value)
# is it problematic that per this logic Key(/a:b) evaluates to the same path
# as Key(/a/b)? may need to check if/how other Datastore implementations
# distinguish them
self.root / joinPath(segments)
method contains*(self: FSDatastore, key: Key): Future[?!bool] {.async.} =
return success fileExists(self.path(key))
method delete*(self: FSDatastore, key: Key): Future[?!void] {.async.} =
let
path = self.path(key)
if checkProtected(path):
return failure "Path is protected!"
try:
removeFile(path)
return success()
# removing an empty directory might lead to surprising behavior depending
# on what the user specified as the `root` of the FSDatastore, so
# until further consideration, empty directories will be left in place
except OSError as e:
return failure e
method get*(self: FSDatastore, key: Key): Future[?!seq[byte]] {.async.} =
# to support finer control of memory allocation, maybe could/should change
# the signature of `get` so that it has a 3rd parameter
# `bytes: var openArray[byte]` and return type `?!bool`; this variant with
# return type `?!(?seq[byte])` would be a special case (convenience method)
# calling the former after allocating a seq with size automatically
# determined via `getFileSize`
let
path = self.path(key)
if checkProtected(path):
return failure "Path is protected!"
if not fileExists(path):
return success(newSeq[byte]())
var
file: File
defer:
file.close
if not file.open(path):
return failure "unable to open file: " & path
try:
let
size = file.getFileSize
var
bytes = newSeq[byte](size)
read = 0
while read < size:
read += file.readBytes(bytes, read, size)
if read < size:
return failure $read & " bytes were read from " & path &
" but " & $size & " bytes were expected"
return success bytes
except IOError as e:
return failure e
method put*(
self: FSDatastore,
key: Key,
data: seq[byte]): Future[?!void] {.async, locks: "unknown".} =
let
path = self.path(key)
if checkProtected(path):
return failure "Path is protected!"
try:
createDir(parentDir(path))
writeFile(path, data)
except IOError as e:
return failure e
except OSError as e:
return failure e
return success()
# method query*(
# self: FSDatastore,
# query: ...): Future[?!(?...)] {.async, locks: "unknown".} =
#
# return success ....some
proc new*(
T: type FSDatastore,
root: string,
caseSensitive = true,
ignoreProtected = false): ?!T =
let root = ? (
block:
if root.isAbsolute: root
else: getCurrentDir() / root).catch
if not dirExists(root):
return failure "directory does not exist: " & root
success T(
root: root,
ignoreProtected: ignoreProtected)