diff options
author | spf13 <steve.francia@gmail.com> | 2014-11-20 12:32:21 -0500 |
---|---|---|
committer | spf13 <steve.francia@gmail.com> | 2014-11-20 12:36:57 -0500 |
commit | 73f203ad86a71136a1d35e0aa1bba574edb91a51 (patch) | |
tree | 8e1e336f03df4b583a27e15f4e1bf28182331605 /hugolib | |
parent | 92a3372a3fdc0365a06a75e6d97f24e624db879b (diff) |
Move template library into it's own package (tpl). No longer dependent on hugolib. Can be used externally.
Diffstat (limited to 'hugolib')
-rw-r--r-- | hugolib/page.go | 15 | ||||
-rw-r--r-- | hugolib/shortcode.go | 20 | ||||
-rw-r--r-- | hugolib/shortcode_test.go | 32 | ||||
-rw-r--r-- | hugolib/site.go | 5 | ||||
-rw-r--r-- | hugolib/site_test.go | 21 | ||||
-rw-r--r-- | hugolib/template.go | 690 | ||||
-rw-r--r-- | hugolib/template_embedded.go | 97 | ||||
-rw-r--r-- | hugolib/template_test.go | 342 |
8 files changed, 54 insertions, 1168 deletions
diff --git a/hugolib/page.go b/hugolib/page.go index 14a290c7e..3cf9843eb 100644 --- a/hugolib/page.go +++ b/hugolib/page.go @@ -17,10 +17,6 @@ import ( "bytes" "errors" "fmt" - "github.com/spf13/hugo/helpers" - "github.com/spf13/hugo/parser" - jww "github.com/spf13/jwalterweatherman" - "github.com/spf13/viper" "html/template" "io" "net/url" @@ -29,8 +25,13 @@ import ( "time" "github.com/spf13/cast" + "github.com/spf13/hugo/helpers" "github.com/spf13/hugo/hugofs" + "github.com/spf13/hugo/parser" "github.com/spf13/hugo/source" + "github.com/spf13/hugo/tpl" + jww "github.com/spf13/jwalterweatherman" + "github.com/spf13/viper" ) type Page struct { @@ -44,7 +45,7 @@ type Page struct { Truncated bool Draft bool PublishDate time.Time - Tmpl Template + Tmpl tpl.Template Markup string extension string @@ -528,7 +529,7 @@ func (p *Page) Render(layout ...string) template.HTML { curLayout = layout[0] } - return ExecuteTemplateToHTML(p, p.Layout(curLayout)...) + return tpl.ExecuteTemplateToHTML(p, p.Layout(curLayout)...) } func (page *Page) guessMarkupType() string { @@ -629,7 +630,7 @@ func (page *Page) SaveSource() error { return page.SaveSourceAs(page.FullFilePath()) } -func (p *Page) ProcessShortcodes(t Template) { +func (p *Page) ProcessShortcodes(t tpl.Template) { // these short codes aren't used until after Page render, // but processed here to avoid coupling diff --git a/hugolib/shortcode.go b/hugolib/shortcode.go index f90093e7a..7e56c2a4a 100644 --- a/hugolib/shortcode.go +++ b/hugolib/shortcode.go @@ -16,14 +16,16 @@ package hugolib import ( "bytes" "fmt" - "github.com/spf13/hugo/helpers" - jww "github.com/spf13/jwalterweatherman" "html/template" "reflect" "regexp" "sort" "strconv" "strings" + + "github.com/spf13/hugo/helpers" + "github.com/spf13/hugo/tpl" + jww "github.com/spf13/jwalterweatherman" ) type ShortcodeFunc func([]string) string @@ -117,7 +119,7 @@ func (sc shortcode) String() string { // all in one go: extract, render and replace // only used for testing -func ShortcodesHandle(stringToParse string, page *Page, t Template) string { +func ShortcodesHandle(stringToParse string, page *Page, t tpl.Template) string { tmpContent, tmpShortcodes := extractAndRenderShortcodes(stringToParse, page, t) @@ -154,7 +156,7 @@ func createShortcodePlaceholder(id int) string { return fmt.Sprintf("<div>%s-%d</div>", shortcodePlaceholderPrefix, id) } -func renderShortcodes(sc shortcode, p *Page, t Template) string { +func renderShortcodes(sc shortcode, p *Page, t tpl.Template) string { tokenizedRenderedShortcodes := make(map[string](string)) startCount := 0 @@ -169,7 +171,7 @@ func renderShortcodes(sc shortcode, p *Page, t Template) string { return shortcodes } -func renderShortcode(sc shortcode, tokenizedShortcodes map[string](string), cnt int, p *Page, t Template) string { +func renderShortcode(sc shortcode, tokenizedShortcodes map[string](string), cnt int, p *Page, t tpl.Template) string { var data = &ShortcodeWithPage{Params: sc.params, Page: p} tmpl := GetTemplate(sc.name, t) @@ -209,7 +211,7 @@ func renderShortcode(sc shortcode, tokenizedShortcodes map[string](string), cnt return ShortcodeRender(tmpl, data) } -func extractAndRenderShortcodes(stringToParse string, p *Page, t Template) (string, map[string]string) { +func extractAndRenderShortcodes(stringToParse string, p *Page, t tpl.Template) (string, map[string]string) { content, shortcodes, err := extractShortcodes(stringToParse, p, t) renderedShortcodes := make(map[string]string) @@ -235,7 +237,7 @@ func extractAndRenderShortcodes(stringToParse string, p *Page, t Template) (stri // pageTokens state: // - before: positioned just before the shortcode start // - after: shortcode(s) consumed (plural when they are nested) -func extractShortcode(pt *pageTokens, p *Page, t Template) (shortcode, error) { +func extractShortcode(pt *pageTokens, p *Page, t tpl.Template) (shortcode, error) { sc := shortcode{} var isInner = false @@ -334,7 +336,7 @@ Loop: return sc, nil } -func extractShortcodes(stringToParse string, p *Page, t Template) (string, map[string]shortcode, error) { +func extractShortcodes(stringToParse string, p *Page, t tpl.Template) (string, map[string]shortcode, error) { shortCodes := make(map[string]shortcode) @@ -452,7 +454,7 @@ func replaceShortcodeTokens(source []byte, prefix string, numReplacements int, w return buff[0:width], nil } -func GetTemplate(name string, t Template) *template.Template { +func GetTemplate(name string, t tpl.Template) *template.Template { if x := t.Lookup("shortcodes/" + name + ".html"); x != nil { return x } diff --git a/hugolib/shortcode_test.go b/hugolib/shortcode_test.go index 91b3dad17..002946bfd 100644 --- a/hugolib/shortcode_test.go +++ b/hugolib/shortcode_test.go @@ -2,20 +2,22 @@ package hugolib import ( "fmt" - "github.com/spf13/hugo/helpers" - "github.com/spf13/viper" "reflect" "regexp" "sort" "strings" "testing" + + "github.com/spf13/hugo/helpers" + "github.com/spf13/hugo/tpl" + "github.com/spf13/viper" ) func pageFromString(in, filename string) (*Page, error) { return NewPageFrom(strings.NewReader(in), filename) } -func CheckShortCodeMatch(t *testing.T, input, expected string, template Template) { +func CheckShortCodeMatch(t *testing.T, input, expected string, template tpl.Template) { p, _ := pageFromString(SIMPLE_PAGE, "simple.md") output := ShortcodesHandle(input, p, template) @@ -26,13 +28,13 @@ func CheckShortCodeMatch(t *testing.T, input, expected string, template Template } func TestNonSC(t *testing.T) { - tem := NewTemplate() + tem := tpl.New() // notice the syntax diff from 0.12, now comment delims must be added CheckShortCodeMatch(t, "{{%/* movie 47238zzb */%}}", "{{% movie 47238zzb %}}", tem) } func TestPositionalParamSC(t *testing.T) { - tem := NewTemplate() + tem := tpl.New() tem.AddInternalShortcode("video.html", `Playing Video {{ .Get 0 }}`) CheckShortCodeMatch(t, "{{< video 47238zzb >}}", "Playing Video 47238zzb", tem) @@ -43,7 +45,7 @@ func TestPositionalParamSC(t *testing.T) { } func TestNamedParamSC(t *testing.T) { - tem := NewTemplate() + tem := tpl.New() tem.AddInternalShortcode("img.html", `<img{{ with .Get "src" }} src="{{.}}"{{end}}{{with .Get "class"}} class="{{.}}"{{end}}>`) CheckShortCodeMatch(t, `{{< img src="one" >}}`, `<img src="one">`, tem) @@ -55,7 +57,7 @@ func TestNamedParamSC(t *testing.T) { } func TestInnerSC(t *testing.T) { - tem := NewTemplate() + tem := tpl.New() tem.AddInternalShortcode("inside.html", `<div{{with .Get "class"}} class="{{.}}"{{end}}>{{ .Inner }}</div>`) CheckShortCodeMatch(t, `{{< inside class="aspen" >}}`, `<div class="aspen"></div>`, tem) @@ -64,7 +66,7 @@ func TestInnerSC(t *testing.T) { } func TestInnerSCWithMarkdown(t *testing.T) { - tem := NewTemplate() + tem := tpl.New() tem.AddInternalShortcode("inside.html", `<div{{with .Get "class"}} class="{{.}}"{{end}}>{{ .Inner }}</div>`) CheckShortCodeMatch(t, `{{% inside %}} @@ -76,7 +78,7 @@ func TestInnerSCWithMarkdown(t *testing.T) { } func TestInnerSCWithAndWithoutMarkdown(t *testing.T) { - tem := NewTemplate() + tem := tpl.New() tem.AddInternalShortcode("inside.html", `<div{{with .Get "class"}} class="{{.}}"{{end}}>{{ .Inner }}</div>`) CheckShortCodeMatch(t, `{{% inside %}} @@ -98,14 +100,14 @@ This is **plain** text. } func TestEmbeddedSC(t *testing.T) { - tem := NewTemplate() + tem := tpl.New() CheckShortCodeMatch(t, "{{% test %}}", "This is a simple Test", tem) CheckShortCodeMatch(t, `{{% figure src="/found/here" class="bananas orange" %}}`, "\n<figure class=\"bananas orange\">\n \n <img src=\"/found/here\" />\n \n \n</figure>\n", tem) CheckShortCodeMatch(t, `{{% figure src="/found/here" class="bananas orange" caption="This is a caption" %}}`, "\n<figure class=\"bananas orange\">\n \n <img src=\"/found/here\" alt=\"This is a caption\" />\n \n \n <figcaption>\n <p>\n This is a caption\n \n \n \n </p> \n </figcaption>\n \n</figure>\n", tem) } func TestNestedSC(t *testing.T) { - tem := NewTemplate() + tem := tpl.New() tem.AddInternalShortcode("scn1.html", `<div>Outer, inner is {{ .Inner }}</div>`) tem.AddInternalShortcode("scn2.html", `<div>SC2</div>`) @@ -113,7 +115,7 @@ func TestNestedSC(t *testing.T) { } func TestNestedComplexSC(t *testing.T) { - tem := NewTemplate() + tem := tpl.New() tem.AddInternalShortcode("row.html", `-row-{{ .Inner}}-rowStop-`) tem.AddInternalShortcode("column.html", `-col-{{.Inner }}-colStop-`) tem.AddInternalShortcode("aside.html", `-aside-{{ .Inner }}-asideStop-`) @@ -127,7 +129,7 @@ func TestNestedComplexSC(t *testing.T) { } func TestFigureImgWidth(t *testing.T) { - tem := NewTemplate() + tem := tpl.New() CheckShortCodeMatch(t, `{{% figure src="/found/here" class="bananas orange" alt="apple" width="100px" %}}`, "\n<figure class=\"bananas orange\">\n \n <img src=\"/found/here\" alt=\"apple\" width=\"100px\" />\n \n \n</figure>\n", tem) } @@ -138,7 +140,7 @@ func TestHighlight(t *testing.T) { defer viper.Set("PygmentsStyle", viper.Get("PygmentsStyle")) viper.Set("PygmentsStyle", "bw") - tem := NewTemplate() + tem := tpl.New() code := ` {{< highlight java >}} @@ -196,7 +198,7 @@ func TestExtractShortcodes(t *testing.T) { } { p, _ := pageFromString(SIMPLE_PAGE, "simple.md") - tem := NewTemplate() + tem := tpl.New() tem.AddInternalShortcode("tag.html", `tag`) tem.AddInternalShortcode("sc1.html", `sc1`) tem.AddInternalShortcode("sc2.html", `sc2`) diff --git a/hugolib/site.go b/hugolib/site.go index f879c5245..fc3183fa3 100644 --- a/hugolib/site.go +++ b/hugolib/site.go @@ -31,6 +31,7 @@ import ( "github.com/spf13/hugo/hugofs" "github.com/spf13/hugo/source" "github.com/spf13/hugo/target" + "github.com/spf13/hugo/tpl" "github.com/spf13/hugo/transform" jww "github.com/spf13/jwalterweatherman" "github.com/spf13/nitro" @@ -61,7 +62,7 @@ var DefaultTimer *nitro.B type Site struct { Pages Pages Files []*source.File - Tmpl Template + Tmpl tpl.Template Taxonomies TaxonomyList Source source.Input Sections Taxonomy @@ -166,7 +167,7 @@ func (s *Site) Analyze() { } func (s *Site) prepTemplates() { - s.Tmpl = NewTemplate() + s.Tmpl = tpl.T() s.Tmpl.LoadTemplates(s.absLayoutDir()) if s.hasTheme() { s.Tmpl.LoadTemplatesWithPrefix(s.absThemeDir()+"/layouts", "theme") diff --git a/hugolib/site_test.go b/hugolib/site_test.go index fc93389dd..ce1d3f0ad 100644 --- a/hugolib/site_test.go +++ b/hugolib/site_test.go @@ -13,6 +13,7 @@ import ( "github.com/spf13/hugo/hugofs" "github.com/spf13/hugo/source" "github.com/spf13/hugo/target" + "github.com/spf13/hugo/tpl" "github.com/spf13/viper" ) @@ -46,6 +47,14 @@ more text ` ) +func templatePrep(s *Site) { + s.Tmpl = tpl.New() + s.Tmpl.LoadTemplates(s.absLayoutDir()) + if s.hasTheme() { + s.Tmpl.LoadTemplatesWithPrefix(s.absThemeDir()+"/layouts", "theme") + } +} + func pageMust(p *Page, err error) *Page { if err != nil { panic(err) @@ -57,7 +66,7 @@ func TestDegenerateRenderThingMissingTemplate(t *testing.T) { p, _ := NewPageFrom(strings.NewReader(PAGE_SIMPLE_TITLE), "content/a/file.md") p.Convert() s := new(Site) - s.prepTemplates() + templatePrep(s) err := s.renderThing(p, "foobar", nil) if err == nil { t.Errorf("Expected err to be returned when missing the template.") @@ -66,7 +75,7 @@ func TestDegenerateRenderThingMissingTemplate(t *testing.T) { func TestAddInvalidTemplate(t *testing.T) { s := new(Site) - s.prepTemplates() + templatePrep(s) err := s.addTemplate("missing", TEMPLATE_MISSING_FUNC) if err == nil { t.Fatalf("Expecting the template to return an error") @@ -108,7 +117,7 @@ func TestRenderThing(t *testing.T) { } s := new(Site) - s.prepTemplates() + templatePrep(s) for i, test := range tests { p, err := NewPageFrom(strings.NewReader(test.content), "content/a/file.md") @@ -154,7 +163,7 @@ func TestRenderThingOrDefault(t *testing.T) { hugofs.DestinationFS = new(afero.MemMapFs) s := &Site{} - s.prepTemplates() + templatePrep(s) for i, test := range tests { p, err := NewPageFrom(strings.NewReader(PAGE_SIMPLE_TITLE), "content/a/file.md") @@ -306,7 +315,7 @@ func TestSkipRender(t *testing.T) { } s.initializeSiteInfo() - s.prepTemplates() + templatePrep(s) must(s.addTemplate("_default/single.html", "{{.Content}}")) must(s.addTemplate("head", "<head><script src=\"script.js\"></script></head>")) @@ -366,7 +375,7 @@ func TestAbsUrlify(t *testing.T) { } t.Logf("Rendering with BaseUrl %q and CanonifyUrls set %v", viper.GetString("baseUrl"), canonify) s.initializeSiteInfo() - s.prepTemplates() + templatePrep(s) must(s.addTemplate("blue/single.html", TEMPLATE_WITH_URL_ABS)) if err := s.CreatePages(); err != nil { diff --git a/hugolib/template.go b/hugolib/template.go deleted file mode 100644 index 59221093f..000000000 --- a/hugolib/template.go +++ /dev/null @@ -1,690 +0,0 @@ -package hugolib - -import ( - "bytes" - "errors" - "html" - "html/template" - "io" - "io/ioutil" - "os" - "path/filepath" - "reflect" - "strconv" - "strings" - - "github.com/eknkc/amber" - "github.com/spf13/cast" - "github.com/spf13/hugo/helpers" - jww "github.com/spf13/jwalterweatherman" -) - -var localTemplates *template.Template - -func Eq(x, y interface{}) bool { - return reflect.DeepEqual(x, y) -} - -func Ne(x, y interface{}) bool { - return !Eq(x, y) -} - -func Ge(a, b interface{}) bool { - left, right := compareGetFloat(a, b) - return left >= right -} - -func Gt(a, b interface{}) bool { - left, right := compareGetFloat(a, b) - return left > right -} - -func Le(a, b interface{}) bool { - left, right := compareGetFloat(a, b) - return left <= right -} - -func Lt(a, b interface{}) bool { - left, right := compareGetFloat(a, b) - return left < right -} - -func compareGetFloat(a interface{}, b interface{}) (float64, float64) { - var left, right float64 - av := reflect.ValueOf(a) - - switch av.Kind() { - case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice: - left = float64(av.Len()) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - left = float64(av.Int()) - case reflect.Float32, reflect.Float64: - left = av.Float() - case reflect.String: - left, _ = strconv.ParseFloat(av.String(), 64) - } - - bv := reflect.ValueOf(b) - - switch bv.Kind() { - case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice: - right = float64(bv.Len()) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - right = float64(bv.Int()) - case reflect.Float32, reflect.Float64: - right = bv.Float() - case reflect.String: - right, _ = strconv.ParseFloat(bv.String(), 64) - } - - return left, right -} - -func Intersect(l1, l2 interface{}) (interface{}, error) { - - if l1 == nil || l2 == nil { - return make([]interface{}, 0), nil - } - - l1v := reflect.ValueOf(l1) - l2v := reflect.ValueOf(l2) - - switch l1v.Kind() { - case reflect.Array, reflect.Slice: - switch l2v.Kind() { - case reflect.Array, reflect.Slice: - r := reflect.MakeSlice(l1v.Type(), 0, 0) - for i := 0; i < l1v.Len(); i++ { - l1vv := l1v.Index(i) - for j := 0; j < l2v.Len(); j++ { - l2vv := l2v.Index(j) - switch l1vv.Kind() { - case reflect.String: - if l1vv.Type() == l2vv.Type() && l1vv.String() == l2vv.String() && !In(r, l2vv) { - r = reflect.Append(r, l2vv) - } - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - switch l2vv.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - if l1vv.Int() == l2vv.Int() && !In(r, l2vv) { - r = reflect.Append(r, l2vv) - } - } - case reflect.Float32, reflect.Float64: - switch l2vv.Kind() { - case reflect.Float32, reflect.Float64: - if l1vv.Float() == l2vv.Float() && !In(r, l2vv) { - r = reflect.Append(r, l2vv) - } - } - } - } - } - return r.Interface(), nil - default: - return nil, errors.New("can't iterate over " + reflect.ValueOf(l2).Type().String()) - } - default: - return nil, errors.New("can't iterate over " + reflect.ValueOf(l1).Type().String()) - } -} - -func In(l interface{}, v interface{}) bool { - lv := reflect.ValueOf(l) - vv := reflect.ValueOf(v) - - switch lv.Kind() { - case reflect.Array, reflect.Slice: - for i := 0; i < lv.Len(); i++ { - lvv := lv.Index(i) - switch lvv.Kind() { - case reflect.String: - if vv.Type() == lvv.Type() && vv.String() == lvv.String() { - return true - } - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - switch vv.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - if vv.Int() == lvv.Int() { - return true - } - } - case reflect.Float32, reflect.Float64: - switch vv.Kind() { - case reflect.Float32, reflect.Float64: - if vv.Float() == lvv.Float() { - return true - } - } - } - } - case reflect.String: - if vv.Type() == lv.Type() && strings.Contains(lv.String(), vv.String()) { - return true - } - } - return false -} - -// First is exposed to templates, to iterate over the first N items in a -// rangeable list. -func First(limit interface{}, seq interface{}) (interface{}, error) { - - limitv, err := cast.ToIntE(limit) - - if err != nil { - return nil, err - } - - if limitv < 1 { - return nil, errors.New("can't return negative/empty count of items from sequence") - } - - seqv := reflect.ValueOf(seq) - // this is better than my first pass; ripped from text/template/exec.go indirect(): - for ; seqv.Kind() == reflect.Ptr || seqv.Kind() == reflect.Interface; seqv = seqv.Elem() { - if seqv.IsNil() { - return nil, errors.New("can't iterate over a nil value") - } - if seqv.Kind() == reflect.Interface && seqv.NumMethod() > 0 { - break - } - } - - switch seqv.Kind() { - case reflect.Array, reflect.Slice, reflect.String: - // okay - default: - return nil, errors.New("can't iterate over " + reflect.ValueOf(seq).Type().String()) - } - if limitv > seqv.Len() { - limitv = seqv.Len() - } - return seqv.Slice(0, limitv).Interface(), nil -} - -func Where(seq, key, match interface{}) (interface{}, error) { - seqv := reflect.ValueOf(seq) - kv := reflect.ValueOf(key) - mv := reflect.ValueOf(match) - - // this is better than my first pass; ripped from text/template/exec.go indirect(): - for ; seqv.Kind() == reflect.Ptr || seqv.Kind() == reflect.Interface; seqv = seqv.Elem() { - if seqv.IsNil() { - return nil, errors.New("can't iterate over a nil value") - } - if seqv.Kind() == reflect.Interface && seqv.NumMethod() > 0 { - break - } - } - - switch seqv.Kind() { - case reflect.Array, reflect.Slice: - r := reflect.MakeSlice(seqv.Type(), 0, 0) - for i := 0; i < seqv.Len(); i++ { - var vvv reflect.Value - vv := seqv.Index(i) - switch vv.Kind() { - case reflect.Map: - if kv.Type() == vv.Type().Key() && vv.MapIndex(kv).IsValid() { - vvv = vv.MapIndex(kv) - } - case reflect.Struct: - if kv.Kind() == reflect.String { - method := vv.MethodByName(kv.String()) - if method.IsValid() && method.Type().NumIn() == 0 && method.Type().NumOut() > 0 { - vvv = method.Call(nil)[0] - } else if vv.FieldByName(kv.String()).IsValid() { - vvv = vv.FieldByName(kv.String()) - } - } - case reflect.Ptr: - if !vv.IsNil() { - ev := vv.Elem() - switch ev.Kind() { - case reflect.Map: - if kv.Type() == ev.Type().Key() && ev.MapIndex(kv).IsValid() { - vvv = ev.MapIndex(kv) - } - case reflect.Struct: - if kv.Kind() == reflect.String { - method := vv.MethodByName(kv.String()) - if method.IsValid() && method.Type().NumIn() == 0 && method.Type().NumOut() > 0 { - vvv = method.Call(nil)[0] - } else if ev.FieldByName(kv.String()).IsValid() { - vvv = ev.FieldByName(kv.String()) - } - } - } - } - } - - if vvv.IsValid() && mv.Type() == vvv.Type() { - switch mv.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - if mv.Int() == vvv.Int() { - r = reflect.Append(r, vv) - } - case reflect.String: - if mv.String() == vvv.String() { - r = reflect.Append(r, vv) - } - } - } - } - return r.Interface(), nil - default: - return nil, errors.New("can't iterate over " + reflect.ValueOf(seq).Type().String()) - } -} - -func IsSet(a interface{}, key interface{}) bool { - av := reflect.ValueOf(a) - kv := reflect.ValueOf(key) - - switch av.Kind() { - case reflect.Array, reflect.Chan, reflect.Slice: - if int64(av.Len()) > kv.Int() { - return true - } - case reflect.Map: - if kv.Type() == av.Type().Key() { - return av.MapIndex(kv).IsValid() - } - } - - return false -} - -func ReturnWhenSet(a interface{}, index int) interface{} { - av := reflect.ValueOf(a) - - switch av.Kind() { - case reflect.Array, reflect.Slice: - if av.Len() > index { - - avv := av.Index(index) - switch avv.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return avv.Int() - case reflect.String: - return avv.String() - } - } - } - - return "" -} - -func Highlight(in interface{}, lang string) template.HTML { - var str string - av := reflect.ValueOf(in) - switch av.Kind() { - case reflect.String: - str = av.String() - } - - return template.HTML(helpers.Highlight(html.UnescapeString(str), lang)) -} - -func SafeHtml(text string) template.HTML { - return template.HTML(text) -} - -func doArithmetic(a, b interface{}, op rune) (interface{}, error) { - av := reflect.ValueOf(a) - bv := reflect.ValueOf(b) - var ai, bi int64 - var af, bf float64 - var au, bu uint64 - switch av.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - ai = av.Int() - switch bv.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - bi = bv.Int() - case reflect.Float32, reflect.Float64: - af = float64(ai) // may overflow - ai = 0 - bf = bv.Float() - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - bu = bv.Uint() - if ai >= 0 { - au = uint64(ai) - ai = 0 - } else { - bi = int64(bu) // may overflow - bu = 0 - } - default: - return nil, errors.New("Can't apply the operator to the values") - } - case reflect.Float32, reflect.Float64: - af = av.Float() - switch bv.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - bf = float64(bv.Int()) // may overflow - case reflect.Float32, reflect.Float64: - bf = bv.Float() - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - bf = float64(bv.Uint()) // may overflow - default: - return nil, errors.New("Can't apply the operator to the values") - } - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - au = av.Uint() - switch bv.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - bi = bv.Int() - if bi >= 0 { - bu = uint64(bi) - bi = 0 - } else { - ai = int64(au) // may overflow - au = 0 - } - case reflect.Float32, reflect.Float64: - af = float64(au) // may overflow - au = 0 - bf = bv.Float() - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - bu = bv.Uint() - default: - return nil, errors.New("Can't apply the operator to the values") - } - case reflect.String: - as := av.String() - if bv.Kind() == reflect.String && op == '+' { - bs := bv.String() - return as + bs, nil - } else { - return nil, errors.New("Can't apply the operator to the values") - } - default: - return nil, errors.New("Can't apply the operator to the values") - } - - switch op { - case '+': - if ai != 0 || bi != 0 { - return ai + bi, nil - } else if af != 0 || bf != 0 { - return af + bf, nil - } else if au != 0 || bu != 0 { - return au + bu, nil - } else { - return 0, nil - } - case '-': - if ai != 0 || bi != 0 { - return ai - bi, nil - } else if af != 0 || bf != 0 { - return af - bf, nil - } else if au != 0 || bu != 0 { - return au - bu, nil - } else { - return 0, nil - } - case '*': - if ai != 0 || bi != 0 { - return ai * bi, nil - } else if af != 0 || bf != 0 { - return af * bf, nil - } else if au != 0 || bu != 0 { - return au * bu, nil - } else { - return 0, nil - } - case '/': - if bi != 0 { - return ai / bi, nil - } else if bf != 0 { - return af / bf, nil - } else if bu != 0 { - return au / bu, nil - } else { - return nil, errors.New("Can't divide the value by 0") - } - default: - return nil, errors.New("There is no such an operation") - } -} - -func Mod(a, b interface{}) (int64, error) { - av := reflect.ValueOf(a) - bv := reflect.ValueOf(b) - var ai, bi int64 - - switch av.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - ai = av.Int() - default: - return 0, errors.New("Modulo operator can't be used with non integer value") - } - - switch bv.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - bi = bv.Int() - default: - return 0, errors.New("Modulo operator can't be used with non integer value") - } - - if bi == 0 { - return 0, errors.New("The number can't be divided by zero at modulo operation") - } - - |