Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generalize Codec to admit 'Format #24

Merged
merged 3 commits into from
Oct 26, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,14 @@ The `Unreleased` section name is replaced by the expected version of next releas

### Added
### Changed

- Generalized `Codec.Create` to no longer presume `Data` and `Metadata` should always be `byte[]` [#24](https://github.com/jet/FsCodec/pull/24)

### Removed
### Fixed

- Removed accidentally pasted `setting` and `allowNullaryCases` in `Codec.Create` [#23](https://github.com/jet/FsCodec/pull/23)

<a name="1.1.0"></a>
## [1.1.0] - 2019-09-19

Expand Down
47 changes: 22 additions & 25 deletions src/FsCodec/Codec.fs
Original file line number Diff line number Diff line change
@@ -1,58 +1,55 @@
namespace FsCodec

open System

/// Provides Codecs that render to a UTF-8 array suitable for storage in Event Stores, based on explicit functions you supply
/// Provides Codecs that render to store-supported form (e.g., a UTF-8 byte array) suitable for storage in Event Stores, based on explicit functions you supply
/// Does not involve conventions / Type Shapes / Reflection or specific Json processing libraries - see FsCodec.*.Codec for batteries-included Coding/Decoding
type Codec =

/// Generate a <code>IUnionEncoder</code> Codec suitable using the supplied pair of <c>encode</c> and <c>tryDecode</code> functions.
// Leaving this helper private until we have a real use case which will e.g. enable us to decide whether to align the signature with the up/down functions
// employed in the convention-based Codec
// (IME, while many systems have some code touching the metadata, it's not something one typically wants to encourage)
static member private Create<'Union,'Context>
( /// Maps a 'Union to an Event Type Name with UTF-8 arrays representing the <c>Data</c> and <c>Meta</c> together with the correlationId, causationId and timestamp.
encode : 'Context option * 'Union -> string * byte[] * byte[] * string * string * System.DateTimeOffset option,
/// Attempts to map from an Event's Data to a <c>'Union</c> case, or <c>None</c> if not mappable.
tryDecode : ITimelineEvent<byte[]> -> 'Union option)
: IUnionEncoder<'Union, byte[], 'Context> =
{ new IUnionEncoder<'Union, byte[], 'Context> with
static member private Create<'Union,'Format,'Context>
( /// Maps a 'Union to an Event Type Name, a pair of <>'Format</c>'s representing the <c>Data</c> and <c>Meta</c> together with the <c>correlationId</c>, <c>causationId</c> and <c>timestamp</c>.
encode : 'Context option * 'Union -> string * 'Format * 'Format * string * string * System.DateTimeOffset option,
/// Attempts to map from an Event's stored data to a <c>'Union</c> case, or <c>None</c> if not mappable.
tryDecode : ITimelineEvent<'Format> -> 'Union option)
: IUnionEncoder<'Union, 'Format, 'Context> =
{ new IUnionEncoder<'Union, 'Format, 'Context> with
member __.Encode(context, union) =
let eventType, data, metadata, correlationId, causationId, timestamp = encode (context,union)
Core.EventData.Create(eventType, data, metadata, correlationId, causationId, ?timestamp=timestamp) :> _
member __.TryDecode encoded =
tryDecode encoded }

/// Generate a <code>IUnionEncoder</code> Codec suitable using the supplied <c>encode</c> and <c>tryDecode</code> functions to map to/from UTF8.
/// <c>mapCausation</c> provides metadata generation and correlation/causationId mapping based on the Context passed to the encoder
static member Create<'Context,'Union>
/// Generate a <code>IUnionEncoder</code> Codec suitable using the supplied <c>encode</c> and <c>tryDecode</code> functions to map to/from the stored form.
/// <c>mapCausation</c> provides metadata generation and correlation/causationId mapping based on the <c>context</c> passed to the encoder
static member Create<'Context,'Union,'Format>
( /// Maps from the TypeShape <c>UnionConverter</c> <c>'Contract</c> case the Event has been mapped to (with the raw event data as context)
/// to the representation (typically a Discriminated Union) that is to be presented to the programming model.
tryDecode : FsCodec.ITimelineEvent<byte[]> -> 'Union option,
tryDecode : FsCodec.ITimelineEvent<'Format> -> 'Union option,
/// Maps a fresh Event resulting from a Decision in the Domain representation type down to the TypeShape <c>UnionConverter</c> <c>'Contract</c>
/// The function is also expected to derive
/// a <c>meta</c> object that will be serialized with the same settings (if it's not <c>None</c>)
/// and an Event Creation <c>timestamp</c>.
encode : 'Union -> string * byte[] * DateTimeOffset option,
encode : 'Union -> string * 'Format * DateTimeOffset option,
/// Uses the 'Context passed to the Encode call and the 'Meta emitted by <c>down</c> to a) the final metadata b) the <c>correlationId</c> and c) the correlationId
mapCausation : 'Context option * 'Union -> byte[] * string * string,
/// Configuration to be used by the underlying <c>Newtonsoft.Json</c> Serializer when encoding/decoding. Defaults to same as <c>Settings.Create()</c>
?settings,
/// Enables one to fail encoder generation if union contains nullary cases. Defaults to <c>false</c>, i.e. permitting them
?rejectNullaryCases)
: FsCodec.IUnionEncoder<'Union,byte[],'Context> =
mapCausation : 'Context option * 'Union -> 'Format * string * string)
: FsCodec.IUnionEncoder<'Union,'Format,'Context> =
let encode (context,union) =
let et, d, t = encode union
let m, correlationId, causationId = mapCausation (context, union)
et, d, m, correlationId, causationId, t
Codec.Create(encode,tryDecode)

/// Generate a <code>IUnionEncoder</code> Codec using the supplied pair of <c>encode</c> and <c>tryDecode</code> functions.
static member Create<'Union>
static member Create<'Union,'Format when 'Format : null>
bartelink marked this conversation as resolved.
Show resolved Hide resolved
( /// Maps a <c>'Union</c> to an Event Type Name and a UTF-8 array representing the <c>Data</c>.
encode : 'Union -> string * byte[],
encode : 'Union -> string * 'Format,
/// Attempts to map an Event Type Name and a UTF-8 <c>Data</c> array to a <c>'Union</c> case, or <c>None</c> if not mappable.
tryDecode : string * byte[] -> 'Union option)
: IUnionEncoder<'Union, byte[], obj> =
let encode' (_context,union) = let et, d = encode union in et, d, null, null, null, None
let tryDecode' (encoded : FsCodec.ITimelineEvent<_>) = tryDecode (encoded.EventType,encoded.Data)
tryDecode : string * 'Format -> 'Union option)
: IUnionEncoder<'Union, 'Format, obj> =
let encode' (_context : obj,union) = let (et, d : 'Format) = encode union in et, d, null, null, null, None
let tryDecode' (encoded : FsCodec.ITimelineEvent<'Format>) = tryDecode (encoded.EventType,encoded.Data)
Codec.Create(encode', tryDecode')