mirror of
https://github.com/wavetermdev/backup.git
synced 2026-04-22 15:26:58 -07:00
Compare commits
5 Commits
evan/grid
...
sawka/ijson
| Author | SHA1 | Date | |
|---|---|---|---|
| f1a6dfb249 | |||
| 6319b26924 | |||
| 41fe49a54c | |||
| e044487489 | |||
| 0fdcd27fee |
@@ -43,6 +43,7 @@
|
||||
"papaparse": "^5.4.1",
|
||||
"react": "^18.1.0",
|
||||
"react-dom": "^18.1.0",
|
||||
"react-frame-component": "^5.2.6",
|
||||
"react-markdown": "^9.0.0",
|
||||
"remark": "^15.0.1",
|
||||
"remark-gfm": "^4.0.0",
|
||||
|
||||
@@ -226,13 +226,12 @@
|
||||
}
|
||||
|
||||
.ts,
|
||||
.termopts,
|
||||
.renderer {
|
||||
.termopts {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.renderer .renderer-icon {
|
||||
margin-right: 0.5em;
|
||||
.renderer-icon {
|
||||
margin-right: 0.2em;
|
||||
}
|
||||
|
||||
.metapart-mono {
|
||||
|
||||
@@ -291,10 +291,10 @@ class LineHeader extends React.Component<{ screen: LineContainerType; line: Line
|
||||
</div>
|
||||
<If condition={!isBlank(renderer) && renderer != "terminal"}>
|
||||
<div className="meta-divider">|</div>
|
||||
<div className="renderer">
|
||||
<div className="renderer-icon">
|
||||
<i className="fa-sharp fa-solid fa-fill renderer-icon" />
|
||||
{renderer}
|
||||
</div>
|
||||
<div className="renderer-name">{renderer}</div>
|
||||
</If>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -129,4 +129,56 @@ class PacketDataBuffer extends PtyDataBuffer {
|
||||
}
|
||||
}
|
||||
|
||||
export { PtyDataBuffer, PacketDataBuffer };
|
||||
class JsonLinesDataBuffer extends PtyDataBuffer {
|
||||
parsePos: number;
|
||||
callback: (any) => void;
|
||||
|
||||
constructor(callback: (any) => void) {
|
||||
super();
|
||||
this.parsePos = 0;
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
reset(): void {
|
||||
super.reset();
|
||||
this.parsePos = 0;
|
||||
}
|
||||
|
||||
processLine(line: string) {
|
||||
if (line.length == 0) {
|
||||
return;
|
||||
}
|
||||
let jsonVal: any = null;
|
||||
try {
|
||||
jsonVal = JSON.parse(line.trim());
|
||||
} catch (e) {
|
||||
console.log("invalid json", line, e);
|
||||
return;
|
||||
}
|
||||
if (jsonVal != null) {
|
||||
this.callback(jsonVal);
|
||||
}
|
||||
}
|
||||
|
||||
parseData() {
|
||||
for (let i = this.parsePos; i < this.dataSize; i++) {
|
||||
let ch = this.rawData[i];
|
||||
if (ch == NewLineCharCode) {
|
||||
// line does *not* include the newline
|
||||
let line = new TextDecoder().decode(
|
||||
new Uint8Array(this.rawData.buffer, this.parsePos, i - this.parsePos)
|
||||
);
|
||||
this.parsePos = i + 1;
|
||||
this.processLine(line);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
receiveData(pos: number, data: Uint8Array, reason?: string): void {
|
||||
super.receiveData(pos, data, reason);
|
||||
this.parseData();
|
||||
}
|
||||
}
|
||||
|
||||
export { PtyDataBuffer, PacketDataBuffer, JsonLinesDataBuffer };
|
||||
|
||||
@@ -9,6 +9,7 @@ import { CSVRenderer } from "./csv/csv";
|
||||
import { OpenAIRenderer, OpenAIRendererModel } from "./openai/openai";
|
||||
import { SimplePdfRenderer } from "./pdf/pdf";
|
||||
import { SimpleMediaRenderer } from "./media/media";
|
||||
import { WaveAppRenderer, WaveAppRendererModel } from "./waveapp/waveapp";
|
||||
import { isBlank } from "@/util/util";
|
||||
import { sprintf } from "sprintf-js";
|
||||
|
||||
@@ -100,6 +101,18 @@ const PluginConfigs: RendererPluginType[] = [
|
||||
mimeTypes: ["video/*", "audio/*"],
|
||||
simpleComponent: SimpleMediaRenderer,
|
||||
},
|
||||
{
|
||||
name: "waveapp",
|
||||
rendererType: "full",
|
||||
heightType: "pixels",
|
||||
dataType: "model",
|
||||
collapseType: "remove",
|
||||
hidePrompt: true,
|
||||
globalCss: null,
|
||||
mimeTypes: ["application/x-waveapp"],
|
||||
fullComponent: WaveAppRenderer,
|
||||
modelCtor: () => new WaveAppRendererModel(),
|
||||
},
|
||||
];
|
||||
|
||||
class PluginModelClass {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
# WaveApp Plugin
|
||||
@@ -0,0 +1,6 @@
|
||||
// Copyright 2024, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
.waveapp-renderer {
|
||||
line-height: normal;
|
||||
}
|
||||
@@ -0,0 +1,248 @@
|
||||
// Copyright 2024, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import * as React from "react";
|
||||
import * as mobx from "mobx";
|
||||
import * as mobxReact from "mobx-react";
|
||||
import { debounce } from "throttle-debounce";
|
||||
import { boundMethod } from "autobind-decorator";
|
||||
import { JsonLinesDataBuffer } from "../core/ptydata";
|
||||
import { Markdown } from "@/elements";
|
||||
import * as ijson from "@/util/ijson";
|
||||
import Frame from "react-frame-component";
|
||||
|
||||
import "./waveapp.less";
|
||||
|
||||
type WaveAppProps = {
|
||||
lineId: string;
|
||||
isSelected: boolean;
|
||||
isFocused: boolean;
|
||||
savedHeight: number;
|
||||
initialData: any;
|
||||
onPacket: (packetFn: (packet: any) => void) => void;
|
||||
};
|
||||
|
||||
type WaveAppNode = {
|
||||
tag: string;
|
||||
props?: Record<string, any>;
|
||||
children?: (WaveAppNode | string)[];
|
||||
};
|
||||
|
||||
const TagMap: Record<string, React.ComponentType<{ node: WaveAppNode }>> = {};
|
||||
|
||||
function convertNodeToTag(node: WaveAppNode | string, idx?: number): JSX.Element | string {
|
||||
if (node == null) {
|
||||
return null;
|
||||
}
|
||||
if (idx == null) {
|
||||
idx = 0;
|
||||
}
|
||||
if (typeof node === "string") {
|
||||
return node;
|
||||
}
|
||||
let key = node.props?.key ?? "child-" + idx;
|
||||
let TagComp = TagMap[node.tag];
|
||||
if (!TagComp) {
|
||||
return (
|
||||
<div key={key} s>
|
||||
Unknown tag:{node.tag}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return <TagComp key={key} node={node} />;
|
||||
}
|
||||
|
||||
@mobxReact.observer
|
||||
class WaveAppHtmlTag extends React.Component<{ node: WaveAppNode }, {}> {
|
||||
render() {
|
||||
let { tag, props, children } = this.props.node;
|
||||
let divProps = {};
|
||||
if (props != null) {
|
||||
for (let [key, val] of Object.entries(props)) {
|
||||
if (key.startsWith("on")) {
|
||||
divProps[key] = (e: any) => {
|
||||
console.log("handler", key, val);
|
||||
};
|
||||
} else {
|
||||
divProps[key] = mobx.toJS(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
let childrenComps: (string | JSX.Element)[] = [];
|
||||
if (children != null) {
|
||||
for (let idx = 0; idx < children.length; idx++) {
|
||||
let comp = convertNodeToTag(children[idx], idx);
|
||||
if (comp != null) {
|
||||
childrenComps.push(comp);
|
||||
}
|
||||
}
|
||||
}
|
||||
return React.createElement(tag, divProps, childrenComps);
|
||||
}
|
||||
}
|
||||
|
||||
TagMap["div"] = WaveAppHtmlTag;
|
||||
TagMap["b"] = WaveAppHtmlTag;
|
||||
TagMap["i"] = WaveAppHtmlTag;
|
||||
TagMap["p"] = WaveAppHtmlTag;
|
||||
TagMap["span"] = WaveAppHtmlTag;
|
||||
TagMap["a"] = WaveAppHtmlTag;
|
||||
TagMap["h1"] = WaveAppHtmlTag;
|
||||
TagMap["h2"] = WaveAppHtmlTag;
|
||||
TagMap["h3"] = WaveAppHtmlTag;
|
||||
TagMap["h4"] = WaveAppHtmlTag;
|
||||
TagMap["h5"] = WaveAppHtmlTag;
|
||||
TagMap["h6"] = WaveAppHtmlTag;
|
||||
TagMap["ul"] = WaveAppHtmlTag;
|
||||
TagMap["ol"] = WaveAppHtmlTag;
|
||||
TagMap["li"] = WaveAppHtmlTag;
|
||||
|
||||
class WaveAppRendererModel {
|
||||
context: RendererContext;
|
||||
opts: RendererOpts;
|
||||
isDone: OV<boolean>;
|
||||
api: RendererModelContainerApi;
|
||||
savedHeight: number;
|
||||
loading: OV<boolean>;
|
||||
ptyDataSource: (termContext: TermContextUnion) => Promise<PtyDataType>;
|
||||
packetData: JsonLinesDataBuffer;
|
||||
rawCmd: WebCmd;
|
||||
version: OV<number>;
|
||||
loadError: OV<string> = mobx.observable.box(null, { name: "renderer-loadError" });
|
||||
data: OV<any> = mobx.observable.box(null, { name: "renderer-data" });
|
||||
|
||||
constructor() {
|
||||
this.packetData = new JsonLinesDataBuffer(this.packetCallback.bind(this));
|
||||
this.version = mobx.observable.box(0);
|
||||
}
|
||||
|
||||
initialize(params: RendererModelInitializeParams): void {
|
||||
this.loading = mobx.observable.box(true, { name: "renderer-loading" });
|
||||
this.isDone = mobx.observable.box(params.isDone, { name: "renderer-isDone" });
|
||||
this.context = params.context;
|
||||
this.opts = params.opts;
|
||||
this.api = params.api;
|
||||
this.savedHeight = params.savedHeight;
|
||||
this.ptyDataSource = params.ptyDataSource;
|
||||
this.rawCmd = params.rawCmd;
|
||||
setTimeout(() => this.reload(0), 10);
|
||||
}
|
||||
|
||||
packetCallback(jsonVal: any) {
|
||||
console.log("packet-callback", jsonVal);
|
||||
try {
|
||||
let data = this.data.get();
|
||||
let newData = ijson.applyCommand(data, jsonVal);
|
||||
console.log("got newdata", newData);
|
||||
if (newData != data) {
|
||||
mobx.action(() => {
|
||||
this.data.set(newData);
|
||||
})();
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("error adding data", e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
return;
|
||||
}
|
||||
|
||||
reload(delayMs: number): void {
|
||||
mobx.action(() => {
|
||||
this.loading.set(true);
|
||||
this.loadError.set(null);
|
||||
})();
|
||||
let rtnp = this.ptyDataSource(this.context);
|
||||
if (rtnp == null) {
|
||||
console.log("no promise returned from ptyDataSource (waveapp renderer)", this.context);
|
||||
return;
|
||||
}
|
||||
rtnp.then((ptydata) => {
|
||||
setTimeout(() => {
|
||||
this.packetData.reset();
|
||||
this.receiveData(ptydata.pos, ptydata.data, "reload");
|
||||
mobx.action(() => {
|
||||
this.loading.set(false);
|
||||
})();
|
||||
}, delayMs);
|
||||
}).catch((e) => {
|
||||
console.log("error loading data", e);
|
||||
mobx.action(() => {
|
||||
this.loadError.set("error loading data: " + e);
|
||||
})();
|
||||
});
|
||||
}
|
||||
|
||||
giveFocus(): void {
|
||||
return;
|
||||
}
|
||||
|
||||
updateOpts(opts: RendererOptsUpdate): void {
|
||||
Object.assign(this.opts, opts);
|
||||
}
|
||||
|
||||
setIsDone(): void {
|
||||
if (this.isDone.get()) {
|
||||
return;
|
||||
}
|
||||
mobx.action(() => {
|
||||
this.isDone.set(true);
|
||||
})();
|
||||
}
|
||||
|
||||
receiveData(pos: number, data: Uint8Array, reason?: string): void {
|
||||
this.packetData.receiveData(pos, data, reason);
|
||||
}
|
||||
|
||||
updateHeight(newHeight: number): void {
|
||||
if (this.savedHeight != newHeight) {
|
||||
this.savedHeight = newHeight;
|
||||
this.api.saveHeight(newHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@mobxReact.observer
|
||||
class WaveAppRenderer extends React.Component<{ model: WaveAppRendererModel }, {}> {
|
||||
renderError() {
|
||||
const model = this.props.model;
|
||||
return <div className="load-error-text">{model.loadError.get()}</div>;
|
||||
}
|
||||
|
||||
render() {
|
||||
let model = this.props.model;
|
||||
let styleVal = null;
|
||||
if (model.loading.get() && model.savedHeight >= 0 && model.isDone) {
|
||||
styleVal = {
|
||||
height: model.savedHeight,
|
||||
maxHeight: model.opts.maxSize.height,
|
||||
};
|
||||
} else {
|
||||
styleVal = {
|
||||
maxHeight: model.opts.maxSize.height,
|
||||
};
|
||||
}
|
||||
let version = model.version.get();
|
||||
let loadError = model.loadError.get();
|
||||
if (loadError != null) {
|
||||
return (
|
||||
<div className="waveapp-renderer" style={styleVal}>
|
||||
{this.renderError()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
let node = model.data.get();
|
||||
if (node == null) {
|
||||
return <div className="waveapp-renderer" style={styleVal} />;
|
||||
}
|
||||
return (
|
||||
<div className="waveapp-renderer" style={styleVal}>
|
||||
<Frame>{convertNodeToTag(node)}</Frame>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export { WaveAppRendererModel, WaveAppRenderer };
|
||||
@@ -0,0 +1,255 @@
|
||||
// Copyright 2024, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// ijson values are regular JSON values: string, number, boolean, null, object, array
|
||||
// path is an array of strings and numbers
|
||||
|
||||
import * as mobx from "mobx";
|
||||
|
||||
type PathType = (string | number)[];
|
||||
|
||||
var simplePathStrRe = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
||||
|
||||
function formatPath(path: PathType): string {
|
||||
if (path.length == 0) {
|
||||
return "$";
|
||||
}
|
||||
let pathStr = "$";
|
||||
for (let pathPart of path) {
|
||||
if (typeof pathPart === "string") {
|
||||
if (simplePathStrRe.test(pathPart)) {
|
||||
pathStr += "." + pathPart;
|
||||
} else {
|
||||
pathStr += "[" + JSON.stringify(pathPart) + "]";
|
||||
}
|
||||
} else if (typeof pathPart === "number") {
|
||||
pathStr += "[" + pathPart + "]";
|
||||
} else {
|
||||
pathStr += ".*";
|
||||
}
|
||||
}
|
||||
return pathStr;
|
||||
}
|
||||
|
||||
function isArray(obj: any): boolean {
|
||||
return obj != null && (Array.isArray(obj) || mobx.isObservableArray(obj));
|
||||
}
|
||||
|
||||
function isObject(obj: any): boolean {
|
||||
return obj != null && obj instanceof Object && !isArray(obj);
|
||||
}
|
||||
|
||||
function getPath(obj: any, path: PathType): any {
|
||||
let cur = obj;
|
||||
for (let pathPart of path) {
|
||||
if (cur == null) {
|
||||
return null;
|
||||
}
|
||||
if (typeof pathPart === "string") {
|
||||
if (isObject(cur)) {
|
||||
cur = cur[pathPart];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else if (typeof pathPart === "number") {
|
||||
if (isArray(cur)) {
|
||||
cur = cur[pathPart];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
throw new Error("Invalid path part: " + pathPart);
|
||||
}
|
||||
}
|
||||
return cur;
|
||||
}
|
||||
|
||||
type SetPathOpts = {
|
||||
force?: boolean;
|
||||
remove?: boolean;
|
||||
combinefn?: (oldVal: any, newVal: any, opts: SetPathOpts) => any;
|
||||
};
|
||||
|
||||
function combineFn_arrayAppend(oldVal: any, newVal: any, opts: SetPathOpts): any {
|
||||
if (oldVal == null) {
|
||||
return [newVal];
|
||||
}
|
||||
if (!isArray(oldVal) && !opts.force) {
|
||||
throw new Error("Cannot append to non-array: " + oldVal);
|
||||
}
|
||||
if (!isArray(oldVal)) {
|
||||
return [newVal];
|
||||
}
|
||||
oldVal.push(newVal);
|
||||
return oldVal;
|
||||
}
|
||||
|
||||
function checkPath(path: PathType): boolean {
|
||||
if (!isArray(path)) {
|
||||
return false;
|
||||
}
|
||||
for (let pathPart of path) {
|
||||
if (typeof pathPart !== "string" && typeof pathPart !== "number") {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function setPath(obj: any, path: PathType, value: any, opts: SetPathOpts) {
|
||||
if (opts == null) {
|
||||
opts = {};
|
||||
}
|
||||
if (opts.remove && value != null) {
|
||||
throw new Error("Cannot set value and remove at the same time");
|
||||
}
|
||||
if (path == null) {
|
||||
path = [];
|
||||
}
|
||||
if (!checkPath(path)) {
|
||||
throw new Error("Invalid path: " + formatPath(path));
|
||||
}
|
||||
return setPathInternal(obj, path, value, opts);
|
||||
}
|
||||
|
||||
function isEmpty(obj: any): boolean {
|
||||
if (obj == null) {
|
||||
return true;
|
||||
}
|
||||
if (isArray(obj)) {
|
||||
return obj.length == 0;
|
||||
}
|
||||
if (isObject(obj)) {
|
||||
for (let key in obj) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function removeFromArr(arr: any[], idx: number): any[] {
|
||||
console.log("removefromarray", arr, idx);
|
||||
if (idx >= arr.length) {
|
||||
return arr;
|
||||
}
|
||||
if (idx == arr.length - 1) {
|
||||
arr.pop();
|
||||
if (arr.length == 0) {
|
||||
return null;
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
arr[idx] = null;
|
||||
return arr;
|
||||
}
|
||||
|
||||
function setPathInternal(obj: any, path: PathType, value: any, opts: SetPathOpts): any {
|
||||
if (path.length == 0) {
|
||||
if (opts.combinefn != null) {
|
||||
return opts.combinefn(obj, value, opts);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
const pathPart = path[0];
|
||||
if (typeof pathPart === "string") {
|
||||
if (obj == null) {
|
||||
if (opts.remove) {
|
||||
return null;
|
||||
}
|
||||
obj = {};
|
||||
}
|
||||
if (!isObject(obj)) {
|
||||
if (opts.force) {
|
||||
obj = {};
|
||||
} else {
|
||||
throw new Error("Cannot set path on non-object: " + obj);
|
||||
}
|
||||
}
|
||||
if (opts.remove && path.length == 1) {
|
||||
delete obj[pathPart];
|
||||
if (isEmpty(obj)) {
|
||||
return null;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
const newVal = setPathInternal(obj[pathPart], path.slice(1), value, opts);
|
||||
if (opts.remove && newVal == null) {
|
||||
delete obj[pathPart];
|
||||
if (isEmpty(obj)) {
|
||||
return null;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
obj[pathPart] = newVal;
|
||||
return obj;
|
||||
} else if (typeof pathPart === "number") {
|
||||
if (pathPart < 0 || !Number.isInteger(pathPart)) {
|
||||
throw new Error("Invalid path part: " + pathPart);
|
||||
}
|
||||
if (obj == null) {
|
||||
if (opts.remove) {
|
||||
return null;
|
||||
}
|
||||
obj = [];
|
||||
}
|
||||
if (!isArray(obj)) {
|
||||
if (opts.force) {
|
||||
obj = [];
|
||||
} else {
|
||||
throw new Error("Cannot set path on non-array: " + obj);
|
||||
}
|
||||
}
|
||||
if (opts.remove && path.length == 1) {
|
||||
return removeFromArr(obj, pathPart);
|
||||
}
|
||||
const newVal = setPathInternal(obj[pathPart], path.slice(1), value, opts);
|
||||
if (opts.remove && newVal == null) {
|
||||
return removeFromArr(obj, pathPart);
|
||||
}
|
||||
obj[pathPart] = newVal;
|
||||
return obj;
|
||||
} else {
|
||||
throw new Error("Invalid path part: " + pathPart);
|
||||
}
|
||||
}
|
||||
|
||||
function getCommandPath(command: object): PathType {
|
||||
if (command["path"] == null) {
|
||||
return [];
|
||||
}
|
||||
return command["path"];
|
||||
}
|
||||
|
||||
function applyCommand(data: any, command: any): any {
|
||||
if (command == null) {
|
||||
throw new Error("Invalid command (null)");
|
||||
}
|
||||
if (!isObject(command)) {
|
||||
throw new Error("Invalid command (not an object): " + command);
|
||||
}
|
||||
const commandType = command.type;
|
||||
if (commandType == null) {
|
||||
throw new Error("Invalid command (no type): " + command);
|
||||
}
|
||||
const path = getCommandPath(command);
|
||||
if (!checkPath(path)) {
|
||||
throw new Error("Invalid command path: " + formatPath(path));
|
||||
}
|
||||
switch (commandType) {
|
||||
case "set":
|
||||
return setPath(data, path, command.value, null);
|
||||
|
||||
case "del":
|
||||
return setPath(data, path, null, { remove: true });
|
||||
|
||||
case "append":
|
||||
return setPath(data, path, command.value, { combinefn: combineFn_arrayAppend });
|
||||
|
||||
default:
|
||||
throw new Error("Invalid command type: " + commandType);
|
||||
}
|
||||
}
|
||||
|
||||
export type { PathType, SetPathOpts };
|
||||
export { getPath, setPath, applyCommand, combineFn_arrayAppend };
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,164 @@
|
||||
// Copyright 2024, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package ijson
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestDeepEqual(t *testing.T) {
|
||||
if !DeepEqual(float64(1), float64(1)) {
|
||||
t.Errorf("DeepEqual(1, 1) should be true")
|
||||
}
|
||||
if DeepEqual(float64(1), float64(2)) {
|
||||
t.Errorf("DeepEqual(1, 2) should be false")
|
||||
}
|
||||
if !DeepEqual([]any{"a", 2.8, true, map[string]any{"c": 1.1}}, []any{"a", 2.8, true, map[string]any{"c": 1.1}}) {
|
||||
t.Errorf("DeepEqual complex should be true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetPath(t *testing.T) {
|
||||
data := []any{"a", 2.8, true, map[string]any{"c": 1.1}}
|
||||
|
||||
rtn, err := GetPath(data, []any{0})
|
||||
if err != nil {
|
||||
t.Errorf("GetPath failed: %v", err)
|
||||
}
|
||||
if rtn != "a" {
|
||||
t.Errorf("GetPath failed: %v", rtn)
|
||||
}
|
||||
|
||||
rtn, err = GetPath(data, []any{50})
|
||||
if err != nil {
|
||||
t.Errorf("GetPath failed: %v", err)
|
||||
}
|
||||
if rtn != nil {
|
||||
t.Errorf("GetPath failed: %v", rtn)
|
||||
}
|
||||
|
||||
rtn, err = GetPath(data, []any{3, "c"})
|
||||
if err != nil {
|
||||
t.Errorf("GetPath failed: %v", err)
|
||||
}
|
||||
if rtn != 1.1 {
|
||||
t.Errorf("GetPath failed: %v", rtn)
|
||||
}
|
||||
}
|
||||
|
||||
func makeValue() any {
|
||||
return []any{"a", 2.8, true, map[string]any{"c": 1.1}}
|
||||
}
|
||||
|
||||
func TestSetPath(t *testing.T) {
|
||||
rtn, err := SetPath(makeValue(), []any{0}, "b", nil)
|
||||
if err != nil {
|
||||
t.Errorf("SetPath failed: %v", err)
|
||||
}
|
||||
if rtn.([]any)[0] != "b" {
|
||||
t.Errorf("SetPath failed: %v", rtn)
|
||||
}
|
||||
rtn, err = SetPath(makeValue(), []any{10}, "b", nil)
|
||||
if err != nil {
|
||||
t.Errorf("SetPath failed: %v", err)
|
||||
}
|
||||
if len(rtn.([]any)) != 11 {
|
||||
t.Errorf("SetPath failed: %v", rtn)
|
||||
}
|
||||
rtn, _ = GetPath(rtn, []any{10})
|
||||
if rtn != "b" {
|
||||
t.Errorf("SetPath failed: %v", rtn)
|
||||
}
|
||||
_, err = SetPath(makeValue(), []any{"a"}, "b", nil)
|
||||
if err == nil {
|
||||
t.Errorf("SetPath should have failed")
|
||||
}
|
||||
rtn, err = SetPath(makeValue(), []any{"a"}, "b", &SetPathOpts{Force: true})
|
||||
if err != nil {
|
||||
t.Errorf("SetPath failed: %v", err)
|
||||
}
|
||||
if !DeepEqual(rtn, map[string]any{"a": "b"}) {
|
||||
t.Errorf("SetPath failed: %v", rtn)
|
||||
}
|
||||
rtn, err = SetPath(makeValue(), nil, "c", &SetPathOpts{CombineFn: CombineFn_ArrayAppend})
|
||||
if err != nil {
|
||||
t.Errorf("SetPath failed: %v", err)
|
||||
}
|
||||
if !DeepEqual(rtn, []any{"a", 2.8, true, map[string]any{"c": 1.1}, "c"}) {
|
||||
t.Errorf("SetPath failed: %v", rtn)
|
||||
}
|
||||
_, err = SetPath(makeValue(), nil, "c", &SetPathOpts{CombineFn: CombineFn_ArrayAppend, Budget: -1})
|
||||
if err == nil {
|
||||
t.Errorf("SetPath should have failed")
|
||||
}
|
||||
rtn, err = SetPath(makeValue(), []any{5000}, "c", nil)
|
||||
if err != nil {
|
||||
t.Errorf("SetPath failed: %v", err)
|
||||
}
|
||||
if len(rtn.([]any)) != 5001 {
|
||||
t.Errorf("SetPath failed: %v", rtn)
|
||||
}
|
||||
_, err = SetPath(makeValue(), []any{5000}, "c", &SetPathOpts{Budget: 1000})
|
||||
if err == nil {
|
||||
t.Errorf("SetPath should have failed")
|
||||
}
|
||||
rtn, err = SetPath(makeValue(), []any{3, "c"}, nil, &SetPathOpts{Remove: true})
|
||||
if err != nil {
|
||||
t.Errorf("SetPath failed: %v", err)
|
||||
}
|
||||
if !DeepEqual(rtn, []any{"a", 2.8, true}) {
|
||||
t.Errorf("SetPath failed: %v", rtn)
|
||||
}
|
||||
rtn, _ = SetPath(makeValue(), []any{3}, nil, &SetPathOpts{Remove: true})
|
||||
rtn, _ = SetPath(rtn, []any{2}, nil, &SetPathOpts{Remove: true})
|
||||
rtn, _ = SetPath(rtn, []any{1}, nil, &SetPathOpts{Remove: true})
|
||||
rtn, _ = SetPath(rtn, []any{0}, nil, &SetPathOpts{Remove: true})
|
||||
if rtn != nil {
|
||||
t.Errorf("SetPath failed: %v", rtn)
|
||||
}
|
||||
rtn, err = SetPath(makeValue(), []any{3, "d"}, 2.2, nil)
|
||||
if err != nil {
|
||||
t.Errorf("SetPath failed: %v", err)
|
||||
}
|
||||
if !DeepEqual(rtn, []any{"a", 2.8, true, map[string]any{"c": 1.1, "d": 2.2}}) {
|
||||
t.Errorf("SetPath failed: %v", rtn)
|
||||
}
|
||||
|
||||
rtn, err = SetPath(makeValue(), []any{1}, 2.2, &SetPathOpts{CombineFn: CombineFn_Inc})
|
||||
if err != nil {
|
||||
t.Errorf("SetPath failed: %v", err)
|
||||
}
|
||||
if !DeepEqual(rtn, []any{"a", 5.0, true, map[string]any{"c": 1.1}}) {
|
||||
t.Errorf("SetPath failed: %v", rtn)
|
||||
}
|
||||
|
||||
rtn, err = SetPath(makeValue(), []any{1}, 500.0, &SetPathOpts{CombineFn: CombineFn_Min})
|
||||
if err != nil {
|
||||
t.Errorf("SetPath failed: %v", err)
|
||||
}
|
||||
if rtn.([]any)[1] != 2.8 {
|
||||
t.Errorf("SetPath failed: %v", rtn)
|
||||
}
|
||||
|
||||
rtn, err = SetPath(makeValue(), []any{1}, 500.0, &SetPathOpts{CombineFn: CombineFn_Max})
|
||||
if err != nil {
|
||||
t.Errorf("SetPath failed: %v", err)
|
||||
}
|
||||
if rtn.([]any)[1] != 500.0 {
|
||||
t.Errorf("SetPath failed: %v", rtn)
|
||||
}
|
||||
|
||||
rtn, err = SetPath(makeValue(), []any{1}, 500.0, &SetPathOpts{CombineFn: CombineFn_SetUnless})
|
||||
if err != nil {
|
||||
t.Errorf("SetPath failed: %v", err)
|
||||
}
|
||||
if rtn.([]any)[1] != 2.8 {
|
||||
t.Errorf("SetPath failed: %v", rtn)
|
||||
}
|
||||
rtn, err = SetPath(makeValue(), []any{8}, 500.0, &SetPathOpts{CombineFn: CombineFn_SetUnless})
|
||||
if err != nil {
|
||||
t.Errorf("SetPath failed: %v", err)
|
||||
}
|
||||
if rtn.([]any)[8] != 500.0 {
|
||||
t.Errorf("SetPath failed: %v", rtn)
|
||||
}
|
||||
}
|
||||
@@ -6901,6 +6901,11 @@ react-error-boundary@^3.1.4:
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.12.5"
|
||||
|
||||
react-frame-component@^5.2.6:
|
||||
version "5.2.6"
|
||||
resolved "https://registry.yarnpkg.com/react-frame-component/-/react-frame-component-5.2.6.tgz#0d9991d251ff1f7177479d8f370deea06b824b79"
|
||||
integrity sha512-CwkEM5VSt6nFwZ1Op8hi3JB5rPseZlmnp5CGiismVTauE6S4Jsc4TNMlT0O7Cts4WgIC3ZBAQ2p1Mm9XgLbj+w==
|
||||
|
||||
react-is@^16.13.1:
|
||||
version "16.13.1"
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
||||
|
||||
Reference in New Issue
Block a user