packages feed

hydra-0.15.0: src/main/haskell/Hydra/Sources/Rust/Serde.hs

-- | Rust serializer: converts Rust AST to concrete syntax (source code).
-- Serializes the Rust syntax model (Hydra.Rust.Syntax) into properly formatted Rust source code.

module Hydra.Sources.Rust.Serde where

-- Standard imports for term-level sources outside of the kernel
import Hydra.Kernel
import Hydra.Sources.Libraries
import           Hydra.Dsl.Meta.Lib.Strings                as Strings
import           Hydra.Dsl.Meta.Phantoms                   as Phantoms
import qualified Hydra.Dsl.Annotations                     as Annotations
import qualified Hydra.Dsl.Bootstrap                       as Bootstrap
import qualified Hydra.Dsl.LiteralTypes                    as LiteralTypes
import qualified Hydra.Dsl.Literals                        as Literals
import qualified Hydra.Dsl.Paths                      as Paths
import qualified Hydra.Dsl.Ast                        as Ast
import qualified Hydra.Dsl.Meta.Base                       as MetaBase
import qualified Hydra.Dsl.Coders                     as Coders
import qualified Hydra.Dsl.Util                    as Util
import qualified Hydra.Dsl.Meta.Core                       as Core
import qualified Hydra.Dsl.Meta.Graph                      as Graph
import qualified Hydra.Dsl.Json.Model                       as Json
import qualified Hydra.Dsl.Meta.Lib.Chars                  as Chars
import qualified Hydra.Dsl.Meta.Lib.Eithers                as Eithers
import qualified Hydra.Dsl.Meta.Lib.Equality               as Equality
import qualified Hydra.Dsl.Meta.Lib.Lists                  as Lists
import qualified Hydra.Dsl.Meta.Lib.Literals               as Literals
import qualified Hydra.Dsl.Meta.Lib.Logic                  as Logic
import qualified Hydra.Dsl.Meta.Lib.Maps                   as Maps
import qualified Hydra.Dsl.Meta.Lib.Math                   as Math
import qualified Hydra.Dsl.Meta.Lib.Maybes                 as Maybes
import qualified Hydra.Dsl.Meta.Lib.Pairs                  as Pairs
import qualified Hydra.Dsl.Meta.Lib.Sets                   as Sets
import qualified Hydra.Dsl.Packaging                     as Packaging
import qualified Hydra.Dsl.Meta.Terms                      as MetaTerms
import qualified Hydra.Dsl.Meta.Testing                    as Testing
import qualified Hydra.Dsl.Topology                   as Topology
import qualified Hydra.Dsl.Meta.Types                      as MetaTypes
import qualified Hydra.Dsl.Typing                     as Typing
import qualified Hydra.Dsl.Util                       as Util
import qualified Hydra.Dsl.Meta.Variants                   as Variants
import qualified Hydra.Dsl.Prims                           as Prims
import qualified Hydra.Dsl.Meta.Tabular                         as Tabular
import qualified Hydra.Dsl.Terms                           as Terms
import qualified Hydra.Dsl.Tests                           as Tests
import qualified Hydra.Dsl.Types                           as Types
import qualified Hydra.Sources.Decode.Core                 as DecodeCore
import qualified Hydra.Sources.Encode.Core                 as EncodeCore
import qualified Hydra.Sources.Kernel.Terms.Adapt           as Adapt
import qualified Hydra.Sources.Kernel.Terms.All            as KernelTerms
import qualified Hydra.Sources.Kernel.Terms.Annotations    as Annotations
import qualified Hydra.Sources.Kernel.Terms.Arity          as Arity
import qualified Hydra.Sources.Kernel.Terms.Checking       as Checking
import qualified Hydra.Sources.Kernel.Terms.Constants      as Constants
import qualified Hydra.Sources.Kernel.Terms.Extract.Core   as ExtractCore
import qualified Hydra.Sources.Kernel.Terms.Extract.Util   as ExtractUtil
import qualified Hydra.Sources.Kernel.Terms.Formatting     as Formatting
import qualified Hydra.Sources.Kernel.Terms.Inference      as Inference
import qualified Hydra.Sources.Kernel.Terms.Languages      as Languages
import qualified Hydra.Sources.Kernel.Terms.Lexical        as Lexical
import qualified Hydra.Sources.Kernel.Terms.Literals       as Literals
import qualified Hydra.Sources.Kernel.Terms.Names          as Names
import qualified Hydra.Sources.Kernel.Terms.Reduction      as Reduction
import qualified Hydra.Sources.Kernel.Terms.Reflect        as Reflect
import qualified Hydra.Sources.Kernel.Terms.Serialization  as Serialization
import qualified Hydra.Sources.Kernel.Terms.Show.Paths as ShowPaths
import qualified Hydra.Sources.Kernel.Terms.Show.Core      as ShowCore
import qualified Hydra.Sources.Kernel.Terms.Show.Graph     as ShowGraph
import qualified Hydra.Sources.Kernel.Terms.Show.Variants  as ShowVariants
import qualified Hydra.Sources.Kernel.Terms.Show.Typing    as ShowTyping
import qualified Hydra.Sources.Kernel.Terms.Sorting        as Sorting
import qualified Hydra.Sources.Kernel.Terms.Substitution   as Substitution
import qualified Hydra.Sources.Kernel.Terms.Templates      as Templates
import qualified Hydra.Sources.Kernel.Terms.Unification    as Unification
import qualified Hydra.Sources.Kernel.Types.All            as KernelTypes
import           Prelude hiding ((++))
import qualified Data.Int                                  as I
import qualified Data.List                                 as L
import qualified Data.Map                                  as M
import qualified Data.Set                                  as S
import qualified Data.Maybe                                as Y

-- Additional imports
import Hydra.Ast
import qualified Hydra.Rust.Syntax as R
import qualified Hydra.Sources.Rust.Syntax as RustSyntax
import qualified Hydra.Sources.Rust.Operators as RustOperators


define :: String -> TTerm a -> TTermDefinition a
define = definitionInModule module_

ns :: Namespace
ns = Namespace "hydra.rust.serde"

module_ :: Module
module_ = Module {
            moduleNamespace = ns,
            moduleDefinitions = definitions,
            moduleTermDependencies = [Constants.ns, Serialization.ns, RustOperators.ns],
            moduleTypeDependencies = (RustSyntax.ns:KernelTypes.kernelTypesNamespaces),
            moduleDescription = Just "Rust serializer: converts Rust AST to concrete syntax"}
  where
    definitions = [
      -- Top-level serialization
      toDefinition crateToExpr,
      toDefinition itemToExpr,
      toDefinition itemWithCommentsToExpr,
      -- Use declarations
      toDefinition useDeclarationToExpr,
      toDefinition useTreeToExpr,
      -- Struct definitions
      toDefinition structDefToExpr,
      toDefinition structBodyToExpr,
      toDefinition structFieldToExpr,
      -- Enum definitions
      toDefinition enumDefToExpr,
      toDefinition enumVariantToExpr,
      toDefinition enumVariantBodyToExpr,
      -- Function definitions
      toDefinition fnDefToExpr,
      toDefinition fnParamToExpr,
      toDefinition methodParamToExpr,
      -- Type aliases
      toDefinition typeAliasToExpr,
      -- Const, static, module
      toDefinition constDefToExpr,
      toDefinition staticDefToExpr,
      toDefinition modDefToExpr,
      -- Impl blocks
      toDefinition implBlockToExpr,
      toDefinition implItemToExpr,
      toDefinition implMethodToExpr,
      -- Trait definitions
      toDefinition traitDefToExpr,
      toDefinition traitItemToExpr,
      toDefinition traitMethodToExpr,
      toDefinition traitTypeToExpr,
      toDefinition traitConstToExpr,
      -- Generics and where clauses
      toDefinition genericParamToExpr,
      toDefinition genericParamsToExpr,
      toDefinition typeParamBoundToExpr,
      toDefinition whereClauseToExpr,
      -- Types
      toDefinition typeToExpr,
      toDefinition typePathToExpr,
      toDefinition pathSegmentToExpr,
      toDefinition genericArgumentsToExpr,
      toDefinition genericArgToExpr,
      toDefinition referenceTypeToExpr,
      -- Expressions
      toDefinition expressionToExpr,
      toDefinition exprPathToExpr,
      toDefinition callExprToExpr,
      toDefinition methodCallExprToExpr,
      toDefinition fieldAccessExprToExpr,
      toDefinition tupleIndexExprToExpr,
      toDefinition closureExprToExpr,
      toDefinition closureParamToStr,
      toDefinition ifExprToExpr,
      toDefinition matchExprToExpr,
      toDefinition matchArmToExpr,
      toDefinition loopExprToExpr,
      toDefinition whileExprToExpr,
      toDefinition forExprToExpr,
      toDefinition binaryExprToExpr,
      toDefinition binaryOpToExpr,
      toDefinition unaryExprToExpr,
      toDefinition refExprToExpr,
      toDefinition structExprToExpr,
      toDefinition fieldValueToExpr,
      toDefinition arrayExprToExpr,
      toDefinition indexExprToExpr,
      toDefinition rangeExprToExpr,
      toDefinition castExprToExpr,
      toDefinition typeAscriptionExprToExpr,
      toDefinition assignExprToExpr,
      toDefinition compoundAssignExprToExpr,
      toDefinition macroInvocationToExpr,
      -- Statements
      toDefinition statementToExpr,
      toDefinition letStatementToExpr,
      toDefinition blockToExpr,
      -- Patterns
      toDefinition patternToExpr,
      toDefinition identifierPatternToExpr,
      toDefinition refPatternToExpr,
      toDefinition structPatternToExpr,
      toDefinition fieldPatternToExpr,
      toDefinition tupleStructPatternToExpr,
      toDefinition rangePatternToExpr,
      -- Literals
      toDefinition literalToExpr,
      toDefinition integerLiteralToExpr,
      toDefinition floatLiteralToExpr,
      -- Visibility and attributes
      toDefinition visibilityToExpr,
      toDefinition attributeToExpr,
      toDefinition derivesToExpr,
      -- Comments
      toDefinition toRustDocComment,
      toDefinition toRustComment]

-- =============================================================================
-- Top-level serialization
-- =============================================================================

crateToExpr :: TTermDefinition (R.Crate -> Expr)
crateToExpr = define "crateToExpr" $
  doc "Serialize a Rust crate to an AST expression" $
  lambda "crate" $
    Serialization.doubleNewlineSep @@ (Lists.map (itemWithCommentsToExpr)
      (project R._Crate R._Crate_items @@ var "crate"))

itemWithCommentsToExpr :: TTermDefinition (R.ItemWithComments -> Expr)
itemWithCommentsToExpr = define "itemWithCommentsToExpr" $
  doc "Serialize an item with optional doc comments and visibility" $
  lambda "iwc" $ lets [
    "doc">: project R._ItemWithComments R._ItemWithComments_doc @@ var "iwc",
    "vis">: project R._ItemWithComments R._ItemWithComments_visibility @@ var "iwc",
    "item">: project R._ItemWithComments R._ItemWithComments_item @@ var "iwc",
    "docPart">: Maybes.maybe
      (list ([] :: [TTerm Expr]))
      (lambda "d" $ list [Serialization.cst @@ (toRustDocComment @@ var "d")])
      (var "doc"),
    "visPart">: visibilityToExpr @@ var "vis",
    "itemPart">: itemToExpr @@ var "item"] $
    Serialization.newlineSep @@ Lists.concat (list [var "docPart", list [
      Serialization.spaceSep @@ Maybes.cat (list [var "visPart", just (var "itemPart")])]])

