Improve complex data types using FastDict
parent
865e83cdae
commit
4777de5b67
3
elm.json
3
elm.json
|
@ -19,7 +19,8 @@
|
||||||
"elm/http": "2.0.0 <= v < 3.0.0",
|
"elm/http": "2.0.0 <= v < 3.0.0",
|
||||||
"elm/json": "1.0.0 <= v < 2.0.0",
|
"elm/json": "1.0.0 <= v < 2.0.0",
|
||||||
"elm/time": "1.0.0 <= v < 2.0.0",
|
"elm/time": "1.0.0 <= v < 2.0.0",
|
||||||
"elm/url": "1.0.0 <= v < 2.0.0"
|
"elm/url": "1.0.0 <= v < 2.0.0",
|
||||||
|
"miniBill/elm-fast-dict": "1.0.0 <= v < 2.0.0"
|
||||||
},
|
},
|
||||||
"test-dependencies": {}
|
"test-dependencies": {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
module Internal.Tools.DefaultDict exposing (..)
|
||||||
|
|
||||||
|
import FastDict as Dict exposing (Dict)
|
||||||
|
|
||||||
|
{-| A dictionary of keys and values that includes a default when a key doesn't exist.
|
||||||
|
-}
|
||||||
|
type DefaultDict k v
|
||||||
|
= DefaultDict
|
||||||
|
{ content : Dict k v
|
||||||
|
, default : v
|
||||||
|
}
|
||||||
|
|
||||||
|
{-| Create an empty dictionary that has a default value.
|
||||||
|
-}
|
||||||
|
empty : v -> DefaultDict k v
|
||||||
|
empty v =
|
||||||
|
DefaultDict
|
||||||
|
{ content = Dict.empty
|
||||||
|
, default = v
|
||||||
|
}
|
||||||
|
|
||||||
|
{-| Get the value associated with the key. Uses the default if not found. -}
|
||||||
|
get : comparable -> DefaultDict comparable v -> v
|
||||||
|
get k (DefaultDict data) =
|
||||||
|
Dict.get k data.content |> Maybe.withDefault data.default
|
||||||
|
|
||||||
|
{-| Insert a key-value pair into a dictionary with a default.
|
||||||
|
-}
|
||||||
|
insert : comparable -> v -> DefaultDict comparable v -> DefaultDict comparable v
|
||||||
|
insert k v (DefaultDict data) =
|
||||||
|
DefaultDict { data | content = Dict.insert k v data.content }
|
||||||
|
|
||||||
|
{-| "Remove" a value by making its value synchronize with the default value.
|
||||||
|
-}
|
||||||
|
remove : comparable -> DefaultDict comparable v -> DefaultDict comparable v
|
||||||
|
remove k (DefaultDict data) =
|
||||||
|
DefaultDict { data | content = Dict.remove k data.content }
|
||||||
|
|
||||||
|
{-| Update the default value of all unset keys.
|
||||||
|
-}
|
||||||
|
setDefault : v -> DefaultDict k v -> DefaultDict k v
|
||||||
|
setDefault v (DefaultDict data) =
|
||||||
|
DefaultDict { data | default = v }
|
||||||
|
|
||||||
|
{-| Update the value of a dictionary. The returned (or received) value is `Nothing`,
|
||||||
|
it means the key synchronizes with the default value.
|
||||||
|
-}
|
||||||
|
update : comparable -> (Maybe v -> Maybe v) -> DefaultDict comparable v -> DefaultDict comparable v
|
||||||
|
update k fv (DefaultDict data) =
|
||||||
|
DefaultDict { data | content = Dict.update k fv data.content }
|
||||||
|
|
||||||
|
{-| Update the default value.
|
||||||
|
-}
|
||||||
|
updateDefault : (v -> v) -> DefaultDict k v -> DefaultDict k v
|
||||||
|
updateDefault f (DefaultDict data) =
|
||||||
|
DefaultDict { data | default = f data.default }
|
|
@ -6,8 +6,10 @@ For example, this is used to store events by their event id, or store rooms by t
|
||||||
|
|
||||||
-}
|
-}
|
||||||
|
|
||||||
import Dict exposing (Dict)
|
import FastDict as Dict exposing (Dict)
|
||||||
|
import Json.Decode as D
|
||||||
|
import Json.Encode as E
|
||||||
|
import Hash exposing (Hash)
|
||||||
|
|
||||||
type Hashdict a
|
type Hashdict a
|
||||||
= Hashdict
|
= Hashdict
|
||||||
|
@ -20,6 +22,12 @@ empty : (a -> String) -> Hashdict a
|
||||||
empty hash =
|
empty hash =
|
||||||
Hashdict { hash = hash, values = Dict.empty }
|
Hashdict { hash = hash, values = Dict.empty }
|
||||||
|
|
||||||
|
encode : Hashdict E.Value -> E.Value
|
||||||
|
encode (Hashdict h) =
|
||||||
|
h.values
|
||||||
|
|> Dict.toList
|
||||||
|
|> E.object
|
||||||
|
|
||||||
|
|
||||||
fromList : (a -> String) -> List a -> Hashdict a
|
fromList : (a -> String) -> List a -> Hashdict a
|
||||||
fromList hash xs =
|
fromList hash xs =
|
||||||
|
@ -46,6 +54,9 @@ keys : Hashdict a -> List String
|
||||||
keys (Hashdict h) =
|
keys (Hashdict h) =
|
||||||
Dict.keys h.values
|
Dict.keys h.values
|
||||||
|
|
||||||
|
toList : Hashdict a -> List (String, a)
|
||||||
|
toList (Hashdict h) =
|
||||||
|
Dict.toList h.values
|
||||||
|
|
||||||
union : Hashdict a -> Hashdict a -> Hashdict a
|
union : Hashdict a -> Hashdict a -> Hashdict a
|
||||||
union (Hashdict h1) (Hashdict h2) =
|
union (Hashdict h1) (Hashdict h2) =
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
module Internal.Tools.Iddict exposing (..)
|
||||||
|
{-| The id-dict stores values and gives them a unique id.
|
||||||
|
-}
|
||||||
|
|
||||||
|
import FastDict as Dict exposing (Dict)
|
||||||
|
|
||||||
|
type Iddict a
|
||||||
|
= Iddict
|
||||||
|
{ cursor : Int
|
||||||
|
, dict : Dict Int a
|
||||||
|
}
|
||||||
|
|
||||||
|
empty : Iddict a
|
||||||
|
empty =
|
||||||
|
Iddict
|
||||||
|
{ cursor = 0
|
||||||
|
, dict = Dict.empty
|
||||||
|
}
|
||||||
|
|
||||||
|
get : Int -> Iddict a -> Maybe a
|
||||||
|
get k (Iddict { dict }) =
|
||||||
|
Dict.get k dict
|
||||||
|
|
||||||
|
insert : a -> Iddict a -> (Int, Iddict a)
|
||||||
|
insert v (Iddict d) =
|
||||||
|
( d.cursor
|
||||||
|
, Iddict { cursor = d.cursor + 1, dict = Dict.insert d.cursor v d.dict }
|
||||||
|
)
|
||||||
|
|
||||||
|
keys : Iddict a -> List Int
|
||||||
|
keys (Iddict { dict }) =
|
||||||
|
Dict.keys dict
|
||||||
|
|
||||||
|
remove : Int -> Iddict a -> Iddict a
|
||||||
|
remove k (Iddict d) =
|
||||||
|
Iddict { d | dict = Dict.remove k d.dict }
|
||||||
|
|
||||||
|
values : Iddict a -> List a
|
||||||
|
values (Iddict { dict }) =
|
||||||
|
Dict.values dict
|
|
@ -1,23 +1,113 @@
|
||||||
module Internal.Values.Timeline exposing (..)
|
module Internal.Values.Timeline exposing (..)
|
||||||
|
{-| The Timeline can be very complex, and it can be represented in surprisingly
|
||||||
{-| This module shapes the Timeline type used to keep track of timelines in Matrix rooms.
|
complex manners. This module aims to provide one single Timeline type that
|
||||||
|
accepts the complex pieces of information from the API and contain it all in
|
||||||
|
a simple way to view events.
|
||||||
-}
|
-}
|
||||||
|
|
||||||
|
import FastDict as Dict exposing (Dict)
|
||||||
import Internal.Config.Leaking as Leaking
|
import Internal.Config.Leaking as Leaking
|
||||||
import Internal.Tools.Fold as Fold
|
import Internal.Tools.Fold as Fold
|
||||||
import Internal.Values.Event as Event exposing (IEvent)
|
import Internal.Values.Event as Event exposing (IEvent)
|
||||||
import Internal.Values.StateManager as StateManager exposing (StateManager)
|
import Internal.Values.StateManager as StateManager exposing (StateManager)
|
||||||
|
import Internal.Tools.DefaultDict as DefaultDict exposing (DefaultDict)
|
||||||
|
import Internal.Tools.Hashdict as Hashdict exposing (Hashdict)
|
||||||
|
import Internal.Tools.Iddict as Iddict exposing (Iddict)
|
||||||
|
import Internal.Tools.Filters.Main as Filter exposing (Filter)
|
||||||
|
import Internal.Config.Leaking exposing (nextBatch)
|
||||||
|
|
||||||
|
{-| The Timeline is a comprehensive object describing a timeline in a room.
|
||||||
|
|
||||||
|
Any Timeline type contains the following pieces of information:
|
||||||
|
|
||||||
|
- `events` Comprehensive dictionary containing all locally stored timeline events
|
||||||
|
- `batches` Comprehensive dictionary containing all batches. Batches are pieces
|
||||||
|
of the timeline that have been sent by the homeserver.
|
||||||
|
- `token` Dictionary that maps for each batch token which batches it borders
|
||||||
|
- `mostRecentSync` Id of the most "recent" batch in the timeline
|
||||||
|
-}
|
||||||
type Timeline
|
type Timeline
|
||||||
= Timeline
|
= Timeline
|
||||||
{ prevBatch : String
|
{ events : Hashdict IEvent
|
||||||
, nextBatch : String
|
, batches : Iddict TimelineBatch
|
||||||
, events : List IEvent
|
, token : DefaultDict String (List Int)
|
||||||
, stateAtStart : StateManager
|
, mostRecentSync : Maybe Int
|
||||||
, previous : BeforeTimeline
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{-| A BatchToken is a token that has been handed out by the server to mark the end of a -}
|
||||||
|
type alias BatchToken = String
|
||||||
|
|
||||||
|
type alias TimelineBatch =
|
||||||
|
{ prevBatch : List Batch
|
||||||
|
, nextBatch : List Batch
|
||||||
|
, filter : Filter
|
||||||
|
, events : List String
|
||||||
|
, stateDelta : StateManager
|
||||||
|
}
|
||||||
|
|
||||||
|
type Batch
|
||||||
|
= Token BatchToken
|
||||||
|
| Batch Int
|
||||||
|
|
||||||
|
addNewSync :
|
||||||
|
{ events : List IEvent
|
||||||
|
, filter : Filter
|
||||||
|
, limited : Bool
|
||||||
|
, nextBatch : String
|
||||||
|
, prevBatch : String
|
||||||
|
, stateDelta : Maybe StateManager
|
||||||
|
} -> Timeline -> Timeline
|
||||||
|
addNewSync data (Timeline timeline) =
|
||||||
|
let
|
||||||
|
batchToInsert : TimelineBatch
|
||||||
|
batchToInsert =
|
||||||
|
{ prevBatch =
|
||||||
|
[ Just <| Token data.prevBatch
|
||||||
|
, Maybe.map Batch timeline.mostRecentSync
|
||||||
|
]
|
||||||
|
|> List.filterMap identity
|
||||||
|
, nextBatch =
|
||||||
|
[ Token data.nextBatch ]
|
||||||
|
, filter = data.filter
|
||||||
|
, events = List.map Event.eventId data.events
|
||||||
|
, stateDelta = Maybe.withDefault StateManager.empty data.stateDelta
|
||||||
|
}
|
||||||
|
in
|
||||||
|
case Iddict.insert batchToInsert timeline.batches of
|
||||||
|
( batchId, batches ) ->
|
||||||
|
Timeline
|
||||||
|
{ events = List.foldl Hashdict.insert timeline.events data.events
|
||||||
|
, batches = batches
|
||||||
|
, mostRecentSync = Just batchId
|
||||||
|
, token =
|
||||||
|
timeline.token
|
||||||
|
|> DefaultDict.update data.prevBatch
|
||||||
|
(\value ->
|
||||||
|
case value of
|
||||||
|
Just v ->
|
||||||
|
Just (batchId :: v)
|
||||||
|
Nothing ->
|
||||||
|
Just [ batchId ]
|
||||||
|
)
|
||||||
|
|> DefaultDict.update data.nextBatch
|
||||||
|
(\value ->
|
||||||
|
case value of
|
||||||
|
Just v ->
|
||||||
|
Just (batchId :: v)
|
||||||
|
Nothing ->
|
||||||
|
Just [ batchId ]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
-- type Timeline
|
||||||
|
-- = Timeline
|
||||||
|
-- { prevBatch : String
|
||||||
|
-- , nextBatch : String
|
||||||
|
-- , events : List IEvent
|
||||||
|
-- , stateAtStart : StateManager
|
||||||
|
-- , previous : BeforeTimeline
|
||||||
|
-- }
|
||||||
|
|
||||||
|
|
||||||
type BeforeTimeline
|
type BeforeTimeline
|
||||||
= Endless String
|
= Endless String
|
||||||
|
|
|
@ -32,10 +32,8 @@ Once you have the event you want, you can explore it with the following function
|
||||||
|
|
||||||
import Internal.Api.VaultUpdate exposing (VaultUpdate)
|
import Internal.Api.VaultUpdate exposing (VaultUpdate)
|
||||||
import Internal.Invite as Internal
|
import Internal.Invite as Internal
|
||||||
import Internal.Tools.Exceptions as X
|
|
||||||
import Internal.Values.RoomInvite as IR
|
import Internal.Values.RoomInvite as IR
|
||||||
import Json.Encode as E
|
import Json.Encode as E
|
||||||
import Task exposing (Task)
|
|
||||||
|
|
||||||
|
|
||||||
{-| The `RoomInvite` type serves as an invite to a given room.
|
{-| The `RoomInvite` type serves as an invite to a given room.
|
||||||
|
|
Loading…
Reference in New Issue