Skip to content

Commit

Permalink
Document error handling behavior of JsonHigh.
Browse files Browse the repository at this point in the history
Add a helper to check for errors (isError).
Add test for error handling.
  • Loading branch information
djedr committed Aug 27, 2024
1 parent c0d6e0c commit 75fd6cb
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 6 deletions.
1 change: 1 addition & 0 deletions JsonLow.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export interface JsonUnexpectedEndFeedback {
export type JsonStandardFeedback = JsonErrorFeedback | JsonUnexpectedFeedback;
export type JsonStandardEnd = JsonErrorFeedback | JsonUnexpectedEndFeedback;
export declare const error: (message: string) => JsonErrorFeedback
export declare const isError: (message: unknown) => boolean
export declare const unexpected: (code: number, context: string, expected: Array<string | [startChar: string, endChar: string]>) => JsonUnexpectedFeedback
export declare const unexpectedEnd: (context?: string, expected?: Array<string | [startChar: string, endChar: string]>) => JsonUnexpectedEndFeedback
export declare const isZeroNine: (code: number) => boolean
Expand Down
3 changes: 3 additions & 0 deletions JsonLow.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ export const error = (message) => {
message,
}
}
export const isError = (value) => {
return value !== null && typeof value === 'object' && value.type === JsonFeedbackType.error
}
export const unexpected = (code, context, expected) => {
return {
type: JsonFeedbackType.error,
Expand Down
49 changes: 43 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,12 +154,6 @@ See [**JsonHigh.d.ts**](JsonHigh.d.ts) for type information and [Quickstart](#qu
* `chunk` which accepts a JSON chunk to parse. It returns the stream object for chaining.
* `end` with no arguments which signals that the current JSON document is finished. If there is no error, it calls the corresponding `end` event handler, passing its return value to the caller.

### Error handling

If there is an error when parsing a `chunk`, an exception is thrown.

If there is an error at the `end`, that error is returned to the caller. The user-provided `end` event handler is not called.

### Events

There are 4 event handlers without arguments which indicate start and end of structures:
Expand All @@ -176,6 +170,49 @@ And 2 event handlers with one argument which capture primitives:

Finally, there is the argumentless `end` event handler which is called by the `end` method of the stream to confirm that the parsed JSON document is complete and valid.

Note that an event handler won't be called if there is an error in the parsed JSON, see [error handling](#error-handling).

### Error handling

If there is an error when parsing a `chunk`, an `Error` is thrown, containing a serialized JSON object with details in the error message.

If there is an error at the `end`, that error is returned to the caller. The user-provided `end` event handler is not called, so it should not contain any [cleanup](#cleanup) code.

### Cleanup

To run cleanup code at the end of parsing a document regardless of whether there was an error or not, **don't put that code in the end handler**. Instead put it after `.end()`, like so:

```js
// ...
stream.end()
cleanup()
```

If you want to also handle an error, you can use the `isError` helper:

```js
import {isError} from '@xtao-org/jsonhilo'

// ...

const ret = stream.end()
if (isError(ret)) { handle(ret) } // handle error
cleanup()
```

If your error handler can throw, you can use `try-catch-finally`:

```js
import {isError} from '@xtao-org/jsonhilo'

// ...

const ret = stream.end()
try { if (isError(ret)) { handle(ret) } }
catch (e) { /* optional */ }
finally { cleanup() }
```

## Fast

Achieving optimal performance without sacrificing simplicity and correctness was a design goal of JsonHilo. This goal was realized and for applications without extreme performance requirements JsonHilo should be more than fast enough.
Expand Down
30 changes: 30 additions & 0 deletions test/error.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import {isError, JsonHigh} from '../mod.js'

Deno.test('error handling', async () => {
// based on a user-provided example from https://github.com/xtao-org/jsonhilo/issues/6
const handlers = {
end: () => {
throw Error('Expected end handler not to be called!')
},
}
const stream = JsonHigh(handlers)
const writable = new WritableStream({
write: (chunk) => {
const decoded = new TextDecoder().decode(chunk)
stream.chunk(decoded)
},
close: () => {
const ret = stream.end()
if (isError(ret) === false) {
throw Error('Expected error to be returned by stream.end()!')
}
},
})
const readable = new ReadableStream({
start(controller) {
controller.enqueue(new TextEncoder().encode('{"a":2,"b":3'))
controller.close()
},
})
await readable.pipeTo(writable)
})

0 comments on commit 75fd6cb

Please sign in to comment.