Copyright 2013 Joshua Tacoma. All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
Package uritemplates is a level 3 implementation of RFC 6570 (URI Template, http://tools.ietf.org/html/rfc6570). uritemplates does not support composite values (in Go: slices or maps) and so does not qualify as a level 4 implementation.
package uritemplates

import (
	
	
	
	
	
)

var (
	unreserved = regexp.MustCompile("[^A-Za-z0-9\\-._~]")
	reserved   = regexp.MustCompile("[^A-Za-z0-9\\-._~:/?#[\\]@!$&'()*+,;=]")
	validname  = regexp.MustCompile("^([A-Za-z0-9_\\.]|%[0-9A-Fa-f][0-9A-Fa-f])+$")
	hex        = []byte("0123456789ABCDEF")
)

func ( []byte) []byte {
	 := make([]byte, len()*3)
	for ,  := range  {
		 := [*3 : *3+3]
		[0] = 0x25
		[1] = hex[/16]
		[2] = hex[%16]
	}
	return 
}
pairWriter is a convenience struct which allows escaped and unescaped versions of the template to be written in parallel.
Write writes the provided string directly without any escaping.
Escape writes the provided string, escaping the string for the escaped output.
func ( *pairWriter) ( string,  bool) {
	.unescaped.WriteString()
	if  {
		.escaped.Write(reserved.ReplaceAllFunc([]byte(), pctEncode))
	} else {
		.escaped.Write(unreserved.ReplaceAllFunc([]byte(), pctEncode))
	}
}
Escaped returns the escaped string.
func ( *pairWriter) () string {
	return .escaped.String()
}
Unescaped returns the unescaped string.
func ( *pairWriter) () string {
	return .unescaped.String()
}
A uriTemplate is a parsed representation of a URI template.
parse parses a URI template string into a uriTemplate object.
func ( string) (*uriTemplate, error) {
	 := strings.Split(, "{")
	 := make([]templatePart, len()*2-1)
	for ,  := range  {
		if  == 0 {
			if strings.Contains(, "}") {
				return nil, errors.New("unexpected }")
			}
			[].raw = 
			continue
		}
		 := strings.Split(, "}")
		if len() != 2 {
			return nil, errors.New("malformed template")
		}
		 := [0]
		var  error
		[*2-1],  = parseExpression()
		if  != nil {
			return nil, 
		}
		[*2].raw = [1]
	}
	return &uriTemplate{
		raw:   ,
		parts: ,
	}, nil
}

type templatePart struct {
	raw           string
	terms         []templateTerm
	first         string
	sep           string
	named         bool
	ifemp         string
	allowReserved bool
}

type templateTerm struct {
	name     string
	explode  bool
	truncate int
}

func ( string) ( templatePart,  error) {
	switch [0] {
	case '+':
		.sep = ","
		.allowReserved = true
		 = [1:]
	case '.':
		.first = "."
		.sep = "."
		 = [1:]
	case '/':
		.first = "/"
		.sep = "/"
		 = [1:]
	case ';':
		.first = ";"
		.sep = ";"
		.named = true
		 = [1:]
	case '?':
		.first = "?"
		.sep = "&"
		.named = true
		.ifemp = "="
		 = [1:]
	case '&':
		.first = "&"
		.sep = "&"
		.named = true
		.ifemp = "="
		 = [1:]
	case '#':
		.first = "#"
		.sep = ","
		.allowReserved = true
		 = [1:]
	default:
		.sep = ","
	}
	 := strings.Split(, ",")
	.terms = make([]templateTerm, len())
	for ,  := range  {
		.terms[],  = parseTerm()
		if  != nil {
			break
		}
	}
	return , 
}

TODO(djd): Remove "*" suffix parsing once we check that no APIs have mistakenly used that attribute.
	if strings.HasSuffix(, "*") {
		.explode = true
		 = [:len()-1]
	}
	 := strings.Split(, ":")
	if len() == 1 {
		.name = 
	} else if len() == 2 {
		.name = [0]
		var  int64
		,  = strconv.ParseInt([1], 10, 0)
		.truncate = int()
	} else {
		 = errors.New("multiple colons in same term")
	}
	if !validname.MatchString(.name) {
		 = errors.New("not a valid name: " + .name)
	}
	if .explode && .truncate > 0 {
		 = errors.New("both explode and prefix modifiers on same term")
	}
	return , 
}
Expand expands a URI template with a set of values to produce the resultant URI. Two forms of the result are returned: one with all the elements escaped, and one with the elements unescaped.
func ( *uriTemplate) ( map[string]string) (,  string) {
	var  pairWriter
	for ,  := range .parts {
		.expand(&, )
	}
	return .Escaped(), .Unescaped()
}

func ( *templatePart) ( *pairWriter,  map[string]string) {
	if len(.raw) > 0 {
		.Write(.raw)
		return
	}
	var  = true
	for ,  := range .terms {
		,  := [.name]
		if ! {
			continue
		}
		if  {
			.Write(.first)
			 = false
		} else {
			.Write(.sep)
		}
		.expandString(, , )
	}
}

func ( *templatePart) ( *pairWriter,  string,  bool) {
	if .named {
		.Write()
		if  {
			.Write(.ifemp)
		} else {
			.Write("=")
		}
	}
}

func ( *templatePart) ( *pairWriter,  templateTerm,  string) {
	if len() > .truncate && .truncate > 0 {
		 = [:.truncate]
	}
	.expandName(, .name, len() == 0)
	.Escape(, .allowReserved)