1package render
2
3import (
4	"bytes"
5	"encoding/json"
6	"encoding/xml"
7	"html/template"
8	"io"
9	"net/http"
10)
11
12// Engine is the generic interface for all responses.
13type Engine interface {
14	Render(io.Writer, interface{}) error
15}
16
17// Head defines the basic ContentType and Status fields.
18type Head struct {
19	ContentType string
20	Status      int
21}
22
23// Data built-in renderer.
24type Data struct {
25	Head
26}
27
28// HTML built-in renderer.
29type HTML struct {
30	Head
31	Name      string
32	Templates *template.Template
33}
34
35// JSON built-in renderer.
36type JSON struct {
37	Head
38	Indent        bool
39	UnEscapeHTML  bool
40	Prefix        []byte
41	StreamingJSON bool
42}
43
44// JSONP built-in renderer.
45type JSONP struct {
46	Head
47	Indent   bool
48	Callback string
49}
50
51// Text built-in renderer.
52type Text struct {
53	Head
54}
55
56// XML built-in renderer.
57type XML struct {
58	Head
59	Indent bool
60	Prefix []byte
61}
62
63// Write outputs the header content.
64func (h Head) Write(w http.ResponseWriter) {
65	w.Header().Set(ContentType, h.ContentType)
66	w.WriteHeader(h.Status)
67}
68
69// Render a data response.
70func (d Data) Render(w io.Writer, v interface{}) error {
71	if hw, ok := w.(http.ResponseWriter); ok {
72		c := hw.Header().Get(ContentType)
73		if c != "" {
74			d.Head.ContentType = c
75		}
76		d.Head.Write(hw)
77	}
78
79	w.Write(v.([]byte))
80	return nil
81}
82
83// Render a HTML response.
84func (h HTML) Render(w io.Writer, binding interface{}) error {
85	// Retrieve a buffer from the pool to write to.
86	out := bufPool.Get()
87	err := h.Templates.ExecuteTemplate(out, h.Name, binding)
88	if err != nil {
89		return err
90	}
91
92	if hw, ok := w.(http.ResponseWriter); ok {
93		h.Head.Write(hw)
94	}
95	out.WriteTo(w)
96
97	// Return the buffer to the pool.
98	bufPool.Put(out)
99	return nil
100}
101
102// Render a JSON response.
103func (j JSON) Render(w io.Writer, v interface{}) error {
104	if j.StreamingJSON {
105		return j.renderStreamingJSON(w, v)
106	}
107
108	var result []byte
109	var err error
110
111	if j.Indent {
112		result, err = json.MarshalIndent(v, "", "  ")
113		result = append(result, '\n')
114	} else {
115		result, err = json.Marshal(v)
116	}
117	if err != nil {
118		return err
119	}
120
121	// Unescape HTML if needed.
122	if j.UnEscapeHTML {
123		result = bytes.Replace(result, []byte("\\u003c"), []byte("<"), -1)
124		result = bytes.Replace(result, []byte("\\u003e"), []byte(">"), -1)
125		result = bytes.Replace(result, []byte("\\u0026"), []byte("&"), -1)
126	}
127
128	// JSON marshaled fine, write out the result.
129	if hw, ok := w.(http.ResponseWriter); ok {
130		j.Head.Write(hw)
131	}
132	if len(j.Prefix) > 0 {
133		w.Write(j.Prefix)
134	}
135	w.Write(result)
136	return nil
137}
138
139func (j JSON) renderStreamingJSON(w io.Writer, v interface{}) error {
140	if hw, ok := w.(http.ResponseWriter); ok {
141		j.Head.Write(hw)
142	}
143	if len(j.Prefix) > 0 {
144		w.Write(j.Prefix)
145	}
146
147	return json.NewEncoder(w).Encode(v)
148}
149
150// Render a JSONP response.
151func (j JSONP) Render(w io.Writer, v interface{}) error {
152	var result []byte
153	var err error
154
155	if j.Indent {
156		result, err = json.MarshalIndent(v, "", "  ")
157	} else {
158		result, err = json.Marshal(v)
159	}
160	if err != nil {
161		return err
162	}
163
164	// JSON marshaled fine, write out the result.
165	if hw, ok := w.(http.ResponseWriter); ok {
166		j.Head.Write(hw)
167	}
168	w.Write([]byte(j.Callback + "("))
169	w.Write(result)
170	w.Write([]byte(");"))
171
172	// If indenting, append a new line.
173	if j.Indent {
174		w.Write([]byte("\n"))
175	}
176	return nil
177}
178
179// Render a text response.
180func (t Text) Render(w io.Writer, v interface{}) error {
181	if hw, ok := w.(http.ResponseWriter); ok {
182		c := hw.Header().Get(ContentType)
183		if c != "" {
184			t.Head.ContentType = c
185		}
186		t.Head.Write(hw)
187	}
188
189	w.Write([]byte(v.(string)))
190	return nil
191}
192
193// Render an XML response.
194func (x XML) Render(w io.Writer, v interface{}) error {
195	var result []byte
196	var err error
197
198	if x.Indent {
199		result, err = xml.MarshalIndent(v, "", "  ")
200		result = append(result, '\n')
201	} else {
202		result, err = xml.Marshal(v)
203	}
204	if err != nil {
205		return err
206	}
207
208	// XML marshaled fine, write out the result.
209	if hw, ok := w.(http.ResponseWriter); ok {
210		x.Head.Write(hw)
211	}
212	if len(x.Prefix) > 0 {
213		w.Write(x.Prefix)
214	}
215	w.Write(result)
216	return nil
217}
218