module Codec.GlTF.URI
  ( URI(..)
  , loadURI
  ) where

import Codec.GlTF.Prelude

import Data.ByteString (ByteString)
import Data.Text.Encoding (encodeUtf8)
import GHC.Stack (HasCallStack)

import qualified Data.ByteString.Base64 as Base64
import qualified Data.Text as Text

-- | The URI of the buffer or image.
--
-- Relative paths are relative to the .gltf file.
-- Instead of referencing an external file, the uri can also be a data-uri.
newtype URI = URI Text
  deriving (URI -> URI -> Bool
(URI -> URI -> Bool) -> (URI -> URI -> Bool) -> Eq URI
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: URI -> URI -> Bool
$c/= :: URI -> URI -> Bool
== :: URI -> URI -> Bool
$c== :: URI -> URI -> Bool
Eq, Eq URI
Eq URI
-> (URI -> URI -> Ordering)
-> (URI -> URI -> Bool)
-> (URI -> URI -> Bool)
-> (URI -> URI -> Bool)
-> (URI -> URI -> Bool)
-> (URI -> URI -> URI)
-> (URI -> URI -> URI)
-> Ord URI
URI -> URI -> Bool
URI -> URI -> Ordering
URI -> URI -> URI
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: URI -> URI -> URI
$cmin :: URI -> URI -> URI
max :: URI -> URI -> URI
$cmax :: URI -> URI -> URI
>= :: URI -> URI -> Bool
$c>= :: URI -> URI -> Bool
> :: URI -> URI -> Bool
$c> :: URI -> URI -> Bool
<= :: URI -> URI -> Bool
$c<= :: URI -> URI -> Bool
< :: URI -> URI -> Bool
$c< :: URI -> URI -> Bool
compare :: URI -> URI -> Ordering
$ccompare :: URI -> URI -> Ordering
$cp1Ord :: Eq URI
Ord, Int -> URI -> ShowS
[URI] -> ShowS
URI -> String
(Int -> URI -> ShowS)
-> (URI -> String) -> ([URI] -> ShowS) -> Show URI
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [URI] -> ShowS
$cshowList :: [URI] -> ShowS
show :: URI -> String
$cshow :: URI -> String
showsPrec :: Int -> URI -> ShowS
$cshowsPrec :: Int -> URI -> ShowS
Show, Value -> Parser [URI]
Value -> Parser URI
(Value -> Parser URI) -> (Value -> Parser [URI]) -> FromJSON URI
forall a.
(Value -> Parser a) -> (Value -> Parser [a]) -> FromJSON a
parseJSONList :: Value -> Parser [URI]
$cparseJSONList :: Value -> Parser [URI]
parseJSON :: Value -> Parser URI
$cparseJSON :: Value -> Parser URI
FromJSON, [URI] -> Encoding
[URI] -> Value
URI -> Encoding
URI -> Value
(URI -> Value)
-> (URI -> Encoding)
-> ([URI] -> Value)
-> ([URI] -> Encoding)
-> ToJSON URI
forall a.
(a -> Value)
-> (a -> Encoding)
-> ([a] -> Value)
-> ([a] -> Encoding)
-> ToJSON a
toEncodingList :: [URI] -> Encoding
$ctoEncodingList :: [URI] -> Encoding
toJSONList :: [URI] -> Value
$ctoJSONList :: [URI] -> Value
toEncoding :: URI -> Encoding
$ctoEncoding :: URI -> Encoding
toJSON :: URI -> Value
$ctoJSON :: URI -> Value
ToJSON, (forall x. URI -> Rep URI x)
-> (forall x. Rep URI x -> URI) -> Generic URI
forall x. Rep URI x -> URI
forall x. URI -> Rep URI x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep URI x -> URI
$cfrom :: forall x. URI -> Rep URI x
Generic)

loadURI :: HasCallStack => (FilePath -> IO (Either String ByteString)) -> URI -> IO (Either String ByteString)
loadURI :: (String -> IO (Either String ByteString))
-> URI -> IO (Either String ByteString)
loadURI String -> IO (Either String ByteString)
fileLoader (URI Text
uri) =
  if Text -> Text -> Bool
Text.isPrefixOf Text
"data:" Text
uri then
    case Text -> Text -> (Text, Text)
Text.breakOn Text
needle Text
uri of
      (Text
_prefix, Text
"") ->
        String -> IO (Either String ByteString)
forall a. HasCallStack => String -> a
error String
"Malformed data: URI"
      (Text
_prefix, Text
found) ->
        Either String ByteString -> IO (Either String ByteString)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Either String ByteString -> IO (Either String ByteString))
-> (Text -> Either String ByteString)
-> Text
-> IO (Either String ByteString)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Either String ByteString
Base64.decode (ByteString -> Either String ByteString)
-> (Text -> ByteString) -> Text -> Either String ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> ByteString
encodeUtf8 (Text -> IO (Either String ByteString))
-> Text -> IO (Either String ByteString)
forall a b. (a -> b) -> a -> b
$ Int -> Text -> Text
Text.drop (Text -> Int
Text.length Text
needle) Text
found
  else
    String -> IO (Either String ByteString)
fileLoader (Text -> String
Text.unpack Text
uri)
  where
    needle :: Text
needle = Text
";base64,"