{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeFamilies #-}

module Cardano.Api.Keys.Read
  ( readKeyFile
  , readKeyFileTextEnvelope
  , readKeyFileAnyOf
  )
where

import           Cardano.Api.DeserialiseAnyOf
import           Cardano.Api.Error
import           Cardano.Api.HasTypeProxy
import           Cardano.Api.IO
import           Cardano.Api.SerialiseBech32
import           Cardano.Api.SerialiseTextEnvelope
import           Cardano.Api.Utils

import           Control.Monad.Except (runExceptT)
import           Data.Bifunctor
import           Data.List.NonEmpty (NonEmpty)

-- | Read a cryptographic key from a file.
--
-- The contents of the file can either be Bech32-encoded, hex-encoded, or in
-- the text envelope format.
readKeyFile
  :: AsType a
  -> NonEmpty (InputFormat a)
  -> FilePath
  -> IO (Either (FileError InputDecodeError) a)
readKeyFile :: forall a.
AsType a
-> NonEmpty (InputFormat a)
-> FilePath
-> IO (Either (FileError InputDecodeError) a)
readKeyFile AsType a
asType NonEmpty (InputFormat a)
acceptedFormats FilePath
path = do
  Either (FileError InputDecodeError) ByteString
eContent <- ExceptT (FileError InputDecodeError) IO ByteString
-> IO (Either (FileError InputDecodeError) ByteString)
forall e (m :: * -> *) a. ExceptT e m a -> m (Either e a)
runExceptT (ExceptT (FileError InputDecodeError) IO ByteString
 -> IO (Either (FileError InputDecodeError) ByteString))
-> ExceptT (FileError InputDecodeError) IO ByteString
-> IO (Either (FileError InputDecodeError) ByteString)
forall a b. (a -> b) -> a -> b
$ FilePath
-> (FilePath -> IO ByteString)
-> ExceptT (FileError InputDecodeError) IO ByteString
forall (m :: * -> *) s e.
MonadIO m =>
FilePath -> (FilePath -> IO s) -> ExceptT (FileError e) m s
fileIOExceptT FilePath
path FilePath -> IO ByteString
readFileBlocking
  case Either (FileError InputDecodeError) ByteString
eContent of
    Left FileError InputDecodeError
e -> Either (FileError InputDecodeError) a
-> IO (Either (FileError InputDecodeError) a)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either (FileError InputDecodeError) a
 -> IO (Either (FileError InputDecodeError) a))
-> Either (FileError InputDecodeError) a
-> IO (Either (FileError InputDecodeError) a)
forall a b. (a -> b) -> a -> b
$ FileError InputDecodeError -> Either (FileError InputDecodeError) a
forall a b. a -> Either a b
Left FileError InputDecodeError
e
    Right ByteString
content ->
      Either (FileError InputDecodeError) a
-> IO (Either (FileError InputDecodeError) a)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either (FileError InputDecodeError) a
 -> IO (Either (FileError InputDecodeError) a))
-> (Either InputDecodeError a
    -> Either (FileError InputDecodeError) a)
