Skip to content

Commit fa2a9f6

Browse files
committed
{feature} make DeserializationContext#run atomic, add "try" variant
1 parent 63a428a commit fa2a9f6

File tree

2 files changed

+59
-20
lines changed

2 files changed

+59
-20
lines changed

src/dessert/deserialization/deserialize.js

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -868,27 +868,43 @@ export function deserializeFromState(state, deserializer, ...parameters) {
868868
}
869869
);
870870

871-
/** @type {t.DeserializationContext["run"]} */
872-
const run = (deserializer, ...params) => {
873-
if (!isDeserializerFromContext(deserializer)) {
874-
throw new TypeError(
875-
"Expected a DeserializerFromContext, got a DeserializerFromNode",
876-
);
871+
const run = /** @type {t.Run} */ (
872+
(deserializer, ...params) => {
873+
if (!isDeserializerFromContext(deserializer)) {
874+
throw new TypeError(
875+
"Expected a DeserializerFromContext, got a DeserializerFromNode",
876+
);
877+
}
878+
879+
const stateBefore = state.clone();
880+
try {
881+
return "deserialize" in deserializer ?
882+
deserializer.deserialize(context, ...params)
883+
: deserializer(context, ...params);
884+
} catch (e) {
885+
state.apply(stateBefore);
886+
887+
if (e instanceof KdlDeserializeError) {
888+
throw e;
889+
} else {
890+
throw new KdlDeserializeError(`Deserializer failed: ${String(e)}`, {
891+
location: state.node,
892+
cause: e,
893+
});
894+
}
895+
}
877896
}
897+
);
878898

899+
run.try = (deserializer, ...params) => {
879900
try {
880-
return "deserialize" in deserializer ?
881-
deserializer.deserialize(context, ...params)
882-
: deserializer(context, ...params);
901+
return run(deserializer, ...params);
883902
} catch (e) {
884903
if (e instanceof KdlDeserializeError) {
885-
throw e;
886-
} else {
887-
throw new KdlDeserializeError(`Deserializer failed: ${String(e)}`, {
888-
location: state.node,
889-
cause: e,
890-
});
904+
return null;
891905
}
906+
907+
throw e;
892908
}
893909
};
894910

src/dessert/deserialization/types.d.ts

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import {JsonObject, JsonValue} from "../../json.js";
22
import {Node, Primitive} from "../../index.js";
33

4+
import {KdlDeserializeError} from "./error.js";
5+
46
/**
57
* String representation of the {@link Primitive} types
68
*/
@@ -408,6 +410,30 @@ export interface Json {
408410
required<T extends [JsonType, ...JsonType[]]>(...types: T): JsonTypeOf<T>;
409411
}
410412

413+
export interface Run {
414+
/**
415+
* Run the given deserializer
416+
*
417+
* The deserializer is run atomically, i.e. if the deserializer fails
418+
* then the context is reset to before the deserializer ran.
419+
*/
420+
<T, P extends unknown[]>(
421+
deserializer: DeserializerFromContext<T, P>,
422+
...parameters: P
423+
): T;
424+
425+
/**
426+
* Run the given deserializer, returning null if the deserializer fails with a {@link KdlDeserializeError}
427+
*
428+
* The deserializer is run atomically, i.e. if the deserializer fails
429+
* then the context is reset to before the deserializer ran.
430+
*/
431+
try<T, P extends unknown[]>(
432+
deserializer: DeserializerFromContext<T, P>,
433+
...parameters: P
434+
): T | null;
435+
}
436+
411437
/**
412438
* Wrapper around a {@link Node} to help deserializing a single {@link Node} into a value
413439
*/
@@ -446,10 +472,7 @@ export interface DeserializationContext {
446472
readonly json: Json;
447473

448474
/**
449-
* Run the given deserializer
475+
* Helper for running other deserializers.
450476
*/
451-
readonly run: <T, P extends unknown[]>(
452-
deserializer: DeserializerFromContext<T, P>,
453-
...parameters: P
454-
) => T;
477+
readonly run: Run;
455478
}

0 commit comments

Comments
 (0)