rustledger-wasm 0.16.4

Beancount WebAssembly bindings for JavaScript/TypeScript
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
# AUTO-GENERATED -- DO NOT EDIT.
#
# This file is regenerated by `scripts/regen-bindings.sh` from the
# JSON Schema at `crates/rustledger-wasm/bindings/index.schema.json`,
# which in turn comes from the Rust DTOs in
# `crates/rustledger-wasm/src/types.rs`. CI fails if this file drifts.
# See ADR-0004 (#1218 + #1232 for Phase 3) for the design.

from __future__ import annotations

from typing import Literal

from pydantic import BaseModel, Field, RootModel



class AmountValue(BaseModel):
    """
    Amount value for serialization.
    """

    currency: str = Field(..., description="The currency.")
    number: str = Field(..., description="The number as a string.")


class CellValue1(BaseModel):
    """
    Amount with number and currency.
    """

    currency: str
    number: str


class CompletionJson(BaseModel):
    """
    BQL completion suggestion for WASM.
    """

    category: str = Field(
        ..., description="Category: keyword, function, column, operator, literal."
    )
    description: str | None = Field(
        None, description="Optional description/documentation."
    )
    text: str = Field(..., description="The completion text to insert.")


class CompletionKind(RootModel[str]):
    root: str = Field(..., description="The kind of a completion item.")


class CompletionResultJson(BaseModel):
    """
    Result of BQL completion request.
    """

    completions: list[CompletionJson] = Field(..., description="List of completions.")
    context: str = Field(..., description="Current context for debugging.")


class CostNumberJson1(BaseModel):
    """
    Per-unit cost (e.g., `{100 USD}`).
    """

    kind: Literal["per_unit"]
    value: str = Field(..., description="Per-unit value.")


class CostNumberJson2(BaseModel):
    """
    Total cost as written (e.g., `{{1000 USD}}`), pre-booking.
    """

    kind: Literal["total"]
    value: str = Field(..., description="Total value.")


class CostNumberJson3(BaseModel):
    """
    Post-booking derived per-unit with preserved source total.
    """

    kind: Literal["per_unit_from_total"]
    per_unit: str = Field(..., description="Derived per-unit.")
    total: str = Field(..., description="Source total.")


class CostNumberJson(RootModel[CostNumberJson1 | CostNumberJson2 | CostNumberJson3]):
    root: CostNumberJson1 | CostNumberJson2 | CostNumberJson3 = Field(
        ...,
        description="Wire-format of the numeric component of a [`PostingCostJson`].\n\nMirrors `rustledger_core::CostNumber` on the wire so JS consumers\nsee the same mutual exclusion the host enforces. Use the `kind`\nfield as the discriminator.",
    )


class CostValue(BaseModel):
    """
    Cost value for serialization.
    """

    currency: str = Field(..., description="Cost currency.")
    date: str | None = Field(None, description="Acquisition date.")
    label: str | None = Field(None, description="Lot label.")
    number: str = Field(..., description="Cost per unit.")


class EditorCompletion(BaseModel):
    """
    A completion item for Beancount source editing.
    """

    detail: str | None = Field(
        None, description="A human-readable string with additional information."
    )
    insert_text: str | None = Field(
        None, description="The text to insert when this completion is selected."
    )
    kind: CompletionKind = Field(..., description="The kind of completion item.")
    label: str = Field(..., description="The label to display in the completion list.")


class EditorCompletionResult(BaseModel):
    """
    Result of a completion request.
    """

    completions: list[EditorCompletion] = Field(..., description="The completions.")
    context: str = Field(..., description="The detected context.")


class EditorLocation(BaseModel):
    """
    A location in the document.
    """

    character: int = Field(..., description="Character offset (0-based).", ge=0)
    line: int = Field(..., description="Line number (0-based).", ge=0)


