99 "io/ioutil"
1010 "net/http"
1111 "reflect"
12- "sort"
1312 "strings"
1413
1514 "github.com/cosmos72/gomacro/base"
@@ -102,24 +101,12 @@ func stubDisplay(Data) error {
102101// fill kernel.renderer map used to convert interpreted types
103102// to known rendering interfaces
104103func (kernel * Kernel ) initRenderers () {
105- type Pair = struct {
106- name string
107- typ xreflect.Type
108- }
109- var pairs []Pair
104+ kernel .render = make (map [string ]xreflect.Type )
110105 for name , typ := range kernel .display .Types {
111106 if typ .Kind () == reflect .Interface {
112- pairs = append ( pairs , Pair { name , typ })
107+ kernel . render [ name ] = typ
113108 }
114109 }
115- // for deterministic behaviour, sort alphabetically by name
116- sort .Slice (pairs , func (i , j int ) bool {
117- return pairs [i ].name < pairs [j ].name
118- })
119- kernel .render = make ([]xreflect.Type , len (pairs ))
120- for i , pair := range pairs {
121- kernel .render [i ] = pair .typ
122- }
123110}
124111
125112// if vals[] contain a single non-nil value which is auto-renderable,
@@ -160,7 +147,7 @@ func (kernel *Kernel) canAutoRender(data interface{}, typ xreflect.Type) bool {
160147 // in gomacro, methods of interpreted types are emulated,
161148 // thus type-asserting them to interface types as done above cannot succeed.
162149 // Manually check if emulated type "pretends" to implement
163- // one of the interfaces above
150+ // at least one of the interfaces above
164151 for _ , xtyp := range kernel .render {
165152 if typ .Implements (xtyp ) {
166153 return true
@@ -169,106 +156,168 @@ func (kernel *Kernel) canAutoRender(data interface{}, typ xreflect.Type) bool {
169156 return false
170157}
171158
159+ var autoRenderers = map [string ]func (Data , interface {}) Data {
160+ "Data" : func (d Data , i interface {}) Data {
161+ if x , ok := i .(Data ); ok {
162+ d .Data = merge (d .Data , x .Data )
163+ d .Metadata = merge (d .Metadata , x .Metadata )
164+ d .Transient = merge (d .Transient , x .Transient )
165+ }
166+ return d
167+ },
168+ "Renderer" : func (d Data , i interface {}) Data {
169+ if r , ok := i .(Renderer ); ok {
170+ x := r .Render ()
171+ d .Data = merge (d .Data , x .Data )
172+ d .Metadata = merge (d .Metadata , x .Metadata )
173+ d .Transient = merge (d .Transient , x .Transient )
174+ }
175+ return d
176+ },
177+ "SimpleRenderer" : func (d Data , i interface {}) Data {
178+ if r , ok := i .(SimpleRenderer ); ok {
179+ x := r .SimpleRender ()
180+ d .Data = merge (d .Data , x )
181+ }
182+ return d
183+ },
184+ "HTMLer" : func (d Data , i interface {}) Data {
185+ if r , ok := i .(HTMLer ); ok {
186+ d .Data = ensure (d .Data )
187+ d .Data [MIMETypeHTML ] = r .HTML ()
188+ }
189+ return d
190+ },
191+ "JavaScripter" : func (d Data , i interface {}) Data {
192+ if r , ok := i .(JavaScripter ); ok {
193+ d .Data = ensure (d .Data )
194+ d .Data [MIMETypeJavaScript ] = r .JavaScript ()
195+ }
196+ return d
197+ },
198+ "JPEGer" : func (d Data , i interface {}) Data {
199+ if r , ok := i .(JPEGer ); ok {
200+ d .Data = ensure (d .Data )
201+ d .Data [MIMETypeJPEG ] = r .JPEG ()
202+ }
203+ return d
204+ },
205+ "JSONer" : func (d Data , i interface {}) Data {
206+ if r , ok := i .(JSONer ); ok {
207+ d .Data = ensure (d .Data )
208+ d .Data [MIMETypeJSON ] = r .JSON ()
209+ }
210+ return d
211+ },
212+ "Latexer" : func (d Data , i interface {}) Data {
213+ if r , ok := i .(Latexer ); ok {
214+ d .Data = ensure (d .Data )
215+ d .Data [MIMETypeLatex ] = r .Latex ()
216+ }
217+ return d
218+ },
219+ "Markdowner" : func (d Data , i interface {}) Data {
220+ if r , ok := i .(Markdowner ); ok {
221+ d .Data = ensure (d .Data )
222+ d .Data [MIMETypeMarkdown ] = r .Markdown ()
223+ }
224+ return d
225+ },
226+ "PNGer" : func (d Data , i interface {}) Data {
227+ if r , ok := i .(PNGer ); ok {
228+ d .Data = ensure (d .Data )
229+ d .Data [MIMETypePNG ] = r .PNG ()
230+ }
231+ return d
232+ },
233+ "PDFer" : func (d Data , i interface {}) Data {
234+ if r , ok := i .(PDFer ); ok {
235+ d .Data = ensure (d .Data )
236+ d .Data [MIMETypePDF ] = r .PDF ()
237+ }
238+ return d
239+ },
240+ "SVGer" : func (d Data , i interface {}) Data {
241+ if r , ok := i .(SVGer ); ok {
242+ d .Data = ensure (d .Data )
243+ d .Data [MIMETypeSVG ] = r .SVG ()
244+ }
245+ return d
246+ },
247+ "Image" : func (d Data , i interface {}) Data {
248+ if r , ok := i .(image.Image ); ok {
249+ b , mimeType , err := encodePng (r )
250+ if err != nil {
251+ d = makeDataErr (err )
252+ } else {
253+ d .Data = ensure (d .Data )
254+ d .Data [mimeType ] = b
255+ d .Metadata = merge (d .Metadata , imageMetadata (r ))
256+ }
257+ }
258+ return d
259+ },
260+ }
261+
172262// detect and render data types that should be auto-rendered graphically
173263func (kernel * Kernel ) autoRender (mimeType string , arg interface {}, typ xreflect.Type ) Data {
174- var s string
175- var b []byte
176- var err error
177- var ret Data
178- datain := arg
179- again:
180- switch data := datain .(type ) {
181- case Data :
182- ret = data
183- case Renderer :
184- ret = data .Render ()
185- case SimpleRenderer :
186- ret .Data = data .SimpleRender ()
187- case HTMLer :
188- mimeType = MIMETypeHTML
189- s = data .HTML ()
190- case JavaScripter :
191- mimeType = MIMETypeJavaScript
192- s = data .JavaScript ()
193- case JPEGer :
194- mimeType = MIMETypeJPEG
195- b = data .JPEG ()
196- case JSONer :
197- ret .Data = MIMEMap {MIMETypeJSON : data .JSON ()}
198- case Latexer :
199- mimeType = MIMETypeLatex
200- s = data .Latex ()
201- case Markdowner :
202- mimeType = MIMETypeMarkdown
203- s = data .Markdown ()
204- case PNGer :
205- mimeType = MIMETypePNG
206- b = data .PNG ()
207- case PDFer :
208- mimeType = MIMETypePDF
209- b = data .PDF ()
210- case SVGer :
211- mimeType = MIMETypeSVG
212- s = data .SVG ()
213- case image.Image :
214- b , mimeType , err = encodePng (data )
215- if err == nil {
216- ret .Metadata = imageMetadata (data )
217- }
218- default :
219- if kernel != nil && typ != nil {
220- // in gomacro, methods of interpreted types are emulated.
221- // Thus type-asserting them to interface types as done above cannot succeed.
222- // Manually check if emulated type "pretends" to implement one of the above interfaces
223- // and, in case, tell the interpreter to convert to them
224- for _ , xtyp := range kernel .render {
225- if typ .Implements (xtyp ) {
226- fun := kernel .ir .Comp .Converter (typ , xtyp )
227- data = base .ValueInterface (fun (reflect .ValueOf (datain )))
228- if data != nil {
229- s = fmt .Sprint (data )
230- datain = data
231- // avoid infinite recursion
232- kernel = nil
233- typ = nil
234- goto again
235- }
236- }
264+ var data Data
265+
266+ // try all autoRenderers
267+ for _ , fun := range autoRenderers {
268+ data = fun (data , arg )
269+ }
270+
271+ if kernel != nil && typ != nil {
272+ // in gomacro, methods of interpreted types are emulated.
273+ // Thus type-asserting them to interface types as done by autoRenderer functions above cannot succeed.
274+ // Manually check if emulated type "pretends" to implement one of the a above interfaces
275+ // and, in case, tell the interpreter to convert to them
276+ for name , xtyp := range kernel .render {
277+ fun := autoRenderers [name ]
278+ if fun == nil || ! typ .Implements (xtyp ) || typ .ReflectType ().Implements (xtyp .ReflectType ()) {
279+ continue
280+ }
281+ conv := kernel .ir .Comp .Converter (typ , xtyp )
282+ x := base .ValueInterface (conv (reflect .ValueOf (arg )))
283+ if x == nil {
284+ continue
237285 }
286+ data = fun (data , x )
238287 }
239- panic (fmt .Errorf ("internal error, autoRender invoked on unexpected type %T" , data ))
240288 }
241- return fillDefaults (ret , arg , s , b , mimeType , err )
289+ return fillDefaults (data , arg , "" , nil , "" , nil )
242290}
243291
244- func fillDefaults (ret Data , data interface {}, s string , b []byte , mimeType string , err error ) Data {
292+ func fillDefaults (data Data , arg interface {}, s string , b []byte , mimeType string , err error ) Data {
245293 if err != nil {
246294 return makeDataErr (err )
247295 }
248- if ret .Data == nil {
249- ret .Data = make (MIMEMap )
296+ if data .Data == nil {
297+ data .Data = make (MIMEMap )
250298 }
251299 // cannot autodetect the mime type of a string
252300 if len (s ) != 0 && len (mimeType ) != 0 {
253- ret .Data [mimeType ] = s
301+ data .Data [mimeType ] = s
254302 }
255303 // ensure plain text is set
256- if ret .Data [MIMETypeText ] == "" {
304+ if data .Data [MIMETypeText ] == "" {
257305 if len (s ) == 0 {
258- s = fmt .Sprint (data )
306+ s = fmt .Sprint (arg )
259307 }
260- ret .Data [MIMETypeText ] = s
308+ data .Data [MIMETypeText ] = s
261309 }
262310 // if []byte is available, use it
263311 if len (b ) != 0 {
264312 if len (mimeType ) == 0 {
265313 mimeType = http .DetectContentType (b )
266314 }
267315 if len (mimeType ) != 0 && mimeType != MIMETypeText {
268- ret .Data [mimeType ] = b
316+ data .Data [mimeType ] = b
269317 }
270318 }
271- return ret
319+ fmt .Printf ("%+v\n " , data )
320+ return data
272321}
273322
274323// do our best to render data graphically
0 commit comments