add generator support

This commit is contained in:
Anton Nesterov 2024-10-17 05:24:06 +02:00
parent 9e3b6a59cb
commit fd83139ed5
No known key found for this signature in database
GPG key ID: 59121E8AE2851FB5
4 changed files with 176 additions and 8 deletions

View file

@ -66,5 +66,41 @@ if (parseStatus.Of(SyntaxError)) {
}
```
## Handle functions:
Grip can also handle functions:
```javascript
const [result, status] = grip(() => "ok");
// result = "ok"
const [result1, status1] = grip(() => {
if (1) throw new Error("error")
});
// result1 = null
// status.Of(Error) = true
```
## Handle generators
Generators can be handled using the `Iter()` method:
```javascript
const res = grip(async function* () {
for (let i = 0; i < 3; i++) {
if (i == 2) throw new Error("2");
yield i;
}
});
for await (let [value, status] of res.Iter()) {
if (status.Of(Error)) {
// handle error properly
break;
}
// typeof value === "number"
console.log(value)
}
```
## License
MIT

48
dist/grip.js vendored
View file

@ -67,13 +67,57 @@ class Result extends Array {
Fail() {
return this[1].Fail();
}
Of(impl) {
return this[1].Of(impl);
Of(cls) {
return this[1].Of(cls);
}
Iter() {
const value = this.value;
const that = this;
if (typeof value !== "object" && !(typeof value[Symbol.iterator] === "function" || typeof value[Symbol.asyncIterator] === "function")) {
return {
async* [Symbol.asyncIterator]() {
yield new Result(that.value, that.status);
},
*[Symbol.iterator]() {
yield new Result(that.value, that.status);
}
};
}
return {
async* [Symbol.asyncIterator]() {
yield* asyncIterator(value);
},
*[Symbol.iterator]() {
yield* iterator(value);
}
};
}
}
var promise = (result) => {
return result.then((res) => new Result(res, new Ok)).catch((err) => new Result(null, Err.fromCatch(err)));
};
var iterator = function* (iter) {
try {
let data = iter.next();
while (!data.done) {
yield new Result(data.value, new Ok);
data = iter.next();
}
} catch (e) {
yield new Result(null, Err.fromCatch(e));
}
};
var asyncIterator = async function* (iter) {
try {
let data = await iter.next();
while (!data.done) {
yield new Result(data.value, new Ok);
data = await iter.next();
}
} catch (e) {
yield new Result(null, Err.fromCatch(e));
}
};
export {
grip,
Ok,

View file

@ -63,3 +63,35 @@ test("fetch json", async () => {
expect(jsonStatus.Of(SyntaxError)).toBe(true);
expect(json === null).toBe(true);
});
test("function*", async () => {
const res = grip(function* () {
for (let i = 0; i < 3; i++) {
if (i == 2) throw new Error("2");
yield i;
}
});
expect(res.Ok()).toBe(true);
for (let [value, status] of res.Iter()) {
if (status.Of(Error)) {
break;
}
expect(value).toBeTypeOf("number");
}
});
test("async function*", async () => {
const res = grip(async function* () {
for (let i = 0; i < 3; i++) {
if (i == 2) throw new Error("2");
yield i;
}
});
expect(res.Ok()).toBe(true);
for await (let [value, status] of res.Iter()) {
if (status.Of(Error)) {
break;
}
expect(value).toBeTypeOf("number");
}
});

68
grip.ts
View file

@ -69,19 +69,51 @@ class Result<T> extends Array<T | Status> implements IResult<T> {
Fail() {
return (this[1] as Status).Fail();
}
Of(impl: any) {
return (this[1] as Status).Of(impl);
Of(cls: any) {
return (this[1] as Status).Of(cls);
}
Iter() {
const value = this.value;
const that = this;
if (
typeof value !== "object" &&
!(
typeof (value as any)[Symbol.iterator] === "function" ||
typeof (value as any)[Symbol.asyncIterator] === "function"
)
) {
return {
async *[Symbol.asyncIterator]() {
yield new Result(that.value, that.status) as SafeResult<T>;
},
*[Symbol.iterator]() {
yield new Result(that.value, that.status) as SafeResult<T>;
},
};
}
return {
async *[Symbol.asyncIterator]() {
yield* asyncIterator<T>(value as AsyncGenerator);
},
*[Symbol.iterator]() {
yield* iterator<T>(value as Generator);
},
};
}
}
type Unwrap<T> =
T extends Promise<infer U>
T extends AsyncGenerator<infer U>
? U
: T extends (...args: any) => Promise<infer U>
: T extends Generator<infer U>
? U
: T extends (...args: any) => infer U
: T extends Promise<infer U>
? U
: T;
: T extends (...args: any) => Promise<infer U>
? U
: T extends (...args: any) => infer U
? U
: T;
export type SafeResult<T> =
T extends Promise<any>
@ -110,3 +142,27 @@ const promise = <T>(result: Promise<T>) => {
.then((res) => new Result(res, new Ok()))
.catch((err) => new Result(null, Err.fromCatch(err))) as Promise<Result<T>>;
};
const iterator = function* <T>(iter: Generator) {
try {
let data = iter.next();
while (!data.done) {
yield new Result<T>(data.value as T, new Ok()) as SafeResult<T>;
data = iter.next();
}
} catch (e) {
yield new Result<T>(null as T, Err.fromCatch(e)) as SafeResult<T>;
}
};
const asyncIterator = async function* <T>(iter: AsyncGenerator) {
try {
let data = await iter.next();
while (!data.done) {
yield new Result<T>(data.value as T, new Ok()) as SafeResult<T>;
data = await iter.next();
}
} catch (e) {
yield new Result<T>(null as T, Err.fromCatch(e)) as SafeResult<T>;
}
};