Skip to content

Commit

Permalink
MapBodies/modules
Browse files Browse the repository at this point in the history
  • Loading branch information
bartelink committed Jan 3, 2025
1 parent 125a415 commit 984b1ea
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 31 deletions.
2 changes: 1 addition & 1 deletion src/FsCodec.Box/ByteArray.fs
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ type ByteArray private () =
[<Extension>]
static member ToByteArrayCodec<'Event, 'Context>(native: IEventCodec<'Event, ReadOnlyMemory<byte>, 'Context>)
: IEventCodec<'Event, byte[], 'Context> =
FsCodec.Core.EventCodec.Map(native, Func<_, _> ByteArray.ReadOnlyMemoryToBytes, Func<_, _> ByteArray.BytesToReadOnlyMemory)
FsCodec.Core.EventCodec.mapBodies ByteArray.ReadOnlyMemoryToBytes ByteArray.BytesToReadOnlyMemory native
8 changes: 4 additions & 4 deletions src/FsCodec.Box/Compression.fs
Original file line number Diff line number Diff line change
Expand Up @@ -87,22 +87,22 @@ type Compression private () =
static member EncodeTryCompress<'Event, 'Context>(native: IEventCodec<'Event, ReadOnlyMemory<byte>, 'Context>, [<Optional; DefaultParameterValue null>] ?options)
: IEventCodec<'Event, EncodedBody, 'Context> =
let opts = defaultArg options CompressionOptions.Default
FsCodec.Core.EventCodec.Map(native, (fun x -> Compression.Utf8ToEncodedTryCompress(opts, x)), Func<_, _> Compression.EncodedToUtf8)
FsCodec.Core.EventCodec.mapBodies_ (fun _x d -> Compression.Utf8ToEncodedTryCompress(opts, d)) Compression.EncodedToUtf8 native

/// <summary>Adapts an <c>IEventCodec</c> rendering to <c>ReadOnlyMemory&lt;byte&gt;</c> Event Bodies to encode as per <c>EncodeTryCompress</c>, but without attempting compression.</summary>
[<Extension>]
static member EncodeUncompressed<'Event, 'Context>(native: IEventCodec<'Event, ReadOnlyMemory<byte>, 'Context>)
: IEventCodec<'Event, EncodedBody, 'Context> =
FsCodec.Core.EventCodec.Map(native, Func<_, _> Compression.Utf8ToEncodedDirect, Func<_, _> Compression.EncodedToUtf8)
FsCodec.Core.EventCodec.mapBodies Compression.Utf8ToEncodedDirect Compression.EncodedToUtf8 native

/// <summary>Adapts an <c>IEventCodec</c> rendering to <c>int * ReadOnlyMemory&lt;byte&gt;</c> Event Bodies to render and/or consume from Uncompressed <c>ReadOnlyMemory&lt;byte&gt;</c>.</summary>
[<Extension>]
static member ToUtf8Codec<'Event, 'Context>(native: IEventCodec<'Event, EncodedBody, 'Context>)
: IEventCodec<'Event, ReadOnlyMemory<byte>, 'Context> =
FsCodec.Core.EventCodec.Map(native, Func<_, _> Compression.EncodedToUtf8, Func<_, _> Compression.Utf8ToEncodedDirect)
FsCodec.Core.EventCodec.mapBodies Compression.EncodedToUtf8 Compression.Utf8ToEncodedDirect native

