Newer
Older
minecraft-ui / internal / rest / content_type.go
package rest

import "strings"

// Enumeration of well-known content-types
const (
	ContentTypeApplicationJSON = "application/json"
)

// ContentType defines a media type and is composed of a type, a subtype (and
// optional suffix), and optional parameters.
type ContentType struct {
	Type       string
	Subtype    string // including tree
	Suffix     string
	Parameters map[string]string
}

// ParseContentType splits a content-type string into its parts.
//
// top-level type name / subtype name [ ; parameters ]
// top-level type name / [ tree. ] subtype name [ +suffix ] [ ; parameters ]
func ParseContentType(s string) ContentType {
	ct := ContentType{}

	// Top-level type name
	parts := strings.SplitN(s, "/", 2) // required
	if len(parts) != 2 {
		return ct
	}
	if parts[1] == "" {
		return ct
	}
	ct.Type = strings.TrimSpace(parts[0])

	// Subtype name
	sParts := strings.SplitN(parts[1], ";", -1) // optional
	if len(sParts) == 0 {
		return ct
	}
	if sParts[0] == "" {
		return ct
	}
	ssParts := strings.SplitN(sParts[0], "+", 2) // optional
	if len(ssParts) == 0 {
		return ct
	}
	if ssParts[0] == "" {
		return ct
	}
	ct.Subtype = strings.TrimSpace(ssParts[0])

	// Optional suffix
	if len(ssParts) == 2 {
		ct.Suffix = strings.TrimSpace(ssParts[1])
	}

	// Optional parameters
	params := map[string]string{}
	for _, param := range sParts[1:] {
		pParts := strings.SplitN(param, "=", 2)
		if len(pParts) == 2 {
			key := strings.TrimSpace(pParts[0])
			params[key] = strings.TrimSpace(pParts[1])
		} else if len(pParts) == 1 {
			key := strings.TrimSpace(pParts[0])
			params[key] = ""
		}
	}
	if len(params) > 0 {
		ct.Parameters = params
	}

	return ct
}