content: module Oden.Go.PrettySpec where
  
  import           Oden.Go.Identifier
  import           Oden.Go.AST
  import           Oden.Go.Type
  import           Oden.Go.Pretty ()
  
  import           Text.PrettyPrint.Leijen
  
  import           Test.Hspec
  
  shouldPrintAs x s = displayS (renderPretty 0.4 100 (pretty x)) "" `shouldBe` s
  
  identifierX = Expression (Operand (OperandName (Identifier "x")))
  
  spec :: Spec
  spec =
    describe "pretty" $ do
  
      describe "Type" $
  
        describe "Interface" $ do
  
          it "prints an empty interface" $
            Interface []
            `shouldPrintAs`
            "interface{}"
  
          it "prints a single-method interface" $
            Interface [Method (Identifier "foo") (Parameters [] False) (Returns [])]
            `shouldPrintAs`
            "interface {\n    foo()\n}"
  
      describe "PrimaryExpression" $ do
  
        it "prints a function application" $
          Application
          (Operand (OperandName (Identifier "f")))
          [Argument identifierX]
          `shouldPrintAs`
          "f(x)"
  
        it "prints a variadic function application" $
          Application
          (Operand (OperandName (Identifier "f")))
          [VariadicSliceArgument identifierX]
          `shouldPrintAs`
          "f(x...)"
  
      describe "Literal" $ do
  
        it "prints a function literal with no parameters and no return value" $
          FunctionLiteral
          (FunctionSignature
           []
           [])
          (Block [])
          `shouldPrintAs`
          "func () {}"
  
        it "prints a function literal with no parameters" $
          FunctionLiteral
          (FunctionSignature
           []
           [Basic (Identifier "int") False])
          (Block [ReturnStmt [identifierX]])
          `shouldPrintAs`
          "func () int {\n    return x\n}"
  
        it "prints a function literal with no return value" $
          FunctionLiteral
          (FunctionSignature
           [FunctionParameter (Identifier "_") (Basic (Identifier "int") False)]
           [])
          (Block [])
          `shouldPrintAs`
          "func (_ int) {}"
  
        it "prints comments inside a a function literal" $
          FunctionLiteral
          (FunctionSignature
           []
           [Basic (Identifier "int") False])
          (Block [StmtComment (Comment "hello"), ReturnStmt [identifierX]])
          `shouldPrintAs`
          "func () int {\n    // hello\n    return x\n}"
  
        it "prints a function literal" $
          FunctionLiteral
          (FunctionSignature
           [FunctionParameter (Identifier "x") (Basic (Identifier "int") False)]
           [Basic (Identifier "int") False])
          (Block [ReturnStmt [identifierX]])
          `shouldPrintAs`
          "func (x int) int {\n    return x\n}"
  
        it "prints a function literal with a variadic parameter" $
          FunctionLiteral
          (FunctionSignature
           [VariadicFunctionParameter (Identifier "_") (Basic (Identifier "int") False)]
           [])
          (Block [])
          `shouldPrintAs`
          "func (_ ...int) {}"
  
        it "prints an empty struct literal" $
          CompositeLiteral
          (Struct [])
          (LiteralValueElements [])
          `shouldPrintAs`
          "struct{}{}"
  
        it "prints a struct literal with a field" $
          CompositeLiteral
          (Struct [StructField (Identifier "foo") (Basic (Identifier "int") False)])
          (LiteralValueElements [KeyedElement
                                 (LiteralKeyName (Identifier "foo"))
                                 (Expression
                                  (Operand
                                   (Literal
                                    (BasicLiteral
                                     (IntLiteral 3)))))])
          `shouldPrintAs`
          "struct {\n    foo int\n}{\n    foo: 3,\n}"
  
        it "prints a struct literal with a field and a comment" $
          CompositeLiteral
          (Struct [StructField (Identifier "foo") (Basic (Identifier "int") False)])
          (LiteralValueElements [ KeyedElement
                                  (LiteralKeyName (Identifier "foo"))
                                  (Expression
                                   (Operand
                                    (Literal
                                     (BasicLiteral
                                      (IntLiteral 3)))))
                                , LiteralComment (Comment "Hello")
                                ])
          `shouldPrintAs`
          "struct {\n    foo int\n}{\n    foo: 3,\n    // Hello\n}"
  
      describe "Declaration" $ do
  
        it "prints a type declaration" $
          TypeDecl (Identifier "Count") (Basic (Identifier "int") False)
          `shouldPrintAs`
          "type Count int"
  
        it "prints a struct type declaration" $
          TypeDecl
          (Identifier "Count")
          (Struct
           [StructField (Identifier "foo") (Basic (Identifier "int") False)])
          `shouldPrintAs`
          "type Count struct {\n    foo int\n}"
  
        it "prints an interface type declaration" $
          TypeDecl
          (Identifier "Count")
          (Interface
           [Method (Identifier "foo") (Parameters [] False) (Returns [])])
          `shouldPrintAs`
          "type Count interface {\n    foo()\n}"
  
        it "prints an const declaration" $
          ConstDecl
          (Identifier "PI")
          (Basic (Identifier "float") False)
          (Expression
           (Operand
           (Literal
             (BasicLiteral
             (FloatLiteral 3.14)))))
          `shouldPrintAs`
          "const PI float = 3.14"
  
        it "prints a var declaration" $
          VarDecl
          (VarDeclInitializer
           (Identifier "count")
           (Basic (Identifier "int") False)
           (Expression
            (Operand
            (Literal
              (BasicLiteral
              (IntLiteral 0))))))
          `shouldPrintAs`
          "var count int = 0"
  
        it "prints a function declaration" $
          FunctionDecl
          (Identifier "identity")
          (FunctionSignature
           [FunctionParameter (Identifier "x") (Basic (Identifier "int") False)]
           [Basic (Identifier "int") False])
          (Block [ReturnStmt [identifierX]])
          `shouldPrintAs`
          "func identity(x int) int {\n    return x\n}"
  
      describe "Block" $ do
        it "prints an empty block" $
          Block []
          `shouldPrintAs`
          "{}"
  
        it "indents a single stmt in a block" $
          Block [ReturnStmt []]
          `shouldPrintAs`
          "{\n    return\n}"
  
        it "indents multiple stmts in a block" $
          Block [ReturnStmt [], ReturnStmt []]
          `shouldPrintAs`
          "{\n    return\n    return\n}"
  
        it "indents nested blocks" $
          Block [BlockStmt (Block [ReturnStmt []])]
          `shouldPrintAs`
          "{\n    {\n        return\n    }\n}"
  
      describe "Stmt" $ do
  
        it "prints a return stmt without expressions" $
          ReturnStmt []
          `shouldPrintAs`
          "return"
  
        it "prints a return stmt with one expression" $
          ReturnStmt [identifierX]
          `shouldPrintAs`
          "return x"
  
        it "prints a return stmt with multiple expressions" $
          ReturnStmt [identifierX, identifierX]
          `shouldPrintAs`
          "return x, x"
  
      describe "SourceFile" $ do
        it "prints a source file with only a package clause" $
          SourceFile (PackageClause (Identifier "foo")) [] []
          `shouldPrintAs`
          "package foo\n"
  
        it "prints a source file with an import" $
          SourceFile
          (PackageClause (Identifier "foo"))
          [ImportDecl (Identifier "bar") (InterpretedStringLiteral "the/bar")]
          []
          `shouldPrintAs`
          "package foo\n\nimport bar \"the/bar\"\n"
  
        it "prints a source file with a function decl with no stmts" $
          SourceFile
          (PackageClause (Identifier "main"))
          []
          [FunctionDecl
           (Identifier "main")
           (FunctionSignature [] [])
           (Block [])]
          `shouldPrintAs`
          "package main\n\nfunc main() {}\n"
  
        it "prints a source file with a function decl with a return stmt" $
          SourceFile
          (PackageClause (Identifier "main"))
          []
          [FunctionDecl
           (Identifier "main")
           (FunctionSignature [] [])
           (Block [ReturnStmt []])]
          `shouldPrintAs`
          "package main\n\nfunc main() {\n    return\n}\n"
  
        it "prints a source file with an import and a function decl" $
          SourceFile
          (PackageClause (Identifier "main"))
          [ImportDecl (Identifier "bar") (InterpretedStringLiteral "the/bar")]
          [FunctionDecl
           (Identifier "main")
           (FunctionSignature [] [])
           (Block [])]
          `shouldPrintAs`
          "package main\n\nimport bar \"the/bar\"\n\nfunc main() {}\n"
  
        it "prints a source file a comment and a function decl" $
          SourceFile
          (PackageClause (Identifier "main"))
          []
          [ TopLevelComment (Comment "yey")
          , FunctionDecl
            (Identifier "main")
            (FunctionSignature [] [])
            (Block [])]
          `shouldPrintAs`
          "package main\n\n// yey\nfunc main() {}\n"
  