structure ANSIColor:
sig
 datatype color =
   DEFAULT
 | BLACK
 | RED
 | GREEN
 | YELLOW
 | BLUE
 | MAGENTA
 | CYAN
 | WHITE
 | BRIGHT_BLACK
 | BRIGHT_RED
 | BRIGHT_GREEN
 | BRIGHT_YELLOW
 | BRIGHT_BLUE
 | BRIGHT_MAGENTA
 | BRIGHT_CYAN
 | BRIGHT_WHITE
 val fromString: string -> color option
 val asForeground: color -> string option
 val asBackground: color -> string option
end =
struct
 datatype color =
   DEFAULT
 | BLACK
 | RED
 | GREEN
 | YELLOW
 | BLUE
 | MAGENTA
 | CYAN
 | WHITE
 | BRIGHT_BLACK
 | BRIGHT_RED
 | BRIGHT_GREEN
 | BRIGHT_YELLOW
 | BRIGHT_BLUE
 | BRIGHT_MAGENTA
 | BRIGHT_CYAN
 | BRIGHT_WHITE
 fun fromString "default" = SOME DEFAULT
   | fromString "black" = SOME BLACK
   | fromString "red" = SOME RED
   | fromString "green" = SOME GREEN
   | fromString "yellow" = SOME YELLOW
   | fromString "blue" = SOME BLUE
   | fromString "magenta" = SOME MAGENTA
   | fromString "cyan" = SOME CYAN
   | fromString "white" = SOME WHITE
   | fromString "brightblack" = SOME BRIGHT_BLACK
   | fromString "brightred" = SOME BRIGHT_RED
   | fromString "brightgreen" = SOME BRIGHT_GREEN
   | fromString "brightyellow" = SOME BRIGHT_YELLOW
   | fromString "brightblue" = SOME BRIGHT_BLUE
   | fromString "brightmagenta" = SOME BRIGHT_MAGENTA
   | fromString "brightcyan" = SOME BRIGHT_CYAN
   | fromString "brightwhite" = SOME BRIGHT_WHITE
   | fromString _ = NONE
 fun asForeground DEFAULT = NONE
   | asForeground BLACK = SOME "30"
   | asForeground RED = SOME "31"
   | asForeground GREEN = SOME "32"
   | asForeground YELLOW = SOME "33"
   | asForeground BLUE = SOME "34"
   | asForeground MAGENTA = SOME "35"
   | asForeground CYAN = SOME "36"
   | asForeground WHITE = SOME "37"
   | asForeground BRIGHT_BLACK = SOME "90"
   | asForeground BRIGHT_RED = SOME "91"
   | asForeground BRIGHT_GREEN = SOME "92"
   | asForeground BRIGHT_YELLOW = SOME "93"
   | asForeground BRIGHT_BLUE = SOME "94"
   | asForeground BRIGHT_MAGENTA = SOME "95"
   | asForeground BRIGHT_CYAN = SOME "96"
   | asForeground BRIGHT_WHITE = SOME "97"
 fun asBackground DEFAULT = NONE
   | asBackground BLACK = SOME "40"
   | asBackground RED = SOME "41"
   | asBackground GREEN = SOME "42"
   | asBackground YELLOW = SOME "43"
   | asBackground BLUE = SOME "44"
   | asBackground MAGENTA = SOME "45"
   | asBackground CYAN = SOME "46"
   | asBackground WHITE = SOME "47"
   | asBackground BRIGHT_BLACK = SOME "100"
   | asBackground BRIGHT_RED = SOME "101"
   | asBackground BRIGHT_GREEN = SOME "102"
   | asBackground BRIGHT_YELLOW = SOME "103"
   | asBackground BRIGHT_BLUE = SOME "104"
   | asBackground BRIGHT_MAGENTA = SOME "105"
   | asBackground BRIGHT_CYAN = SOME "106"
   | asBackground BRIGHT_WHITE = SOME "107"
end;
structure ANSIStyle:
sig
 type style =
   { foreground: ANSIColor.color option
   , background: ANSIColor.color option
   , bold: bool
   , dim: bool
   , underline: bool
   , blink: bool
   , reverse: bool
   , italic: bool
   , strike: bool
   }
 val defaultStyle: style
 val toString: style -> string
 val resetAll: string
end =
struct
 type style =
   { foreground: ANSIColor.color option
   , background: ANSIColor.color option
   , bold: bool
   , dim: bool
   , underline: bool
   , blink: bool
   , reverse: bool
   , italic: bool
   , strike: bool
   }
 val defaultStyle: style =
   { foreground = NONE
   , background = NONE
   , bold = false
   , dim = false
   , underline = false
   , blink = false
   , reverse = false
   , italic = false
   , strike = false
   }
 fun prependOption (SOME x, xs) = x :: xs
   | prependOption (NONE, xs) = xs
 fun toString
   ({ foreground
    , background
    , bold
    , dim
    , underline
    , blink
    , reverse
    , italic
    , strike
    }: style) =
   let
     val attrs = []
     val attrs = if strike then "9" :: attrs else attrs
     val attrs = if reverse then "7" :: attrs else attrs
     val attrs = if blink then "5" :: attrs else attrs
     val attrs = if underline then "4" :: attrs else attrs
     val attrs = if italic then "3" :: attrs else attrs
     val attrs = if dim then "2" :: attrs else attrs
     val attrs = if bold then "1" :: attrs else attrs
     val attrs = prependOption
       (Option.mapPartial ANSIColor.asBackground background, attrs)
     val attrs = prependOption
       (Option.mapPartial ANSIColor.asForeground foreground, attrs)
   in
     "\027[" ^ String.concatWith ";" attrs ^ "m"
   end
 val resetAll: string = "\027[0m"
end;