197 lines
3.7 KiB
Go
197 lines
3.7 KiB
Go
|
package mediarange
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
errBadMediaRange = errors.New("bad media-range")
|
||
|
)
|
||
|
|
||
|
// MediaRange represents a "media-range" as defined by IETF RFC-2616 (Hypertext Transfer Protocol — HTTP/1.1) in section 14.1.
|
||
|
//
|
||
|
// Here are some sample media-ranges:
|
||
|
//
|
||
|
// */*
|
||
|
// audio/*
|
||
|
// image/*
|
||
|
// text/*
|
||
|
// video/*
|
||
|
// application/x-tar
|
||
|
// audio/wav
|
||
|
// image/gif
|
||
|
// image/png
|
||
|
// text/html
|
||
|
// text/plain
|
||
|
// video/mp4
|
||
|
//
|
||
|
// Note that some of these sample media-ranges look like media-types / mime-types:
|
||
|
//
|
||
|
// application/x-tar
|
||
|
// audio/wav
|
||
|
// image/gif
|
||
|
// image/png
|
||
|
// text/html
|
||
|
// text/plain
|
||
|
// video/mp4
|
||
|
//
|
||
|
// But some of these sample media-ranges are not media-types / mime-types:
|
||
|
//
|
||
|
// */*
|
||
|
// audio/*
|
||
|
// image/*
|
||
|
// text/*
|
||
|
// video/*
|
||
|
//
|
||
|
// These later kinds of media-ranges (that do not look like media-types / mime-types) use the asterisk ("*") as a wild-card symbol.
|
||
|
// So "*/*" represents any media-type / mime-type.
|
||
|
// ANd "image/*" represents "image/gif", "image/png", etc.
|
||
|
type MediaRange struct {
|
||
|
typ string
|
||
|
subtyp string
|
||
|
}
|
||
|
|
||
|
// Create creates a MediaRange.
|
||
|
//
|
||
|
// For example:
|
||
|
//
|
||
|
// // audio/ogg
|
||
|
// mediarange.Create("audio", "ogg")
|
||
|
//
|
||
|
// // audio/*
|
||
|
// mediarange.Create("audio", "*")
|
||
|
//
|
||
|
// // */*
|
||
|
// mediarange.Create("*", "*")
|
||
|
//
|
||
|
// Note that an empty string for mediaRangeType or mediaRangeSubType will be treated as a wild-card (i.e., "*").
|
||
|
func Create(mediaRangeType string, mediaRangeSubType string) MediaRange {
|
||
|
mediaRangeType = strings.TrimSpace(mediaRangeType)
|
||
|
mediaRangeSubType = strings.TrimSpace(mediaRangeSubType)
|
||
|
|
||
|
if "*" == mediaRangeType {
|
||
|
mediaRangeType = ""
|
||
|
}
|
||
|
if "*" == mediaRangeSubType {
|
||
|
mediaRangeSubType = ""
|
||
|
}
|
||
|
|
||
|
return MediaRange{
|
||
|
typ: strings.ToLower(mediaRangeType),
|
||
|
subtyp: strings.ToLower(mediaRangeSubType),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Parse parses a string and returns a MediaRange.
|
||
|
//
|
||
|
// For example:
|
||
|
//
|
||
|
// mr, err := mediarange.Parse("application/activity+json")
|
||
|
func Parse(value string) (MediaRange, error) {
|
||
|
|
||
|
value = strings.TrimSpace(value)
|
||
|
|
||
|
parts := strings.SplitN(value, "/", 2)
|
||
|
if 2 != len(parts) {
|
||
|
return MediaRange{}, errBadMediaRange
|
||
|
}
|
||
|
|
||
|
var typ string = parts[0]
|
||
|
|
||
|
var subtyp string
|
||
|
{
|
||
|
a := strings.SplitN(parts[1], ";", 2)
|
||
|
{
|
||
|
length := len(a)
|
||
|
if 2 != length && 1 != length {
|
||
|
return MediaRange{}, errBadMediaRange
|
||
|
}
|
||
|
}
|
||
|
|
||
|
subtyp = a[0]
|
||
|
}
|
||
|
|
||
|
return Create(typ, subtyp), nil
|
||
|
}
|
||
|
|
||
|
// SubType returns the subtype of the media-range in its canonical form — i.e. lower-case and without spacing.
|
||
|
func (receiver MediaRange) SubType() string {
|
||
|
var typ string = receiver.typ
|
||
|
var subtyp string = receiver.subtyp
|
||
|
|
||
|
if "" == typ || "*" == typ {
|
||
|
return "*"
|
||
|
}
|
||
|
|
||
|
if "" == subtyp {
|
||
|
return "*"
|
||
|
}
|
||
|
|
||
|
return subtyp
|
||
|
}
|
||
|
|
||
|
// Type returns the type of the media-range in its canonical form — i.e. lower-case and without spacing.
|
||
|
func (receiver MediaRange) Type() string {
|
||
|
var typ string = receiver.typ
|
||
|
|
||
|
if "" == typ {
|
||
|
return "*"
|
||
|
}
|
||
|
|
||
|
return typ
|
||
|
}
|
||
|
|
||
|
func (receiver MediaRange) Match(value string) bool {
|
||
|
|
||
|
var typ string = receiver.Type()
|
||
|
if "*" == typ {
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
// Since media-types are a sub-set of media-ranges, we can do this.
|
||
|
var mediatype MediaRange
|
||
|
{
|
||
|
var err error
|
||
|
|
||
|
mediatype, err = Parse(value)
|
||
|
if nil != err {
|
||
|
return false
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if mediatype.typ != typ {
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
var subtyp string = receiver.SubType()
|
||
|
if "*" == subtyp {
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
if mediatype.subtyp != subtyp {
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
// String returns the serialed media-range.
|
||
|
//
|
||
|
// For example:
|
||
|
//
|
||
|
// "text/html" == mediarange.Create("text", "html").String()
|
||
|
func (receiver MediaRange) String() string {
|
||
|
var p []byte
|
||
|
|
||
|
var typ string = receiver.Type()
|
||
|
p = append(p, typ...)
|
||
|
|
||
|
p = append(p, '/')
|
||
|
|
||
|
var subtyp string = receiver.SubType()
|
||
|
p = append(p, subtyp...)
|
||
|
|
||
|
return string(p)
|
||
|
}
|