22using Equinox . Store ;
33using Microsoft . FSharp . Core ;
44using Newtonsoft . Json ;
5+ using OneOf ;
56using Serilog ;
67using System ;
78using System . Collections . Generic ;
@@ -14,6 +15,7 @@ public static class Todo
1415 {
1516 /// NB - these types and names reflect the actual storage formats and hence need to be versioned with care
1617 public abstract class Event
18+ : OneOfBase < Event . Added , Event . Updated , Event . Deleted , Event . Cleared , Event . Compacted >
1719 {
1820 /// Information we retain per Todo List entry
1921 public abstract class ItemData
@@ -95,37 +97,23 @@ public static State Fold(State origin, IEnumerable<Event> xs)
9597 var nextId = origin . NextId ;
9698 var items = origin . Items . ToList ( ) ;
9799 foreach ( var x in xs )
98- switch ( x )
99- {
100- case Event . Added e :
101- nextId ++ ;
102- items . Insert ( 0 , e . Data ) ;
103- break ;
104- case Event . Updated e :
100+ x . Switch (
101+ ( Event . Added e ) => { nextId ++ ; items . Insert ( 0 , e . Data ) ; } ,
102+ ( Event . Updated e ) =>
103+ {
105104 var i = items . FindIndex ( item => item . Id == e . Data . Id ) ;
106105 if ( i != - 1 )
107106 items [ i ] = e . Data ;
108- break ;
109- case Event . Deleted e :
110- items . RemoveAll ( item => item . Id == e . Id ) ;
111- break ;
112- case Event . Cleared e :
113- nextId = e . NextId ;
114- items . Clear ( ) ;
115- break ;
116- case Event . Compacted e :
117- nextId = e . NextId ;
118- items = e . Items . ToList ( ) ;
119- break ;
120- default :
121- throw new ArgumentOutOfRangeException ( nameof ( x ) , x , "invalid" ) ;
122- }
107+ } ,
108+ ( Event . Deleted e ) => items . RemoveAll ( item => item . Id == e . Id ) ,
109+ ( Event . Cleared e ) => { nextId = e . NextId ; items . Clear ( ) ; } ,
110+ ( Event . Compacted e ) => { nextId = e . NextId ; items = e . Items . ToList ( ) ; } ) ;
123111 return new State ( nextId , items . ToArray ( ) ) ;
124112 }
125-
113+
126114 /// Determines whether a given event represents a checkpoint that implies we don't need to see any preceding events
127115 public static bool IsOrigin ( Event e ) => e is Event . Cleared || e is Event . Compacted ;
128-
116+
129117 /// Prepares an Event that encodes all relevant aspects of a State such that `evolve` can rehydrate a complete State from it
130118 public static Event Compact ( State state ) => new Event . Compacted { NextId = state . NextId , Items = state . Items } ;
131119 }
@@ -140,6 +128,7 @@ public class Props
140128
141129 /// Defines the operations a caller can perform on a Todo List
142130 public abstract class Command
131+ : OneOfBase < Command . Add , Command . Update , Command . Delete , Command . Clear >
143132 {
144133 /// Create a single item
145134 public class Add : Command
@@ -166,38 +155,26 @@ public class Clear : Command
166155 }
167156
168157 /// Defines the decision process which maps from the intent of the `Command` to the `Event`s that represent that decision in the Stream
169- public static IEnumerable < Event > Interpret ( State s , Command x )
170- {
171- switch ( x )
172- {
173- case Add c :
174- yield return Make < Event . Added > ( s . NextId , c . Props ) ;
175- break ;
176- case Update c :
177- var proposed = new { c . Props . Order , c . Props . Title , c . Props . Completed } ;
158+ public static IEnumerable < Event > Interpret ( State s , Command x ) =>
159+ x . Match (
160+ ( Add c ) => new Event [ ] { Make < Event . Added > ( s . NextId , c . Props ) } ,
161+ ( Update c ) =>
162+ {
163+ var proposed = new { c . Props . Order , c . Props . Title , c . Props . Completed } ;
178164
179165 bool IsEquivalent ( Event . ItemData i ) =>
180166 i . Id == c . Id
181- && new { i . Order , i . Title , i . Completed } == proposed ;
182-
183- if ( ! s . Items . Any ( IsEquivalent ) )
184- yield return Make < Event . Updated > ( c . Id , c . Props ) ;
185- break ;
186- case Delete c :
187- if ( s . Items . Any ( i => i . Id == c . Id ) )
188- yield return new Event . Deleted { Id = c . Id } ;
189- break ;
190- case Clear _:
191- if ( s . Items . Any ( ) ) yield return new Event . Cleared { NextId = s . NextId } ;
192- break ;
193-
194- default :
195- throw new ArgumentOutOfRangeException ( nameof ( x ) , x , "invalid" ) ;
196- }
167+ && new { i . Order , i . Title , i . Completed } == proposed ;
197168
198- T Make < T > ( int id , Props value ) where T : Event . ItemEvent , new ( ) =>
199- new T { Data = { Id = id , Order = value . Order , Title = value . Title , Completed = value . Completed } } ;
200- }
169+ if ( s . Items . Any ( IsEquivalent ) )
170+ return Enumerable . Empty < Event > ( ) ;
171+ return new [ ] { Make < Event . Updated > ( c . Id , c . Props ) } ;
172+ } ,
173+ ( Delete c ) => s . Items . Any ( i => i . Id == c . Id ) ? new Event [ ] { new Event . Deleted { Id = c . Id } } : new Event [ 0 ] ,
174+ ( Clear _ ) => s . Items . Any ( ) ? new Event [ ] { new Event . Cleared { NextId = s . NextId } } : new Event [ 0 ] ) ;
175+
176+ static T Make < T > ( int id , Props value ) where T : Event . ItemEvent , new ( ) =>
177+ new T { Data = { Id = id , Order = value . Order , Title = value . Title , Completed = value . Completed } } ;
201178 }
202179
203180 /// Defines low level stream operations relevant to the Todo Stream in terms of Command and Events
@@ -279,19 +256,19 @@ public Task<Unit> Execute(ClientId clientId, Command command) =>
279256 /// Create a new ToDo List item; response contains the generated `id`
280257 public async Task < View > Create ( ClientId clientId , Props template )
281258 {
282- var state = await _stream ( clientId ) . Decide ( new Command . Add { Props = template } ) ;
259+ var state = await _stream ( clientId ) . Decide ( new Command . Add { Props = template } ) ;
283260 return Render ( state . First ( ) ) ;
284261 }
285262
286263 /// Update the specified item as referenced by the `item.id`
287264 public async Task < View > Patch ( ClientId clientId , int id , Props value )
288265 {
289- var state = await _stream ( clientId ) . Decide ( new Command . Update { Id = id , Props = value } ) ;
266+ var state = await _stream ( clientId ) . Decide ( new Command . Update { Id = id , Props = value } ) ;
290267 return Render ( state . Single ( x => x . Id == id ) ) ;
291268 }
292269
293270 static View Render ( Event . ItemData i ) =>
294- new View { Id = i . Id , Order = i . Order , Title = i . Title , Completed = i . Completed } ;
271+ new View { Id = i . Id , Order = i . Order , Title = i . Title , Completed = i . Completed } ;
295272 }
296273 }
297274}
0 commit comments