/*
* 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 {YamlVisitor} from "./visitor";
import {RpcCodecs, RpcReceiveQueue, RpcSendQueue} from "../rpc";
import {Yaml} from "./tree";
import {updateIfChanged} from "../util";
class YamlSender extends YamlVisitor {
protected async preVisit(y: Yaml, q: RpcSendQueue): Promise {
await q.getAndSend(y, y2 => y2.id);
await q.getAndSend(y, y2 => y2.prefix);
await q.getAndSend(y, y2 => y2.markers);
return y;
}
protected async visitDocuments(documents: Yaml.Documents, q: RpcSendQueue): Promise {
await q.getAndSend(documents, d => d.sourcePath);
await q.getAndSend(documents, d => d.charsetName);
await q.getAndSend(documents, d => d.charsetBomMarked);
await q.getAndSend(documents, d => d.checksum);
await q.getAndSend(documents, d => d.fileAttributes);
await q.getAndSendList(documents, d => d.documents, doc => doc.id,
async doc => await this.visit(doc, q));
await q.getAndSend(documents, d => d.suffix);
return documents;
}
protected async visitDocument(document: Yaml.Document, q: RpcSendQueue): Promise {
await q.getAndSendList(document, d => d.directives, dir => dir.id,
async dir => await this.visit(dir, q));
await q.getAndSend(document, d => d.explicit);
await q.getAndSend(document, d => d.block, async b => await this.visit(b, q));
await q.getAndSend(document, d => d.end, async e => await this.visit(e, q));
return document;
}
protected async visitDirective(directive: Yaml.Directive, q: RpcSendQueue): Promise {
await q.getAndSend(directive, d => d.value);
await q.getAndSend(directive, d => d.suffix);
return directive;
}
protected async visitDocumentEnd(end: Yaml.DocumentEnd, q: RpcSendQueue): Promise {
await q.getAndSend(end, e => e.explicit);
return end;
}
protected async visitMapping(mapping: Yaml.Mapping, q: RpcSendQueue): Promise {
await q.getAndSend(mapping, m => m.openingBracePrefix);
await q.getAndSendList(mapping, m => m.entries, e => e.id,
async e => await this.visit(e, q));
await q.getAndSend(mapping, m => m.closingBracePrefix);
await q.getAndSend(mapping, m => m.anchor, async a => a ? await this.visit(a, q) : undefined);
await q.getAndSend(mapping, m => m.tag, async t => t ? await this.visit(t, q) : undefined);
return mapping;
}
protected async visitMappingEntry(entry: Yaml.MappingEntry, q: RpcSendQueue): Promise {
await q.getAndSend(entry, e => e.key, async k => await this.visit(k, q));
await q.getAndSend(entry, e => e.beforeMappingValueIndicator);
await q.getAndSend(entry, e => e.value, async v => await this.visit(v, q));
return entry;
}
protected async visitScalar(scalar: Yaml.Scalar, q: RpcSendQueue): Promise {
await q.getAndSend(scalar, s => s.style);
await q.getAndSend(scalar, s => s.anchor, async a => a ? await this.visit(a, q) : undefined);
await q.getAndSend(scalar, s => s.tag, async t => t ? await this.visit(t, q) : undefined);
await q.getAndSend(scalar, s => s.value);
return scalar;
}
protected async visitSequence(sequence: Yaml.Sequence, q: RpcSendQueue): Promise {
await q.getAndSend(sequence, s => s.openingBracketPrefix);
await q.getAndSendList(sequence, s => s.entries, e => e.id,
async e => await this.visit(e, q));
await q.getAndSend(sequence, s => s.closingBracketPrefix);
await q.getAndSend(sequence, s => s.anchor, async a => a ? await this.visit(a, q) : undefined);
await q.getAndSend(sequence, s => s.tag, async t => t ? await this.visit(t, q) : undefined);
return sequence;
}
protected async visitSequenceEntry(entry: Yaml.SequenceEntry, q: RpcSendQueue): Promise {
await q.getAndSend(entry, e => e.block, async b => await this.visit(b, q));
await q.getAndSend(entry, e => e.dash);
await q.getAndSend(entry, e => e.trailingCommaPrefix);
return entry;
}
protected async visitAnchor(anchor: Yaml.Anchor, q: RpcSendQueue): Promise {
await q.getAndSend(anchor, a => a.postfix);
await q.getAndSend(anchor, a => a.key);
return anchor;
}
protected async visitAlias(alias: Yaml.Alias, q: RpcSendQueue): Promise {
await q.getAndSend(alias, a => a.anchor, async a => await this.visit(a, q));
return alias;
}
protected async visitTag(tag: Yaml.Tag, q: RpcSendQueue): Promise {
await q.getAndSend(tag, t => t.name);
await q.getAndSend(tag, t => t.suffix);
await q.getAndSend(tag, t => t.tagKind);
return tag;
}
}
class YamlReceiver extends YamlVisitor {
protected async preVisit(y: Yaml, q: RpcReceiveQueue): Promise {
return updateIfChanged(y, {
id: await q.receive(y.id),
prefix: await q.receive(y.prefix),
markers: await q.receive(y.markers),
});
}
protected async visitDocuments(documents: Yaml.Documents, q: RpcReceiveQueue): Promise {
return updateIfChanged(documents, {
sourcePath: await q.receive(documents.sourcePath),
charsetName: await q.receive(documents.charsetName),
charsetBomMarked: await q.receive(documents.charsetBomMarked),
checksum: await q.receive(documents.checksum),
fileAttributes: await q.receive(documents.fileAttributes),
documents: await q.receiveListDefined(documents.documents,
async doc => await this.visit(doc, q) as Yaml.Document),
suffix: await q.receive(documents.suffix),
});
}
protected async visitDocument(document: Yaml.Document, q: RpcReceiveQueue): Promise {
return updateIfChanged(document, {
directives: await q.receiveListDefined(document.directives,
async dir => await this.visit(dir, q) as Yaml.Directive),
explicit: await q.receive(document.explicit),
block: await q.receive(document.block,
async b => await this.visit(b, q) as Yaml.Block),
end: await q.receive(document.end,
async e => await this.visit(e, q) as Yaml.DocumentEnd),
});
}
protected async visitDirective(directive: Yaml.Directive, q: RpcReceiveQueue): Promise {
return updateIfChanged(directive, {
value: await q.receive(directive.value),
suffix: await q.receive(directive.suffix),
});
}
protected async visitDocumentEnd(end: Yaml.DocumentEnd, q: RpcReceiveQueue): Promise {
return updateIfChanged(end, {
explicit: await q.receive(end.explicit),
});
}
protected async visitMapping(mapping: Yaml.Mapping, q: RpcReceiveQueue): Promise {
return updateIfChanged(mapping, {
openingBracePrefix: await q.receive(mapping.openingBracePrefix),
entries: await q.receiveListDefined(mapping.entries,
async e => await this.visit(e, q) as Yaml.MappingEntry),
closingBracePrefix: await q.receive(mapping.closingBracePrefix),
anchor: await q.receive(mapping.anchor,
async a => a ? await this.visit(a, q) as Yaml.Anchor : undefined),
tag: await q.receive(mapping.tag,
async t => t ? await this.visit(t, q) as Yaml.Tag : undefined),
});
}
protected async visitMappingEntry(entry: Yaml.MappingEntry, q: RpcReceiveQueue): Promise {
return updateIfChanged(entry, {
key: await q.receive(entry.key,
async k => await this.visit(k, q) as Yaml.YamlKey),
beforeMappingValueIndicator: await q.receive(entry.beforeMappingValueIndicator),
value: await q.receive(entry.value,
async v => await this.visit(v, q) as Yaml.Block),
});
}
protected async visitScalar(scalar: Yaml.Scalar, q: RpcReceiveQueue): Promise {
return updateIfChanged(scalar, {
style: await q.receive(scalar.style),
anchor: await q.receive(scalar.anchor,
async a => a ? await this.visit(a, q) as Yaml.Anchor : undefined),
tag: await q.receive(scalar.tag,
async t => t ? await this.visit(t, q) as Yaml.Tag : undefined),
value: await q.receive(scalar.value),
});
}
protected async visitSequence(sequence: Yaml.Sequence, q: RpcReceiveQueue): Promise {
return updateIfChanged(sequence, {
openingBracketPrefix: await q.receive(sequence.openingBracketPrefix),
entries: await q.receiveListDefined(sequence.entries,
async e => await this.visit(e, q) as Yaml.SequenceEntry),
closingBracketPrefix: await q.receive(sequence.closingBracketPrefix),
anchor: await q.receive(sequence.anchor,
async a => a ? await this.visit(a, q) as Yaml.Anchor : undefined),
tag: await q.receive(sequence.tag,
async t => t ? await this.visit(t, q) as Yaml.Tag : undefined),
});
}
protected async visitSequenceEntry(entry: Yaml.SequenceEntry, q: RpcReceiveQueue): Promise {
return updateIfChanged(entry, {
block: await q.receive(entry.block,
async b => await this.visit(b, q) as Yaml.Block),
dash: await q.receive(entry.dash),
trailingCommaPrefix: await q.receive(entry.trailingCommaPrefix),
});
}
protected async visitAnchor(anchor: Yaml.Anchor, q: RpcReceiveQueue): Promise {
return updateIfChanged(anchor, {
postfix: await q.receive(anchor.postfix),
key: await q.receive(anchor.key),
});
}
protected async visitAlias(alias: Yaml.Alias, q: RpcReceiveQueue): Promise {
return updateIfChanged(alias, {
anchor: await q.receive(alias.anchor,
async a => await this.visit(a, q) as Yaml.Anchor),
});
}
protected async visitTag(tag: Yaml.Tag, q: RpcReceiveQueue): Promise {
return updateIfChanged(tag, {
name: await q.receive(tag.name),
suffix: await q.receive(tag.suffix),
tagKind: await q.receive(tag.tagKind),
});
}
}
const receiver = new YamlReceiver();
const sender = new YamlSender();
// Register codec for all YAML AST node types
for (const kind of Object.values(Yaml.Kind)) {
RpcCodecs.registerCodec(kind as string, {
async rpcReceive(before: Yaml, q: RpcReceiveQueue): Promise {
return (await receiver.visit(before, q))!;
},
async rpcSend(after: Yaml, q: RpcSendQueue): Promise {
await sender.visit(after, q);
}
}, Yaml.Kind.Documents);
}