content: {-
    Copyright (c) Meta Platforms, Inc. and affiliates.
    All rights reserved.
  
    This source code is licensed under the BSD-style license found in the
    LICENSE file in the root directory of this source tree.
  -}
  
  module Thrift.Compiler.GenUtils
    ( textToName
    , qualSym, unqualSym, nameToQName
    , qualType, simpleType
    , qvar, var, pvar, con, qcon, tvar
    , app, appT, infixApp, compose
    , genImportModule, importFromInclude, typeToImport
    , intLit, stringLit, intP, stringP, listE
    , deriving_
    , genType, isBaseType, genThriftType, genConst, typeToDefault
    , qualifyType, qualifyField, qualifyResolvedType
    , genConstructor
    , Import(..)
    , protocolFun
    , genCALL, genREPLY, genEXCEPTION, genONEWAY
    , PrimType(..), getPrim
    ) where
  
  import Data.Proxy
  import Data.Set (union)
  import Data.Some
  import Data.Text (Text)
  import Data.Text.Encoding hiding (Some)
  import GHC.TypeLits
  import Language.Haskell.Exts.Syntax hiding (List, Name, Type)
  import qualified Language.Haskell.Exts.Syntax as HS
  import qualified Data.Set as Set
  import qualified Data.Text as Text
  
  import Thrift.Compiler.Plugins.Haskell
  import Thrift.Compiler.Types
  
  data Import
     = QImport Text Text
     | SymImport Text [Text]
     | TypesImport Text
     deriving (Show, Eq, Ord)
  
  textToName :: Text -> HS.Name ()
  textToName = Ident () . Text.unpack
  
  genImportModule :: Import -> ImportDecl ()
  genImportModule (QImport modName modAs) = ImportDecl
    { importAnn       = ()
    , importModule    = ModuleName () $ Text.unpack modName
    , importQualified = True
    , importSrc       = False
    , importSafe      = False
    , importPkg       = Nothing
    , importAs        = Just $ ModuleName () $ Text.unpack modAs
    , importSpecs     = Nothing
    }
  genImportModule (SymImport modName symbols) = ImportDecl
    { importAnn       = ()
    , importModule    = ModuleName () $ Text.unpack modName
    -- Symbols are not imported qualified because you can't define your own
    -- operators in Thrift, therefore they are guaranteed not to collide
    , importQualified = False
    , importSrc       = False
    , importSafe      = False
    , importPkg       = Nothing
    , importAs        = Nothing
    , importSpecs     = Just $ ImportSpecList () False (map mkImport symbols)
    }
      where mkImport = IVar () . Symbol () . Text.unpack
  genImportModule (TypesImport modName) = ImportDecl
    { importAnn       = ()
    , importModule    = ModuleName () $ Text.unpack modName ++ ".Types"
    , importQualified = False
    , importSrc       = False
    , importSafe      = False
    , importPkg       = Nothing
    , importAs        = Nothing
    , importSpecs     = Nothing
    }
  
  importFromInclude :: Program Haskell a -> Import
  importFromInclude Program{..} =
    QImport (progHSName <> ".Types") progHSName
  
  typeToImport :: HSType t -> Set.Set Import
  typeToImport = typeToImportRec True
  
  typeToImportRec :: Bool -> HSType t -> Set.Set Import
  typeToImportRec _ I8  = Set.singleton (QImport "Data.Int" "Int")
  typeToImportRec _ I16 = Set.singleton (QImport "Data.Int" "Int")
  typeToImportRec _ I32 = Set.singleton (QImport "Data.Int" "Int")
  typeToImportRec _ I64 = Set.singleton (QImport "Data.Int" "Int")
  typeToImportRec _ (TSpecial HsInt) = Set.empty
  typeToImportRec _ TFloat = Set.empty
  typeToImportRec _ TDouble = Set.empty
  typeToImportRec _ TText = Set.fromList
    [ QImport "Data.Text" "Text"
    , QImport "Data.Text.Encoding" "Text"
    ]
  typeToImportRec r (TSpecial HsString) = typeToImportRec r TText
  typeToImportRec _ (TSpecial HsByteString) = Set.fromList
    [ QImport "Data.ByteString" "ByteString"
    , QImport "Data.Text" "Text"
    , QImport "Data.Text.Encoding" "Text"
    ]
  typeToImportRec _ TBytes = Set.fromList
    [ QImport "Data.ByteString" "ByteString"
    ]
  typeToImportRec _ TBool = Set.empty
  typeToImportRec r (TSet t) =
    Set.singleton (QImport "Data.Set" "Set") `union`
    typeToImportRec r t
  typeToImportRec r (THashSet t) =
    Set.singleton (QImport "Data.HashSet" "HashSet") `union`
    typeToImportRec r t
  typeToImportRec r (TList t) = typeToImportRec r t
  typeToImportRec r (TSpecial (HsVector vec t)) =
    Set.singleton (QImport (hsVectorImport vec) (hsVectorQual vec)) `union`
    typeToImportRec r t
  typeToImportRec r (TMap k v) =
    Set.singleton (QImport "Data.Map.Strict" "Map") `union`
    typeToImportRec r k `union`
    typeToImportRec r v
  typeToImportRec r (THashMap k v) =
    Set.singleton (QImport "Data.HashMap.Strict" "HashMap") `union`
    typeToImportRec r k `union`
    typeToImportRec r v
  typeToImportRec r (TStruct name _loc) = nameToImport r name
  typeToImportRec r (TException name _loc) = nameToImport r name
  typeToImportRec r (TUnion name _loc) = nameToImport r name
  typeToImportRec r (TEnum name _loc _) = nameToImport r name
  typeToImportRec r (TTypedef name ty _loc) =
    nameToImport r name `union` typeToImportRec False ty
  typeToImportRec r (TNewtype name ty _loc) =
    nameToImport r name `union` typeToImportRec False ty
  
  nameToImport :: Bool -> Name -> Set.Set Import
  nameToImport cond Name{..} = case resolvedName of
    QName q _ | cond-> Set.singleton $ QImport (q <> ".Types") q
    _ -> Set.empty
  
  unqualSym :: Text -> QName ()
  unqualSym = UnQual () . textToName
  
  qualSym :: Text -> Text -> QName ()
  qualSym m sym = Qual () (ModuleName () $ Text.unpack m) (textToName sym)
  
  nameToQName :: Name -> QName ()
  nameToQName Name{..} = case resolvedName of
    UName n -> unqualSym n
    QName m n -> qualSym m n
  
  qualType :: Text -> Text -> HS.Type ()
  qualType mname tname = TyCon () $ qualSym mname tname
  
  simpleType :: Text -> HS.Type ()
  simpleType tname = TyCon () $ unqualSym tname
  
  app :: Exp () -> Exp () -> Exp ()
  app = App ()
  infixl `app`
  
  appT :: HS.Type () -> HS.Type () -> HS.Type ()
  appT = TyApp ()
  infixl `appT`
  
  infixApp :: Text -> Exp () -> Exp () -> Exp ()
  infixApp sym e1 e2 =
    InfixApp () e1 (QVarOp () $ UnQual () $ Symbol () $ Text.unpack sym) e2
  
  compose :: Exp () -> Exp () -> Exp ()
  compose = infixApp "."
  
  stringLit :: Text -> Exp ()
  stringLit text = Lit () $ String () s s
    where s = Text.unpack text
  
  listE :: [Exp ()] -> Exp ()
  listE = HS.List ()
  
  intLit :: (Integral a, Show a) => a -> Exp ()
  intLit x = wrapNegativeLit x $ Lit () $ Int () (fromIntegral x) (show x)
  
  floatLit :: (Real a, Show a) => a -> Exp ()
  floatLit x = wrapNegativeLit x $ Lit () $ Frac () (toRational x) (show x)
  
  wrapNegativeLit :: Show a => a -> Exp () -> Exp ()
  wrapNegativeLit x = case show x of
    '-':_ -> Paren ()
    _ -> id
    -- NB: we check the result of show directly to handle negative zeros
    --
    -- >>> let x = -0.0 in (show x, signum x, x < 0)
    -- ("-0.0", -0.0, False)
  
  intP :: (Integral a, Show a) => a -> Pat ()
  intP x = PLit () (sign ()) $ Int () (fromIntegral $ abs x) (show x)
    where sign | x < 0 = Negative
               | otherwise = Signless
  
  stringP :: Text -> Pat ()
  stringP text = PLit () (Signless ()) $ String () s s
    where s = Text.unpack text
  
  var :: Text -> Exp ()
  var = Var () . unqualSym
  
  qvar :: Text -> Text -> Exp ()
  qvar m n = Var () $ qualSym m n
  
  pvar :: Text -> Pat ()
  pvar name = PVar () $ textToName name
  
  con :: Text -> Exp ()
  con = Con () . unqualSym
  
  qcon :: Text -> Text -> Exp ()
  qcon m n = Con () $ qualSym m n
  
  tvar :: Text -> HS.Type ()
  tvar = TyVar () . textToName
  
  deriving_ :: [InstRule ()] -> Deriving ()
  deriving_ = Deriving () Nothing
  
  genType :: HSType t -> HS.Type ()
  genType I8      = qualType "Int" "Int8"
  genType I16     = qualType "Int" "Int16"
  genType I32     = qualType "Int" "Int32"
  genType I64     = qualType "Int" "Int64"
  genType (TSpecial HsInt) = qualType "Prelude" "Int"
  genType TFloat  = qualType "Prelude" "Float"
  genType TDouble = qualType "Prelude" "Double"
  genType TBool   = qualType "Prelude" "Bool"
  genType TText   = qualType "Text" "Text"
  genType (TSpecial HsString) = qualType "Prelude" "String"
  genType (TSpecial HsByteString) = qualType "ByteString" "ByteString"
  genType TBytes  = qualType "ByteString" "ByteString"
  genType (TList ty) = TyList () (genType ty)
  genType (TSpecial (HsVector vec ty)) =
    qualType (hsVectorQual vec) "Vector" `appT` genType ty
  genType (TSet ty)  = qualType "Set" "Set" `appT` genType ty
  genType (THashSet ty) = qualType "HashSet" "HashSet" `appT` genType ty
  genType (TMap k v) = qualType "Map" "Map" `appT` genType k `appT` genType v
  genType (THashMap k v) =
    qualType "HashMap" "HashMap" `appT` genType k `appT` genType v
  genType (TStruct name _loc)    = TyCon () $ nameToQName name
  genType (TException name _loc) = TyCon () $ nameToQName name
  genType (TUnion name _loc)     = TyCon () $ nameToQName name
  genType (TEnum name _loc _)      = TyCon () $ nameToQName name
  genType (TTypedef name _ _loc) = TyCon () $ nameToQName name
  genType (TNewtype name _ _loc) = TyCon () $ nameToQName name
  
  genConst :: HSType t -> TypedConst Haskell t -> Exp ()
  -- Base Type Literals
  genConst I8  (Literal x) = intLit x
  genConst I16 (Literal x) = intLit x
  genConst I32 (Literal x) = intLit x
  genConst I64 (Literal x) = intLit x
  genConst (TSpecial HsInt) (Literal x) = intLit x
  genConst TFloat  (Literal x) = floatLit x
  genConst TDouble (Literal x) = floatLit x
  genConst TBool   (Literal x) = qvar "Prelude" $ if x then "True" else "False"
  genConst TText   (Literal s) = stringLit s
  genConst (TSpecial HsString) (Literal s) = Lit () $ String () s s
  genConst (TSpecial HsByteString) (Literal s) = stringLit $ decodeUtf8 s
  genConst TBytes (Literal s) = stringLit $ decodeUtf8 s
  -- Collection Literals
  genConst (TList ty)   (Literal (List l)) = HS.List () $ map (genConst ty) l
  genConst (TSpecial (HsVector vec ty)) (Literal (List l)) =
    qvar (hsVectorQual vec) "fromList" `app` HS.List () (map (genConst ty) l)
  genConst (TSet ty) (Literal (Set l)) =
    qvar "Set" "fromList" `app` HS.List () (map (genConst ty) l)
  genConst (THashSet ty) (Literal (HashSet l)) =
    qvar "HashSet" "fromList" `app` HS.List () (map (genConst ty) l)
  genConst (TMap kt vt) (Literal (Map m)) =
    qvar "Map" "fromList" `app`
    HS.List () (map (\(k, v) -> Tuple () Boxed [genConst kt k, genConst vt v]) m)
  genConst (THashMap kt vt) (Literal (HashMap m)) =
    qvar "HashMap" "fromList" `app`
    HS.List () (map (\(k, v) -> Tuple () Boxed [genConst kt k, genConst vt v]) m)
  -- Names Type Constants
  genConst (TTypedef _ ty _loc) lit@Literal{} = genConst ty lit
  genConst (TNewtype name ty _loc) (Literal (New lit)) =
    Con () (nameToQName name) `app` genConst ty (Literal lit)
  genConst (TStruct name _loc) (Literal (Some s)) = genStructConst name s
  genConst (TException name _loc) (Literal (Some (EV s))) = genStructConst name s
  genConst (TUnion name _loc) (Literal (Some (UnionVal proxy ty val _))) =
    Con () (nameToQName cname) `app` genConst ty val
    where
      conName = Text.pack $ symbolVal proxy
      cname = name { resolvedName = mapName (const conName) $ resolvedName name }
  genConst (TEnum _ _loc _) (Literal (EnumVal name _)) = Con () $ nameToQName name
  -- Identifiers
  genConst _ (Identifier name _ _loc) = Var () $ nameToQName name
  -- TODO: WeirdEnumToInt needs some kind of explicit conversion, this should fail
  genConst _ (WeirdEnumToInt _ name _ _loc) = Var () $ nameToQName name
  
  genStructConst :: Name -> StructVal Haskell s -> Exp ()
  genStructConst name struct =
    case fields struct of
      [] -> def
      flds -> RecUpdate () (Paren () def) flds
    where
      def = ExpTypeSig () (qvar "Default" "def") (TyCon () $ nameToQName name)
      fields :: StructVal Haskell s -> [FieldUpdate ()]
      fields Empty = []
      fields (ConsVal proxy ty val s) =
        FieldUpdate () (getName proxy) (genConst ty val) : fields s
      fields (ConsDefault _ _ s) = fields s
      fields (ConsJust proxy ty val s) =
        FieldUpdate () (getName proxy)
          (qcon "Prelude" "Just" `app` genConst ty val) : fields s
      fields (ConsNothing _ s) = fields s
      getName :: KnownSymbol s => Proxy s -> QName ()
      getName proxy =
        nameToQName name { resolvedName = mapName (const n) $ resolvedName name }
        where n = Text.pack $ symbolVal proxy
  
  typeToDefault :: HSType t -> Exp ()
  typeToDefault TBool = qcon "Prelude" "False"
  typeToDefault TText = stringLit ""
  typeToDefault TBytes = stringLit ""
  typeToDefault THashMap{} = qvar "HashMap" "empty"
  typeToDefault THashSet{} = qvar "HashSet" "empty"
  typeToDefault (TSpecial (HsVector vec _)) = qvar (hsVectorQual vec) "empty"
  typeToDefault (TTypedef _ ty _loc) = typeToDefault ty
  typeToDefault (TNewtype name ty _loc) =
    Con () (nameToQName name) `app` typeToDefault ty
  -- Everything else has default instances
  typeToDefault _ = qvar "Default" "def"
  
  isBaseType :: HSType t -> Bool
  isBaseType I8 = True
  isBaseType I16 = True
  isBaseType I32 = True
  isBaseType I64 = True
  isBaseType (TSpecial HsInt) = True
  isBaseType TFloat = True
  isBaseType TDouble = True
  isBaseType TBool = True
  isBaseType TText = True
  isBaseType (TSpecial HsString) = True
  isBaseType (TSpecial HsByteString) = True
  isBaseType TBytes = True
  isBaseType (TTypedef _ t _loc) = isBaseType t
  isBaseType (TNewtype _ t _loc) = isBaseType t
  isBaseType _ = False
  
  genThriftType :: HSType t -> Exp ()
  genThriftType I8      = protocolFun "getByteType"
  genThriftType I16     = protocolFun "getI16Type"
  genThriftType I32     = protocolFun "getI32Type"
  genThriftType I64     = protocolFun "getI64Type"
  genThriftType (TSpecial HsInt) = protocolFun "getI64Type"
  genThriftType TFloat  = protocolFun "getFloatType"
  genThriftType TDouble = protocolFun "getDoubleType"
  genThriftType TBool   = protocolFun "getBoolType"
  genThriftType TText   = protocolFun "getStringType"
  genThriftType (TSpecial HsString) = protocolFun "getStringType"
  genThriftType (TSpecial HsByteString) = protocolFun "getStringType"
  genThriftType TBytes  = protocolFun "getStringType"
  genThriftType TList{}    = protocolFun "getListType"
  genThriftType (TSpecial HsVector{}) = protocolFun "getListType"
  genThriftType TSet{}     = protocolFun "getSetType"
  genThriftType THashSet{} = protocolFun "getSetType"
  genThriftType TMap{}     = protocolFun "getMapType"
  genThriftType THashMap{} = protocolFun "getMapType"
  genThriftType TStruct{}  = protocolFun "getStructType"
  genThriftType TException{} = protocolFun "getStructType"
  genThriftType TUnion{} = protocolFun "getStructType"
  genThriftType TEnum{}    = protocolFun "getI32Type"
  genThriftType (TTypedef _ ty _loc) = genThriftType ty
  genThriftType (TNewtype _ ty _loc) = genThriftType ty
  
  protocolFun :: Text -> Exp ()
  protocolFun fun = qvar "Thrift" fun `app` var "_proxy"
  
  genCALL, genREPLY, genEXCEPTION, genONEWAY :: Int
  genCALL      = 1
  genREPLY     = 2
  genEXCEPTION = 3
  genONEWAY    = 4
  
  data PrimType = P_I8 | P_I16 | P_I32 | P_I64 | P_Bool
  
  getPrim :: HSType t -> Maybe PrimType
  getPrim I8    = Just P_I8
  getPrim I16   = Just P_I16
  getPrim I32   = Just P_I32
  getPrim I64   = Just P_I64
  getPrim TBool = Just P_Bool
  getPrim _ = Nothing
  
  qualifyType :: Text -> HSType t -> HSType t
  qualifyType _ I8      = I8
  qualifyType _ I16     = I16
  qualifyType _ I32     = I32
  qualifyType _ I64     = I64
  qualifyType _ (TSpecial HsInt) = TSpecial HsInt
  qualifyType _ TFloat  = TFloat
  qualifyType _ TDouble = TDouble
  qualifyType _ TBool   = TBool
  qualifyType _ TText   = TText
  qualifyType _ (TSpecial HsString) = TSpecial HsString
  qualifyType _ (TSpecial HsByteString) = TSpecial HsByteString
  qualifyType _ TBytes  = TBytes
  qualifyType q (TList u) = TList $ qualifyType q u
  qualifyType q (TSpecial (HsVector vec u)) =
    TSpecial $ HsVector vec $ qualifyType q u
  qualifyType q (TSet u) = TSet $ qualifyType q u
  qualifyType q (THashSet u) = THashSet $ qualifyType q u
  qualifyType q (TMap k v) = TMap (qualifyType q k) (qualifyType q v)
  qualifyType q (THashMap k v) = THashMap (qualifyType q k) (qualifyType q v)
  qualifyType q (TStruct name loc) = TStruct (qualifyName q name) loc
  qualifyType q (TException name loc) = TException (qualifyName q name) loc
  qualifyType q (TEnum name loc nounknown) =
    TEnum (qualifyName q name) loc nounknown
  qualifyType q (TUnion name loc) = TUnion (qualifyName q name) loc
  qualifyType q (TTypedef name ty loc) =
    TTypedef (qualifyName q name) (qualifyType q ty) loc
  qualifyType q (TNewtype name ty loc) =
    TNewtype (qualifyName q name) (qualifyType q ty) loc
  
  qualifySomeType :: Text -> Some HSType -> Some HSType
  qualifySomeType q ty = mapSome (qualifyType q) ty
  
  qualifyResolvedType :: Text -> Maybe (Some HSType) -> Maybe (Some HSType)
  qualifyResolvedType q ty = fmap (qualifySomeType q) ty
  
  qualifyField
    :: Text -> Field u 'Resolved Haskell a -> Field u 'Resolved Haskell a
  qualifyField q Field{..} = Field
    { fieldResolvedType = qualifyType q fieldResolvedType
    , fieldResolvedVal  = qualifyConst q fieldResolvedType <$> fieldResolvedVal
    , ..
    }
  
  qualifyConst :: Text -> HSType t -> TypedConst Haskell t -> TypedConst Haskell t
  qualifyConst q _ (Identifier name ty loc) =
    Identifier (qualifyName q name) (qualifyType q ty) loc
  qualifyConst q _ (WeirdEnumToInt ty name tEnum loc) =
    WeirdEnumToInt (qualifyType q ty)
      (qualifyName q name) (qualifyType q tEnum) loc
  qualifyConst q ty (Literal lit) = Literal $ qualifyLit q ty lit
  
  qualifyLit :: Text -> HSType t -> t -> t
  -- Base types don't need to be qualified
  qualifyLit _ I8  x = x
  qualifyLit _ I16 x = x
  qualifyLit _ I32 x = x
  qualifyLit _ I64 x = x
  qualifyLit _ (TSpecial HsInt) x = x
  qualifyLit _ TFloat f = f
  qualifyLit _ TDouble d = d
  qualifyLit _ TBool b = b
  qualifyLit _ TText t = t
  qualifyLit _ TBytes t = t
  qualifyLit _ (TSpecial HsString) s = s
  qualifyLit _ (TSpecial HsByteString) s = s
  -- Collections
  qualifyLit q (TList u) (List l) = List $ map (qualifyConst q u) l
  qualifyLit q (TSpecial (HsVector _ u)) (List l) =
    List $ map (qualifyConst q u) l
  qualifyLit q (TSet u) (Set s) = Set $ map (qualifyConst q u) s
  qualifyLit q (THashSet u) (HashSet s) = HashSet $ map (qualifyConst q u) s
  qualifyLit q (TMap kt vt) (Map m) =
    Map [ (qualifyConst q kt k, qualifyConst q vt v) | (k, v) <- m ]
  qualifyLit q (THashMap kt vt) (HashMap m) =
    HashMap [ (qualifyConst q kt k, qualifyConst q vt v) | (k, v) <- m ]
  -- Named Types
  qualifyLit q TStruct{} (Some s) = Some $ qualifyStruct q s
  qualifyLit q TException{} (Some (EV s)) = Some $ EV $ qualifyStruct q s
  qualifyLit q TEnum{} (EnumVal name loc) = EnumVal (qualifyName q name) loc
  qualifyLit q TUnion{} (Some u) = Some $ qualifyUnion q u
  qualifyLit q (TTypedef _ ty _loc) lit = qualifyLit q ty lit
  qualifyLit q (TNewtype _ ty _loc) (New lit) = New $ qualifyLit q ty lit
  
  qualifyStruct :: Text -> StructVal Haskell s -> StructVal Haskell s
  qualifyStruct _ Empty = Empty
  qualifyStruct q (ConsVal pr ty val s) =
    ConsVal pr (qualifyType q ty) (qualifyConst q ty val) $ qualifyStruct q s
  qualifyStruct q (ConsDefault pr ty s) =
    ConsDefault pr (qualifyType q ty) $ qualifyStruct q s
  qualifyStruct q (ConsJust pr ty val s) =
    ConsJust pr (qualifyType q ty) (qualifyConst q ty val) $
    qualifyStruct q s
  qualifyStruct q (ConsNothing pr s) = ConsNothing pr $ qualifyStruct q s
  
  qualifyUnion :: Text -> UnionVal Haskell s -> UnionVal Haskell s
  qualifyUnion q (UnionVal pr ty val proof) =
    UnionVal pr (qualifyType q ty) (qualifyConst q ty val) proof
  
  -- Note: we only care about qualifying the resolved name because the source name
  -- is not used anymore after typechecking
  qualifyName :: Text -> Name -> Name
  qualifyName q name@Name{..} = name
    { resolvedName = case resolvedName of
        UName n -> QName q n
        n@QName{} -> n
    }
  
  genConstructor
    :: Maybe Text -> HSType (Some (ExceptionVal Haskell)) -> QName ()
  genConstructor qual (TException name _loc)
    | Just uname <- getUName name = case qual of
        Nothing -> unqualSym uname
        Just q  -> qualSym q uname
  genConstructor _ (TException name _loc) = nameToQName name
  genConstructor qual (TTypedef _ ty _loc) = genConstructor qual ty
  -- This case is impossible since there is no possible value for
  -- SpecialType Haskell (Some (ExceptionVal Haskell))
  -- , which is obvious from looking at the data family instance.
  -- However, the pattern match completeness checker for HSType
  -- doesn't seem to be able to eliminate this case.
  genConstructor _ (TSpecial _) = error "This is provably unreachable"
  
  getUName :: Name -> Maybe Text
  getUName Name{..} = case resolvedName of
    UName n -> Just n
    QName{} -> Nothing
  