summaryrefslogtreecommitdiff
path: root/lib/trace/itrace.ts
blob: e2019fa341a8c1f7d67b8ad835abbee858ce8743 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
import type { Mapper, SideEffect, Supplier } from '@emprespresso/pengueno';

/**
 * the "thing" every Trace writer must "trace()".
 */
type BaseTraceWith = string;
export type ITraceWith<T> = BaseTraceWith | T;
export interface ITrace<TraceWith> {
    /**
     * creates a new trace scope which inherits from this trace.
     */
    traceScope: Mapper<ITraceWith<TraceWith>, ITrace<TraceWith>>;

    /**
     * does the tracing.
     */
    trace: SideEffect<ITraceWith<TraceWith>>;
}

export type ITraceableTuple<T, TraceWith> = { item: T; trace: BaseTraceWith | TraceWith };
export type ITraceableMapper<T, _T, TraceWith> = (w: ITraceable<T, TraceWith>) => _T;

export interface ITraceable<T, Trace = BaseTraceWith> {
    readonly trace: ITrace<Trace>;
    readonly get: Supplier<T>;

    readonly move: <_T>(t: _T) => ITraceable<_T, Trace>;
    readonly map: <_T>(mapper: ITraceableMapper<T, _T, Trace>) => ITraceable<_T, Trace>;
    readonly bimap: <_T>(mapper: ITraceableMapper<T, ITraceableTuple<_T, Trace>, Trace>) => ITraceable<_T, Trace>;
    readonly coExtend: <_T>(mapper: ITraceableMapper<T, Array<_T>, Trace>) => Array<ITraceable<_T, Trace>>;
    readonly peek: (peek: ITraceableMapper<T, void, Trace>) => ITraceable<T, Trace>;

    readonly traceScope: (mapper: ITraceableMapper<T, Trace, Trace>) => ITraceable<T, Trace>;

    readonly flatMap: <_T>(mapper: ITraceableMapper<T, ITraceable<_T, Trace>, Trace>) => ITraceable<_T, Trace>;
    readonly flatMapAsync: <_T>(
        mapper: ITraceableMapper<T, Promise<ITraceable<_T, Trace>>, Trace>,
    ) => ITraceable<Promise<_T>, Trace>;
}

export class TraceableImpl<T, Trace> implements ITraceable<T, Trace> {
    protected constructor(
        private readonly item: T,
        public readonly trace: ITrace<Trace>,
    ) {}

    public map<_T>(mapper: ITraceableMapper<T, _T, Trace>) {
        const result = mapper(this);
        return new TraceableImpl(result, this.trace);
    }

    public coExtend<_T>(mapper: ITraceableMapper<T, Array<_T>, Trace>): Array<ITraceable<_T, Trace>> {
        const results = mapper(this);
        return Array.from(results).map((result) => this.move(result));
    }

    public flatMap<_T>(mapper: ITraceableMapper<T, ITraceable<_T, Trace>, Trace>): ITraceable<_T, Trace> {
        return mapper(this);
    }

    public flatMapAsync<_T>(
        mapper: ITraceableMapper<T, Promise<ITraceable<_T, Trace>>, Trace>,
    ): ITraceable<Promise<_T>, Trace> {
        return new TraceableImpl(
            mapper(this).then((t) => t.get()),
            this.trace,
        );
    }

    public traceScope(mapper: ITraceableMapper<T, Trace, Trace>): ITraceable<T, Trace> {
        return new TraceableImpl(this.get(), this.trace.traceScope(mapper(this)));
    }

    public peek(peek: ITraceableMapper<T, void, Trace>) {
        peek(this);
        return this;
    }

    public move<_T>(t: _T): ITraceable<_T, Trace> {
        return this.map(() => t);
    }

    public bimap<_T>(mapper: ITraceableMapper<T, ITraceableTuple<_T, Trace>, Trace>) {
        const { item, trace: _trace } = mapper(this);
        return this.move(item).traceScope(() => <Trace>_trace);
    }

    public get() {
        return this.item;
    }
}