awakesecurity / proto3-suite

Haskell Protobuf Implementation

Home Page:https://hackage.haskell.org/package/proto3-suite

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

I'm unable to build this library on ghc 9.8.2 with aeson-2. Any pointers?

tonyalaribe opened this issue · comments

Hi, I'm trying to setup a grpc server for a couple days now, but have been unable to figure this out.

My current situation is the error below, which I wasn't able to resolve by forking this library and adding attoparsec-aeson to the dependencies list (I got new errors). Do you have any pointers on the issue?

proto3-suite> [17 of 22] Compiling Proto3.Suite
proto3-suite> /private/var/folders/nt/myzgjzxx7vb4g3j1gv7g52nw0000gn/T/stack-b1359b359667a672/proto3-suite-0.8.0/src/Proto3/Suite/JSONPB/Class.hs:83:1: error: [GHC-87110]
proto3-suite>     Could not load module ‘Data.Aeson.Parser’.
proto3-suite>     It is a member of the hidden package ‘attoparsec-aeson-2.2.2.0’.
proto3-suite>     Perhaps you need to add ‘attoparsec-aeson’ to the build-depends in your .cabal file.
proto3-suite>     Use -v to see a list of the files searched for.
proto3-suite>    |
proto3-suite> 83 | import qualified Data.Aeson.Parser                as A (eitherDecodeWith, json)
proto3-suite>    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
proto3-suite> [18 of 22] Compiling Proto3.Suite.Tutorial
proto3-suite> [19 of 22] Compiling Proto3.Suite.DotProto.Generate.Swagger
proto3-suite> [22 of 22] Compiling Proto3.Suite.DotProto.Generate
Progress 1/3
commented

Its an error in our bounds. We should still have aeson < 2.2, not aeson < 2.3. The problem was introduced by @j6carey in bec9d40#diff-bfbb0a670352f7c28e30ad16b0d5dd36f12d85ec61a3238918cf0dcf974d2b43 it appears.

Changing that in the fork doesn't help, so I don't think that's the issue.
That, or my stack allow-newer: true doesn't help

Its an error in our bounds. We should still have aeson < 2.2, not aeson < 2.3. The problem was introduced by @j6carey in bec9d40#diff-bfbb0a670352f7c28e30ad16b0d5dd36f12d85ec61a3238918cf0dcf974d2b43 it appears.

Yeah, it looks like that part of my change was incorrect; sorry about that.

@tonyalaribe , our continuous integration includes the following Nix build, which succeeds:

nix-build --argstr compiler ghc981 --arg enableDhall false --arg enableSwagger true --arg swaggerWrapperFormat true --arg enableLargeRecords false

The version of Aeson that we use in that build is aeson-2.1.2.1 but with this patch:

--- a/src/Data/Aeson/Internal/Text.hs	2001-09-08 18:46:40.000000000 -0700
+++ b/src/Data/Aeson/Internal/Text.hs	2024-05-09 21:16:49.964743729 -0700
@@ -8,5 +8,5 @@
 import qualified Data.Text                      as T

 #if MIN_VERSION_text(2,0,0)
-import           Data.Text.Array                (Array (..))
+import           Data.Text.Array
 import qualified Data.Text.Internal             as T (Text (..))

 import qualified Data.ByteString.Short.Internal as SBS

Does that help?

commented

That, or my stack allow-newer: true doesn't help

indeed, if you allow-newer then it will discard all our bounds, leading to failures. A patch to adapt to using a newer aeson would be welcome if we could also ensure we maintain back-compat.

I spent the last 2 days trying to fix this library to work with aeson 2.2.3, and I'm surprised at how difficult this is.

Specifically translating these two instances to an implementation that supports aeson 2.2.3

instance (A.ToJSONKey k, ToJSONPB k, ToJSONPB v) => ToJSONPB (M.Map k v) where
  toJSONPB m opts = A.liftToJSON @(M.Map k) (`toJSONPB` opts) (A.Array . V.fromList . map (`toJSONPB` opts)) m
  toEncodingPB m opts = A.liftToEncoding @(M.Map k) (`toEncodingPB` opts) (E.list (`toEncodingPB` opts)) m

instance (Ord k, A.FromJSONKey k, FromJSONPB k, FromJSONPB v) => FromJSONPB (M.Map k v) where
  parseJSONPB = A.liftParseJSON @(M.Map k) parseJSONPB parseList
    where
      parseList (A.Array a) = traverse parseJSONPB (V.toList a)
      parseList v = A.typeMismatch "not a list" v
      

I got the first instance to compile with this implementation. But don't have a solution for the second yet

instance (A.ToJSONKey k, ToJSONPB k, ToJSONPB v) => ToJSONPB (M.Map k v) where
  toJSONPB m opts = A.toJSON $ M.map (\v -> toJSONPB v opts) m

@tonyalaribe , I'll take a look, but I may have trouble getting the build environment established, and I have a few other projects, so I can't promise an immediate answer.

@tonyalaribe , I'm going to propose this solution, though I haven't yet gotten it reviewed:

instance (A.ToJSONKey k, ToJSONPB k, ToJSONPB v) => ToJSONPB (M.Map k v) where
  toJSONPB m opts = A.liftToJSON @(M.Map k)
#if MIN_VERSION_aeson(2,2,0)
                      (const False)  -- Unless and until we test for the protobuf default value.
#endif
                         (`toJSONPB` opts) (A.Array . V.fromList . map (`toJSONPB` opts)) m
  toEncodingPB m opts = A.liftToEncoding @(M.Map k)
#if MIN_VERSION_aeson(2,2,0)
                          (const False)  -- Unless and until we test for the protobuf default value.
#endif
                            (`toEncodingPB` opts) (E.list (`toEncodingPB` opts)) m

instance (Ord k, A.FromJSONKey k, FromJSONPB k, FromJSONPB v) => FromJSONPB (M.Map k v) where
  parseJSONPB = A.liftParseJSON @(M.Map k)
#if MIN_VERSION_aeson(2,2,0)
                  Nothing  -- Unless and until we decide to use the protobuf default value.
#endif
                    parseJSONPB parseList
    where
      parseList (A.Array a) = traverse parseJSONPB (V.toList a)
      parseList v = A.typeMismatch "not a list" v

@j6carey this worked nicely. Thanks for helping with this. I'll clean up the branch and make a PR.