// Package linkheader provides functions for parsing HTTP Link headers package linkheader import ( "fmt" "strings" ) // A Link is a single URL and related parameters type Link struct { URL string Rel string Params map[string]string } // HasParam returns if a Link has a particular parameter or not func (l Link) HasParam(key string) bool { for p := range l.Params { if p == key { return true } } return false } // Param returns the value of a parameter if it exists func (l Link) Param(key string) string { for k, v := range l.Params { if key == k { return v } } return "" } // String returns the string representation of a link func (l Link) String() string { p := make([]string, 0, len(l.Params)) for k, v := range l.Params { p = append(p, fmt.Sprintf("%s=\"%s\"", k, v)) } if l.Rel != "" { p = append(p, fmt.Sprintf("%s=\"%s\"", "rel", l.Rel)) } return fmt.Sprintf("<%s>; %s", l.URL, strings.Join(p, "; ")) } // Links is a slice of Link structs type Links []Link // FilterByRel filters a group of Links by the provided Rel attribute func (l Links) FilterByRel(r string) Links { links := make(Links, 0) for _, link := range l { if link.Rel == r { links = append(links, link) } } return links } // String returns the string representation of multiple Links // for use in HTTP responses etc func (l Links) String() string { if l == nil { return fmt.Sprint(nil) } var strs []string for _, link := range l { strs = append(strs, link.String()) } return strings.Join(strs, ", ") } // Parse parses a raw Link header in the form: // ; rel="foo", ; rel="bar"; wat="dis" // returning a slice of Link structs func Parse(raw string) Links { var links Links // One chunk: ; rel="foo" for _, chunk := range strings.Split(raw, ",") { link := Link{URL: "", Rel: "", Params: make(map[string]string)} // Figure out what each piece of the chunk is for _, piece := range strings.Split(chunk, ";") { piece = strings.Trim(piece, " ") if piece == "" { continue } // URL if piece[0] == '<' && piece[len(piece)-1] == '>' { link.URL = strings.Trim(piece, "<>") continue } // Params key, val := parseParam(piece) if key == "" { continue } // Special case for rel if strings.ToLower(key) == "rel" { link.Rel = val } else { link.Params[key] = val } } if link.URL != "" { links = append(links, link) } } return links } // ParseMultiple is like Parse, but accepts a slice of headers // rather than just one header string func ParseMultiple(headers []string) Links { links := make(Links, 0) for _, header := range headers { links = append(links, Parse(header)...) } return links } // parseParam takes a raw param in the form key="val" and // returns the key and value as seperate strings func parseParam(raw string) (key, val string) { parts := strings.SplitN(raw, "=", 2) if len(parts) == 1 { return parts[0], "" } if len(parts) != 2 { return "", "" } key = parts[0] val = strings.Trim(parts[1], "\"") return key, val }