UNPKG

farjs-app

Version:

FAR.js - Cross-platform File and Archive Manager app in your terminal

124 lines (110 loc) 3.14 kB
/** * @import { FileListItem } from "@farjs/filelist/api/FileListItem.mjs" */ import { lazyFn, stripPrefix, stripSuffix } from "@farjs/filelist/utils.mjs"; import DateTimeUtil from "../DateTimeUtil.mjs"; /** * @typedef {FileListItem & { * readonly parent: string; * }} ZipEntry */ /** * @param {string} parent * @param {string} name * @param {boolean} [isDir] * @param {number} [size] * @param {number} [datetimeMs] * @param {string} [permissions] * @returns {ZipEntry} */ function ZipEntry(parent, name, isDir, size, datetimeMs, permissions) { const entryTimeMs = datetimeMs ?? 0; return { parent, name, isDir: isDir ?? false, isSymLink: false, size: size ?? 0, atimeMs: entryTimeMs, mtimeMs: entryTimeMs, ctimeMs: entryTimeMs, birthtimeMs: entryTimeMs, permissions: permissions ?? "", }; } /** * @param {string} output * @returns {ZipEntry[]} */ ZipEntry.fromUnzipCommand = (output) => { return output .trim() .split("\n") .map((line) => { const regexRes = itemRegex().exec(line); if (regexRes) { const permissions = regexRes[1]; const size = parseFloat(regexRes[4]); const datetime = regexRes[7]; const pathName = regexRes[8]; const path = stripSuffix(pathName, "/"); const lastSlash = path.lastIndexOf("/"); const [parent, name] = lastSlash !== -1 ? [path.substring(0, lastSlash), path.substring(lastSlash + 1)] : ["", path]; return ZipEntry( parent, stripPrefix(name, "/"), pathName.endsWith("/"), size, DateTimeUtil.parseDateTime(datetime), permissions, ); } return undefined; }) .filter((_) => _ !== undefined); }; /** * @param {readonly ZipEntry[]} entries * @returns {Map<string, readonly FileListItem[]>} */ ZipEntry.groupByParent = (entries) => { /** @type {Set<string>} */ const processedDirs = new Set(); /** @type {Map<string, FileListItem[]>} */ const entriesByParent = new Map(); /** @type {(entry: ZipEntry) => void} */ function ensureDirs(entry) { while (true) { const values = entriesByParent.get(entry.parent) ?? []; if (entry.name === "" || values.find((_) => _.name === entry.name)) { return; } values.push(entry); entriesByParent.set(entry.parent, values); if (processedDirs.has(entry.parent)) { return; } processedDirs.add(entry.parent); const [parent, name] = (() => { const lastSlash = entry.parent.lastIndexOf("/"); return lastSlash !== -1 ? [ entry.parent.substring(0, lastSlash), entry.parent.substring(lastSlash + 1), ] : ["", entry.parent]; })(); entry = ZipEntry(parent, name, true, 0, entry.mtimeMs, "drw-r--r--"); } } entries.forEach(ensureDirs); return entriesByParent; }; /** @type {() => RegExp} */ const itemRegex = lazyFn( () => /([d|-].+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+)/, ); export default ZipEntry;