-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlocalstorage-fs.js
250 lines (219 loc) · 6.75 KB
/
localstorage-fs.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
////////////////////////////////////////////////////////////////////////////////
// Initial fns for file system
////////////////////////////////////////////////////////////////////////////////
// key used to access the localStorage
const fs_key = "local-fs"
// check if localStorage entry exists
// and correctly initialized with root directory
const check_fs = () => {
if (localStorage.getItem(fs_key) === null) return false;
let root = read_directory(0);
if (!(root.type == "directory" && root.name == "/")) return false;
return true;
};
// initialize the fs
// if fs does not exist, add localStorage entry and add root directory.
export const init_fs = () => {
if (!check_fs()) {
let rootDir = new_directory("/", 0, {
// parent of root = root
type: "directory",
name: "/",
createAt: new Date(),
});
write_directory(0, rootDir);
}
};
// clear out the fs
const clear_fs = () => {
localStorage.removeItem(fs_key);
init_fs();
};
// load the file array from localStorage
export const load_fs = () => {
return JSON.parse(localStorage.getItem(fs_key)) || [];
};
// store the given file arrya to localStorage
const store_fs = (fsList) => {
return localStorage.setItem(fs_key, JSON.stringify(fsList));
};
// get_new_id: finding new array index for allocation
// tries to find first empty cell,
// if such cell does not exist, the array is extended
export const get_new_id = () => {
const fsList = load_fs();
for (let i = 0; i < fsList.length; i++) {
if (fsList[i] == undefined) {
// empty element is undefined
return i;
}
}
return fsList.length; // else, return the length
};
////////////////////////////////////////////////////////////////////////////////
// Default object constructors of file / directory
////////////////////////////////////////////////////////////////////////////////
// default constructor for file object
// name: string, parent: id of parent dir, data: object
export const new_file = (name, parent, data) => {
return {
type: "file",
name: name,
parent: parent,
data: data,
};
};
// default constructor for directory
// name: string, parent: id of parent dir,
// children: array of id of children file / directory
export const new_directory = (name, parent, data) => {
return {
type: "directory",
name: name,
parent: parent,
children: [],
data: data,
};
};
////////////////////////////////////////////////////////////////////////////////
// atomic fs access functions
////////////////////////////////////////////////////////////////////////////////
const NotFileError = new Error("Not a file");
const NotDirectoryError = new Error("Not a directory");
const NotExistError = new Error("Not exist");
const AlreadyExistError = new Error("Already exist");
// Implementation Detail
// each funciton start with load_fs, end with store_fs, each exactly once
// (except read functions)
// think fsList as "cache" of the actual fs inside the localStorage
// return file type at id
export const get_fs_type = (id) => {
let fsList = load_fs();
let loaded = fsList[id] || { type: "error" };
return loaded.type;
};
// return file at id
export const read_file = (id) => {
let fsList = load_fs();
let loaded = fsList[id] || { type: "error" };
if (loaded.type != "file") {
throw NotFileError;
}
return loaded;
};
// return directory at id
export const read_directory = (id) => {
let fsList = load_fs();
let loaded = fsList[id] || { type: "error" };
if (loaded.type != "directory") {
throw NotDirectoryError;
}
return loaded;
};
// write a file at id
export const write_file = (id, file) => {
if (file.type != "file") {
throw NotFileError;
}
let fsList = load_fs();
fsList[id] = file;
store_fs(fsList);
};
// write file, but only changing data
export const write_file_data = (id, data) => {
let fsList = load_fs();
let file = fsList[id] || { type: "error" };
if (file.type != "file") {
throw NotFileError;
}
file.data = data;
store_fs(fsList);
};
// write directory at id
export const write_directory = (id, directory) => {
if (directory.type != "directory") {
throw NotDirectoryError;
}
let fsList = load_fs();
fsList[id] = directory;
store_fs(fsList);
};
////////////////////////////////////////////////////////////////////////////////
// adding new files / directories under directory
////////////////////////////////////////////////////////////////////////////////
// add file to directory at id
// also assigns new id to file
// return id of new file
export const add_file_to_dir = (id, new_file) => {
let fsList = load_fs();
// check directory
let dir = fsList[id] || { type: "error" };
if (dir.type != "directory") {
throw NotDirectoryError;
}
let new_id = get_new_id();
fsList[new_id] = new_file;
dir.children.push(new_id);
store_fs(fsList);
return new_id;
};
// add directory to directory at id
// also assigns new id to file
// return id of new dir
export const add_dir_to_dir = (id, new_dir) => {
let fsList = load_fs();
let dir = fsList[id] || { type: "error" };
if (dir.type != "directory") {
throw NotDirectoryError;
}
let new_id = get_new_id();
fsList[new_id] = new_dir;
dir.children.push(new_id);
store_fs(fsList);
return new_id;
};
////////////////////////////////////////////////////////////////////////////////
// remove file at id
////////////////////////////////////////////////////////////////////////////////
export const remove_file = (id, temp_ls) => {
let deleted_temp_ls = temp_ls
let file = temp_ls[id] || { type: "error" };
if (file.type != "file") {
throw NotFileError;
}
// 1) delete link to this child from parent
let parent = temp_ls[file.parent] || { type: "error" };
if (parent.type != "directory") {
throw NotDirectoryError;
}
parent.children = parent.children.filter((child) => child != id);
// 2) actually delete
delete deleted_temp_ls[id];
store_fs(deleted_temp_ls);
return deleted_temp_ls
};
// remove directory at id
export const remove_directory = (id, temp_ls) => {
let deleted_temp_ls = temp_ls
let directory = temp_ls[id] || { type: "error" };
if (directory.type != "directory") {
throw NotDirectoryError;
}
let parent = temp_ls[directory.parent] || { type: "error" };
if (parent.type != "directory") {
throw NotDirectoryError;
}
parent.children = parent.children.filter((child) => child != id);
// recursively delete all children
directory.children.forEach((child) => {
if (temp_ls[child].type == "file") {
deleted_temp_ls= remove_file(child, temp_ls);
} else if (temp_ls[child].type == "directory") {
deleted_temp_ls= remove_directory(child, temp_ls);
}
});
// finally delete the directory
delete deleted_temp_ls[id];
store_fs(deleted_temp_ls);
return deleted_temp_ls
};