Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial cleanup of FileSystems and file tree #176

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions src/v1/FileSystems/FileSystemHelper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import {FILE_SYSTEM_TYPE_LOCAL, IFsItem, IFsItemSummary, IJSTreeNodeHelper, ITEM_MODE_DIRECTORY} from "./FileSystemsTypes";


export const findOrCreateFsPath = (root: IFsItem, filePath: string) => {
var currNode = root;
var fnParts = filePath.split("/");
var currPath = "";
for (var i = 0; i < fnParts.length; i++) {
var fnPart = fnParts[i];
currPath += (currPath ? "/" : "") + fnPart;

if (!("children" in currNode)) {
currNode.children = {};
currNode.type = ITEM_MODE_DIRECTORY;
}

if (!(fnPart in currNode.children))
currNode.children[fnPart] = {fsType: root.fsType, type: "file", fn: currPath};

currNode = currNode.children[fnPart];
}
return currNode;
};


export const mapToJSTreeNode = (fsItem: IFsItem, fn: string): IJSTreeNodeHelper => {
const isFolder = fsItem.type === ITEM_MODE_DIRECTORY;
return {
text: fn,
icon: "glyphicon glyphicon-" + (isFolder ? "folder-open" : fn.endsWith(".ksy") ? "list-alt" : "file"),
children: isFolder ? mapToJSTreeNodes(fsItem) : null,
data: fsItem
};
};

export const mapToJSTreeNodes = (fsItem: IFsItem): IJSTreeNodeHelper[] => {
return Object.keys(fsItem.children || [])
.map(k => mapToJSTreeNode(fsItem.children[k], k));
};

export const getSummaryIfPresent = (fsItem?: IFsItem): IFsItemSummary => {
return fsItem
? getSummary(fsItem)
: emptySummary();
};

const getSummary = (data: IFsItem): IFsItemSummary => {
const isFolder = data.type === ITEM_MODE_DIRECTORY;
return {
isFolder: isFolder,
isLocal: data.fsType === FILE_SYSTEM_TYPE_LOCAL,
isKsy: !isFolder && data.fn && data.fn.endsWith(".ksy")
};
};

const emptySummary = (): IFsItemSummary => {
return {
isFolder: false,
isLocal: false,
isKsy: false
};
};

11 changes: 11 additions & 0 deletions src/v1/FileSystems/FileSystemManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import {LocalStorageFileSystem} from "./LocalStorageFileSystem";
import {initKaitaiFs} from "./KaitaiFileSystem";
import {IFileSystemManager} from "./FileSystemsTypes";

const localFs = new LocalStorageFileSystem("fs");
const kaitaiFs = initKaitaiFs();

export var fileSystemsManager: IFileSystemManager = {
local: localFs,
kaitai: kaitaiFs
};
44 changes: 44 additions & 0 deletions src/v1/FileSystems/FileSystemsTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import {KaitaiFileSystem} from "./KaitaiFileSystem";
import {LocalStorageFileSystem} from "./LocalStorageFileSystem";

export const FILE_SYSTEM_TYPE_LOCAL = "local";
export const FILE_SYSTEM_TYPE_KAITAI = "kaitai";
export const ITEM_MODE_FILE = "file";
export const ITEM_MODE_DIRECTORY = "folder";

export interface IFsItem {
fsType: typeof FILE_SYSTEM_TYPE_LOCAL | typeof FILE_SYSTEM_TYPE_KAITAI;
type: typeof ITEM_MODE_FILE | typeof ITEM_MODE_DIRECTORY;
fn?: string;
children?: { [key: string]: IFsItem; };
}

export interface IFsItemSummary {
isLocal: boolean;
isFolder: boolean;
isKsy: boolean;
}

export interface IFileSystem {
setRootNode(newRoot: IFsItem): Promise<IFsItem>;

getRootNode(): Promise<IFsItem>;

get(fn: string): Promise<string | ArrayBuffer>;

put(fn: string, data: any): Promise<IFsItem>;
}

export interface IFileSystemManager {
local: LocalStorageFileSystem;
kaitai: KaitaiFileSystem;
}

export interface IJSTreeNodeHelper {
id?: string;
text: string;
icon: string;
state?: { opened: boolean; };
children?: IJSTreeNodeHelper[];
data?: IFsItem;
}
83 changes: 83 additions & 0 deletions src/v1/FileSystems/KaitaiFileSystem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import {downloadFile} from "../../utils";
import {findOrCreateFsPath, mapToJSTreeNodes} from "./FileSystemHelper";
import {FILE_SYSTEM_TYPE_KAITAI, IFileSystem, IFsItem, IJSTreeNodeHelper} from "./FileSystemsTypes";

declare var kaitaiFsFiles: string[];