itemToExpr :: TTermDefinition (R.Item -> Expr)
itemToExpr = define "itemToExpr" $
  doc "Serialize a Rust item to an AST expression" $
  lambda "item" $
    cases R._Item (var "item") Nothing [
      R._Item_use>>: lambda "u" $ useDeclarationToExpr @@ var "u",
      R._Item_struct>>: lambda "s" $ structDefToExpr @@ var "s",
      R._Item_enum>>: lambda "e" $ enumDefToExpr @@ var "e",
      R._Item_fn>>: lambda "f" $ fnDefToExpr @@ var "f",
      R._Item_typeAlias>>: lambda "t" $ typeAliasToExpr @@ var "t",
      R._Item_impl>>: lambda "i" $ implBlockToExpr @@ var "i",
      R._Item_trait>>: lambda "t" $ traitDefToExpr @@ var "t",
      R._Item_mod>>: lambda "m" $ modDefToExpr @@ var "m",
      R._Item_const>>: lambda "c" $ constDefToExpr @@ var "c",
      R._Item_static>>: lambda "s" $ staticDefToExpr @@ var "s",
      R._Item_macro>>: lambda "m" $ macroInvocationToExpr @@ var "m"]

-- =============================================================================
-- Use declarations
-- =============================================================================

useDeclarationToExpr :: TTermDefinition (R.UseDeclaration -> Expr)
useDeclarationToExpr = define "useDeclarationToExpr" $
  doc "Serialize a use declaration" $
  lambda "use" $ lets [
    "pub">: project R._UseDeclaration R._UseDeclaration_public @@ var "use",
    "tree">: project R._UseDeclaration R._UseDeclaration_tree @@ var "use",
    "pubKw">: Logic.ifElse (var "pub") (just $ Serialization.cst @@ string "pub") nothing] $
    Serialization.spaceSep @@ Maybes.cat (list [
      var "pubKw",
      just $ Serialization.cst @@ string "use",
      just $ useTreeToExpr @@ var "tree",
      just $ Serialization.cst @@ string ";"])

useTreeToExpr :: TTermDefinition (R.UseTree -> Expr)
useTreeToExpr = define "useTreeToExpr" $
  doc "Serialize a use tree" $
  lambda "tree" $
    cases R._UseTree (var "tree") Nothing [
      R._UseTree_path>>: lambda "p" $
        Serialization.cst @@ (Strings.intercalate (string "::") (project R._UsePath R._UsePath_segments @@ var "p")),
      R._UseTree_rename>>: lambda "r" $ lets [
        "path">: project R._UseRename R._UseRename_path @@ var "r",
        "alias">: project R._UseRename R._UseRename_alias @@ var "r"] $
        Serialization.spaceSep @@ list [
          Serialization.cst @@ (Strings.intercalate (string "::") (var "path")),
          Serialization.cst @@ string "as",
          Serialization.cst @@ var "alias"],
      R._UseTree_glob>>: lambda "segs" $
        Serialization.cst @@ (Strings.cat2 (Strings.intercalate (string "::") (var "segs")) (string "::*")),
      R._UseTree_group>>: lambda "g" $ lets [
        "prefix">: project R._UseGroup R._UseGroup_prefix @@ var "g",
        "trees">: project R._UseGroup R._UseGroup_trees @@ var "g",
        "prefixStr">: Logic.ifElse (Lists.null $ var "prefix")
          (string "")
          (Strings.cat2 (Strings.intercalate (string "::") (var "prefix")) (string "::"))] $
        Serialization.cst @@ (Strings.cat (list [
          var "prefixStr",
          string "{",
          Strings.intercalate (string ", ") (Lists.map (lambda "t" $ Serialization.printExpr @@ (useTreeToExpr @@ var "t")) (var "trees")),
          string "}"]))]

-- =============================================================================
-- Struct definitions
-- =============================================================================

structDefToExpr :: TTermDefinition (R.StructDef -> Expr)
structDefToExpr = define "structDefToExpr" $
  doc "Serialize a struct definition" $
  lambda "s" $ lets [
    "name">: project R._StructDef R._StructDef_name @@ var "s",
    "generics">: project R._StructDef R._StructDef_generics @@ var "s",
    "whereC">: project R._StructDef R._StructDef_whereClause @@ var "s",
    "body">: project R._StructDef R._StructDef_body @@ var "s",
    "derives">: project R._StructDef R._StructDef_derives @@ var "s",
    "docC">: project R._StructDef R._StructDef_doc @@ var "s",
    "derivesAttr">: derivesToExpr @@ var "derives",
    "docPart">: Maybes.maybe (list ([] :: [TTerm Expr])) (lambda "d" $ list [Serialization.cst @@ (toRustDocComment @@ var "d")]) (var "docC"),
    "header">: Serialization.spaceSep @@ Maybes.cat (list [
      just $ Serialization.cst @@ string "struct",
      just $ Serialization.cst @@ var "name",
      genericParamsToExpr @@ var "generics"]),
    "wherePart">: Maybes.maybe nothing (lambda "w" $ just $ whereClauseToExpr @@ var "w") (var "whereC")] $
    Serialization.newlineSep @@ Lists.concat (list [
      var "docPart",
      Maybes.maybe (list ([] :: [TTerm Expr])) (lambda "d" $ list [var "d"]) (var "derivesAttr"),
      list [Serialization.spaceSep @@ Maybes.cat (list [
        just $ var "header",
        var "wherePart",
        just $ structBodyToExpr @@ var "body"])]])

structBodyToExpr :: TTermDefinition (R.StructBody -> Expr)
structBodyToExpr = define "structBodyToExpr" $
  doc "Serialize a struct body" $
  lambda "body" $
    cases R._StructBody (var "body") Nothing [
      R._StructBody_named>>: lambda "fields" $
        Serialization.curlyBracesList @@ nothing @@ Serialization.halfBlockStyle @@
          (Lists.map (structFieldToExpr) (var "fields")),
      R._StructBody_tuple>>: lambda "fields" $
        Serialization.spaceSep @@ list [
          Serialization.parenList @@ false @@ (Lists.map (lambda "f" $ typeToExpr @@ (project R._TupleField R._TupleField_type @@ var "f")) (var "fields")),
          Serialization.cst @@ string ";"],
      R._StructBody_unit>>: constant $ Serialization.cst @@ string ";"]

structFieldToExpr :: TTermDefinition (R.StructField -> Expr)
structFieldToExpr = define "structFieldToExpr" $
  doc "Serialize a struct field" $
  lambda "field" $ lets [
    "name">: project R._StructField R._StructField_name @@ var "field",
    "typ">: project R._StructField R._StructField_type @@ var "field",
    "pub">: project R._StructField R._StructField_public @@ var "field",
    "docC">: project R._StructField R._StructField_doc @@ var "field",
    "pubKw">: Logic.ifElse (var "pub") (just $ Serialization.cst @@ string "pub") nothing,
    "docPart">: Maybes.maybe (list ([] :: [TTerm Expr])) (lambda "d" $ list [Serialization.cst @@ (toRustDocComment @@ var "d")]) (var "docC")] $
    Serialization.newlineSep @@ Lists.concat (list [
      var "docPart",
      list [Serialization.spaceSep @@ Maybes.cat (list [
        var "pubKw",
        just $ Serialization.cst @@ (Strings.cat2 (var "name") (string ":")),
        just $ typeToExpr @@ var "typ"])]])

-- =============================================================================
-- Enum definitions
-- =============================================================================

enumDefToExpr :: TTermDefinition (R.EnumDef -> Expr)
enumDefToExpr = define "enumDefToExpr" $
  doc "Serialize an enum definition" $
  lambda "e" $ lets [
    "name">: project R._EnumDef R._EnumDef_name @@ var "e",
    "generics">: project R._EnumDef R._EnumDef_generics @@ var "e",
    "whereC">: project R._EnumDef R._EnumDef_whereClause @@ var "e",
    "variants">: project R._EnumDef R._EnumDef_variants @@ var "e",
    "derives">: project R._EnumDef R._EnumDef_derives @@ var "e",
    "docC">: project R._EnumDef R._EnumDef_doc @@ var "e",
    "derivesAttr">: derivesToExpr @@ var "derives",
    "docPart">: Maybes.maybe (list ([] :: [TTerm Expr])) (lambda "d" $ list [Serialization.cst @@ (toRustDocComment @@ var "d")]) (var "docC"),
    "header">: Serialization.spaceSep @@ Maybes.cat (list [
      just $ Serialization.cst @@ string "enum",
      just $ Serialization.cst @@ var "name",
      genericParamsToExpr @@ var "generics"]),
    "wherePart">: Maybes.maybe nothing (lambda "w" $ just $ whereClauseToExpr @@ var "w") (var "whereC"),
    "body">: Serialization.curlyBracesList @@ nothing @@ Serialization.halfBlockStyle @@
      (Lists.map (enumVariantToExpr) (var "variants"))] $
    Serialization.newlineSep @@ Lists.concat (list [
      var "docPart",
      Maybes.maybe (list ([] :: [TTerm Expr])) (lambda "d" $ list [var "d"]) (var "derivesAttr"),
      list [Serialization.spaceSep @@ Maybes.cat (list [
        just $ var "header",
        var "wherePart",
        just $ var "body"])]])

enumVariantToExpr :: TTermDefinition (R.EnumVariant -> Expr)
enumVariantToExpr = define "enumVariantToExpr" $
  doc "Serialize an enum variant" $
  lambda "v" $ lets [
    "name">: project R._EnumVariant R._EnumVariant_name @@ var "v",
    "body">: project R._EnumVariant R._EnumVariant_body @@ var "v",
    "docC">: project R._EnumVariant R._EnumVariant_doc @@ var "v",
    "docPart">: Maybes.maybe (list ([] :: [TTerm Expr])) (lambda "d" $ list [Serialization.cst @@ (toRustDocComment @@ var "d")]) (var "docC")] $
    Serialization.newlineSep @@ Lists.concat (list [
      var "docPart",
      list [Serialization.spaceSep @@ list [
        Serialization.cst @@ var "name",
        enumVariantBodyToExpr @@ var "body"]]])

enumVariantBodyToExpr :: TTermDefinition (R.EnumVariantBody -> Expr)
enumVariantBodyToExpr = define "enumVariantBodyToExpr" $
  doc "Serialize an enum variant body" $
  lambda "body" $
    cases R._EnumVariantBody (var "body") Nothing [
      R._EnumVariantBody_unit>>: constant $ Serialization.cst @@ string "",
      R._EnumVariantBody_tuple>>: lambda "types" $
        Serialization.parenList @@ false @@ (Lists.map (typeToExpr) (var "types")),
      R._EnumVariantBody_struct>>: lambda "fields" $
        Serialization.curlyBracesList @@ nothing @@ Serialization.halfBlockStyle @@
          (Lists.map (structFieldToExpr) (var "fields"))]

-- =============================================================================
-- Function definitions
-- =============================================================================

fnDefToExpr :: TTermDefinition (R.FnDef -> Expr)
fnDefToExpr = define "fnDefToExpr" $
  doc "Serialize a function definition" $
  lambda "f" $ lets [
    "name">: project R._FnDef R._FnDef_name @@ var "f",
    "generics">: project R._FnDef R._FnDef_generics @@ var "f",
    "whereC">: project R._FnDef R._FnDef_whereClause @@ var "f",
    "params">: project R._FnDef R._FnDef_params @@ var "f",
    "retType">: project R._FnDef R._FnDef_returnType @@ var "f",
    "body">: project R._FnDef R._FnDef_body @@ var "f",
    "isAsync">: project R._FnDef R._FnDef_async @@ var "f",
    "isConst">: project R._FnDef R._FnDef_const @@ var "f",
    "isUnsafe">: project R._FnDef R._FnDef_unsafe @@ var "f",
    "docC">: project R._FnDef R._FnDef_doc @@ var "f",
    "docPart">: Maybes.maybe (list ([] :: [TTerm Expr])) (lambda "d" $ list [Serialization.cst @@ (toRustDocComment @@ var "d")]) (var "docC"),
    "asyncKw">: Logic.ifElse (var "isAsync") (just $ Serialization.cst @@ string "async") nothing,
    "constKw">: Logic.ifElse (var "isConst") (just $ Serialization.cst @@ string "const") nothing,
    "unsafeKw">: Logic.ifElse (var "isUnsafe") (just $ Serialization.cst @@ string "unsafe") nothing,
    "fnKw">: Serialization.cst @@ string "fn",
    "nameExpr">: Serialization.cst @@ var "name",
    "genericsExpr">: genericParamsToExpr @@ var "generics",
    "paramsExpr">: Serialization.parenList @@ false @@ (Lists.map (fnParamToExpr) (var "params")),
    "retTypeExpr">: Maybes.maybe nothing (lambda "t" $ just $ Serialization.spaceSep @@ list [
      Serialization.cst @@ string "->",
      typeToExpr @@ var "t"]) (var "retType"),
    "whereExpr">: Maybes.maybe nothing (lambda "w" $ just $ whereClauseToExpr @@ var "w") (var "whereC"),
    "header">: Serialization.spaceSep @@ Maybes.cat (list [
      var "asyncKw", var "constKw", var "unsafeKw",
      just $ var "fnKw", just $ var "nameExpr", var "genericsExpr",
      just $ var "paramsExpr", var "retTypeExpr", var "whereExpr"])] $
    Serialization.newlineSep @@ Lists.concat (list [
      var "docPart",
      list [Serialization.spaceSep @@ list [var "header", blockToExpr @@ var "body"]]])

