diff --git a/dist/option/api/format.d.ts b/dist/option/api/format.d.ts index dde4e7b..7dee634 100644 --- a/dist/option/api/format.d.ts +++ b/dist/option/api/format.d.ts @@ -1,3 +1,3 @@ -import { OptionUnion } from "../interfaces"; -export declare function format(option: OptionUnion): string; +import { Option } from "../interfaces"; +export declare function format(option: Option, fn?: (option: Option) => string): string; //# sourceMappingURL=format.d.ts.map \ No newline at end of file diff --git a/dist/option/api/format.d.ts.map b/dist/option/api/format.d.ts.map index 4e1dfe5..4033bd1 100644 --- a/dist/option/api/format.d.ts.map +++ b/dist/option/api/format.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../../src/option/api/format.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,wBAAgB,MAAM,CAAC,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,UAE/C"} \ No newline at end of file +{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../../src/option/api/format.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAe,MAAM,eAAe,CAAC;AAEpD,wBAAgB,MAAM,CAAC,CAAC,EACtB,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,EACjB,EAAE,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,UAMnC"} \ No newline at end of file diff --git a/dist/option/api/format.js b/dist/option/api/format.js index 7d2ee67..30a855f 100644 --- a/dist/option/api/format.js +++ b/dist/option/api/format.js @@ -1,3 +1,6 @@ -export function format(option) { - return option.type === "Some" ? `Some(${option.value})` : `None`; +export function format(option, fn) { + const inner = option.inner(); + return inner.type === "Some" + ? `Some(${fn?.(option) ?? inner.value})` + : `None`; } diff --git a/dist/option/api/isNoneAnd.d.ts b/dist/option/api/isNoneAnd.d.ts new file mode 100644 index 0000000..a87d858 --- /dev/null +++ b/dist/option/api/isNoneAnd.d.ts @@ -0,0 +1,3 @@ +import { Option } from "../interfaces"; +export declare function isNoneAnd(option: Option, fn: () => boolean): boolean; +//# sourceMappingURL=isNoneAnd.d.ts.map \ No newline at end of file diff --git a/dist/option/api/isNoneAnd.d.ts.map b/dist/option/api/isNoneAnd.d.ts.map new file mode 100644 index 0000000..594185d --- /dev/null +++ b/dist/option/api/isNoneAnd.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"isNoneAnd.d.ts","sourceRoot":"","sources":["../../../src/option/api/isNoneAnd.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAEvC,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,GAAG,OAAO,CAE1E"} \ No newline at end of file diff --git a/dist/option/api/isNoneAnd.js b/dist/option/api/isNoneAnd.js new file mode 100644 index 0000000..45aa246 --- /dev/null +++ b/dist/option/api/isNoneAnd.js @@ -0,0 +1,3 @@ +export function isNoneAnd(option, fn) { + return option.isNone() && fn(); +} diff --git a/dist/option/api/map.d.ts b/dist/option/api/map.d.ts index 27ab925..e3e31f4 100644 --- a/dist/option/api/map.d.ts +++ b/dist/option/api/map.d.ts @@ -1,3 +1,3 @@ -import { OptionUnion } from "../interfaces"; -export declare function map(option: OptionUnion, fn: (value: T) => U): OptionUnion; +import { Option, OptionUnion } from "../interfaces"; +export declare function map(option: Option, fn: (value: T) => U): OptionUnion; //# sourceMappingURL=map.d.ts.map \ No newline at end of file diff --git a/dist/option/api/map.d.ts.map b/dist/option/api/map.d.ts.map index 93f14ab..17054d3 100644 --- a/dist/option/api/map.d.ts.map +++ b/dist/option/api/map.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"map.d.ts","sourceRoot":"","sources":["../../../src/option/api/map.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAK5C,wBAAgB,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,kBAKpE"} \ No newline at end of file +{"version":3,"file":"map.d.ts","sourceRoot":"","sources":["../../../src/option/api/map.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAKpD,wBAAgB,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,kBAM/D"} \ No newline at end of file diff --git a/dist/option/api/map.js b/dist/option/api/map.js index 839a17b..feec208 100644 --- a/dist/option/api/map.js +++ b/dist/option/api/map.js @@ -2,7 +2,8 @@ import { unionNone } from "./unionNone"; import { unionSome } from "./unionSome"; import { unwrap } from "./unwrap"; export function map(option, fn) { - if (option.type === "Some") { + const inner = option.inner(); + if (inner.type === "Some") { return unionSome(fn(unwrap(option))); } return unionNone(); diff --git a/dist/option/api/unwrap.d.ts b/dist/option/api/unwrap.d.ts index 81f10c4..cef01ed 100644 --- a/dist/option/api/unwrap.d.ts +++ b/dist/option/api/unwrap.d.ts @@ -1,3 +1,3 @@ -import { OptionUnion } from "../interfaces"; -export declare function unwrap(option: OptionUnion): T; +import { Option } from "../interfaces"; +export declare function unwrap(option: Option): T; //# sourceMappingURL=unwrap.d.ts.map \ No newline at end of file diff --git a/dist/option/api/unwrap.d.ts.map b/dist/option/api/unwrap.d.ts.map index 498fd19..c974093 100644 --- a/dist/option/api/unwrap.d.ts.map +++ b/dist/option/api/unwrap.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"unwrap.d.ts","sourceRoot":"","sources":["../../../src/option/api/unwrap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAG5C,wBAAgB,MAAM,CAAC,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,KAK/C"} \ No newline at end of file +{"version":3,"file":"unwrap.d.ts","sourceRoot":"","sources":["../../../src/option/api/unwrap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAe,MAAM,eAAe,CAAC;AAGpD,wBAAgB,MAAM,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,KAM1C"} \ No newline at end of file diff --git a/dist/option/api/unwrap.js b/dist/option/api/unwrap.js index 0a321e8..02e104a 100644 --- a/dist/option/api/unwrap.js +++ b/dist/option/api/unwrap.js @@ -1,7 +1,8 @@ import { format } from "./format"; export function unwrap(option) { - if (option.type === "None") { + const inner = option.inner(); + if (inner.type === "None") { throw new Error(`unwrap called on ${format(option)}`); } - return option.value; + return inner.value; } diff --git a/dist/option/index.d.ts.map b/dist/option/index.d.ts.map index 87fbc14..4d3546c 100644 --- a/dist/option/index.d.ts.map +++ b/dist/option/index.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/option/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAmBpC,OAAO,KAAK,EAAE,MAAM,EAAQ,WAAW,EAAQ,MAAM,cAAc,CAAC;AAGpE,wBAAgB,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAuB5D;AAED,wBAAgB,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,aAE3B;AACD,wBAAgB,IAAI,CAAC,CAAC,eAErB"} \ No newline at end of file +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/option/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAmBpC,OAAO,KAAK,EAAE,MAAM,EAAQ,WAAW,EAAQ,MAAM,cAAc,CAAC;AAIpE,wBAAgB,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAwB5D;AAED,wBAAgB,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,aAE3B;AACD,wBAAgB,IAAI,CAAC,CAAC,eAErB"} \ No newline at end of file diff --git a/dist/option/index.js b/dist/option/index.js index 0bf8771..07d4c4e 100644 --- a/dist/option/index.js +++ b/dist/option/index.js @@ -18,20 +18,22 @@ import { format } from "./api/format"; import { unionNone } from "./api/unionNone"; import { unionSome } from "./api/unionSome"; import { filter } from "./api/filter"; +import { isNoneAnd } from "./api/isNoneAnd"; export function createOption(v) { let inner = v; const api = { inner: () => inner, eq: (value, by) => eq(api, value, by), - format: () => format(inner), + format: (formatter) => format(api, formatter), clone: () => createOption(clone(inner)), - unwrap: () => unwrap(inner), + unwrap: () => unwrap(api), unwrapOr: (default_value) => unwrapOr(inner, default_value), isNone: () => isNone(inner), isSome: () => isSome(inner), take: () => createOption(take(inner)), isSomeAnd: (fn) => isSomeAnd(api, fn), - map: (fn) => createOption(map(inner, fn)), + isNoneAnd: (fn) => isNoneAnd(api, fn), + map: (fn) => createOption(map(api, fn)), or: (new_value) => createOption(or(api, new_value)), orElse: (fn) => createOption(orElse(api, fn)), and: (new_value) => createOption(and(api, new_value)), diff --git a/dist/option/interfaces.d.ts b/dist/option/interfaces.d.ts index 9f4dbb5..8f83d78 100644 --- a/dist/option/interfaces.d.ts +++ b/dist/option/interfaces.d.ts @@ -8,22 +8,213 @@ export interface None { } export type OptionUnion = None | Some; export interface Option { + /** + * Extract internals as union. Useful when you want to handle option yourself + * # Example + * ```ts + * const option = Some(3).inner(); + * if (option.type === "Some") { + * option.value // number + * } + * ``` + */ inner(): OptionUnion; + /** + * Compare two `Option`'s to each other + * # Example + * ```ts + * Some(3).eq(Some(3)) // true + * Some(3).eq(Some(4)) // false + * Some(3).eq(None()) // false + * ``` + */ eq(v: Option, by?: (item: T) => U): boolean; - format(): string; + /** + * Just a simple data formatter + * # Example + * ```ts + * Some(3).format() // `Some(3)` + * + * // or if you want to format your value yourself + * Some({ x: 3 }).format((v) => JSON.stringify(v.inner().value, null, 2)) + * ``` + */ + format(formatter?: (result: Option) => string): string; + /** Clone option internal state + * # Example + * ```ts + * // Imagine we mutating Option's inner state + * const maybeString = Some("data"); // Some("data") + * const takenString = maybeString.take(); // Some("data") + * maybeString.isNone() // true + * // In order to save maybeString inner state we have to clone it + * const maybeString = Some("data"); // Some("data") + * const takenString = maybeString.clone().take(); // Some("data") + * maybeString.isSome() // true + * ``` + */ clone(): Option; + /** + * Returns the contained `Some` value. It is preferred to use other methods rather than using `unwrap` + * + * But it's usefull for quick prototyping or when you know type inside + * + * # Throws + * When value is `None` + * # Example + * ```ts + * Some(3).unwrap() // 3 + * None().unwrap() // throw Error + * ``` + */ unwrap(): T; + /** + * Returns the contained `Some` or default value + * # Example + * ```ts + * Some(3).unwrapOr(4) // 3 + * None().unwrapOr(4) // 4 + * ``` + */ unwrapOr(v: T): T; + /** + * Returns `true` if value is `None` + * # Example + * ```ts + * Some(3).isNone() // false + * None().isNone() // true + * ``` + */ isNone(): boolean; + /** + * Returns `true` if value is `Some` + * # Example + * ```ts + * Some(3).isSome() // true + * None().isSome() // false + * ``` + */ isSome(): boolean; + /** + * Takes the value out of the option, leaving a `None` in its place. + * + * # Example + * ```ts + * const maybeString = Some("data"); // Some("data") + * const takenString = maybeString.take(); // Some("data") + * maybeString.isNone() // true + * ``` + */ take(): Option; + /** + * Returns `true` if value `Some` and predicate `fn` returns `true`. + * + * # Example + * ```ts + * Some(3).isSomeAnd(v => v === 3) // true + * Some(3).isSomeAnd(v => v === 4) // false + * None().isSomeAnd(v => true) // false + * ``` + */ isSomeAnd(fn: (v: T) => boolean): boolean; + /** + * Returns `true` if value `Some` and predicate `fn` returns `true`. + * + * # Example + * ```ts + * Some(3).isNoneAnd(v => true) // false + * None().isNoneAnd(v => true) // true + * None().isNoneAnd(v => false) // false + * ``` + */ + isNoneAnd(fn: () => boolean): boolean; + /** + * Changes contained value if `Some` by applying `fn` to previous `Some`. + * + * # Example + * ```ts + * Some(3).map(v => "value") // Some("value") + * None().map(v => "value") // None() + * ``` + */ map(fn: (v: T) => U): Option; - or(v: Option): Option; + /** + * Returns `option` if the result is `None`, otherwise returns the `Some` value. + * + * This function can be used for control flow based on `Option` values. + * # Example + * ```ts + * Some(3).or(Some(6)) // Some(3) + * None().or(Some(6)) // Some(6) + * None().or(None()) // None() + * ``` + */ + or(option: Option): Option; + /** + * Calls `fn` if the result is `None`, otherwise returns the `Some` value. + * + * This function can be used for control flow based on `Option` values. + * # Example + * ```ts + * Some(3).orElse(() => Some(6)) // Some(3) + * None().orElse(() => Some(6)) // Some(6) + * None().orElse(() => None()) // None() + * ``` + */ orElse(fn: () => Option): Option; + /** + * Returns `option` if the result is `Some`, otherwise returns the `None`. + * + * This function can be used for control flow based on `Option` values. + * # Example + * ```ts + * Some(3).and(Some(6)) // Some(6) + * Some(3).and(None()) // None() + * None().and(Some(6)) // None() + * ``` + */ and(v: Option): Option; + /** + * Calls `fn` if the result is `Some`, otherwise returns the `None`. + * + * This function can be used for control flow based on `Option` values. + * # Example + * ```ts + * Some(3).andThen(() => Some(6)) // Some(6) + * Some(3).andThen(() => None()) // None() + * None().andThen(() => Some(6)) // None() + * ``` + */ andThen(fn: (v: T) => Option): Option; + /** + * Convert `Option` to `Result` with provided default `Err` if value is `None` + * # Example + * ```ts + * const withString = Some("data"); + * const withoutString = None(); + * + * withString.result(() => "Not a string!") // Ok("data") + * withoutString.result(() => "Not a string!") // Err("Not a string!") + * ``` + */ result(noneErr: () => E): Result; + /** + * Returns `None` if the option is `None`, otherwise calls predicate with the wrapped value and returns: + * + * - `Some(t)` if predicate returns `true` (where `t` is the wrapped value) + * - `None` if predicate returns `false`. + * + * This function works similar to `Iter.filter()`. You can imagine the `Option` being an iterator over one or zero elements. `filter()` lets you decide which elements to keep. + * + * # Example + * ```ts + * const data = Some("coolvalue"); + * const CoolData = data.filter(v => v.includes("cool")) + * assert(CoolData, Some("coolvalue")) + * const boringData = data.filter(v => v.includes("boring")) + * assert(boringData, None()) + * ``` + */ filter(fn: (item: T) => boolean): Option; } //# sourceMappingURL=interfaces.d.ts.map \ No newline at end of file diff --git a/dist/option/interfaces.d.ts.map b/dist/option/interfaces.d.ts.map index 34cdc5c..0fcb6a0 100644 --- a/dist/option/interfaces.d.ts.map +++ b/dist/option/interfaces.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"interfaces.d.ts","sourceRoot":"","sources":["../../src/option/interfaces.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAEnD,MAAM,WAAW,IAAI,CAAC,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,CAAC,CAAC;CACV;AACD,MAAM,WAAW,IAAI;IACnB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;AAE5C,MAAM,WAAW,MAAM,CAAC,CAAC;IACvB,KAAK,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC;IACxB,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC;IAClD,MAAM,IAAI,MAAM,CAAC;IACjB,KAAK,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC;IACnB,MAAM,IAAI,CAAC,CAAC;IACZ,QAAQ,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;IAClB,MAAM,IAAI,OAAO,CAAC;IAClB,MAAM,IAAI,OAAO,CAAC;IAClB,IAAI,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC;IAClB,SAAS,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,OAAO,GAAG,OAAO,CAAC;IAC1C,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACnC,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IAC5B,MAAM,CAAC,EAAE,EAAE,MAAM,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACvC,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IAChC,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IAC/C,MAAM,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC1C,MAAM,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;CAC7C"} \ No newline at end of file +{"version":3,"file":"interfaces.d.ts","sourceRoot":"","sources":["../../src/option/interfaces.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAEnD,MAAM,WAAW,IAAI,CAAC,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,CAAC,CAAC;CACV;AACD,MAAM,WAAW,IAAI;IACnB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;AAE5C,MAAM,WAAW,MAAM,CAAC,CAAC;IACvB;;;;;;;;;OASG;IACH,KAAK,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC;IACxB;;;;;;;;OAQG;IACH,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC;IAClD;;;;;;;;;OASG;IACH,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,GAAG,MAAM,CAAC;IAC1D;;;;;;;;;;;;OAYG;IACH,KAAK,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC;IACnB;;;;;;;;;;;;OAYG;IACH,MAAM,IAAI,CAAC,CAAC;IACZ;;;;;;;OAOG;IACH,QAAQ,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;IAClB;;;;;;;OAOG;IACH,MAAM,IAAI,OAAO,CAAC;IAClB;;;;;;;OAOG;IACH,MAAM,IAAI,OAAO,CAAC;IAClB;;;;;;;;;OASG;IACH,IAAI,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC;IAClB;;;;;;;;;OASG;IACH,SAAS,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,OAAO,GAAG,OAAO,CAAC;IAC1C;;;;;;;;;OASG;IACH,SAAS,CAAC,EAAE,EAAE,MAAM,OAAO,GAAG,OAAO,CAAC;IACtC;;;;;;;;OAQG;IACH,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACnC;;;;;;;;;;OAUG;IACH,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACjC;;;;;;;;;;OAUG;IACH,MAAM,CAAC,EAAE,EAAE,MAAM,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACvC;;;;;;;;;;OAUG;IACH,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IAChC;;;;;;;;;;OAUG;IACH,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IAC/C;;;;;;;;;;OAUG;IACH,MAAM,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC1C;;;;;;;;;;;;;;;;OAgBG;IACH,MAAM,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;CAC7C"} \ No newline at end of file diff --git a/dist/result/api/format.d.ts b/dist/result/api/format.d.ts index e20b988..14a5211 100644 --- a/dist/result/api/format.d.ts +++ b/dist/result/api/format.d.ts @@ -1,3 +1,3 @@ -import { ResultUnion } from "../interfaces"; -export declare function format(result: ResultUnion): string; +import { Result } from "../interfaces"; +export declare function format(result: Result, formatter?: (result: Result) => string): string; //# sourceMappingURL=format.d.ts.map \ No newline at end of file diff --git a/dist/result/api/format.d.ts.map b/dist/result/api/format.d.ts.map index 8bea7b5..ddaee88 100644 --- a/dist/result/api/format.d.ts.map +++ b/dist/result/api/format.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../../src/result/api/format.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,wBAAgB,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,UAErD"} \ No newline at end of file +{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../../src/result/api/format.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAe,MAAM,eAAe,CAAC;AAEpD,wBAAgB,MAAM,CAAC,CAAC,EAAE,CAAC,EACzB,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,EACpB,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,MAAM,UAI7C"} \ No newline at end of file diff --git a/dist/result/api/format.js b/dist/result/api/format.js index d4415f0..9591917 100644 --- a/dist/result/api/format.js +++ b/dist/result/api/format.js @@ -1,3 +1,4 @@ -export function format(result) { - return `Result.${result.type}(${result.value})`; +export function format(result, formatter) { + const inner = result.inner(); + return `Result.${inner.type}(${formatter?.(result) ?? inner.value})`; } diff --git a/dist/result/index.js b/dist/result/index.js index 66d98a7..6afeb3a 100644 --- a/dist/result/index.js +++ b/dist/result/index.js @@ -26,7 +26,7 @@ export function createResult(result) { const api = { inner: () => result, eq: (other) => eq(api, other), - format: () => format(result), + format: (formatter) => format(api, formatter), isOk: () => isOk(result), isErr: () => isErr(result), unwrap: () => unwrap(api), diff --git a/dist/result/interfaces.d.ts b/dist/result/interfaces.d.ts index d9c3e44..db57533 100644 --- a/dist/result/interfaces.d.ts +++ b/dist/result/interfaces.d.ts @@ -13,13 +13,13 @@ export interface Result { * Just a simple data formatter * # Example * ```ts - * Ok(3).format() === `Result.Ok(3)` + * Ok(3).format() // `Result.Ok(3)` * * // or if you want to format your value yourself * Err({ x: 3 }).format((v) => JSON.stringify(v.inner().value, null, 2)) * ``` */ - format(fn?: (result: Result) => string): string; + format(formatter?: (result: Result) => string): string; /** * Compare two `Result`'s to each other * # Example diff --git a/dist/result/interfaces.d.ts.map b/dist/result/interfaces.d.ts.map index 8796919..3e3b80a 100644 --- a/dist/result/interfaces.d.ts.map +++ b/dist/result/interfaces.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"interfaces.d.ts","sourceRoot":"","sources":["../../src/result/interfaces.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"interfaces.d.ts","sourceRoot":"","sources":["../../src/result/interfaces.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/readme.md b/readme.md index 7394074..6f30748 100644 --- a/readme.md +++ b/readme.md @@ -10,7 +10,7 @@ ### Modules - [Option<T>](/src/option/) - Forget about nulls and undefined -- [Result<L, R>](/src/result/) - Handle errors like a god +- [Result<T, E>](/src/result/) - Handle errors like a god - [Iter<T>](/src/iter/) - Lazy iterables with superpowers # Installation diff --git a/src/iter/readme.md b/src/iter/readme.md index c251819..29041d7 100644 --- a/src/iter/readme.md +++ b/src/iter/readme.md @@ -1,13 +1,15 @@