class EditorRange(BaseModel):
    """
    A range in the document.
    """

    end_character: int = Field(..., description="End character (0-based).", ge=0)
    end_line: int = Field(..., description="End line (0-based).", ge=0)
    start_character: int = Field(..., description="Start character (0-based).", ge=0)
    start_line: int = Field(..., description="Start line (0-based).", ge=0)


class LedgerOptions(BaseModel):
    """
    Ledger options.
    """

    operating_currencies: list[str] = Field(..., description="Operating currencies.")
    title: str | None = Field(
        ...,
        description="Ledger title. Emitted as JSON `null` when no title is set\n(no `skip_serializing_if`; field is always present on the\nwire). TS: `string | null`, not `title?`. The required-and-\nnullable wire contract is enforced via the `schemars(extend)`\non the struct itself; see `ParseResult` for the rationale.",
    )


class MetaValueJson1(BaseModel):
    """
    Amount values (`{number, currency}`) — the only structured
    shape that survives the round-trip. Same `{number, currency}`
    envelope as [`AmountValue`] so JS consumers can branch on
    shape without a discriminator tag.

    **Deserialize note**: serde's untagged-enum matcher accepts
    extra fields in a JSON object (`#[serde(deny_unknown_fields)]`
    can't be applied per-variant on an untagged enum without
    breaking the wider match). A JS client sending
    `{number: "100", currency: "USD", extra: "x"}` deserializes as
    `Amount { number: "100", currency: "USD" }` with `extra`
    silently dropped. Output-side consumers (the production path)
    are unaffected; treat `Deserialize` here as best-effort and
    validate at the host boundary if you need stricter checks.
    """

    currency: str = Field(..., description="The currency code.")
    number: str = Field(
        ..., description="The decimal quantity, stringified for precision."
    )


class MetaValueJson(RootModel[str | bool | MetaValueJson1 | None]):
    root: str | bool | MetaValueJson1 | None = Field(
        ...,
        description="Metadata-value wire format for WASM consumers.\n\n**JSON output is byte-equivalent to FFI-WASI's\n`meta_value_to_json`** — JS clients writing portable code see\nidentical metadata values from both bindings. The Rust-side\ntypes are independent though: FFI-WASI emits\n`serde_json::Value` (untyped), this crate emits a typed enum.\nUnifying the source-of-truth is tracked by issue #1200 item 2.\n\nThe host's [`rustledger_core::MetaValue`] is richer than the wire\ntype — `Account`/`Currency`/`Tag`/`Link`/`Date`/`Number` all\nflatten to JSON strings here, matching FFI-WASI behavior. JS\nconsumers that need the strong type info should query the host\nvia a typed API; this enum is the lossy-but-portable view.\n\nUntagged on the wire: `\"hello\"` serializes as a string,\n`true` as a boolean, `null` as null, and an [`AmountValue`]\n`{number,currency}` as a plain object. The TypeScript union is\n`Record<string, string | boolean | {number, currency} | null>` —\nno raw JSON number arm because `MetaValue::Number` (`Decimal`)\nstringifies to preserve precision. Issue #1168 proposed\n`string | number | boolean | null`; we substitute the\n`{number,currency}` shape for `number` so cost-bearing metadata\nround-trips cleanly and so JS numeric literals don't silently\nalias into the wire (see the `meta_value_json_rejects_raw_json_number`\ntest).",
    )


class PluginInfo(BaseModel):
    """
    Plugin information.
    """

    description: str = Field(..., description="Plugin description.")
    name: str = Field(..., description="Plugin name.")


class PositionValue(BaseModel):
    """
    Position value for serialization.
    """

    units: AmountValue = Field(..., description="The units.")


class PostingCostJson(BaseModel):
    """
    A posting cost in JSON-serializable form.
    """

    currency: str | None = Field(None, description="Cost currency.")
    date: str | None = Field(None, description="Acquisition date.")
    label: str | None = Field(None, description="Lot label.")
    number: CostNumberJson | None = Field(
        None, description="Cost number (per-unit, total, or post-booking pair)."
    )