fnParamToExpr :: TTermDefinition (R.FnParam -> Expr)
fnParamToExpr = define "fnParamToExpr" $
  doc "Serialize a function parameter" $
  lambda "param" $ lets [
    "pat">: project R._FnParam R._FnParam_pattern @@ var "param",
    "typ">: project R._FnParam R._FnParam_type @@ var "param"] $
    Serialization.spaceSep @@ list [
      Serialization.cst @@ (Strings.cat2 (Serialization.printExpr @@ (patternToExpr @@ var "pat")) (string ":")),
      typeToExpr @@ var "typ"]

methodParamToExpr :: TTermDefinition (R.MethodParam -> Expr)
methodParamToExpr = define "methodParamToExpr" $
  doc "Serialize a method parameter" $
  lambda "param" $
    cases R._MethodParam (var "param") Nothing [
      R._MethodParam_self>>: lambda "sp" $
        cases R._SelfParam (var "sp") Nothing [
          R._SelfParam_owned>>: constant $ Serialization.cst @@ string "self",
          R._SelfParam_ref>>: constant $ Serialization.cst @@ string "&self",
          R._SelfParam_refMut>>: constant $ Serialization.cst @@ string "&mut self"],
      R._MethodParam_regular>>: lambda "fp" $ fnParamToExpr @@ var "fp"]

-- =============================================================================
-- Type aliases
-- =============================================================================

typeAliasToExpr :: TTermDefinition (R.TypeAlias -> Expr)
typeAliasToExpr = define "typeAliasToExpr" $
  doc "Serialize a type alias" $
  lambda "ta" $ lets [
    "name">: project R._TypeAlias R._TypeAlias_name @@ var "ta",
    "generics">: project R._TypeAlias R._TypeAlias_generics @@ var "ta",
    "typ">: project R._TypeAlias R._TypeAlias_type @@ var "ta",
    "docC">: project R._TypeAlias R._TypeAlias_doc @@ var "ta",
    "docPart">: Maybes.maybe (list ([] :: [TTerm Expr])) (lambda "d" $ list [Serialization.cst @@ (toRustDocComment @@ var "d")]) (var "docC")] $
    Serialization.newlineSep @@ Lists.concat (list [
      var "docPart",
      list [Serialization.spaceSep @@ Maybes.cat (list [
        just $ Serialization.cst @@ string "type",
        just $ Serialization.cst @@ var "name",
        genericParamsToExpr @@ var "generics",
        just $ Serialization.cst @@ string "=",
        just $ typeToExpr @@ var "typ",
        just $ Serialization.cst @@ string ";"])]])

-- =============================================================================
-- Const, static, module definitions
-- =============================================================================

constDefToExpr :: TTermDefinition (R.ConstDef -> Expr)
constDefToExpr = define "constDefToExpr" $
  doc "Serialize a const definition" $
  lambda "c" $ lets [
    "name">: project R._ConstDef R._ConstDef_name @@ var "c",
    "typ">: project R._ConstDef R._ConstDef_type @@ var "c",
    "val">: project R._ConstDef R._ConstDef_value @@ var "c"] $
    Serialization.spaceSep @@ list [
      Serialization.cst @@ string "const",
      Serialization.cst @@ (Strings.cat2 (var "name") (string ":")),
      typeToExpr @@ var "typ",
      Serialization.cst @@ string "=",
      expressionToExpr @@ var "val",
      Serialization.cst @@ string ";"]

staticDefToExpr :: TTermDefinition (R.StaticDef -> Expr)
staticDefToExpr = define "staticDefToExpr" $
  doc "Serialize a static definition" $
  lambda "s" $ lets [
    "name">: project R._StaticDef R._StaticDef_name @@ var "s",
    "typ">: project R._StaticDef R._StaticDef_type @@ var "s",
    "val">: project R._StaticDef R._StaticDef_value @@ var "s",
    "mut">: project R._StaticDef R._StaticDef_mutable @@ var "s",
    "mutKw">: Logic.ifElse (var "mut") (just $ Serialization.cst @@ string "mut") nothing] $
    Serialization.spaceSep @@ Maybes.cat (list [
      just $ Serialization.cst @@ string "static",
      var "mutKw",
      just $ Serialization.cst @@ (Strings.cat2 (var "name") (string ":")),
      just $ typeToExpr @@ var "typ",
      just $ Serialization.cst @@ string "=",
      just $ expressionToExpr @@ var "val",
      just $ Serialization.cst @@ string ";"])

modDefToExpr :: TTermDefinition (R.ModDef -> Expr)
modDefToExpr = define "modDefToExpr" $
  doc "Serialize a module definition" $
  lambda "m" $ lets [
    "name">: project R._ModDef R._ModDef_name @@ var "m",
    "body">: project R._ModDef R._ModDef_body @@ var "m"] $
    Maybes.maybe
      (Serialization.spaceSep @@ list [
        Serialization.cst @@ string "mod",
        Serialization.cst @@ var "name",
        Serialization.cst @@ string ";"])
      (lambda "items" $ Serialization.spaceSep @@ list [
        Serialization.cst @@ string "mod",
        Serialization.cst @@ var "name",
        Serialization.curlyBracesList @@ nothing @@ Serialization.halfBlockStyle @@
          (Lists.map (itemToExpr) (var "items"))])
      (var "body")

-- =============================================================================
-- Impl blocks
-- =============================================================================

implBlockToExpr :: TTermDefinition (R.ImplBlock -> Expr)
implBlockToExpr = define "implBlockToExpr" $
  doc "Serialize an impl block" $
  lambda "i" $ lets [
    "generics">: project R._ImplBlock R._ImplBlock_generics @@ var "i",
    "whereC">: project R._ImplBlock R._ImplBlock_whereClause @@ var "i",
    "trait">: project R._ImplBlock R._ImplBlock_trait @@ var "i",
    "selfType">: project R._ImplBlock R._ImplBlock_selfType @@ var "i",
    "items">: project R._ImplBlock R._ImplBlock_items @@ var "i",
    "genericsExpr">: genericParamsToExpr @@ var "generics",
    "traitPart">: Maybes.maybe nothing (lambda "t" $ just $ Serialization.spaceSep @@ list [
      typePathToExpr @@ var "t",
      Serialization.cst @@ string "for"]) (var "trait"),
    "wherePart">: Maybes.maybe nothing (lambda "w" $ just $ whereClauseToExpr @@ var "w") (var "whereC"),
    "header">: Serialization.spaceSep @@ Maybes.cat (list [
      just $ Serialization.cst @@ string "impl",
      var "genericsExpr",
      var "traitPart",
      just $ typeToExpr @@ var "selfType",
      var "wherePart"]),
    "body">: Serialization.curlyBracesList @@ nothing @@ Serialization.halfBlockStyle @@
      (Lists.map (implItemToExpr) (var "items"))] $
    Serialization.spaceSep @@ list [var "header", var "body"]

implItemToExpr :: TTermDefinition (R.ImplItem -> Expr)
implItemToExpr = define "implItemToExpr" $
  doc "Serialize an impl item" $
  lambda "item" $
    cases R._ImplItem (var "item") Nothing [
      R._ImplItem_method>>: lambda "m" $ implMethodToExpr @@ var "m",
      R._ImplItem_type>>: lambda "t" $ typeAliasToExpr @@ var "t",
      R._ImplItem_const>>: lambda "c" $ constDefToExpr @@ var "c"]

implMethodToExpr :: TTermDefinition (R.ImplMethod -> Expr)
implMethodToExpr = define "implMethodToExpr" $
  doc "Serialize an impl method" $
  lambda "m" $ lets [
    "name">: project R._ImplMethod R._ImplMethod_name @@ var "m",
    "generics">: project R._ImplMethod R._ImplMethod_generics @@ var "m",
    "whereC">: project R._ImplMethod R._ImplMethod_whereClause @@ var "m",
    "params">: project R._ImplMethod R._ImplMethod_params @@ var "m",
    "retType">: project R._ImplMethod R._ImplMethod_returnType @@ var "m",
    "body">: project R._ImplMethod R._ImplMethod_body @@ var "m",
    "pub">: project R._ImplMethod R._ImplMethod_public @@ var "m",
    "docC">: project R._ImplMethod R._ImplMethod_doc @@ var "m",
    "docPart">: Maybes.maybe (list ([] :: [TTerm Expr])) (lambda "d" $ list [Serialization.cst @@ (toRustDocComment @@ var "d")]) (var "docC"),
    "pubKw">: Logic.ifElse (var "pub") (just $ Serialization.cst @@ string "pub") nothing,
    "genericsExpr">: genericParamsToExpr @@ var "generics",
    "paramsExpr">: Serialization.parenList @@ false @@ (Lists.map (methodParamToExpr) (var "params")),
    "retTypeExpr">: Maybes.maybe nothing (lambda "t" $ just $ Serialization.spaceSep @@ list [
      Serialization.cst @@ string "->",
      typeToExpr @@ var "t"]) (var "retType"),
    "whereExpr">: Maybes.maybe nothing (lambda "w" $ just $ whereClauseToExpr @@ var "w") (var "whereC"),
    "header">: Serialization.spaceSep @@ Maybes.cat (list [
      var "pubKw",
      just $ Serialization.cst @@ string "fn",
      just $ Serialization.cst @@ var "name",
      var "genericsExpr",
      just $ var "paramsExpr",
      var "retTypeExpr",
      var "whereExpr"])] $
    Serialization.newlineSep @@ Lists.concat (list [
      var "docPart",
      list [Serialization.spaceSep @@ list [var "header", blockToExpr @@ var "body"]]])

-- =============================================================================
-- Trait definitions
-- =============================================================================

