Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions @coven/compare/compareIterables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export const compareIterables =
const leftIterator = getIterator(left);
const rightIterator = getIterator(right);

// deno-lint-ignore coven/no-for
for (
let index = 0,
{ done: leftDone = false, value: leftValue } =
Expand Down
2 changes: 1 addition & 1 deletion @coven/compare/deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"$schema": "https://raw.githubusercontent.com/denoland/deno/main/cli/schemas/config-file.v1.json",
"exports": "./mod.ts",
"name": "@coven/compare",
"version": "0.9.7"
"version": "0.9.8"
}
25 changes: 17 additions & 8 deletions @coven/compare/tests/compare.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { EMPTY_ARRAY, EMPTY_OBJECT } from "@coven/constants";
import { EMPTY_ITERABLE_ITERATOR } from "@coven/iterables";
import {
EMPTY_ITERABLE_ITERATOR,
filter,
iterableToArray,
map,
} from "@coven/iterables";
import { createObject } from "@coven/utils";
import { assertEquals } from "@std/assert";
import { compare } from "../compare.ts";
Expand Down Expand Up @@ -195,16 +200,20 @@ Deno.test(
constructor: Array,
length: 1,
});
const keyFilter = filter(
(key: string | symbol) => !Reflect.ownKeys(fakeArray).includes(key),
);
const keyMap = map((key: string | symbol) => ({
kind: DELETE_KIND,
left: Array.prototype[key as keyof typeof Array.prototype],
path: [key],
}));

return assertEquals(
flat(compare(["coven"])(fakeArray)),
Reflect.ownKeys(Array.prototype)
.filter((key) => !Reflect.ownKeys(fakeArray).includes(key))
.map((key) => ({
kind: DELETE_KIND,
left: Array.prototype[key as keyof typeof Array.prototype],
path: [key],
})) as ReturnType<typeof flat>,
iterableToArray(
keyMap(keyFilter(Reflect.ownKeys(Array.prototype))),
) as ReturnType<typeof flat>,
);
},
);
Expand Down
2 changes: 1 addition & 1 deletion @coven/constants/deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"$schema": "https://raw.githubusercontent.com/denoland/deno/main/cli/schemas/config-file.v1.json",
"exports": "./mod.ts",
"name": "@coven/constants",
"version": "0.9.7"
"version": "0.9.8"
}
3 changes: 2 additions & 1 deletion @coven/cron/compareField.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { some } from "@coven/iterables";
import { memoFunction } from "@coven/memo";
import type { Filter, Unary } from "@coven/types";
import { always } from "@coven/utils";
Expand Down Expand Up @@ -34,7 +35,7 @@ export const compareField: Unary<
isAllToken(field) ? alwaysTrue : (
memoFunction(
isListField(field) ?
(value) => field.some(compareRangeOrValue(value))
(value) => some(compareRangeOrValue(value))(field)
: (value) => compareRangeOrValue(value)(field),
)
),
Expand Down
2 changes: 1 addition & 1 deletion @coven/cron/deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"$schema": "https://raw.githubusercontent.com/denoland/deno/main/cli/schemas/config-file.v1.json",
"exports": "./mod.ts",
"name": "@coven/cron",
"version": "0.9.7"
"version": "0.9.8"
}
9 changes: 6 additions & 3 deletions @coven/cron/isRangeString.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { iterableToArray, map } from "@coven/iterables";
import { memoFunction } from "@coven/memo";
import { parseDecimal } from "@coven/parsers";
import type { Predicate } from "@coven/types";
import type { RangeString } from "./RangeString.ts";
import { rangeStringTest } from "./rangeStringTest.ts";
import { RANGE_EXPRESSION_SEPARATOR_TOKEN } from "./tokens.ts";

const mapParseDecimal = map(parseDecimal);

