import { CHAR_COLON, CHAR_DOT } from "../_common/constants.ts";import type { ParsedPath } from "../_interface.ts";import { assertPath } from "../_common/assert_path.ts";import { isPathSeparator, isWindowsDeviceRoot } from "./_util.ts";
export function parse(path: string): ParsedPath { assertPath(path);
const ret: ParsedPath = { root: "", dir: "", base: "", ext: "", name: "" };
const len = path.length; if (len === 0) return ret;
let rootEnd = 0; let code = path.charCodeAt(0);
if (len > 1) { if (isPathSeparator(code)) {
rootEnd = 1; if (isPathSeparator(path.charCodeAt(1))) { let j = 2; let last = j; for (; j < len; ++j) { if (isPathSeparator(path.charCodeAt(j))) break; } if (j < len && j !== last) { last = j; for (; j < len; ++j) { if (!isPathSeparator(path.charCodeAt(j))) break; } if (j < len && j !== last) { last = j; for (; j < len; ++j) { if (isPathSeparator(path.charCodeAt(j))) break; } if (j === len) {
rootEnd = j; } else if (j !== last) {
rootEnd = j + 1; } } } } } else if (isWindowsDeviceRoot(code)) {
if (path.charCodeAt(1) === CHAR_COLON) { rootEnd = 2; if (len > 2) { if (isPathSeparator(path.charCodeAt(2))) { if (len === 3) { ret.root = ret.dir = path; ret.base = "\\"; return ret; } rootEnd = 3; } } else { ret.root = ret.dir = path; return ret; } } } } else if (isPathSeparator(code)) { ret.root = ret.dir = path; ret.base = "\\"; return ret; }
if (rootEnd > 0) ret.root = path.slice(0, rootEnd);
let startDot = -1; let startPart = rootEnd; let end = -1; let matchedSlash = true; let i = path.length - 1;
let preDotState = 0;
for (; i >= rootEnd; --i) { code = path.charCodeAt(i); if (isPathSeparator(code)) { if (!matchedSlash) { startPart = i + 1; break; } continue; } if (end === -1) { matchedSlash = false; end = i + 1; } if (code === CHAR_DOT) { if (startDot === -1) startDot = i; else if (preDotState !== 1) preDotState = 1; } else if (startDot !== -1) { preDotState = -1; } }
if ( startDot === -1 || end === -1 || preDotState === 0 || (preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) ) { if (end !== -1) { ret.base = ret.name = path.slice(startPart, end); } } else { ret.name = path.slice(startPart, startDot); ret.base = path.slice(startPart, end); ret.ext = path.slice(startDot, end); }
ret.base = ret.base || "\\";
if (startPart > 0 && startPart !== rootEnd) { ret.dir = path.slice(0, startPart - 1); } else ret.dir = ret.root;
return ret;}