-- | String formatting utilities

module Hydra.Util.Formatting where

import qualified Hydra.Lib.Strings as Strings

import qualified Data.Char as C
import qualified Data.List as L
import qualified Data.Map as M
import qualified Data.Set as S
import qualified Data.Maybe as Y


data CaseConvention = CaseConventionCamel | CaseConventionPascal | CaseConventionLowerSnake | CaseConventionUpperSnake

capitalize :: String -> String
capitalize :: String -> String
capitalize String
s = case String
s of
  [] -> []
  (Char
h:String
r) -> Char -> Char
C.toUpper Char
h forall a. a -> [a] -> [a]
: String
r

convertCase :: CaseConvention -> CaseConvention -> String -> String
convertCase :: CaseConvention -> CaseConvention -> String -> String
convertCase CaseConvention
from CaseConvention
to String
original = case CaseConvention
to of
    CaseConvention
CaseConventionCamel -> String -> String
decapitalize forall a b. (a -> b) -> a -> b
$ forall (t :: * -> *) a. Foldable t => t [a] -> [a]
L.concat (String -> String
capitalize forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Char -> Char
C.toLower forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [String]
parts)
    CaseConvention
CaseConventionPascal -> forall (t :: * -> *) a. Foldable t => t [a] -> [a]
L.concat (String -> String
capitalize forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Char -> Char
C.toLower forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [String]
parts)
    CaseConvention
CaseConventionLowerSnake -> forall a. [a] -> [[a]] -> [a]
L.intercalate String
"_" (forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Char -> Char
C.toLower forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [String]
parts)
    CaseConvention
CaseConventionUpperSnake -> forall a. [a] -> [[a]] -> [a]
L.intercalate String
"_" (forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Char -> Char
C.toUpper forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [String]
parts)
  where
    parts :: [String]
parts = case CaseConvention
from of
      CaseConvention
CaseConventionCamel -> [String]
byCaps
      CaseConvention
CaseConventionPascal -> [String]
byCaps
      CaseConvention
CaseConventionLowerSnake -> [String]
byUnderscores
      CaseConvention
CaseConventionUpperSnake -> [String]
byUnderscores
    byUnderscores :: [String]
byUnderscores = String -> String -> [String]
Strings.splitOn String
"_" String
original
    byCaps :: [String]
byCaps = forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
L.foldl [String] -> Char -> [String]
helper [String
""] forall a b. (a -> b) -> a -> b
$ forall a. [a] -> [a]
L.reverse forall a b. (a -> b) -> a -> b
$ String -> String
decapitalize String
original
      where
        helper :: [String] -> Char -> [String]
helper (String
h:[String]
r) Char
c = [String
"" | Char -> Bool
C.isUpper Char
c] forall a. [a] -> [a] -> [a]
++ ((Char
cforall a. a -> [a] -> [a]
:String
h)forall a. a -> [a] -> [a]
:[String]
r)

decapitalize :: String -> String
decapitalize :: String -> String
decapitalize String
s = case String
s of
  [] -> []
  (Char
h:String
r) -> Char -> Char
C.toLower Char
h forall a. a -> [a] -> [a]
: String
r

escapeWithUnderscore :: S.Set String -> String -> String
escapeWithUnderscore :: Set String -> String -> String
escapeWithUnderscore Set String
reserved String
s = if forall a. Ord a => a -> Set a -> Bool
S.member String
s Set String
reserved then String
s forall a. [a] -> [a] -> [a]
++ String
"_" else String
s

indentLines :: String -> String
indentLines :: String -> String
indentLines String
s = [String] -> String
unlines (String -> String
indent forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> [String]
lines String
s)
  where
    indent :: String -> String
indent String
l = String
"    " forall a. [a] -> [a] -> [a]
++ String
l

javaStyleComment :: String -> String
javaStyleComment :: String -> String
javaStyleComment String
s = String
"/**\n" forall a. [a] -> [a] -> [a]
++ String
" * " forall a. [a] -> [a] -> [a]
++ String
s forall a. [a] -> [a] -> [a]
++ String
"\n */"

nonAlnumToUnderscores :: String -> String
nonAlnumToUnderscores :: String -> String
nonAlnumToUnderscores = forall a. [a] -> [a]
L.reverse forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a, b) -> a
fst forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
L.foldl (String, Bool) -> Char -> (String, Bool)
replace ([], Bool
False)
  where
    replace :: (String, Bool) -> Char -> (String, Bool)
replace (String
s, Bool
b) Char
c = if Char -> Bool
isAlnum Char
c
      then (Char
cforall a. a -> [a] -> [a]
:String
s, Bool
False)
      else if Bool
b
        then (String
s, Bool
True)
        else (Char
'_'forall a. a -> [a] -> [a]
:String
s, Bool
True)
    isAlnum :: Char -> Bool
isAlnum Char
c = (Char
c forall a. Ord a => a -> a -> Bool
>= Char
'A' Bool -> Bool -> Bool
&& Char
c forall a. Ord a => a -> a -> Bool
<= Char
'Z')
      Bool -> Bool -> Bool
|| (Char
c forall a. Ord a => a -> a -> Bool
>= Char
'a' Bool -> Bool -> Bool
&& Char
c forall a. Ord a => a -> a -> Bool
<= Char
'z')
      Bool -> Bool -> Bool
|| (Char
c forall a. Ord a => a -> a -> Bool
>= Char
'0' Bool -> Bool -> Bool
&& Char
c forall a. Ord a => a -> a -> Bool
<= Char
'9')

sanitizeWithUnderscores :: S.Set String -> String -> String
sanitizeWithUnderscores :: Set String -> String -> String
sanitizeWithUnderscores Set String
reserved = Set String -> String -> String
escapeWithUnderscore Set String
reserved forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> String
nonAlnumToUnderscores

toLower :: String -> String
toLower :: String -> String
toLower = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Char -> Char
C.toLower

toUpper :: String -> String
toUpper :: String -> String
toUpper = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Char -> Char
C.toLower

withCharacterAliases :: String -> String
withCharacterAliases :: String -> String
withCharacterAliases String
original = forall a. (a -> Bool) -> [a] -> [a]
L.filter Char -> Bool
C.isAlphaNum forall a b. (a -> b) -> a -> b
$ forall (t :: * -> *) a. Foldable t => t [a] -> [a]
L.concat forall a b. (a -> b) -> a -> b
$ Char -> String
alias forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String
original
  where
    alias :: Char -> String
alias Char
c = forall b a. b -> (a -> b) -> Maybe a -> b
Y.maybe [Char
c] String -> String
capitalize forall a b. (a -> b) -> a -> b
$ forall k a. Ord k => k -> Map k a -> Maybe a
M.lookup (Char -> Int
C.ord Char
c) Map Int String
aliases

    -- Taken from: https://cs.stanford.edu/people/miles/iso8859.html
    aliases :: Map Int String
aliases = forall k a. Ord k => [(k, a)] -> Map k a
M.fromList [
      (Int
32, String
"sp"),
      (Int
33, String
"excl"),
      (Int
34, String
"quot"),
      (Int
35, String
"num"),
      (Int
36, String
"dollar"),
      (Int
37, String
"percnt"),
      (Int
38, String
"amp"),
      (Int
39, String
"apos"),
      (Int
40, String
"lpar"),
      (Int
41, String
"rpar"),
      (Int
42, String
"ast"),
      (Int
43, String
"plus"),
      (Int
44, String
"comma"),
      (Int
45, String
"minus"),
      (Int
46, String
"period"),
      (Int
47, String
"sol"),
      (Int
58, String
"colon"),
      (Int
59, String
"semi"),
      (Int
60, String
"lt"),
      (Int
61, String
"equals"),
      (Int
62, String
"gt"),
      (Int
63, String
"quest"),
      (Int
64, String
"commat"),
      (Int
91, String
"lsqb"),
      (Int
92, String
"bsol"),
      (Int
93, String
"rsqb"),
      (Int
94, String
"circ"),
      (Int
95, String
"lowbar"),
      (Int
96, String
"grave"),
      (Int
123, String
"lcub"),
      (Int
124, String
"verbar"),
      (Int
125, String
"rcub"),
      (Int
126, String
"tilde")]