module Fantomas.Core.Tests.StructTests

open NUnit.Framework
open FsUnit
open Fantomas.Core.Tests.TestHelpers

[<Test>]
let ``struct type`` () =
    formatSourceString
        """
type NameStruct =
    struct
        val Name : string
        new (name) = { Name = name }

        member x.Upper() =
            x.Name.ToUpper()

        member x.Lower() =
            x.Name.ToLower()
    end

let n = new NameStruct("Hippo")"""
        { config with
            MaxValueBindingWidth = 120 }
    |> prepend newline
    |> should
        equal
        """
type NameStruct =
    struct
        val Name: string
        new(name) = { Name = name }

        member x.Upper() = x.Name.ToUpper()

        member x.Lower() = x.Name.ToLower()
    end

let n = new NameStruct("Hippo")
"""

[<Test>]
let ``struct type retains members outside struct-end`` () =
    formatSourceString
        """
type NameStruct =
    struct
        val Name : string
        new (name) = { Name = name }
    end

    member x.Upper() =
        x.Name.ToUpper()

    member x.Lower() =
        x.Name.ToLower()

let n = new NameStruct("Hippo")"""
        config
    |> prepend newline
    |> should
        equal
        """
type NameStruct =
    struct
        val Name: string
        new(name) = { Name = name }
    end

    member x.Upper() = x.Name.ToUpper()

    member x.Lower() = x.Name.ToLower()

let n = new NameStruct("Hippo")
"""

[<Test>]
let ``struct tuple`` () =
    formatSourceString
        """
type S = S of struct (int * int)
let g : struct (int*int) = struct (1,1)
let z = fun (S (struct (u, v)): S) -> u + v
let t = struct (1,2)
match t with
| struct (x,y) -> ()"""
        config
    |> prepend newline
    |> should
        equal
        """
type S = S of struct (int * int)
let g: struct (int * int) = struct (1, 1)
let z = fun (S(struct (u, v)): S) -> u + v
let t = struct (1, 2)

match t with
| struct (x, y) -> ()
"""

[<Test>]
let ``struct tuple type abbreviation, 605`` () =
    formatSourceString "type TupleStruct = (struct (string * string))" config
    |> prepend newline
    |> should
        equal
        """
type TupleStruct = (struct (string * string))
"""

[<Test>]
let ``struct tuple type abbreviation, sigfile`` () =
    formatSignatureString
        """namespace meh

type TupleStruct = (struct (string * string))"""
        config
    |> prepend newline
    |> should
        equal
        """
namespace meh

type TupleStruct = (struct (string * string))
"""

[<Test>]
let ``struct empty type, 2592`` () =
    formatSourceString
        """
type NameStruct = struct end
"""
        config
    |> prepend newline
    |> should
        equal
        """
type NameStruct = struct end
"""

[<Test>]
let ``struct empty type with ctor`` () =
    formatSourceString
        """
type NameStruct() =
    struct
    end
"""
        config
    |> prepend newline
    |> should
        equal
        """
type NameStruct() = struct end
"""

[<Test>]
let ``anonymous struct record with trivia`` () =
    formatSourceString
        """
struct // 1
    {| // 2
        // 3
        X = 4
    // 5       
    |} // 6 
"""
        config
    |> prepend newline
    |> should
        equal
        """
struct // 1
    {| // 2
       // 3
       X = 4
    // 5
    |} // 6
"""

[<Test>]
let ``second binding does not contain accessibility, 2902`` () =
    formatSourceString
        """
module Telplin

type T =
    struct
        member private this.X with get () : int = 1 and set (_:int) = ()
    end
"""
        config
    |> prepend newline
    |> should
        equal
        """
module Telplin

type T =
    struct
        member private this.X
            with get (): int = 1
            and set (_: int) = ()
    end
"""

[<Test>]
let ``different accessibility on setter`` () =
    formatSourceString
        """
module Telplin

type T =
    struct
        member this.X with get () : int = 1 and private set (_:int) = ()
        member this.Y with internal get () : int = 1 and private set (_:int) = ()
        member private this.Z with get () : int = 1 and set (_:int) = ()
        member this.S with internal set (_:int) = () and private get () : int = 1
    end
"""
        config
    |> prepend newline
    |> should
        equal
        """
module Telplin

type T =
    struct
        member this.X
            with get (): int = 1
            and private set (_: int) = ()

        member this.Y
            with internal get (): int = 1
            and private set (_: int) = ()

        member private this.Z
            with get (): int = 1
            and set (_: int) = ()

        member this.S
            with internal set (_: int) = ()
            and private get (): int = 1
    end
"""

[<Test>]
let ``private setter on next line`` () =
    formatSourceString
        """
type Y =
    member this.X
        with get (): int = 1
        and private set (_: int) = ()
"""
        config
    |> prepend newline
    |> should
        equal
        """
type Y =
    member this.X
        with get (): int = 1
        and private set (_: int) = ()
"""

[<Test>]
let ``private property with identifier on next line`` () =
    formatSourceString
        """
type Y =
    member                      private
                    this.X
                            with get (): int = 1
                            and  set (_: int) = ()
"""
        config
    |> prepend newline
    |> should
        equal
        """
type Y =
    member private this.X
        with get (): int = 1
        and set (_: int) = ()
"""
