go-httpaccept/mediarange/mediarange.go

197 lines
3.7 KiB
Go
Raw Permalink Normal View History

2023-08-11 19:08:28 +00:00
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)
}