mirror of
https://github.com/urbit/shrub.git
synced 2024-12-20 17:32:11 +03:00
189 lines
4.7 KiB
Haskell
189 lines
4.7 KiB
Haskell
module Arvo.Common
|
|
( NounTree(..), NounMap, NounSet
|
|
, Json, JsonNode(..)
|
|
, Desk(..), Mime(..)
|
|
, AtomIf, AtomIs, Lane(..), Port(..), Turf(..)
|
|
, HttpServerConf(..), HttpEvent(..), PEM, Method, Header
|
|
, Routed(..), Reorgd(..), organized, reorg, disorg
|
|
) where
|
|
|
|
import Urbit.Time
|
|
import UrbitPrelude hiding (Term)
|
|
|
|
import qualified Network.HTTP.Types.Method as H
|
|
|
|
|
|
-- Misc Types ------------------------------------------------------------------
|
|
|
|
type AtomIf = Atom -- @if (TODO: What does this mean?)
|
|
type AtomIs = Atom -- @is (TODO: What does this mean?)
|
|
|
|
-- Domain Name
|
|
newtype Turf = Turf { unTurf :: [Cord] }
|
|
deriving newtype (Eq, Ord, Show, ToNoun, FromNoun)
|
|
|
|
|
|
-- Http Common -----------------------------------------------------------------
|
|
|
|
data Header = Header Cord Bytes
|
|
deriving (Eq, Ord, Show)
|
|
|
|
data ResponseHeader = ResponseHeader
|
|
{ statusCode :: Word
|
|
, headers :: [Header]
|
|
}
|
|
deriving (Eq, Ord, Show)
|
|
|
|
data HttpEvent
|
|
= Start ResponseHeader (Maybe File) Bool
|
|
| Continue (Maybe File) Bool
|
|
| Cancel ()
|
|
deriving (Eq, Ord, Show)
|
|
|
|
deriveNoun ''ResponseHeader
|
|
deriveNoun ''Header
|
|
deriveNoun ''HttpEvent
|
|
|
|
|
|
-- Http Requests ---------------------------------------------------------------
|
|
|
|
type Method = H.StdMethod
|
|
|
|
-- TODO Hack! Don't write instances for library types. Write them for
|
|
-- our types instead.
|
|
|
|
instance ToNoun H.StdMethod where
|
|
toNoun = toNoun . Cord . decodeUtf8 . H.renderStdMethod
|
|
|
|
instance FromNoun H.StdMethod where
|
|
parseNoun n = named "StdMethod" $ do
|
|
Cord m <- parseNoun n
|
|
case H.parseMethod (encodeUtf8 m) of
|
|
Left bs -> fail ("Unexpected method: " <> unpack (decodeUtf8 bs))
|
|
Right m -> pure m
|
|
|
|
|
|
|
|
-- Http Server Configuration ---------------------------------------------------
|
|
|
|
newtype PEM = PEM Cord
|
|
deriving newtype (Eq, Ord, Show, ToNoun, FromNoun)
|
|
|
|
type Key = PEM
|
|
type Cert = PEM
|
|
|
|
data HttpServerConf = HttpServerConf
|
|
{ secure :: Maybe (Key, Cert)
|
|
, proxy :: Bool
|
|
, log :: Bool
|
|
, redirect :: Bool
|
|
}
|
|
deriving (Eq, Ord, Show)
|
|
|
|
deriveNoun ''HttpServerConf
|
|
|
|
|
|
-- Desk and Mime ---------------------------------------------------------------
|
|
|
|
newtype Desk = Desk { unDesk :: Cord }
|
|
deriving newtype (Eq, Ord, Show, ToNoun, FromNoun)
|
|
|
|
data Mime = Mime Path File
|
|
deriving (Eq, Ord, Show)
|
|
|
|
deriveNoun ''Mime
|
|
|
|
|
|
-- Trees, Maps, and Sets -------------------------------------------------------
|
|
|
|
data NounTreeNode a = HTN
|
|
{ ntnNode :: a
|
|
, ntnLeft :: NounTree a
|
|
, ntnRite :: NounTree a
|
|
}
|
|
deriving (Eq, Ord, Show)
|
|
|
|
type NounTree a = Nullable (NounTreeNode a)
|
|
|
|
type NounMap k v = NounTree (k, v)
|
|
type NounSet a = NounTree a
|
|
|
|
deriveNoun ''NounTreeNode
|
|
|
|
|
|
-- Json ------------------------------------------------------------------------
|
|
|
|
type Json = Nullable JsonNode
|
|
|
|
data JsonNode
|
|
= JNA [Json]
|
|
| JNB Bool
|
|
| JNO (NounMap Cord Json)
|
|
| JNN Knot
|
|
| JNS Cord
|
|
deriving (Eq, Ord, Show)
|
|
|
|
deriveNoun ''JsonNode
|
|
|
|
|
|
-- Lanes -----------------------------------------------------------------------
|
|
|
|
-- Network Port
|
|
newtype Port = Port { unPort :: Word }
|
|
deriving newtype (Eq, Ord, Show, ToNoun, FromNoun)
|
|
|
|
data Lane
|
|
= If Wen Port AtomIf
|
|
| Is Atom (Maybe Lane) AtomIs
|
|
| Ix Wen Port AtomIf
|
|
deriving (Eq, Ord, Show)
|
|
|
|
deriveNoun ''Lane
|
|
|
|
|
|
-- Path+Tagged Restructuring ---------------------------------------------------
|
|
|
|
data Routed = Routed (Cord, Cord, EvilPath) (Cord, Noun)
|
|
|
|
data Reorgd = Reorgd Cord Cord Cord EvilPath Noun
|
|
|
|
deriveNoun ''Reorgd
|
|
deriveToNoun ''Routed
|
|
|
|
instance FromNoun Routed where
|
|
parseNoun = named "Routed" . \case
|
|
A _ -> expected "got atom"
|
|
C (A _) _ -> expected "empty route"
|
|
C _ (A _) -> expected "value not tagged"
|
|
C (C _ (A _)) (C _ _) -> expected "route is too short"
|
|
C (C f (C s p)) (C t v) -> do
|
|
f <- named "first-route" $ parseNoun f
|
|
s <- named "second-route" $ parseNoun s
|
|
p <- named "rest-of-route" $ parseNoun p
|
|
t <- named "tag" $ parseNoun t
|
|
pure (Routed (f, s, p) (t, v))
|
|
where
|
|
expected got = fail ("expected route+tagged; " <> got)
|
|
|
|
organized :: Iso' Routed Reorgd
|
|
organized = iso to from
|
|
where
|
|
to = \case Routed (b, r, p) (t, v) -> Reorgd b r t p v
|
|
from = \case Reorgd b r t p v -> Routed (b, r, p) (t, v)
|
|
|
|
reorg :: Routed -> Noun
|
|
reorg = toNoun . view organized
|
|
|
|
{-
|
|
This code may crash, but only if the FromNoun/ToNoun instances for
|
|
the effects are incorrect.
|
|
-}
|
|
disorg :: Noun -> Noun
|
|
disorg = toNoun . view (from organized) . fromNounCrash
|
|
where
|
|
fromNounCrash :: FromNoun a => Noun -> a
|
|
fromNounCrash =
|
|
fromNounErr >>> \case
|
|
Left err -> error (show err)
|
|
Right vl -> vl
|