class PostingJson(BaseModel):
    """
    A posting in JSON-serializable form.
    """

    account: str = Field(..., description="Account name.")
    cost: PostingCostJson | None = Field(None, description="Cost specification.")
    flag: str | None = Field(
        None,
        description='Posting-level flag (e.g., `"!"` for pending). Mirrors\n`rustledger_core::Posting::flag`.',
    )
    meta: dict[str, MetaValueJson] | None = Field(
        None,
        description="Posting-level metadata (issue #1168). Empty when the posting\nhas no explicit metadata.",
    )
    price: AmountValue | None = Field(None, description="Price annotation.")
    units: AmountValue | None = Field(None, description="Units (amount).")


class ReferenceKind(RootModel[str]):
    root: str = Field(..., description="The kind of symbol being referenced.")


class Severity(RootModel[str]):
    root: str = Field(..., description="Error severity level.")


class SymbolKind(RootModel[str]):
    root: str = Field(..., description="The kind of a symbol.")


class TypedValueJson(BaseModel):
    """
    Tagged-union wire-format for a [`rustledger_core::MetaValue`] that
    preserves the host's variant tag.

    Used **only** in `DirectiveJson::Custom`'s `values` field, where
    callers genuinely need to distinguish (for example) a `Date` from
    a `String` or an `Account` — all three of which collapse to a bare
    JSON string under the untagged [`MetaValueJson`] shape.

    Wire shape: `{"type": "<variant>", "value": ...}` — mirrors
    `rustledger-ffi-wasi::TypedValue` (see
    `crates/rustledger-ffi-wasi/src/types/output.rs::TypedValue`) so
    portable JS consumers see identical envelopes across both bindings.

    **Why `value: MetaValueJson` and not `serde_json::Value`** —
    `serde_json` is intentionally a host-only dev-dependency for this
    crate (the runtime build avoids it to keep the wasm32 dep chain
    small). [`MetaValueJson`] already covers every payload shape
    FFI-WASI's `TypedValue` emits: `String` for the string-flavored
    variants, `Bool` for `bool`, `Amount` for `amount`, `Null` for
    `null`. The serialized JSON is bit-identical to FFI-WASI's.

    `MetaValueJson` (untagged) is retained for the `meta` map of every
    directive — there the lossy shape is intentional and matches what
    FFI-WASI's metadata side also emits.

    **Breaking change from #1199** for the WASM binding: pre-#1207
    `Custom.values` emitted raw `MetaValueJson` values (lossy). Closes
    #1207.
    """

    type: str = Field(
        ...,
        description='Variant tag — one of `"string"`, `"account"`, `"currency"`,\n`"tag"`, `"link"`, `"date"`, `"number"`, `"bool"`, `"amount"`,\n`"null"`. Matches FFI-WASI\'s tag strings exactly.\n\nRenamed via `#[ts(type = ...)]` so the discriminator is a\nstring-literal union on the TS side. The post-process script\nfurther narrows the full struct shape into a discriminated\nunion (per-variant `{type, value}` rows) -- see ADR-0004 for\nwhy the narrowing is hand-tuned rather than generator-driven.',
    )
    value: MetaValueJson = Field(
        ..., description="Variant payload (see [`MetaValueJson`] for the four shapes)."
    )


class BeancountError(BaseModel):
    """
    An error with source location.

    **Renamed to `BeancountError` on the TS side** to avoid shadowing
    the JS-builtin `Error` type. The Rust struct keeps the shorter
    `Error` name for internal use; the rename is applied via
    `#[ts(rename = ...)]` so consumers see a non-shadowing name.
    """

    column: int | None = Field(
        ...,
        description="Column number (1-based). `null` when the error has no source\nlocation. See `line` above for `range` rationale.",
        ge=1,
    )
    line: int | None = Field(
        ...,
        description="Line number (1-based). `null` when the error has no source\nlocation (e.g. validation errors not tied to a span). Field is\nalways present on the wire (no `skip_serializing_if`); see the\nstruct-level `schemars(extend)` for the required-and-nullable\nrationale. `range(min = 1)` enforces the 1-based documented\ncontract on the JSON Schema side (schemars defaults to\n`minimum: 0` for u32).",
        ge=1,
    )
    message: str = Field(..., description="Error message.")
    severity: Severity = Field(..., description="Error severity.")


