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) }