netw-0.1.0.0: src/Netw/Internal/Port.hs
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE LambdaCase #-}
module Netw.Internal.Port where
import Netw.Internal.Type
import Data.List
import Data.Char
import qualified Data.List.NonEmpty as N
import Data.Word
import Language.Haskell.TH
declarePorts :: Name -> DecsQ
declarePorts constructor =
-- runIO (print . grpOnNameAndPort =<< servents) >> return []
runIO servents >>= emit . concatMap service . grpOnNameAndPort
where emit :: [(String, Word16)] -> DecsQ
emit (sortOn fst -> ports) =
let grp1 name (a:as)
| name == fst a = (a:) <$> grp1 name as
| otherwise = (a:as,[])
grp1 _ [] = ([], [])
grp2 (a:as) =
let (as', p) = grp1 (fst a) as
in (a N.:| p):grp2 as'
grp2 [] = []
list =
let f (a N.:| as)
| null as = [a]
| otherwise = map (\ (name, port) -> (name ++ '_' : show port, port)) (a:as)
in concatMap f (grp2 ports)
-- in runIO (print list) >> return []
in sequence [ patSynD (mkName name)
(prefixPatSyn [])
implBidir
(conP constructor [w16ToLit port])
| (name, port) <- list ]
service :: (String, N.NonEmpty (Word16, N.NonEmpty String)) -> [(String, Word16)]
service (name, pp N.:| pps)
| null pps = [("IPPORT_" ++ normalize name, fst pp)]
| otherwise = concat
[
[ ("IPPORT_" ++ normalize name ++ '_' : normalize proto, port)
| proto <- N.toList protos]
| (port, protos) <- pp:pps]
w16ToLit = litP . integerL . fromIntegral
stage1 :: [Servent] -> [(String, N.NonEmpty (Word16, String))]
stage1 (sortOn sName -> db) =
let grp1 name (e@(sName -> name'):es)
| name == name' = ((sPort e, sProto e):) <$> grp1 name es
| otherwise = (e:es, [])
grp1 _ [] = ([], [])
grp2 (e@(sName -> name):es) =
let (es', ports) = grp1 name es
in (name, (sPort e, sProto e) N.:| ports):grp2 es'
grp2 [] = []
in grp2 db
stage2 :: [(String, N.NonEmpty (Word16, String))] -> [(String, N.NonEmpty (Word16, N.NonEmpty String))]
stage2 =
map $
\ (serviceName, N.sortWith fst -> portProt) ->
let grp1 port (pp@(port', proto):pps)
| port == port' = (proto:) <$> grp1 port pps
| otherwise = (pp:pps, [])
grp1 _ [] = ([], [])
grp2 ((port, proto):pps) =
let (pps', protos) = grp1 port pps
in (port, proto N.:| protos):grp2 pps'
grp2 [] = []
grp3 ((port, proto) N.:| pps) =
let (pps', protos) = grp1 port pps
in (port, proto N.:| protos) N.:| grp2 pps'
in (serviceName, grp3 portProt)
grpOnNameAndPort :: [Servent] -> [(String, N.NonEmpty (Word16, N.NonEmpty String))]
grpOnNameAndPort = stage2 . stage1
normalize = map $ (\case '-' -> '_'; other -> other) . toUpper