mirror of
https://github.com/aykhans/sarin.git
synced 2026-01-13 20:11:21 +00:00
v1.0.0: here we go again
This commit is contained in:
46
internal/types/config_file.go
Normal file
46
internal/types/config_file.go
Normal 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
40
internal/types/cookie.go
Normal 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
189
internal/types/errors.go
Normal 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
49
internal/types/header.go
Normal 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]}}
|
||||
}
|
||||
6
internal/types/key_value.go
Normal file
6
internal/types/key_value.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package types
|
||||
|
||||
type KeyValue[K, V any] struct {
|
||||
Key K
|
||||
Value V
|
||||
}
|
||||
40
internal/types/param.go
Normal file
40
internal/types/param.go
Normal 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 ¶ms[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
38
internal/types/proxy.go
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user