commit ea253cbfb6ba391fc1cd6803fb75557be35575dd
Author: Milarin <milaring@proton.me>
Date:   Wed Sep 11 13:32:52 2024 +0200

    initial commit

diff --git a/do.go b/do.go
new file mode 100644
index 0000000..337333e
--- /dev/null
+++ b/do.go
@@ -0,0 +1,19 @@
+package ezhttp
+
+import (
+	"encoding/json"
+	"io"
+	"net/http"
+)
+
+func Do(req *http.Request) (*http.Response, error) {
+	return http.DefaultClient.Do(req)
+}
+
+func ParseJsonResponse[T any](r io.Reader) (*T, error) {
+	res := new(T)
+	if err := json.NewDecoder(r).Decode(res); err != nil {
+		return nil, err
+	}
+	return res, nil
+}
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..ebb50df
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,3 @@
+module git.milar.in/milarin/ezhttp
+
+go 1.23.0
diff --git a/request.go b/request.go
new file mode 100644
index 0000000..80cc5b2
--- /dev/null
+++ b/request.go
@@ -0,0 +1,193 @@
+package ezhttp
+
+import (
+	"bytes"
+	"encoding/base64"
+	"encoding/json"
+	"io"
+	"net/http"
+	urlpkg "net/url"
+	"strings"
+)
+
+type RequestModifier func(r *RequestBuilder) *RequestBuilder
+type ValueProvider[T any] func() T
+type AuthProvider ValueProvider[string]
+type RequestBodyProvider func() (io.Reader, string)
+
+func Request(mods ...RequestModifier) (*http.Request, error) {
+	r := NewRequestBuilder()
+	for _, mod := range mods {
+		r = mod(r)
+	}
+	return r.Build()
+}
+
+func Template(req *http.Request) RequestModifier {
+	return func(r *RequestBuilder) *RequestBuilder {
+		return NewRequestBuilderFromRequest(req)
+	}
+}
+
+func URL(url string) RequestModifier {
+	parsedUrl, err := urlpkg.Parse(url)
+	if err != nil {
+		panic(err)
+	}
+
+	return func(r *RequestBuilder) *RequestBuilder {
+		r.URL = parsedUrl
+		return r
+		//return Headers("Host", parsedUrl.Host)(r)
+	}
+}
+
+func Scheme(scheme string) RequestModifier {
+	return func(r *RequestBuilder) *RequestBuilder {
+		r.URL.Scheme = scheme
+		return r
+	}
+}
+
+func Host(host string) RequestModifier {
+	return func(r *RequestBuilder) *RequestBuilder {
+		r.URL.Host = host
+		return r
+	}
+}
+
+func Path(path string) RequestModifier {
+	return func(r *RequestBuilder) *RequestBuilder {
+		r.URL.Path = path
+		return r
+	}
+}
+
+func AppendPath(path ...string) RequestModifier {
+	return func(r *RequestBuilder) *RequestBuilder {
+		r.URL = r.URL.JoinPath(path...)
+		return r
+	}
+}
+
+func Fragment(fragment string) RequestModifier {
+	return func(r *RequestBuilder) *RequestBuilder {
+		r.URL.Fragment = fragment
+		return r
+	}
+}
+
+func Query(keyValuePairs ...string) RequestModifier {
+	if len(keyValuePairs)%2 != 0 {
+		panic("keyValuePairs must have an even length")
+	}
+
+	return func(r *RequestBuilder) *RequestBuilder {
+		q := r.URL.Query()
+		for i := 0; i < len(keyValuePairs)-1; i += 2 {
+			q.Add(keyValuePairs[i], keyValuePairs[i+1])
+		}
+		r.URL.RawQuery = q.Encode()
+		return r
+	}
+}
+
+func QueryMap(query map[string]string) RequestModifier {
+	return func(r *RequestBuilder) *RequestBuilder {
+		q := r.URL.Query()
+		for key, value := range query {
+			q.Add(key, value)
+		}
+		r.URL.RawQuery = q.Encode()
+		return r
+	}
+}
+
+func Method(method string) RequestModifier {
+	return func(r *RequestBuilder) *RequestBuilder {
+		r.Method = method
+		return r
+	}
+}
+
+func Headers(keyValuePairs ...string) RequestModifier {
+	if len(keyValuePairs)%2 != 0 {
+		panic("keyValuePairs must have an even length")
+	}
+
+	return func(r *RequestBuilder) *RequestBuilder {
+		for i := 0; i < len(keyValuePairs)-1; i += 2 {
+			r.Header.Add(keyValuePairs[i], keyValuePairs[i+1])
+		}
+		return r
+	}
+}
+
+func HeadersMap(headers map[string]string) RequestModifier {
+	return func(r *RequestBuilder) *RequestBuilder {
+		for key, value := range headers {
+			r.Header.Add(key, value)
+		}
+		return r
+	}
+}
+
+func Auth(auth AuthProvider) RequestModifier {
+	return Headers("Authentication", auth())
+}
+
+func Basic(username, password string) AuthProvider {
+	return func() string {
+		return "Basic " + base64.StdEncoding.EncodeToString([]byte(username+":"+password))
+	}
+}
+
+func Bearer(token string) AuthProvider {
+	return func() string {
+		return "Bearer " + token
+	}
+}
+
+func ContentType(contentType string) RequestModifier {
+	return Headers("Content-Type", contentType)
+}
+
+func Body(bodyProvider RequestBodyProvider) RequestModifier {
+	return func(r *RequestBuilder) *RequestBuilder {
+		body, contentType := bodyProvider()
+		r.Body = body
+		if contentType != "" && r.Header.Get("Content-Type") != "" {
+			r.Header.Set("Content-Type", contentType)
+		}
+		return r
+	}
+}
+
+func Reader(r io.Reader) RequestBodyProvider {
+	return func() (io.Reader, string) {
+		return r, "application/octet-stream"
+	}
+}
+
+func String(s string) RequestBodyProvider {
+	return func() (io.Reader, string) {
+		return strings.NewReader(s), "text/plain"
+	}
+}
+
+func Bytes(b []byte) RequestBodyProvider {
+	return func() (io.Reader, string) {
+		return bytes.NewReader(b), "application/octet-stream"
+	}
+}
+
+func JSON[T any](value T) RequestBodyProvider {
+	buf := bytes.NewBuffer(make([]byte, 0, 1024))
+	if err := json.NewEncoder(buf).Encode(value); err != nil {
+		panic(err)
+	}
+
+	return func() (io.Reader, string) {
+		return buf, "application/json"
+	}
+}
diff --git a/request_builder.go b/request_builder.go
new file mode 100644
index 0000000..3fbe140
--- /dev/null
+++ b/request_builder.go
@@ -0,0 +1,41 @@
+package ezhttp
+
+import (
+	"io"
+	"net/http"
+	urlpkg "net/url"
+)
+
+type RequestBuilder struct {
+	URL    *urlpkg.URL
+	Method string
+	Header http.Header
+	Body   io.Reader
+}
+
+func NewRequestBuilder() *RequestBuilder {
+	return &RequestBuilder{
+		URL:    &urlpkg.URL{},
+		Header: make(http.Header),
+		Body:   nil,
+	}
+}
+
+func NewRequestBuilderFromRequest(r *http.Request) *RequestBuilder {
+	return &RequestBuilder{
+		URL:    r.URL,
+		Method: r.Method,
+		Header: r.Header,
+		Body:   r.Body,
+	}
+}
+
+func (b *RequestBuilder) Build() (*http.Request, error) {
+	r, err := http.NewRequest(b.Method, b.URL.String(), b.Body)
+	if err != nil {
+		return nil, err
+	}
+
+	r.Header = b.Header
+	return r, nil
+}