nupp.serde.document
A document is a value without a schema, which any codec reads and writes. This module holds the Document tree, conversion between bound values and documents, typed views, and saved nupp.util.Store snapshots; see Serialization bindings for the guide.
local document = require("nupp.serde.document")
local json = require("nupp.serde.json")
local value = json.decodeDocument(document.adapter(), '{"id":18446744073709551615,"tags":["a","b"]}')
assert(value.kind == "structure")
assert(assert(value:get("id")):numberToken() == "18446744073709551615")
assert(assert(value:get("tags")):at(2):string() == "b")
assert(json.encodeDocument(document.adapter(), value) == '{"id":18446744073709551615,"tags":["a","b"]}')A document keeps what the codec read without rounding it: integers beyond 2^53 stay exact, decimals keep their digits, floats keep their width, and absent, null, and present members stay distinct. It also keeps the nupp.serde.Context it was read under, so a protocol can tell where it came from.
A document-valued member, one a schema declares without describing, is a document boundary: boundary wraps its contents so a protocol can treat the member separately from the value inside it.
from and into cross between a bound value and its document, which is how a value moves between codecs or is inspected without knowing its type:
local serde = require("nupp.serde")
local document = require("nupp.serde.document")
local record Point
x: integer
y: integer
end
local points = serde.binding(Point)
local tree = document.from(points, new Point(x = 1, y = 2))
assert(assert(tree:get("y")):numberToken() == "2")
assert(document.into(points, tree).x == 1)Module contents
Types
| Type | Kind | Description |
|---|---|---|
Decoder | interface | Constructs values of one binding from documents, without looking the binding up each time. |
Document | record | An immutable value tree. |
Entry | record | A named structure member, as object takes and Document:field returns. |
MapEntry | record | A map entry, whose key is itself a document. |
Migration | interface | Upgrades a saved entry from an older version to the current one. |
StoreEntry | record | One saved store entry: its name, version, and document. |
StoreRegistry | interface | Saves and restores the entries of a nupp.util.Store by name and version. |
StoreSnapshot | record | The saved entries of a store. |
View | record | A bound value presented as a document, without building the tree. |
Functions
| Function | Kind | Description |
|---|---|---|
adapter | function | Returns the adapter that reads and writes a Document as its contents. |
atom | function | A model-defined atom, snapshotted so the document owns it. |
bigInteger | function | An integer of unbounded size, from its decimal text. |
boolean | function | A boolean. |
boundary | function | A document boundary around contents, for a document-valued member. |
bytes | function | Binary data. |
cachedBindings | function | Returns how many bindings the decoder cache holds. |
clear | function | Empties the decoder cache. |
decimal | function | An exact decimal. |
decoder | function | Returns a binding's Decoder, cached by binding identity. |
float | function | A float with its width. |
from | function | Converts a bound value to its document. |
integer | function | A fixed-width integer. |
into | function | Constructs a bound value from a document. |
list | function | A list. |
map | function | A map whose keys are documents. |
null | function | A null. |
numberToken | function | A number kept as the lexeme it was written as, such as a JSON number too large for any integer width. |
object | function | A structure, from its members in order. |
reader | function | Reads a document through the nupp.serde.DocumentReader contract, as a codec's own reader would. |
snapshot | function | Copies a document into one that owns all of its values. |
storeBinding | function | Returns the binding a codec writes and reads a StoreSnapshot through. |
storeRegistry | function | Starts an empty StoreRegistry. |
string | function | A string. |
timestamp | function | A timestamp. |
tuple | function | A tuple: a list whose length is part of its shape. |
union | function | A union: the one member that is set. |
view | function | Presents a Copyable value as a View. |
viewAdapter | function | Returns the document adapter for views of one binding. |
withContext | function | The same document with a context attached. |
Types#
Decoderinterface#
interface Decoder<T> ...Constructs values of one binding from documents, without looking the binding up each time.
Type parameters
| Name | Description |
|---|---|
T |
Members
| Name | Kind | Description |
|---|---|---|
read | method | Constructs a value from a document. |
readmethod#
Constructs a value from a document.
The binding's construction runs exactly as a codec decoder's would, so required fields and defaults are checked the same way.
Arguments
| Name | Type | Description |
|---|---|---|
? | self | |
value | documents.Document | the document |
Returns
| Type | Description |
|---|---|
T | the value |
Raises
| Type | Condition |
|---|---|
nupp.serde.Error | when the document does not fit the binding |
string | when the binding does not consume the document |
Documentrecord#
record Document ...An immutable value tree.
kind says which accessor applies: string() for a string, at(i) and size() for a list or tuple, get(name) and field(i) for a structure, entry(i) for a map, document() for a boundary. get returns nil on any other kind; a scalar accessor raises on a document of another kind.
Members
| Name | Kind | Description |
|---|---|---|
kind | field | The kind of value the document holds, which decides the accessor that applies. |
text | field | |
flag | field | |
time | field | |
integerValue | field | |
decimalValue | field | |
floatValue | field | |
atomValue | field | |
entries | field | |
elements | field | |
fields | field | |
source | field | |
size | method | Returns how many values the document holds directly. |
at | method | Returns the element at a position in a list or tuple. |
get | method | Finds the member of a structure or union by name. |
string | method | Returns the string a string document holds. |
numberToken | method | Returns the lexeme a numberToken document holds. |
boolean | method | Returns the value a boolean document holds. |
integer | method | Returns the fixed-width integer an integer document holds. |
decimal | method | Returns the exact decimal a decimal document holds. |
float | method | Returns the float a float document holds. |
bytes | method | Returns the binary data a bytes document holds. |
bigInteger | method | Returns the integer a bigInteger document holds. |
timestamp | method | Returns the timestamp a timestamp document holds. |
atom | method | Returns a snapshot of the atom an atom document holds. |
entry | method | Returns the entry at a position in a map document. |
field | method | Returns the member at a position in a structure or union document. |
document | method | Returns the contents of a document boundary. |
context | method | Returns the context the document carries. |
kindfield#
kind: core.KindThe kind of value the document holds, which decides the accessor that applies.
sizemethod#
size: function size(self): integerReturns how many values the document holds directly.
A structure or union counts its members, a map its entries, and a list or tuple its elements. A boundary counts its contents as one, and any other kind returns 0.
Arguments
| Name | Type | Description |
|---|---|---|
self | any |
Returns
| Type | Description |
|---|---|
integer | the count |
atmethod#
Returns the element at a position in a list or tuple.
Arguments
| Name | Type | Description |
|---|---|---|
self | any | |
index | integer | the 1-based position |
Returns
| Type | Description |
|---|---|
Document | the element |
Raises
| Type | Condition |
|---|---|
string | when no element is at the index |
getmethod#
Finds the member of a structure or union by name.
Any other kind has no members, so it returns nil.
Arguments
| Name | Type | Description |
|---|---|---|
self | any | |
name | string | the member name |
Returns
| Type | Description |
|---|---|
Document? | the member's value, or nil when no member has the name |
stringmethod#
Returns the string a string document holds.
Arguments
| Name | Type | Description |
|---|---|---|
self | any |
Returns
| Type | Description |
|---|---|
string |
Raises
| Type | Condition |
|---|---|
string | when the document is not a string |
numberTokenmethod#
numberToken: function numberToken(self): stringReturns the lexeme a numberToken document holds.
Arguments
| Name | Type | Description |
|---|---|---|
self | any |
Returns
| Type | Description |
|---|---|
string | the number as it was written |
Raises
| Type | Condition |
|---|---|
string | when the document is not a number token |
booleanmethod#
Returns the value a boolean document holds.
Arguments
| Name | Type | Description |
|---|---|---|
self | any |
Returns
| Type | Description |
|---|---|
boolean |
Raises
| Type | Condition |
|---|---|
string | when the document is not a boolean |
integermethod#
Returns the fixed-width integer an integer document holds.
Arguments
| Name | Type | Description |
|---|---|---|
self | any |
Returns
| Type | Description |
|---|---|
core.Integer | the integer's decimal digits, width, and signedness |
Raises
| Type | Condition |
|---|---|
string | when the document is not an integer |
decimalmethod#
Returns the exact decimal a decimal document holds.
Arguments
| Name | Type | Description |
|---|---|---|
self | any |
Returns
| Type | Description |
|---|---|
core.Decimal | the decimal's coefficient and exponent |
Raises
| Type | Condition |
|---|---|
string | when the document is not a decimal |
floatmethod#
Returns the float a float document holds.
Arguments
| Name | Type | Description |
|---|---|---|
self | any |
Returns
| Type | Description |
|---|---|
core.Float | the float's value and width |
Raises
| Type | Condition |
|---|---|
string | when the document is not a float |
bytesmethod#
Returns the binary data a bytes document holds.
Arguments
| Name | Type | Description |
|---|---|---|
self | any |
Returns
| Type | Description |
|---|---|
string | the data as a string |
Raises
| Type | Condition |
|---|---|
string | when the document is not bytes |
bigIntegermethod#
bigInteger: function bigInteger(self): stringReturns the integer a bigInteger document holds.
Arguments
| Name | Type | Description |
|---|---|---|
self | any |
Returns
| Type | Description |
|---|---|
string | the integer's decimal text |
Raises
| Type | Condition |
|---|---|
string | when the document is not a big integer |
timestampmethod#
Returns the timestamp a timestamp document holds.
Arguments
| Name | Type | Description |
|---|---|---|
self | any |
Returns
| Type | Description |
|---|---|
core.Timestamp |
Raises
| Type | Condition |
|---|---|
string | when the document is not a timestamp |
atommethod#
Returns a snapshot of the atom an atom document holds.
Each call returns a new snapshot, so the document keeps its own.
Arguments
| Name | Type | Description |
|---|---|---|
self | any |
Returns
| Type | Description |
|---|---|
core.Atom |
Raises
| Type | Condition |
|---|---|
string | when the document is not an atom |
entrymethod#
Returns the entry at a position in a map document.
Arguments
| Name | Type | Description |
|---|---|---|
self | any | |
index | integer | the 1-based position |
Returns
| Type | Description |
|---|---|
MapEntry | the entry |
Raises
| Type | Condition |
|---|---|
string | when the document is not a map |
string | when no entry is at the index |
fieldmethod#
Returns the member at a position in a structure or union document.
Arguments
| Name | Type | Description |
|---|---|---|
self | any | |
index | integer | the 1-based position |
Returns
| Type | Description |
|---|---|
Entry | the member |
Raises
| Type | Condition |
|---|---|
string | when the document is neither a structure nor a union |
string | when no member is at the index |
documentmethod#
Returns the contents of a document boundary.
Arguments
| Name | Type | Description |
|---|---|---|
self | any |
Returns
| Type | Description |
|---|---|
Document |
Raises
| Type | Condition |
|---|---|
string | when the document is not a boundary |
contextmethod#
context: function context(self): core.Context?Returns the context the document carries.
A decoded document carries the context it was read under, and nupp.serde.document.withContext attaches one to any document.
Arguments
| Name | Type | Description |
|---|---|---|
self | any |
Returns
| Type | Description |
|---|---|
core.Context? | the context, or nil when the document has none |
Entryrecord#
record Entry ...A named structure member, as object takes and Document:field returns.
MapEntryrecord#
record MapEntry ...A map entry, whose key is itself a document.
Migrationinterface#
interface Migration ...Upgrades a saved entry from an older version to the current one.
Members
| Name | Kind | Description |
|---|---|---|
apply | method | Upgrades a saved entry's document to the registered version. |
applymethod#
Upgrades a saved entry's document to the registered version.
Loading calls it once per older entry, and its result is constructed through the registered binding, so it must return the current version's document directly.
Arguments
| Name | Type | Description |
|---|---|---|
? | self | |
previousVersion | integer | the version the entry was saved at |
value | documents.Document | the saved document |
Returns
| Type | Description |
|---|---|
documents.Document | the document at the registered version |
StoreEntryrecord#
record StoreEntry ...One saved store entry: its name, version, and document.
StoreRegistryinterface#
interface StoreRegistry ...Saves and restores the entries of a nupp.util.Store by name and version.
Each registered key names its entry, its binding, its current version, and the migration that upgrades an older one. Saving produces a StoreSnapshot any codec can carry through storeBinding.
local serde = require("nupp.serde")
local document = require("nupp.serde.document")
local json = require("nupp.serde.json")
local record Settings
volume: integer
end
local settings: nupp.util.Key<Settings> = nupp.util.newKey()
local registry = document.storeRegistry()
registry:register("settings", settings, serde.binding(Settings), 1, nil)
local store = nupp.util.newStore()
store:set(settings, new Settings(volume = 7))
local bytes = json.encode(document.storeBinding(), registry:save(store))
local restored = registry:load(json.decode(document.storeBinding(), bytes), "reject")
assert(assert(restored:get(settings)).volume == 7)Members
| Name | Kind | Description |
|---|---|---|
register | method | Registers a store key for saving under a persistent name. |
save | method | Saves the registered keys that hold a value in a store. |
load | method | Restores a snapshot's entries into a new store. |
registermethod#
register: function<T is nupp.Copyable>(
self,
name: string,
key: util.Key<T>,
binding: core.Binding<T>,
version: integer,
migration: Migration?
): nilRegisters a store key for saving under a persistent name.
Arguments
| Name | Type | Description |
|---|---|---|
? | self | |
name | string | the name the key's entry is saved under |
key | util.Key<T> | the store key |
binding | core.Binding<T> | the binding the key's value is saved and restored through |
version | integer | the current version, a positive integer |
migration | Migration? | the migration that upgrades an older entry, or nil when only the current version loads |
Returns
| Type | Description |
|---|---|
nil |
Raises
| Type | Condition |
|---|---|
string | when the name is empty or already registered |
string | when the key is already registered |
string | when the version is not a positive integer |
savemethod#
save: function(self, borrows store: util.Store): SnapshotSaves the registered keys that hold a value in a store.
Entries are in registration order, each at its key's current version. A registered key the store holds no value under is left out.
Arguments
| Name | Type | Description |
|---|---|---|
? | self | |
borrows store | util.Store | the store to read, which is borrowed |
Returns
| Type | Description |
|---|---|
Snapshot | the snapshot |
loadmethod#
Restores a snapshot's entries into a new store.
An entry at its key's registered version is constructed through the binding as it is. An older entry passes through the key's migration first. A newer entry, or an older one with no migration, raises.
Arguments
| Name | Type | Description |
|---|---|---|
? | self | |
snapshot | Snapshot | the saved entries |
unknown | "reject" | "skip" | "reject" to raise on an entry whose name is not registered, or "skip" to leave it out |
Returns
| Type | Description |
|---|---|
util.Store | the store holding every restored entry |
Raises
| Type | Condition |
|---|---|
string | when the snapshot's format is not 1 |
string | when two entries share a name |
string | when an entry's name is not registered and |
string | when an entry's version is not a positive integer, is newer than the registered version, or is older with no migration |
nupp.serde.Error | when an entry's document does not fit its binding |
string | when an entry's document constructs a nil value |
StoreSnapshotrecord#
record StoreSnapshot ...The saved entries of a store.
Viewrecord#
A bound value presented as a document, without building the tree.
A codec reads the current value through the binding each time, so a view follows changes to its owner. snapshot() reconstructs a detached copy, and document() materializes the tree when one is wanted.
local serde = require("nupp.serde")
local document = require("nupp.serde.document")
local json = require("nupp.serde.json")
local record Counter
count: integer
end
local counters = serde.binding(Counter)
local owner = new Counter(count = 1)
local live = document.view(counters, owner)
local frozen = live:snapshot()
owner.count = 2
local views = document.viewAdapter(counters)
assert(json.encodeDocument(views, live) == '{"count":2}')
assert(json.encodeDocument(views, frozen) == '{"count":1}')Type parameters
| Name | Description |
|---|---|
T |
Members
| Name | Kind | Description |
|---|---|---|
binding | field | The binding the view reads its value through. |
value | field | |
source | field | |
context | method | Returns the context the view carries. |
document | method | Builds the document tree of the value's current state. |
snapshot | method | Returns a view of an independent copy of the value. |
contextmethod#
context: function context(self): core.Context?Returns the context the view carries.
A view made by nupp.serde.document.view carries the context it was given, and a decoded view the context it was read under.
Arguments
| Name | Type | Description |
|---|---|---|
self | any |
Returns
| Type | Description |
|---|---|
core.Context? | the context, or nil when the view has none |
documentmethod#
Builds the document tree of the value's current state.
Arguments
| Name | Type | Description |
|---|---|---|
self | any |
Returns
| Type | Description |
|---|---|
documents.Document | the document, carrying the view's context |
snapshotmethod#
Returns a view of an independent copy of the value.
The copy is reconstructed from the value's document through the binding, so later changes to the original do not reach it.
Arguments
| Name | Type | Description |
|---|---|---|
self | any |
Returns
| Type | Description |
|---|---|
View<T> | the detached view, with the same binding and context |
Raises
| Type | Condition |
|---|---|
nupp.serde.Error | when the binding cannot construct a value from the document it wrote |
Functions#
adapterfunction#
function adapter(): core.DocumentAdapter<Document>Returns the adapter that reads and writes a Document as its contents.
Pass it to a codec's encodeDocument and decodeDocument, or to nupp.serde.adapters.document for a document-valued member.
Returns
| Type | Description |
|---|---|
core.DocumentAdapter<Document> |
atomfunction#
A model-defined atom, snapshotted so the document owns it.
Arguments
| Name | Type | Description |
|---|---|---|
borrows value | core.Atom |
Returns
| Type | Description |
|---|---|
Document |
bigIntegerfunction#
An integer of unbounded size, from its decimal text.
Arguments
| Name | Type | Description |
|---|---|---|
value | string |
Returns
| Type | Description |
|---|---|
Document |
booleanfunction#
A boolean.
Arguments
| Name | Type | Description |
|---|---|---|
value | boolean |
Returns
| Type | Description |
|---|---|
Document |
boundaryfunction#
A document boundary around contents, for a document-valued member.
Arguments
| Name | Type | Description |
|---|---|---|
value | Document |
Returns
| Type | Description |
|---|---|
Document |
bytesfunction#
Binary data.
Arguments
| Name | Type | Description |
|---|---|---|
value | string |
Returns
| Type | Description |
|---|---|
Document |
cachedBindingsfunction#
function cachedBindings(): integerReturns how many bindings the decoder cache holds.
Returns
| Type | Description |
|---|---|
integer |
clearfunction#
function clear(): nilEmpties the decoder cache. Decoders already handed out keep working.
Returns
| Type | Description |
|---|---|
nil |
decimalfunction#
An exact decimal.
Arguments
| Name | Type | Description |
|---|---|---|
value | core.Decimal |
Returns
| Type | Description |
|---|---|
Document |
decoderfunction#
function decoder<T>(binding: core.Binding<T>): Decoder<T>Returns a binding's Decoder, cached by binding identity.
The cache keeps at most 256 bindings and evicts the least recently used.
Type parameters
| Name | Description |
|---|---|
T |
Arguments
| Name | Type | Description |
|---|---|---|
binding | core.Binding<T> |
Returns
| Type | Description |
|---|---|
Decoder<T> |
floatfunction#
A float with its width.
Arguments
| Name | Type | Description |
|---|---|---|
value | core.Float |
Returns
| Type | Description |
|---|---|
Document |
fromfunction#
function from<T>(binding: core.Binding<T>, borrows value: T, context: core.Context?): documents.DocumentConverts a bound value to its document.
Type parameters
| Name | Description |
|---|---|
T |
Arguments
| Name | Type | Description |
|---|---|---|
binding | core.Binding<T> | |
borrows value | T | |
context | core.Context? |
Returns
| Type | Description |
|---|---|
documents.Document |
integerfunction#
A fixed-width integer.
Arguments
| Name | Type | Description |
|---|---|---|
value | core.Integer |
Returns
| Type | Description |
|---|---|
Document |
intofunction#
Constructs a bound value from a document.
The binding's construction runs exactly as a decoder's would, so required fields and defaults are checked the same way.
Type parameters
| Name | Description |
|---|---|
T |
Arguments
| Name | Type | Description |
|---|---|---|
binding | core.Binding<T> | |
value | documents.Document |
Returns
| Type | Description |
|---|---|
T |
listfunction#
A list.
Arguments
| Name | Type | Description |
|---|---|---|
values | {Document} |
Returns
| Type | Description |
|---|---|
Document |
mapfunction#
A map whose keys are documents.
Arguments
| Name | Type | Description |
|---|---|---|
values | {MapEntry} |
Returns
| Type | Description |
|---|---|
Document |
nullfunction#
function null(): DocumentA null.
Returns
| Type | Description |
|---|---|
Document |
numberTokenfunction#
A number kept as the lexeme it was written as, such as a JSON number too large for any integer width.
Arguments
| Name | Type | Description |
|---|---|---|
value | string |
Returns
| Type | Description |
|---|---|
Document |
objectfunction#
A structure, from its members in order.
local document = require("nupp.serde.document")
local json = require("nupp.serde.json")
local value = document.object({
new document.Entry(name = "ok", value = document.boolean(true)),
new document.Entry(name = "note", value = document.null())
})
assert(json.encodeDocument(document.adapter(), value) == '{"ok":true,"note":null}')Arguments
| Name | Type | Description |
|---|---|---|
values | {Entry} |
Returns
| Type | Description |
|---|---|
Document |
readerfunction#
Reads a document through the nupp.serde.DocumentReader contract, as a codec's own reader would.
Arguments
| Name | Type | Description |
|---|---|---|
value | documents.Document |
Returns
| Type | Description |
|---|---|
Reader |
snapshotfunction#
Copies a document into one that owns all of its values.
Arguments
| Name | Type | Description |
|---|---|---|
value | documents.Document |
Returns
| Type | Description |
|---|---|
documents.Document |
storeBindingfunction#
function storeBinding(): core.Binding<Snapshot>Returns the binding a codec writes and reads a StoreSnapshot through.
Returns
| Type | Description |
|---|---|
core.Binding<Snapshot> |
storeRegistryfunction#
function storeRegistry(): RegistryStarts an empty StoreRegistry.
Returns
| Type | Description |
|---|---|
Registry |
stringfunction#
A string.
Arguments
| Name | Type | Description |
|---|---|---|
value | string |
Returns
| Type | Description |
|---|---|
Document |
timestampfunction#
A timestamp.
Arguments
| Name | Type | Description |
|---|---|---|
value | core.Timestamp |
Returns
| Type | Description |
|---|---|
Document |
tuplefunction#
A tuple: a list whose length is part of its shape.
Arguments
| Name | Type | Description |
|---|---|---|
values | {Document} |
Returns
| Type | Description |
|---|---|
Document |
unionfunction#
A union: the one member that is set.
Arguments
| Name | Type | Description |
|---|---|---|
name | string | |
value | Document |
Returns
| Type | Description |
|---|---|
Document |
viewfunction#
function view<T is nupp.Copyable>(binding: core.Binding<T>, value: T, context: core.Context?): View<T>Presents a Copyable value as a View.
Type parameters
| Name | Description |
|---|---|
T |
Arguments
| Name | Type | Description |
|---|---|---|
binding | core.Binding<T> | |
value | T | |
context | core.Context? |
Returns
| Type | Description |
|---|---|
View<T> |
viewAdapterfunction#
function viewAdapter<T is nupp.Copyable>(binding: core.Binding<T>): core.DocumentAdapter<View<T>>Returns the document adapter for views of one binding.
Type parameters
| Name | Description |
|---|---|
T |
Arguments
| Name | Type | Description |
|---|---|---|
binding | core.Binding<T> |
Returns
| Type | Description |
|---|---|
core.DocumentAdapter<View<T>> |
withContextfunction#
The same document with a context attached.
Arguments
| Name | Type | Description |
|---|---|---|
value | Document | |
context | core.Context |
Returns
| Type | Description |
|---|---|
Document |