traitDefToExpr :: TTermDefinition (R.TraitDef -> Expr)
traitDefToExpr = define "traitDefToExpr" $
  doc "Serialize a trait definition" $
  lambda "t" $ lets [
    "name">: project R._TraitDef R._TraitDef_name @@ var "t",
    "generics">: project R._TraitDef R._TraitDef_generics @@ var "t",
    "whereC">: project R._TraitDef R._TraitDef_whereClause @@ var "t",
    "supers">: project R._TraitDef R._TraitDef_superTraits @@ var "t",
    "items">: project R._TraitDef R._TraitDef_items @@ var "t",
    "isUnsafe">: project R._TraitDef R._TraitDef_unsafe @@ var "t",
    "docC">: project R._TraitDef R._TraitDef_doc @@ var "t",
    "docPart">: Maybes.maybe (list ([] :: [TTerm Expr])) (lambda "d" $ list [Serialization.cst @@ (toRustDocComment @@ var "d")]) (var "docC"),
    "unsafeKw">: Logic.ifElse (var "isUnsafe") (just $ Serialization.cst @@ string "unsafe") nothing,
    "genericsExpr">: genericParamsToExpr @@ var "generics",
    "superPart">: Logic.ifElse (Lists.null $ var "supers") nothing
      (just $ Serialization.spaceSep @@ list [
        Serialization.cst @@ string ":",
        Serialization.cst @@ (Strings.intercalate (string " + ") (Lists.map (lambda "b" $ Serialization.printExpr @@ (typeParamBoundToExpr @@ var "b")) (var "supers")))]),
    "wherePart">: Maybes.maybe nothing (lambda "w" $ just $ whereClauseToExpr @@ var "w") (var "whereC"),
    "header">: Serialization.spaceSep @@ Maybes.cat (list [
      var "unsafeKw",
      just $ Serialization.cst @@ string "trait",
      just $ Serialization.cst @@ var "name",
      var "genericsExpr",
      var "superPart",
      var "wherePart"]),
    "body">: Serialization.curlyBracesList @@ nothing @@ Serialization.halfBlockStyle @@
      (Lists.map (traitItemToExpr) (var "items"))] $
    Serialization.newlineSep @@ Lists.concat (list [
      var "docPart",
      list [Serialization.spaceSep @@ list [var "header", var "body"]]])

traitItemToExpr :: TTermDefinition (R.TraitItem -> Expr)
traitItemToExpr = define "traitItemToExpr" $
  doc "Serialize a trait item" $
  lambda "item" $
    cases R._TraitItem (var "item") Nothing [
      R._TraitItem_method>>: lambda "m" $ traitMethodToExpr @@ var "m",
      R._TraitItem_type>>: lambda "t" $ traitTypeToExpr @@ var "t",
      R._TraitItem_const>>: lambda "c" $ traitConstToExpr @@ var "c"]

traitMethodToExpr :: TTermDefinition (R.TraitMethod -> Expr)
traitMethodToExpr = define "traitMethodToExpr" $
  doc "Serialize a trait method" $
  lambda "m" $ lets [
    "name">: project R._TraitMethod R._TraitMethod_name @@ var "m",
    "generics">: project R._TraitMethod R._TraitMethod_generics @@ var "m",
    "params">: project R._TraitMethod R._TraitMethod_params @@ var "m",
    "retType">: project R._TraitMethod R._TraitMethod_returnType @@ var "m",
    "defBody">: project R._TraitMethod R._TraitMethod_defaultBody @@ var "m",
    "genericsExpr">: genericParamsToExpr @@ var "generics",
    "paramsExpr">: Serialization.parenList @@ false @@ (Lists.map (methodParamToExpr) (var "params")),
    "retTypeExpr">: Maybes.maybe nothing (lambda "t" $ just $ Serialization.spaceSep @@ list [
      Serialization.cst @@ string "->",
      typeToExpr @@ var "t"]) (var "retType"),
    "header">: Serialization.spaceSep @@ Maybes.cat (list [
      just $ Serialization.cst @@ string "fn",
      just $ Serialization.cst @@ var "name",
      var "genericsExpr",
      just $ var "paramsExpr",
      var "retTypeExpr"])] $
    Maybes.maybe
      (Serialization.spaceSep @@ list [var "header", Serialization.cst @@ string ";"])
      (lambda "body" $ Serialization.spaceSep @@ list [var "header", blockToExpr @@ var "body"])
      (var "defBody")

traitTypeToExpr :: TTermDefinition (R.TraitType -> Expr)
traitTypeToExpr = define "traitTypeToExpr" $
  doc "Serialize a trait associated type" $
  lambda "t" $ lets [
    "name">: project R._TraitType R._TraitType_name @@ var "t",
    "bounds">: project R._TraitType R._TraitType_bounds @@ var "t",
    "def">: project R._TraitType R._TraitType_default @@ var "t",
    "boundsPart">: Logic.ifElse (Lists.null $ var "bounds") nothing
      (just $ Serialization.spaceSep @@ list [
        Serialization.cst @@ string ":",
        Serialization.cst @@ (Strings.intercalate (string " + ") (Lists.map (lambda "b" $ Serialization.printExpr @@ (typeParamBoundToExpr @@ var "b")) (var "bounds")))]),
    "defPart">: Maybes.maybe nothing (lambda "d" $ just $ Serialization.spaceSep @@ list [
      Serialization.cst @@ string "=",
      typeToExpr @@ var "d"]) (var "def")] $
    Serialization.spaceSep @@ Maybes.cat (list [
      just $ Serialization.cst @@ string "type",
      just $ Serialization.cst @@ var "name",
      var "boundsPart",
      var "defPart",
      just $ Serialization.cst @@ string ";"])

traitConstToExpr :: TTermDefinition (R.TraitConst -> Expr)
traitConstToExpr = define "traitConstToExpr" $
  doc "Serialize a trait associated constant" $
  lambda "c" $ lets [
    "name">: project R._TraitConst R._TraitConst_name @@ var "c",
    "typ">: project R._TraitConst R._TraitConst_type @@ var "c",
    "def">: project R._TraitConst R._TraitConst_default @@ var "c",
    "defPart">: Maybes.maybe nothing (lambda "d" $ just $ Serialization.spaceSep @@ list [
      Serialization.cst @@ string "=",
      expressionToExpr @@ var "d"]) (var "def")] $
    Serialization.spaceSep @@ Maybes.cat (list [
      just $ Serialization.cst @@ string "const",
      just $ Serialization.cst @@ (Strings.cat2 (var "name") (string ":")),
      just $ typeToExpr @@ var "typ",
      var "defPart",
      just $ Serialization.cst @@ string ";"])

-- =============================================================================
-- Generics and where clauses
-- =============================================================================

genericParamToExpr :: TTermDefinition (R.GenericParam -> Expr)
genericParamToExpr = define "genericParamToExpr" $
  doc "Serialize a generic parameter" $
  lambda "gp" $ lets [
    "name">: project R._GenericParam R._GenericParam_name @@ var "gp",
    "bounds">: project R._GenericParam R._GenericParam_bounds @@ var "gp"] $
    Logic.ifElse (Lists.null $ var "bounds")
      (Serialization.cst @@ var "name")
      (Serialization.spaceSep @@ list [
        Serialization.cst @@ (Strings.cat2 (var "name") (string ":")),
        Serialization.cst @@ (Strings.intercalate (string " + ") (Lists.map (lambda "b" $ Serialization.printExpr @@ (typeParamBoundToExpr @@ var "b")) (var "bounds")))])

genericParamsToExpr :: TTermDefinition ([R.GenericParam] -> Maybe Expr)
genericParamsToExpr = define "genericParamsToExpr" $
  doc "Serialize a list of generic parameters" $
  lambda "gps" $
    Logic.ifElse (Lists.null $ var "gps")
      nothing
      (just $ Serialization.angleBracesList @@ Serialization.inlineStyle @@ (Lists.map (genericParamToExpr) (var "gps")))

typeParamBoundToExpr :: TTermDefinition (R.TypeParamBound -> Expr)
typeParamBoundToExpr = define "typeParamBoundToExpr" $
  doc "Serialize a type parameter bound" $
  lambda "bound" $
    cases R._TypeParamBound (var "bound") Nothing [
      R._TypeParamBound_trait>>: lambda "tp" $ typePathToExpr @@ var "tp",
      R._TypeParamBound_lifetime>>: lambda "lt" $
        Serialization.cst @@ (Strings.cat2 (string "'") (project R._Lifetime R._Lifetime_name @@ var "lt"))]

whereClauseToExpr :: TTermDefinition (R.WhereClause -> Expr)
whereClauseToExpr = define "whereClauseToExpr" $
  doc "Serialize a where clause" $
  lambda "wc" $ lets [
    "preds">: project R._WhereClause R._WhereClause_predicates @@ var "wc",
    "predExprs">: Lists.map (lambda "p" $ lets [
      "typ">: project R._WherePredicate R._WherePredicate_type @@ var "p",
      "bounds">: project R._WherePredicate R._WherePredicate_bounds @@ var "p"] $
      Serialization.spaceSep @@ list [
        typeToExpr @@ var "typ",
        Serialization.cst @@ string ":",
        Serialization.cst @@ (Strings.intercalate (string " + ") (Lists.map (lambda "b" $ Serialization.printExpr @@ (typeParamBoundToExpr @@ var "b")) (var "bounds")))]) (var "preds")] $
    Serialization.spaceSep @@ list [
      Serialization.cst @@ string "where",
      Serialization.commaSep @@ Serialization.inlineStyle @@ var "predExprs"]

-- =============================================================================
-- Types
-- =============================================================================

typeToExpr :: TTermDefinition (R.Type -> Expr)
typeToExpr = define "typeToExpr" $
  doc "Serialize a Rust type" $
  lambda "typ" $
    cases R._Type (var "typ") Nothing [
      R._Type_path>>: lambda "tp" $ typePathToExpr @@ var "tp",
      R._Type_reference>>: lambda "rt" $ referenceTypeToExpr @@ var "rt",
      R._Type_slice>>: lambda "t" $ Serialization.bracketList @@ Serialization.inlineStyle @@ list [typeToExpr @@ var "t"],
      R._Type_array>>: lambda "at" $ lets [
        "elem">: project R._ArrayType R._ArrayType_element @@ var "at",
        "len">: project R._ArrayType R._ArrayType_length @@ var "at"] $
        Serialization.cst @@ (Strings.cat (list [
          string "[",
          Serialization.printExpr @@ (typeToExpr @@ var "elem"),
          string "; ",
          Serialization.printExpr @@ (expressionToExpr @@ var "len"),
          string "]"])),
      R._Type_tuple>>: lambda "ts" $ Serialization.parenList @@ false @@ (Lists.map (typeToExpr) (var "ts")),
      R._Type_fnPointer>>: lambda "fp" $ lets [
        "params">: project R._FnPointerType R._FnPointerType_params @@ var "fp",
        "ret">: project R._FnPointerType R._FnPointerType_returnType @@ var "fp"] $
        Serialization.spaceSep @@ list [
          Serialization.cst @@ string "fn",
          Serialization.parenList @@ false @@ (Lists.map (typeToExpr) (var "params")),
          Serialization.cst @@ string "->",
          typeToExpr @@ var "ret"],
      R._Type_implTrait>>: lambda "bounds" $
        Serialization.spaceSep @@ list [
          Serialization.cst @@ string "impl",
          Serialization.cst @@ (Strings.intercalate (string " + ") (Lists.map (lambda "b" $ Serialization.printExpr @@ (typeParamBoundToExpr @@ var "b")) (var "bounds")))],
      R._Type_dynTrait>>: lambda "bounds" $
        Serialization.spaceSep @@ list [
          Serialization.cst @@ string "dyn",
          Serialization.cst @@ (Strings.intercalate (string " + ") (Lists.map (lambda "b" $ Serialization.printExpr @@ (typeParamBoundToExpr @@ var "b")) (var "bounds")))],
      R._Type_inferred>>: constant $ Serialization.cst @@ string "_",
      R._Type_unit>>: constant $ Serialization.cst @@ string "()",
      R._Type_never>>: constant $ Serialization.cst @@ string "!",
      R._Type_rawPointer>>: lambda "rp" $ lets [
        "mut">: project R._RawPointerType R._RawPointerType_mutable @@ var "rp",
        "t">: project R._RawPointerType R._RawPointerType_type @@ var "rp",
        "kw">: Logic.ifElse (var "mut") (string "*mut") (string "*const")] $
        Serialization.spaceSep @@ list [
          Serialization.cst @@ var "kw",
          typeToExpr @@ var "t"],
      R._Type_macro>>: lambda "m" $ macroInvocationToExpr @@ var "m"]

