diff --git a/elm.json b/elm.json index 5cb6da1..214e084 100644 --- a/elm.json +++ b/elm.json @@ -10,12 +10,14 @@ "Internal.Config.Log", "Internal.Config.Phantom", "Internal.Config.Text", + "Internal.Filter.Timeline", "Internal.Tools.DecodeExtra", "Internal.Tools.EncodeExtra", "Internal.Tools.Hashdict", "Internal.Tools.Iddict", "Internal.Tools.Json", "Internal.Tools.Mashdict", + "Internal.Tools.RationalOrder", "Internal.Tools.Timestamp", "Internal.Tools.VersionControl", "Internal.Values.Context", @@ -27,7 +29,8 @@ "Internal.Values.Vault", "Matrix", "Matrix.Event", - "Matrix.Settings" + "Matrix.Settings", + "Types" ], "elm-version": "0.19.0 <= v < 0.20.0", "dependencies": { diff --git a/src/Internal/Config/Text.elm b/src/Internal/Config/Text.elm index f3c6354..e2f4ed5 100644 --- a/src/Internal/Config/Text.elm +++ b/src/Internal/Config/Text.elm @@ -485,18 +485,16 @@ leakingValueFound leaking_value = "Found leaking value : " ++ leaking_value -{-| --} +{-| -} logs : { keyIsNotAnInt : String -> String } logs = { keyIsNotAnInt = - (\key -> + \key -> String.concat [ "Encountered a key `" , key , "` that cannot be converted to an Int" ] - ) } diff --git a/src/Internal/Filter/Timeline.elm b/src/Internal/Filter/Timeline.elm index 79875bf..a4231bf 100644 --- a/src/Internal/Filter/Timeline.elm +++ b/src/Internal/Filter/Timeline.elm @@ -161,52 +161,57 @@ and (Filter f1) (Filter f2) = else stdAnd + +{-| Define how to encode and decode a Timeline Filter to and from a JSON value. +-} coder : Json.Coder Filter coder = Json.object4 { name = Text.docs.timelineFilter.name , description = Text.docs.timelineFilter.description , init = - (\a b c d -> + \a b c d -> Filter - { senders = a, sendersAllowOthers = b - , types = c, typesAllowOthers = d + { senders = a + , sendersAllowOthers = b + , types = c + , typesAllowOthers = d } - ) } - ( Json.field.optional.withDefault + (Json.field.optional.withDefault { fieldName = "senders" - , toField = (\(Filter f) -> f.senders) + , toField = \(Filter f) -> f.senders , description = Text.fields.timelineFilter.senders , coder = Json.set Json.string , default = ( Set.empty, [] ) , defaultToString = always "[]" } ) - ( Json.field.required + (Json.field.required { fieldName = "sendersAllowOthers" - , toField = (\(Filter f) -> f.sendersAllowOthers) + , toField = \(Filter f) -> f.sendersAllowOthers , description = Text.fields.timelineFilter.sendersAllowOthers , coder = Json.bool } ) - ( Json.field.optional.withDefault + (Json.field.optional.withDefault { fieldName = "types" - , toField = (\(Filter f) -> f.types) + , toField = \(Filter f) -> f.types , description = Text.fields.timelineFilter.types , coder = Json.set Json.string , default = ( Set.empty, [] ) , defaultToString = always "[]" } ) - ( Json.field.required + (Json.field.required { fieldName = "typesAllowOthers" - , toField = (\(Filter f) -> f.typesAllowOthers) + , toField = \(Filter f) -> f.typesAllowOthers , description = Text.fields.timelineFilter.typesAllowOthers , coder = Json.bool } ) + {-| Decode a Filter from a JSON value. -} decoder : Json.Decoder Filter diff --git a/src/Internal/Tools/Iddict.elm b/src/Internal/Tools/Iddict.elm index 96efd31..da718f2 100644 --- a/src/Internal/Tools/Iddict.elm +++ b/src/Internal/Tools/Iddict.elm @@ -53,6 +53,7 @@ type Iddict a , dict : Dict Int a } + {-| Define how an Iddict can be encoded and decoded to and from a JSON value. -} coder : Json.Coder a -> Json.Coder (Iddict a) @@ -61,7 +62,7 @@ coder x = { name = Text.docs.iddict.name , description = Text.docs.iddict.description , init = - (\c d -> + \c d -> Iddict { cursor = Dict.keys d @@ -72,25 +73,25 @@ coder x = |> max c , dict = d } - ) } - ( Json.field.optional.withDefault + (Json.field.optional.withDefault { fieldName = "cursor" - , toField = (\(Iddict i) -> i.cursor) + , toField = \(Iddict i) -> i.cursor , description = Text.fields.iddict.cursor , coder = Json.int , default = ( 0, [] ) , defaultToString = String.fromInt } ) - ( Json.field.required + (Json.field.required { fieldName = "dict" - , toField = (\(Iddict i) -> i.dict) + , toField = \(Iddict i) -> i.dict , description = Text.fields.iddict.dict , coder = Json.fastIntDict x } ) + {-| Decode an id-dict from a JSON value. -} decoder : Json.Coder a -> Json.Decoder (Iddict a) diff --git a/src/Internal/Tools/Json.elm b/src/Internal/Tools/Json.elm index 8876ca5..246e29d 100644 --- a/src/Internal/Tools/Json.elm +++ b/src/Internal/Tools/Json.elm @@ -294,6 +294,7 @@ fastDict (Coder old) = , docs = DocsDict old.docs } + {-| Define a fast dict where the keys are integers, not strings. -} fastIntDict : Coder value -> Coder (FastDict.Dict Int value) @@ -308,8 +309,8 @@ fastIntDict (Coder old) = ( items |> List.map (Tuple.mapSecond Tuple.first) |> List.filterMap - (\(k, v) -> - Maybe.map (\a -> (a, v)) (String.toInt k) + (\( k, v ) -> + Maybe.map (\a -> ( a, v )) (String.toInt k) ) |> FastDict.fromList , List.concat @@ -320,7 +321,7 @@ fastIntDict (Coder old) = case String.toInt k of Just _ -> True - + Nothing -> False ) @@ -335,6 +336,7 @@ fastIntDict (Coder old) = , docs = DocsIntDict old.docs } + {-| Create a new field using any of the three provided options. For example, suppose we are creating a `Field String User` to represent the @@ -509,12 +511,13 @@ list (Coder old) = , docs = DocsList old.docs } + {-| Define a list that has at least one value -} -listWithOne : Coder a -> Coder (a, List a) +listWithOne : Coder a -> Coder ( a, List a ) listWithOne (Coder old) = Coder - { encoder = (\(h, t) -> E.list old.encoder (h :: t)) + { encoder = \( h, t ) -> E.list old.encoder (h :: t) , decoder = old.decoder |> D.list @@ -523,10 +526,10 @@ listWithOne (Coder old) = case items of [] -> D.fail "Expected at least one value in list" - - ( h, l1) :: t -> + + ( h, l1 ) :: t -> D.succeed - ( (h, List.map Tuple.first items) + ( ( h, List.map Tuple.first items ) , List.concatMap Tuple.second t |> List.append l1 ) @@ -1170,6 +1173,7 @@ set (Coder data) = , docs = DocsSet data.docs } + {-| Define a slow dict from the `elm/core` library. -} slowDict : Coder value -> Coder (SlowDict.Dict String value) diff --git a/src/Internal/Values/Timeline.elm b/src/Internal/Values/Timeline.elm index ddf2307..9bded76 100644 --- a/src/Internal/Values/Timeline.elm +++ b/src/Internal/Values/Timeline.elm @@ -2,7 +2,7 @@ module Internal.Values.Timeline exposing ( Batch, Timeline , empty, singleton , mostRecentEvents, mostRecentEventsFrom - , insert, addSync + , addSync, insert , coder, encode, decoder ) @@ -67,11 +67,11 @@ events! -} import FastDict as Dict exposing (Dict) +import Internal.Config.Text as Text import Internal.Filter.Timeline as Filter exposing (Filter) import Internal.Tools.Hashdict as Hashdict exposing (Hashdict) import Internal.Tools.Iddict as Iddict exposing (Iddict) import Internal.Tools.Json as Json -import Internal.Config.Text as Text import Recursion import Recursion.Traverse import Set exposing (Set) @@ -82,6 +82,7 @@ that require an insertion, generally require this data type. If the `start` value is `Nothing`, it is either the start of the timeline or the start of the timeline part that the user is allowed to view. + -} type alias Batch = { events : List String @@ -167,22 +168,26 @@ type Timeline type alias TokenValue = String + {-| Add a new batch as a sync -} addSync : Batch -> Timeline -> Timeline addSync batch timeline = case insertBatch batch timeline of - ( Timeline t, { start, end }) -> + ( Timeline t, { start, end } ) -> let old : ITokenPTR - old = t.mostRecentBatch + old = + t.mostRecentBatch in - case Timeline { t | mostRecentBatch = end } of - tl -> - if old == start then - tl - else - connectITokenToIToken old start tl + case Timeline { t | mostRecentBatch = end } of + tl -> + if old == start then + tl + + else + connectITokenToIToken old start tl + {-| Define how a Timeline can be encoded and decoded to and from a JSON value. -} @@ -192,51 +197,54 @@ coder = { name = Text.docs.timeline.name , description = Text.docs.timeline.description , init = - (\a b c d e -> + \a b c d e -> Timeline - { batches = a, events = b, filledBatches = c - , mostRecentBatch = d, tokens = e + { batches = a + , events = b + , filledBatches = c + , mostRecentBatch = d + , tokens = e } - ) } - ( Json.field.required + (Json.field.required { fieldName = "batches" - , toField = (\(Timeline t) -> t.batches) + , toField = \(Timeline t) -> t.batches , description = Text.fields.timeline.batches , coder = Iddict.coder coderIBatch } ) - ( Json.field.required + (Json.field.required { fieldName = "events" - , toField = (\(Timeline t) -> t.events) + , toField = \(Timeline t) -> t.events , description = Text.fields.timeline.events , coder = Json.fastDict (Json.listWithOne coderIBatchPTR) } ) - ( Json.field.optional.withDefault + (Json.field.optional.withDefault { fieldName = "filledBatches" - , toField = (\(Timeline t) -> t.filledBatches) + , toField = \(Timeline t) -> t.filledBatches , description = Text.fields.timeline.filledBatches , coder = Json.int , default = ( 0, [] ) , defaultToString = String.fromInt } ) - ( Json.field.required + (Json.field.required { fieldName = "mostRecentBatch" - , toField = (\(Timeline t) -> t.mostRecentBatch) + , toField = \(Timeline t) -> t.mostRecentBatch , description = Text.fields.timeline.mostRecentBatch , coder = coderITokenPTR } ) - ( Json.field.required + (Json.field.required { fieldName = "tokens" - , toField = (\(Timeline t) -> t.tokens) + , toField = \(Timeline t) -> t.tokens , description = Text.fields.timeline.tokens , coder = Hashdict.coder .name coderIToken } ) + {-| Define how to encode and decode a IBatch to and from a JSON value. -} coderIBatch : Json.Coder IBatch @@ -246,28 +254,28 @@ coderIBatch = , description = Text.docs.ibatch.description , init = IBatch } - ( Json.field.required + (Json.field.required { fieldName = "events" , toField = .events , description = Text.fields.ibatch.events , coder = Json.list Json.string } ) - ( Json.field.required + (Json.field.required { fieldName = "filter" , toField = .filter , description = Text.fields.ibatch.filter , coder = Filter.coder } ) - ( Json.field.required + (Json.field.required { fieldName = "start" , toField = .start , description = Text.fields.ibatch.start , coder = coderITokenPTR } ) - ( Json.field.required + (Json.field.required { fieldName = "end" , toField = .end , description = Text.fields.ibatch.end @@ -275,6 +283,7 @@ coderIBatch = } ) + {-| Define how to encode and decode a IBatchPTR to and from a JSON value. -} coderIBatchPTR : Json.Coder IBatchPTR @@ -282,15 +291,18 @@ coderIBatchPTR = Json.map { name = Text.docs.itoken.name , description = Text.docs.itoken.description - , back = (\(IBatchPTR value) -> value) + , back = \(IBatchPTR value) -> value , forth = IBatchPTR } coderIBatchPTRValue + {-| Define how to encode and decode a IBatchPTRValue to and from a JSON value. -} coderIBatchPTRValue : Json.Coder IBatchPTRValue -coderIBatchPTRValue = Json.int +coderIBatchPTRValue = + Json.int + {-| Define how to encode and decode a IToken to and from a JSON value. -} @@ -301,14 +313,14 @@ coderIToken = , description = Text.docs.itoken.description , init = IToken } - ( Json.field.required + (Json.field.required { fieldName = "name" , toField = .name , description = Text.fields.itoken.name , coder = coderTokenValue } ) - ( Json.field.optional.withDefault + (Json.field.optional.withDefault { fieldName = "starts" , toField = .starts , description = Text.fields.itoken.starts @@ -317,7 +329,7 @@ coderIToken = , defaultToString = always "[]" } ) - ( Json.field.optional.withDefault + (Json.field.optional.withDefault { fieldName = "ends" , toField = .ends , description = Text.fields.itoken.ends @@ -326,7 +338,7 @@ coderIToken = , defaultToString = always "[]" } ) - ( Json.field.optional.withDefault + (Json.field.optional.withDefault { fieldName = "inFrontOf" , toField = .inFrontOf , description = Text.fields.itoken.inFrontOf @@ -335,7 +347,7 @@ coderIToken = , defaultToString = always "[]" } ) - ( Json.field.optional.withDefault + (Json.field.optional.withDefault { fieldName = "behind" , toField = .behind , description = Text.fields.itoken.behind @@ -345,6 +357,7 @@ coderIToken = } ) + {-| Define how to encode and decode a ITokenPTR to and from a JSON value. -} coderITokenPTR : Json.Coder ITokenPTR @@ -354,34 +367,37 @@ coderITokenPTR = { name = Text.mappings.itokenPTR.name , description = Text.mappings.itokenPTR.description , back = - (\itokenptr -> + \itokenptr -> case itokenptr of ITokenPTR name -> Just name - + StartOfTimeline -> Nothing - ) , forth = - (\value -> + \value -> case value of Just name -> ITokenPTR name - + Nothing -> StartOfTimeline - ) } + {-| Define how to encode and decode a ITokenPTRValue to and from a JSON value. -} coderITokenPTRValue : Json.Coder ITokenPTRValue -coderITokenPTRValue = Json.string +coderITokenPTRValue = + Json.string + {-| Define how to encode and decode a TokenValue to and from a JSON value. -} coderTokenValue : Json.Coder TokenValue -coderTokenValue = Json.string +coderTokenValue = + Json.string + {-| Append a token at the end of a batch. -} @@ -454,10 +470,13 @@ connectITokenToIToken pointer1 pointer2 (Timeline tl) = ( _, _ ) -> Timeline tl + {-| Timeline JSON decoder that helps decode a Timeline from JSON. -} decoder : Json.Decoder Timeline -decoder = Json.decode coder +decoder = + Json.decode coder + {-| Create a new empty timeline. -} @@ -471,10 +490,13 @@ empty = , tokens = Hashdict.empty .name } + {-| Directly encode a Timeline into a JSON value. -} encode : Json.Encoder Timeline -encode = Json.encode coder +encode = + Json.encode coder + {-| Get an IBatch from the Timeline. -} @@ -608,6 +630,7 @@ mostRecentEvents : Filter -> Timeline -> List (List String) mostRecentEvents filter (Timeline timeline) = mostRecentFrom filter (Timeline timeline) timeline.mostRecentBatch + {-| Instead of finding the most recent events from the latest sync, users can also find the most recent events given a token value. -} diff --git a/tests/Test/Filter/Timeline.elm b/tests/Test/Filter/Timeline.elm index 7022738..6e880e7 100644 --- a/tests/Test/Filter/Timeline.elm +++ b/tests/Test/Filter/Timeline.elm @@ -428,7 +428,7 @@ suite = |> Filter.encode |> E.encode 0 |> D.decodeString Filter.decoder - |> Expect.equal (Ok (filter, [])) + |> Expect.equal (Ok ( filter, [] )) ) ] ] diff --git a/tests/Test/Values/Timeline.elm b/tests/Test/Values/Timeline.elm index 2dca2d0..e1a9fff 100644 --- a/tests/Test/Values/Timeline.elm +++ b/tests/Test/Values/Timeline.elm @@ -3,11 +3,11 @@ module Test.Values.Timeline exposing (..) import Expect import Fuzz exposing (Fuzzer) import Internal.Filter.Timeline as Filter exposing (Filter) +import Internal.Tools.Json as Json import Internal.Values.Timeline as Timeline exposing (Batch, Timeline) import Json.Decode as D import Test exposing (..) import Test.Filter.Timeline as TestFilter -import Internal.Tools.Json as Json fuzzer : Fuzzer Timeline @@ -216,9 +216,11 @@ suite = |> Timeline.mostRecentEventsFrom filter "token_4" |> Expect.equal [ [ "d", "e", "f" ] ] ) - , fuzz3 TestFilter.fuzzer (Fuzz.list Fuzz.string) (Fuzz.pair (Fuzz.list Fuzz.string) (Fuzz.list Fuzz.string)) + , fuzz3 TestFilter.fuzzer + (Fuzz.list Fuzz.string) + (Fuzz.pair (Fuzz.list Fuzz.string) (Fuzz.list Fuzz.string)) "Gaps can be bridged" - (\filter l1 (l2, l3) -> + (\filter l1 ( l2, l3 ) -> Timeline.empty |> Timeline.insert { events = l1 @@ -243,7 +245,8 @@ suite = ) ] , describe "JSON" - [ fuzz fuzzer "Encode + Decode gives same output" + [ fuzz fuzzer + "Encode + Decode gives same output" (\timeline -> timeline |> Json.encode Timeline.coder @@ -254,7 +257,8 @@ suite = ) ] , describe "Weird loops" - [ fuzz TestFilter.fuzzer "Weird loops stop looping" + [ fuzz TestFilter.fuzzer + "Weird loops stop looping" (\filter -> Timeline.empty |> Timeline.insert @@ -283,7 +287,8 @@ suite = ) ] , describe "Sync" - [ fuzz TestFilter.fuzzer "Sync fills gaps" + [ fuzz TestFilter.fuzzer + "Sync fills gaps" (\filter -> Timeline.empty |> Timeline.addSync @@ -293,7 +298,7 @@ suite = , end = "token_2" } |> Timeline.addSync - { events = [ "f", "g", "h"] + { events = [ "f", "g", "h" ] , filter = filter , start = Just "token_3" , end = "token_4" @@ -305,9 +310,10 @@ suite = , end = "token_3" } |> Timeline.mostRecentEvents filter - |> Expect.equal [ [ "a", "b", "c", "d", "e", "f", "g", "h" ]] + |> Expect.equal [ [ "a", "b", "c", "d", "e", "f", "g", "h" ] ] ) - , fuzz TestFilter.fuzzer "Sync doesn't fill open gaps" + , fuzz TestFilter.fuzzer + "Sync doesn't fill open gaps" (\filter -> Timeline.empty |> Timeline.addSync @@ -317,31 +323,36 @@ suite = , end = "token_2" } |> Timeline.addSync - { events = [ "f", "g", "h"] + { events = [ "f", "g", "h" ] , filter = filter , start = Just "token_3" , end = "token_4" } |> Timeline.mostRecentEvents filter - |> Expect.equal [ [ "f", "g", "h" ]] + |> Expect.equal [ [ "f", "g", "h" ] ] ) - , fuzz3 (Fuzz.pair Fuzz.string Fuzz.string) fuzzer TestFilter.fuzzer "Getting /sync is the same as getting from the token" - (\(start, end) timeline filter -> + , fuzz3 (Fuzz.pair Fuzz.string Fuzz.string) + fuzzer + TestFilter.fuzzer + "Getting /sync is the same as getting from the token" + (\( start, end ) timeline filter -> let t : Timeline - t = Timeline.addSync - { events = [ "a", "b", "c" ] - , filter = filter - , start = Just start - , end = end - } - timeline + t = + Timeline.addSync + { events = [ "a", "b", "c" ] + , filter = filter + , start = Just start + , end = end + } + timeline in - Expect.equal - (Timeline.mostRecentEvents filter t) - (Timeline.mostRecentEventsFrom filter end t) + Expect.equal + (Timeline.mostRecentEvents filter t) + (Timeline.mostRecentEventsFrom filter end t) ) - , fuzz TestFilter.fuzzer "Weird loops stop looping" + , fuzz TestFilter.fuzzer + "Weird loops stop looping" (\filter -> Timeline.empty |> Timeline.insert