/// <summary>Adapts an <c>IEventCodec</c> rendering to <c>int * ReadOnlyMemory&lt;byte&gt;</c> Event Bodies to render and/or consume from Uncompressed <c>byte[]</c>.</summary>
[<Extension>]
static member ToByteArrayCodec<'Event, 'Context>(native: IEventCodec<'Event, EncodedBody, 'Context>)
: IEventCodec<'Event, byte[], 'Context> =
FsCodec.Core.EventCodec.Map(native, Func<_, _> Compression.EncodedToByteArray, Func<_, _> Compression.Utf8ToEncodedDirect)
FsCodec.Core.EventCodec.mapBodies Compression.EncodedToByteArray Compression.Utf8ToEncodedDirect native
6 changes: 4 additions & 2 deletions src/FsCodec.Box/FsCodec.Box.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Condition=" '$(Configuration)' == 'Debug' " Include="../FsCodec/FsCodec.fsproj" />
<PackageReference Condition=" '$(Configuration)' == 'Release' " Include="FsCodec" Version="[3.0.0, 4.0.0)" />
<ProjectReference Include="../FsCodec/FsCodec.fsproj" />
<!-- NEXT PUBLISHED VERSION will take a 3.1 dependency to avoid using the Obsoleted API-->
<!-- <ProjectReference Condition=" '$(Configuration)' == 'Debug' " Include="../FsCodec/FsCodec.fsproj" />-->
<!-- <PackageReference Condition=" '$(Configuration)' == 'Release' " Include="FsCodec" Version="[3.1.0, 4.0.0)" />-->
</ItemGroup>

</Project>
6 changes: 4 additions & 2 deletions src/FsCodec.SystemTextJson/FsCodec.SystemTextJson.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Condition=" '$(Configuration)' == 'Debug' " Include="../FsCodec.Box/FsCodec.Box.fsproj" />
<PackageReference Condition=" '$(Configuration)' == 'Release' " Include="FsCodec.Box" Version="[3.0.0, 4.0.0)" />
<ProjectReference Include="../FsCodec.Box/FsCodec.Box.fsproj" />
<!-- NEXT PUBLISHED VERSION will take a 3.1 dependency to avoid using the Obsoleted API-->
<!-- <ProjectReference Condition=" '$(Configuration)' == 'Debug' " Include="../FsCodec.Box/FsCodec.Box.fsproj" />-->
<!-- <PackageReference Condition=" '$(Configuration)' == 'Release' " Include="FsCodec.Box" Version="[3.0.0, 4.0.0)" />-->
</ItemGroup>

</Project>
6 changes: 2 additions & 4 deletions src/FsCodec.SystemTextJson/Interop.fs
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,11 @@ type InteropHelpers private () =
[<Extension>]
static member ToUtf8Codec<'Event, 'Context>(native: FsCodec.IEventCodec<'Event, JsonElement, 'Context>)
: FsCodec.IEventCodec<'Event, ReadOnlyMemory<byte>, 'Context> =

FsCodec.Core.EventCodec.Map(native, Func<_, _> InteropHelpers.JsonElementToUtf8, Func<_, _> InteropHelpers.Utf8ToJsonElement)
FsCodec.Core.EventCodec.mapBodies InteropHelpers.JsonElementToUtf8 InteropHelpers.Utf8ToJsonElement native

/// <summary>Adapts an IEventCodec that's rendering to <c>ReadOnlyMemory&lt;byte&gt;</c> Event Bodies to handle <c>JsonElement</c> bodies instead.<br/>
/// NOTE where possible, it's better to use <c>CodecJsonElement</c> in preference to <c>Codec</c> to encode directly in order to avoid this mapping process.</summary>
[<Extension>]
static member ToJsonElementCodec<'Event, 'Context>(native: FsCodec.IEventCodec<'Event, ReadOnlyMemory<byte>, 'Context>)
: FsCodec.IEventCodec<'Event, JsonElement, 'Context> =

FsCodec.Core.EventCodec.Map(native, Func<_, _> InteropHelpers.Utf8ToJsonElement, Func<_, _> InteropHelpers.JsonElementToUtf8)
FsCodec.Core.EventCodec.mapBodies InteropHelpers.Utf8ToJsonElement InteropHelpers.JsonElementToUtf8 native
71 changes: 53 additions & 18 deletions src/FsCodec/FsCodec.fs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ namespace FsCodec.Core

