Elm in TypeScript
This commit is contained in:
Родитель
6cc0c3c0e8
Коммит
2b369f1de1
|
@ -15,6 +15,7 @@ import JavaTargetLanguage from "./Java";
|
|||
import SimpleTypesTargetLanguage from "./SimpleTypes";
|
||||
import TypeScriptTargetLanguage from "./TypeScript";
|
||||
import Swift4TargetLanguage from "./Swift";
|
||||
import ElmTargetLanguage from "./Elm";
|
||||
|
||||
enum SwiftVersion {
|
||||
Swift3,
|
||||
|
@ -59,6 +60,7 @@ const typeScriptTargetLanguages: TargetLanguage[] = [
|
|||
new JavaTargetLanguage(),
|
||||
new TypeScriptTargetLanguage(),
|
||||
new SwiftTargetLanguage(),
|
||||
new ElmTargetLanguage(),
|
||||
new SimpleTypesTargetLanguage()
|
||||
];
|
||||
|
||||
|
|
|
@ -0,0 +1,597 @@
|
|||
"use strict";
|
||||
|
||||
import { Map, List } from "immutable";
|
||||
|
||||
import { TypeScriptTargetLanguage } from "../TargetLanguage";
|
||||
import { EnumOption, StringOption } from "../RendererOptions";
|
||||
import {
|
||||
TopLevels,
|
||||
NamedType,
|
||||
Type,
|
||||
matchType,
|
||||
nullableFromUnion,
|
||||
ClassType,
|
||||
UnionType,
|
||||
EnumType,
|
||||
PrimitiveType
|
||||
} from "../Type";
|
||||
import { RenderResult } from "../Renderer";
|
||||
import { ConvenienceRenderer } from "../ConvenienceRenderer";
|
||||
import { Namer, Name, DependencyName, funPrefixNamer, AssociatedName, Namespace } from "../Naming";
|
||||
import {
|
||||
legalizeCharacters,
|
||||
isLetterOrUnderscoreOrDigit,
|
||||
pascalCase,
|
||||
startWithLetter,
|
||||
isLetterOrUnderscore,
|
||||
decapitalize,
|
||||
defined,
|
||||
stringEscape,
|
||||
intercalate
|
||||
} from "../Support";
|
||||
import { Sourcelike, maybeAnnotated, modifySource } from "../Source";
|
||||
import { anyTypeIssueAnnotation, nullTypeIssueAnnotation } from "../Annotation";
|
||||
|
||||
export default class ElmTargetLanguage extends TypeScriptTargetLanguage {
|
||||
private readonly _listOption: EnumOption<boolean>;
|
||||
private readonly _moduleOption: StringOption;
|
||||
|
||||
constructor() {
|
||||
const listOption = new EnumOption("array-type", "Use Array or List", [["array", false], ["list", true]]);
|
||||
// FIXME: Do this via a configurable named eventually.
|
||||
const moduleOption = new StringOption("module", "Generated module name", "NAME", "QuickType");
|
||||
const options = [moduleOption, listOption];
|
||||
super("Elm", ["elm"], "elm", options.map(o => o.definition));
|
||||
this._listOption = listOption;
|
||||
this._moduleOption = moduleOption;
|
||||
}
|
||||
|
||||
renderGraph(topLevels: TopLevels, optionValues: { [name: string]: any }): RenderResult {
|
||||
const renderer = new ElmRenderer(
|
||||
topLevels,
|
||||
this._listOption.getValue(optionValues),
|
||||
this._moduleOption.getValue(optionValues)
|
||||
);
|
||||
return renderer.render();
|
||||
}
|
||||
}
|
||||
|
||||
const forbiddenNames = [
|
||||
"if",
|
||||
"then",
|
||||
"else",
|
||||
"case",
|
||||
"of",
|
||||
"let",
|
||||
"in",
|
||||
"type",
|
||||
"module",
|
||||
"where",
|
||||
"import",
|
||||
"exposing",
|
||||
"as",
|
||||
"port",
|
||||
"int",
|
||||
"float",
|
||||
"bool",
|
||||
"string",
|
||||
"Jenc",
|
||||
"Jdec",
|
||||
"Jpipe",
|
||||
"always",
|
||||
"identity",
|
||||
"Array",
|
||||
"List",
|
||||
"Dict",
|
||||
"Maybe",
|
||||
"map",
|
||||
"toList",
|
||||
"makeArrayEncoder",
|
||||
"makeDictEncoder",
|
||||
"makeNullableEncoder"
|
||||
];
|
||||
|
||||
const legalizeName = legalizeCharacters(isLetterOrUnderscoreOrDigit);
|
||||
|
||||
function elmNameStyle(original: string, upper: boolean): string {
|
||||
const legalized = legalizeName(original);
|
||||
const pascaled = pascalCase(legalized);
|
||||
const result = startWithLetter(isLetterOrUnderscore, upper, pascaled);
|
||||
return result;
|
||||
}
|
||||
|
||||
const upperNamingFunction = funPrefixNamer(n => elmNameStyle(n, true));
|
||||
const lowerNamingFunction = funPrefixNamer(n => elmNameStyle(n, false));
|
||||
|
||||
type MultiWord = {
|
||||
source: Sourcelike;
|
||||
needsParens: boolean;
|
||||
};
|
||||
|
||||
function singleWord(source: Sourcelike): MultiWord {
|
||||
return { source, needsParens: false };
|
||||
}
|
||||
|
||||
function multiWord(a: Sourcelike, b: Sourcelike): MultiWord {
|
||||
return { source: [a, " ", b], needsParens: true };
|
||||
}
|
||||
|
||||
function parenIfNeeded({ source, needsParens }: MultiWord): Sourcelike {
|
||||
if (needsParens) {
|
||||
return ["(", source, ")"];
|
||||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
type RequiredOrOptional = {
|
||||
reqOrOpt: string;
|
||||
fallback: string;
|
||||
};
|
||||
|
||||
function requiredOrOptional(t: Type): RequiredOrOptional {
|
||||
function optional(fallback: string): RequiredOrOptional {
|
||||
return { reqOrOpt: "Jpipe.optional", fallback };
|
||||
}
|
||||
if (t.kind === "null") {
|
||||
return optional(" ()");
|
||||
}
|
||||
if (t instanceof UnionType && nullableFromUnion(t)) {
|
||||
return optional(" Nothing");
|
||||
}
|
||||
return { reqOrOpt: "Jpipe.required", fallback: "" };
|
||||
}
|
||||
|
||||
type TopLevelDependent = {
|
||||
encoder: Name;
|
||||
decoder?: Name;
|
||||
};
|
||||
|
||||
type NamedTypeDependent = {
|
||||
encoder: Name;
|
||||
decoder: Name;
|
||||
};
|
||||
|
||||
class ElmRenderer extends ConvenienceRenderer {
|
||||
private _topLevelDependents: Map<Name, TopLevelDependent> = Map();
|
||||
private _namedTypeDependents: Map<Name, NamedTypeDependent> = Map();
|
||||
|
||||
constructor(topLevels: TopLevels, private readonly _useList: boolean, private readonly _moduleName: string) {
|
||||
super(topLevels);
|
||||
}
|
||||
|
||||
protected get forbiddenNamesForGlobalNamespace(): string[] {
|
||||
return forbiddenNames;
|
||||
}
|
||||
|
||||
protected topLevelNameStyle(rawName: string): string {
|
||||
return elmNameStyle(rawName, true);
|
||||
}
|
||||
|
||||
protected topLevelDependencyNames(t: Type, topLevelName: Name): DependencyName[] {
|
||||
const encoder = new DependencyName(
|
||||
lowerNamingFunction,
|
||||
List([topLevelName]),
|
||||
names => `${decapitalize(defined(names.first()))}ToString`
|
||||
);
|
||||
let decoder: DependencyName | undefined = undefined;
|
||||
if (!this.namedTypeToNameForTopLevel(t)) {
|
||||
decoder = new DependencyName(lowerNamingFunction, List([topLevelName]), names => defined(names.first()));
|
||||
}
|
||||
this._topLevelDependents = this._topLevelDependents.set(topLevelName, { encoder, decoder });
|
||||
if (decoder !== undefined) {
|
||||
return [encoder, decoder];
|
||||
}
|
||||
return [encoder];
|
||||
}
|
||||
|
||||
protected get namedTypeNamer(): Namer {
|
||||
return upperNamingFunction;
|
||||
}
|
||||
|
||||
protected namedTypeDependencyNames(t: NamedType, typeName: Name): DependencyName[] {
|
||||
const encoder = new DependencyName(
|
||||
lowerNamingFunction,
|
||||
List([typeName]),
|
||||
names => `encode${defined(names.first())}`
|
||||
);
|
||||
const decoder = new DependencyName(lowerNamingFunction, List([typeName]), names => defined(names.first()));
|
||||
this._namedTypeDependents = this._namedTypeDependents.set(typeName, { encoder, decoder });
|
||||
return [encoder, decoder];
|
||||
}
|
||||
|
||||
protected get propertyNamer(): Namer {
|
||||
return lowerNamingFunction;
|
||||
}
|
||||
|
||||
protected forbiddenForProperties(c: ClassType, classNamed: Name): { names: Name[]; namespaces: Namespace[] } {
|
||||
return { names: [], namespaces: [this.globalNamespace] };
|
||||
}
|
||||
|
||||
protected get caseNamer(): Namer {
|
||||
return upperNamingFunction;
|
||||
}
|
||||
|
||||
protected get casesInGlobalNamespace(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected namedTypeToNameForTopLevel(type: Type): NamedType | null {
|
||||
if (type.isNamedType()) {
|
||||
return type;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private get arrayType(): string {
|
||||
return this._useList ? "List" : "Array";
|
||||
}
|
||||
|
||||
private elmType = (t: Type, withIssues: boolean = false): MultiWord => {
|
||||
return matchType<MultiWord>(
|
||||
t,
|
||||
anyType => singleWord(maybeAnnotated(withIssues, anyTypeIssueAnnotation, "Jdec.Value")),
|
||||
nullType => singleWord(maybeAnnotated(withIssues, nullTypeIssueAnnotation, "()")),
|
||||
boolType => singleWord("Bool"),
|
||||
integerType => singleWord("Int"),
|
||||
doubleType => singleWord("Float"),
|
||||
stringType => singleWord("String"),
|
||||
arrayType => multiWord(this.arrayType, parenIfNeeded(this.elmType(arrayType.items, withIssues))),
|
||||
classType => singleWord(this.nameForNamedType(classType)),
|
||||
mapType => multiWord("Dict String", parenIfNeeded(this.elmType(mapType.values, withIssues))),
|
||||
enumType => singleWord(this.nameForNamedType(enumType)),
|
||||
unionType => {
|
||||
const nullable = nullableFromUnion(unionType);
|
||||
if (nullable) return multiWord("Maybe", parenIfNeeded(this.elmType(nullable, withIssues)));
|
||||
return singleWord(this.nameForNamedType(unionType));
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
private decoderNameForNamedType = (t: NamedType): Name => {
|
||||
const name = this.nameForNamedType(t);
|
||||
return defined(this._namedTypeDependents.get(name)).decoder;
|
||||
};
|
||||
|
||||
private decoderNameForType = (t: Type): MultiWord => {
|
||||
return matchType<MultiWord>(
|
||||
t,
|
||||
anyType => singleWord("Jdec.value"),
|
||||
nullType => multiWord("Jdec.null", "()"),
|
||||
boolType => singleWord("Jdec.bool"),
|
||||
integerType => singleWord("Jdec.int"),
|
||||
doubleType => singleWord("Jdec.float"),
|
||||
stringType => singleWord("Jdec.string"),
|
||||
arrayType =>
|
||||
multiWord(
|
||||
["Jdec.", decapitalize(this.arrayType)],
|
||||
parenIfNeeded(this.decoderNameForType(arrayType.items))
|
||||
),
|
||||
classType => singleWord(this.decoderNameForNamedType(classType)),
|
||||
mapType => multiWord("Jdec.dict", parenIfNeeded(this.decoderNameForType(mapType.values))),
|
||||
enumType => singleWord(this.decoderNameForNamedType(enumType)),
|
||||
unionType => {
|
||||
const nullable = nullableFromUnion(unionType);
|
||||
if (nullable) return multiWord("Jdec.nullable", parenIfNeeded(this.decoderNameForType(nullable)));
|
||||
return singleWord(this.decoderNameForNamedType(unionType));
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
private encoderNameForNamedType = (t: NamedType): Name => {
|
||||
const name = this.nameForNamedType(t);
|
||||
return defined(this._namedTypeDependents.get(name)).encoder;
|
||||
};
|
||||
|
||||
private encoderNameForType = (t: Type): MultiWord => {
|
||||
return matchType<MultiWord>(
|
||||
t,
|
||||
anyType => singleWord("identity"),
|
||||
nullType => multiWord("always", "Jenc.null"),
|
||||
boolType => singleWord("Jenc.bool"),
|
||||
integerType => singleWord("Jenc.int"),
|
||||
doubleType => singleWord("Jenc.float"),
|
||||
stringType => singleWord("Jenc.string"),
|
||||
arrayType =>
|
||||
multiWord(["make", this.arrayType, "Encoder"], parenIfNeeded(this.encoderNameForType(arrayType.items))),
|
||||
classType => singleWord(this.encoderNameForNamedType(classType)),
|
||||
mapType => multiWord("makeDictEncoder", parenIfNeeded(this.encoderNameForType(mapType.values))),
|
||||
enumType => singleWord(this.encoderNameForNamedType(enumType)),
|
||||
unionType => {
|
||||
const nullable = nullableFromUnion(unionType);
|
||||
if (nullable) return multiWord("makeNullableEncoder", parenIfNeeded(this.encoderNameForType(nullable)));
|
||||
return singleWord(this.encoderNameForNamedType(unionType));
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
private unionConstructorName = (unionName: Name, t: Type): Sourcelike => {
|
||||
return [elmNameStyle(this.unionFieldName(t), true), "In", unionName];
|
||||
};
|
||||
|
||||
private emitTopLevelDefinition = (t: Type, topLevelName: Name): void => {
|
||||
this.emitLine("type alias ", topLevelName, " = ", this.elmType(t).source);
|
||||
};
|
||||
|
||||
private emitClassDefinition = (c: ClassType, className: Name): void => {
|
||||
this.emitLine("type alias ", className, " =");
|
||||
this.indent(() => {
|
||||
let onFirst = true;
|
||||
this.forEachProperty(c, "none", (name, _, t) => {
|
||||
this.emitLine(onFirst ? "{" : ",", " ", name, " : ", this.elmType(t).source);
|
||||
onFirst = false;
|
||||
});
|
||||
if (onFirst) {
|
||||
this.emitLine("{");
|
||||
}
|
||||
this.emitLine("}");
|
||||
});
|
||||
};
|
||||
|
||||
private emitEnumDefinition = (e: EnumType, enumName: Name): void => {
|
||||
this.emitLine("type ", enumName);
|
||||
this.indent(() => {
|
||||
let onFirst = true;
|
||||
this.forEachCase(e, "none", name => {
|
||||
const equalsOrPipe = onFirst ? "=" : "|";
|
||||
this.emitLine(equalsOrPipe, " ", name);
|
||||
onFirst = false;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
private emitUnionDefinition = (u: UnionType, unionName: Name): void => {
|
||||
const members = u.members.sortBy(this.unionFieldName);
|
||||
this.emitLine("type ", unionName);
|
||||
this.indent(() => {
|
||||
let onFirst = true;
|
||||
members.forEach(t => {
|
||||
const equalsOrPipe = onFirst ? "=" : "|";
|
||||
const constructor = this.unionConstructorName(unionName, t);
|
||||
if (t.kind === "null") {
|
||||
this.emitLine(equalsOrPipe, " ", constructor);
|
||||
} else {
|
||||
this.emitLine(equalsOrPipe, " ", constructor, " ", parenIfNeeded(this.elmType(t)));
|
||||
}
|
||||
onFirst = false;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
private emitTopLevelFunctions = (t: Type, topLevelName: Name): void => {
|
||||
const { encoder, decoder } = defined(this._topLevelDependents.get(topLevelName));
|
||||
if (!this.namedTypeToNameForTopLevel(t)) {
|
||||
this.emitLine(defined(decoder), " : Jdec.Decoder ", topLevelName);
|
||||
this.emitLine(defined(decoder), " = ", this.decoderNameForType(t).source);
|
||||
this.emitNewline();
|
||||
}
|
||||
this.emitLine(encoder, " : ", topLevelName, " -> String");
|
||||
this.emitLine(encoder, " r = Jenc.encode 0 (", this.encoderNameForType(t).source, " r)");
|
||||
};
|
||||
|
||||
private emitClassFunctions = (c: ClassType, className: Name): void => {
|
||||
const decoderName = this.decoderNameForNamedType(c);
|
||||
this.emitLine(decoderName, " : Jdec.Decoder ", className);
|
||||
this.emitLine(decoderName, " =");
|
||||
this.indent(() => {
|
||||
this.emitLine("Jpipe.decode ", className);
|
||||
this.indent(() => {
|
||||
this.forEachProperty(c, "none", (name, jsonName, t) => {
|
||||
const propDecoder = parenIfNeeded(this.decoderNameForType(t));
|
||||
const { reqOrOpt, fallback } = requiredOrOptional(t);
|
||||
this.emitLine("|> ", reqOrOpt, ' "', stringEscape(jsonName), '" ', propDecoder, fallback);
|
||||
});
|
||||
});
|
||||
});
|
||||
this.emitNewline();
|
||||
|
||||
const encoderName = this.encoderNameForNamedType(c);
|
||||
this.emitLine(encoderName, " : ", className, " -> Jenc.Value");
|
||||
this.emitLine(encoderName, " x =");
|
||||
this.indent(() => {
|
||||
this.emitLine("Jenc.object");
|
||||
this.indent(() => {
|
||||
let onFirst = true;
|
||||
this.forEachProperty(c, "none", (name, jsonName, t) => {
|
||||
const bracketOrComma = onFirst ? "[" : ",";
|
||||
const propEncoder = this.encoderNameForType(t).source;
|
||||
this.emitLine(bracketOrComma, ' ("', stringEscape(jsonName), '", ', propEncoder, " x.", name, ")");
|
||||
onFirst = false;
|
||||
});
|
||||
if (onFirst) {
|
||||
this.emitLine("[");
|
||||
}
|
||||
this.emitLine("]");
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
private emitEnumFunctions = (e: EnumType, enumName: Name): void => {
|
||||
const decoderName = this.decoderNameForNamedType(e);
|
||||
this.emitLine(decoderName, " : Jdec.Decoder ", enumName);
|
||||
this.emitLine(decoderName, " =");
|
||||
this.indent(() => {
|
||||
this.emitLine("Jdec.string");
|
||||
this.indent(() => {
|
||||
this.emitLine("|> Jdec.andThen (str ->");
|
||||
this.indent(() => {
|
||||
this.emitLine("case str of");
|
||||
this.indent(() => {
|
||||
this.forEachCase(e, "none", (name, jsonName) => {
|
||||
this.emitLine('"', stringEscape(jsonName), '" -> Jdec.succeed ', name);
|
||||
});
|
||||
this.emitLine('somethingElse -> Jdec.fail <| "Invalid ', enumName, ': " ++ somethingElse');
|
||||
});
|
||||
});
|
||||
this.emitLine(")");
|
||||
});
|
||||
});
|
||||
this.emitNewline();
|
||||
|
||||
const encoderName = this.encoderNameForNamedType(e);
|
||||
this.emitLine(encoderName, " : ", enumName, " -> Jenc.Value");
|
||||
this.emitLine(encoderName, " x = case x of");
|
||||
this.indent(() => {
|
||||
this.forEachCase(e, "none", (name, jsonName) => {
|
||||
this.emitLine(name, ' -> Jenc.string "', stringEscape(jsonName), '"');
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
private emitUnionFunctions = (u: UnionType, unionName: Name): void => {
|
||||
// We need arrays first, then strings.
|
||||
function sortOrder(t: Type): string {
|
||||
if (t.kind === "array") {
|
||||
return " array";
|
||||
} else if (t instanceof PrimitiveType) {
|
||||
return " " + t.kind;
|
||||
}
|
||||
return t.kind;
|
||||
}
|
||||
|
||||
const members = u.members.sortBy(sortOrder);
|
||||
const decoderName = this.decoderNameForNamedType(u);
|
||||
this.emitLine(decoderName, " : Jdec.Decoder ", unionName);
|
||||
this.emitLine(decoderName, " =");
|
||||
this.indent(() => {
|
||||
this.emitLine("Jdec.oneOf");
|
||||
this.indent(() => {
|
||||
let onFirst = true;
|
||||
members.forEach(t => {
|
||||
const bracketOrComma = onFirst ? "[" : ",";
|
||||
const constructor = this.unionConstructorName(unionName, t);
|
||||
if (t.kind === "null") {
|
||||
this.emitLine(bracketOrComma, " Jdec.null ", constructor);
|
||||
} else {
|
||||
const decoder = parenIfNeeded(this.decoderNameForType(t));
|
||||
this.emitLine(bracketOrComma, " Jdec.map ", constructor, " ", decoder);
|
||||
}
|
||||
onFirst = false;
|
||||
});
|
||||
this.emitLine("]");
|
||||
});
|
||||
});
|
||||
this.emitNewline();
|
||||
|
||||
const encoderName = this.encoderNameForNamedType(u);
|
||||
this.emitLine(encoderName, " : ", unionName, " -> Jenc.Value");
|
||||
this.emitLine(encoderName, " x = case x of");
|
||||
this.indent(() => {
|
||||
members.forEach(t => {
|
||||
const constructor = this.unionConstructorName(unionName, t);
|
||||
if (t.kind === "null") {
|
||||
this.emitLine(constructor, " -> Jenc.null");
|
||||
} else {
|
||||
const encoder = this.encoderNameForType(t).source;
|
||||
this.emitLine(constructor, " y -> ", encoder, " y");
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
protected emitSourceStructure(): void {
|
||||
const exports: Sourcelike[] = [];
|
||||
let topLevelDecoders: List<Sourcelike> = List();
|
||||
this.forEachTopLevel("none", (_, name) => {
|
||||
let { encoder, decoder } = defined(this._topLevelDependents.get(name));
|
||||
if (decoder === undefined) {
|
||||
decoder = defined(this._namedTypeDependents.get(name)).decoder;
|
||||
}
|
||||
topLevelDecoders = topLevelDecoders.push(decoder);
|
||||
exports.push(name, encoder, decoder);
|
||||
});
|
||||
this.forEachClass("none", (t, name) => {
|
||||
if (!this.topLevels.contains(t)) exports.push(name);
|
||||
});
|
||||
this.forEachUnion("none", (t, name) => {
|
||||
if (!this.topLevels.contains(t)) exports.push([name, "(..)"]);
|
||||
});
|
||||
|
||||
this.emitMultiline(`-- To decode the JSON data, add this file to your project, run
|
||||
--
|
||||
-- elm-package install NoRedInk/elm-decode-pipeline
|
||||
--
|
||||
-- add these imports
|
||||
--
|
||||
-- import Json.Decode exposing (decodeString)`);
|
||||
this.emitLine(
|
||||
"-- import ",
|
||||
this._moduleName,
|
||||
" exposing (",
|
||||
intercalate(", ", topLevelDecoders).toArray(),
|
||||
")"
|
||||
);
|
||||
this.emitMultiline(`--
|
||||
-- and you're off to the races with
|
||||
--`);
|
||||
this.forEachTopLevel("none", (_, name) => {
|
||||
let { decoder } = defined(this._topLevelDependents.get(name));
|
||||
if (decoder === undefined) {
|
||||
decoder = defined(this._namedTypeDependents.get(name)).decoder;
|
||||
}
|
||||
this.emitLine("-- decodeString ", decoder, " myJsonString");
|
||||
});
|
||||
this.emitNewline();
|
||||
|
||||
this.emitLine("module ", this._moduleName, " exposing");
|
||||
this.indent(() => {
|
||||
for (let i = 0; i < exports.length; i++) {
|
||||
this.emitLine(i === 0 ? "(" : ",", " ", exports[i]);
|
||||
}
|
||||
this.emitLine(")");
|
||||
});
|
||||
this.emitNewline();
|
||||
|
||||
this.emitMultiline(`import Json.Decode as Jdec
|
||||
import Json.Decode.Pipeline as Jpipe
|
||||
import Json.Encode as Jenc
|
||||
import Dict exposing (Dict, map, toList)`);
|
||||
if (this._useList) {
|
||||
this.emitLine("import List exposing (map)");
|
||||
} else {
|
||||
this.emitLine("import Array exposing (Array, map)");
|
||||
}
|
||||
|
||||
this.forEachTopLevel(
|
||||
"leading-and-interposing",
|
||||
this.emitTopLevelDefinition,
|
||||
t => !this.namedTypeToNameForTopLevel(t)
|
||||
);
|
||||
this.forEachNamedType(
|
||||
"leading-and-interposing",
|
||||
false,
|
||||
this.emitClassDefinition,
|
||||
this.emitEnumDefinition,
|
||||
this.emitUnionDefinition
|
||||
);
|
||||
this.emitNewline();
|
||||
|
||||
this.emitLine("-- decoders and encoders");
|
||||
this.forEachTopLevel("leading-and-interposing", this.emitTopLevelFunctions);
|
||||
this.forEachNamedType(
|
||||
"leading-and-interposing",
|
||||
false,
|
||||
this.emitClassFunctions,
|
||||
this.emitEnumFunctions,
|
||||
this.emitUnionFunctions
|
||||
);
|
||||
this.emitNewline();
|
||||
|
||||
this.emitLine("--- encoder helpers");
|
||||
this.emitNewline();
|
||||
this.emitLine("make", this.arrayType, "Encoder : (a -> Jenc.Value) -> ", this.arrayType, " a -> Jenc.Value");
|
||||
this.emitLine("make", this.arrayType, "Encoder f arr =");
|
||||
this.indent(() => {
|
||||
this.emitLine("Jenc.", decapitalize(this.arrayType), " (", this.arrayType, ".map f arr)");
|
||||
});
|
||||
this.emitNewline();
|
||||
this.emitMultiline(`makeDictEncoder : (a -> Jenc.Value) -> Dict String a -> Jenc.Value
|
||||
makeDictEncoder f dict =
|
||||
Jenc.object (toList (Dict.map (\\k -> f) dict))
|
||||
|
||||
makeNullableEncoder : (a -> Jenc.Value) -> Maybe a -> Jenc.Value
|
||||
makeNullableEncoder f m =
|
||||
case m of
|
||||
Just x -> f x
|
||||
Nothing -> Jenc.null`);
|
||||
}
|
||||
}
|
|
@ -1,417 +0,0 @@
|
|||
module Language.Elm
|
||||
( renderer
|
||||
) where
|
||||
|
||||
import Prelude
|
||||
|
||||
import Data.Array as A
|
||||
import Data.Foldable (for_, intercalate)
|
||||
import Data.List (List, (:))
|
||||
import Data.List as L
|
||||
import Data.Map (Map)
|
||||
import Data.Map as M
|
||||
import Data.Maybe (Maybe(..))
|
||||
import Data.String.Util (camelCase, capitalize, decapitalize, isLetterOrUnderscore, isLetterOrUnderscoreOrDigit, legalizeCharacters, startWithLetter, stringEscape)
|
||||
import Data.Tuple (Tuple(..), fst)
|
||||
import Doc (Doc, Namer, Renderer, blank, combineNames, forEachTopLevel_, getClasses, getOptionValue, getTopLevelNames, getTopLevels, getTypeNameForUnion, getUnions, indent, line, lookupClassName, lookupName, lookupUnionName, lookupEnumName, renderRenderItems, simpleNamer, transformPropertyNames, unionIsNotSimpleNullable, intercalatedName, unlessOption, whenOption)
|
||||
import IRGraph (IRClassData(..), IRType(..), IRUnionRep, IREnumData, isArray, nullableFromUnion, unionToList)
|
||||
import Options (Option, enumOption, stringOption)
|
||||
import Utils (forEnumerated_, sortByKey, sortByKeyM, mapM)
|
||||
|
||||
forbiddenNames :: Array String
|
||||
forbiddenNames =
|
||||
[ "if", "then", "else"
|
||||
, "case", "of"
|
||||
, "let", "in"
|
||||
, "type"
|
||||
, "module", "where"
|
||||
, "import", "exposing"
|
||||
, "as"
|
||||
, "port"
|
||||
, "int", "float", "bool", "string"
|
||||
, "Jenc", "Jdec", "Jpipe"
|
||||
, "always", "identity"
|
||||
, "Array", "List", "Dict", "Maybe", "map", "toList"
|
||||
, "makeArrayEncoder", "makeDictEncoder", "makeNullableEncoder"
|
||||
]
|
||||
|
||||
listOption :: Option Boolean
|
||||
listOption = enumOption "array-type" "Use Array or List" [Tuple "array" false, Tuple "list" true]
|
||||
|
||||
moduleOption :: Option String
|
||||
moduleOption = stringOption "module" "Generated module name" "NAME" "QuickType"
|
||||
|
||||
renderer :: Renderer
|
||||
renderer =
|
||||
{ displayName: "Elm"
|
||||
, names: [ "elm" ]
|
||||
, aceMode: "elm"
|
||||
, extension: "elm"
|
||||
, doc: elmDoc
|
||||
, options:
|
||||
[ moduleOption.specification
|
||||
, listOption.specification
|
||||
]
|
||||
, transforms:
|
||||
{ nameForClass: elmNamer nameForClass
|
||||
, nextName: \s -> "Other" <> s
|
||||
, forbiddenNames
|
||||
, topLevelName: elmNamer upperNameStyle
|
||||
, unions: Just
|
||||
{ predicate: unionIsNotSimpleNullable
|
||||
, properName: elmNamer (upperNameStyle <<< combineNames)
|
||||
, nameFromTypes: elmNamer (intercalatedName upperNameStyle "Or")
|
||||
}
|
||||
, enums: Just
|
||||
{ properName: elmNamer (upperNameStyle <<< combineNames)
|
||||
, nameFromValues: elmNamer (intercalatedName upperNameStyle "Or")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
decoderNameFromTypeName :: String -> String
|
||||
decoderNameFromTypeName = decapitalize
|
||||
|
||||
encoderNameFromTypeName :: String -> String
|
||||
encoderNameFromTypeName className = "encode" <> className
|
||||
|
||||
alsoForbiddenForTypeName :: String -> Array String
|
||||
alsoForbiddenForTypeName n = [decoderNameFromTypeName n, encoderNameFromTypeName n]
|
||||
|
||||
elmNamer :: forall a. Ord a => (a -> String) -> Namer a
|
||||
elmNamer namer thing = case _ of
|
||||
Just name -> result name
|
||||
Nothing -> result $ namer thing
|
||||
where
|
||||
result name = { name, forbid: A.cons name $ alsoForbiddenForTypeName name }
|
||||
|
||||
nameForClass :: IRClassData -> String
|
||||
nameForClass (IRClassData { names }) = upperNameStyle $ combineNames names
|
||||
|
||||
typeNameForTopLevelNameGiven :: String -> String
|
||||
typeNameForTopLevelNameGiven = upperNameStyle
|
||||
|
||||
namesFromTopLevelNameGiven :: String -> Array String
|
||||
namesFromTopLevelNameGiven given =
|
||||
let name = typeNameForTopLevelNameGiven given
|
||||
in A.cons name $ alsoForbiddenForTypeName name
|
||||
|
||||
legalize :: String -> String
|
||||
legalize = legalizeCharacters isLetterOrUnderscoreOrDigit
|
||||
|
||||
elmNameStyle :: Boolean -> String -> String
|
||||
elmNameStyle upper = legalize >>> camelCase >>> (startWithLetter isLetterOrUnderscore upper)
|
||||
|
||||
lowerNameStyle :: String -> String
|
||||
lowerNameStyle = elmNameStyle false
|
||||
|
||||
upperNameStyle :: String -> String
|
||||
upperNameStyle = elmNameStyle true
|
||||
|
||||
renderComment :: Maybe String -> String
|
||||
renderComment (Just s) = " -- " <> s
|
||||
renderComment Nothing = ""
|
||||
|
||||
renderArrayType :: Doc String
|
||||
renderArrayType = do
|
||||
useList <- getOptionValue listOption
|
||||
pure $ if useList then "List" else "Array"
|
||||
|
||||
elmDoc :: Doc Unit
|
||||
elmDoc = do
|
||||
topLevels <- getTopLevels
|
||||
-- givenTopLevel <- typeNameForTopLevelNameGiven <$> getTopLevelNameGiven
|
||||
-- let topLevelDecoder = decoderNameFromTypeName givenTopLevel
|
||||
-- let topLevelEncoder = encoderNameFromTypeName givenTopLevel
|
||||
classes <- getClasses
|
||||
unions <- getUnions
|
||||
classNames <- mapM (\t -> lookupClassName $ fst t) classes
|
||||
unionExports <- map (\n -> n <> "(..)") <$> mapM lookupUnionName unions
|
||||
topLevelNames <- M.values <$> getTopLevelNames
|
||||
let topLevelDecoders = map decoderNameFromTypeName topLevelNames
|
||||
let alsoTopLevelExports = L.concat $ map (alsoForbiddenForTypeName >>> L.fromFoldable) topLevelNames
|
||||
let exports = L.concat $ topLevelNames : alsoTopLevelExports : classNames : unionExports : L.Nil
|
||||
moduleName <- getOptionValue moduleOption
|
||||
line """-- To decode the JSON data, add this file to your project, run
|
||||
--
|
||||
-- elm-package install NoRedInk/elm-decode-pipeline
|
||||
--
|
||||
-- add these imports
|
||||
--
|
||||
-- import Json.Decode exposing (decodeString)"""
|
||||
line $ "-- import " <> moduleName <> " exposing (" <> (intercalate ", " topLevelDecoders) <> ")"
|
||||
line """--
|
||||
-- and you're off to the races with
|
||||
--"""
|
||||
forEachTopLevel_ \topLevelName topLevelType -> do
|
||||
let topLevelDecoder = decoderNameFromTypeName topLevelName
|
||||
line $ "-- decodeString " <> topLevelDecoder <> " myJsonString"
|
||||
blank
|
||||
line $ "module " <> moduleName <> " exposing"
|
||||
indent do
|
||||
forWithPrefix_ exports "( " ", " \parenOrComma name ->
|
||||
line $ parenOrComma <> name
|
||||
line ")"
|
||||
blank
|
||||
line """import Json.Decode as Jdec
|
||||
import Json.Decode.Pipeline as Jpipe
|
||||
import Json.Encode as Jenc
|
||||
import Dict exposing (Dict, map, toList)"""
|
||||
arrayType <- renderArrayType
|
||||
whenOption listOption do
|
||||
line $ "import List exposing (map)"
|
||||
unlessOption listOption do
|
||||
line $ "import Array exposing (Array, map)"
|
||||
blank
|
||||
-- FIXME: render enums
|
||||
renderRenderItems blank (Just renderTopLevelDefinition) (typeRenderer renderTypeDefinition) (Just renderUnionDefinition) Nothing
|
||||
blank
|
||||
line "-- decoders and encoders"
|
||||
blank
|
||||
renderRenderItems blank (Just renderTopLevelFunctions) (typeRenderer renderTypeFunctions) (Just renderUnionFunctions) Nothing
|
||||
blank
|
||||
line "--- encoder helpers"
|
||||
blank
|
||||
line $ "make" <> arrayType <> "Encoder : (a -> Jenc.Value) -> " <> arrayType <> " a -> Jenc.Value"
|
||||
line $ "make" <> arrayType <> "Encoder f arr ="
|
||||
indent do
|
||||
line $ "Jenc." <> decapitalize arrayType <> " (" <> arrayType <> ".map f arr)"
|
||||
blank
|
||||
line """makeDictEncoder : (a -> Jenc.Value) -> Dict String a -> Jenc.Value
|
||||
makeDictEncoder f dict =
|
||||
Jenc.object (toList (Dict.map (\k -> f) dict))
|
||||
|
||||
makeNullableEncoder : (a -> Jenc.Value) -> Maybe a -> Jenc.Value
|
||||
makeNullableEncoder f m =
|
||||
case m of
|
||||
Just x -> f x
|
||||
Nothing -> Jenc.null"""
|
||||
|
||||
renderTopLevelDefinition :: String -> IRType -> Doc Unit
|
||||
renderTopLevelDefinition topLevelName topLevel = do
|
||||
{ rendered: topLevelRendered } <- typeStringForType topLevel
|
||||
line $ "type alias " <> topLevelName <> " = " <> topLevelRendered
|
||||
|
||||
renderTopLevelFunctions :: String -> IRType -> Doc Unit
|
||||
renderTopLevelFunctions topLevelName topLevel = do
|
||||
let topLevelDecoder = decoderNameFromTypeName topLevelName
|
||||
let topLevelEncoder = encoderNameFromTypeName topLevelName
|
||||
{ rendered: rootDecoder } <- decoderNameForType topLevel
|
||||
line $ topLevelDecoder <> " : Jdec.Decoder " <> topLevelName
|
||||
line $ topLevelDecoder <> " = " <> rootDecoder
|
||||
blank
|
||||
{ rendered: rootEncoder } <- encoderNameForType topLevel
|
||||
line $ topLevelEncoder <> " : " <> topLevelName <> " -> String"
|
||||
line $ topLevelEncoder <> " r = Jenc.encode 0 (" <> rootEncoder <> " r)"
|
||||
|
||||
singleWord :: String -> Doc { rendered :: String, multiWord :: Boolean }
|
||||
singleWord w = pure { rendered: w, multiWord: false }
|
||||
|
||||
multiWord :: String -> String -> Doc { rendered :: String, multiWord :: Boolean }
|
||||
multiWord s1 s2 = pure { rendered: s1 <> " " <> s2, multiWord: true }
|
||||
|
||||
parenIfNeeded :: { rendered :: String, multiWord :: Boolean } -> String
|
||||
parenIfNeeded { rendered, multiWord: false } = rendered
|
||||
parenIfNeeded { rendered, multiWord: true } = "(" <> rendered <> ")"
|
||||
|
||||
typeStringForType :: IRType -> Doc { rendered :: String, multiWord :: Boolean }
|
||||
typeStringForType = case _ of
|
||||
IRNoInformation -> singleWord "FIXME_THIS_SHOULD_NOT_HAPPEN"
|
||||
IRAnyType -> singleWord "Jdec.Value"
|
||||
IRNull -> singleWord "()"
|
||||
IRInteger -> singleWord "Int"
|
||||
IRDouble -> singleWord "Float"
|
||||
IRBool -> singleWord "Bool"
|
||||
IRString -> singleWord "String"
|
||||
IRArray a -> do
|
||||
ts <- typeStringForType a
|
||||
arrayType <- renderArrayType
|
||||
multiWord arrayType $ parenIfNeeded ts
|
||||
IRClass i -> singleWord =<< lookupClassName i
|
||||
IRMap t -> do
|
||||
ts <- typeStringForType t
|
||||
multiWord "Dict String" $ parenIfNeeded ts
|
||||
IREnum e -> singleWord =<< lookupEnumName e
|
||||
IRUnion u ->
|
||||
case nullableFromUnion u of
|
||||
Just x -> do
|
||||
ts <- typeStringForType x
|
||||
multiWord "Maybe" $ parenIfNeeded ts
|
||||
Nothing -> do
|
||||
singleWord =<< lookupUnionName u
|
||||
|
||||
unionConstructorName :: String -> IRType -> Doc String
|
||||
unionConstructorName unionName t = do
|
||||
typeName <- upperNameStyle <$> getTypeNameForUnion t
|
||||
pure $ typeName <> "In" <> unionName
|
||||
|
||||
decoderNameForType :: IRType -> Doc { rendered :: String, multiWord :: Boolean }
|
||||
decoderNameForType = case _ of
|
||||
IRNoInformation -> singleWord "FIXME_THIS_SHOULD_NOT_HAPPEN"
|
||||
IRAnyType -> singleWord "Jdec.value"
|
||||
IRNull -> multiWord "Jdec.null" "()"
|
||||
IRInteger -> singleWord "Jdec.int"
|
||||
IRDouble -> singleWord "Jdec.float"
|
||||
IRBool -> singleWord "Jdec.bool"
|
||||
IRString -> singleWord "Jdec.string"
|
||||
IRArray a -> do
|
||||
dn <- decoderNameForType a
|
||||
arrayType <- renderArrayType
|
||||
multiWord ("Jdec." <> decapitalize arrayType) $ parenIfNeeded dn
|
||||
IRClass i -> singleWord =<< decoderNameFromTypeName <$> lookupClassName i
|
||||
IRMap t -> do
|
||||
dn <- decoderNameForType t
|
||||
multiWord "Jdec.dict" $ parenIfNeeded dn
|
||||
IREnum e -> singleWord =<< decoderNameFromTypeName <$> lookupEnumName e
|
||||
IRUnion u ->
|
||||
case nullableFromUnion u of
|
||||
Just t -> do
|
||||
dn <- decoderNameForType t
|
||||
multiWord "Jdec.nullable" $ parenIfNeeded dn
|
||||
Nothing -> do
|
||||
singleWord =<< decoderNameFromTypeName <$> lookupUnionName u
|
||||
|
||||
encoderNameForType :: IRType -> Doc { rendered :: String, multiWord :: Boolean }
|
||||
encoderNameForType = case _ of
|
||||
IRNoInformation -> singleWord "FIXME_THIS_SHOULD_NOT_HAPPEN"
|
||||
IRAnyType -> singleWord "identity"
|
||||
IRNull -> multiWord "always" "Jenc.null"
|
||||
IRInteger -> singleWord "Jenc.int"
|
||||
IRDouble -> singleWord "Jenc.float"
|
||||
IRBool -> singleWord "Jenc.bool"
|
||||
IRString -> singleWord "Jenc.string"
|
||||
IRArray a -> do
|
||||
rendered <- encoderNameForType a
|
||||
arrayType <- renderArrayType
|
||||
multiWord ("make" <> arrayType <> "Encoder") $ parenIfNeeded rendered
|
||||
IRClass i -> singleWord =<< encoderNameFromTypeName <$> lookupClassName i
|
||||
IRMap t -> do
|
||||
rendered <- encoderNameForType t
|
||||
multiWord "makeDictEncoder" $ parenIfNeeded rendered
|
||||
IREnum e -> singleWord =<< encoderNameFromTypeName <$> lookupEnumName e
|
||||
IRUnion u ->
|
||||
case nullableFromUnion u of
|
||||
Just t -> do
|
||||
rendered <- encoderNameForType t
|
||||
multiWord "makeNullableEncoder" $ parenIfNeeded rendered
|
||||
Nothing ->
|
||||
singleWord =<< encoderNameFromTypeName <$> lookupUnionName u
|
||||
|
||||
forWithPrefix_ :: forall a b p m. Applicative m => List a -> p -> p -> (p -> a -> m b) -> m Unit
|
||||
forWithPrefix_ l firstPrefix restPrefix f =
|
||||
forEnumerated_ l (\i -> f $ if i == 0 then firstPrefix else restPrefix)
|
||||
|
||||
renderTypeDefinition :: String -> Map String String -> List (Tuple String IRType) -> Doc Unit
|
||||
renderTypeDefinition className propertyNames propsList = do
|
||||
line $ "type alias " <> className <> " ="
|
||||
indent do
|
||||
forWithPrefix_ propsList "{ " ", " \braceOrComma (Tuple pname ptype) -> do
|
||||
let propName = lookupName pname propertyNames
|
||||
{ rendered } <- typeStringForType ptype
|
||||
line $ braceOrComma <> propName <> " : " <> rendered
|
||||
when (propsList == L.Nil) do
|
||||
line "{"
|
||||
line "}"
|
||||
|
||||
renderTypeFunctions :: String -> Map String String -> List (Tuple String IRType) -> Doc Unit
|
||||
renderTypeFunctions className propertyNames propsList = do
|
||||
let decoderName = decoderNameFromTypeName className
|
||||
line $ decoderName <> " : Jdec.Decoder " <> className
|
||||
line $ decoderName <> " ="
|
||||
indent do
|
||||
line $ "Jpipe.decode " <> className
|
||||
for_ propsList \(Tuple pname ptype) -> do
|
||||
indent do
|
||||
propDecoder <- decoderNameForType ptype
|
||||
let { reqOrOpt, fallback } = requiredOrOptional ptype
|
||||
line $ "|> " <> reqOrOpt <> " \"" <> stringEscape pname <> "\" " <> (parenIfNeeded propDecoder) <> fallback
|
||||
blank
|
||||
let encoderName = encoderNameFromTypeName className
|
||||
line $ encoderName <> " : " <> className <> " -> Jenc.Value"
|
||||
line $ encoderName <> " x ="
|
||||
indent do
|
||||
line "Jenc.object"
|
||||
indent do
|
||||
forWithPrefix_ propsList "[ " ", " \bracketOrComma (Tuple pname ptype) -> do
|
||||
let propName = lookupName pname propertyNames
|
||||
{ rendered: propEncoder } <- encoderNameForType ptype
|
||||
line $ bracketOrComma <> "(\"" <> stringEscape pname <> "\", " <> propEncoder <> " x." <> propName <> ")"
|
||||
when (propsList == L.Nil) do
|
||||
line "["
|
||||
line "]"
|
||||
where
|
||||
requiredOrOptional :: IRType -> { reqOrOpt :: String, fallback :: String }
|
||||
requiredOrOptional = case _ of
|
||||
IRNull -> optional " ()"
|
||||
IRUnion u ->
|
||||
if not $ unionIsNotSimpleNullable u then
|
||||
optional " Nothing"
|
||||
else
|
||||
required
|
||||
_ -> required
|
||||
required =
|
||||
{ reqOrOpt: "Jpipe.required", fallback: "" }
|
||||
optional fallback =
|
||||
{ reqOrOpt: "Jpipe.optional", fallback }
|
||||
|
||||
typeRenderer :: (String -> Map String String -> List (Tuple String IRType) -> Doc Unit) -> String -> Map String IRType -> Doc Unit
|
||||
typeRenderer renderer' className properties = do
|
||||
let propertyNames = transformPropertyNames (simpleNamer lowerNameStyle) (\n -> "other" <> capitalize n) forbiddenNames properties
|
||||
let propsList = M.toUnfoldable properties # sortByKey (\t -> lookupName (fst t) propertyNames)
|
||||
renderer' className propertyNames propsList
|
||||
|
||||
renderUnionDefinition :: String -> IRUnionRep -> Doc Unit
|
||||
renderUnionDefinition unionName unionRep = do
|
||||
fields <- unionToList unionRep # sortByKeyM (unionConstructorName unionName)
|
||||
line $ "type " <> unionName
|
||||
forWithPrefix_ fields "=" "|" \equalsOrPipe t -> do
|
||||
indent do
|
||||
constructor <- unionConstructorName unionName t
|
||||
when (t == IRNull) do
|
||||
line $ equalsOrPipe <> " " <> constructor
|
||||
unless (t == IRNull) do
|
||||
ts <- typeStringForType t
|
||||
line $ equalsOrPipe <> " " <> constructor <> " " <> (parenIfNeeded ts)
|
||||
|
||||
renderUnionFunctions :: String -> IRUnionRep -> Doc Unit
|
||||
renderUnionFunctions unionName unionRep = do
|
||||
let decoderName = decoderNameFromTypeName unionName
|
||||
line $ decoderName <> " : Jdec.Decoder " <> unionName
|
||||
line $ decoderName <> " ="
|
||||
indent do
|
||||
let decFields = L.sortBy arrayFirstOrder $ unionToList unionRep
|
||||
line "Jdec.oneOf"
|
||||
indent do
|
||||
forWithPrefix_ decFields "[" "," \bracketOrComma t -> do
|
||||
constructor <- unionConstructorName unionName t
|
||||
when (t == IRNull) do
|
||||
line $ bracketOrComma <> " Jdec.null " <> constructor
|
||||
unless (t == IRNull) do
|
||||
decoder <- decoderNameForType t
|
||||
line $ bracketOrComma <> " Jdec.map " <> constructor <> " " <> parenIfNeeded decoder
|
||||
line "]"
|
||||
blank
|
||||
let encoderName = encoderNameFromTypeName unionName
|
||||
line $ encoderName <> " : " <> unionName <> " -> Jenc.Value"
|
||||
line $ encoderName <> " x = case x of"
|
||||
indent do
|
||||
fields <- unionToList unionRep # sortByKeyM (unionConstructorName unionName)
|
||||
for_ fields \t -> do
|
||||
constructor <- unionConstructorName unionName t
|
||||
when (t == IRNull) do
|
||||
line $ constructor <> " -> Jenc.null"
|
||||
unless (t == IRNull) do
|
||||
{ rendered: encoder } <- encoderNameForType t
|
||||
line $ constructor <> " y -> " <> encoder <> " y"
|
||||
where
|
||||
arrayFirstOrder a b =
|
||||
if isArray a then
|
||||
if isArray b then
|
||||
compare a b
|
||||
else
|
||||
LT
|
||||
else
|
||||
if isArray b then
|
||||
GT
|
||||
else
|
||||
compare a b
|
|
@ -3,7 +3,6 @@ module Language.Renderers
|
|||
, rendererForLanguage
|
||||
) where
|
||||
|
||||
import Language.Elm as Elm
|
||||
import Language.Swift as Swift
|
||||
import Language.JsonSchema as JsonSchema
|
||||
|
||||
|
@ -14,8 +13,7 @@ import Data.Foldable (elem, find)
|
|||
|
||||
all :: Array Doc.Renderer
|
||||
all =
|
||||
[ Elm.renderer
|
||||
, Swift.renderer
|
||||
[ Swift.renderer
|
||||
, JsonSchema.renderer
|
||||
]
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ update msg _ =
|
|||
case msg of
|
||||
FromJS str ->
|
||||
case decodeString QuickType.quickType str of
|
||||
Ok r -> ((), toJS (QuickType.encodeQuickType r))
|
||||
Ok r -> ((), toJS (QuickType.quickTypeToString r))
|
||||
Err err -> ((), toJS ("Error: " ++ err))
|
||||
|
||||
subscriptions : () -> Sub Msg
|
||||
|
|
Загрузка…
Ссылка в новой задаче