import std/options import std/os import std/strutils import js/fromjs import js/javascript import js/jstypes import utils/mimeguess import utils/twtstr type DeallocFun = proc() {.closure.} Blob* = ref object of RootObj isfile*: bool size* {.jsget.}: uint64 ctype* {.jsget: "type".}: string buffer*: pointer deallocFun*: DeallocFun WebFile* = ref object of Blob webkitRelativePath {.jsget.}: string path*: string file: File #TODO maybe use fd? jsDestructor(Blob) jsDestructor(WebFile) proc newBlob*(buffer: pointer, size: int, ctype: string, deallocFun: DeallocFun): Blob = return Blob( buffer: buffer, size: uint64(size), ctype: ctype, deallocFun: deallocFun ) proc finalize(blob: Blob) {.jsfin.} = if blob.deallocFun != nil and blob.buffer != nil: blob.deallocFun() blob.buffer = nil proc finalize(file: WebFile) {.jsfin.} = if file.deallocFun != nil and file.buffer != nil: file.deallocFun() file.buffer = nil proc newWebFile*(path: string, webkitRelativePath = ""): WebFile = var file: File if not open(file, path, fmRead): raise newException(IOError, "Failed to open file") return WebFile( isfile: true, path: path, file: file, ctype: DefaultGuess.guessContentType(path), webkitRelativePath: webkitRelativePath ) type BlobPropertyBag = object of JSDict `type`: string #TODO endings FilePropertyBag = object of BlobPropertyBag #TODO lastModified: int64 proc newWebFile(ctx: JSContext, fileBits: seq[string], fileName: string, options = FilePropertyBag()): WebFile {.jsctor.} = let file = WebFile( isfile: false, path: fileName, ) var len = 0 for blobPart in fileBits: len += blobPart.len let buffer = alloc(len) file.buffer = buffer file.deallocFun = proc() = dealloc(buffer) var buf = cast[ptr UncheckedArray[uint8]](file.buffer) var i = 0 for blobPart in fileBits: if blobPart.len > 0: copyMem(addr buf[i], unsafeAddr blobPart[0], blobPart.len) i += blobPart.len file.size = uint64(len) block ctype: for c in options.`type`: if c notin char(0x20)..char(0x7E): break ctype file.ctype &= c.toLowerAscii() return file #TODO File, Blob constructors proc getSize*(this: Blob): uint64 = if this.isfile: return uint64(WebFile(this).path.getFileSize()) return this.size proc size*(this: WebFile): uint64 {.jsfget.} = return this.getSize() func name*(this: WebFile): string {.jsfget.} = if this.path.len > 0 and this.path[^1] != '/': return this.path.afterLast('/') return this.path.afterLast('/', 2) #TODO lastModified proc addBlobModule*(ctx: JSContext) = let blobCID = ctx.registerType(Blob) ctx.registerType(WebFile, parent = blobCID, name = "File")