uritemplate.go

  1// Copyright (C) 2016 Kohei YOSHIDA. All rights reserved.
  2//
  3// This program is free software; you can redistribute it and/or
  4// modify it under the terms of The BSD 3-Clause License
  5// that can be found in the LICENSE file.
  6
  7package uritemplate
  8
  9import (
 10	"log"
 11	"regexp"
 12	"strings"
 13	"sync"
 14)
 15
 16var (
 17	debug = debugT(false)
 18)
 19
 20type debugT bool
 21
 22func (t debugT) Printf(format string, v ...interface{}) {
 23	if t {
 24		log.Printf(format, v...)
 25	}
 26}
 27
 28// Template represents a URI Template.
 29type Template struct {
 30	raw   string
 31	exprs []template
 32
 33	// protects the rest of fields
 34	mu       sync.Mutex
 35	varnames []string
 36	re       *regexp.Regexp
 37	prog     *prog
 38}
 39
 40// New parses and constructs a new Template instance based on the template.
 41// New returns an error if the template cannot be recognized.
 42func New(template string) (*Template, error) {
 43	return (&parser{r: template}).parseURITemplate()
 44}
 45
 46// MustNew panics if the template cannot be recognized.
 47func MustNew(template string) *Template {
 48	ret, err := New(template)
 49	if err != nil {
 50		panic(err)
 51	}
 52	return ret
 53}
 54
 55// Raw returns a raw URI template passed to New in string.
 56func (t *Template) Raw() string {
 57	return t.raw
 58}
 59
 60// Varnames returns variable names used in the template.
 61func (t *Template) Varnames() []string {
 62	t.mu.Lock()
 63	defer t.mu.Unlock()
 64	if t.varnames != nil {
 65		return t.varnames
 66	}
 67
 68	reg := map[string]struct{}{}
 69	t.varnames = []string{}
 70	for i := range t.exprs {
 71		expr, ok := t.exprs[i].(*expression)
 72		if !ok {
 73			continue
 74		}
 75		for _, spec := range expr.vars {
 76			if _, ok := reg[spec.name]; ok {
 77				continue
 78			}
 79			reg[spec.name] = struct{}{}
 80			t.varnames = append(t.varnames, spec.name)
 81		}
 82	}
 83
 84	return t.varnames
 85}
 86
 87// Expand returns a URI reference corresponding to the template expanded using the passed variables.
 88func (t *Template) Expand(vars Values) (string, error) {
 89	var w strings.Builder
 90	for i := range t.exprs {
 91		expr := t.exprs[i]
 92		if err := expr.expand(&w, vars); err != nil {
 93			return w.String(), err
 94		}
 95	}
 96	return w.String(), nil
 97}
 98
 99// Regexp converts the template to regexp and returns compiled *regexp.Regexp.
100func (t *Template) Regexp() *regexp.Regexp {
101	t.mu.Lock()
102	defer t.mu.Unlock()
103	if t.re != nil {
104		return t.re
105	}
106
107	var b strings.Builder
108	b.WriteByte('^')
109	for _, expr := range t.exprs {
110		expr.regexp(&b)
111	}
112	b.WriteByte('$')
113	t.re = regexp.MustCompile(b.String())
114
115	return t.re
116}