open FsCodec
open System
open System.ComponentModel

/// <summary>An Event about to be written, see <c>IEventData</c> for further information.</summary>
[<NoComparison; NoEquality>]
Expand All @@ -61,20 +62,31 @@ type EventData<'Format>(eventType, data, meta, eventId, correlationId, causation
member _.CausationId = causationId
member _.Timestamp = timestamp

static member MapEx<'Mapped>(f: Func<IEventData<'Format>, 'Format, 'Mapped>)
(x: IEventData<'Format>): IEventData<'Mapped> =
static member MapBodies<'Mapped>(f: Func<IEventData<'Format>, 'Format, 'Mapped>): Func<IEventData<'Format>, IEventData<'Mapped>> =
Func<_, _>(fun x ->
{ new IEventData<'Mapped> with
member _.EventType = x.EventType
member _.Data = f.Invoke(x, x.Data)
member _.Meta = f.Invoke(x, x.Meta)
member _.EventId = x.EventId
member _.CorrelationId = x.CorrelationId
member _.CausationId = x.CausationId
member _.Timestamp = x.Timestamp }
member _.Timestamp = x.Timestamp })

static member Map<'Mapped>(f: Func<'Format, 'Mapped>)
(x: IEventData<'Format>): IEventData<'Mapped> =
EventData.MapEx(Func<_, _, _>(fun _x -> f.Invoke)) x
// Original ugly signature
[<Obsolete "Superseded by MapBodies / EventData.mapBodies; more importantly, the original signature mixed F# and C# types so was messy in all contexts"; EditorBrowsable(EditorBrowsableState.Never)>]
static member Map<'Mapped>(f: Func<'Format, 'Mapped>) (x: IEventData<'Format>): IEventData<'Mapped> =
EventData.MapBodies(Func<_, _, _>(fun _x -> f.Invoke)).Invoke(x)

/// F#-specific wrappers; for C#, use EventData.MapBodies directly
// These helper modules may move up to the FsCodec namespace in V4, along with breaking changes moving IsUnfold and Context from ITimelineEvent to IEventData
// If you have helpers that should be in the box alongside these, raise an Issue please
module EventData =

