/*
* Copyright 2025 the original author or authors.
*
* Licensed under the Moderne Source Available License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://docs.moderne.io/licensing/moderne-source-available-license
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {mapAsync, updateIfChanged} from "../util";
import {TreeVisitor, ValidRecipeReturnType} from "../visitor";
import {SourceFile} from "../tree";
import {isJson, Json} from "./tree";
import {create, Draft} from "mutative";
export class JsonVisitor
extends TreeVisitor {
async isAcceptable(sourceFile: SourceFile): Promise {
return isJson(sourceFile);
}
protected async visitArray(array: Json.Array, p: P): Promise {
const updates: any = {
prefix: await this.visitSpace(array.prefix, p),
markers: await this.visitMarkers(array.markers, p),
values: await mapAsync(array.values, value => this.visitRightPadded(value, p))
};
return updateIfChanged(array, updates);
}
protected async visitDocument(document: Json.Document, p: P): Promise {
const updates: any = {
prefix: await this.visitSpace(document.prefix, p),
markers: await this.visitMarkers(document.markers, p),
value: await this.visitDefined(document.value, p),
eof: await this.visitSpace(document.eof, p)
};
return updateIfChanged(document, updates);
}
protected async visitEmpty(empty: Json.Empty, p: P): Promise {
const updates: any = {
prefix: await this.visitSpace(empty.prefix, p),
markers: await this.visitMarkers(empty.markers, p)
};
return updateIfChanged(empty, updates);
}
protected async visitIdentifier(identifier: Json.Identifier, p: P): Promise {
const updates: any = {
prefix: await this.visitSpace(identifier.prefix, p),
markers: await this.visitMarkers(identifier.markers, p)
};
return updateIfChanged(identifier, updates);
}
protected async visitLiteral(literal: Json.Literal, p: P): Promise {
const updates: any = {
prefix: await this.visitSpace(literal.prefix, p),
markers: await this.visitMarkers(literal.markers, p)
};
return updateIfChanged(literal, updates);
}
protected async visitMember(member: Json.Member, p: P): Promise {
const updates: any = {
prefix: await this.visitSpace(member.prefix, p),
markers: await this.visitMarkers(member.markers, p),
key: (await this.visitRightPadded(member.key, p))!,
value: await this.visitDefined(member.value, p)
};
return updateIfChanged(member, updates);
}
protected async visitObject(jsonObject: Json.Object, p: P): Promise {
const updates: any = {
prefix: await this.visitSpace(jsonObject.prefix, p),
markers: await this.visitMarkers(jsonObject.markers, p),
members: await mapAsync(jsonObject.members, member => this.visitRightPadded(member, p))
};
return updateIfChanged(jsonObject, updates);
}
public async visitRightPadded(right: Json.RightPadded, p: P):
Promise | undefined> {
const updates: any = {
element: await this.visitDefined(right.element, p),
after: await this.visitSpace(right.after, p)
};
return updateIfChanged(right, updates);
}
public async visitSpace(space: Json.Space, p: P): Promise {
return space;
}
protected async produceJson(
before: Json | undefined,
p: P,
recipe?: (draft: Draft) =>
ValidRecipeReturnType> |
PromiseLike>>
): Promise {
if (before === undefined) {
return undefined;
}
const [draft, finishDraft] = create(before as J);
(draft as Draft).prefix = await this.visitSpace(before!.prefix, p);
(draft as Draft).markers = await this.visitMarkers(before!.markers, p);
if (recipe) {
await recipe(draft);
}
return finishDraft() as J;
}
protected accept(t: Json, p: P): Promise {
switch (t.kind) {
case Json.Kind.Array:
return this.visitArray(t as Json.Array, p);
case Json.Kind.Document:
return this.visitDocument(t as Json.Document, p);
case Json.Kind.Empty:
return this.visitEmpty(t as Json.Empty, p);
case Json.Kind.Identifier:
return this.visitIdentifier(t as Json.Identifier, p);
case Json.Kind.Literal:
return this.visitLiteral(t as Json.Literal, p);
case Json.Kind.Member:
return this.visitMember(t as Json.Member, p);
case Json.Kind.Object:
return this.visitObject(t as Json.Object, p);
default:
throw new Error(`Unexpected JSON kind ${t.kind}`);
}
}
}