Skip to content

Commit

Permalink
Added FileListSort
Browse files Browse the repository at this point in the history
  • Loading branch information
viktor-podzigun committed Apr 4, 2024
1 parent b6a7b47 commit ff3f1b9
Show file tree
Hide file tree
Showing 5 changed files with 271 additions and 0 deletions.
6 changes: 6 additions & 0 deletions src/sort/FileListSort.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { SortMode } from "./SortMode.mjs";

export interface FileListSort {
readonly mode: SortMode;
readonly asc: boolean;
}
113 changes: 113 additions & 0 deletions src/sort/FileListSort.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/**
* @typedef {import("../api/FileListItem").FileListItem} FileListItem
* @typedef {import("./SortMode.mjs").SortMode} SortMode
* @typedef {import("./FileListSort").FileListSort} FileListSort
*/
import SortMode from "./SortMode.mjs";

/**
* @param {FileListSort} sort
* @param {SortMode} nextMode
* @returns {FileListSort}
*/
function nextSort(sort, nextMode) {
/** @returns {boolean} */
function nextAsc() {
if (sort.mode === nextMode) {
return !sort.asc;
}

switch (nextMode) {
case SortMode.Name:
case SortMode.Extension:
case SortMode.Unsorted:
return true;
case SortMode.ModificationTime:
case SortMode.Size:
case SortMode.CreationTime:
case SortMode.AccessTime:
return false;
}
}

return {
mode: nextMode,
asc: nextAsc(),
};
}

/**
* @param {string} s1
* @param {string} s2
* @returns {number}
*/
function strCompare(s1, s2) {
return s1 === s2 ? 0 : s1 < s2 ? -1 : 1;
}

/**
* @param {FileListItem} i1
* @param {FileListItem} i2
* @returns {number}
*/
function sortByName(i1, i2) {
const res = strCompare(i1.nameNormalized(), i2.nameNormalized());
return res === 0 ? strCompare(i1.name, i2.name) : res;
}

/**
* @param {FileListItem} i1
* @param {FileListItem} i2
* @returns {number}
*/
function sortByExt(i1, i2) {
const res = strCompare(i1.extNormalized(), i2.extNormalized());
return res === 0 ? strCompare(i1.ext(), i2.ext()) : res;
}

/**
* @param {FileListItem[]} items
* @param {SortMode} mode
* @returns {FileListItem[]}
*/
function sortItems(items, mode) {
const newItems = [...items];
switch (mode) {
case SortMode.Name:
return newItems.sort(sortByName);
case SortMode.Extension:
return newItems.sort((i1, i2) => {
const res = sortByExt(i1, i2);
return res === 0 ? sortByName(i1, i2) : res;
});
case SortMode.ModificationTime:
return newItems.sort((i1, i2) => {
const res = i1.mtimeMs - i2.mtimeMs;
return res === 0 ? sortByName(i1, i2) : res;
});
case SortMode.Size:
return newItems.sort((i1, i2) => {
const res = i1.size - i2.size;
return res === 0 ? sortByName(i1, i2) : res;
});
case SortMode.Unsorted:
return items;
case SortMode.CreationTime:
return newItems.sort((i1, i2) => {
const res = i1.ctimeMs - i2.ctimeMs;
return res === 0 ? sortByName(i1, i2) : res;
});
case SortMode.AccessTime:
return newItems.sort((i1, i2) => {
const res = i1.atimeMs - i2.atimeMs;
return res === 0 ? sortByName(i1, i2) : res;
});
}
}

const FileListSort = {
nextSort,
sortItems,
};

export default FileListSort;
36 changes: 36 additions & 0 deletions src/sort/SortMode.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* @typedef {"Name" | "Extension" | "ModificationTime" | "Size" | "Unsorted" | "CreationTime" | "AccessTime"} SortMode
*/

/** @type {SortMode} */
const Name = "Name";

/** @type {SortMode} */
const Extension = "Extension";

/** @type {SortMode} */
const ModificationTime = "ModificationTime";

/** @type {SortMode} */
const Size = "Size";

/** @type {SortMode} */
const Unsorted = "Unsorted";

/** @type {SortMode} */
const CreationTime = "CreationTime";

/** @type {SortMode} */
const AccessTime = "AccessTime";

const SortMode = Object.freeze({
Name,
Extension,
ModificationTime,
Size,
Unsorted,
CreationTime,
AccessTime,
});

export default SortMode;
2 changes: 2 additions & 0 deletions test/all.mjs
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
await import("./api/FileListItem.test.mjs");