typePathToExpr :: TTermDefinition (R.TypePath -> Expr)
typePathToExpr = define "typePathToExpr" $
  doc "Serialize a type path" $
  lambda "tp" $ lets [
    "global">: project R._TypePath R._TypePath_global @@ var "tp",
    "segs">: project R._TypePath R._TypePath_segments @@ var "tp",
    "prefix">: Logic.ifElse (var "global") (string "::") (string ""),
    "segStrs">: Lists.map (lambda "s" $ Serialization.printExpr @@ (pathSegmentToExpr @@ var "s")) (var "segs")] $
    Serialization.cst @@ (Strings.cat2 (var "prefix") (Strings.intercalate (string "::") (var "segStrs")))

pathSegmentToExpr :: TTermDefinition (R.PathSegment -> Expr)
pathSegmentToExpr = define "pathSegmentToExpr" $
  doc "Serialize a path segment" $
  lambda "seg" $ lets [
    "name">: project R._PathSegment R._PathSegment_name @@ var "seg",
    "args">: project R._PathSegment R._PathSegment_arguments @@ var "seg"] $
    Serialization.spaceSep @@ Maybes.cat (list [
      just $ Serialization.cst @@ var "name",
      genericArgumentsToExpr @@ var "args"])

genericArgumentsToExpr :: TTermDefinition (R.GenericArguments -> Maybe Expr)
genericArgumentsToExpr = define "genericArgumentsToExpr" $
  doc "Serialize generic arguments" $
  lambda "args" $
    cases R._GenericArguments (var "args") Nothing [
      R._GenericArguments_none>>: constant nothing,
      R._GenericArguments_angleBracketed>>: lambda "ab" $ lets [
        "args">: project R._AngleBracketedArgs R._AngleBracketedArgs_args @@ var "ab"] $
        just $ Serialization.angleBracesList @@ Serialization.inlineStyle @@ (Lists.map (genericArgToExpr) (var "args")),
      R._GenericArguments_parenthesized>>: lambda "pa" $ lets [
        "inputs">: project R._ParenthesizedArgs R._ParenthesizedArgs_inputs @@ var "pa",
        "output">: project R._ParenthesizedArgs R._ParenthesizedArgs_output @@ var "pa",
        "inputPart">: Serialization.parenList @@ false @@ (Lists.map (typeToExpr) (var "inputs")),
        "outputPart">: Maybes.maybe nothing (lambda "t" $ just $ Serialization.spaceSep @@ list [
          Serialization.cst @@ string "->",
          typeToExpr @@ var "t"]) (var "output")] $
        just $ Serialization.spaceSep @@ Maybes.cat (list [just $ var "inputPart", var "outputPart"])]

genericArgToExpr :: TTermDefinition (R.GenericArg -> Expr)
genericArgToExpr = define "genericArgToExpr" $
  doc "Serialize a generic argument" $
  lambda "arg" $
    cases R._GenericArg (var "arg") Nothing [
      R._GenericArg_type>>: lambda "t" $ typeToExpr @@ var "t",
      R._GenericArg_lifetime>>: lambda "lt" $
        Serialization.cst @@ (Strings.cat2 (string "'") (project R._Lifetime R._Lifetime_name @@ var "lt")),
      R._GenericArg_const>>: lambda "e" $ expressionToExpr @@ var "e",
      R._GenericArg_binding>>: lambda "tb" $ lets [
        "name">: project R._TypeBinding R._TypeBinding_name @@ var "tb",
        "typ">: project R._TypeBinding R._TypeBinding_type @@ var "tb"] $
        Serialization.spaceSep @@ list [
          Serialization.cst @@ var "name",
          Serialization.cst @@ string "=",
          typeToExpr @@ var "typ"]]

referenceTypeToExpr :: TTermDefinition (R.ReferenceType -> Expr)
referenceTypeToExpr = define "referenceTypeToExpr" $
  doc "Serialize a reference type" $
  lambda "rt" $ lets [
    "lt">: project R._ReferenceType R._ReferenceType_lifetime @@ var "rt",
    "mut">: project R._ReferenceType R._ReferenceType_mutable @@ var "rt",
    "t">: project R._ReferenceType R._ReferenceType_type @@ var "rt",
    "ltPart">: Maybes.maybe (string "") (lambda "l" $ Strings.cat2 (string "'") (Strings.cat2 (project R._Lifetime R._Lifetime_name @@ var "l") (string " "))) (var "lt"),
    "mutPart">: Logic.ifElse (var "mut") (string "mut ") (string "")] $
    Serialization.cst @@ (Strings.cat (list [
      string "&",
      var "ltPart",
      var "mutPart",
      Serialization.printExpr @@ (typeToExpr @@ var "t")]))

-- =============================================================================
-- Expressions
-- =============================================================================

expressionToExpr :: TTermDefinition (R.Expression -> Expr)
expressionToExpr = define "expressionToExpr" $
  doc "Serialize a Rust expression" $
  lambda "expr" $
    cases R._Expression (var "expr") Nothing [
      R._Expression_literal>>: lambda "l" $ literalToExpr @@ var "l",
      R._Expression_path>>: lambda "p" $ exprPathToExpr @@ var "p",
      R._Expression_block>>: lambda "b" $ blockToExpr @@ var "b",
      R._Expression_call>>: lambda "c" $ callExprToExpr @@ var "c",
      R._Expression_methodCall>>: lambda "m" $ methodCallExprToExpr @@ var "m",
      R._Expression_fieldAccess>>: lambda "f" $ fieldAccessExprToExpr @@ var "f",
      R._Expression_tupleIndex>>: lambda "t" $ tupleIndexExprToExpr @@ var "t",
      R._Expression_closure>>: lambda "c" $ closureExprToExpr @@ var "c",
      R._Expression_if>>: lambda "i" $ ifExprToExpr @@ var "i",
      R._Expression_match>>: lambda "m" $ matchExprToExpr @@ var "m",
      R._Expression_loop>>: lambda "l" $ loopExprToExpr @@ var "l",
      R._Expression_while>>: lambda "w" $ whileExprToExpr @@ var "w",
      R._Expression_for>>: lambda "f" $ forExprToExpr @@ var "f",
      R._Expression_binary>>: lambda "b" $ binaryExprToExpr @@ var "b",
      R._Expression_unary>>: lambda "u" $ unaryExprToExpr @@ var "u",
      R._Expression_reference>>: lambda "r" $ refExprToExpr @@ var "r",
      R._Expression_dereference>>: lambda "d" $ Serialization.prefix @@ (string "*") @@ (expressionToExpr @@ var "d"),
      R._Expression_struct>>: lambda "s" $ structExprToExpr @@ var "s",
      R._Expression_tuple>>: lambda "es" $ Serialization.parenList @@ false @@ (Lists.map (expressionToExpr) (var "es")),
      R._Expression_array>>: lambda "a" $ arrayExprToExpr @@ var "a",
      R._Expression_index>>: lambda "i" $ indexExprToExpr @@ var "i",
      R._Expression_range>>: lambda "r" $ rangeExprToExpr @@ var "r",
      R._Expression_return>>: lambda "mr" $ Maybes.maybe
        (Serialization.cst @@ string "return")
        (lambda "e" $ Serialization.spaceSep @@ list [Serialization.cst @@ string "return", expressionToExpr @@ var "e"])
        (var "mr"),
      R._Expression_break>>: lambda "mb" $ Maybes.maybe
        (Serialization.cst @@ string "break")
        (lambda "e" $ Serialization.spaceSep @@ list [Serialization.cst @@ string "break", expressionToExpr @@ var "e"])
        (var "mb"),
      R._Expression_continue>>: constant $ Serialization.cst @@ string "continue",
      R._Expression_try>>: lambda "e" $ Serialization.cst @@ (Strings.cat2 (Serialization.printExpr @@ (expressionToExpr @@ var "e")) (string "?")),
      R._Expression_cast>>: lambda "c" $ castExprToExpr @@ var "c",
      R._Expression_typeAscription>>: lambda "t" $ typeAscriptionExprToExpr @@ var "t",
      R._Expression_await>>: lambda "e" $ Serialization.cst @@ (Strings.cat2 (Serialization.printExpr @@ (expressionToExpr @@ var "e")) (string ".await")),
      R._Expression_assign>>: lambda "a" $ assignExprToExpr @@ var "a",
      R._Expression_compoundAssign>>: lambda "c" $ compoundAssignExprToExpr @@ var "c",
      R._Expression_macro>>: lambda "m" $ macroInvocationToExpr @@ var "m",
      R._Expression_paren>>: lambda "e" $ Serialization.parenthesize @@ (expressionToExpr @@ var "e")]

exprPathToExpr :: TTermDefinition (R.ExprPath -> Expr)
exprPathToExpr = define "exprPathToExpr" $
  doc "Serialize an expression path" $
  lambda "ep" $ lets [
    "global">: project R._ExprPath R._ExprPath_global @@ var "ep",
    "segs">: project R._ExprPath R._ExprPath_segments @@ var "ep",
    "prefix">: Logic.ifElse (var "global") (string "::") (string ""),
    "segStrs">: Lists.map (lambda "s" $ Serialization.printExpr @@ (pathSegmentToExpr @@ var "s")) (var "segs")] $
    Serialization.cst @@ (Strings.cat2 (var "prefix") (Strings.intercalate (string "::") (var "segStrs")))

callExprToExpr :: TTermDefinition (R.CallExpr -> Expr)
callExprToExpr = define "callExprToExpr" $
  doc "Serialize a function call expression" $
  lambda "c" $ lets [
    "func">: project R._CallExpr R._CallExpr_function @@ var "c",
    "args">: project R._CallExpr R._CallExpr_args @@ var "c"] $
    Serialization.spaceSep @@ list [
      expressionToExpr @@ var "func",
      Serialization.parenList @@ false @@ (Lists.map (expressionToExpr) (var "args"))]

methodCallExprToExpr :: TTermDefinition (R.MethodCallExpr -> Expr)
methodCallExprToExpr = define "methodCallExprToExpr" $
  doc "Serialize a method call expression" $
  lambda "m" $ lets [
    "recv">: project R._MethodCallExpr R._MethodCallExpr_receiver @@ var "m",
    "method">: project R._MethodCallExpr R._MethodCallExpr_method @@ var "m",
    "turbo">: project R._MethodCallExpr R._MethodCallExpr_turbofish @@ var "m",
    "args">: project R._MethodCallExpr R._MethodCallExpr_args @@ var "m",
    "turboPart">: Logic.ifElse (Lists.null $ var "turbo")
      (string "")
      (Strings.cat (list [
        string "::<",
        Strings.intercalate (string ", ") (Lists.map (lambda "t" $ Serialization.printExpr @@ (typeToExpr @@ var "t")) (var "turbo")),
        string ">"]))] $
    Serialization.cst @@ (Strings.cat (list [
      Serialization.printExpr @@ (expressionToExpr @@ var "recv"),
      string ".",
      var "method",
      var "turboPart",
      string "(",
      Strings.intercalate (string ", ") (Lists.map (lambda "a" $ Serialization.printExpr @@ (expressionToExpr @@ var "a")) (var "args")),
      string ")"]))

fieldAccessExprToExpr :: TTermDefinition (R.FieldAccessExpr -> Expr)
fieldAccessExprToExpr = define "fieldAccessExprToExpr" $
  doc "Serialize a field access expression" $
  lambda "f" $ lets [
    "obj">: project R._FieldAccessExpr R._FieldAccessExpr_object @@ var "f",
    "field">: project R._FieldAccessExpr R._FieldAccessExpr_field @@ var "f"] $
    Serialization.cst @@ (Strings.cat (list [
      Serialization.printExpr @@ (expressionToExpr @@ var "obj"),
      string ".",
      var "field"]))

tupleIndexExprToExpr :: TTermDefinition (R.TupleIndexExpr -> Expr)
tupleIndexExprToExpr = define "tupleIndexExprToExpr" $
  doc "Serialize a tuple index expression" $
  lambda "t" $ lets [
    "tuple">: project R._TupleIndexExpr R._TupleIndexExpr_tuple @@ var "t",
    "idx">: project R._TupleIndexExpr R._TupleIndexExpr_index @@ var "t"] $
    Serialization.cst @@ (Strings.cat (list [
      Serialization.printExpr @@ (expressionToExpr @@ var "tuple"),
      string ".",
      Literals.showInt32 $ var "idx"]))

