-- |
-- Module      : Amazonka.Data.Path
-- Copyright   : (c) 2013-2021 Brendan Hay
-- License     : Mozilla Public License, v. 2.0.
-- Maintainer  : Brendan Hay <brendan.g.hay+amazonka@gmail.com>
-- Stability   : provisional
-- Portability : non-portable (GHC extensions)
module Amazonka.Data.Path
  ( -- * Path Types
    Path (..),
    RawPath,
    EscapedPath,

    -- * Constructing Paths
    ToPath (..),
    rawPath,

    -- * Manipulating Paths
    escapePath,
    collapsePath,
  )
where

import Amazonka.Data.ByteString
import Amazonka.Data.Text
import Amazonka.Prelude
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as BS8
import qualified Network.HTTP.Types.URI as URI

class ToPath a where
  toPath :: a -> ByteString

instance ToPath ByteString where
  toPath :: ByteString -> ByteString
toPath = ByteString -> ByteString
forall a. a -> a
id

instance ToPath Text where
  toPath :: Text -> ByteString
toPath = Text -> ByteString
forall a. ToByteString a => a -> ByteString
toBS

rawPath :: ToPath a => a -> Path 'NoEncoding
rawPath :: a -> Path 'NoEncoding
rawPath = [ByteString] -> Path 'NoEncoding
Raw ([ByteString] -> Path 'NoEncoding)
-> (a -> [ByteString]) -> a -> Path 'NoEncoding
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [ByteString] -> [ByteString]
strip ([ByteString] -> [ByteString])
-> (a -> [ByteString]) -> a -> [ByteString]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Char -> ByteString -> [ByteString]
BS8.split Char
sep (ByteString -> [ByteString])
-> (a -> ByteString) -> a -> [ByteString]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> ByteString
forall a. ToPath a => a -> ByteString
toPath
  where
    strip :: [ByteString] -> [ByteString]
strip (ByteString
x : [ByteString]
xs)
      | ByteString -> Bool
BS.null ByteString
x = [ByteString]
xs
    strip [ByteString]
xs = [ByteString]
xs

data Encoding = NoEncoding | Percent
  deriving stock (Encoding -> Encoding -> Bool
(Encoding -> Encoding -> Bool)
-> (Encoding -> Encoding -> Bool) -> Eq Encoding
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Encoding -> Encoding -> Bool
$c/= :: Encoding -> Encoding -> Bool
== :: Encoding -> Encoding -> Bool
$c== :: Encoding -> Encoding -> Bool
Eq, Int -> Encoding -> ShowS
[Encoding] -> ShowS
Encoding -> String
(Int -> Encoding -> ShowS)
-> (Encoding -> String) -> ([Encoding] -> ShowS) -> Show Encoding
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Encoding] -> ShowS
$cshowList :: [Encoding] -> ShowS
show :: Encoding -> String
$cshow :: Encoding -> String
showsPrec :: Int -> Encoding -> ShowS
$cshowsPrec :: Int -> Encoding -> ShowS
Show)

data Path :: Encoding -> * where
  Raw :: [ByteString] -> Path 'NoEncoding
  Encoded :: [ByteString] -> Path 'Percent

deriving stock instance Show (Path a)

deriving stock instance Eq (Path a)

type RawPath = Path 'NoEncoding

type EscapedPath = Path 'Percent

instance Semigroup RawPath where
  Raw [ByteString]
xs <> :: Path 'NoEncoding -> Path 'NoEncoding -> Path 'NoEncoding
<> Raw [ByteString]
ys = [ByteString] -> Path 'NoEncoding
Raw ([ByteString]
xs [ByteString] -> [ByteString] -> [ByteString]
forall a. [a] -> [a] -> [a]
++ [ByteString]
ys)

instance Monoid RawPath where
  mempty :: Path 'NoEncoding
mempty = [ByteString] -> Path 'NoEncoding
Raw []
  mappend :: Path 'NoEncoding -> Path 'NoEncoding -> Path 'NoEncoding
mappend = Path 'NoEncoding -> Path 'NoEncoding -> Path 'NoEncoding
forall a. Semigroup a => a -> a -> a
(<>)

instance ToByteString EscapedPath where
  toBS :: EscapedPath -> ByteString
toBS (Encoded []) = ByteString
slash
  toBS (Encoded [ByteString]
xs) = ByteString
slash ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString -> [ByteString] -> ByteString
BS8.intercalate ByteString
slash [ByteString]
xs

escapePath :: Path a -> EscapedPath
escapePath :: Path a -> EscapedPath
escapePath (Raw [ByteString]
xs) = [ByteString] -> EscapedPath
Encoded ((ByteString -> ByteString) -> [ByteString] -> [ByteString]
forall a b. (a -> b) -> [a] -> [b]
map (Bool -> ByteString -> ByteString
URI.urlEncode Bool
True) [ByteString]
xs)
escapePath (Encoded [ByteString]
xs) = [ByteString] -> EscapedPath
Encoded [ByteString]
xs

collapsePath :: Path a -> Path a
collapsePath :: Path a -> Path a
collapsePath = \case
  Raw [ByteString]
xs -> [ByteString] -> Path 'NoEncoding
Raw ([ByteString] -> [ByteString]
go [ByteString]
xs)
  Encoded [ByteString]
xs -> [ByteString] -> EscapedPath
Encoded ([ByteString] -> [ByteString]
go [ByteString]
xs)
  where
    go :: [ByteString] -> [ByteString]
go = [ByteString] -> [ByteString]
forall a. [a] -> [a]
reverse ([ByteString] -> [ByteString])
-> ([ByteString] -> [ByteString]) -> [ByteString] -> [ByteString]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [ByteString] -> [ByteString]
f ([ByteString] -> [ByteString])
-> ([ByteString] -> [ByteString]) -> [ByteString] -> [ByteString]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [ByteString] -> [ByteString]
forall a. [a] -> [a]
reverse

    f :: [ByteString] -> [ByteString]
    f :: [ByteString] -> [ByteString]
f [] = []
    f (ByteString
x : [ByteString]
xs)
      | ByteString
x ByteString -> ByteString -> Bool
forall a. Eq a => a -> a -> Bool
== ByteString
dot = [ByteString] -> [ByteString]
f [ByteString]
xs
      | ByteString
x ByteString -> ByteString -> Bool
forall a. Eq a => a -> a -> Bool
== ByteString
dots = Int -> [ByteString] -> [ByteString]
forall a. Int -> [a] -> [a]
drop Int
1 ([ByteString] -> [ByteString]
f [ByteString]
xs)
      | Bool
otherwise = ByteString
x ByteString -> [ByteString] -> [ByteString]
forall a. a -> [a] -> [a]
: [ByteString] -> [ByteString]
f [ByteString]
xs

    dot :: ByteString
dot = ByteString
"."
    dots :: ByteString
dots = ByteString
".."

slash :: ByteString
slash :: ByteString
slash = Char -> ByteString
BS8.singleton Char
sep

sep :: Char
sep :: Char
sep = Char
'/'