From 4b92d9ea20eb0eb98bbf3abc4e621e5b30caced9 Mon Sep 17 00:00:00 2001 From: Bram van den Heuvel Date: Fri, 3 Nov 2023 22:45:09 +0100 Subject: [PATCH] Add new filter + VaultResult type --- src/Internal/Tools/Filters/Filter.elm | 384 ++++++++++++++++++++ src/Internal/Tools/Filters/Main.elm | 85 +++++ src/Internal/Tools/Filters/SimpleFilter.elm | 172 +++++++++ src/Internal/Tools/Filters/SpecObjects.elm | 246 +++++++++++++ src/Internal/Tools/Filters/SpecObjects.yaml | 135 +++++++ src/Internal/Tools/VaultResult.elm | 14 + 6 files changed, 1036 insertions(+) create mode 100644 src/Internal/Tools/Filters/Filter.elm create mode 100644 src/Internal/Tools/Filters/Main.elm create mode 100644 src/Internal/Tools/Filters/SimpleFilter.elm create mode 100644 src/Internal/Tools/Filters/SpecObjects.elm create mode 100644 src/Internal/Tools/Filters/SpecObjects.yaml create mode 100644 src/Internal/Tools/VaultResult.elm diff --git a/src/Internal/Tools/Filters/Filter.elm b/src/Internal/Tools/Filters/Filter.elm new file mode 100644 index 0000000..0a4e4b9 --- /dev/null +++ b/src/Internal/Tools/Filters/Filter.elm @@ -0,0 +1,384 @@ +module Internal.Tools.Filters.Filter exposing (..) + +import Internal.Tools.Filters.SpecObjects as SO +import Internal.Tools.Filters.SimpleFilter as SF exposing (SimpleFilter) +import Internal.Tools.SpecEnums as Enums + +{-| Event filters tell the API what events to look for, +but specifically for events that are unrelated to any room. +-} +type EventFilter + = EventFilter + { limit : Maybe Int + , senders : SimpleFilter String + , types : SimpleFilter String + } + +{-| The final type dictates how everything else behaves. +-} +type Filter = + Filter + { accountData : EventFilter + , presence : EventFilter + , room : RoomFilter + } + +{-| RoomFilter types tell the API what is considered relevant in a room, +and which rooms to include. +-} +type RoomFilter + = RoomFilter + { accountData : RoomEventFilter + , ephemeral : RoomEventFilter + , rooms : SimpleFilter String + , timeline : RoomEventFilter + } + +{-| RoomEventFilter types tell the API what events to look for, +and what ones to ignore. +-} +type RoomEventFilter + = RoomEventFilter + { lazyLoadMembers : Bool + , limit : Maybe Int + , rooms : SimpleFilter String + , senders : SimpleFilter String + , types : SimpleFilter String + } + +allEvents : EventFilter +allEvents = + EventFilter + { limit = Nothing + , senders = SF.all + , types = SF.all + } + +allFilters : Filter +allFilters = + Filter + { accountData = allEvents + , presence = allEvents + , room = allRooms + } + +allRooms : RoomFilter +allRooms = + RoomFilter + { accountData = allRoomEvents + , ephemeral = allRoomEvents + , rooms = SF.all + , timeline = allRoomEvents + } + +allRoomEvents : RoomEventFilter +allRoomEvents = + RoomEventFilter + { lazyLoadMembers = False + , limit = Nothing + , rooms = SF.all + , senders = SF.all + , types = SF.all + } + +decodeEventFilter : SO.EventFilter -> EventFilter +decodeEventFilter data = + EventFilter + { limit = data.limit + , senders = SF.toSimpleFilter data.senders data.notSenders + , types = SF.toSimpleFilter data.types data.notTypes + } + +decodeFilter : SO.Filter -> Filter +decodeFilter data = + Filter + { accountData = + data.accountData + |> Maybe.map decodeEventFilter + |> Maybe.withDefault allEvents + , presence = + data.presence + |> Maybe.map decodeEventFilter + |> Maybe.withDefault allEvents + , room = + data.room + |> Maybe.map decodeRoomFilter + |> Maybe.withDefault allRooms + } + +{-| Decode a RoomFilter from a spec-compliant format. +-} +decodeRoomFilter : SO.RoomFilter -> RoomFilter +decodeRoomFilter data = + let + decodeREF : Maybe SO.RoomEventFilter -> RoomEventFilter + decodeREF = + Maybe.map decodeRoomEventFilter >> Maybe.withDefault allRoomEvents + in + RoomFilter + { accountData = decodeREF data.accountData + , ephemeral = decodeREF data.ephemeral + , rooms = SF.toSimpleFilter data.rooms data.notRooms + , timeline = decodeREF data.timeline + } + +{-| Decode a RoomEventFilter from a spec-compliant format. +-} +decodeRoomEventFilter : SO.RoomEventFilter -> RoomEventFilter +decodeRoomEventFilter data = + RoomEventFilter + { lazyLoadMembers = data.lazyLoadMembers + , limit = data.limit + , rooms = SF.toSimpleFilter data.rooms data.notRooms + , senders = SF.toSimpleFilter data.senders data.notSenders + , types = SF.toSimpleFilter data.types data.notTypes + } + +{-| Encode an EventFilter into a spec-compliant format. +-} +encodeEventFilter : EventFilter -> SO.EventFilter +encodeEventFilter (EventFilter data) = + { limit = data.limit + , notSenders = SF.toExclude data.senders + , notTypes = SF.toExclude data.types + , senders = SF.toInclude data.senders + , types = SF.toInclude data.types + } + +{-| Encode a Filter into a spec-compliant format. +-} +encodeFilter : Filter -> SO.Filter +encodeFilter (Filter data) = + { accountData = Just <| encodeEventFilter data.accountData + , eventFields = Nothing + , eventFormat = Enums.Client + , presence = Just <| encodeEventFilter data.presence + , room = Just <| encodeRoomFilter data.room + } + +{-| Encode a RoomFilter into a spec-compliant format. +-} +encodeRoomFilter : RoomFilter -> SO.RoomFilter +encodeRoomFilter (RoomFilter data) = + { accountData = Just <| encodeRoomEventFilter data.accountData + , ephemeral = Just <| encodeRoomEventFilter data.ephemeral + , includeLeave = False + , notRooms = SF.toExclude data.rooms + , rooms = SF.toInclude data.rooms + , state = Just <| encodeRoomEventFilter data.timeline + , timeline = Just <| encodeRoomEventFilter data.timeline + } + +{-| Encode a RoomEventFilter into a spec-compliant format. +-} +encodeRoomEventFilter : RoomEventFilter -> SO.RoomEventFilter +encodeRoomEventFilter (RoomEventFilter data) = + { containsUrl = Nothing + , includeRedundantMembers = False + , lazyLoadMembers = data.lazyLoadMembers + , limit = data.limit + , notRooms = SF.toExclude data.rooms + , notSenders = SF.toExclude data.senders + , notTypes = SF.toExclude data.types + , rooms = SF.toInclude data.rooms + , senders = SF.toInclude data.senders + , types = SF.toInclude data.types + , unreadThreadNotifications = True + } + +{-| Flatten a filter. +-} +flattenFilter : Filter -> List (SimpleFilter String) +flattenFilter (Filter f) = + List.concat + [ flattenEventFilter f.accountData + , flattenEventFilter f.presence + , flattenRoomFilter f.room + ] + +{-| Flatten a EventFilter. +-} +flattenEventFilter : EventFilter -> List (SimpleFilter String) +flattenEventFilter (EventFilter f) = [ f.senders, f.types ] + +{-| Flatten a RoomFilter. +-} +flattenRoomFilter : RoomFilter -> List (SimpleFilter String) +flattenRoomFilter (RoomFilter f) = + [ f.accountData, f.ephemeral, f.timeline ] + |> List.map flattenRoomEventFilter + |> List.concat + |> (::) f.rooms + +{-| Flatten a RoomEventFilter. +-} +flattenRoomEventFilter : RoomEventFilter -> List (SimpleFilter String) +flattenRoomEventFilter (RoomEventFilter f) = [ f.rooms, f.senders, f.types ] + +{-| Get an intersection of a Filter. +-} +intersectFilter : Filter -> Filter -> Filter +intersectFilter (Filter f1) (Filter f2) = + Filter + { accountData = intersectEventFilter f1.accountData f2.accountData + , presence = intersectEventFilter f1.presence f2.presence + , room = intersectRoomFilter f1.room f2.room + } + +{-| Get an intersection of a EventFilter. +-} +intersectEventFilter : EventFilter -> EventFilter -> EventFilter +intersectEventFilter (EventFilter f1) (EventFilter f2) = + EventFilter + { limit = + case (f1.limit, f2.limit) of + (Just l1, Just l2) -> + Just (max l1 l2) + + (Just _, Nothing) -> + f1.limit + + (Nothing, Just _) -> + f2.limit + + (Nothing, Nothing) -> + Nothing + , senders = SF.intersect f1.senders f2.senders + , types = SF.intersect f1.types f2.types + } + +{-| Get an intersection of a RoomFilter. +-} +intersectRoomFilter : RoomFilter -> RoomFilter -> RoomFilter +intersectRoomFilter (RoomFilter f1) (RoomFilter f2) = + RoomFilter + { accountData = intersectRoomEventFilter f1.accountData f2.accountData + , ephemeral = intersectRoomEventFilter f1.ephemeral f2.ephemeral + , rooms = SF.intersect f1.rooms f2.rooms + , timeline = intersectRoomEventFilter f1.timeline f2.timeline + } + +{-| Get an intersection of a RoomEventFilter. +-} +intersectRoomEventFilter : RoomEventFilter -> RoomEventFilter -> RoomEventFilter +intersectRoomEventFilter (RoomEventFilter f1) (RoomEventFilter f2) = + RoomEventFilter + { lazyLoadMembers = f1.lazyLoadMembers && f2.lazyLoadMembers + , limit = + case (f1.limit, f2.limit) of + (Just l1, Just l2) -> + Just (max l1 l2) + + (Just _, Nothing) -> + f1.limit + + (Nothing, Just _) -> + f2.limit + + (Nothing, Nothing) -> + Nothing + , rooms = SF.intersect f1.rooms f2.rooms + , senders = SF.intersect f1.senders f2.senders + , types = SF.intersect f1.types f2.types + } + +{-| Check whether a filter is a subset of another filter. +-} +isSubSet : Filter -> Filter -> Bool +isSubSet f1 f2 = + let + isSame : List (SimpleFilter String) -> List (SimpleFilter String) -> Bool + isSame l1 l2 = + case (l1, l2) of + (h1 :: t1, h2 :: t2) -> + SF.subset h1 h2 && isSame t1 t2 + ([], []) -> + True + _ -> + False + in + isSame (flattenFilter f1) (flattenFilter f2) + +lazyLoadMembers : Bool -> RoomEventFilter -> RoomEventFilter +lazyLoadMembers b (RoomEventFilter data) = + RoomEventFilter { data | lazyLoadMembers = b } + +{-| Determine a limit for the amount of events. If no limit is given, the homeserver decides this limit for itself. +-} +setEventLimit : Maybe Int -> RoomEventFilter -> RoomEventFilter +setEventLimit i (RoomEventFilter data) = + RoomEventFilter { data | limit = i } + +{-| Include a specific event type. +-} +withEventType : String -> RoomEventFilter -> RoomEventFilter +withEventType x (RoomEventFilter ({ types } as data)) = + RoomEventFilter { data | types = SF.with x types } + +{-| Include all event types that haven't been explicitly mentioned. +-} +withOtherEventTypes : RoomEventFilter -> RoomEventFilter +withOtherEventTypes (RoomEventFilter ({ types } as data)) = + RoomEventFilter { data | types = SF.withOthers types } + +{-| Include all rooms that haven't been explicitly mentioned. +-} +withOtherRooms : RoomEventFilter -> RoomEventFilter +withOtherRooms (RoomEventFilter ({ rooms } as data)) = + RoomEventFilter { data | rooms = SF.withOthers rooms } + +{-| Include all senders that haven't been explicitly mentioned. +-} +withOtherSenders : RoomEventFilter -> RoomEventFilter +withOtherSenders (RoomEventFilter ({ senders } as data)) = + RoomEventFilter { data | senders = SF.withOthers senders } + +{-| Include a specific room. +-} +withRoom : String -> RoomEventFilter -> RoomEventFilter +withRoom x (RoomEventFilter ({ rooms } as data)) = + RoomEventFilter { data | rooms = SF.with x rooms } + +{-| Include a specific sender. +-} +withSender : String -> RoomEventFilter -> RoomEventFilter +withSender x (RoomEventFilter ({ senders } as data)) = + RoomEventFilter { data | senders = SF.with x senders } + +{-| Ignore a specific event type. +-} +withoutEventType : String -> RoomEventFilter -> RoomEventFilter +withoutEventType x (RoomEventFilter ({ types } as data)) = + RoomEventFilter { data | types = SF.without x types } + +{-| Ignore all rooms that haven't been explicitly mentioned. +-} +withoutOtherEventTypes : RoomEventFilter -> RoomEventFilter +withoutOtherEventTypes (RoomEventFilter ({ types } as data)) = + RoomEventFilter { data | types = SF.withoutOthers types } + +{-| Ignore all rooms that haven't been explicitly mentioned. +-} +withoutOtherRooms : RoomEventFilter -> RoomEventFilter +withoutOtherRooms (RoomEventFilter ({ rooms } as data)) = + RoomEventFilter { data | rooms = SF.withoutOthers rooms } + +{-| Ignore all senders that haven't been explicitly mentioned. +-} +withoutOtherSenders : RoomEventFilter -> RoomEventFilter +withoutOtherSenders (RoomEventFilter ({ senders } as data)) = + RoomEventFilter { data | senders = SF.withoutOthers senders } + +{-| Ignore a specific room. +-} +withoutRoom : String -> RoomEventFilter -> RoomEventFilter +withoutRoom x (RoomEventFilter ({ rooms } as data)) = + RoomEventFilter { data | rooms = SF.without x rooms } + +{-| Ignore a specific sender. +-} +withoutSender : String -> RoomEventFilter -> RoomEventFilter +withoutSender x (RoomEventFilter ({ senders } as data)) = + RoomEventFilter { data | senders = SF.without x senders } + + diff --git a/src/Internal/Tools/Filters/Main.elm b/src/Internal/Tools/Filters/Main.elm new file mode 100644 index 0000000..ed319e5 --- /dev/null +++ b/src/Internal/Tools/Filters/Main.elm @@ -0,0 +1,85 @@ +module Internal.Tools.Filters.Main exposing (..) +{-| This module contains the main functions used to get, manipulate and change +filters according to their needs. +-} + +import Internal.Tools.Filters.Filter as F +import Internal.Tools.Filters.SimpleFilter as SF + +type alias Filter = F.Filter + +type alias SimpleFilter = SF.SimpleFilter String + +{-| Filter that adds all occurrences by default, but leaves a few ones out. + +When provided with an empty list, the filter allows all types. +-} +allExcept : List String -> SimpleFilter +allExcept = + List.foldl SF.without SF.all + +{-| Filter that removes everything by default, but leaves a few ones in. + +When provided with an empty list, the filter allows nothing. +-} +only : List String -> SimpleFilter +only = + List.foldl SF.with SF.none + +fromSimpleFilter : + { accountDataTypes : SimpleFilter + , presence : { limit : Maybe Int, senders : SimpleFilter, types : SimpleFilter } + , ephemeral : { limit : Maybe Int, senders : SimpleFilter, types : SimpleFilter } + , roomIds : SimpleFilter + , lazyLoadMembers : Bool + , roomEvents : { limit : Maybe Int, senders : SimpleFilter, types : SimpleFilter } + } -> Filter +fromSimpleFilter data = + F.Filter + { accountData = + F.EventFilter + { limit = Nothing + , senders = SF.all + , types = data.accountDataTypes + } + , presence = + F.EventFilter + { limit = data.presence.limit + , senders = data.presence.senders + , types = data.presence.types + } + , room = + F.RoomFilter + { accountData = + F.RoomEventFilter + { lazyLoadMembers = data.lazyLoadMembers + , limit = Nothing + , rooms = data.roomIds + , senders = SF.all + , types = data.accountDataTypes + } + , ephemeral = + F.RoomEventFilter + { lazyLoadMembers = data.lazyLoadMembers + , limit = data.ephemeral.limit + , rooms = data.roomIds + , senders = data.ephemeral.senders + , types = data.ephemeral.types + } + , rooms = data.roomIds + , timeline = + F.RoomEventFilter + { lazyLoadMembers = data.lazyLoadMembers + , limit = data.roomEvents.limit + , rooms = data.roomIds + , senders = data.roomEvents.senders + , types = data.roomEvents.types + } + } + } + +{-| Get the intersection of two filters. +-} +intersect : Filter -> Filter -> Filter +intersect = + F.intersectFilter diff --git a/src/Internal/Tools/Filters/SimpleFilter.elm b/src/Internal/Tools/Filters/SimpleFilter.elm new file mode 100644 index 0000000..c57c7bd --- /dev/null +++ b/src/Internal/Tools/Filters/SimpleFilter.elm @@ -0,0 +1,172 @@ +module Internal.Tools.Filters.SimpleFilter exposing (..) +{-| The SimpleFilter tracks values that should or should not be monitored. +-} + +import Dict exposing (Dict) + +{-| SimpleFilter type that tracks items to include or exclude. +-} +type alias SimpleFilter a = + { specificOnes : Dict a Bool + , includeOthers : Bool + } + +all : SimpleFilter a +all = + { specificOnes = Dict.empty + , includeOthers = True + } + +{-| Use filter ones that are only available in the first filter. +-} +diff : SimpleFilter comparable -> SimpleFilter comparable -> SimpleFilter comparable +diff f1 f2 = + { specificOnes = + Dict.merge + (\k v1 -> Dict.insert k (v1 && not f2.includeOthers)) + (\k v1 v2 -> Dict.insert k (v1 && not v2)) + (\k v2 -> Dict.insert k (f1.includeOthers && not v2)) + f1.specificOnes + f2.specificOnes + Dict.empty + , includeOthers = f1.includeOthers && not f2.includeOthers + } + +{-| Form a filter that only shows the values that two filters have in common. +-} +intersect : SimpleFilter comparable -> SimpleFilter comparable -> SimpleFilter comparable +intersect f1 f2 = + { specificOnes = + Dict.merge + (\key v1 -> Dict.insert key (v1 && f2.includeOthers)) + (\key v1 v2 -> Dict.insert key (v1 && v2)) + (\key v2 -> Dict.insert key (f1.includeOthers && v2)) + f1.specificOnes + f2.specificOnes + Dict.empty + , includeOthers = f1.includeOthers && f2.includeOthers + } + +{-| Start with a filter that includes none. +-} +none : SimpleFilter a +none = + { specificOnes = Dict.empty + , includeOthers = False + } + +{-| Check whether a SimpleFilter is a subset of another filter. +-} +subset : SimpleFilter comparable -> SimpleFilter comparable -> Bool +subset small large = + if small.includeOthers && not large.includeOthers then + False + else + -- All elements of small are in large + Dict.merge + (\_ s -> + if s && not large.includeOthers then + always False + else + identity + ) + (\_ s l -> + if s && not l then + always False + else + identity + ) + (\_ l -> + if small.includeOthers && not l then + always False + else + identity + ) + small.specificOnes + large.specificOnes + True + +{-| Encode a SimpleFilter into a list of items to exclude. +-} +toExclude : SimpleFilter comparable -> Maybe (List comparable) +toExclude f = + f.specificOnes + |> Dict.filter (always not) + |> Dict.keys + |> Just + +{-| Encode a SimpleFilter into a list of items to include. +-} +toInclude : SimpleFilter comparable -> Maybe (List comparable) +toInclude f = + if f.includeOthers then + Nothing + else + f.specificOnes + |> Dict.filter (always identity) + |> Dict.keys + |> Just + +{-| Create a SimpleFilter out of two optionally present lists. +-} +toSimpleFilter : Maybe (List comparable) -> Maybe (List comparable) -> SimpleFilter comparable +toSimpleFilter these notThese = + let + no : List comparable + no = Maybe.withDefault [] notThese + in + case these of + Just yes -> + { specificOnes = + Dict.union + (Dict.fromList ( List.map (\x -> Tuple.pair x False) no )) + (Dict.fromList ( List.map (\x -> Tuple.pair x True) yes )) + , includeOthers = False + } + + Nothing -> + { specificOnes = + no + |> List.map (\x -> Tuple.pair x False) + |> Dict.fromList + , includeOthers = True + } + +{-| Form a filter that includes values if it is included in either filters. +-} +union : SimpleFilter comparable -> SimpleFilter comparable -> SimpleFilter comparable +union f1 f2 = + { specificOnes = + Dict.merge + (\key v1 -> Dict.insert key (v1 || f2.includeOthers)) + (\key v1 v2 -> Dict.insert key (v1 || v2)) + (\key v2 -> Dict.insert key (f1.includeOthers || v2)) + f1.specificOnes + f2.specificOnes + Dict.empty + , includeOthers = f1.includeOthers && f2.includeOthers + } + +{-| Add a value that should be included. +-} +with : comparable -> SimpleFilter comparable -> SimpleFilter comparable +with x f = + { f | specificOnes = Dict.insert x True f.specificOnes } + +{-| Include all values that haven't been mentioned. +-} +withOthers : SimpleFilter comparable -> SimpleFilter comparable +withOthers f = + { f | includeOthers = True } + +{-| Add a value that should be ignored. +-} +without : comparable -> SimpleFilter comparable -> SimpleFilter comparable +without x f = + { f | specificOnes = Dict.insert x False f.specificOnes } + +{-| Ignore all values that haven't been mentioned. +-} +withoutOthers : SimpleFilter comparable -> SimpleFilter comparable +withoutOthers f = + { f | includeOthers = False } \ No newline at end of file diff --git a/src/Internal/Tools/Filters/SpecObjects.elm b/src/Internal/Tools/Filters/SpecObjects.elm new file mode 100644 index 0000000..a2d2ca8 --- /dev/null +++ b/src/Internal/Tools/Filters/SpecObjects.elm @@ -0,0 +1,246 @@ +module Internal.Tools.Filters.SpecObjects exposing + ( EventFilter + , Filter + , RoomEventFilter + , RoomFilter + , StateFilter + , encodeEventFilter + , encodeFilter + , encodeRoomEventFilter + , encodeRoomFilter + , encodeStateFilter + , eventFilterDecoder + , filterDecoder + , roomEventFilterDecoder + , roomFilterDecoder + , stateFilterDecoder + ) + +{-| Automatically generated 'SpecObjects' + +Last generated at Unix time 1681915222 + +-} + +import Internal.Tools.DecodeExtra as D exposing (opField, opFieldWithDefault) +import Internal.Tools.EncodeExtra exposing (maybeObject) +import Internal.Tools.SpecEnums as Enums +import Json.Decode as D +import Json.Encode as E + + +{-| Filter that describes which events to include/exclude. +-} +type alias EventFilter = + { limit : Maybe Int + , notSenders : Maybe (List String) + , notTypes : Maybe (List String) + , senders : Maybe (List String) + , types : Maybe (List String) + } + + +encodeEventFilter : EventFilter -> E.Value +encodeEventFilter data = + maybeObject + [ ( "limit", Maybe.map E.int data.limit ) + , ( "not_senders", Maybe.map (E.list E.string) data.notSenders ) + , ( "not_types", Maybe.map (E.list E.string) data.notTypes ) + , ( "senders", Maybe.map (E.list E.string) data.senders ) + , ( "types", Maybe.map (E.list E.string) data.types ) + ] + + +eventFilterDecoder : D.Decoder EventFilter +eventFilterDecoder = + D.map5 + (\a b c d e -> + { limit = a, notSenders = b, notTypes = c, senders = d, types = e } + ) + (opField "limit" D.int) + (opField "not_senders" (D.list D.string)) + (opField "not_types" (D.list D.string)) + (opField "senders" (D.list D.string)) + (opField "types" (D.list D.string)) + + +{-| Main filter for filtering results +-} +type alias Filter = + { accountData : Maybe EventFilter + , eventFields : Maybe (List String) + , eventFormat : Enums.EventFormat + , presence : Maybe EventFilter + , room : Maybe RoomFilter + } + + +encodeFilter : Filter -> E.Value +encodeFilter data = + maybeObject + [ ( "account_data", Maybe.map encodeEventFilter data.accountData ) + , ( "event_fields", Maybe.map (E.list E.string) data.eventFields ) + , ( "event_format", Just <| Enums.encodeEventFormat data.eventFormat ) + , ( "presence", Maybe.map encodeEventFilter data.presence ) + , ( "room", Maybe.map encodeRoomFilter data.room ) + ] + + +filterDecoder : D.Decoder Filter +filterDecoder = + D.map5 + (\a b c d e -> + { accountData = a, eventFields = b, eventFormat = c, presence = d, room = e } + ) + (opField "account_data" eventFilterDecoder) + (opField "event_fields" (D.list D.string)) + (opFieldWithDefault "event_format" Enums.Client Enums.eventFormatDecoder) + (opField "presence" eventFilterDecoder) + (opField "room" roomFilterDecoder) + + +{-| Filter that describes which events to include/exclude in a Matrix room. +-} +type alias RoomEventFilter = + { containsUrl : Maybe Bool + , includeRedundantMembers : Bool + , lazyLoadMembers : Bool + , limit : Maybe Int + , notRooms : Maybe (List String) + , notSenders : Maybe (List String) + , notTypes : Maybe (List String) + , rooms : Maybe (List String) + , senders : Maybe (List String) + , types : Maybe (List String) + , unreadThreadNotifications : Bool + } + + +encodeRoomEventFilter : RoomEventFilter -> E.Value +encodeRoomEventFilter data = + maybeObject + [ ( "contains_url", Maybe.map E.bool data.containsUrl ) + , ( "include_redundant_members", Just <| E.bool data.includeRedundantMembers ) + , ( "lazy_load_members", Just <| E.bool data.lazyLoadMembers ) + , ( "limit", Maybe.map E.int data.limit ) + , ( "not_rooms", Maybe.map (E.list E.string) data.notRooms ) + , ( "not_senders", Maybe.map (E.list E.string) data.notSenders ) + , ( "not_types", Maybe.map (E.list E.string) data.notTypes ) + , ( "rooms", Maybe.map (E.list E.string) data.rooms ) + , ( "senders", Maybe.map (E.list E.string) data.senders ) + , ( "types", Maybe.map (E.list E.string) data.types ) + , ( "unread_thread_notifications", Just <| E.bool data.unreadThreadNotifications ) + ] + + +roomEventFilterDecoder : D.Decoder RoomEventFilter +roomEventFilterDecoder = + D.map11 + (\a b c d e f g h i j k -> + { containsUrl = a, includeRedundantMembers = b, lazyLoadMembers = c, limit = d, notRooms = e, notSenders = f, notTypes = g, rooms = h, senders = i, types = j, unreadThreadNotifications = k } + ) + (opField "contains_url" D.bool) + (opFieldWithDefault "include_redundant_members" False D.bool) + (opFieldWithDefault "lazy_load_members" False D.bool) + (opField "limit" D.int) + (opField "not_rooms" (D.list D.string)) + (opField "not_senders" (D.list D.string)) + (opField "not_types" (D.list D.string)) + (opField "rooms" (D.list D.string)) + (opField "senders" (D.list D.string)) + (opField "types" (D.list D.string)) + (opFieldWithDefault "unread_thread_notifications" False D.bool) + + +{-| Filter that describes what should and shouldn't be included for rooms. +-} +type alias RoomFilter = + { accountData : Maybe RoomEventFilter + , ephemeral : Maybe RoomEventFilter + , includeLeave : Bool + , notRooms : Maybe (List String) + , rooms : Maybe (List String) + , state : Maybe StateFilter + , timeline : Maybe RoomEventFilter + } + + +encodeRoomFilter : RoomFilter -> E.Value +encodeRoomFilter data = + maybeObject + [ ( "account_data", Maybe.map encodeRoomEventFilter data.accountData ) + , ( "ephemeral", Maybe.map encodeRoomEventFilter data.ephemeral ) + , ( "include_leave", Just <| E.bool data.includeLeave ) + , ( "not_rooms", Maybe.map (E.list E.string) data.notRooms ) + , ( "rooms", Maybe.map (E.list E.string) data.rooms ) + , ( "state", Maybe.map encodeStateFilter data.state ) + , ( "timeline", Maybe.map encodeRoomEventFilter data.timeline ) + ] + + +roomFilterDecoder : D.Decoder RoomFilter +roomFilterDecoder = + D.map7 + (\a b c d e f g -> + { accountData = a, ephemeral = b, includeLeave = c, notRooms = d, rooms = e, state = f, timeline = g } + ) + (opField "account_data" roomEventFilterDecoder) + (opField "ephemeral" roomEventFilterDecoder) + (opFieldWithDefault "include_leave" False D.bool) + (opField "not_rooms" (D.list D.string)) + (opField "rooms" (D.list D.string)) + (opField "state" stateFilterDecoder) + (opField "timeline" roomEventFilterDecoder) + + +{-| Filter that describes which events to include/exclude in a Matrix room. +-} +type alias StateFilter = + { containsUrl : Maybe Bool + , includeRedundantMembers : Bool + , lazyLoadMembers : Bool + , limit : Maybe Int + , notRooms : Maybe (List String) + , notSenders : Maybe (List String) + , notTypes : Maybe (List String) + , rooms : Maybe (List String) + , senders : Maybe (List String) + , types : Maybe (List String) + , unreadThreadNotifications : Bool + } + + +encodeStateFilter : StateFilter -> E.Value +encodeStateFilter data = + maybeObject + [ ( "contains_url", Maybe.map E.bool data.containsUrl ) + , ( "include_redundant_members", Just <| E.bool data.includeRedundantMembers ) + , ( "lazy_load_members", Just <| E.bool data.lazyLoadMembers ) + , ( "limit", Maybe.map E.int data.limit ) + , ( "not_rooms", Maybe.map (E.list E.string) data.notRooms ) + , ( "not_senders", Maybe.map (E.list E.string) data.notSenders ) + , ( "not_types", Maybe.map (E.list E.string) data.notTypes ) + , ( "rooms", Maybe.map (E.list E.string) data.rooms ) + , ( "senders", Maybe.map (E.list E.string) data.senders ) + , ( "types", Maybe.map (E.list E.string) data.types ) + , ( "unread_thread_notifications", Just <| E.bool data.unreadThreadNotifications ) + ] + + +stateFilterDecoder : D.Decoder StateFilter +stateFilterDecoder = + D.map11 + (\a b c d e f g h i j k -> + { containsUrl = a, includeRedundantMembers = b, lazyLoadMembers = c, limit = d, notRooms = e, notSenders = f, notTypes = g, rooms = h, senders = i, types = j, unreadThreadNotifications = k } + ) + (opField "contains_url" D.bool) + (opFieldWithDefault "include_redundant_members" False D.bool) + (opFieldWithDefault "lazy_load_members" False D.bool) + (opField "limit" D.int) + (opField "not_rooms" (D.list D.string)) + (opField "not_senders" (D.list D.string)) + (opField "not_types" (D.list D.string)) + (opField "rooms" (D.list D.string)) + (opField "senders" (D.list D.string)) + (opField "types" (D.list D.string)) + (opFieldWithDefault "unread_thread_notifications" False D.bool) diff --git a/src/Internal/Tools/Filters/SpecObjects.yaml b/src/Internal/Tools/Filters/SpecObjects.yaml new file mode 100644 index 0000000..16ec004 --- /dev/null +++ b/src/Internal/Tools/Filters/SpecObjects.yaml @@ -0,0 +1,135 @@ +version: v1 +name: SpecObjects +objects: + Filter: + description: Main filter for filtering results + fields: + account_data: + type: EventFilter + required: false + event_fields: + type: '[string]' + required: false + event_format: + type: Enums.EventFormat + default: Enums.Client + presence: + type: EventFilter + required: false + room: + type: RoomFilter + required: false + EventFilter: + description: Filter that describes which events to include/exclude. + fields: + limit: + type: int + required: false + not_senders: + type: '[string]' + required: false + not_types: + type: '[string]' + required: false + senders: + type: '[string]' + required: false + types: + type: '[string]' + required: false + RoomFilter: + description: Filter that describes what should and shouldn't be included for rooms. + fields: + account_data: + type: RoomEventFilter + required: false + ephemeral: + type: RoomEventFilter + required: false + include_leave: + type: bool + default: 'False' + not_rooms: + type: '[string]' + required: false + rooms: + type: '[string]' + required: false + state: + type: StateFilter + required: false + timeline: + type: RoomEventFilter + required: false + RoomEventFilter: + description: Filter that describes which events to include/exclude in a Matrix room. + fields: + contains_url: + type: bool + required: false + include_redundant_members: + type: bool + default: 'False' + lazy_load_members: + type: bool + default: 'False' + limit: + type: int + required: false + not_rooms: + type: '[string]' + required: false + not_senders: + type: '[string]' + required: false + not_types: + type: '[string]' + required: false + rooms: + type: '[string]' + required: false + senders: + type: '[string]' + required: false + types: + type: '[string]' + required: false + unread_thread_notifications: + type: bool + default: 'False' + StateFilter: + description: Filter that describes which events to include/exclude in a Matrix room. + fields: + contains_url: + type: bool + required: false + include_redundant_members: + type: bool + default: 'False' + lazy_load_members: + type: bool + default: 'False' + limit: + type: int + required: false + not_rooms: + type: '[string]' + required: false + not_senders: + type: '[string]' + required: false + not_types: + type: '[string]' + required: false + rooms: + type: '[string]' + required: false + senders: + type: '[string]' + required: false + types: + type: '[string]' + required: false + unread_thread_notifications: + type: bool + default: 'False' diff --git a/src/Internal/Tools/VaultResult.elm b/src/Internal/Tools/VaultResult.elm new file mode 100644 index 0000000..1ef1afc --- /dev/null +++ b/src/Internal/Tools/VaultResult.elm @@ -0,0 +1,14 @@ +module Internal.Tools.VaultResult exposing (..) + +import Internal.Tools.Exceptions as X +import Task exposing (Task) + + +type Info b a + = Info a + | NoInfo + | InfoFailed { status : LoadingError, retry : Task X.Error b } + + +type LoadingError + = NeverRequested