Iter<T>

More efficient way to work with arrays

-```ts -import { IterFrom } from "kirka"; -``` +If you’ve found yourself with a collection of some kind, and needed to perform an operation on the elements of said collection, you’ll quickly run into ‘iterators’. Iterators are heavily used in idiomatic Rust code, but it's very rare to find iterators (generators) in someone else's code. + +Let's change that! Create `Iter` ```ts +import { IterFrom } from "kirka"; + const numIter = IterFrom.array([1, 2, 3, 4]); const numIter = IterFrom.range(1, 4, true); const numIter = IterFrom.iterable(numIter); // can be Generator or anything that implements Iterable @@ -44,5 +46,12 @@ const iterByTwo = iter.map((v) => v * 2); // [2,4,6,8] const iterByThree = iter.map((v) => v * 3); // [3,6,9,12] ``` +Insert items between elements + +```ts +const iter = IterFrom.array(1, 2, 3, 4); +iter.intersperse(10); // [1,10,2,10,3,10,4] +``` + > This hints and a more you can just read from IntelliSense (your editor suggestions) > or in source code `./interfaces.ts` diff --git a/src/option/api/format.ts b/src/option/api/format.ts index aeebe27..c94091c 100644 --- a/src/option/api/format.ts +++ b/src/option/api/format.ts @@ -1,5 +1,11 @@ -import { OptionUnion } from "../interfaces"; +import { Option, OptionUnion } from "../interfaces"; -export function format(option: OptionUnion) { - return option.type === "Some" ? `Some(${option.value})` : `None`; +export function format( + option: Option, + fn?: (option: Option) => string +) { + const inner = option.inner(); + return inner.type === "Some" + ? `Some(${fn?.(option) ?? inner.value})` + : `None`; } diff --git a/src/option/api/isNoneAnd.ts b/src/option/api/isNoneAnd.ts new file mode 100644 index 0000000..7e2b514 --- /dev/null +++ b/src/option/api/isNoneAnd.ts @@ -0,0 +1,5 @@ +import { Option } from "../interfaces"; + +export function isNoneAnd(option: Option, fn: () => boolean): boolean { + return option.isNone() && fn(); +} diff --git a/src/option/api/map.ts b/src/option/api/map.ts index 4438528..c3a94fd 100644 --- a/src/option/api/map.ts +++ b/src/option/api/map.ts @@ -1,10 +1,11 @@ -import { OptionUnion } from "../interfaces"; +import { Option, OptionUnion } from "../interfaces"; import { unionNone } from "./unionNone"; import { unionSome } from "./unionSome"; import { unwrap } from "./unwrap"; -export function map(option: OptionUnion, fn: (value: T) => U) { - if (option.type === "Some") { +export function map(option: Option, fn: (value: T) => U) { + const inner = option.inner(); + if (inner.type === "Some") { return unionSome(fn(unwrap(option))); } return unionNone(); diff --git a/src/option/api/unwrap.ts b/src/option/api/unwrap.ts index 9a5459c..46c5390 100644 --- a/src/option/api/unwrap.ts +++ b/src/option/api/unwrap.ts @@ -1,9 +1,10 @@ -import { OptionUnion } from "../interfaces"; +import { Option, OptionUnion } from "../interfaces"; import { format } from "./format"; -export function unwrap(option: OptionUnion) { - if (option.type === "None") { +export function unwrap(option: Option) { + const inner = option.inner(); + if (inner.type === "None") { throw new Error(`unwrap called on ${format(option)}`); } - return option.value; + return inner.value; } diff --git a/src/option/index.ts b/src/option/index.ts index 76acbac..dac74e5 100644 --- a/src/option/index.ts +++ b/src/option/index.ts @@ -20,6 +20,7 @@ import { unionNone } from "./api/unionNone"; import { unionSome } from "./api/unionSome"; import type { Option, Some, OptionUnion, None } from "./interfaces"; import { filter } from "./api/filter"; +import { isNoneAnd } from "./api/isNoneAnd"; export function createOption(v: OptionUnion): Option { let inner = v; @@ -27,15 +28,16 @@ export function createOption(v: OptionUnion): Option { const api: Option = { inner: () => inner, eq: (value, by) => eq(api, value, by), - format: () => format(inner), + format: (formatter) => format(api, formatter), clone: () => createOption(clone(inner)), - unwrap: () => unwrap(inner), + unwrap: () => unwrap(api), unwrapOr: (default_value) => unwrapOr(inner, default_value), isNone: () => isNone(inner), isSome: () => isSome(inner), take: () => createOption(take(inner)), isSomeAnd: (fn) => isSomeAnd(api, fn), - map: (fn) => createOption(map(inner, fn)), + isNoneAnd: (fn) => isNoneAnd(api, fn), + map: (fn) => createOption(map(api, fn)), or: (new_value) => createOption(or(api, new_value)), orElse: (fn) => createOption(orElse(api, fn)), and: (new_value) => createOption(and(api, new_value)), diff --git a/src/option/interfaces.ts b/src/option/interfaces.ts index b49483b..6bbacbc 100644 --- a/src/option/interfaces.ts +++ b/src/option/interfaces.ts @@ -11,21 +11,212 @@ export interface None { export type OptionUnion = None | Some; export interface Option { + /** + * Extract internals as union. Useful when you want to handle option yourself + * # Example + * ```ts + * const option = Some(3).inner(); + * if (option.type === "Some") { + * option.value // number + * } + * ``` + */ inner(): OptionUnion; + /** + * Compare two `Option`'s to each other + * # Example + * ```ts + * Some(3).eq(Some(3)) // true + * Some(3).eq(Some(4)) // false + * Some(3).eq(None()) // false + * ``` + */ eq(v: Option, by?: (item: T) => U): boolean; - format(): string; + /** + * Just a simple data formatter + * # Example + * ```ts + * Some(3).format() // `Some(3)` + * + * // or if you want to format your value yourself + * Some({ x: 3 }).format((v) => JSON.stringify(v.inner().value, null, 2)) + * ``` + */ + format(formatter?: (result: Option) => string): string; + /** Clone option internal state + * # Example + * ```ts + * // Imagine we mutating Option's inner state + * const maybeString = Some("data"); // Some("data") + * const takenString = maybeString.take(); // Some("data") + * maybeString.isNone() // true + * // In order to save maybeString inner state we have to clone it + * const maybeString = Some("data"); // Some("data") + * const takenString = maybeString.clone().take(); // Some("data") + * maybeString.isSome() // true + * ``` + */ clone(): Option; + /** + * Returns the contained `Some` value. It is preferred to use other methods rather than using `unwrap` + * + * But it's usefull for quick prototyping or when you know type inside + * + * # Throws + * When value is `None` + * # Example + * ```ts + * Some(3).unwrap() // 3 + * None().unwrap() // throw Error + * ``` + */ unwrap(): T; + /** + * Returns the contained `Some` or default value + * # Example + * ```ts + * Some(3).unwrapOr(4) // 3 + * None().unwrapOr(4) // 4 + * ``` + */ unwrapOr(v: T): T; + /** + * Returns `true` if value is `None` + * # Example + * ```ts + * Some(3).isNone() // false + * None().isNone() // true + * ``` + */ isNone(): boolean; + /** + * Returns `true` if value is `Some` + * # Example + * ```ts + * Some(3).isSome() // true + * None().isSome() // false + * ``` + */ isSome(): boolean; + /** + * Takes the value out of the option, leaving a `None` in its place. + * + * # Example + * ```ts + * const maybeString = Some("data"); // Some("data") + * const takenString = maybeString.take(); // Some("data") + * maybeString.isNone() // true + * ``` + */ take(): Option; + /** + * Returns `true` if value `Some` and predicate `fn` returns `true`. + * + * # Example + * ```ts + * Some(3).isSomeAnd(v => v === 3) // true + * Some(3).isSomeAnd(v => v === 4) // false + * None().isSomeAnd(v => true) // false + * ``` + */ isSomeAnd(fn: (v: T) => boolean): boolean; + /** + * Returns `true` if value `Some` and predicate `fn` returns `true`. + * + * # Example + * ```ts + * Some(3).isNoneAnd(v => true) // false + * None().isNoneAnd(v => true) // true + * None().isNoneAnd(v => false) // false + * ``` + */ + isNoneAnd(fn: () => boolean): boolean; + /** + * Changes contained value if `Some` by applying `fn` to previous `Some`. + * + * # Example + * ```ts + * Some(3).map(v => "value") // Some("value") + * None().map(v => "value") // None() + * ``` + */ map(fn: (v: T) => U): Option; - or(v: Option): Option; + /** + * Returns `option` if the result is `None`, otherwise returns the `Some` value. + * + * This function can be used for control flow based on `Option` values. + * # Example + * ```ts + * Some(3).or(Some(6)) // Some(3) + * None().or(Some(6)) // Some(6) + * None().or(None()) // None() + * ``` + */ + or(option: Option): Option; + /** + * Calls `fn` if the result is `None`, otherwise returns the `Some` value. + * + * This function can be used for control flow based on `Option` values. + * # Example + * ```ts + * Some(3).orElse(() => Some(6)) // Some(3) + * None().orElse(() => Some(6)) // Some(6) + * None().orElse(() => None()) // None() + * ``` + */ orElse(fn: () => Option): Option; + /** + * Returns `option` if the result is `Some`, otherwise returns the `None`. + * + * This function can be used for control flow based on `Option` values. + * # Example + * ```ts + * Some(3).and(Some(6)) // Some(6) + * Some(3).and(None()) // None() + * None().and(Some(6)) // None() + * ``` + */ and(v: Option): Option; + /** + * Calls `fn` if the result is `Some`, otherwise returns the `None`. + * + * This function can be used for control flow based on `Option` values. + * # Example + * ```ts + * Some(3).andThen(() => Some(6)) // Some(6) + * Some(3).andThen(() => None()) // None() + * None().andThen(() => Some(6)) // None() + * ``` + */ andThen(fn: (v: T) => Option): Option; + /** + * Convert `Option` to `Result` with provided default `Err` if value is `None` + * # Example + * ```ts + * const withString = Some("data"); + * const withoutString = None(); + * + * withString.result(() => "Not a string!") // Ok("data") + * withoutString.result(() => "Not a string!") // Err("Not a string!") + * ``` + */ result(noneErr: () => E): Result; + /** + * Returns `None` if the option is `None`, otherwise calls predicate with the wrapped value and returns: + * + * - `Some(t)` if predicate returns `true` (where `t` is the wrapped value) + * - `None` if predicate returns `false`. + * + * This function works similar to `Iter.filter()`. You can imagine the `Option` being an iterator over one or zero elements. `filter()` lets you decide which elements to keep. + * + * # Example + * ```ts + * const data = Some("coolvalue"); + * const CoolData = data.filter(v => v.includes("cool")) + * assert(CoolData, Some("coolvalue")) + * const boringData = data.filter(v => v.includes("boring")) + * assert(boringData, None()) + * ``` + */ filter(fn: (item: T) => boolean): Option; } diff --git a/src/option/option.test.ts b/src/option/option.test.ts index 0fb8ed6..944ea69 100644 --- a/src/option/option.test.ts +++ b/src/option/option.test.ts @@ -14,6 +14,18 @@ test(`.eq()`, (t) => { t.false(Some(3).eq(None())); t.false(None().eq(Some(3))); }); +test(`.format()`, (t) => { + t.is(Some(3).format(), `Some(3)`); + t.is(None().format(), `None`); + t.is( + Some(3).format((t) => `${t.unwrap() * 2}`), + `Some(6)` + ); + t.is( + None().format((t) => `wow`), + `None` + ); +}); test(`.eq(by)`, (t) => { t.true(Some({ x: 3 }).eq(Some({ x: 3 }), (v) => v.x)); t.true(None<{ x: number }>().eq(None(), (v) => v.x)); @@ -62,6 +74,18 @@ test(`None.isSomeAnd()`, (t) => { t.false(option.isSomeAnd((v) => true)); t.false(option.isSomeAnd((v) => false)); }); +test(`Some.isNoneAnd()`, (t) => { + const option = Some(5); + + t.false(option.isNoneAnd(() => true)); + t.false(option.isNoneAnd(() => false)); +}); +test(`None.isNoneAnd()`, (t) => { + const option = None(); + + t.true(option.isNoneAnd(() => true)); + t.false(option.isNoneAnd(() => false)); +}); test(`Some.map()`, (t) => { const optionSome = Some(5).map((v) => v * 2); t.true(optionSome.eq(Some(10))); diff --git a/src/option/readme.md b/src/option/readme.md index a7b912e..6bf6f08 100644 --- a/src/option/readme.md +++ b/src/option/readme.md @@ -1,13 +1,21 @@

