split 'environment' example up into library and interactive prompt
This commit is contained in:
parent
255bc475d7
commit
77dfc8b182
3 changed files with 308 additions and 261 deletions
243
examples/prompt/prompt.js
Normal file
243
examples/prompt/prompt.js
Normal file
|
|
@ -0,0 +1,243 @@
|
|||
import { number, select } from "@inquirer/prompts";
|
||||
|
||||
import { makeCompareFn } from "../../lib/compare/dynamic.js";
|
||||
import { getFunctions, getInstances, getTypes, growEnv } from "../../lib/environment/env.js";
|
||||
import { assignFn } from "../../lib/generics/generics.js";
|
||||
import { getInst, getType, newDynamic } from "../../lib/primitives/dynamic.js";
|
||||
import { Bool, Double, Int, SymbolDynamic, Type, UUID } from "../../lib/primitives/primitive_types.js";
|
||||
import { eqType, getSymbol } from "../../lib/primitives/type.js";
|
||||
import { ModuleStd } from "../../lib/stdlib.js";
|
||||
import { fold as foldList } from "../../lib/structures/list.js";
|
||||
import { fold as foldSet } from "../../lib/structures/set.js";
|
||||
import { symbolFunction } from "../../lib/structures/type_constructors.js";
|
||||
import { pretty, prettyT } from "../../lib/util/pretty.js";
|
||||
import { genUUID } from "../../lib/util/random.js";
|
||||
|
||||
const defaultSelectOptions = {
|
||||
pageSize: 13,
|
||||
}
|
||||
|
||||
const proxyDynamic = async (env, dynamic) => {
|
||||
const newEnv = growEnv(env)(dynamic);
|
||||
const type = getType(dynamic)
|
||||
if (eqType(type)(Type)) {
|
||||
return listTypeOptions(newEnv, getInst(dynamic));
|
||||
}
|
||||
if (getSymbol(type) === symbolFunction) {
|
||||
return listFunctionOptions(newEnv, dynamic);
|
||||
}
|
||||
if (getSymbol(type) === SymbolDynamic) {
|
||||
return proxyDynamic(newEnv, getInst(dynamic));
|
||||
}
|
||||
return listInstanceOptions(newEnv, dynamic);
|
||||
}
|
||||
|
||||
const selectInstance = async (env, type, msg) => {
|
||||
const instances = getInstances(env)(type);
|
||||
const choices = foldSet(acc => instance => {
|
||||
return [...acc, {
|
||||
value: instance,
|
||||
name: pretty(instance),
|
||||
}];
|
||||
})(["(go back)", "(new)"])(instances);
|
||||
const choice = await select({
|
||||
message: `${msg} ${pretty(type)}:`,
|
||||
choices,
|
||||
...defaultSelectOptions,
|
||||
});
|
||||
if (choice === "(go back)") {
|
||||
return;
|
||||
}
|
||||
if (choice === "(new)") {
|
||||
return createInstance(type);
|
||||
}
|
||||
return choice;
|
||||
}
|
||||
|
||||
export const listInstances = async (env, type) => {
|
||||
const choice = await selectInstance(env, type, "instances of");
|
||||
if (choice === undefined) {
|
||||
return;
|
||||
}
|
||||
await proxyDynamic(env, newDynamic(choice)(type));
|
||||
return await listInstances(env, type);
|
||||
}
|
||||
|
||||
const listTypeOptions = async (env, type) => {
|
||||
const choice = await select({
|
||||
message: `type ${pretty(type)}:`,
|
||||
choices: [
|
||||
"(go back)",
|
||||
"new",
|
||||
"list instances",
|
||||
"treat as instance",
|
||||
],
|
||||
...defaultSelectOptions,
|
||||
});
|
||||
if (choice === "(go back)") {
|
||||
return;
|
||||
}
|
||||
if (choice === "new") {
|
||||
const i = await createInstance(type);
|
||||
if (i !== undefined) {
|
||||
const dynamic = newDynamic(i)(type);
|
||||
await proxyDynamic(env, dynamic);
|
||||
}
|
||||
}
|
||||
if (choice === "list instances") {
|
||||
await listInstances(env, type);
|
||||
}
|
||||
if (choice === "treat as instance") {
|
||||
await listInstanceOptions(newDynamic(type)(Type));
|
||||
}
|
||||
return await listTypeOptions(env, type);
|
||||
}
|
||||
|
||||
const listInstanceOptions = async (env, dynamic) => {
|
||||
const choice = await select({
|
||||
message: `instance ${pretty(dynamic)}:`,
|
||||
choices: [
|
||||
"(go back)",
|
||||
"transform",
|
||||
"get type",
|
||||
],
|
||||
...defaultSelectOptions,
|
||||
});
|
||||
if (choice === "(go back)") {
|
||||
return;
|
||||
}
|
||||
if (choice === "transform") {
|
||||
await transform(env, dynamic);
|
||||
}
|
||||
if (choice === "get type") {
|
||||
await listTypeOptions(env, getType(dynamic));
|
||||
}
|
||||
return await listInstanceOptions(env, dynamic);
|
||||
}
|
||||
|
||||
const listFunctionOptions = async (env, dynamic) => {
|
||||
const choice = await select({
|
||||
message: `function ${pretty(dynamic)}:`,
|
||||
choices: [
|
||||
"(go back)",
|
||||
"call",
|
||||
],
|
||||
...defaultSelectOptions,
|
||||
});
|
||||
if (choice === "(go back)") {
|
||||
return;
|
||||
}
|
||||
if (choice === "call") {
|
||||
await call(env, dynamic);
|
||||
}
|
||||
}
|
||||
|
||||
const createInstance = async (type) => {
|
||||
if (eqType(type)(Int)) {
|
||||
const n = await number({
|
||||
message: `enter an integer (leave empty to go back):`,
|
||||
step: 1, // only integers
|
||||
});
|
||||
if (n === undefined) {
|
||||
return;
|
||||
}
|
||||
return BigInt(n);
|
||||
}
|
||||
if (eqType(type)(Double)) {
|
||||
const n = await number({
|
||||
message: `enter a number (leave empty to go back):`,
|
||||
step: 'any',
|
||||
});
|
||||
return n;
|
||||
}
|
||||
if (eqType(type)(Bool)) {
|
||||
const b = await select({
|
||||
message: `select:`,
|
||||
choices: [
|
||||
{value: false, name: 'false'},
|
||||
{value: true, name: 'true'},
|
||||
],
|
||||
...defaultSelectOptions,
|
||||
});
|
||||
return b;
|
||||
}
|
||||
if (eqType(type)(UUID)) {
|
||||
const descr = await input({message: "enter UUID description:"});
|
||||
return descr + '__' + genUUID(16);
|
||||
}
|
||||
console.log("no prompt handler for creating new", prettyT(type));
|
||||
};
|
||||
|
||||
const transform = async (env, dynamic) => {
|
||||
const allFunctions = getFunctions(env);
|
||||
const enabled = foldList(enabled => fun => {
|
||||
try {
|
||||
const outType = assignFn(getType(fun), getType(dynamic));
|
||||
return [...enabled, {fun, outType}];
|
||||
} catch (e) {
|
||||
// console.log('warning:', e);
|
||||
return enabled;
|
||||
}
|
||||
})([])(allFunctions);
|
||||
|
||||
const choice = await select({
|
||||
message: `select:`,
|
||||
choices: [
|
||||
"(go back)",
|
||||
...enabled.map(({fun, outType}) => ({
|
||||
value: {fun, outType},
|
||||
name: `${getInst(fun).name} ${pretty(getInst(dynamic))} :: ${pretty(outType)}`,
|
||||
})),
|
||||
],
|
||||
...defaultSelectOptions,
|
||||
});
|
||||
|
||||
if (choice === "(go back)") {
|
||||
return;
|
||||
}
|
||||
const {fun, outType} = choice;
|
||||
const outValue = getInst(fun)(getInst(dynamic));
|
||||
await proxyDynamic(env, newDynamic(outValue)(outType));
|
||||
return transform(env, dynamic);
|
||||
}
|
||||
|
||||
const call = async (env, funDynamic) => {
|
||||
const funType = getType(funDynamic);
|
||||
const allTypes = getTypes(env);
|
||||
|
||||
// compatible input types
|
||||
const inTypes = foldSet(types => inType => {
|
||||
try {
|
||||
const outType = assignFn(funType, inType); // may throw
|
||||
return [...types, {inType, outType}];
|
||||
} catch (e) {
|
||||
return types;
|
||||
}
|
||||
})([])(allTypes);
|
||||
|
||||
const choice = inTypes.length === 1
|
||||
? inTypes[0]
|
||||
: await select({
|
||||
message: `types assignable to ${pretty(funType.params[0](funType))}:`,
|
||||
choices: [
|
||||
"(go back)",
|
||||
...inTypes.map(({inType, outType}) => ({
|
||||
value: {inType, outType},
|
||||
name: pretty(inType),
|
||||
})),
|
||||
],
|
||||
...defaultSelectOptions,
|
||||
});
|
||||
|
||||
if (choice === "(go back)") {
|
||||
return;
|
||||
}
|
||||
const inValue = await selectInstance(env, choice.inType, "select input value of type");
|
||||
if (inValue === undefined) {
|
||||
return;
|
||||
}
|
||||
const envWithInput = growEnv(env)(newDynamic(inValue)(choice.inType));
|
||||
const outValue = getInst(funDynamic)(inValue);
|
||||
await proxyDynamic(envWithInput, newDynamic(outValue)(choice.outType));
|
||||
return call(env, funDynamic);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue