{-# LANGUAGE OverloadedStrings, GeneralizedNewtypeDeriving #-}
module Clay.Property where
import Control.Arrow (second)
import Data.Fixed (Fixed, HasResolution (resolution), showFixed)
import Data.List (partition, sort)
import Data.List.NonEmpty (NonEmpty, toList)
import Data.Maybe
import Data.Semigroup
import Data.String
import Data.Text (Text, replace)
data Prefixed = Prefixed { unPrefixed :: [(Text, Text)] } | Plain { unPlain :: Text }
deriving (Show, Eq)
instance IsString Prefixed where
fromString s = Plain (fromString s)
instance Semigroup Prefixed where
(<>) = merge
instance Monoid Prefixed where
mempty = ""
mappend = (<>)
merge :: Prefixed -> Prefixed -> Prefixed
merge (Plain x ) (Plain y ) = Plain (x <> y)
merge (Plain x ) (Prefixed ys) = Prefixed (map (second (x <>)) ys)
merge (Prefixed xs) (Plain y ) = Prefixed (map (second (<> y)) xs)
merge (Prefixed xs) (Prefixed ys) =
let kys = map fst ys
kxs = map fst xs
in Prefixed $ zipWith (\(p, a) (_, b) -> (p, a <> b))
(sort (fst (partition ((`elem` kys) . fst) xs)))
(sort (fst (partition ((`elem` kxs) . fst) ys)))
plain :: Prefixed -> Text
plain (Prefixed xs) = "" `fromMaybe` lookup "" xs
plain (Plain p ) = p
quote :: Text -> Text
quote t = "\"" <> replace "\"" "\\\"" t <> "\""
newtype Key a = Key { unKeys :: Prefixed }
deriving (Show, Semigroup, Monoid, IsString)
cast :: Key a -> Key ()
cast (Key k) = Key k
newtype Value = Value { unValue :: Prefixed }
deriving (Show, Semigroup, Monoid, IsString, Eq)
class Val a where
value :: a -> Value
instance Val Text where
value t = Value (Plain t)
newtype Literal = Literal Text
deriving (Show, Semigroup, Monoid, IsString)
instance Val Literal where
value (Literal t) = Value (Plain (quote t))
instance Val Integer where
value = fromString . show
data E5 = E5
instance HasResolution E5 where resolution _ = 100000
instance Val Double where
value = Value . Plain . cssDoubleText
cssDoubleText :: Double -> Text
cssDoubleText = fromString . showFixed' . realToFrac
where
showFixed' :: Fixed E5 -> String
showFixed' = showFixed True
instance Val Value where
value = id
instance Val a => Val (Maybe a) where
value Nothing = ""
value (Just a) = value a
instance (Val a, Val b) => Val (a, b) where
value (a, b) = value a <> " " <> value b
instance (Val a, Val b) => Val (Either a b) where
value (Left a) = value a
value (Right a) = value a
instance Val a => Val [a] where
value xs = intercalate "," (map value xs)
instance Val a => Val (NonEmpty a) where
value = value . toList
intercalate :: Monoid a => a -> [a] -> a
intercalate _ [] = mempty
intercalate s (x:xs) = foldl (\a b -> a `mappend` s `mappend` b) x xs
noCommas :: Val a => [a] -> Value
noCommas xs = intercalate " " (map value xs)
infixr !
(!) :: a -> b -> (a, b)
(!) = (,)