export class KaitaiFileSystem implements IFileSystem {
constructor(public files: IFsItem) {
}

getRootNode() {
return Promise.resolve(this.files);
}

setRootNode(newRoot: IFsItem): Promise<IFsItem> {
throw "KaitaiFileSystem.setRootNode is not implemented";
}
get(fn: string): Promise<string | ArrayBuffer> {
if (fn.toLowerCase().endsWith(".ksy"))
return fetch(fn)
.then(response => {
if (!response.ok) {
let msg;
if (response.status === 404) {
msg = "file not found";
} else {
const textAppendix = response.statusText ? ` (${response.statusText})` : "";
msg = `server responded with HTTP status ${response.status}${textAppendix}`;
}
throw new Error(msg);
}
return response.text();
}, err => {
if (err instanceof TypeError) {
throw new Error(`cannot reach the server (message: ${err.message}), check your internet connection`);
}
throw err;
});
else
return downloadFile(fn);
}

put(fn: string, data: any) {
return Promise.reject("KaitaiFileSystem.put is not implemented!");
}
}

export const initKaitaiFsTreeData = (kaitaiFs: KaitaiFileSystem): IJSTreeNodeHelper => {
const root = kaitaiFs.files;

if (!root.children["formats"]) {
console.error("'formats' node is missing from js/kaitaiFsFiles.js, are you sure 'formats' git submodule is initialized? Try run 'git submodule init; git submodule update --recursive; ./genKaitaiFsFiles.py'!");
(<any>root.children["formats"]) = {};
}


return {
text: "kaitai.io",
icon: "glyphicon glyphicon-cloud",
state: {opened: true},
children: [
{
text: "formats",
icon: "glyphicon glyphicon-book",
children: mapToJSTreeNodes(root.children["formats"]),
state: {opened: true}
},
{
text: "samples",
icon: "glyphicon glyphicon-cd",
children: mapToJSTreeNodes(root.children["samples"]),
state: {opened: true}
},
]
};
};

export const initKaitaiFs = () => {
const kaitaiRoot = <IFsItem>{fsType: FILE_SYSTEM_TYPE_KAITAI};
kaitaiFsFiles.forEach(fn => findOrCreateFsPath(kaitaiRoot, fn));
return new KaitaiFileSystem(kaitaiRoot);
};

68 changes: 68 additions & 0 deletions src/v1/FileSystems/LocalStorageFileSystem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import {findOrCreateFsPath} from "./FileSystemHelper";
import * as localforage from "localforage";
import {FILE_SYSTEM_TYPE_LOCAL, IFileSystem, IFsItem, IJSTreeNodeHelper, ITEM_MODE_DIRECTORY} from "./FileSystemsTypes";

export class LocalStorageFileSystem implements IFileSystem {
constructor(public prefix: string) {
}

private root: IFsItem;

private filesKey() {
return `${this.prefix}_files`;
}

private fileKey(fn: string) {
return `${this.prefix}_file[${fn}]`;
}

private save() {
return localforage.setItem(this.filesKey(), this.root);
}

async getRootNode() {
if (!this.root)
this.root = await localforage.getItem<IFsItem>(this.filesKey()) ||
<IFsItem>{fsType: FILE_SYSTEM_TYPE_LOCAL, type: ITEM_MODE_DIRECTORY, children: {}};
return this.root;
}

setRootNode(newRoot: IFsItem) {
this.root = newRoot;
return this.save();
}

get(fn: string): Promise<string | ArrayBuffer> {
return localforage.getItem<string | ArrayBuffer>(this.fileKey(fn))
.then(content => {
if (content === null) {
throw new Error("file not found");
}
return content;
});
}

put(fn: string, data: any): Promise<IFsItem> {
return this.getRootNode().then(root => {
const node = findOrCreateFsPath(root, fn);
const saveFileAction = localforage.setItem(this.fileKey(fn), data);
const updateFileTreeAction = this.save();
return Promise.all([saveFileAction, updateFileTreeAction])
.then(_ => node);
});
}
}

export const initLocalStorageFsTreeData = (): IJSTreeNodeHelper => {
return {
text: "Local storage",
id: "localStorage",
icon: "glyphicon glyphicon-hdd",
state: {opened: true},
children: [],
data: {
fsType: FILE_SYSTEM_TYPE_LOCAL,
type: ITEM_MODE_DIRECTORY
}
};
};
7 changes: 4 additions & 3 deletions src/v1/KaitaiServices.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { fss, IFsItem } from "./app.files";
import { performanceHelper } from "./utils/PerformanceHelper";
import { performanceHelper } from "./utils/PerformanceHelper";
import KaitaiStructCompiler = require("kaitai-struct-compiler");
import * as jsyaml from "js-yaml";
import {fileSystemsManager} from "./FileSystems/FileSystemManager";
import {IFsItem} from "./FileSystems/FileSystemsTypes";

class SchemaUtils {
static ksyNameToJsName(ksyName: string, isProp: boolean) {
Expand Down Expand Up @@ -63,7 +64,7 @@ class JsImporter implements IYamlImporter {
const sourceAppendix = mode === 'abs' ? 'kaitai.io' : 'local storage';
let ksyContent;
try {
ksyContent = await fss[importedFsType].get(fn);
ksyContent = await fileSystemsManager[importedFsType].get(fn);
} catch (e) {
const error = new Error(`failed to import spec ${fn} from ${sourceAppendix}${e.message ? ': ' + e.message : ''}`);

Expand Down
Loading