closureExprToExpr :: TTermDefinition (R.ClosureExpr -> Expr)
closureExprToExpr = define "closureExprToExpr" $
  doc "Serialize a closure expression" $
  lambda "c" $ lets [
    "move">: project R._ClosureExpr R._ClosureExpr_move @@ var "c",
    "params">: project R._ClosureExpr R._ClosureExpr_params @@ var "c",
    "retType">: project R._ClosureExpr R._ClosureExpr_returnType @@ var "c",
    "body">: project R._ClosureExpr R._ClosureExpr_body @@ var "c",
    "moveKw">: Logic.ifElse (var "move") (just $ Serialization.cst @@ string "move") nothing,
    "paramsStr">: Strings.cat (list [
      string "|",
      Strings.intercalate (string ", ") (Lists.map (closureParamToStr) (var "params")),
      string "|"]),
    "retPart">: Maybes.maybe nothing (lambda "t" $ just $ Serialization.spaceSep @@ list [
      Serialization.cst @@ string "->",
      typeToExpr @@ var "t"]) (var "retType")] $
    Serialization.spaceSep @@ Maybes.cat (list [
      var "moveKw",
      just $ Serialization.cst @@ var "paramsStr",
      var "retPart",
      just $ expressionToExpr @@ var "body"])

closureParamToStr :: TTermDefinition (R.ClosureParam -> String)
closureParamToStr = define "closureParamToStr" $
  doc "Serialize a closure parameter to string" $
  lambda "cp" $ lets [
    "pat">: project R._ClosureParam R._ClosureParam_pattern @@ var "cp",
    "typ">: project R._ClosureParam R._ClosureParam_type @@ var "cp",
    "patStr">: Serialization.printExpr @@ (patternToExpr @@ var "pat")] $
    Maybes.maybe
      (var "patStr")
      (lambda "t" $ Strings.cat (list [var "patStr", string ": ", Serialization.printExpr @@ (typeToExpr @@ var "t")]))
      (var "typ")

ifExprToExpr :: TTermDefinition (R.IfExpr -> Expr)
ifExprToExpr = define "ifExprToExpr" $
  doc "Serialize an if expression" $
  lambda "i" $ lets [
    "cond">: project R._IfExpr R._IfExpr_condition @@ var "i",
    "thenB">: project R._IfExpr R._IfExpr_thenBlock @@ var "i",
    "elseB">: project R._IfExpr R._IfExpr_elseBranch @@ var "i",
    "condExpr">: cases R._IfCondition (var "cond") Nothing [
      R._IfCondition_bool>>: lambda "e" $ expressionToExpr @@ var "e",
      R._IfCondition_let>>: lambda "lc" $ lets [
        "pat">: project R._LetCondition R._LetCondition_pattern @@ var "lc",
        "expr">: project R._LetCondition R._LetCondition_expr @@ var "lc"] $
        Serialization.spaceSep @@ list [
          Serialization.cst @@ string "let",
          patternToExpr @@ var "pat",
          Serialization.cst @@ string "=",
          expressionToExpr @@ var "expr"]],
    "elsePart">: Maybes.maybe nothing (lambda "e" $ just $ Serialization.spaceSep @@ list [
      Serialization.cst @@ string "else",
      expressionToExpr @@ var "e"]) (var "elseB")] $
    Serialization.spaceSep @@ Maybes.cat (list [
      just $ Serialization.cst @@ string "if",
      just $ var "condExpr",
      just $ blockToExpr @@ var "thenB",
      var "elsePart"])

matchExprToExpr :: TTermDefinition (R.MatchExpr -> Expr)
matchExprToExpr = define "matchExprToExpr" $
  doc "Serialize a match expression" $
  lambda "m" $ lets [
    "scrut">: project R._MatchExpr R._MatchExpr_scrutinee @@ var "m",
    "arms">: project R._MatchExpr R._MatchExpr_arms @@ var "m"] $
    Serialization.spaceSep @@ list [
      Serialization.cst @@ string "match",
      expressionToExpr @@ var "scrut",
      Serialization.curlyBracesList @@ nothing @@ Serialization.halfBlockStyle @@
        (Lists.map (matchArmToExpr) (var "arms"))]

matchArmToExpr :: TTermDefinition (R.MatchArm -> Expr)
matchArmToExpr = define "matchArmToExpr" $
  doc "Serialize a match arm" $
  lambda "arm" $ lets [
    "pat">: project R._MatchArm R._MatchArm_pattern @@ var "arm",
    "guard">: project R._MatchArm R._MatchArm_guard @@ var "arm",
    "body">: project R._MatchArm R._MatchArm_body @@ var "arm",
    "guardPart">: Maybes.maybe nothing (lambda "g" $ just $ Serialization.spaceSep @@ list [
      Serialization.cst @@ string "if",
      expressionToExpr @@ var "g"]) (var "guard")] $
    Serialization.spaceSep @@ Maybes.cat (list [
      just $ patternToExpr @@ var "pat",
      var "guardPart",
      just $ Serialization.cst @@ string "=>",
      just $ expressionToExpr @@ var "body",
      just $ Serialization.cst @@ string ","])

loopExprToExpr :: TTermDefinition (R.LoopExpr -> Expr)
loopExprToExpr = define "loopExprToExpr" $
  doc "Serialize a loop expression" $
  lambda "l" $ lets [
    "label">: project R._LoopExpr R._LoopExpr_label @@ var "l",
    "body">: project R._LoopExpr R._LoopExpr_body @@ var "l",
    "labelPart">: Maybes.maybe nothing (lambda "lbl" $ just $ Serialization.cst @@ (Strings.cat2 (string "'") (Strings.cat2 (var "lbl") (string ":")))) (var "label")] $
    Serialization.spaceSep @@ Maybes.cat (list [
      var "labelPart",
      just $ Serialization.cst @@ string "loop",
      just $ blockToExpr @@ var "body"])

whileExprToExpr :: TTermDefinition (R.WhileExpr -> Expr)
whileExprToExpr = define "whileExprToExpr" $
  doc "Serialize a while expression" $
  lambda "w" $ lets [
    "label">: project R._WhileExpr R._WhileExpr_label @@ var "w",
    "cond">: project R._WhileExpr R._WhileExpr_condition @@ var "w",
    "body">: project R._WhileExpr R._WhileExpr_body @@ var "w",
    "labelPart">: Maybes.maybe nothing (lambda "lbl" $ just $ Serialization.cst @@ (Strings.cat2 (string "'") (Strings.cat2 (var "lbl") (string ":")))) (var "label"),
    "condExpr">: cases R._IfCondition (var "cond") Nothing [
      R._IfCondition_bool>>: lambda "e" $ expressionToExpr @@ var "e",
      R._IfCondition_let>>: lambda "lc" $ lets [
        "pat">: project R._LetCondition R._LetCondition_pattern @@ var "lc",
        "expr">: project R._LetCondition R._LetCondition_expr @@ var "lc"] $
        Serialization.spaceSep @@ list [
          Serialization.cst @@ string "let",
          patternToExpr @@ var "pat",
          Serialization.cst @@ string "=",
          expressionToExpr @@ var "expr"]]] $
    Serialization.spaceSep @@ Maybes.cat (list [
      var "labelPart",
      just $ Serialization.cst @@ string "while",
      just $ var "condExpr",
      just $ blockToExpr @@ var "body"])

forExprToExpr :: TTermDefinition (R.ForExpr -> Expr)
forExprToExpr = define "forExprToExpr" $
  doc "Serialize a for expression" $
  lambda "f" $ lets [
    "label">: project R._ForExpr R._ForExpr_label @@ var "f",
    "pat">: project R._ForExpr R._ForExpr_pattern @@ var "f",
    "iter">: project R._ForExpr R._ForExpr_iter @@ var "f",
    "body">: project R._ForExpr R._ForExpr_body @@ var "f",
    "labelPart">: Maybes.maybe nothing (lambda "lbl" $ just $ Serialization.cst @@ (Strings.cat2 (string "'") (Strings.cat2 (var "lbl") (string ":")))) (var "label")] $
    Serialization.spaceSep @@ Maybes.cat (list [
      var "labelPart",
      just $ Serialization.cst @@ string "for",
      just $ patternToExpr @@ var "pat",
      just $ Serialization.cst @@ string "in",
      just $ expressionToExpr @@ var "iter",
      just $ blockToExpr @@ var "body"])

binaryExprToExpr :: TTermDefinition (R.BinaryExpr -> Expr)
binaryExprToExpr = define "binaryExprToExpr" $
  doc "Serialize a binary expression" $
  lambda "b" $ lets [
    "left">: project R._BinaryExpr R._BinaryExpr_left @@ var "b",
    "op">: project R._BinaryExpr R._BinaryExpr_op @@ var "b",
    "right">: project R._BinaryExpr R._BinaryExpr_right @@ var "b"] $
    Serialization.spaceSep @@ list [
      expressionToExpr @@ var "left",
      binaryOpToExpr @@ var "op",
      expressionToExpr @@ var "right"]

binaryOpToExpr :: TTermDefinition (R.BinaryOp -> Expr)
binaryOpToExpr = define "binaryOpToExpr" $
  doc "Serialize a binary operator" $
  lambda "op" $
    Serialization.cst @@ (cases R._BinaryOp (var "op") Nothing [
      R._BinaryOp_add>>: constant $ string "+",
      R._BinaryOp_sub>>: constant $ string "-",
      R._BinaryOp_mul>>: constant $ string "*",
      R._BinaryOp_div>>: constant $ string "/",
      R._BinaryOp_rem>>: constant $ string "%",
      R._BinaryOp_and>>: constant $ string "&&",
      R._BinaryOp_or>>: constant $ string "||",
      R._BinaryOp_bitAnd>>: constant $ string "&",
      R._BinaryOp_bitOr>>: constant $ string "|",
      R._BinaryOp_bitXor>>: constant $ string "^",
      R._BinaryOp_shl>>: constant $ string "<<",
      R._BinaryOp_shr>>: constant $ string ">>",
      R._BinaryOp_eq>>: constant $ string "==",
      R._BinaryOp_ne>>: constant $ string "!=",
      R._BinaryOp_lt>>: constant $ string "<",
      R._BinaryOp_le>>: constant $ string "<=",
      R._BinaryOp_gt>>: constant $ string ">",
      R._BinaryOp_ge>>: constant $ string ">="])

unaryExprToExpr :: TTermDefinition (R.UnaryExpr -> Expr)
unaryExprToExpr = define "unaryExprToExpr" $
  doc "Serialize a unary expression" $
  lambda "u" $ lets [
    "op">: project R._UnaryExpr R._UnaryExpr_op @@ var "u",
    "operand">: project R._UnaryExpr R._UnaryExpr_operand @@ var "u",
    "opStr">: cases R._UnaryOp (var "op") Nothing [
      R._UnaryOp_neg>>: constant $ string "-",
      R._UnaryOp_not>>: constant $ string "!"]] $
    Serialization.cst @@ (Strings.cat2 (var "opStr") (Serialization.printExpr @@ (expressionToExpr @@ var "operand")))

refExprToExpr :: TTermDefinition (R.RefExpr -> Expr)
refExprToExpr = define "refExprToExpr" $
  doc "Serialize a reference expression" $
  lambda "r" $ lets [
    "mut">: project R._RefExpr R._RefExpr_mutable @@ var "r",
    "expr">: project R._RefExpr R._RefExpr_expr @@ var "r",
    "prefix">: Logic.ifElse (var "mut") (string "&mut ") (string "&")] $
    Serialization.cst @@ (Strings.cat2 (var "prefix") (Serialization.printExpr @@ (expressionToExpr @@ var "expr")))

