packages feed

elm2nix-0.5.0: src/Elm2Nix/ElmJson.hs

{-# LANGUAGE CPP #-}
{-# LANGUAGE OverloadedStrings #-}

module Elm2Nix.ElmJson
  ( Dep,
    Elm2NixError (..),
    toErrorMessage,
    readElmJson,
  )
where

import Control.Monad (liftM2)
import Data.Aeson (Value (..))
import Data.List (nub)
import Data.Text (Text)

#if MIN_VERSION_aeson(2,0,0)
import qualified Data.Aeson.Key as AK
import qualified Data.Aeson.KeyMap as HM
#else
import qualified Data.HashMap.Strict as HM
#endif

import qualified Data.Aeson as Json
import qualified Data.ByteString.Lazy as LBS
import qualified Data.Text as Text

type Dep = (String, String)

data Elm2NixError
  = ElmJsonReadError String
  | UnexpectedValue Value
  | KeyNotFound Text
  deriving (Show)

toErrorMessage :: Elm2NixError -> String
toErrorMessage err =
  case err of
    UnexpectedValue v -> "Unexpected Value: \n" ++ show v
    ElmJsonReadError s -> "Error reading json: " ++ s
    KeyNotFound key -> "Key not found in json: " ++ Text.unpack key

readElmJson :: FilePath -> IO (Either Elm2NixError [Dep])
readElmJson path = do
  res <- Json.eitherDecode <$> LBS.readFile path
  pure $
    either
      (Left . ElmJsonReadError)
      parseElmJson
      res

parseElmJson :: Value -> Either Elm2NixError [Dep]
parseElmJson obj =
  nub
    <$> liftM2
      (++)
      (parseElmJsonDeps "dependencies" obj)
      (parseElmJsonDeps "test-dependencies" obj)

parseElmJsonDeps :: Text -> Value -> Either Elm2NixError [Dep]
parseElmJsonDeps depsKey obj =
  case obj of
    Object hm -> do
      deps <- tryLookup hm depsKey
      case deps of
        Object dhm -> do
          direct <- tryLookup dhm "direct"
          indirect <- tryLookup dhm "indirect"
          liftM2 (++) (parseDeps direct) (parseDeps indirect)
        v -> Left (UnexpectedValue v)
    v -> Left (UnexpectedValue v)

#if MIN_VERSION_aeson(2,0,0)
parseDep :: Json.Key -> Value -> Either Elm2NixError Dep
parseDep name (String ver) = Right (Text.unpack (AK.toText name), Text.unpack ver)
#else
parseDep :: Text -> Value -> Either Elm2NixError Dep
parseDep name (String ver) = Right (Text.unpack name, Text.unpack ver)
#endif
parseDep _ v = Left (UnexpectedValue v)

parseDeps :: Value -> Either Elm2NixError [Dep]
parseDeps (Object hm) = mapM (uncurry parseDep) (HM.toList hm)
parseDeps v = Left (UnexpectedValue v)

maybeToRight :: b -> Maybe a -> Either b a
maybeToRight _ (Just x) = Right x
maybeToRight y Nothing = Left y

#if MIN_VERSION_aeson(2,0,0)
tryLookup :: HM.KeyMap Value -> Text -> Either Elm2NixError Value
tryLookup hm key =
  maybeToRight (KeyNotFound key) (HM.lookup (AK.fromText key) hm)
#else
tryLookup :: HM.HashMap Text Value -> Text -> Either Elm2NixError Value
tryLookup hm key =
  maybeToRight (KeyNotFound key) (HM.lookup key hm)
#endif