/**
* Predicate checking if given value is a cron string range
* ({@linkcode RangeString}).
Expand All @@ -15,9 +18,9 @@ import { RANGE_EXPRESSION_SEPARATOR_TOKEN } from "./tokens.ts";
export const isRangeString: Predicate<string, RangeString> = memoFunction(
(value): value is RangeString => {
if (rangeStringTest(value)) {
const [from, to] = value
.split(RANGE_EXPRESSION_SEPARATOR_TOKEN)
.map(parseDecimal) as [from: number, to: number];
const [from, to] = iterableToArray(
mapParseDecimal(value.split(RANGE_EXPRESSION_SEPARATOR_TOKEN)),
) as [from: number, to: number];

return from <= to;
} else {
Expand Down
2 changes: 1 addition & 1 deletion @coven/cron/isValidExpression.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { cronRegExp } from "./cronRegExp.ts";
/**
* Validates if a string is a cron expression.
*
* @see {CronString}
* @see {@linkcode CronString}
*/
export const isValidExpression = test(build("iu")(cronRegExp)) as Predicate<
string,
Expand Down
14 changes: 5 additions & 9 deletions @coven/cron/minutes.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { iteratorFunctionToIterableIterator } from "@coven/iterables";
import { forever, iteratorFunctionToIterableIterator } from "@coven/iterables";

const minute = Temporal.Duration.from("PT1M");

