diff --git a/httpaccept.go b/httpaccept.go new file mode 100644 index 0000000..0dc85bc --- /dev/null +++ b/httpaccept.go @@ -0,0 +1,104 @@ +package httpaccept + +import ( + "sourcecode.social/reiver/go-httpaccept/mediarange" + + "strings" +) + +// HTTPAccept represents the values of the Accept headers of an HTTP request. +// +// Note that an HTTP request can have multople Accept headers. +type HTTPAccept struct { + mediaranges []mediarange.MediaRange +} + +// Create creates an HTTPAccept. +// +// For example: +// +// // Accept: application/activity+json +// accept := httpaccept.Create( +// mediarange.Create("application", "activity+json"), // application/activity+json +// ) +// +// // Accept: image/png,image/gif +// accept := httpaccept.Create( +// mediarange.Create("image", "png"), // image/png +// mediarange.Create("image", "gif"), // image/gif +// ) +func Create(mediaranges ...mediarange.MediaRange) HTTPAccept { + return HTTPAccept{ + mediaranges:mediaranges, + } +} + +// Negotiate figures out if there is an acceptable media-type, and if there is returns the "best" one. +// +// For example: +// +// var accept httpaccept.HTTPAccept +// +// // ... +// +// contentType, err := accept.Negotiate("text/gemini", "text/html", "application/activity+json") +func (receiver HTTPAccept) Negotiate(mediatypes ...string) (string, error) { + + var temp []string + + { + temp, mediatypes = mediatypes, nil + + for _, mediatype := range temp { + + a := strings.SplitN(mediatype, ";", 2) + if len(a) < 1 { + continue + } + + mediatypes = append(mediatypes, a[0]) + } + } + + for _, mediarange := range receiver.mediaranges { + + for _, mediatype := range mediatypes { + + if mediarange.Match(mediatype) { + return mediatype, nil + } + } + } + + return "", internalNotAcceptable{temp} +} + +// String returns the serialized HTTP Accept header. +// +// For example: +// +// // "Accept: text/html,application/xhtml+xml\r\n" +// s := httpaccept.Create(mediarange.Create("text", "html"), mediarange.Create("application","xhtml+xml")).String() +func (receiver HTTPAccept) String() string { + var mediaranges []mediarange.MediaRange = receiver.mediaranges + + if len(mediaranges) < 1 { + return "" + } + + var buffer strings.Builder + { + buffer.WriteString("Accept: ") + for i, mediarange := range receiver.mediaranges { + + if 0 != i { + buffer.WriteRune(',') + } + buffer.WriteString(mediarange.String()) + } + buffer.WriteString("\r\n") + + } + + return buffer.String() +} diff --git a/httpaccept_string_test.go b/httpaccept_string_test.go new file mode 100644 index 0000000..52546de --- /dev/null +++ b/httpaccept_string_test.go @@ -0,0 +1,61 @@ +package httpaccept_test + +import ( + "sourcecode.social/reiver/go-httpaccept" + "sourcecode.social/reiver/go-httpaccept/mediarange" + + "testing" +) + +func TestHTTPAccept_String(t *testing.T) { + + tests := []struct{ + HTTPAccept httpaccept.HTTPAccept + Expected string + }{ + { + HTTPAccept: httpaccept.Create(), + Expected: "", + }, + + + + { + HTTPAccept: httpaccept.Create(mediarange.Create("*", "*")), + Expected: "Accept: */*"+"\r\n", + }, + { + HTTPAccept: httpaccept.Create(mediarange.Create("text", "html"), mediarange.Create("*", "*")), + Expected: "Accept: text/html,*/*"+"\r\n", + }, + { + HTTPAccept: httpaccept.Create(mediarange.Create("text", "html"), mediarange.Create("application", "xhtml+xml"), mediarange.Create("*", "*")), + Expected: "Accept: text/html,application/xhtml+xml,*/*"+"\r\n", + }, + + + + { + HTTPAccept: httpaccept.Create(mediarange.Create("application", "activity+json")), + Expected: "Accept: application/activity+json"+"\r\n", + }, + { + HTTPAccept: httpaccept.Create(mediarange.Create("application", "activity+json"), mediarange.Create("application", "json")), + Expected: "Accept: application/activity+json,application/json"+"\r\n", + }, + } + + for testNumber, test := range tests { + + actual := test.HTTPAccept.String() + expected := test.Expected + + if expected != actual { + t.Errorf("For test #%d, the actual (serialized) stringer value is not what was expected.", testNumber) + t.Logf("HTTP-ACCEPT: %#v", test.HTTPAccept) + t.Logf("EXPECTED: %q", expected) + t.Logf("ACTUAL: %q", actual) + continue + } + } +}