hnix/src/Nix/Convert.hs

378 lines
13 KiB
Haskell
Raw Normal View History

{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE ConstraintKinds #-}
2019-03-19 02:20:07 +01:00
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
2019-03-18 23:27:12 +01:00
{-# LANGUAGE IncoherentInstances #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -Wno-missing-signatures #-}
{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
-- | Although there are a lot of instances in this file, really it's just a
-- combinatorial explosion of the following combinations:
--
-- - Several Haskell types being converted to/from Nix wrappers
-- - Several types of Nix wrappers
-- - Whether to be shallow or deep while unwrapping
module Nix.Convert where
import Control.Monad.Free
import Data.ByteString
import qualified Data.HashMap.Lazy as M
import Data.Maybe
import Data.Text ( Text )
import qualified Data.Text as Text
import Data.Text.Encoding ( encodeUtf8
, decodeUtf8
)
import Nix.Atoms
import Nix.Effects
import Nix.Expr.Types
import Nix.Expr.Types.Annotated
import Nix.Frames
import Nix.String
import Nix.Value
import Nix.Value.Monad
2019-03-19 02:20:07 +01:00
import Nix.Thunk
import Nix.Utils
2019-03-18 23:27:12 +01:00
newtype Deeper a = Deeper { getDeeper :: a }
2019-03-19 02:20:07 +01:00
deriving (Typeable, Functor, Foldable, Traversable)
2019-03-18 23:27:12 +01:00
{-
IMPORTANT NOTE
We used to have Text instances of FromValue, ToValue, FromNix, and ToNix.
However, we're removing these instances because they are dangerous due to the
fact that they hide the way string contexts are handled. It's better to have to
explicitly handle string context in a way that is appropriate for the situation.
Do not add these instances back!
-}
{-----------------------------------------------------------------------
FromValue
-----------------------------------------------------------------------}
class FromValue a m v where
fromValue :: v -> m a
2018-04-16 05:22:24 +02:00
fromValueMay :: v -> m (Maybe a)
2019-03-19 02:20:07 +01:00
type Convertible e t f m
2019-03-19 05:47:43 +01:00
= (Framed e m, MonadDataErrorContext t f m, MonadThunk t m (NValue t f m))
2018-04-16 08:00:09 +02:00
instance ( Convertible e t f m
, MonadValue (NValue t f m) m
, FromValue a m (NValue' t f m (NValue t f m))
)
=> FromValue a m (NValue t f m) where
fromValueMay = flip demand $ \case
2019-03-19 02:20:07 +01:00
Pure t -> force t fromValueMay
Free v -> fromValueMay v
2019-03-19 05:47:43 +01:00
fromValue = flip demand $ \case
2019-03-19 02:20:07 +01:00
Pure t -> force t fromValue
Free v -> fromValue v
2019-03-18 23:27:12 +01:00
instance ( Convertible e t f m
, MonadValue (NValue t f m) m
, FromValue a m (Deeper (NValue' t f m (NValue t f m)))
)
=> FromValue a m (Deeper (NValue t f m)) where
fromValueMay (Deeper v) = demand v $ \case
2019-03-19 02:20:07 +01:00
Pure t -> force t (fromValueMay . Deeper)
2019-03-18 23:27:12 +01:00
Free v -> fromValueMay (Deeper v)
2019-03-19 05:47:43 +01:00
fromValue (Deeper v) = demand v $ \case
2019-03-19 02:20:07 +01:00
Pure t -> force t (fromValue . Deeper)
2019-03-18 23:27:12 +01:00
Free v -> fromValue (Deeper v)
instance Convertible e t f m
=> FromValue () m (NValue' t f m (NValue t f m)) where
fromValueMay = \case
NVConstant' NNull -> pure $ Just ()
_ -> pure Nothing
fromValue v = fromValueMay v >>= \case
Just b -> pure b
_ -> throwError $ Expectation @t @f @m TNull (Free v)
instance Convertible e t f m
=> FromValue Bool m (NValue' t f m (NValue t f m)) where
fromValueMay = \case
NVConstant' (NBool b) -> pure $ Just b
_ -> pure Nothing
fromValue v = fromValueMay v >>= \case
Just b -> pure b
_ -> throwError $ Expectation @t @f @m TBool (Free v)
instance Convertible e t f m
=> FromValue Int m (NValue' t f m (NValue t f m)) where
fromValueMay = \case
NVConstant' (NInt b) -> pure $ Just (fromInteger b)
_ -> pure Nothing
fromValue v = fromValueMay v >>= \case
Just b -> pure b
_ -> throwError $ Expectation @t @f @m TInt (Free v)
instance Convertible e t f m
=> FromValue Integer m (NValue' t f m (NValue t f m)) where
fromValueMay = \case
NVConstant' (NInt b) -> pure $ Just b
_ -> pure Nothing
fromValue v = fromValueMay v >>= \case
Just b -> pure b
_ -> throwError $ Expectation @t @f @m TInt (Free v)
instance Convertible e t f m
=> FromValue Float m (NValue' t f m (NValue t f m)) where
fromValueMay = \case
NVConstant' (NFloat b) -> pure $ Just b
NVConstant' (NInt i) -> pure $ Just (fromInteger i)
_ -> pure Nothing
fromValue v = fromValueMay v >>= \case
Just b -> pure b
_ -> throwError $ Expectation @t @f @m TFloat (Free v)
instance ( Convertible e t f m
, MonadValue (NValue t f m) m
, MonadEffects t f m
)
=> FromValue NixString m (NValue' t f m (NValue t f m)) where
fromValueMay = \case
NVStr' ns -> pure $ Just ns
NVPath' p ->
Just
. hackyMakeNixStringWithoutContext
. Text.pack
. unStorePath
<$> addPath p
NVSet' s _ -> case M.lookup "outPath" s of
Nothing -> pure Nothing
Just p -> fromValueMay p
_ -> pure Nothing
fromValue v = fromValueMay v >>= \case
Just b -> pure b
_ -> throwError $ Expectation @t @f @m (TString NoContext) (Free v)
instance Convertible e t f m
=> FromValue ByteString m (NValue' t f m (NValue t f m)) where
fromValueMay = \case
NVStr' ns -> pure $ encodeUtf8 <$> hackyGetStringNoContext ns
2019-03-19 05:47:43 +01:00
_ -> pure Nothing
fromValue v = fromValueMay v >>= \case
Just b -> pure b
_ -> throwError $ Expectation @t @f @m (TString NoContext) (Free v)
newtype Path = Path { getPath :: FilePath }
deriving Show
instance ( Convertible e t f m
, MonadValue (NValue t f m) m
)
=> FromValue Path m (NValue' t f m (NValue t f m)) where
fromValueMay = \case
NVPath' p -> pure $ Just (Path p)
NVStr' ns -> pure $ Path . Text.unpack <$> hackyGetStringNoContext ns
NVSet' s _ -> case M.lookup "outPath" s of
Nothing -> pure Nothing
Just p -> fromValueMay @Path p
_ -> pure Nothing
fromValue v = fromValueMay v >>= \case
Just b -> pure b
_ -> throwError $ Expectation @t @f @m TPath (Free v)
instance Convertible e t f m
=> FromValue [NValue t f m] m (NValue' t f m (NValue t f m)) where
fromValueMay = \case
NVList' l -> pure $ Just l
_ -> pure Nothing
fromValue v = fromValueMay v >>= \case
Just b -> pure b
_ -> throwError $ Expectation @t @f @m TList (Free v)
instance ( Convertible e t f m
, FromValue a m (NValue t f m)
)
=> FromValue [a] m (Deeper (NValue' t f m (NValue t f m))) where
2019-03-18 23:27:12 +01:00
fromValueMay = \case
Deeper (NVList' l) -> sequence <$> traverse fromValueMay l
_ -> pure Nothing
fromValue v = fromValueMay v >>= \case
Just b -> pure b
_ -> throwError $ Expectation @t @f @m TList (Free (getDeeper v))
2019-03-18 23:27:12 +01:00
instance Convertible e t f m
=> FromValue (AttrSet (NValue t f m)) m (NValue' t f m (NValue t f m)) where
fromValueMay = \case
NVSet' s _ -> pure $ Just s
_ -> pure Nothing
fromValue v = fromValueMay v >>= \case
Just b -> pure b
_ -> throwError $ Expectation @t @f @m TSet (Free v)
instance ( Convertible e t f m
, FromValue a m (NValue t f m)
)
=> FromValue (AttrSet a) m (Deeper (NValue' t f m (NValue t f m))) where
2019-03-18 23:27:12 +01:00
fromValueMay = \case
Deeper (NVSet' s _) -> sequence <$> traverse fromValueMay s
_ -> pure Nothing
fromValue v = fromValueMay v >>= \case
Just b -> pure b
_ -> throwError $ Expectation @t @f @m TSet (Free (getDeeper v))
2019-03-18 23:27:12 +01:00
instance Convertible e t f m
=> FromValue (AttrSet (NValue t f m), AttrSet SourcePos) m
(NValue' t f m (NValue t f m)) where
fromValueMay = \case
NVSet' s p -> pure $ Just (s, p)
_ -> pure Nothing
fromValue v = fromValueMay v >>= \case
Just b -> pure b
_ -> throwError $ Expectation @t @f @m TSet (Free v)
2018-04-15 08:52:21 +02:00
instance ( Convertible e t f m
, FromValue a m (NValue t f m)
)
=> FromValue (AttrSet a, AttrSet SourcePos) m
(Deeper (NValue' t f m (NValue t f m))) where
2019-03-18 23:27:12 +01:00
fromValueMay = \case
2019-03-19 05:47:43 +01:00
Deeper (NVSet' s p) -> fmap (, p) <$> sequence <$> traverse fromValueMay s
2019-03-18 23:27:12 +01:00
_ -> pure Nothing
fromValue v = fromValueMay v >>= \case
Just b -> pure b
_ -> throwError $ Expectation @t @f @m TSet (Free (getDeeper v))
2019-03-18 23:27:12 +01:00
2019-03-19 02:20:07 +01:00
-- This instance needs IncoherentInstances, and only because of ToBuiltin
instance ( Convertible e t f m
, FromValue a m (NValue' t f m (NValue t f m))
)
2019-03-19 02:20:07 +01:00
=> FromValue a m (Deeper (NValue' t f m (NValue t f m))) where
2019-03-18 23:27:12 +01:00
fromValueMay = fromValueMay . getDeeper
fromValue = fromValue . getDeeper
{-----------------------------------------------------------------------
ToValue
-----------------------------------------------------------------------}
class ToValue a m v where
toValue :: a -> m v
instance (Convertible e t f m, ToValue a m (NValue' t f m (NValue t f m)))
=> ToValue a m (NValue t f m) where
toValue = fmap Free . toValue
2019-03-19 02:20:07 +01:00
instance ( Convertible e t f m
, ToValue a m (Deeper (NValue' t f m (NValue t f m)))
)
=> ToValue a m (Deeper (NValue t f m)) where
toValue = fmap (fmap Free) . toValue
instance Convertible e t f m
=> ToValue () m (NValue' t f m (NValue t f m)) where
toValue _ = pure . nvConstant' $ NNull
instance Convertible e t f m
=> ToValue Bool m (NValue' t f m (NValue t f m)) where
toValue = pure . nvConstant' . NBool
instance Convertible e t f m
=> ToValue Int m (NValue' t f m (NValue t f m)) where
toValue = pure . nvConstant' . NInt . toInteger
instance Convertible e t f m
=> ToValue Integer m (NValue' t f m (NValue t f m)) where
toValue = pure . nvConstant' . NInt
instance Convertible e t f m
=> ToValue Float m (NValue' t f m (NValue t f m)) where
toValue = pure . nvConstant' . NFloat
instance Convertible e t f m
=> ToValue NixString m (NValue' t f m (NValue t f m)) where
toValue = pure . nvStr'
instance Convertible e t f m
=> ToValue ByteString m (NValue' t f m (NValue t f m)) where
toValue = pure . nvStr' . hackyMakeNixStringWithoutContext . decodeUtf8
instance Convertible e t f m
=> ToValue Path m (NValue' t f m (NValue t f m)) where
toValue = pure . nvPath' . getPath
instance Convertible e t f m
=> ToValue StorePath m (NValue' t f m (NValue t f m)) where
toValue = toValue . Path . unStorePath
2018-11-16 23:06:34 +01:00
instance ( Convertible e t f m
)
=> ToValue SourcePos m (NValue' t f m (NValue t f m)) where
toValue (SourcePos f l c) = do
f' <- toValue (principledMakeNixStringWithoutContext (Text.pack f))
l' <- toValue (unPos l)
c' <- toValue (unPos c)
2019-03-19 05:47:43 +01:00
let pos = M.fromList [("file" :: Text, f'), ("line", l'), ("column", c')]
pure $ nvSet' pos mempty
2018-04-15 08:52:21 +02:00
2019-03-18 23:27:12 +01:00
-- | With 'ToValue', we can always act recursively
instance Convertible e t f m
=> ToValue [NValue t f m] m (NValue' t f m (NValue t f m)) where
2019-03-19 02:20:07 +01:00
toValue = pure . nvList'
instance (Convertible e t f m, ToValue a m (NValue t f m))
=> ToValue [a] m (Deeper (NValue' t f m (NValue t f m))) where
2019-03-19 02:20:07 +01:00
toValue = fmap (Deeper . nvList') . traverse toValue
instance Convertible e t f m
=> ToValue (AttrSet (NValue t f m)) m (NValue' t f m (NValue t f m)) where
2019-03-19 02:20:07 +01:00
toValue s = pure $ nvSet' s mempty
instance (Convertible e t f m, ToValue a m (NValue t f m))
=> ToValue (AttrSet a) m (Deeper (NValue' t f m (NValue t f m))) where
2019-03-19 02:20:07 +01:00
toValue s = (Deeper .) . nvSet' <$> traverse toValue s <*> pure mempty
instance Convertible e t f m
=> ToValue (AttrSet (NValue t f m), AttrSet SourcePos) m
(NValue' t f m (NValue t f m)) where
2019-03-19 02:20:07 +01:00
toValue (s, p) = pure $ nvSet' s p
instance (Convertible e t f m, ToValue a m (NValue t f m))
=> ToValue (AttrSet a, AttrSet SourcePos) m
(Deeper (NValue' t f m (NValue t f m))) where
2019-03-19 02:20:07 +01:00
toValue (s, p) = (Deeper .) . nvSet' <$> traverse toValue s <*> pure p
instance Convertible e t f m
=> ToValue NixLikeContextValue m (NValue' t f m (NValue t f m)) where
toValue nlcv = do
path <- if nlcvPath nlcv then Just <$> toValue True else return Nothing
allOutputs <- if nlcvAllOutputs nlcv
then Just <$> toValue True
else return Nothing
outputs <- do
let outputs =
fmap principledMakeNixStringWithoutContext $ nlcvOutputs nlcv
ts :: [NValue t f m] <- traverse toValue outputs
case ts of
[] -> return Nothing
_ -> Just <$> toValue ts
pure $ flip nvSet' M.empty $ M.fromList $ catMaybes
2019-03-19 05:47:43 +01:00
[ (\p -> ("path", p)) <$> path
, (\ao -> ("allOutputs", ao)) <$> allOutputs
2019-03-19 05:47:43 +01:00
, (\os -> ("outputs", os)) <$> outputs
]
instance Convertible e t f m => ToValue () m (NExprF (NValue t f m)) where
toValue _ = pure . NConstant $ NNull
2019-03-14 07:24:11 +01:00
instance Convertible e t f m => ToValue Bool m (NExprF (NValue t f m)) where
toValue = pure . NConstant . NBool