2020-01-03 13:13:49 -07:00
|
|
|
import {
|
2020-02-28 09:35:32 -07:00
|
|
|
RootFolderNode, FolderUnion, FolderNodeMedial, FolderNodeInitial,
|
2020-01-03 13:13:49 -07:00
|
|
|
} from "./interfaces";
|
2019-11-27 06:01:39 -07:00
|
|
|
import { defensiveClone } from "../util";
|
2019-11-26 14:49:18 -07:00
|
|
|
|
|
|
|
interface TreeClimberState {
|
|
|
|
active: boolean;
|
|
|
|
}
|
|
|
|
|
|
|
|
interface VisitorProps {
|
2019-11-27 06:01:39 -07:00
|
|
|
node: FolderNodeInitial | FolderNodeMedial;
|
2019-11-26 14:49:18 -07:00
|
|
|
callback: TreeClimber;
|
|
|
|
halt: Halt;
|
|
|
|
state: TreeClimberState;
|
|
|
|
}
|
|
|
|
|
2019-12-02 09:07:37 -07:00
|
|
|
type Halt = () => RootFolderNode;
|
2019-11-27 06:01:39 -07:00
|
|
|
type TreeClimber = (t: FolderUnion,
|
|
|
|
/** Calling this function stops tree climb from continuing. */
|
2019-12-02 09:07:37 -07:00
|
|
|
halt: Halt) => void;
|
2019-11-26 14:49:18 -07:00
|
|
|
|
|
|
|
function visit(p: VisitorProps) {
|
|
|
|
const { node, callback, halt } = p;
|
|
|
|
|
|
|
|
callback(node, halt);
|
2019-11-26 15:00:07 -07:00
|
|
|
const children: FolderUnion[] = (node.children);
|
|
|
|
return p.state.active && children.map(nextNode => {
|
|
|
|
if (nextNode.kind != "terminal") {
|
|
|
|
p.state.active && visit({ ...p, node: nextNode });
|
|
|
|
} else {
|
|
|
|
p.state.active && callback(nextNode, p.halt);
|
|
|
|
}
|
|
|
|
});
|
2019-11-26 14:49:18 -07:00
|
|
|
}
|
|
|
|
|
2019-11-27 06:01:39 -07:00
|
|
|
/** Recursively climb a directory structure. */
|
2019-11-26 14:49:18 -07:00
|
|
|
export const climb = (t: RootFolderNode, callback: TreeClimber) => {
|
|
|
|
const state: TreeClimberState = { active: true };
|
2019-12-02 09:07:37 -07:00
|
|
|
const halt = () => {
|
|
|
|
state.active = false;
|
|
|
|
return t;
|
|
|
|
};
|
2019-11-26 14:49:18 -07:00
|
|
|
t.folders.map((node) => {
|
|
|
|
const props = { node, callback, halt, state };
|
|
|
|
state.active && visit(props);
|
|
|
|
});
|
2019-11-27 06:01:39 -07:00
|
|
|
return t;
|
|
|
|
};
|
|
|
|
|
|
|
|
/** TODO: Create strategies for non-destructively
|
|
|
|
* transforming a RootFolderNode. */
|
|
|
|
export const cloneAndClimb = (t: RootFolderNode, callback: TreeClimber) => {
|
|
|
|
return climb(defensiveClone(t), callback);
|
2019-11-26 14:49:18 -07:00
|
|
|
};
|