await import("./sort/FileListSort.test.mjs");
114 changes: 114 additions & 0 deletions test/sort/FileListSort.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/**
* @typedef {import("../../src/api/FileListItem").FileListItem} FileListItem
* @typedef {import("../../src/sort/SortMode.mjs").SortMode} SortMode
* @typedef {import("../../src/sort/FileListSort").FileListSort} FileListSort
*/
import assert from "node:assert/strict";
import FileListItem from "../../src/api/FileListItem.mjs";
import SortMode from "../../src/sort/SortMode.mjs";
import FileListSort from "../../src/sort/FileListSort.mjs";

const { describe, it } = await (async () => {
// @ts-ignore
const module = process.isBun ? "bun:test" : "node:test";
// @ts-ignore
return process.isBun // @ts-ignore
? Promise.resolve({ describe: (_, fn) => fn(), it: test })
: import(module);
})();

const {
Name,
Extension,
ModificationTime,
Size,
Unsorted,
CreationTime,
AccessTime,
} = SortMode;

const { nextSort, sortItems } = FileListSort;

describe("FileListSort.test.mjs", () => {
it("should return next sort ordering when nextSort", () => {
//given
/**
* @param {FileListSort} sort
* @param {SortMode} nextMode
* @param {boolean} nextAsc
*/
function check(sort, nextMode, nextAsc) {
//when & then
assert.deepEqual(nextSort(sort, nextMode), {
mode: nextMode,
asc: nextAsc,
});
}

//when & then
check({ mode: Name, asc: false }, Name, true);
check({ mode: Name, asc: true }, Name, false);
check({ mode: Unsorted, asc: false }, Name, true);
check({ mode: Unsorted, asc: true }, Name, true);
check({ mode: Name, asc: false }, Extension, true);
check({ mode: Name, asc: true }, Extension, true);
check({ mode: Name, asc: false }, Unsorted, true);
check({ mode: Name, asc: true }, Unsorted, true);
check({ mode: Extension, asc: false }, ModificationTime, false);
check({ mode: Extension, asc: true }, ModificationTime, false);
check({ mode: Name, asc: false }, Size, false);
check({ mode: Name, asc: true }, Size, false);
check({ mode: ModificationTime, asc: false }, CreationTime, false);
check({ mode: ModificationTime, asc: true }, CreationTime, false);
check({ mode: Unsorted, asc: false }, AccessTime, false);
check({ mode: Unsorted, asc: true }, AccessTime, false);
});

//prettier-ignore
it("should sort items when sortItems", () => {
//given
const item0 = {...FileListItem("item.bin"), size : 2, atimeMs : 1, mtimeMs : 4, ctimeMs : 2};
const item1 = {...FileListItem("Item.bin"), size : 1, atimeMs : 1, mtimeMs : 3, ctimeMs : 2};
const item2 = {...FileListItem("item2.BIN"), size : 4, atimeMs : 2, mtimeMs : 1, ctimeMs : 3};
const item3 = {...FileListItem("Item3.zip"), size : 3, atimeMs : 4, mtimeMs : 2, ctimeMs : 4};
const item4 = {...FileListItem("item4.ZIP"), size : 3, atimeMs : 3, mtimeMs : 2, ctimeMs : 1};
const items = [item0, item1, item2, item3, item4];
const itemsR = [...items].reverse();

assert.deepEqual(item1.name , "Item.bin");
assert.deepEqual(item1.nameNormalized() , "item.bin");
assert.deepEqual(item2.ext() , "BIN");
assert.deepEqual(item2.extNormalized() , "bin");

/**
* @param {FileListItem[]} items
* @param {SortMode} mode
* @param {FileListItem[]} expectedItems
*/
function check(items, mode, expectedItems) {
//when
const result = sortItems(items, mode);

//then
assert.deepEqual(result, expectedItems);
//should always return new array, if sorted
assert.deepEqual(result !== expectedItems, mode !== Unsorted);
}

//when & then
check(items, Name, [item1, item0, item2, item3, item4]);
check(itemsR, Name, [item1, item0, item2, item3, item4]);
check(items, Extension, [item2, item1, item0, item4, item3]);
check(itemsR, Extension, [item2, item1, item0, item4, item3]);
check(items, ModificationTime, [item2, item3, item4, item1, item0]);
check(itemsR, ModificationTime, [item2, item3, item4, item1, item0]);
check(items, Size, [item1, item0, item3, item4, item2]);
check(itemsR, Size, [item1, item0, item3, item4, item2]);
check(items, Unsorted, items);
check(itemsR, Unsorted, itemsR);
check(items, CreationTime, [item4, item1, item0, item2, item3]);
check(itemsR, CreationTime, [item4, item1, item0, item2, item3]);
check(items, AccessTime, [item1, item0, item2, item4, item3]);
check(itemsR, AccessTime, [item1, item0, item2, item4, item3]);
});
});

0 comments on commit ff3f1b9

Please sign in to comment.