Skip to content

Commit 7ef6d0a

Browse files
authored
feat(EventData): Add MapBodies overload with context (#127)
1 parent 7ba4c4c commit 7ef6d0a

File tree

5 files changed

+70
-20
lines changed

5 files changed

+70
-20
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ The `Unreleased` section name is replaced by the expected version of next releas
99
## [Unreleased]
1010

1111
### Added
12+
13+
- `MapBodies`: Enable contextual encoding of bodies [#127](https://github.com/jet/FsCodec/pull/127)
14+
1215
### Changed
1316
### Removed
1417
### Fixed

src/FsCodec.Box/FsCodec.Box.fsproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
<ItemGroup>
2626
<ProjectReference Condition=" '$(Configuration)' == 'Debug' " Include="../FsCodec/FsCodec.fsproj" />
27+
<!-- TODO if taking a dependency on 3.1, the impl should switch to EventCodec.mapBodies, and EventCodec.Map should be Obsoleted -->
2728
<PackageReference Condition=" '$(Configuration)' == 'Release' " Include="FsCodec" Version="[3.0.0, 4.0.0)" />
2829
</ItemGroup>
2930

src/FsCodec.SystemTextJson/FsCodec.SystemTextJson.fsproj

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,10 @@
3030
</ItemGroup>
3131

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

3739
</Project>

src/FsCodec.SystemTextJson/Interop.fs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,11 @@ type InteropHelpers private () =
2121
[<Extension>]
2222
static member ToUtf8Codec<'Event, 'Context>(native: FsCodec.IEventCodec<'Event, JsonElement, 'Context>)
2323
: FsCodec.IEventCodec<'Event, ReadOnlyMemory<byte>, 'Context> =
24-
2524
FsCodec.Core.EventCodec.Map(native, Func<_, _> InteropHelpers.JsonElementToUtf8, Func<_, _> InteropHelpers.Utf8ToJsonElement)
2625

2726
/// <summary>Adapts an IEventCodec that's rendering to <c>ReadOnlyMemory&lt;byte&gt;</c> Event Bodies to handle <c>JsonElement</c> bodies instead.<br/>
2827
/// 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>
2928
[<Extension>]
3029
static member ToJsonElementCodec<'Event, 'Context>(native: FsCodec.IEventCodec<'Event, ReadOnlyMemory<byte>, 'Context>)
3130
: FsCodec.IEventCodec<'Event, JsonElement, 'Context> =
32-
3331
FsCodec.Core.EventCodec.Map(native, Func<_, _> InteropHelpers.Utf8ToJsonElement, Func<_, _> InteropHelpers.JsonElementToUtf8)

src/FsCodec/FsCodec.fs

Lines changed: 62 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ namespace FsCodec.Core
4141

4242
open FsCodec
4343
open System
44+
open System.ComponentModel
4445

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

64-
static member Map<'Mapped>(f: Func<'Format, 'Mapped>)
65-
(x: IEventData<'Format>): IEventData<'Mapped> =
65+
static member MapBodies<'Mapped>(f: Func<IEventData<'Format>, 'Format, 'Mapped>): Func<IEventData<'Format>, IEventData<'Mapped>> =
66+
Func<_, _>(fun x ->
6667
{ new IEventData<'Mapped> with
6768
member _.EventType = x.EventType
68-
member _.Data = f.Invoke x.Data
69-
member _.Meta = f.Invoke x.Meta
69+
member _.Data = f.Invoke(x, x.Data)
70+
member _.Meta = f.Invoke(x, x.Meta)
7071
member _.EventId = x.EventId
7172
member _.CorrelationId = x.CorrelationId
7273
member _.CausationId = x.CausationId
73-
member _.Timestamp = x.Timestamp }
74+
member _.Timestamp = x.Timestamp })
75+
76+
// Original ugly signature
77+
[<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)>]
78+
static member Map<'Mapped>(f: Func<'Format, 'Mapped>) (x: IEventData<'Format>): IEventData<'Mapped> =
79+
EventData.MapBodies(Func<_, _, _>(fun _x -> f.Invoke)).Invoke(x)
80+
81+
/// F#-specific wrappers; for C#, use EventData.MapBodies directly
82+
// These helper modules may move up to the FsCodec namespace in V4, along with breaking changes moving IsUnfold and Context from ITimelineEvent to IEventData
83+
// If you have helpers that should be in the box alongside these, raise an Issue please
84+
module EventData =
85+
86+
let mapBodies_<'Format, 'Mapped> (f: IEventData<'Format> -> 'Format -> 'Mapped) =
87+
EventData.MapBodies(Func<IEventData<'Format>, 'Format, 'Mapped> f).Invoke
88+
let mapBodies<'Format, 'Mapped> (f: 'Format -> 'Mapped) =
89+
EventData.MapBodies(Func<IEventData<'Format>, 'Format, 'Mapped>(fun _ -> f)).Invoke
7490

7591
/// <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>
7692
[<NoComparison; NoEquality>]
@@ -90,7 +106,7 @@ type TimelineEvent<'Format>(index, eventType, data, meta, eventId, correlationId
90106
TimelineEvent(index, inner.EventType, inner.Data, inner.Meta, inner.EventId, inner.CorrelationId, inner.CausationId, inner.Timestamp, isUnfold, Option.toObj context, size) :> _
91107

92108
override _.ToString() = sprintf "%s %s @%i" (if isUnfold then "Unfold" else "Event") eventType index
93-
109+
94110
interface ITimelineEvent<'Format> with
95111
member _.Index = index
96112
member _.IsUnfold = isUnfold
@@ -104,36 +120,66 @@ type TimelineEvent<'Format>(index, eventType, data, meta, eventId, correlationId
104120
member _.CausationId = causationId
105121
member _.Timestamp = timestamp
106122

107-
static member Map<'Mapped>(f: Func<'Format, 'Mapped>)
108-
(x: ITimelineEvent<'Format>): ITimelineEvent<'Mapped> =
123+
static member MapBodies<'Mapped>(f: Func<ITimelineEvent<'Format>, 'Format, 'Mapped>): Func<ITimelineEvent<'Format>, ITimelineEvent<'Mapped>> =
124+
Func<_, _>(fun x ->
109125
{ new ITimelineEvent<'Mapped> with
110126
member _.Index = x.Index
111127
member _.IsUnfold = x.IsUnfold
112128
member _.Context = x.Context
113129
member _.Size = x.Size
114130
member _.EventType = x.EventType
115-
member _.Data = f.Invoke x.Data
116-
member _.Meta = f.Invoke x.Meta
131+
member _.Data = f.Invoke(x, x.Data)
132+
member _.Meta = f.Invoke(x, x.Meta)
117133
member _.EventId = x.EventId
118134
member _.CorrelationId = x.CorrelationId
119135
member _.CausationId = x.CausationId
120-
member _.Timestamp = x.Timestamp }
136+
member _.Timestamp = x.Timestamp })
137+
// Original ugly signature
138+
[<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)>]
139+
static member Map<'Mapped>(f: Func<'Format, 'Mapped>) (x: ITimelineEvent<'Format>): ITimelineEvent<'Mapped> =
140+
TimelineEvent.MapBodies(Func<_, _, _>(fun _x -> f.Invoke)).Invoke(x)
141+
142+
/// F#-specific wrappers; for C#, use TimelineEvent.MapBodies directly
143+
module TimelineEvent =
144+
145+
let mapBodies_<'Format, 'Mapped> (f: ITimelineEvent<'Format> -> 'Format -> 'Mapped) =
146+
TimelineEvent.MapBodies(Func<ITimelineEvent<'Format>, 'Format, 'Mapped> f).Invoke
147+
let mapBodies<'Format, 'Mapped> (f: 'Format -> 'Mapped) =
148+
TimelineEvent.MapBodies(Func<ITimelineEvent<'Format>, 'Format, 'Mapped>(fun _ -> f)).Invoke
121149

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

125-
static member Map<'TargetFormat>(native: IEventCodec<'Event, 'Format, 'Context>, up: Func<'Format,'TargetFormat>, down: Func<'TargetFormat, 'Format>)
153+
static member MapBodies<'TargetFormat>(
154+
native: IEventCodec<'Event, 'Format, 'Context>,
155+
up: Func<IEventData<'Format>, 'Format, 'TargetFormat>,
156+
down: Func<'TargetFormat, 'Format>)
126157
: IEventCodec<'Event, 'TargetFormat, 'Context> =
127158

128-
let upConvert = EventData.Map up
129-
let downConvert = TimelineEvent.Map down
159+
let upConvert = EventData.MapBodies up
160+
let downConvert = TimelineEvent.MapBodies(fun _ x -> down.Invoke x)
130161

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

133164
member _.Encode(context, event) =
134165
let encoded = native.Encode(context, event)
135-
upConvert encoded
166+
upConvert.Invoke encoded
136167

137168
member _.Decode target =
138-
let encoded = downConvert target
169+
let encoded = downConvert.Invoke target
139170
native.Decode encoded }
171+
172+
// NOTE To be be replaced by MapBodies/EventCodec.mapBodies for symmetry with TimelineEvent and EventData
173+
// TO BE be Obsoleted and whenever FsCodec.Box is next released
174+
[<EditorBrowsable(EditorBrowsableState.Never)>]
175+
static member Map<'TargetFormat>(native: IEventCodec<'Event, 'Format, 'Context>, up: Func<'Format, 'TargetFormat>, down: Func<'TargetFormat, 'Format>)
176+
: IEventCodec<'Event, 'TargetFormat, 'Context> =
177+
EventCodec.MapBodies(native, Func<_, _, _>(fun _x -> up.Invoke), down)
178+
179+
/// F#-specific wrappers; for C#, use EventCodec.MapBodies directly
180+
module EventCodec =
181+
182+
let mapBodies_ (up: IEventData<'Format> -> 'Format -> 'TargetFormat) (down: 'TargetFormat -> 'Format) x =
183+
EventCodec<'Event, 'Format, 'Context>.MapBodies<'TargetFormat>(x, up, down)
184+
let mapBodies (up: 'Format -> 'TargetFormat) (down: 'TargetFormat -> 'Format) x =
185+
EventCodec<'Event, 'Format, 'Context>.MapBodies<'TargetFormat>(x, Func<_, _, _>(fun _ -> up), down)

0 commit comments

Comments
 (0)