structExprToExpr :: TTermDefinition (R.StructExpr -> Expr)
structExprToExpr = define "structExprToExpr" $
  doc "Serialize a struct literal expression" $
  lambda "s" $ lets [
    "path">: project R._StructExpr R._StructExpr_path @@ var "s",
    "fields">: project R._StructExpr R._StructExpr_fields @@ var "s",
    "rest">: project R._StructExpr R._StructExpr_rest @@ var "s",
    "fieldExprs">: Lists.map (fieldValueToExpr) (var "fields"),
    "restExpr">: Maybes.maybe (list ([] :: [TTerm Expr])) (lambda "r" $ list [
      Serialization.spaceSep @@ list [
        Serialization.cst @@ string "..",
        expressionToExpr @@ var "r"]]) (var "rest"),
    "allFields">: Lists.concat2 (var "fieldExprs") (var "restExpr")] $
    Serialization.spaceSep @@ list [
      exprPathToExpr @@ var "path",
      Serialization.curlyBracesList @@ nothing @@ Serialization.halfBlockStyle @@ var "allFields"]

fieldValueToExpr :: TTermDefinition (R.FieldValue -> Expr)
fieldValueToExpr = define "fieldValueToExpr" $
  doc "Serialize a field-value pair" $
  lambda "fv" $ lets [
    "name">: project R._FieldValue R._FieldValue_name @@ var "fv",
    "val">: project R._FieldValue R._FieldValue_value @@ var "fv"] $
    Maybes.maybe
      (Serialization.cst @@ var "name")
      (lambda "v" $ Serialization.spaceSep @@ list [
        Serialization.cst @@ (Strings.cat2 (var "name") (string ":")),
        expressionToExpr @@ var "v"])
      (var "val")

arrayExprToExpr :: TTermDefinition (R.ArrayExpr -> Expr)
arrayExprToExpr = define "arrayExprToExpr" $
  doc "Serialize an array expression" $
  lambda "a" $
    cases R._ArrayExpr (var "a") Nothing [
      R._ArrayExpr_elements>>: lambda "es" $ Serialization.bracketList @@ Serialization.halfBlockStyle @@ (Lists.map (expressionToExpr) (var "es")),
      R._ArrayExpr_repeat>>: lambda "r" $ lets [
        "elem">: project R._ArrayRepeat R._ArrayRepeat_element @@ var "r",
        "len">: project R._ArrayRepeat R._ArrayRepeat_length @@ var "r"] $
        Serialization.cst @@ (Strings.cat (list [
          string "[",
          Serialization.printExpr @@ (expressionToExpr @@ var "elem"),
          string "; ",
          Serialization.printExpr @@ (expressionToExpr @@ var "len"),
          string "]"]))]

indexExprToExpr :: TTermDefinition (R.IndexExpr -> Expr)
indexExprToExpr = define "indexExprToExpr" $
  doc "Serialize an index expression" $
  lambda "i" $ lets [
    "obj">: project R._IndexExpr R._IndexExpr_object @@ var "i",
    "idx">: project R._IndexExpr R._IndexExpr_index @@ var "i"] $
    Serialization.cst @@ (Strings.cat (list [
      Serialization.printExpr @@ (expressionToExpr @@ var "obj"),
      string "[",
      Serialization.printExpr @@ (expressionToExpr @@ var "idx"),
      string "]"]))

rangeExprToExpr :: TTermDefinition (R.RangeExpr -> Expr)
rangeExprToExpr = define "rangeExprToExpr" $
  doc "Serialize a range expression" $
  lambda "r" $ lets [
    "from">: project R._RangeExpr R._RangeExpr_from @@ var "r",
    "to">: project R._RangeExpr R._RangeExpr_to @@ var "r",
    "incl">: project R._RangeExpr R._RangeExpr_inclusive @@ var "r",
    "fromStr">: Maybes.maybe (string "") (lambda "f" $ Serialization.printExpr @@ (expressionToExpr @@ var "f")) (var "from"),
    "toStr">: Maybes.maybe (string "") (lambda "t" $ Serialization.printExpr @@ (expressionToExpr @@ var "t")) (var "to"),
    "op">: Logic.ifElse (var "incl") (string "..=") (string "..")] $
    Serialization.cst @@ (Strings.cat (list [var "fromStr", var "op", var "toStr"]))

castExprToExpr :: TTermDefinition (R.CastExpr -> Expr)
castExprToExpr = define "castExprToExpr" $
  doc "Serialize a cast expression" $
  lambda "c" $ lets [
    "expr">: project R._CastExpr R._CastExpr_expr @@ var "c",
    "typ">: project R._CastExpr R._CastExpr_type @@ var "c"] $
    Serialization.spaceSep @@ list [
      expressionToExpr @@ var "expr",
      Serialization.cst @@ string "as",
      typeToExpr @@ var "typ"]

typeAscriptionExprToExpr :: TTermDefinition (R.TypeAscriptionExpr -> Expr)
typeAscriptionExprToExpr = define "typeAscriptionExprToExpr" $
  doc "Serialize a type ascription expression" $
  lambda "t" $ lets [
    "expr">: project R._TypeAscriptionExpr R._TypeAscriptionExpr_expr @@ var "t",
    "typ">: project R._TypeAscriptionExpr R._TypeAscriptionExpr_type @@ var "t"] $
    Serialization.spaceSep @@ list [
      expressionToExpr @@ var "expr",
      Serialization.cst @@ string ":",
      typeToExpr @@ var "typ"]

assignExprToExpr :: TTermDefinition (R.AssignExpr -> Expr)
assignExprToExpr = define "assignExprToExpr" $
  doc "Serialize an assignment expression" $
  lambda "a" $ lets [
    "target">: project R._AssignExpr R._AssignExpr_target @@ var "a",
    "val">: project R._AssignExpr R._AssignExpr_value @@ var "a"] $
    Serialization.spaceSep @@ list [
      expressionToExpr @@ var "target",
      Serialization.cst @@ string "=",
      expressionToExpr @@ var "val"]

compoundAssignExprToExpr :: TTermDefinition (R.CompoundAssignExpr -> Expr)
compoundAssignExprToExpr = define "compoundAssignExprToExpr" $
  doc "Serialize a compound assignment expression" $
  lambda "c" $ lets [
    "target">: project R._CompoundAssignExpr R._CompoundAssignExpr_target @@ var "c",
    "op">: project R._CompoundAssignExpr R._CompoundAssignExpr_op @@ var "c",
    "val">: project R._CompoundAssignExpr R._CompoundAssignExpr_value @@ var "c",
    "opStr">: cases R._CompoundAssignOp (var "op") Nothing [
      R._CompoundAssignOp_addAssign>>: constant $ string "+=",
      R._CompoundAssignOp_subAssign>>: constant $ string "-=",
      R._CompoundAssignOp_mulAssign>>: constant $ string "*=",
      R._CompoundAssignOp_divAssign>>: constant $ string "/=",
      R._CompoundAssignOp_remAssign>>: constant $ string "%=",
      R._CompoundAssignOp_bitAndAssign>>: constant $ string "&=",
      R._CompoundAssignOp_bitOrAssign>>: constant $ string "|=",
      R._CompoundAssignOp_bitXorAssign>>: constant $ string "^=",
      R._CompoundAssignOp_shlAssign>>: constant $ string "<<=",
      R._CompoundAssignOp_shrAssign>>: constant $ string ">>="]] $
    Serialization.spaceSep @@ list [
      expressionToExpr @@ var "target",
      Serialization.cst @@ var "opStr",
      expressionToExpr @@ var "val"]

macroInvocationToExpr :: TTermDefinition (R.MacroInvocation -> Expr)
macroInvocationToExpr = define "macroInvocationToExpr" $
  doc "Serialize a macro invocation" $
  lambda "m" $ lets [
    "path">: project R._MacroInvocation R._MacroInvocation_path @@ var "m",
    "delim">: project R._MacroInvocation R._MacroInvocation_delimiter @@ var "m",
    "tokens">: project R._MacroInvocation R._MacroInvocation_tokens @@ var "m",
    "pathStr">: Strings.intercalate (string "::") (var "path"),
    "open">: cases R._MacroDelimiter (var "delim") Nothing [
      R._MacroDelimiter_paren>>: constant $ string "(",
      R._MacroDelimiter_bracket>>: constant $ string "[",
      R._MacroDelimiter_brace>>: constant $ string "{"],
    "close">: cases R._MacroDelimiter (var "delim") Nothing [
      R._MacroDelimiter_paren>>: constant $ string ")",
      R._MacroDelimiter_bracket>>: constant $ string "]",
      R._MacroDelimiter_brace>>: constant $ string "}"]] $
    Serialization.cst @@ (Strings.cat (list [var "pathStr", string "!", var "open", var "tokens", var "close"]))

-- =============================================================================
-- Statements
-- =============================================================================

statementToExpr :: TTermDefinition (R.Statement -> Expr)
statementToExpr = define "statementToExpr" $
  doc "Serialize a statement" $
  lambda "stmt" $
    cases R._Statement (var "stmt") Nothing [
      R._Statement_let>>: lambda "l" $ letStatementToExpr @@ var "l",
      R._Statement_expression>>: lambda "e" $ Serialization.spaceSep @@ list [expressionToExpr @@ var "e", Serialization.cst @@ string ";"],
      R._Statement_item>>: lambda "i" $ itemToExpr @@ var "i",
      R._Statement_empty>>: constant $ Serialization.cst @@ string ";"]

letStatementToExpr :: TTermDefinition (R.LetStatement -> Expr)
letStatementToExpr = define "letStatementToExpr" $
  doc "Serialize a let statement" $
  lambda "l" $ lets [
    "pat">: project R._LetStatement R._LetStatement_pattern @@ var "l",
    "mut">: project R._LetStatement R._LetStatement_mutable @@ var "l",
    "typ">: project R._LetStatement R._LetStatement_type @@ var "l",
    "init">: project R._LetStatement R._LetStatement_init @@ var "l",
    "mutKw">: Logic.ifElse (var "mut") (just $ Serialization.cst @@ string "mut") nothing,
    "typPart">: Maybes.maybe nothing (lambda "t" $ just $ Serialization.spaceSep @@ list [
      Serialization.cst @@ string ":",
      typeToExpr @@ var "t"]) (var "typ"),
    "initPart">: Maybes.maybe nothing (lambda "e" $ just $ Serialization.spaceSep @@ list [
      Serialization.cst @@ string "=",
      expressionToExpr @@ var "e"]) (var "init")] $
    Serialization.spaceSep @@ Maybes.cat (list [
      just $ Serialization.cst @@ string "let",
      var "mutKw",
      just $ patternToExpr @@ var "pat",
      var "typPart",
      var "initPart",
      just $ Serialization.cst @@ string ";"])

blockToExpr :: TTermDefinition (R.Block -> Expr)
blockToExpr = define "blockToExpr" $
  doc "Serialize a block" $
  lambda "b" $ lets [
    "stmts">: project R._Block R._Block_statements @@ var "b",
    "expr">: project R._Block R._Block_expression @@ var "b",
    "stmtExprs">: Lists.map (statementToExpr) (var "stmts"),
    "exprPart">: Maybes.maybe (list ([] :: [TTerm Expr])) (lambda "e" $ list [expressionToExpr @@ var "e"]) (var "expr"),
    "allParts">: Lists.concat2 (var "stmtExprs") (var "exprPart")] $
    Serialization.curlyBracesList @@ nothing @@ Serialization.halfBlockStyle @@ var "allParts"

-- =============================================================================
-- Patterns
-- =============================================================================

