# `nupp.serde.codec`
A new codec uses these helpers to apply the number, timestamp, error-path, and
context rules the built-in codecs share. It implements `nupp.serde.Writer`
and `nupp.serde.Reader` itself; see
[Serialization bindings](../../../../learn/runtime/data/serde/index.html#model-owned-bindings) for where a
codec sits beside a model library.
A codec that follows these rules accepts and rejects the same values as
`nupp.serde.json` and `nupp.serde.native`, so a binding behaves the same
whichever one carries it.
```nupp
local codec = require("nupp.serde.codec")
assert(codec.integer("255", 8, false).decimal == "255")
assert(not pcall(codec.integer, "256", 8, false))
assert(codec.formatDateTime(codec.parseEpoch("86400")) == "1970-01-02T00:00:00Z")
```
## Types
### `ContextBuilder` _record_
```nupp
record ContextBuilder ...
```
Collects the values of a `nupp.serde.Context` before it is frozen.
#### Members
| Name | Kind | Description |
| --- | --- | --- |
| [`codec`](#nupp.serde.codec.ContextBuilder.codec) | field | |
| [`profile`](#nupp.serde.codec.ContextBuilder.profile) | field | |
| [`parent`](#nupp.serde.codec.ContextBuilder.parent) | field | |
| [`values`](#nupp.serde.codec.ContextBuilder.values) | field | |
| [`frozen`](#nupp.serde.codec.ContextBuilder.frozen) | field | |
| [`set`](#nupp.serde.codec.ContextBuilder.set) | method | Stores a value under a key, replacing any value set before. |
| [`freeze`](#nupp.serde.codec.ContextBuilder.freeze) | method | Returns the context holding the values set so far. |
#### `codec` _field_
```nupp
codec: table
```
`@private` `@readonly`
#### `profile` _field_
```nupp
profile: table
```
`@private` `@readonly`
#### `parent` _field_
```nupp
parent: core.Context?
```
`@private` `@readonly`
#### `values` _field_
```nupp
values: {[unknown]: Entry}
```
`@private` `@readonly`
#### `frozen` _field_
```nupp
frozen: boolean
```
`@private`
#### `set` _method_
```nupp
set: function set(self, key: store.Key, value: V): nil
```
Stores a value under a key, replacing any value set before.
##### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `self` | `any` | |
| `key` | `store.Key\` | the key to store under |
| `value` | `V` | the value |
##### Returns
| Type | Description |
| --- | --- |
| `nil` | |
##### Raises
| Type | Condition |
| --- | --- |
| `string` | when the builder is frozen |
#### `freeze` _method_
```nupp
freeze: function freeze(self): core.Context
```
Returns the context holding the values set so far.
The builder can then no longer change, so the context it returned
cannot either. A key the context does not hold is looked up in its
parent.
##### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `self` | `any` | |
##### Returns
| Type | Description |
| --- | --- |
| `core.Context` | the context |
##### Raises
| Type | Condition |
| --- | --- |
| `string` | when the builder is already frozen |
## Functions
### `contextBuilder` _function_
```nupp
function contextBuilder(codec: table, profile: table, parent: core.Context?): Builder
```
Starts a context for what a codec reads.
`codec` and `profile` identify where the value came from, so a protocol can
refuse to replay a document captured under an incompatible one. Values are
set under `nupp.util.Key`s and must be Copyable.
```nupp
local codec = require("nupp.serde.codec")
local source: nupp.util.Key = nupp.util.newKey()
local builder = codec.contextBuilder({}, {}, nil)
builder:set(source, "upload.json")
assert(builder:freeze():get(source) == "upload.json")
```
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `codec` | `table` | |
| `profile` | `table` | |
| `parent` | `core.Context?` | |
#### Returns
| Type | Description |
| --- | --- |
| `Builder` | |
### `discardFailed` _function_
```nupp
function discardFailed(owner: core.Discard, takes value: T, cause: unknown): unknown
```
Discards a value a codec decoded and then has to reject, returning the
failure to raise.
When the discard itself fails, its failure is recorded in the error's
`suppressed` list and the original cause is kept.
#### Type parameters
| Name | Description |
| --- | --- |
| `T` | |
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `owner` | `core.Discard\` | |
| `takes value` | `T` | |
| `cause` | `unknown` | |
#### Returns
| Type | Description |
| --- | --- |
| `unknown` | |
### `float` _function_
```nupp
function float(value: number, width: 32 | 64, allowNonfinite: boolean?): number
```
Rounds a number to a float width and checks it is finite.
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `value` | `number` | |
| `width` | `32 | 64` | |
| `allowNonfinite` | `boolean?` | |
#### Returns
| Type | Description |
| --- | --- |
| `number` | |
### `formatDateTime` _function_
```nupp
function formatDateTime(borrows value: core.Timestamp): string
```
Formats a timestamp as an RFC 3339 date-time in UTC.
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `borrows value` | `core.Timestamp` | |
#### Returns
| Type | Description |
| --- | --- |
| `string` | |
### `formatEpoch` _function_
```nupp
function formatEpoch(borrows value: core.Timestamp): string
```
Formats a timestamp as epoch seconds.
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `borrows value` | `core.Timestamp` | |
#### Returns
| Type | Description |
| --- | --- |
| `string` | |
### `integer` _function_
```nupp
function integer(text: string, width: 8 | 16 | 32 | 64, signed: boolean): core.Integer
```
Checks an integer lexeme against a width and signedness.
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `text` | `string` | |
| `width` | `8 | 16 | 32 | 64` | |
| `signed` | `boolean` | |
#### Returns
| Type | Description |
| --- | --- |
| `core.Integer` | |
### `parseDateTime` _function_
```nupp
function parseDateTime(text: string): core.Timestamp
```
Parses an RFC 3339 date-time.
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `text` | `string` | |
#### Returns
| Type | Description |
| --- | --- |
| `core.Timestamp` | |
### `parseEpoch` _function_
```nupp
function parseEpoch(text: string): core.Timestamp
```
Parses epoch seconds, with an optional fraction.
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `text` | `string` | |
#### Returns
| Type | Description |
| --- | --- |
| `core.Timestamp` | |
### `prefixError` _function_
```nupp
function prefixError(problem: unknown, parent: string): Error
```
Prefixes a failure's path with its parent's.
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `problem` | `unknown` | the raised value |
| `parent` | `string` | the path of the enclosing value |
#### Returns
| Type | Description |
| --- | --- |
| `Error` | the error, carrying the combined path |
### `wrapError` _function_
```nupp
function wrapError(problem: unknown, path: string, byte: integer?): Error
```
Wraps any failure as a `nupp.serde.Error` at a path.
An existing error is returned unchanged, so wrapping at each level keeps the
innermost path.
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `problem` | `unknown` | the raised value |
| `path` | `string` | the logical path to record on a new error |
| `byte` | `integer?` | the input offset to record on a new error, or nil |
#### Returns
| Type | Description |
| --- | --- |
| `Error` | the error |