{-# LANGUAGE OverloadedStrings #-}

module Language.Docker.Parser.Expose
  ( parseExpose,
  )
where

import qualified Data.Text as T
import Language.Docker.Parser.Prelude
import Language.Docker.Syntax

parseExpose :: Parser (Instruction Text)
parseExpose = do
  reserved "EXPOSE"
  Expose <$> ports

port :: Parser Port
port =
  (try portVariable <?> "a variable")
    <|> (try portRange <?> "a port range optionally followed by the protocol (udp/tcp)") -- There a many valid representations of ports
    <|> (try portWithProtocol <?> "a port with its protocol (udp/tcp)")
    <|> (try portInt <?> "a valid port number")

ports :: Parser Ports
ports = Ports <$> port `sepEndBy` requiredWhitespace

portRange :: Parser Port
portRange = do
  start <- natural
  void $ char '-'
  finish <- try natural
  proto <- try protocol <|> return TCP
  return $ PortRange (fromIntegral start) (fromIntegral finish) proto

protocol :: Parser Protocol
protocol = do
  void (char '/')
  tcp <|> udp
  where
    tcp = caseInsensitiveString "tcp" >> return TCP
    udp = caseInsensitiveString "udp" >> return UDP

portInt :: Parser Port
portInt = do
  portNumber <- natural
  notFollowedBy (string "/" <|> string "-")
  return $ Port (fromIntegral portNumber) TCP

portWithProtocol :: Parser Port
portWithProtocol = do
  portNumber <- natural
  Port (fromIntegral portNumber) <$> protocol

portVariable :: Parser Port
portVariable = do
  void (char '$')
  variable <- someUnless "the variable name" (== '$')
  return $ PortStr (T.append "$" variable)