class CellValue2(BaseModel):
    """
    Position with units and optional cost.
    """

    cost: CostValue | None = None
    units: AmountValue


class CellValue3(BaseModel):
    """
    Inventory with positions.
    """

    positions: list[PositionValue]


class CellValue(
    RootModel[
        str
        | int
        | bool
        | CellValue1
        | CellValue2
        | CellValue3
        | list[str]
        | list[CellValue]
        | dict[str, CellValue]
        | None
    ]
):
    root: (
        str
        | int
        | bool
        | CellValue1
        | CellValue2
        | CellValue3
        | list[str]
        | list[CellValue]
        | dict[str, CellValue]
        | None
    ) = Field(
        ...,
        description="A cell value that serializes properly to JavaScript.\n\nUses untagged serialization to produce clean JSON output.",
    )


class DirectiveJson1(BaseModel):
    """
    Transaction directive.
    """

    date: str
    flag: str
    links: list[str]
    meta: dict[str, MetaValueJson] | None = None
    narration: str | None = Field(
        None,
        description="Optional narration. Empty narrations are normalized to\n`None` in `convert.rs` so the field is absent on the wire\nin the empty case -- matches FFI-WASI's pattern (#1221).",
    )
    payee: str | None = Field(
        None,
        description="Optional payee. Mirrors FFI-WASI's shape: absent on the\nwire when `None` (closes #1221).",
    )
    postings: list[PostingJson]
    tags: list[str]
    type: Literal["transaction"]


class DirectiveJson2(BaseModel):
    """
    Balance assertion.
    """

    account: str
    amount: AmountValue
    date: str
    meta: dict[str, MetaValueJson] | None = None
    tolerance: str | None = Field(
        None,
        description="Explicit tolerance from the `~ 0.01` annotation, stringified.\nMirrors `rustledger_core::Balance::tolerance`.",
    )
    type: Literal["balance"]


class DirectiveJson3(BaseModel):
    """
    Open account.
    """

    account: str
    booking: str | None = None
    currencies: list[str]
    date: str
    meta: dict[str, MetaValueJson] | None = None
    type: Literal["open"]


class DirectiveJson4(BaseModel):
    """
    Close account.
    """

    account: str
    date: str
    meta: dict[str, MetaValueJson] | None = None
    type: Literal["close"]


class DirectiveJson5(BaseModel):
    """
    Commodity declaration.
    """

    currency: str
    date: str
    meta: dict[str, MetaValueJson] | None = None
    type: Literal["commodity"]


class DirectiveJson6(BaseModel):
    """
    Pad directive.
    """

    account: str
    date: str
    meta: dict[str, MetaValueJson] | None = None
    source_account: str
    type: Literal["pad"]


class DirectiveJson7(BaseModel):
    """
    Event directive.
    """

    date: str
    event_type: str
    meta: dict[str, MetaValueJson] | None = None
    type: Literal["event"]
    value: str


class DirectiveJson8(BaseModel):
    """
    Note directive.
    """

    account: str
    comment: str
    date: str
    meta: dict[str, MetaValueJson] | None = None
    type: Literal["note"]


class DirectiveJson9(BaseModel):
    """
    Document directive.
    """

    account: str
    date: str
    links: list[str] | None = Field(
        None, description="Links attached to the document directive (issue #1144)."
    )
    meta: dict[str, MetaValueJson] | None = None
    path: str
    tags: list[str] | None = Field(
        None, description="Tags attached to the document directive (issue #1144)."
    )
    type: Literal["document"]


