{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeOperators #-}
module Servant.Seo.Robots where
import Control.Lens
import Data.Coerce (coerce)
import Data.Kind (Type)
import Data.Text (Text)
import qualified Data.Text as Text
import GHC.Generics (Generic)
import GHC.TypeLits (KnownNat, KnownSymbol, Nat, symbolVal)
import Servant
import Text.Blaze (ToMarkup)
import Servant.Seo.Combinators
data RobotsInfo = RobotsInfo
{ _robotsSitemapPath :: Maybe ()
, _robotsDisallowedPaths :: [DisallowedPathPiece]
}
deriving (Show, Eq, Generic)
newtype DisallowedPathPiece = DisallowedPathPiece Text
deriving stock (Show, Eq, Generic)
deriving newtype (Ord, ToMarkup)
makeLenses ''RobotsInfo
instance Monoid RobotsInfo where
mempty = RobotsInfo Nothing []
instance Semigroup RobotsInfo where
RobotsInfo s1 d1 <> RobotsInfo s2 d2 = RobotsInfo (s1 <> s2) (d1 <> d2)
class HasRobots a where
toRobots :: Proxy a -> RobotsInfo
instance HasRobots Raw where
toRobots _ = mempty
instance HasRobots EmptyAPI where
toRobots _ = mempty
instance (HasRobots a, HasRobots b) => HasRobots (a :<|> b) where
toRobots _ = toRobots (Proxy :: Proxy a) <> toRobots (Proxy :: Proxy b)
instance HasRobots sub => HasRobots (WithNamedContext x c sub) where
toRobots _ = toRobots (Proxy :: Proxy sub)
instance HasRobots sub => HasRobots (HttpVersion :> sub) where
toRobots _ = toRobots (Proxy :: Proxy sub)
instance HasRobots sub => HasRobots (StreamBody' mods fr ct a :> sub) where
toRobots _ = toRobots (Proxy :: Proxy sub)
instance HasRobots sub => HasRobots (ReqBody' mods cs a :> sub) where
toRobots _ = toRobots (Proxy :: Proxy sub)
instance HasRobots sub => HasRobots (RemoteHost :> sub) where
toRobots _ = toRobots (Proxy :: Proxy sub)
instance HasRobots sub => HasRobots (QueryParam' mods sym a :> sub) where
toRobots _ = toRobots (Proxy :: Proxy sub)
instance HasRobots sub => HasRobots (QueryParams sym a :> sub) where
toRobots _ = toRobots (Proxy :: Proxy sub)
instance HasRobots sub => HasRobots (QueryFlag sym :> sub) where
toRobots _ = toRobots (Proxy :: Proxy sub)
instance HasRobots sub => HasRobots (Header' mods sym a :> sub) where
toRobots _ = toRobots (Proxy :: Proxy sub)
instance HasRobots sub => HasRobots (IsSecure :> sub) where
toRobots _ = toRobots (Proxy :: Proxy sub)
instance HasRobots api => HasRobots (Summary desc :> api) where
toRobots _ = toRobots (Proxy :: Proxy api)
instance HasRobots api => HasRobots (Description desc :> api) where
toRobots _ = toRobots (Proxy :: Proxy api)
instance (KnownSymbol sym, HasRobots sub) =>
HasRobots (Capture' mods sym a :> sub) where
toRobots _ = toRobots (Proxy :: Proxy sub)
instance HasRobots sub => HasRobots (CaptureAll sym a :> sub) where
toRobots _ = toRobots (Proxy :: Proxy sub)
instance HasRobots sub => HasRobots (Vault :> sub) where
toRobots _ = toRobots (Proxy :: Proxy sub)
instance (HasRobots sub, KnownSymbol sym) => HasRobots (sym :> sub) where
toRobots _ = toRobots (Proxy :: Proxy sub) & decide piece
where
piece = (Text.append "/" . Text.pack . symbolVal) (Proxy :: Proxy sym)
decide x ys@RobotsInfo{..} = case _robotsDisallowedPaths of
[] | piece /= "/sitemap.xml" -> ys
| otherwise -> ys & robotsSitemapPath ?~ ()
_ -> ys & robotsDisallowedPaths .~ (DisallowedPathPiece . Text.append x . coerce <$> (ys ^. robotsDisallowedPaths))
instance (HasRobots sub, KnownSymbol sym) => HasRobots (Disallow sym :> sub) where
toRobots _ = toRobots (Proxy :: Proxy sub) & addPath piece
where
piece = (DisallowedPathPiece . Text.append "/" . Text.pack . symbolVal) (Proxy :: Proxy sym)
addPath x xs = RobotsInfo Nothing [x] <> xs
instance {-# OVERLAPPABLE #-} (KnownNat status) => HasRobots (Verb method status cs NoContent) where
toRobots _ = mempty
instance {-# OVERLAPPABLE #-} (KnownNat status) =>
HasRobots (Verb method status cs a) where
toRobots _ = mempty
instance {-# OVERLAPPABLE #-} (KnownNat status) => HasRobots (Verb (method :: Type) (status :: Nat) (cs :: [Type]) (Headers hs NoContent)) where
toRobots _ = mempty
instance {-# OVERLAPPABLE #-} ( KnownNat status) => HasRobots (Verb (method :: Type) (status :: Nat) (cs :: [Type]) (Headers hs a)) where
toRobots _ = mempty
instance (HasPeriod period, HasRobots api) => HasRobots (Frequency period :> api) where
toRobots _ = toRobots (Proxy :: Proxy api)
instance (HasRobots api) =>
HasRobots (Priority priority :> api) where
toRobots _ = toRobots (Proxy :: Proxy api)