Expand All @@ -17,12 +17,8 @@ const minute = Temporal.Duration.from("PT1M");
export const minutes = (
plainDateTime: Temporal.PlainDateTime,
): IterableIterator<Temporal.PlainDateTime> =>
iteratorFunctionToIterableIterator(
function* (): Generator<Temporal.PlainDateTime> {
let currentDateTime = plainDateTime;
iteratorFunctionToIterableIterator(() => {
let currentDateTime = plainDateTime;

for (;;) {
yield (currentDateTime = currentDateTime.add(minute));
}
},
);
return forever(() => (currentDateTime = currentDateTime.add(minute)));
});
2 changes: 1 addition & 1 deletion @coven/cron/nextISODate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { nextISODates } from "./nextISODates.ts";
/**
* Get next ISO date string for the given date and the given cron expression.
*
* @example Getting the next ISO Date string corresponding to ghe given cron expression
* @example Getting the next ISO Date string corresponding to the given cron expression
* ```typescript
* nextISODate("1989-10-13T10:15:00.000Z")("* * * * *"); // "1989-10-13T10:16:00.000Z"
* ```
Expand Down
2 changes: 1 addition & 1 deletion @coven/cron/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export const parse: Unary<
);

return (
length(entries) === 0 ? undefined : (
length(entries) === 0n ? undefined : (
memo(entriesToObject(entries))
)) as Maybe<CronObject>;
});
17 changes: 11 additions & 6 deletions @coven/cron/stringify.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
import { EMPTY_OBJECT } from "@coven/constants";
import { join, map } from "@coven/iterables";
import { memoFunction } from "@coven/memo";
import type { Maybe, Unary } from "@coven/types";
import type { Maybe, Unary, ValueOf } from "@coven/types";
import type { CronObject } from "./CronObject.ts";
import type { CronString } from "./CronString.ts";
import { fieldNamesTuple } from "./fieldNamesTuple.ts";
import { isValidExpression } from "./isValidExpression.ts";
import { stringifyField } from "./stringifyField.ts";
import { ALL_TOKEN } from "./tokens.ts";

const spaceJoin = join(" ");

const mapStringifyField = (cron?: Partial<CronObject>) =>
map((name: ValueOf<typeof fieldNamesTuple>) =>
stringifyField(cron?.[name] ?? ALL_TOKEN),
);

/**
* Takes a cron object and returns a sting expression.
*
Expand All @@ -32,10 +39,8 @@ import { ALL_TOKEN } from "./tokens.ts";
export const stringify: Unary<
[cron: Maybe<Partial<CronObject>>],
Maybe<CronString>
> = memoFunction((cron = EMPTY_OBJECT) => {
const expression = fieldNamesTuple
.map((name) => stringifyField(cron[name] ?? ALL_TOKEN))
.join(" ");
> = memoFunction((cron) => {
const expression = spaceJoin(mapStringifyField(cron)(fieldNamesTuple));

return isValidExpression(expression) ? expression : undefined;
});
21 changes: 15 additions & 6 deletions @coven/cron/stringifyList.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,24 @@
import { join, map } from "@coven/iterables";
import { memoFunction } from "@coven/memo";
import type { Maybe, Unary } from "@coven/types";
import type { Maybe, Stringable, Unary } from "@coven/types";
import type { Field } from "./Field.ts";
import type { ListString } from "./ListString.ts";
import type { ValueOrRangeField } from "./ValueOrRangeField.ts";
import { isListField } from "./isListField.ts";
import { stringifyRange } from "./stringifyRange.ts";
import { LIST_EXPRESSION_SEPARATOR_TOKEN } from "./tokens.ts";

const stringifyRangeMap = map(
(item: ValueOrRangeField<number>) =>
stringifyRange(item) ?? `${item as number}`,
);

const joinList = join(LIST_EXPRESSION_SEPARATOR_TOKEN) as <
Item extends Stringable,
>(
iterable: Iterable<Item>,
) => ListString;

/**
* Turns cron list into a string.
*
Expand All @@ -23,9 +36,5 @@ export const stringifyList: Unary<
[field: Readonly<Field<number>>],
Maybe<ListString>
> = memoFunction((field) =>
isListField(field) ?
(field
.map((item) => stringifyRange(item) ?? `${item as number}`)
.join(LIST_EXPRESSION_SEPARATOR_TOKEN) as ListString)
: undefined,
isListField(field) ? joinList(stringifyRangeMap(field)) : undefined,
);
14 changes: 8 additions & 6 deletions @coven/cron/tests/parse.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// This file is huge, but is because we have to cover quite a lot
// deno-lint-ignore coven/max-lines
import { forEach } from "@coven/iterables";
import { memo } from "@coven/memo";
import type { ValueOf } from "@coven/types";
import { assertStrictEquals } from "@std/assert";
import type { CronString } from "../CronString.ts";
import { parse } from "../parse.ts";
Expand Down Expand Up @@ -300,16 +302,16 @@ Deno.test(
),
);

februaryBadDaysOfMonth.forEach((februaryBadDayOfMonth) =>
forEach((februaryBadDayOfMonth: ValueOf<typeof februaryBadDaysOfMonth>) =>
Deno.test(`* * ${februaryBadDayOfMonth} 2 * returns undefined`, () =>
assertStrictEquals(
parse(`* * ${februaryBadDayOfMonth} 2 *`),
undefined,
),
),
);
)(februaryBadDaysOfMonth);

februaryBadDaysOfMonth.forEach((februaryBadDayOfMonth) =>
forEach((februaryBadDayOfMonth: ValueOf<typeof februaryBadDaysOfMonth>) =>
Deno.test(
`* * ${februaryBadDayOfMonth} 2,3 * returns valid date because 3 is included`,
() =>
Expand All @@ -328,9 +330,9 @@ februaryBadDaysOfMonth.forEach((februaryBadDayOfMonth) =>
} as const),
),
),
);
)(februaryBadDaysOfMonth);

februaryBadDaysOfMonth.forEach((februaryBadDayOfMonth) =>
forEach((februaryBadDayOfMonth: ValueOf<typeof februaryBadDaysOfMonth>) =>
Deno.test(
`* * ${februaryBadDayOfMonth} 2-4 * returns valid date because 3 is included`,
() =>
Expand All @@ -348,4 +350,4 @@ februaryBadDaysOfMonth.forEach((februaryBadDayOfMonth) =>
} as const),
),
),
);
)(februaryBadDaysOfMonth);
2 changes: 1 addition & 1 deletion @coven/expression/deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"$schema": "https://raw.githubusercontent.com/denoland/deno/main/cli/schemas/config-file.v1.json",
"exports": "./mod.ts",
"name": "@coven/expression",
"version": "0.9.7"
"version": "0.9.8"
}
6 changes: 3 additions & 3 deletions @coven/iterables/async/count.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ import { length } from "./length.ts";
* @example
* ```typescript
* const countOdds = count((number: number) => number % 2 !== 1);
* countOdds([1, 2, 3, 4, 5]); // 3
* countOdds([0, 2, 4, 6, 8]); // 0
* countOdds([1, 2, 3, 4, 5]); // 3n
* countOdds([0, 2, 4, 6, 8]); // 0n
* ```
* @param predicate Predicate function for items to be counted.
* @returns Curried function with `predicate` in context.
*/
export const count = <Item>(
predicate: Filter<[item: Item]>,
): AsyncUnary<[iterable: AwaitableIterable<Item>], number> => {
): AsyncUnary<[iterable: AwaitableIterable<Item>], bigint> => {
const predicateFilter = filter(predicate);

return (iterable) => length(predicateFilter(iterable));
Expand Down
10 changes: 5 additions & 5 deletions @coven/iterables/async/drop.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { AwaitableIterable } from "@coven/types";
import type { AwaitableIterable, Numeric } from "@coven/types";
import { filter } from "./filter.ts";
import { iteratorFunctionToAsyncIterableIterator } from "./iteratorFunctionToAsyncIterableIterator.ts";

Expand All @@ -8,21 +8,21 @@ import { iteratorFunctionToAsyncIterableIterator } from "./iteratorFunctionToAsy
*
* @example
* ```typescript
* const drop2 = drop(2);
* const drop2 = drop(2n);
* drop2([1, 2, 3, 4, 5]); // [3, 4, 5]
* ```
* @param amount Amount of items to drop.
* @returns Curried function with `amount` in context.
*/
export const drop = (
amount: number,
amount: Numeric,
): (<Item>(
iterable: AwaitableIterable<Item>,
) => Readonly<AsyncIterableIterator<Awaited<Item>>>) => {
const amountFilter = <Item>(iterable: AwaitableIterable<Item>) => {
let count = -1;
let count = -1n;

return filter(() => (count += 1) >= amount)(
return filter(() => (count += 1n) >= amount)(
iterable,
) as AwaitableIterable<Item>;
};
Expand Down
1 change: 1 addition & 0 deletions @coven/iterables/async/entriesToObject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { reduce } from "./reduce.ts";
* ["number", 1]
* ]); // { foo: "bar", number: 1 }
* ```
* @param iterable `AsyncIterable` of entries to turn into an object.
* @returns Object constructed from entries.
*/
export const entriesToObject = reduce(
Expand Down
1 change: 1 addition & 0 deletions @coven/iterables/async/every.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export const every: {
async (iterable) => {
for await (const item of iterable) {
if (!predicate(item)) {
// deno-lint-ignore coven/no-early-return
return false;
}
}
Expand Down
3 changes: 2 additions & 1 deletion @coven/iterables/async/find.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ export const find: {
async (iterable): Promise<Maybe<Item>> => {
for await (const item of iterable) {
if (predicate(item)) {
return item as Maybe<Item>;
// deno-lint-ignore coven/no-early-return
return item;
}
}

Expand Down
1 change: 1 addition & 0 deletions @coven/iterables/async/initial.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export const initial = <Iterable extends AwaitableIterable>(
const iterator = getIterator(iterable);
let item = await iterator.next();

// deno-lint-ignore coven/no-for
for (; !item.done; ) {
const value = item.value;

Expand Down
9 changes: 6 additions & 3 deletions @coven/iterables/async/length.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@ import { always } from "@coven/utils";
import { reduce } from "./reduce.ts";

/**
* Get the length of an iterable or asynchronous iterable.
* Get the length of an iterable or asynchronous iterable (using `bigint` for
* really big iterables).
*
* @example
* ```typescript
* length([1, 2, 3]); // 3
* length([1, 2, 3]); // 3n
* ```
* @param iterable Iterable or asynchronous iterable to get the length from.
* @returns Promise with the length of the iterable.
*/
export const length: <Iterable extends AwaitableIterable>(
iterable: Iterable,
) => Promise<number> = reduce<unknown, number>(always((total) => total + 1))(0);
) => Promise<bigint> = reduce<unknown, bigint>(always((total) => total + 1n))(
0n,
);
Loading