class DirectiveJson10(BaseModel):
    """
    Price directive.
    """

    amount: AmountValue
    currency: str
    date: str
    meta: dict[str, MetaValueJson] | None = None
    type: Literal["price"]


class DirectiveJson11(BaseModel):
    """
    Query directive.
    """

    date: str
    meta: dict[str, MetaValueJson] | None = None
    name: str
    query_string: str
    type: Literal["query"]


class DirectiveJson12(BaseModel):
    """
    Custom directive.

    `values` carries the positional arguments after the type
    keyword. Each value is a [`TypedValueJson`] tagged union
    (`{type, value}`) that preserves the host `MetaValue`
    variant tag, so JS consumers can distinguish (for example)
    a `Date` from a `String` from an `Account` — all of which
    would otherwise collapse to bare JSON strings under the
    untagged `MetaValueJson` shape.

    Pre-#1168: `values` was dropped entirely from the JSON output.
    Pre-#1207: present but emitted raw via `MetaValueJson` (lossy).
    Post-#1207: emitted via `TypedValueJson` (this variant), mirroring
    FFI-WASI's `Vec<TypedValue>`.

    Both `values` and `meta` use `skip_serializing_if` to omit
    the field when empty (consistent shape: a Custom directive
    with no positional args and no metadata serializes as
    `{type, date, custom_type}`, matching what the TS shape
    declares via `values?` / `meta?`).
    """

    custom_type: str
    date: str
    meta: dict[str, MetaValueJson] | None = None
    type: Literal["custom"]
    values: list[TypedValueJson] | None = Field(
        None,
        description="Positional values after the `custom TYPE` keyword. Each\nentry is a [`TypedValueJson`] (`{type, value}`) — the\ntagged shape preserves the host `MetaValue` variant tag so\nJS consumers can distinguish a `Date` from a `String` from\nan `Account` (closes #1207). Mirrors FFI-WASI's\n`Vec<TypedValue>` exactly.",
    )


class DirectiveJson(
    RootModel[
        DirectiveJson1
        | DirectiveJson2
        | DirectiveJson3
        | DirectiveJson4
        | DirectiveJson5
        | DirectiveJson6
        | DirectiveJson7
        | DirectiveJson8
        | DirectiveJson9
        | DirectiveJson10
        | DirectiveJson11
        | DirectiveJson12
    ]
):
    root: (
        DirectiveJson1
        | DirectiveJson2
        | DirectiveJson3
        | DirectiveJson4
        | DirectiveJson5
        | DirectiveJson6
        | DirectiveJson7
        | DirectiveJson8
        | DirectiveJson9
        | DirectiveJson10
        | DirectiveJson11
        | DirectiveJson12
    ) = Field(
        ...,
        description="A directive in JSON-serializable form.\n\nEach variant corresponds to a Beancount directive type, with fields\nrepresenting the directive's data in a JavaScript-friendly format.\n\nAll variants carry a `meta` field with user-defined key/value\nmetadata from the source (issue #1168). Empty metadata serializes\nas an absent field, so existing consumers continue to see the\npre-#1168 shape on directives without explicit metadata.",
    )


class EditorDocumentSymbol(BaseModel):
    """
    A document symbol for the outline view.
    """

    children: list[EditorDocumentSymbol] | None = Field(
        None, description="Children of this symbol (e.g., postings in a transaction)."
    )
    deprecated: bool | None = Field(
        None, description="Whether this symbol is deprecated (e.g., closed account)."
    )
    detail: str | None = Field(None, description="More detail for this symbol.")
    kind: SymbolKind = Field(..., description="The kind of this symbol.")
    name: str = Field(..., description="The name of this symbol.")
    range: EditorRange = Field(..., description="The range enclosing this symbol.")


