# stew
# Copyright 2023 Status Research & Development GmbH
# Licensed under either of
#
#  * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
#  * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
#
# at your option. This file may not be copied, modified, or distributed except according to those terms.

import std/[macros, options]

type EnumStyle* {.pure.} = enum
  Numeric,
  AssociatedStrings

func setMode(style: var Option[EnumStyle], s: EnumStyle, typ: auto) =
  if style.isNone:
    style = some s
  elif style.get != s:
    error("Mixed enum styles not supported for deserialization: " & $typ)
  else:
    discard

macro enumStyle*(t: typedesc[enum]): untyped =
  let
    typ = t.getTypeInst[1]
    impl = typ.getImpl[2]
  expectKind impl, nnkEnumTy

  var style: Option[EnumStyle]
  for f in impl:
    case f.kind
    of nnkEmpty:
      continue
    of nnkIdent:
      error("Unexpected enum node for deserialization: " & $f.kind)
    of nnkSym:
      style.setMode(EnumStyle.Numeric, typ)
    of nnkEnumFieldDef:
      case f[1].kind
      of nnkIntLit:
        style.setMode(EnumStyle.Numeric, typ)
      of nnkStrLit:
        style.setMode(EnumStyle.AssociatedStrings, typ)
      else: error("Unexpected enum tuple for deserialization: " & $f[1].kind)
    else: error("Unexpected enum node for deserialization: " & $f.kind)

  if style.isNone:
    error("Cannot determine enum style for deserialization: " & $typ)
  case style.get
  of EnumStyle.Numeric:
    quote do:
      EnumStyle.Numeric
  of EnumStyle.AssociatedStrings:
    quote do:
      EnumStyle.AssociatedStrings