patternToExpr :: TTermDefinition (R.Pattern -> Expr)
patternToExpr = define "patternToExpr" $
  doc "Serialize a pattern" $
  lambda "pat" $
    cases R._Pattern (var "pat") Nothing [
      R._Pattern_wildcard>>: constant $ Serialization.cst @@ string "_",
      R._Pattern_identifier>>: lambda "ip" $ identifierPatternToExpr @@ var "ip",
      R._Pattern_literal>>: lambda "l" $ literalToExpr @@ var "l",
      R._Pattern_reference>>: lambda "rp" $ refPatternToExpr @@ var "rp",
      R._Pattern_struct>>: lambda "sp" $ structPatternToExpr @@ var "sp",
      R._Pattern_tupleStruct>>: lambda "tsp" $ tupleStructPatternToExpr @@ var "tsp",
      R._Pattern_tuple>>: lambda "ps" $ Serialization.parenList @@ false @@ (Lists.map (patternToExpr) (var "ps")),
      R._Pattern_slice>>: lambda "ps" $ Serialization.bracketList @@ Serialization.halfBlockStyle @@ (Lists.map (patternToExpr) (var "ps")),
      R._Pattern_or>>: lambda "ps" $ Serialization.cst @@ (Strings.intercalate (string " | ") (Lists.map (lambda "p" $ Serialization.printExpr @@ (patternToExpr @@ var "p")) (var "ps"))),
      R._Pattern_path>>: lambda "ep" $ exprPathToExpr @@ var "ep",
      R._Pattern_range>>: lambda "rp" $ rangePatternToExpr @@ var "rp",
      R._Pattern_rest>>: constant $ Serialization.cst @@ string "..",
      R._Pattern_paren>>: lambda "p" $ Serialization.parenthesize @@ (patternToExpr @@ var "p")]

identifierPatternToExpr :: TTermDefinition (R.IdentifierPattern -> Expr)
identifierPatternToExpr = define "identifierPatternToExpr" $
  doc "Serialize an identifier pattern" $
  lambda "ip" $ lets [
    "name">: project R._IdentifierPattern R._IdentifierPattern_name @@ var "ip",
    "mut">: project R._IdentifierPattern R._IdentifierPattern_mutable @@ var "ip",
    "atPat">: project R._IdentifierPattern R._IdentifierPattern_atPattern @@ var "ip",
    "mutKw">: Logic.ifElse (var "mut") (just $ Serialization.cst @@ string "mut") nothing,
    "atPart">: Maybes.maybe nothing (lambda "p" $ just $ Serialization.spaceSep @@ list [
      Serialization.cst @@ string "@",
      patternToExpr @@ var "p"]) (var "atPat")] $
    Serialization.spaceSep @@ Maybes.cat (list [
      var "mutKw",
      just $ Serialization.cst @@ var "name",
      var "atPart"])

refPatternToExpr :: TTermDefinition (R.RefPattern -> Expr)
refPatternToExpr = define "refPatternToExpr" $
  doc "Serialize a reference pattern" $
  lambda "rp" $ lets [
    "mut">: project R._RefPattern R._RefPattern_mutable @@ var "rp",
    "pat">: project R._RefPattern R._RefPattern_pattern @@ var "rp",
    "prefix">: Logic.ifElse (var "mut") (string "&mut ") (string "&")] $
    Serialization.cst @@ (Strings.cat2 (var "prefix") (Serialization.printExpr @@ (patternToExpr @@ var "pat")))

structPatternToExpr :: TTermDefinition (R.StructPattern -> Expr)
structPatternToExpr = define "structPatternToExpr" $
  doc "Serialize a struct pattern" $
  lambda "sp" $ lets [
    "path">: project R._StructPattern R._StructPattern_path @@ var "sp",
    "fields">: project R._StructPattern R._StructPattern_fields @@ var "sp",
    "rest">: project R._StructPattern R._StructPattern_rest @@ var "sp",
    "fieldExprs">: Lists.map (fieldPatternToExpr) (var "fields"),
    "restExpr">: Logic.ifElse (var "rest") (list [Serialization.cst @@ string ".."]) (list ([] :: [TTerm Expr])),
    "allFields">: Lists.concat2 (var "fieldExprs") (var "restExpr")] $
    Serialization.spaceSep @@ list [
      exprPathToExpr @@ var "path",
      Serialization.curlyBracesList @@ nothing @@ Serialization.halfBlockStyle @@ var "allFields"]

fieldPatternToExpr :: TTermDefinition (R.FieldPattern -> Expr)
fieldPatternToExpr = define "fieldPatternToExpr" $
  doc "Serialize a field pattern" $
  lambda "fp" $ lets [
    "name">: project R._FieldPattern R._FieldPattern_name @@ var "fp",
    "pat">: project R._FieldPattern R._FieldPattern_pattern @@ var "fp"] $
    Maybes.maybe
      (Serialization.cst @@ var "name")
      (lambda "p" $ Serialization.spaceSep @@ list [
        Serialization.cst @@ (Strings.cat2 (var "name") (string ":")),
        patternToExpr @@ var "p"])
      (var "pat")

tupleStructPatternToExpr :: TTermDefinition (R.TupleStructPattern -> Expr)
tupleStructPatternToExpr = define "tupleStructPatternToExpr" $
  doc "Serialize a tuple struct pattern" $
  lambda "tsp" $ lets [
    "path">: project R._TupleStructPattern R._TupleStructPattern_path @@ var "tsp",
    "elems">: project R._TupleStructPattern R._TupleStructPattern_elements @@ var "tsp"] $
    Serialization.spaceSep @@ list [
      exprPathToExpr @@ var "path",
      Serialization.parenList @@ false @@ (Lists.map (patternToExpr) (var "elems"))]

rangePatternToExpr :: TTermDefinition (R.RangePattern -> Expr)
rangePatternToExpr = define "rangePatternToExpr" $
  doc "Serialize a range pattern" $
  lambda "rp" $ lets [
    "from">: project R._RangePattern R._RangePattern_from @@ var "rp",
    "to">: project R._RangePattern R._RangePattern_to @@ var "rp",
    "incl">: project R._RangePattern R._RangePattern_inclusive @@ var "rp",
    "fromStr">: Maybes.maybe (string "") (lambda "p" $ Serialization.printExpr @@ (patternToExpr @@ var "p")) (var "from"),
    "toStr">: Maybes.maybe (string "") (lambda "p" $ Serialization.printExpr @@ (patternToExpr @@ var "p")) (var "to"),
    "op">: Logic.ifElse (var "incl") (string "..=") (string "..")] $
    Serialization.cst @@ (Strings.cat (list [var "fromStr", var "op", var "toStr"]))

-- =============================================================================
-- Literals
-- =============================================================================

literalToExpr :: TTermDefinition (R.Literal -> Expr)
literalToExpr = define "literalToExpr" $
  doc "Serialize a literal" $
  lambda "lit" $
    cases R._Literal (var "lit") Nothing [
      R._Literal_integer>>: lambda "il" $ integerLiteralToExpr @@ var "il",
      R._Literal_float>>: lambda "fl" $ floatLiteralToExpr @@ var "fl",
      R._Literal_string>>: lambda "s" $ Serialization.cst @@ (Literals.showString $ var "s"),
      R._Literal_rawString>>: lambda "s" $ Serialization.cst @@ (Strings.cat (list [string "r\"", var "s", string "\""])),
      R._Literal_byteString>>: lambda "bs" $ Serialization.cst @@ string "b\"...\"", -- TODO: Proper binary encoding
      R._Literal_char>>: lambda "c" $ Serialization.cst @@ (Strings.cat (list [string "'", Literals.showUint32 $ var "c", string "'"])),
      R._Literal_byte>>: lambda "b" $ Serialization.cst @@ (Strings.cat (list [string "b'", Literals.showUint8 $ var "b", string "'"])),
      R._Literal_bool>>: lambda "b" $ Serialization.cst @@ (Logic.ifElse (var "b") (string "true") (string "false"))]

integerLiteralToExpr :: TTermDefinition (R.IntegerLiteral -> Expr)
integerLiteralToExpr = define "integerLiteralToExpr" $
  doc "Serialize an integer literal" $
  lambda "il" $ lets [
    "val">: project R._IntegerLiteral R._IntegerLiteral_value @@ var "il",
    "suf">: project R._IntegerLiteral R._IntegerLiteral_suffix @@ var "il",
    "valStr">: Literals.showBigint $ var "val",
    "sufStr">: Maybes.maybe (string "") (lambda "s" $ var "s") (var "suf")] $
    Serialization.cst @@ (Strings.cat2 (var "valStr") (var "sufStr"))

floatLiteralToExpr :: TTermDefinition (R.FloatLiteral -> Expr)
floatLiteralToExpr = define "floatLiteralToExpr" $
  doc "Serialize a float literal" $
  lambda "fl" $ lets [
    "val">: project R._FloatLiteral R._FloatLiteral_value @@ var "fl",
    "suf">: project R._FloatLiteral R._FloatLiteral_suffix @@ var "fl",
    "valStr">: Literals.showFloat64 $ var "val",
    "sufStr">: Maybes.maybe (string "") (lambda "s" $ var "s") (var "suf")] $
    Serialization.cst @@ (Strings.cat2 (var "valStr") (var "sufStr"))

-- =============================================================================
-- Visibility and attributes
-- =============================================================================

visibilityToExpr :: TTermDefinition (R.Visibility -> Maybe Expr)
visibilityToExpr = define "visibilityToExpr" $
  doc "Serialize visibility to an optional expression" $
  lambda "vis" $
    cases R._Visibility (var "vis") Nothing [
      R._Visibility_public>>: constant $ just $ Serialization.cst @@ string "pub",
      R._Visibility_crate>>: constant $ just $ Serialization.cst @@ string "pub(crate)",
      R._Visibility_restricted>>: lambda "path" $
        just $ Serialization.cst @@ (Strings.cat (list [
          string "pub(in ",
          Strings.intercalate (string "::") (var "path"),
          string ")"])),
      R._Visibility_private>>: constant nothing]

attributeToExpr :: TTermDefinition (R.Attribute -> Expr)
attributeToExpr = define "attributeToExpr" $
  doc "Serialize an attribute" $
  lambda "attr" $ lets [
    "inner">: project R._Attribute R._Attribute_inner @@ var "attr",
    "path">: project R._Attribute R._Attribute_path @@ var "attr",
    "tokens">: project R._Attribute R._Attribute_tokens @@ var "attr",
    "prefix">: Logic.ifElse (var "inner") (string "#![") (string "#["),
    "pathStr">: Strings.intercalate (string "::") (var "path"),
    "tokensPart">: Maybes.maybe (string "") (lambda "t" $ Strings.cat (list [string "(", var "t", string ")"])) (var "tokens")] $
    Serialization.cst @@ (Strings.cat (list [var "prefix", var "pathStr", var "tokensPart", string "]"]))

derivesToExpr :: TTermDefinition ([String] -> Maybe Expr)
derivesToExpr = define "derivesToExpr" $
  doc "Serialize derive macros to an attribute expression" $
  lambda "derives" $
    Logic.ifElse (Lists.null $ var "derives")
      nothing
      (just $ Serialization.cst @@ (Strings.cat (list [
        string "#[derive(",
        Strings.intercalate (string ", ") (var "derives"),
        string ")]"])))

-- =============================================================================
-- Comments
-- =============================================================================

toRustDocComment :: TTermDefinition (String -> String)
toRustDocComment = define "toRustDocComment" $
  doc ("Convert a string to Rust doc comments. Empty source lines emit `///`"
    <> " (no trailing space) so blank doc lines don't carry trailing whitespace.") $
  lambda "c" $ Strings.intercalate (string "\n") $ Lists.map
    (lambda "s" $ Logic.ifElse (Equality.equal (var "s") (string ""))
      (string "///")
      (Strings.cat2 (string "/// ") (var "s")))
    (Strings.lines $ var "c")

toRustComment :: TTermDefinition (String -> String)
toRustComment = define "toRustComment" $
  doc ("Convert a string to Rust line comments. Empty source lines emit `//`"
    <> " (no trailing space).") $
  lambda "c" $ Strings.intercalate (string "\n") $ Lists.map
    (lambda "s" $ Logic.ifElse (Equality.equal (var "s") (string ""))
      (string "//")
      (Strings.cat2 (string "// ") (var "s")))
    (Strings.lines $ var "c")