@rustable/utils
Version:
Essential utilities for object cloning, string manipulation, and value comparison in TypeScript, inspired by Rust's standard library.
131 lines (128 loc) • 3.23 kB
JavaScript
;
;
function stringifyObject(obj) {
const objectRefs = /* @__PURE__ */ new Map();
let nextRefId = 0;
function scanObject(value) {
if (typeof value !== "object" || value === null) {
return;
}
const existing = objectRefs.get(value);
if (existing) {
existing.count++;
return;
}
objectRefs.set(value, { count: 1 });
if (value[Symbol.iterator]) {
for (const item of value) {
scanObject(item);
}
} else {
Object.keys(value).forEach((key) => scanObject(value[key]));
}
}
scanObject(obj);
for (const [_, info] of objectRefs) {
if (info.count > 1) {
info.id = nextRefId++;
}
}
const processedRefs = /* @__PURE__ */ new Set();
function stringifyValue(value) {
if (value === null || typeof value === "undefined") {
return "";
}
if (Object.is(value, NaN)) {
return "NaN";
}
if (typeof value === "string") {
return `"${value}"`;
}
if (typeof value !== "object") {
return value.toString();
}
if (value instanceof Date) {
return `Date("${value.getTime()}")`;
}
const ref = objectRefs.get(value);
if (ref?.id !== void 0) {
if (processedRefs.has(value)) {
return "#" + ref.id;
}
processedRefs.add(value);
}
let result = "";
if (value[Symbol.iterator]) {
const elements = Array.from(value).map(stringifyValue).join(",");
const typeName = value.constructor.name;
result = typeName !== "Array" ? `${typeName}{${elements}}` : `[${elements}]`;
} else {
const pairs = Object.keys(value).sort().map((key) => `${key}:${stringifyValue(value[key])}`).join(",");
const constructorName = value.constructor && value.constructor.name !== "Object" ? value.constructor.name : null;
if (constructorName) {
result = `${constructorName}{${pairs}}`;
} else {
result = "{" + pairs + "}";
}
}
if (ref?.id !== void 0) {
return "#" + ref.id + result;
}
return result;
}
return stringifyValue(obj);
}
function stringify(obj) {
if (obj === null || typeof obj === "undefined") {
return "";
}
if (Object.is(obj, NaN)) {
return "NaN";
}
if (typeof obj === "string") {
return obj;
}
if (typeof obj === "number") {
return obj + "";
}
if (typeof obj === "boolean") {
return obj ? "true" : "false";
}
if (typeof obj === "object") {
return stringifyObject(obj);
}
return obj.toString();
}
function stringHash(str) {
if (str.length === 0)
return 0;
let hash2 = 0;
let i;
let chr;
for (i = 0; i < str.length; i++) {
chr = str.charCodeAt(i);
hash2 = (hash2 << 5) - hash2 + chr;
hash2 |= 0;
}
return hash2;
}
function hash(obj) {
if (obj === null || typeof obj === "undefined") {
return -1;
}
if (typeof obj === "boolean") {
return obj ? 1 : 0;
}
if (typeof obj === "number") {
return obj;
}
if (typeof obj === "string") {
return stringHash(obj);
}
if (typeof obj === "object") {
return stringHash(stringifyObject(obj));
}
return stringHash(obj.toString());
}
exports.hash = hash;
exports.stringify = stringify;