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}