diff --git a/src/Internal/Config/Text.elm b/src/Internal/Config/Text.elm index 1159868..7cd0932 100644 --- a/src/Internal/Config/Text.elm +++ b/src/Internal/Config/Text.elm @@ -112,14 +112,30 @@ decodedDictSize from to = {-| Documentation used for all functions and data types in JSON coders -} docs : - { event : TypeDocs + { context : TypeDocs + , envelope : TypeDocs + , event : TypeDocs , hashdict : TypeDocs , mashdict : TypeDocs + , settings : TypeDocs , stateManager : TypeDocs , unsigned : TypeDocs } docs = - { event = + { context = + { name = "Context" + , description = + [ "The Context is the set of variables that the user (mostly) cannot control." + , "The Context contains tokens, values and other bits that the Vault receives from the Matrix API." + ] + } + , envelope = + { name = "Envelope" + , description = + [ "The Envelope module wraps existing data types with lots of values and settings that can be adjusted manually." + ] + } + , event = { name = "Event" , description = [ "The Event type represents a single value that contains all the information for a single event in the room." @@ -138,6 +154,12 @@ docs = [ "The mashdict exclusively stores values for which the hashing algorithm returns a value, and it ignores the outcome for all other scenarios." ] } + , settings = + { name = "Settings" + , description = + [ "The settings type is a data type to configure settings in the enveloped data type." + ] + } , stateManager = { name = "StateManager" , description = @@ -173,7 +195,21 @@ failures = fields : - { event : + { context : + { accessToken : Desc + , baseUrl : Desc + , password : Desc + , refreshToken : Desc + , username : Desc + , transaction : Desc + , versions : Desc + } + , envelope : + { content : Desc + , context : Desc + , settings : Desc + } + , event : { content : Desc , eventId : Desc , originServerTs : Desc @@ -183,6 +219,11 @@ fields : , eventType : Desc , unsigned : Desc } + , settings : + { currentVersion : Desc + , deviceName : Desc + , syncTime : Desc + } , unsigned : { age : Desc , prevContent : Desc @@ -191,7 +232,21 @@ fields : } } fields = - { event = + { context = + { accessToken = [] + , baseUrl = [] + , password = [] + , refreshToken = [] + , username = [] + , transaction = [] + , versions = [] + } + , envelope = + { content = [] + , context = [] + , settings = [] + } + , event = { content = [] , eventId = [] , originServerTs = [] @@ -201,6 +256,11 @@ fields = , eventType = [] , unsigned = [] } + , settings = + { currentVersion = [] + , deviceName = [] + , syncTime = [] + } , unsigned = { age = [] , prevContent = [] diff --git a/src/Internal/Tools/Json.elm b/src/Internal/Tools/Json.elm index d8f21cd..9a68f14 100644 --- a/src/Internal/Tools/Json.elm +++ b/src/Internal/Tools/Json.elm @@ -69,7 +69,7 @@ Once all fields are constructed, the user can create JSON objects. import Dict as SlowDict import FastDict -import Internal.Config.Log exposing (Log, log) +import Internal.Config.Log exposing (Log) import Internal.Tools.DecodeExtra as D import Internal.Tools.EncodeExtra as E import Json.Decode as D @@ -382,7 +382,14 @@ field = { fieldName = fieldName , toField = toField , description = description - , encoder = encoder >> Maybe.Just + , encoder = + \o -> + -- If the value matches the default, do not record + if o == Tuple.first default then + Nothing + + else + Maybe.Just (encoder o) , decoder = D.opFieldWithDefault fieldName default decoder , docs = docs , requiredness = diff --git a/src/Internal/Values/Context.elm b/src/Internal/Values/Context.elm index d25d906..6d2319b 100644 --- a/src/Internal/Values/Context.elm +++ b/src/Internal/Values/Context.elm @@ -1,5 +1,5 @@ module Internal.Values.Context exposing - ( Context, init, encode, decoder + ( Context, init, coder, encode, decoder , APIContext, apiFormat , setAccessToken, getAccessToken , setBaseUrl, getBaseUrl @@ -14,7 +14,7 @@ the Matrix API. ## Context -@docs Context, init, encode, decoder +@docs Context, init, coder, encode, decoder ## APIContext @@ -50,10 +50,8 @@ information that can be inserted. -} import Internal.Config.Leaks as L -import Internal.Tools.DecodeExtra as D -import Internal.Tools.EncodeExtra as E -import Json.Decode as D -import Json.Encode as E +import Internal.Config.Text as Text +import Internal.Tools.Json as Json {-| The Context type stores all the information in the Vault. This data type is @@ -97,33 +95,76 @@ apiFormat context = } +coder : Json.Coder Context +coder = + Json.object7 + { name = Text.docs.context.name + , description = Text.docs.context.description + , init = Context + } + (Json.field.optional.value + { fieldName = "accessToken" + , toField = .accessToken + , description = Text.fields.context.accessToken + , coder = Json.string + } + ) + (Json.field.optional.value + { fieldName = "baseUrl" + , toField = .baseUrl + , description = Text.fields.context.baseUrl + , coder = Json.string + } + ) + (Json.field.optional.value + { fieldName = "password" + , toField = .password + , description = Text.fields.context.password + , coder = Json.string + } + ) + (Json.field.optional.value + { fieldName = "refreshToken" + , toField = .refreshToken + , description = Text.fields.context.refreshToken + , coder = Json.string + } + ) + (Json.field.optional.value + { fieldName = "username" + , toField = .username + , description = Text.fields.context.username + , coder = Json.string + } + ) + (Json.field.optional.value + { fieldName = "transaction" + , toField = .transaction + , description = Text.fields.context.transaction + , coder = Json.string + } + ) + (Json.field.optional.value + { fieldName = "versions" + , toField = .versions + , description = Text.fields.context.versions + , coder = Json.list Json.string + } + ) + + {-| Decode a Context type from a JSON value. -} -decoder : D.Decoder Context +decoder : Json.Decoder Context decoder = - D.map7 Context - (D.opField "accessToken" D.string) - (D.opField "baseUrl" D.string) - (D.opField "password" D.string) - (D.opField "refreshToken" D.string) - (D.opField "username" D.string) - (D.opField "transaction" D.string) - (D.opField "versions" (D.list D.string)) + Json.decode coder {-| Encode a Context type into a JSON value. -} -encode : Context -> E.Value -encode context = - E.maybeObject - [ ( "accessToken", Maybe.map E.string context.accessToken ) - , ( "baseUrl", Maybe.map E.string context.baseUrl ) - , ( "password", Maybe.map E.string context.password ) - , ( "refreshToken", Maybe.map E.string context.refreshToken ) - , ( "username", Maybe.map E.string context.username ) - , ( "transaction", Maybe.map E.string context.transaction ) - , ( "versions", Maybe.map (E.list E.string) context.versions ) - ] +encode : Json.Encoder Context +encode = + Json.encode coder {-| A basic, untouched version of the Context, containing no information. diff --git a/src/Internal/Values/Envelope.elm b/src/Internal/Values/Envelope.elm index 1037318..3c0fec0 100644 --- a/src/Internal/Values/Envelope.elm +++ b/src/Internal/Values/Envelope.elm @@ -38,17 +38,14 @@ settings that can be adjusted manually. ## JSON coders -@docs encode, decoder +@docs coder, encode, decoder -} -import Internal.Config.Default as Default -import Internal.Tools.DecodeExtra as D -import Internal.Tools.EncodeExtra as E +import Internal.Config.Text as Text +import Internal.Tools.Json as Json import Internal.Values.Context as Context exposing (Context) import Internal.Values.Settings as Settings -import Json.Decode as D -import Json.Encode as E {-| There are lots of different data types in the Elm SDK, and many of them @@ -71,28 +68,52 @@ type alias Settings = Settings.Settings +coder : Json.Coder a -> Json.Coder (Envelope a) +coder c1 = + Json.object3 + { name = Text.docs.envelope.name + , description = Text.docs.envelope.description + , init = Envelope + } + (Json.field.required + { fieldName = "content" + , toField = .content + , description = Text.fields.envelope.content + , coder = c1 + } + ) + (Json.field.required + { fieldName = "context" + , toField = .context + , description = Text.fields.envelope.context + , coder = Context.coder + } + ) + (Json.field.optional.withDefault + { fieldName = "settings" + , toField = .settings + , description = Text.fields.envelope.settings + , coder = Settings.coder + , default = Tuple.pair Settings.init [] + , defaultToString = always "" + } + ) + + {-| Decode an enveloped type from a JSON value. The decoder also imports any potential tokens, values and settings included in the JSON. -} -decoder : D.Decoder a -> D.Decoder (Envelope a) -decoder xDecoder = - D.map3 Envelope - (D.field "content" xDecoder) - (D.field "context" Context.decoder) - (D.field "settings" Settings.decoder) +decoder : Json.Coder a -> Json.Decoder (Envelope a) +decoder c1 = + Json.decode (coder c1) {-| Encode an enveloped type into a JSON value. The function encodes all non-standard settings, tokens and values. -} -encode : (a -> E.Value) -> Envelope a -> E.Value -encode encodeX data = - E.object - [ ( "content", encodeX data.content ) - , ( "context", Context.encode data.context ) - , ( "settings", Settings.encode data.settings ) - , ( "version", E.string Default.currentVersion ) - ] +encode : Json.Coder a -> Json.Encoder (Envelope a) +encode c1 = + Json.encode (coder c1) {-| Map a function, then get its content. This is useful for getting information diff --git a/src/Internal/Values/Event.elm b/src/Internal/Values/Event.elm index 304e7cb..4f61693 100644 --- a/src/Internal/Values/Event.elm +++ b/src/Internal/Values/Event.elm @@ -26,7 +26,6 @@ of a room. -} -import Internal.Config.Default as Default import Internal.Config.Text as Text import Internal.Tools.Json as Json import Internal.Tools.Timestamp as Timestamp exposing (Timestamp) diff --git a/src/Internal/Values/Settings.elm b/src/Internal/Values/Settings.elm index 9ac52ac..aa7d24a 100644 --- a/src/Internal/Values/Settings.elm +++ b/src/Internal/Values/Settings.elm @@ -1,6 +1,6 @@ module Internal.Values.Settings exposing ( Settings, init - , encode, decoder + , coder, encode, decoder ) {-| @@ -16,15 +16,13 @@ data types. ## JSON coders -@docs encode, decoder +@docs coder, encode, decoder -} import Internal.Config.Default as Default -import Internal.Tools.DecodeExtra as D -import Internal.Tools.EncodeExtra as E -import Json.Decode as D -import Json.Encode as E +import Internal.Config.Text as Text +import Internal.Tools.Json as Json {-| Custom settings that can be manipulated by the user. These serve as a @@ -41,46 +39,54 @@ type alias Settings = } +coder : Json.Coder Settings +coder = + Json.object3 + { name = Text.docs.settings.name + , description = Text.docs.settings.description + , init = Settings + } + (Json.field.optional.withDefault + { fieldName = "currentVersion" + , toField = .currentVersion + , description = Text.fields.settings.currentVersion + , coder = Json.string + , default = Tuple.pair Default.currentVersion [] + , defaultToString = identity + } + ) + (Json.field.optional.withDefault + { fieldName = "deviceName" + , toField = .deviceName + , description = Text.fields.settings.deviceName + , coder = Json.string + , default = Tuple.pair Default.deviceName [] + , defaultToString = identity + } + ) + (Json.field.optional.withDefault + { fieldName = "syncTime" + , toField = .syncTime + , description = Text.fields.settings.syncTime + , coder = Json.int + , default = Tuple.pair Default.syncTime [] + , defaultToString = String.fromInt + } + ) + + {-| Decode settings from a JSON value. -} -decoder : D.Decoder Settings +decoder : Json.Decoder Settings decoder = - D.map3 Settings - (D.opFieldWithDefault "currentVersion" Default.currentVersion D.string) - (D.opFieldWithDefault "deviceName" Default.deviceName D.string) - (D.opFieldWithDefault "syncTime" Default.syncTime D.int) + Json.decode coder {-| Encode the settings into a JSON value. -} -encode : Settings -> E.Value -encode settings = - let - differentFrom : b -> b -> Maybe b - differentFrom defaultValue currentValue = - if currentValue == defaultValue then - Nothing - - else - Just currentValue - in - E.maybeObject - [ ( "currentVersion" - , settings.currentVersion - |> differentFrom Default.currentVersion - |> Maybe.map E.string - ) - , ( "deviceName" - , settings.deviceName - |> differentFrom Default.deviceName - |> Maybe.map E.string - ) - , ( "syncTime" - , settings.syncTime - |> differentFrom Default.syncTime - |> Maybe.map E.int - ) - ] +encode : Json.Encoder Settings +encode = + Json.encode coder {-| Create a new Settings module based on default values diff --git a/tests/Test/Values/Context.elm b/tests/Test/Values/Context.elm index 1b6dc55..c412daf 100644 --- a/tests/Test/Values/Context.elm +++ b/tests/Test/Values/Context.elm @@ -138,6 +138,6 @@ json = context |> Context.encode |> D.decodeValue Context.decoder - |> Expect.equal (Ok context) + |> Expect.equal (Ok ( context, [] )) ) ] diff --git a/tests/Test/Values/Envelope.elm b/tests/Test/Values/Envelope.elm index deb5036..e147b8d 100644 --- a/tests/Test/Values/Envelope.elm +++ b/tests/Test/Values/Envelope.elm @@ -3,6 +3,7 @@ module Test.Values.Envelope exposing (..) import Expect import Fuzz exposing (Fuzzer) import Internal.Config.Default as Default +import Internal.Tools.Json as Json import Internal.Values.Envelope as Envelope exposing (Envelope) import Json.Decode as D import Json.Encode as E @@ -56,10 +57,10 @@ suite = "JSON encode -> JSON decode" (\envelope indent -> envelope - |> Envelope.encode E.string + |> Envelope.encode Json.string |> E.encode indent - |> D.decodeString (Envelope.decoder D.string) - |> Expect.equal (Ok envelope) + |> D.decodeString (Envelope.decoder Json.string) + |> Expect.equal (Ok ( envelope, [] )) ) ] ] diff --git a/tests/Test/Values/Settings.elm b/tests/Test/Values/Settings.elm index 8edf86c..d48a851 100644 --- a/tests/Test/Values/Settings.elm +++ b/tests/Test/Values/Settings.elm @@ -61,7 +61,7 @@ suite = , test "JSON decode {} is init" ("{}" |> D.decodeString Settings.decoder - |> Expect.equal (Ok Settings.init) + |> Expect.equal (Ok ( Settings.init, [] )) |> always ) ] @@ -74,7 +74,7 @@ suite = |> Settings.encode |> E.encode indent |> D.decodeString Settings.decoder - |> Expect.equal (Ok settings) + |> Expect.equal (Ok ( settings, [] )) ) ] ]