Last active
October 1, 2017 19:16
-
-
Save mrange/f1d33cc14874b3a283a5691effc02847 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace AnyTransformer { | |
export interface Cons<T> { | |
kind: "Cons" | |
head: T | |
tail: ImmutableList<T> | |
} | |
export interface Empty { | |
kind: "Empty" | |
} | |
export type ImmutableList<T> = Cons<T> | Empty | |
export let empty : Empty = { kind: "Empty" } | |
export function cons<T>(head: T, tail: ImmutableList<T>) : ImmutableList<T> { | |
return { | |
kind: "Cons" , | |
head: head , | |
tail: tail | |
} | |
} | |
export interface BadCause { | |
message: string | |
} | |
function badCauseIndexOutOfRange(index: number): BadCause { | |
return { | |
message: "IndexOutOfRange: " + index | |
} | |
} | |
function badCauseMessage(msg: string) : BadCause { | |
return { | |
message: "Message: " + msg | |
} | |
} | |
function badCauseNotAnArray(): BadCause { | |
return { | |
message: "NotAnObject" | |
} | |
} | |
function badCauseNotAnObject(): BadCause { | |
return { | |
message: "NotAnObject" | |
} | |
} | |
function badCauseMissingMember(name: string): BadCause { | |
return { | |
message: "MissingMember:" + name | |
} | |
} | |
export interface MemberPathPart { | |
kind : "Member" | |
member: string | |
} | |
export interface IndexPathPart { | |
kind : "Index" | |
index : number | |
} | |
export interface NamedPathPart { | |
kind: "Named" | |
name: string | |
} | |
export type PathPart = MemberPathPart | IndexPathPart | NamedPathPart | |
function indexPathPart(index: number): IndexPathPart { | |
return { | |
kind : "Index", | |
index : index | |
} | |
} | |
function memberPathPart(name: string) : MemberPathPart { | |
return { | |
kind : "Member" , | |
member: name | |
} | |
} | |
export type Path = ImmutableList<PathPart> | |
export interface Leaf { | |
kind : "Leaf" | |
path : Path | |
badCause : BadCause | |
} | |
export interface Fork { | |
kind : "Fork" | |
left : BadTree | |
right : BadTree | |
} | |
export interface Suppress { | |
kind : "Suppress" | |
tree : BadTree | |
} | |
export type BadTree = Empty | Leaf | Fork | Suppress | |
function btleaf(path: Path, badCause: BadCause): Leaf { | |
return { | |
kind : "Leaf" , | |
path : path , | |
badCause: badCause | |
} | |
} | |
function btfork(l: BadTree, r: BadTree) : Fork { | |
return { | |
kind : "Fork", | |
left : l , | |
right : r | |
} | |
} | |
function btsuppress(t: BadTree): Suppress { | |
return { | |
kind: "Suppress", | |
tree: t | |
} | |
} | |
function btjoin(l: BadTree, r: BadTree) : BadTree { | |
if (l.kind == "Empty") return r | |
else if (r.kind == "Empty") return l | |
else if (l.kind == "Suppress" && r.kind == "Suppress") return btsuppress(btfork(l.tree, r.tree)) | |
else return btfork(l, r) | |
} | |
export interface TransformResult<T> { | |
value : T | |
badTree : BadTree | |
} | |
export interface Transform<T> { | |
(path: Path, value: any) : TransformResult<T> | |
} | |
export function tresult<T>(value: T, badTree: BadTree) : TransformResult<T> { | |
return { | |
value: value , | |
badTree: badTree | |
} | |
} | |
export function trgood<T>(value: T) { | |
return tresult<T>(value, empty) | |
} | |
export function treturn<T>(goodValue: T) : Transform<T> { | |
return (path, value) => { | |
return trgood(goodValue) | |
} | |
} | |
export function tbad<T>(badValue: T, badCause: BadCause) : Transform<T> { | |
return (path, value) => { | |
return tresult(badValue, btleaf(path, badCause)) | |
} | |
} | |
export function tbind<T, U>(t: Transform<T>, uf: (T) => Transform<U> ): Transform<U> { | |
return (path, value) => { | |
let tr = t(path, value) | |
let u = uf(tr.value) | |
let ur = u(path, value) | |
return tresult(ur.value, btjoin(tr.badTree, ur.badTree)) | |
} | |
} | |
export function tapply<T, U>(f: Transform<(T) => U>, t: Transform<T> ): Transform<U> { | |
return (path, value) => { | |
let fr = f(path, value) | |
let tr = t(path, value) | |
return tresult(fr.value(tr.value), btjoin(fr.badTree, tr.badTree)) | |
} | |
} | |
export function tmap<T, U>(m: (T) => U, t: Transform<T>): Transform<U> { | |
return (path, value) => { | |
let tr = t(path, value) | |
return tresult(m(tr.value), tr.badTree) | |
} | |
} | |
export function tindex<T>(index: number, badValue: T, t: Transform<T>): Transform<T> { | |
return (path, value) => { | |
if (value instanceof Array) { | |
if (index >= 0 && index < value.length) { | |
let nv = value[index] | |
let np = cons(indexPathPart(index), path) | |
t(np, nv) | |
} else { | |
return tresult(badValue, btleaf(path, badCauseIndexOutOfRange(name))) | |
} | |
} else { | |
return tresult(badValue, btleaf(path, badCauseNotAnArray())) | |
} | |
} | |
} | |
export function tmember<T>(name: string, badValue: T, t: Transform<T>): Transform<T> { | |
return (path, value) => { | |
if (value instanceof Object) { | |
let nv = value[name] | |
if (nv != null) { | |
let np = cons(memberPathPart(name), path) | |
t(np, nv) | |
} else { | |
return tresult(badValue, btleaf(path, badCauseMissingMember(name))) | |
} | |
} else { | |
return tresult(badValue, btleaf(path, badCauseNotAnObject())) | |
} | |
} | |
} | |
export function tasString(): Transform<string> { | |
return (path, value) => { | |
if (value == null) { | |
return tresult("", empty) | |
} else if (value instanceof String) { | |
return tresult(<string>value, empty) | |
} else { | |
return tresult(value.toString(), empty) | |
} | |
} | |
} | |
export function tasNumber(): Transform<number> { | |
return (path, value) => { | |
if (value == null) { | |
return tresult(0.0, empty) | |
} else if (value instanceof Number) { | |
return tresult(<number>value, empty) | |
} else { | |
let n = Number(value) | |
if (isNaN(n)) { | |
return tresult(n, btleaf(path, badCauseMessage("Can't interpret value as number"))) | |
} else { | |
return tresult(n, empty) | |
} | |
} | |
} | |
} | |
export function trun<T>(t: Transform<T>, value: any) : [T, BadTree] { | |
let tr = t(empty, value) | |
return [tr.value, tr.badTree] | |
} | |
} | |
let customer = | |
{ | |
envelopeId: "uuid1001" , | |
timestamp : "2017-10-01" , | |
schema : "new_customer" , | |
request : | |
{ | |
id : 1001 , | |
firstName : "Bill" , | |
lastName : "Gates" | |
} | |
} | |
let t = AnyTransformer.tasNumber() | |
document.body.innerHTML = AnyTransformer.trun(transf, "1.0").toString() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment