v1.0.0: here we go again

This commit is contained in:
2026-01-10 17:06:25 +04:00
parent 25d4762a3c
commit 2d7ba34cb8
68 changed files with 6805 additions and 4548 deletions

View File

@@ -0,0 +1,46 @@
package types
import (
"path/filepath"
"strings"
)
type ConfigFileType string
const (
ConfigFileTypeUnknown ConfigFileType = "unknown"
ConfigFileTypeYAML ConfigFileType = "yaml/yml"
)
type ConfigFile struct {
path string
_type ConfigFileType
}
func (configFile ConfigFile) Path() string {
return configFile.path
}
func (configFile ConfigFile) Type() ConfigFileType {
return configFile._type
}
func ParseConfigFile(configFileRaw string) *ConfigFile {
// TODO: Improve file type detection
// (e.g., use magic bytes or content inspection instead of relying solely on file extension)
configFileParsed := &ConfigFile{
path: configFileRaw,
}
configFileExtension, _ := strings.CutPrefix(filepath.Ext(configFileRaw), ".")
switch strings.ToLower(configFileExtension) {
case "yml", "yaml":
configFileParsed._type = ConfigFileTypeYAML
default:
configFileParsed._type = ConfigFileTypeUnknown
}
return configFileParsed
}

40
internal/types/cookie.go Normal file
View File

@@ -0,0 +1,40 @@
package types
import "strings"
type Cookie KeyValue[string, []string]
type Cookies []Cookie
func (cookies Cookies) GetValue(key string) *[]string {
for i := range cookies {
if cookies[i].Key == key {
return &cookies[i].Value
}
}
return nil
}
func (cookies *Cookies) Append(cookie ...Cookie) {
for _, c := range cookie {
if item := cookies.GetValue(c.Key); item != nil {
*item = append(*item, c.Value...)
} else {
*cookies = append(*cookies, c)
}
}
}
func (cookies *Cookies) Parse(rawValues ...string) {
for _, rawValue := range rawValues {
cookies.Append(*ParseCookie(rawValue))
}
}
func ParseCookie(rawValue string) *Cookie {
parts := strings.SplitN(rawValue, "=", 2)
if len(parts) == 1 {
return &Cookie{Key: parts[0], Value: []string{""}}
}
return &Cookie{Key: parts[0], Value: []string{parts[1]}}
}

189
internal/types/errors.go Normal file
View File

@@ -0,0 +1,189 @@
package types
import (
"errors"
"fmt"
"strings"
)
var (
// General
ErrNoError = errors.New("no error (internal)")
// CLI
ErrCLINoArgs = errors.New("CLI expects arguments but received none")
)
// ======================================== General ========================================
type FieldParseError struct {
Field string
Value string
Err error
}
func NewFieldParseError(field string, value string, err error) FieldParseError {
if err == nil {
err = ErrNoError
}
return FieldParseError{field, value, err}
}
func (e FieldParseError) Error() string {
return fmt.Sprintf("Field '%s' parse failed: %v", e.Field, e.Err)
}
func (e FieldParseError) Unwrap() error {
return e.Err
}
type FieldParseErrors struct {
Errors []FieldParseError
}
func NewFieldParseErrors(fieldParseErrors []FieldParseError) FieldParseErrors {
return FieldParseErrors{fieldParseErrors}
}
func (e FieldParseErrors) Error() string {
if len(e.Errors) == 0 {
return "No field parse errors"
}
if len(e.Errors) == 1 {
return e.Errors[0].Error()
}
var builder strings.Builder
for i, err := range e.Errors {
if i > 0 {
builder.WriteString("\n")
}
builder.WriteString(err.Error())
}
return builder.String()
}
type FieldValidationError struct {
Field string
Value string
Err error
}
func NewFieldValidationError(field string, value string, err error) FieldValidationError {
if err == nil {
err = ErrNoError
}
return FieldValidationError{field, value, err}
}
func (e FieldValidationError) Error() string {
return fmt.Sprintf("Field '%s' validation failed: %v", e.Field, e.Err)
}
func (e FieldValidationError) Unwrap() error {
return e.Err
}
type FieldValidationErrors struct {
Errors []FieldValidationError
}
func NewFieldValidationErrors(fieldValidationErrors []FieldValidationError) FieldValidationErrors {
return FieldValidationErrors{fieldValidationErrors}
}
func (e FieldValidationErrors) Error() string {
if len(e.Errors) == 0 {
return "No field validation errors"
}
if len(e.Errors) == 1 {
return e.Errors[0].Error()
}
var builder strings.Builder
for i, err := range e.Errors {
if i > 0 {
builder.WriteString("\n")
}
builder.WriteString(err.Error())
}
return builder.String()
}
type UnmarshalError struct {
error error
}
func NewUnmarshalError(err error) UnmarshalError {
if err == nil {
err = ErrNoError
}
return UnmarshalError{err}
}
func (e UnmarshalError) Error() string {
return "Unmarshal error: " + e.error.Error()
}
func (e UnmarshalError) Unwrap() error {
return e.error
}
// ======================================== CLI ========================================
type CLIUnexpectedArgsError struct {
Args []string
}
func NewCLIUnexpectedArgsError(args []string) CLIUnexpectedArgsError {
return CLIUnexpectedArgsError{args}
}
func (e CLIUnexpectedArgsError) Error() string {
return fmt.Sprintf("CLI received unexpected arguments: %v", strings.Join(e.Args, ","))
}
// ======================================== Config File ========================================
type ConfigFileReadError struct {
error error
}
func NewConfigFileReadError(err error) ConfigFileReadError {
if err == nil {
err = ErrNoError
}
return ConfigFileReadError{err}
}
func (e ConfigFileReadError) Error() string {
return "Config file read error: " + e.error.Error()
}
func (e ConfigFileReadError) Unwrap() error {
return e.error
}
// ======================================== Proxy ========================================
type ProxyDialError struct {
Proxy string
Err error
}
func NewProxyDialError(proxy string, err error) ProxyDialError {
if err == nil {
err = ErrNoError
}
return ProxyDialError{proxy, err}
}
func (e ProxyDialError) Error() string {
return "proxy \"" + e.Proxy + "\": " + e.Err.Error()
}
func (e ProxyDialError) Unwrap() error {
return e.Err
}

49
internal/types/header.go Normal file
View File

@@ -0,0 +1,49 @@
package types
import "strings"
type Header KeyValue[string, []string]
type Headers []Header
func (headers Headers) Has(key string) bool {
for i := range headers {
if headers[i].Key == key {
return true
}
}
return false
}
func (headers Headers) GetValue(key string) *[]string {
for i := range headers {
if headers[i].Key == key {
return &headers[i].Value
}
}
return nil
}
func (headers *Headers) Append(header ...Header) {
for _, h := range header {
if item := headers.GetValue(h.Key); item != nil {
*item = append(*item, h.Value...)
} else {
*headers = append(*headers, h)
}
}
}
func (headers *Headers) Parse(rawValues ...string) {
for _, rawValue := range rawValues {
headers.Append(*ParseHeader(rawValue))
}
}
func ParseHeader(rawValue string) *Header {
parts := strings.SplitN(rawValue, ": ", 2)
if len(parts) == 1 {
return &Header{Key: parts[0], Value: []string{""}}
}
return &Header{Key: parts[0], Value: []string{parts[1]}}
}

View File

@@ -0,0 +1,6 @@
package types
type KeyValue[K, V any] struct {
Key K
Value V
}

40
internal/types/param.go Normal file
View File

@@ -0,0 +1,40 @@
package types
import "strings"
type Param KeyValue[string, []string]
type Params []Param
func (params Params) GetValue(key string) *[]string {
for i := range params {
if params[i].Key == key {
return &params[i].Value
}
}
return nil
}
func (params *Params) Append(param ...Param) {
for _, p := range param {
if item := params.GetValue(p.Key); item != nil {
*item = append(*item, p.Value...)
} else {
*params = append(*params, p)
}
}
}
func (params *Params) Parse(rawValues ...string) {
for _, rawValue := range rawValues {
params.Append(*ParseParam(rawValue))
}
}
func ParseParam(rawValue string) *Param {
parts := strings.SplitN(rawValue, "=", 2)
if len(parts) == 1 {
return &Param{Key: parts[0], Value: []string{""}}
}
return &Param{Key: parts[0], Value: []string{parts[1]}}
}

38
internal/types/proxy.go Normal file
View File

@@ -0,0 +1,38 @@
package types
import (
"fmt"
"net/url"
)
type Proxy url.URL
func (proxy Proxy) String() string {
return (*url.URL)(&proxy).String()
}
type Proxies []Proxy
func (proxies *Proxies) Append(proxy ...Proxy) {
*proxies = append(*proxies, proxy...)
}
func (proxies *Proxies) Parse(rawValue string) error {
parsedProxy, err := ParseProxy(rawValue)
if err != nil {
return err
}
proxies.Append(*parsedProxy)
return nil
}
func ParseProxy(rawValue string) (*Proxy, error) {
urlParsed, err := url.Parse(rawValue)
if err != nil {
return nil, fmt.Errorf("failed to parse proxy URL: %w", err)
}
proxyParsed := Proxy(*urlParsed)
return &proxyParsed, nil
}