-> Either InputDecodeError a
-> IO (Either (FileError InputDecodeError) a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (InputDecodeError -> FileError InputDecodeError)
-> Either InputDecodeError a
-> Either (FileError InputDecodeError) a
forall a b c. (a -> b) -> Either a c -> Either b c
forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first (FilePath -> InputDecodeError -> FileError InputDecodeError
forall e. FilePath -> e -> FileError e
FileError FilePath
path) (Either InputDecodeError a
 -> IO (Either (FileError InputDecodeError) a))
-> Either InputDecodeError a
-> IO (Either (FileError InputDecodeError) a)
forall a b. (a -> b) -> a -> b
$ AsType a
-> NonEmpty (InputFormat a)
-> ByteString
-> Either InputDecodeError a
forall a.
AsType a
-> NonEmpty (InputFormat a)
-> ByteString
-> Either InputDecodeError a
deserialiseInput AsType a
asType NonEmpty (InputFormat a)
acceptedFormats ByteString
content

-- | Read a cryptographic key from a file.
--
-- The contents of the file must be in the text envelope format.
readKeyFileTextEnvelope
  :: HasTextEnvelope a
  => AsType a
  -> File content In
  -> IO (Either (FileError InputDecodeError) a)
readKeyFileTextEnvelope :: forall a content.
HasTextEnvelope a =>
AsType a
-> File content 'In -> IO (Either (FileError InputDecodeError) a)
readKeyFileTextEnvelope AsType a
asType File content 'In
fp =
  (FileError TextEnvelopeError -> FileError InputDecodeError)
-> Either (FileError TextEnvelopeError) a
-> Either (FileError InputDecodeError) a
forall a b c. (a -> b) -> Either a c -> Either b c
forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first ((TextEnvelopeError -> InputDecodeError)
-> FileError TextEnvelopeError -> FileError InputDecodeError
forall a b. (a -> b) -> FileError a -> FileError b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap TextEnvelopeError -> InputDecodeError
InputTextEnvelopeError) (Either (FileError TextEnvelopeError) a
 -> Either (FileError InputDecodeError) a)
-> IO (Either (FileError TextEnvelopeError) a)
-> IO (Either (FileError InputDecodeError) a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> AsType a
-> File content 'In -> IO (Either (FileError TextEnvelopeError) a)
forall a content.
HasTextEnvelope a =>
AsType a
-> File content 'In -> IO (Either (FileError TextEnvelopeError) a)
readFileTextEnvelope AsType a
asType File content 'In
fp

-- | Read a cryptographic key from a file given that it is one of the provided
-- types.
--
-- The contents of the file can either be Bech32-encoded or in the text
-- envelope format.
readKeyFileAnyOf
  :: forall content b
   . [FromSomeType SerialiseAsBech32 b]
  -> [FromSomeType HasTextEnvelope b]
  -> File content In
  -> IO (Either (FileError InputDecodeError) b)
readKeyFileAnyOf :: forall content b.
[FromSomeType SerialiseAsBech32 b]
-> [FromSomeType HasTextEnvelope b]
-> File content 'In
-> IO (Either (FileError InputDecodeError) b)
readKeyFileAnyOf [FromSomeType SerialiseAsBech32 b]
bech32Types [FromSomeType HasTextEnvelope b]
textEnvTypes File content 'In
path = do
  Either (FileError InputDecodeError) ByteString
eContent <- ExceptT (FileError InputDecodeError) IO ByteString
-> IO (Either (FileError InputDecodeError) ByteString)
forall e (m :: * -> *) a. ExceptT e m a -> m (Either e a)
runExceptT (ExceptT (FileError InputDecodeError) IO ByteString
 -> IO (Either (FileError InputDecodeError) ByteString))
-> ExceptT (FileError InputDecodeError) IO ByteString
-> IO (Either (FileError InputDecodeError) ByteString)
forall a b. (a -> b) -> a -> b
$ FilePath
-> (FilePath -> IO ByteString)
-> ExceptT (FileError InputDecodeError) IO ByteString
forall (m :: * -> *) s e.
MonadIO m =>
FilePath -> (FilePath -> IO s) -> ExceptT (FileError e) m s
fileIOExceptT (File content 'In -> FilePath
forall content (direction :: FileDirection).
File content direction -> FilePath
unFile File content 'In
path) FilePath -> IO ByteString
readFileBlocking
  case Either (FileError InputDecodeError) ByteString
eContent of
    Left FileError InputDecodeError
e -> Either (FileError InputDecodeError) b
-> IO (Either (FileError InputDecodeError) b)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either (FileError InputDecodeError) b
 -> IO (Either (FileError InputDecodeError) b))
-> Either (FileError InputDecodeError) b
-> IO (Either (FileError InputDecodeError) b)
forall a b. (a -> b) -> a -> b
$ FileError InputDecodeError -> Either (FileError InputDecodeError) b
forall a b. a -> Either a b
Left FileError InputDecodeError
e
    Right ByteString
content ->
      Either (FileError InputDecodeError) b
-> IO (Either (FileError InputDecodeError) b)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either (FileError InputDecodeError) b
 -> IO (Either (FileError InputDecodeError) b))
-> (Either InputDecodeError b
    -> Either (FileError InputDecodeError) b)
-> Either InputDecodeError b
-> IO (Either (FileError InputDecodeError) b)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (InputDecodeError -> FileError InputDecodeError)
-> Either InputDecodeError b
-> Either (FileError InputDecodeError) b
forall a b c. (a -> b) -> Either a c -> Either b c
forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first (FilePath -> InputDecodeError -> FileError InputDecodeError
forall e. FilePath -> e -> FileError e
FileError (File content 'In -> FilePath
forall content (direction :: FileDirection).
File content direction -> FilePath
unFile File content 'In
path)) (Either InputDecodeError b
 -> IO (Either (FileError InputDecodeError) b))
-> Either InputDecodeError b
-> IO (Either (FileError InputDecodeError) b)
forall a b. (a -> b) -> a -> b
$ [FromSomeType SerialiseAsBech32 b]
-> [FromSomeType HasTextEnvelope b]
-> ByteString
-> Either InputDecodeError b
forall b.
[FromSomeType SerialiseAsBech32 b]
-> [FromSomeType HasTextEnvelope b]
-> ByteString
-> Either InputDecodeError b
deserialiseInputAnyOf [FromSomeType SerialiseAsBech32 b]
bech32Types [FromSomeType HasTextEnvelope b]
textEnvTypes ByteString
content