1// Copyright 2015 Jean Niklas L'orange.  All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package edn
6
7import (
8	"bytes"
9	"testing"
10)
11
12func TestEncoding(t *testing.T) {
13	testEncode(t, Keyword("foo"), ":foo")
14	testEncode(t, Symbol("foo"), "foo")
15	testEncode(t, "foo", `"foo"`)
16
17	testEncode(t, 1, "1")
18	testEncode(t, -1.2, "-1.2")
19	testEncode(t, 3.0, "3.0")
20	testEncode(t, 10E12, "1e+13")
21
22	var val interface{}
23
24	val = struct {
25		Foo int `edn:"quux"`
26	}{10}
27	testEncode(t, val, "{:quux 10}")
28
29	val = struct {
30		Bar [2]int `edn:",sym,list"`
31	}{[2]int{3, 4}}
32	testEncode(t, val, "{bar(3 4)}")
33
34	val = struct {
35		Comp string `edn:",str"`
36	}{"ressed"}
37	testEncode(t, val, `{"comp""ressed"}`)
38
39	val = struct {
40		TheSet [2]int   `edn:"the-set,set,sym"`
41		Slice  []string `edn:",set"`
42	}{
43		[2]int{3, 4},
44		[]string{"foo", "bar"},
45	}
46	testEncode(t, val, `{the-set #{3 4}:slice #{"foo""bar"}}`)
47
48	val = map[float64]struct{}{
49		1.0: {},
50	}
51	testEncode(t, val, `#{1.0}`)
52
53	val = Tag{
54		Tagname: "floatset",
55		Value: map[float64]struct{}{
56			1.0: {},
57		},
58	}
59	testEncode(t, val, `#floatset #{1.0}`)
60
61	val = Tag{
62		Tagname: "some/tag",
63		Value:   1,
64	}
65	testEncode(t, val, `#some/tag 1`)
66
67	val = Tag{
68		Tagname: "some/tag",
69		Value:   struct{ X int }{1},
70	}
71	testEncode(t, val, `#some/tag{:x 1}`)
72
73	val = Tag{
74		Tagname: "a",
75		Value: Tag{
76			Tagname: "b",
77			Value: Tag{
78				Tagname: "c",
79				Value:   nil,
80			},
81		},
82	}
83	testEncode(t, val, `#a #b #c nil`)
84
85	val = Tag{
86		Tagname: "a",
87		Value: Tag{
88			Tagname: "b",
89			Value:   1,
90		},
91	}
92	testEncode(t, val, `#a #b 1`)
93
94	val = Tag{
95		Tagname: "a",
96		Value: Tag{
97			Tagname: "b",
98			Value:   "c",
99		},
100	}
101	testEncode(t, val, `#a #b"c"`)
102
103	val = Tag{
104		Tagname: "a",
105		Value:   []int{1},
106	}
107	testEncode(t, val, `#a[1]`)
108
109	val = Tag{
110		Tagname: "a",
111		Value: Tag{
112			Tagname: "b",
113			Value:   []int{1},
114		},
115	}
116	testEncode(t, val, `#a #b[1]`)
117
118	val = Tag{
119		Tagname: "some/tag",
120		Value: Tag{
121			Tagname: "inner",
122			Value:   struct{ X int }{1},
123		},
124	}
125	testEncode(t, val, `#some/tag #inner{:x 1}`)
126
127	val = A{}
128	testEncode(t, val, `#tag/a{:x 1}`)
129
130	val = Tag{
131		Tagname: "outer",
132		Value:   A{},
133	}
134	testEncode(t, val, `#outer #tag/a{:x 1}`)
135}
136
137func testEncode(t *testing.T, val interface{}, expects string) {
138	bs, err := Marshal(val)
139	if err != nil {
140		t.Errorf("Unexpected error marshalling %q: %s", val, err.Error())
141	} else if !bytes.Equal([]byte(expects), bs) {
142		t.Errorf("Expected to see '%s', but got '%s' instead", expects, string(bs))
143	}
144}
145
146type A struct{}
147
148func (a A) MarshalEDN() ([]byte, error) {
149	t := Tag{
150		Tagname: "tag/a",
151		Value:   struct{ X int }{1},
152	}
153	return Marshal(t)
154}
155
156func TestJSONEncoding(t *testing.T) {
157	jsonOnly := struct {
158		Data string `json:"json"`
159	}{Data: "hi"}
160	jsonAndEdn := struct {
161		Data string `json:"json" edn:"edn"`
162	}{Data: "hi"}
163
164	UseJSONAsFallback(false)
165	testEncode(t, jsonOnly, `{:data"hi"}`)
166	testEncode(t, jsonAndEdn, `{:edn"hi"}`)
167
168	UseJSONAsFallback(true)
169	testEncode(t, jsonOnly, `{:json"hi"}`)
170	testEncode(t, jsonAndEdn, `{:edn"hi"}`)
171
172	UseJSONAsFallback(false)
173	testEncode(t, jsonOnly, `{:data"hi"}`)
174	testEncode(t, jsonAndEdn, `{:edn"hi"}`)
175}
176