class EditorHoverInfo(BaseModel):
    """
    Hover information for a symbol.
    """

    contents: str = Field(..., description="The hover content (Markdown formatted).")
    range: EditorRange | None = Field(
        None, description="The range of the hovered symbol (optional)."
    )


class EditorReference(BaseModel):
    """
    A reference to a symbol in the document.
    """

    context: str | None = Field(
        None, description="Human-readable context (e.g., directive type)."
    )
    is_definition: bool = Field(
        ..., description="Whether this is the defining occurrence."
    )
    kind: ReferenceKind = Field(..., description="The kind of reference.")
    range: EditorRange = Field(..., description="The range of this reference.")


class EditorReferencesResult(BaseModel):
    """
    Result of a find-references request.
    """

    kind: ReferenceKind = Field(..., description="The kind of symbol.")
    references: list[EditorReference] = Field(..., description="All references found.")
    symbol: str = Field(..., description="The symbol being searched for.")


class FormatResult(BaseModel):
    """
    Result of formatting.
    """

    errors: list[BeancountError] = Field(..., description="Format errors.")
    formatted: str | None = Field(
        ...,
        description="Formatted source (if successful). Emitted as JSON `null` on\nfailure; no `skip_serializing_if`, so the field is always\npresent on the wire.",
    )


class LedgerJson(BaseModel):
    """
    A parsed Beancount ledger.

    **Renamed to `LedgerJson` on the TS side** to avoid colliding with
    the wasm-bindgen-exported `Ledger` class (the runtime wrapper that
    owns the parsed data). `LedgerJson` is the wire shape; `Ledger` is
    the class consumers instantiate via `Ledger.fromFiles(...)`. The
    Rust struct keeps the shorter name for internal use; the rename
    is applied via `#[ts(rename = ...)]`.
    """

    directives: list[DirectiveJson] = Field(
        ..., description="All directives in the ledger."
    )
    options: LedgerOptions = Field(..., description="Ledger options.")


class PadResult(BaseModel):
    """
    Result of pad expansion.
    """

    directives: list[DirectiveJson] = Field(
        ...,
        description="The original directives, verbatim. `Pad` directives are NOT\nremoved — consumers wanting a pads-removed view should\nfilter on directive type. The `padding_transactions` field\ncarries the synthesized P-flag transactions separately.",
    )
    errors: list[BeancountError] = Field(
        ...,
        description="Pad processing errors (e.g. unused pads with no matching\nbalance assertion).",
    )
    padding_transactions: list[DirectiveJson] = Field(
        ...,
        description="Generated padding transactions (synthesized P-flag, one per\npad-balance pair, multi-currency pads produce one per\ncurrency).",
    )


class ParseResult(BaseModel):
    """
    Result of parsing a Beancount file.
    """

    errors: list[BeancountError] = Field(..., description="Parse errors.")
    ledger: LedgerJson | None = Field(
        ...,
        description='The parsed ledger (if successful). Emitted as JSON `null` when\nparsing failed entirely; no `skip_serializing_if`, so the field\nis always present on the wire (TS: `Ledger | null`, not\n`ledger?`). See the `#[schemars(extend(...))]` on the struct\nitself for the "required-and-nullable" wire-contract enforcement.',
    )


class PluginResult(BaseModel):
    """
    Result of running a plugin.
    """

    directives: list[DirectiveJson] = Field(..., description="Modified directives.")
    errors: list[BeancountError] = Field(..., description="Plugin errors/warnings.")


class QueryResult(BaseModel):
    """
    Result of a BQL query.
    """

    columns: list[str] = Field(..., description="Column names.")
    errors: list[BeancountError] = Field(..., description="Query errors.")
    rows: list[list[CellValue]] = Field(..., description="Result rows.")


class ValidationResult(BaseModel):
    """
    Result of validation.
    """

    errors: list[BeancountError] = Field(..., description="Validation errors.")
    valid: bool = Field(..., description="Whether the ledger is valid.")


CellValue.model_rebuild()
EditorDocumentSymbol.model_rebuild()