let mapBodies_<'Format, 'Mapped> (f: IEventData<'Format> -> 'Format -> 'Mapped) =
EventData.MapBodies(Func<IEventData<'Format>, 'Format, 'Mapped> f).Invoke
let mapBodies<'Format, 'Mapped> (f: 'Format -> 'Mapped) =
EventData.MapBodies(Func<IEventData<'Format>, 'Format, 'Mapped>(fun _ -> f)).Invoke

/// <summary>An Event or Unfold that's been read from a Store and hence has a defined <c>Index</c> on the Event Timeline.</summary>
[<NoComparison; NoEquality>]
Expand Down Expand Up @@ -108,41 +120,64 @@ type TimelineEvent<'Format>(index, eventType, data, meta, eventId, correlationId
member _.CausationId = causationId
member _.Timestamp = timestamp

static member Map<'Mapped>(f: Func<'Format, 'Mapped>)
(x: ITimelineEvent<'Format>): ITimelineEvent<'Mapped> =
static member MapBodies<'Mapped>(f: Func<ITimelineEvent<'Format>, 'Format, 'Mapped>): Func<ITimelineEvent<'Format>, ITimelineEvent<'Mapped>> =
Func<_, _>(fun x ->
{ new ITimelineEvent<'Mapped> with
member _.Index = x.Index
member _.IsUnfold = x.IsUnfold
member _.Context = x.Context
member _.Size = x.Size
member _.EventType = x.EventType
member _.Data = f.Invoke x.Data
member _.Meta = f.Invoke x.Meta
member _.Data = f.Invoke(x, x.Data)
member _.Meta = f.Invoke(x, x.Meta)
member _.EventId = x.EventId
member _.CorrelationId = x.CorrelationId
member _.CausationId = x.CausationId
member _.Timestamp = x.Timestamp }
member _.Timestamp = x.Timestamp })
// Original ugly signature
[<Obsolete "Superseded by MapBodies / TimeLineEvent.mapBodies; more importantly, the original signature mixed F# and C# types so was messy in all contexts"; EditorBrowsable(EditorBrowsableState.Never)>]
static member Map<'Mapped>(f: Func<'Format, 'Mapped>) (x: ITimelineEvent<'Format>): ITimelineEvent<'Mapped> =
TimelineEvent.MapBodies(Func<_, _, _>(fun _x -> f.Invoke)).Invoke(x)

/// F#-specific wrappers; for C#, use TimelineEvent.MapBodies directly
module TimelineEvent =

let mapBodies_<'Format, 'Mapped> (f: ITimelineEvent<'Format> -> 'Format -> 'Mapped) =
TimelineEvent.MapBodies(Func<ITimelineEvent<'Format>, 'Format, 'Mapped> f).Invoke
let mapBodies<'Format, 'Mapped> (f: 'Format -> 'Mapped) =
TimelineEvent.MapBodies(Func<ITimelineEvent<'Format>, 'Format, 'Mapped>(fun _ -> f)).Invoke

[<AbstractClass; Sealed>]
type EventCodec<'Event, 'Format, 'Context> private () =

static member MapEx<'TargetFormat>(native: IEventCodec<'Event, 'Format, 'Context>, up: Func<IEventData<'Format>, 'Format,'TargetFormat>, down: Func<'TargetFormat, 'Format>)
static member MapBodies<'TargetFormat>(
native: IEventCodec<'Event, 'Format, 'Context>,
up: Func<IEventData<'Format>, 'Format, 'TargetFormat>,
down: Func<'TargetFormat, 'Format>)
: IEventCodec<'Event, 'TargetFormat, 'Context> =

let upConvert = EventData.MapEx up
let downConvert = TimelineEvent.Map down
let upConvert = EventData.MapBodies up
let downConvert = TimelineEvent.MapBodies(fun _ x -> down.Invoke x)

{ new IEventCodec<'Event, 'TargetFormat, 'Context> with

member _.Encode(context, event) =
let encoded = native.Encode(context, event)
upConvert encoded
upConvert.Invoke encoded

member _.Decode target =
let encoded = downConvert target
let encoded = downConvert.Invoke target
native.Decode encoded }

static member Map<'TargetFormat>(native: IEventCodec<'Event, 'Format, 'Context>, up: Func<'Format,'TargetFormat>, down: Func<'TargetFormat, 'Format>)
[<Obsolete "Replaced by MapBodies/EventCodec.mapBodies for symmetry with TimelineEvent and EventData"; EditorBrowsable(EditorBrowsableState.Never)>]
static member Map<'TargetFormat>(native: IEventCodec<'Event, 'Format, 'Context>, up: Func<'Format, 'TargetFormat>, down: Func<'TargetFormat, 'Format>)
: IEventCodec<'Event, 'TargetFormat, 'Context> =
EventCodec.MapBodies(native, Func<_, _, _>(fun _x -> up.Invoke), down)

/// F#-specific wrappers; for C#, use EventCodec.MapBodies directly
module EventCodec =

EventCodec.MapEx(native, Func<_, _, _>(fun _x -> up.Invoke), down)
let mapBodies_ (up: IEventData<'Format> -> 'Format -> 'TargetFormat) (down: 'TargetFormat -> 'Format) x =
EventCodec<'Event, 'Format, 'Context>.MapBodies<'TargetFormat>(x, up, down)
let mapBodies (up: 'Format -> 'TargetFormat) (down: 'TargetFormat -> 'Format) x =
EventCodec<'Event, 'Format, 'Context>.MapBodies<'TargetFormat>(x, Func<_, _, _>(fun _ -> up), down)

0 comments on commit 984b1ea

Please sign in to comment.