Option<T>

Forget about null and undefined

-```ts -import { None, Some } from "kirka"; -``` +Type `Option` represents an optional value: every `Option` is either `Some` and contains a value, or `None`, and does not. -Create Option +Options can be used as: + +- Initial values +- Return values for functions that are not defined over their input range +- Return value for otherwise reporting simple errors, where None is returned on error +- Optional struct fields +- Struct fields that can be loaned or “taken” +- Optional function arguments +- Nulls ```ts +import { None, Some } from "kirka"; + const MaybeNumber = Some(3); MaybeNumber.isSome(); // true const MaybeNumber = None(); @@ -71,3 +79,4 @@ Some("value").isSomeAnd((v) => v === "value"); // or this ``` > This hints and a more you can just read from IntelliSense (your editor suggestions) +> or in source code `./interfaces.ts` diff --git a/src/result/api/format.ts b/src/result/api/format.ts index a00aca5..f5cabf6 100644 --- a/src/result/api/format.ts +++ b/src/result/api/format.ts @@ -1,5 +1,9 @@ -import { ResultUnion } from "../interfaces"; +import { Result, ResultUnion } from "../interfaces"; -export function format(result: ResultUnion) { - return `Result.${result.type}(${result.value})`; +export function format( + result: Result, + formatter?: (result: Result) => string +) { + const inner = result.inner(); + return `Result.${inner.type}(${formatter?.(result) ?? inner.value})`; } diff --git a/src/result/index.ts b/src/result/index.ts index 9f3c681..ab58785 100644 --- a/src/result/index.ts +++ b/src/result/index.ts @@ -29,7 +29,7 @@ export function createResult(result: ResultUnion): Result { const api: Result = { inner: () => result, eq: (other: Result) => eq(api, other), - format: () => format(result), + format: (formatter) => format(api, formatter), isOk: () => isOk(result), isErr: () => isErr(result), unwrap: () => unwrap(api), diff --git a/src/result/interfaces.ts b/src/result/interfaces.ts index 16e1698..5072153 100644 --- a/src/result/interfaces.ts +++ b/src/result/interfaces.ts @@ -15,13 +15,13 @@ export interface Result { * Just a simple data formatter * # Example * ```ts - * Ok(3).format() === `Result.Ok(3)` + * Ok(3).format() // `Result.Ok(3)` * * // or if you want to format your value yourself * Err({ x: 3 }).format((v) => JSON.stringify(v.inner().value, null, 2)) * ``` */ - format(fn?: (result: Result) => string): string; + format(formatter?: (result: Result) => string): string; /** * Compare two `Result`'s to each other * # Example diff --git a/src/result/result.test.ts b/src/result/result.test.ts index 537a18f..9245318 100644 --- a/src/result/result.test.ts +++ b/src/result/result.test.ts @@ -17,6 +17,18 @@ test(`x.eq(y) when x != y`, (t) => { t.false(Ok(4).eq(Ok(3))); t.false(Ok(4).eq(Err(3))); }); +test(`.format()`, (t) => { + t.is(Err(3).format(), `Result.Err(3)`); + t.is(Ok(3).format(), `Result.Ok(3)`); + t.is( + Err(3).format((t) => `${t.unwrapErr() * 2}`), + `Result.Err(6)` + ); + t.is( + Ok(3).format((t) => `${t.unwrap() * 2}`), + `Result.Ok(6)` + ); +}); test(`.isOk()`, (t) => { t.false(Err(3).isOk()); t.true(Ok(3).isOk());