Compare commits

...

29 Commits

Author SHA1 Message Date
yndu13 cdfd54484c fix: improve DNS robustness and refine timeout logic
- Fix DNS resolution failure by prioritizing CGO resolver (PreferGo: false)
  to stay consistent with system tools like curl when /etc/resolv.conf
  contains invalid nameservers.
- Fix logic bug in getLocalAddr where LocalAddr string was incorrectly
  cast to []byte instead of being parsed via net.ParseIP.
- Refactor HTTP timeout logic:
    * Set httpClient.Timeout to ConnectTimeout + ReadTimeout.
    * Set http.Transport.ResponseHeaderTimeout to ReadTimeout for
      granular control.
- Modernize net.Dialer by removing deprecated DualStack field (now
  enabled by default in Go 1.12+).
- Add unit tests for LocalAddr parsing, dial context, and timeout
  configuration in both tea and dara packages.
2026-01-06 17:13:59 +08:00
weeping af5f206bd8 fix socks5 proxy 2025-12-08 15:28:43 +08:00
weeping 5c6eb77b42 support IdleTimeout 2025-10-13 11:43:22 +08:00
weeping 0f6d084c67 fix validate 2025-09-11 18:13:59 +08:00
weeping df04276c08 add ReadAsSSEWithContext 2025-09-11 17:00:10 +08:00
weeping 622f315135 fix sse 2025-08-19 16:58:21 +08:00
peze 31de98dc53 add context 2025-07-23 20:05:57 +08:00
peze f93724f865 fix the stringify 2025-04-21 18:18:26 +08:00
peze fdbb29ef15 fix the toMap when input is basic map 2025-04-07 17:32:23 +08:00
peze a4d8c8f7f5 fix default condition 2025-04-07 17:32:23 +08:00
peze 4820a881e8 add retry options config 2025-04-07 17:32:23 +08:00
peze 6d4a89b84e add httpclient config in dara 2025-03-27 14:47:24 +08:00
nanhe a17e6d71ba feat: support custom httpClient 2025-03-26 19:26:04 +08:00
peze 5379d4dfbb fix the desc type 2025-03-25 21:07:01 +08:00
peze 027c7539de fix resp error 2025-03-25 20:44:49 +08:00
peze 6192dca562 fix arr append 2025-03-14 16:27:38 +08:00
peze c032b7c3e5 compatible with old err 2025-03-12 15:51:16 +08:00
peze 171239d819 compatible with old err 2025-03-11 21:08:20 +08:00
peze 4f3624270b add to writer 2025-03-11 21:08:20 +08:00
peze 8d3b5385f0 fix the readSSE 2025-02-25 11:37:57 +08:00
peze cf679a8bdd compatible with old sdk error 2025-02-17 16:44:27 +08:00
peze 6af7becd53 fix to reader 2025-02-12 20:43:19 +08:00
yndu13 eb3f1e523f Merge pull request #123 from alibabacloud-go/v2
V2
2025-02-12 16:19:18 +08:00
peze b59ecf51be add tea build 2025-02-12 16:14:35 +08:00
page 39fa3715e4 Merge branch 'master' into v2 2025-02-12 16:10:47 +08:00
peze 1a7a3b600b fix by compatible bug 2025-02-12 14:27:23 +08:00
peze 89d6a19474 upgrade by darabonba v2 2024-11-14 22:05:43 +08:00
dependabot[bot] b8dbae11d9 Bump golang.org/x/net from 0.22.0 to 0.26.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.22.0 to 0.26.0.
- [Commits](https://github.com/golang/net/compare/v0.22.0...v0.26.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-21 20:42:19 +08:00
dependabot[bot] 8e3b4252f0 Bump golang.org/x/net from 0.20.0 to 0.22.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.20.0 to 0.22.0.
- [Commits](https://github.com/golang/net/compare/v0.20.0...v0.22.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-22 13:53:16 +08:00
43 changed files with 10852 additions and 105 deletions
+3 -1
View File
@@ -24,13 +24,15 @@ jobs:
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Build Darabonba
run: go build ./dara
- name: Build Tea
run: go build ./tea
- name: Build Util
run: go build ./utils
- name: Test
run: go test -race -coverprofile=coverage.txt -covermode=atomic ./tea/... ./utils/...
run: go test -race -coverprofile=coverage.txt -covermode=atomic ./dara/... ./utils/... ./tea/...
- name: CodeCov
run: bash <(curl -s https://codecov.io/bash)
+2 -1
View File
@@ -2,4 +2,5 @@ coverage.html
coverage.txt
coverage.out
.DS_Store
.history/
.history/
vendor/
+2 -2
View File
@@ -1,8 +1,8 @@
all:
fmt:
go fmt ./
go fmt ./tea ./dara ./utils
test:
go test -race -coverprofile=coverage.txt -covermode=atomic ./tea ./utils
go test -race -coverprofile=coverage.txt -covermode=atomic ./tea ./utils ./dara
go tool cover -html=coverage.txt -o coverage.html
+439
View File
File diff suppressed because it is too large Load Diff
+597
View File
File diff suppressed because it is too large Load Diff
+1172
View File
File diff suppressed because it is too large Load Diff
+1579
View File
File diff suppressed because it is too large Load Diff
+162
View File
@@ -0,0 +1,162 @@
package dara
import (
"fmt"
"strings"
"time"
)
type Date struct {
date time.Time
}
func NewDate(dateInput string) (*Date, error) {
var t time.Time
var err error
// 解析输入时间,如果输入格式不对,返回错误
formats := []string{
"2006-01-02 15:04:05",
"2006-01-02 15:04:05.999999999 -0700 MST",
"2006-01-02T15:04:05-07:00",
"2006-01-02T15:04:05Z",
}
for _, format := range formats {
t, err = time.Parse(format, dateInput)
if err == nil {
return &Date{date: t}, nil
}
}
return nil, fmt.Errorf("unable to parse date: %v", dateInput)
}
func (t *Date) Format(layout string) string {
layout = strings.Replace(layout, "yyyy", "2006", 1)
layout = strings.Replace(layout, "MM", "01", 1)
layout = strings.Replace(layout, "dd", "02", 1)
// layout = strings.Replace(layout, "HH", "15", 1)
layout = strings.Replace(layout, "hh", "15", 1)
layout = strings.Replace(layout, "mm", "04", 1)
layout = strings.Replace(layout, "ss", "05", 1)
layout = strings.Replace(layout, "a", "PM", 1)
layout = strings.Replace(layout, "EEEE", "Monday", 1)
layout = strings.Replace(layout, "E", "Mon", 1)
return t.date.Format(layout)
}
func (t *Date) Unix() int64 {
return t.date.Unix()
}
func (t *Date) UTC() string {
return t.date.UTC().Format("2006-01-02 15:04:05.000000000 -0700 MST")
}
func (t *Date) Sub(amount int, unit string) *Date {
var duration time.Duration
switch unit {
case "second", "seconds":
duration = time.Duration(-amount) * time.Second
case "minute", "minutes":
duration = time.Duration(-amount) * time.Minute
case "hour", "hours":
duration = time.Duration(-amount) * time.Hour
case "day", "days":
duration = time.Duration(-amount) * 24 * time.Hour
case "week", "weeks":
duration = time.Duration(-amount) * 7 * 24 * time.Hour
case "month", "months":
return &Date{date: t.date.AddDate(0, -amount, 0)}
case "year", "years":
return &Date{date: t.date.AddDate(-amount, 0, 0)}
default:
return nil
}
newDate := t.date.Add(duration)
return &Date{date: newDate}
}
func (t *Date) Add(amount int, unit string) *Date {
var duration time.Duration
switch unit {
case "second", "seconds":
duration = time.Duration(amount) * time.Second
case "minute", "minutes":
duration = time.Duration(amount) * time.Minute
case "hour", "hours":
duration = time.Duration(amount) * time.Hour
case "day", "days":
duration = time.Duration(amount) * 24 * time.Hour
case "week", "weeks":
duration = time.Duration(amount) * 7 * 24 * time.Hour
case "month", "months":
return &Date{date: t.date.AddDate(0, amount, 0)}
case "year", "years":
return &Date{date: t.date.AddDate(amount, 0, 0)}
default:
return nil
}
newDate := t.date.Add(duration)
return &Date{date: newDate}
}
func (t *Date) Diff(amount string, diffDate *Date) int64 {
switch amount {
case "second", "seconds":
return int64(t.date.Sub(diffDate.date).Seconds())
case "minute", "minutes":
return int64(t.date.Sub(diffDate.date).Minutes())
case "hour", "hours":
return int64(t.date.Sub(diffDate.date).Hours())
case "day", "days":
return int64(t.date.Sub(diffDate.date).Hours() / 24)
case "week", "weeks":
return int64(t.date.Sub(diffDate.date).Hours() / (24 * 7))
case "month", "months":
return int64(diffDate.date.Year()*12 + int(diffDate.date.Month()) - (t.date.Year()*12 + int(t.date.Month())))
case "year", "years":
return int64(t.date.Year() - diffDate.date.Year())
default:
return 0
}
}
func (t *Date) Hour() int {
return t.date.Hour()
}
func (t *Date) Minute() int {
return t.date.Minute()
}
func (t *Date) Second() int {
return t.date.Second()
}
func (t *Date) Month() int {
return int(t.date.Month())
}
func (t *Date) Year() int {
return t.date.Year()
}
func (t *Date) DayOfMonth() int {
return t.date.Day()
}
func (t *Date) DayOfWeek() int {
weekday := int(t.date.Weekday())
if weekday == 0 {
return 7 // Sunday
}
return weekday
}
func (t *Date) WeekOfYear() int {
_, week := t.date.ISOWeek()
return week
}
+160
View File
@@ -0,0 +1,160 @@
package dara
import (
"testing"
"time"
)
func TestConstructWithNow(t *testing.T) {
date := &Date{date: time.Now()}
currentTime := time.Now()
if currentTime.Format("2006-01-02 15:04:05") != date.Format("yyyy-MM-dd hh:mm:ss") {
t.Errorf("Expected %v, got %v", currentTime.Format("2006-01-02 15:04:05"), date.Format("yyyy-MM-dd hh:mm:ss"))
}
}
func TestConstructWithDateTimeString(t *testing.T) {
datetime := "2023-03-01T12:00:00Z" // Use RFC3339 format
date, err := NewDate(datetime)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if datetime != date.Format("yyyy-MM-ddThh:mm:ssZ") {
t.Errorf("Expected %v, got %v", datetime, date.Format("yyyy-MM-ddThh:mm:ssZ"))
}
}
func TestConstructWithWrongType(t *testing.T) {
_, err := NewDate("20230301 12:00:00 +0000 UTC")
if err == nil {
t.Errorf("Expected error, but got nil")
}
}
func TestConstructWithUTC(t *testing.T) {
datetimeUTC := "2023-03-01T12:00:00Z"
dateWithUTC, err := NewDate(datetimeUTC)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
referenceDateTime, _ := time.Parse(time.RFC3339, datetimeUTC)
if referenceDateTime.Unix() != dateWithUTC.Unix() {
t.Errorf("Expected %v, got %v", referenceDateTime.Unix(), dateWithUTC.Unix())
}
formattedDateTime := dateWithUTC.UTC()
expectedFormattedDateTime := referenceDateTime.UTC().Format("2006-01-02 15:04:05.000000000 -0700 MST")
if formattedDateTime != expectedFormattedDateTime {
t.Errorf("Expected %v, got %v", expectedFormattedDateTime, formattedDateTime)
}
}
func TestFormat(t *testing.T) {
datetime := "2023-03-01T12:00:00Z"
date, _ := NewDate(datetime)
expected := "2023-03-01 12:00 PM"
if result := date.Format("yyyy-MM-dd hh:mm a"); result != expected {
t.Errorf("Expected %v, got %v", expected, result)
}
}
func TestUTC(t *testing.T) {
datetime := "2023-03-01T12:00:00+08:00"
date, _ := NewDate(datetime)
expected := "2023-03-01 04:00:00.000000000 +0000 UTC"
if result := date.UTC(); result != expected {
t.Errorf("Expected %v, got %v", expected, result)
}
}
func TestUnix(t *testing.T) {
datetime := "1970-01-01T00:00:00Z"
date, _ := NewDate(datetime)
if result := date.Unix(); result != 0 {
t.Errorf("Expected 0, got %v", result)
}
datetime = "2023-12-31T08:00:00+08:00"
date, _ = NewDate(datetime)
if result := date.Unix(); result != 1703980800 {
t.Errorf("Expected 1703980800, got %v", result)
}
}
func TestAddSub(t *testing.T) {
datetime := "2023-03-01T12:00:00Z"
date, _ := NewDate(datetime)
date = date.Add(1, "day")
expectedDate := time.Date(2023, 3, 2, 12, 0, 0, 0, time.UTC)
if date.date != expectedDate {
t.Errorf("Expected %v, got %v", expectedDate, date.date)
}
date = date.Sub(1, "day") // Subtract 1 day
expectedDate = time.Date(2023, 3, 1, 12, 0, 0, 0, time.UTC)
if date.date != expectedDate {
t.Errorf("Expected %v, got %v", expectedDate, date.date)
}
}
func TestDiff(t *testing.T) {
datetime1 := "2023-03-01T12:00:00Z"
datetime2 := "2023-04-01T12:00:00Z"
date1, _ := NewDate(datetime1)
date2, _ := NewDate(datetime2)
diffInSeconds := date1.Diff("seconds", date2)
if diffInSeconds != -31*24*60*60 {
t.Errorf("Expected %v, got %v", -31*24*60*60, diffInSeconds)
}
}
func TestHourMinuteSecond(t *testing.T) {
datetime := "2023-03-01T12:34:56Z"
date, _ := NewDate(datetime)
if result := date.Hour(); result != 12 {
t.Errorf("Expected 12, got %d", result)
}
if result := date.Minute(); result != 34 {
t.Errorf("Expected 34, got %d", result)
}
if result := date.Second(); result != 56 {
t.Errorf("Expected 56, got %d", result)
}
}
func TestMonthYearDay(t *testing.T) {
datetime := "2023-03-01T12:00:00Z"
date, _ := NewDate(datetime)
if result := date.Month(); result != 3 {
t.Errorf("Expected 3, got %d", result)
}
if result := date.Year(); result != 2023 {
t.Errorf("Expected 2023, got %d", result)
}
if result := date.DayOfMonth(); result != 1 {
t.Errorf("Expected 1, got %d", result)
}
}
func TestDayOfWeekWeekOfYear(t *testing.T) {
datetime := "2023-03-01 00:00:00"
date, _ := NewDate(datetime)
if result := date.DayOfWeek(); result != 3 {
t.Errorf("Expected 3, got %d", result)
}
if result := date.WeekOfYear(); result != 9 {
t.Errorf("Expected 9, got %d", result)
}
datetime1 := "2023-12-31T12:00:00Z"
date1, _ := NewDate(datetime1)
if result := date1.DayOfMonth(); result != 31 {
t.Errorf("Expected 31, got %d", result)
}
if result := date1.DayOfWeek(); result != 7 {
t.Errorf("Expected 7, got %d", result)
}
if result := date1.WeekOfYear(); result != 52 {
t.Errorf("Expected 52, got %d", result)
}
}
+206
View File
@@ -0,0 +1,206 @@
package dara
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"reflect"
"strconv"
"github.com/alibabacloud-go/tea/tea"
)
type BaseError interface {
error
GetName() *string
GetCode() *string
}
type ResponseError interface {
BaseError
GetRetryAfter() *int64
GetStatusCode() *int
GetAccessDeniedDetail() map[string]interface{}
GetDescription() *string
GetData() map[string]interface{}
}
// SDKError struct is used save error code and message
type SDKError struct {
BaseError
Code *string
Name *string
StatusCode *int
Message *string
Data *string
Stack *string
errMsg *string
Description *string
AccessDeniedDetail map[string]interface{}
}
// CastError is used for cast type fails
type CastError struct {
Message *string
}
func TeaSDKError(err error) error {
if(err == nil) {
return nil
}
if te, ok := err.(*SDKError); ok {
return tea.NewSDKError(map[string]interface{}{
"code": StringValue(te.Code),
"statusCode": IntValue(te.StatusCode),
"message": StringValue(te.Message),
"data": te.Data,
"description": StringValue(te.Description),
"accessDeniedDetail": te.AccessDeniedDetail,
})
}
if respErr, ok := err.(ResponseError); ok {
return tea.NewSDKError(map[string]interface{}{
"code": StringValue(respErr.GetCode()),
"statusCode": IntValue(respErr.GetStatusCode()),
"message": respErr.Error(),
"description": StringValue(respErr.GetDescription()),
"data": respErr.GetData(),
"accessDeniedDetail": respErr.GetAccessDeniedDetail(),
})
}
if baseErr, ok := err.(BaseError); ok {
return tea.NewSDKError(map[string]interface{}{
"code": StringValue(baseErr.GetCode()),
"message": baseErr.Error(),
})
}
return err
}
// NewSDKError is used for shortly create SDKError object
func NewSDKError(obj map[string]interface{}) *SDKError {
err := &SDKError{}
err.Name = String("BaseError")
if val, ok := obj["code"].(int); ok {
err.Code = String(strconv.Itoa(val))
} else if val, ok := obj["code"].(string); ok {
err.Code = String(val)
}
if obj["message"] != nil {
err.Message = String(obj["message"].(string))
}
if obj["name"] != nil {
}
if obj["description"] != nil {
err.Description = String(obj["description"].(string))
}
if detail := obj["accessDeniedDetail"]; detail != nil {
r := reflect.ValueOf(detail)
if r.Kind().String() == "map" {
res := make(map[string]interface{})
tmp := r.MapKeys()
for _, key := range tmp {
res[key.String()] = r.MapIndex(key).Interface()
}
err.AccessDeniedDetail = res
}
}
if data := obj["data"]; data != nil {
r := reflect.ValueOf(data)
if r.Kind().String() == "map" {
res := make(map[string]interface{})
tmp := r.MapKeys()
for _, key := range tmp {
res[key.String()] = r.MapIndex(key).Interface()
}
if statusCode := res["statusCode"]; statusCode != nil {
if code, ok := statusCode.(int); ok {
err.StatusCode = Int(code)
} else if tmp, ok := statusCode.(string); ok {
code, err_ := strconv.Atoi(tmp)
if err_ == nil {
err.StatusCode = Int(code)
}
} else if code, ok := statusCode.(*int); ok {
err.StatusCode = code
}
}
}
byt := bytes.NewBuffer([]byte{})
jsonEncoder := json.NewEncoder(byt)
jsonEncoder.SetEscapeHTML(false)
jsonEncoder.Encode(data)
err.Data = String(string(bytes.TrimSpace(byt.Bytes())))
}
if statusCode, ok := obj["statusCode"].(int); ok {
err.StatusCode = Int(statusCode)
} else if status, ok := obj["statusCode"].(string); ok {
statusCode, err_ := strconv.Atoi(status)
if err_ == nil {
err.StatusCode = Int(statusCode)
}
}
return err
}
func (err *SDKError) ErrorName() *string {
return err.Name
}
func (err *SDKError) ErrorMessage() *string {
return err.Message
}
func (err *SDKError) GetCode() *string {
return err.Code
}
// Set ErrMsg by msg
func (err *SDKError) SetErrMsg(msg string) {
err.errMsg = String(msg)
}
func (err *SDKError) Error() string {
if err.errMsg == nil {
str := fmt.Sprintf("SDKError:\n StatusCode: %d\n Code: %s\n Message: %s\n Data: %s\n",
IntValue(err.StatusCode), StringValue(err.Code), StringValue(err.Message), StringValue(err.Data))
err.SetErrMsg(str)
}
return StringValue(err.errMsg)
}
// Return message of CastError
func (err *CastError) Error() string {
return StringValue(err.Message)
}
func Retryable(err error) *bool {
if err == nil {
return Bool(false)
}
if realErr, ok := err.(*SDKError); ok {
if realErr.StatusCode == nil {
return Bool(false)
}
code := IntValue(realErr.StatusCode)
return Bool(code >= http.StatusInternalServerError)
}
return Bool(true)
}
// NewCastError is used for cast type fails
func NewCastError(message *string) (err error) {
return &CastError{
Message: message,
}
}
+146
View File
@@ -0,0 +1,146 @@
package dara
import (
"os"
)
// File struct to represent the file
type DaraFile struct {
path string
fileInfo os.FileInfo
file *os.File
position int64
}
// NewFile creates a new instance of File
func NewDaraFile(path string) *DaraFile {
return &DaraFile{
path: path,
position: 0,
}
}
// Path returns the path of the file
func (tf *DaraFile) Path() string {
return tf.path
}
// CreateTime returns the creation time of the file
func (tf *DaraFile) CreateTime() (*Date, error) {
if tf.fileInfo == nil {
var err error
tf.fileInfo, err = os.Stat(tf.path)
if err != nil {
return nil, err
}
}
return &Date{tf.fileInfo.ModTime()}, nil
}
// ModifyTime returns the modification time of the file
func (tf *DaraFile) ModifyTime() (*Date, error) {
if tf.fileInfo == nil {
var err error
tf.fileInfo, err = os.Stat(tf.path)
if err != nil {
return nil, err
}
}
return &Date{tf.fileInfo.ModTime()}, nil
}
// Length returns the size of the file
func (tf *DaraFile) Length() (int64, error) {
if tf.fileInfo == nil {
var err error
tf.fileInfo, err = os.Stat(tf.path)
if err != nil {
return 0, err
}
}
return tf.fileInfo.Size(), nil
}
// Read reads a specified number of bytes from the file
func (tf *DaraFile) Read(size int) ([]byte, error) {
if tf.file == nil {
file, err := os.OpenFile(tf.path, os.O_RDWR|os.O_CREATE, 0755)
if err != nil {
return nil, err
}
tf.file = file
}
fileInfo, err := tf.file.Stat()
if err != nil {
return nil, err
}
// 获取文件大小
fileSize := fileInfo.Size()
// 计算可以读取的实际大小
if tf.position >= fileSize {
return nil, nil // End of file reached
}
// 确保 size 不超过剩余文件大小
actualSize := size
if tf.position+int64(size) > fileSize {
actualSize = int(fileSize - tf.position)
}
buf := make([]byte, actualSize)
bytesRead, err := tf.file.ReadAt(buf, tf.position)
if err != nil {
return nil, err
}
tf.position += int64(bytesRead)
return buf[:bytesRead], nil
}
// Write writes data to the file
func (tf *DaraFile) Write(data []byte) error {
if tf.file == nil {
file, err := os.OpenFile(tf.path, os.O_RDWR|os.O_CREATE, 0755)
if err != nil {
return err
}
tf.file = file
}
_, err := tf.file.Write(data)
if err != nil {
return err
}
tf.fileInfo, err = os.Stat(tf.path) // Update fileInfo after write
return err
}
// Close closes the file
func (tf *DaraFile) Close() error {
if tf.file == nil {
return nil
}
return tf.file.Close()
}
// Exists checks if the file exists
func Exists(path string) (bool, error) {
_, err := os.Stat(path)
if os.IsNotExist(err) {
return false, nil
}
return err == nil, err
}
// CreateReadStream would typically return an os.File or similar
func CreateReadStream(path string) (*os.File, error) {
return os.Open(path)
}
// CreateWriteStream would typically return an os.File or similar
func CreateWriteStream(path string) (*os.File, error) {
return os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755)
}
+183
View File
@@ -0,0 +1,183 @@
package dara
import (
"io"
"io/ioutil"
"os"
"path/filepath"
"testing"
)
// TestNewDaraFile tests the NewDaraFile function
func TestNewDaraFile(t *testing.T) {
path := "testfile.txt"
tf := NewDaraFile(path)
if tf.Path() != path {
t.Errorf("Expected path to be %s, got %s", path, tf.Path())
}
}
// TestCreateTime tests the CreateTime method
func TestCreateTime(t *testing.T) {
path := "testfile.txt"
ioutil.WriteFile(path, []byte("test"), 0644) // 创建文件以确保它存在
defer os.Remove(path)
tf := NewDaraFile(path)
date, err := tf.CreateTime()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if date == nil {
t.Error("expected a valid TeaDate, got nil")
}
}
// TestModifyTime tests the ModifyTime method
func TestModifyTime(t *testing.T) {
path := "testfile.txt"
ioutil.WriteFile(path, []byte("test"), 0644)
defer os.Remove(path)
tf := NewDaraFile(path)
date, err := tf.ModifyTime()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if date == nil {
t.Error("expected a valid TeaDate, got nil")
}
}
// TestLength tests the Length method
func TestLength(t *testing.T) {
path := "testfile.txt"
content := []byte("Hello, World!")
ioutil.WriteFile(path, content, 0644)
defer os.Remove(path)
tf := NewDaraFile(path)
length, err := tf.Length()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if length != int64(len(content)) {
t.Errorf("expected length %d, got %d", len(content), length)
}
}
// TestRead tests the Read method
func TestRead(t *testing.T) {
path := "testfile.txt"
content := []byte("Hello, World!")
ioutil.WriteFile(path, content, 0644)
defer os.Remove(path)
tf := NewDaraFile(path)
data, err := tf.Read(5)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if string(data) != "Hello" {
t.Errorf("expected 'Hello', got '%s'", string(data))
}
// Read the rest of the file
data, err = tf.Read(10)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if string(data) != ", World!" {
t.Errorf("expected ', World!', got '%s'", string(data))
}
}
// TestWrite tests the Write method
func TestWrite(t *testing.T) {
path := "testfile.txt"
tf := NewDaraFile(path)
data := []byte("Hello, Write Test!")
err := tf.Write(data)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
// Validate the content of the file
readData, _ := ioutil.ReadFile(path)
if string(readData) != "Hello, Write Test!" {
t.Errorf("expected file content to be %s, got %s", string(data), string(readData))
}
}
// TestClose tests the Close method
func TestClose(t *testing.T) {
path := "testfile.txt"
tf := NewDaraFile(path)
err := tf.Close()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
}
// TestExists tests the Exists function
func TestExists(t *testing.T) {
path := "testfile.txt"
ioutil.WriteFile(path, []byte("test"), 0644)
defer os.Remove(path)
exists, err := Exists(path)
if err != nil || !exists {
t.Errorf("expected file to exist, got error: %v", err)
}
exists, err = Exists("nonexistent.txt")
if err != nil || exists {
t.Errorf("expected file to not exist, got error: %v", err)
}
}
func TestCreateReadWriteStream(t *testing.T) {
tempDir, err := ioutil.TempDir("", "example")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tempDir)
testFile := filepath.Join(tempDir, "test.txt")
testWFile := filepath.Join(tempDir, "test2.txt")
// Prepare the test file
originalContent := "Hello, World!"
if err := ioutil.WriteFile(testFile, []byte(originalContent), 0644); err != nil {
t.Fatalf("failed to write test file: %v", err)
}
// Test CreateReadStream
rs, err := CreateReadStream(testFile)
if err != nil {
t.Fatalf("failed to create read stream: %v", err)
}
defer rs.Close()
// Test CreateWriteStream
ws, err := CreateWriteStream(testWFile)
if err != nil {
t.Fatalf("failed to create write stream: %v", err)
}
defer ws.Close()
// Pipe data from read stream to write stream
if _, err := io.Copy(ws, rs); err != nil {
t.Fatalf("failed to copy data from read stream to write stream: %v", err)
}
// Read back the content to check if it's correct
data, err := ioutil.ReadFile(testWFile)
if err != nil {
t.Fatalf("failed to read back test file: %v", err)
}
if string(data) != originalContent {
t.Fatalf("expected %q but got %q", originalContent, string(data))
}
}
+177
View File
@@ -0,0 +1,177 @@
package dara
import (
"bytes"
"fmt"
"io"
"math/rand"
"net/url"
"strings"
)
type FileField struct {
Filename *string `json:"filename" xml:"filename" require:"true"`
ContentType *string `json:"content-type" xml:"content-type" require:"true"`
Content io.Reader `json:"content" xml:"content" require:"true"`
}
func (s *FileField) SetFilename(v string) *FileField {
s.Filename = &v
return s
}
func (s *FileField) SetContentType(v string) *FileField {
s.ContentType = &v
return s
}
func (s *FileField) SetContent(v io.Reader) *FileField {
s.Content = v
return s
}
type FileFormReader struct {
formFiles []*formFile
formField io.Reader
index int
streaming bool
ifField bool
}
type formFile struct {
StartField io.Reader
EndField io.Reader
File io.Reader
start bool
end bool
}
const numBytes = "1234567890"
func GetBoundary() string {
b := make([]byte, 14)
for i := range b {
b[i] = numBytes[rand.Intn(len(numBytes))]
}
return string(b)
}
func ToFileForm(body map[string]interface{}, boundary string) io.Reader {
out := bytes.NewBuffer(nil)
line := "--" + boundary + "\r\n"
forms := make(map[string]string)
files := make(map[string]map[string]interface{})
for key, value := range body {
switch value.(type) {
case *FileField:
if val, ok := value.(*FileField); ok {
out := make(map[string]interface{})
out["filename"] = StringValue(val.Filename)
out["content-type"] = StringValue(val.ContentType)
out["content"] = val.Content
files[key] = out
}
case map[string]interface{}:
if val, ok := value.(map[string]interface{}); ok {
files[key] = val
}
default:
forms[key] = fmt.Sprintf("%v", value)
}
}
for key, value := range forms {
if value != "" {
out.Write([]byte(line))
out.Write([]byte("Content-Disposition: form-data; name=\"" + key + "\"" + "\r\n\r\n"))
out.Write([]byte(value + "\r\n"))
}
}
formFiles := make([]*formFile, 0)
for key, value := range files {
var file io.Reader
start := line
start += "Content-Disposition: form-data; name=\"" + key + "\"; filename=\"" + value["filename"].(string) + "\"\r\n"
start += "Content-Type: " + value["content-type"].(string) + "\r\n\r\n"
if content, ok := value["content"].(io.Reader); ok {
file = content
} else {
file = strings.NewReader("")
}
formFile := &formFile{
File: file,
start: true,
StartField: strings.NewReader(start),
}
if len(files) == len(formFiles)+1 {
end := "\r\n\r\n--" + boundary + "--\r\n"
formFile.EndField = strings.NewReader(end)
} else {
formFile.EndField = strings.NewReader("\r\n\r\n")
}
formFiles = append(formFiles, formFile)
}
return &FileFormReader{
formFiles: formFiles,
formField: out,
ifField: true,
}
}
func (f *FileFormReader) Read(p []byte) (n int, err error) {
if f.ifField {
n, err = f.formField.Read(p)
if err != nil && err != io.EOF {
return n, err
} else if err == io.EOF {
err = nil
f.ifField = false
f.streaming = true
}
} else if f.streaming {
form := f.formFiles[f.index]
if form.start {
n, err = form.StartField.Read(p)
if err != nil && err != io.EOF {
return n, err
} else if err == io.EOF {
err = nil
form.start = false
}
} else if form.end {
n, err = form.EndField.Read(p)
if err != nil && err != io.EOF {
return n, err
} else if err == io.EOF {
f.index++
form.end = false
if f.index < len(f.formFiles) {
err = nil
}
}
} else {
n, err = form.File.Read(p)
if err != nil && err != io.EOF {
return n, err
} else if err == io.EOF {
err = nil
form.end = true
}
}
}
return n, err
}
func ToFormString(a map[string]interface{}) string {
if a == nil {
return ""
}
res := ""
urlEncoder := url.Values{}
for key, value := range a {
v := fmt.Sprintf("%v", value)
urlEncoder.Add(key, v)
}
res = urlEncoder.Encode()
return res
}
+64
View File
@@ -0,0 +1,64 @@
package dara
import (
"io/ioutil"
"strings"
"testing"
"github.com/alibabacloud-go/tea/utils"
)
func Test_ToFormString(t *testing.T) {
str := ToFormString(nil)
utils.AssertEqual(t, "", str)
a := map[string]interface{}{
"key1": "value1",
"key2": "value2",
}
str = ToFormString(a)
utils.AssertEqual(t, str, "key1=value1&key2=value2")
}
type TestForm struct {
Ak *string `json:"ak"`
File1 *FileField `json:"file1"`
File2 *FileField `json:"file2"`
}
func Test_ToFileForm(t *testing.T) {
file1 := new(FileField).
SetContent(strings.NewReader("ok")).
SetContentType("jpg").
SetFilename("a.jpg")
body := map[string]interface{}{
"ak": "accesskey",
"file1": file1,
}
res := ToFileForm(ToMap(body), "28802961715230")
byt, err := ioutil.ReadAll(res)
utils.AssertNil(t, err)
utils.AssertEqual(t, string(byt), "--28802961715230\r\nContent-Disposition: "+
"form-data; name=\"ak\"\r\n\r\naccesskey\r\n--28802961715230\r\nContent-Disposition: "+
"form-data; name=\"file1\"; filename=\"a.jpg\"\r\nContent-Type: jpg\r\n\r\nok\r\n\r\n--28802961715230--\r\n")
body1 := &TestForm{
Ak: String("accesskey"),
File1: &FileField{
Filename: String("a.jpg"),
ContentType: String("jpg"),
Content: strings.NewReader("ok"),
},
}
res = ToFileForm(ToMap(body1), "28802961715230")
byt, err = ioutil.ReadAll(res)
utils.AssertNil(t, err)
utils.AssertEqual(t, string(byt), "--28802961715230\r\nContent-Disposition: form-data; "+
"name=\"ak\"\r\n\r\naccesskey\r\n--28802961715230\r\nContent-Disposition: "+
"form-data; name=\"file1\"; filename=\"a.jpg\"\r\nContent-Type: jpg\r\n\r\n\r\n\r\n--28802961715230--\r\n")
}
func Test_GetBoundary(t *testing.T) {
bound := GetBoundary()
utils.AssertEqual(t, len(bound), 14)
}
+389
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+49
View File
@@ -0,0 +1,49 @@
package dara
import (
"reflect"
)
type Entry struct {
Key string
Value interface{}
}
// toFloat64 converts a numeric value to float64 for comparison
func Entries(m interface{}) []*Entry {
v := reflect.ValueOf(m)
if v.Kind() != reflect.Map {
panic("Entries: input must be a map")
}
entries := make([]*Entry, 0, v.Len())
for _, key := range v.MapKeys() {
// 确保 Key 是字符串类型
if key.Kind() != reflect.String {
panic("Entries: map keys must be of type string")
}
value := v.MapIndex(key)
entries = append(entries, &Entry{
Key: key.String(),
Value: value.Interface(),
})
}
return entries
}
func KeySet(m interface{}) []string {
v := reflect.ValueOf(m)
if v.Kind() != reflect.Map {
panic("KeySet: input must be a map")
}
keys := make([]string, 0, v.Len())
for _, key := range v.MapKeys() {
if key.Kind() != reflect.String {
panic("KeySet: map keys must be of type string")
}
keys = append(keys, key.String())
}
return keys
}
+59
View File
@@ -0,0 +1,59 @@
package dara
import (
"reflect"
"testing"
)
type Person struct {
Name string
Age int
}
func TestEntries(t *testing.T) {
// 定义一个包含多种类型的 map
type Person struct {
Name string
Age int
}
testMap := map[string]interface{}{
"one": 1,
"two": "two",
"three": &Person{Name: "Alice", Age: 30},
}
entries := Entries(testMap)
if len(entries) != 3 {
t.Errorf("expected %d entries, got %d", 3, len(entries))
}
for _, entry := range entries {
if !reflect.DeepEqual(entry.Value, testMap[entry.Key]) {
t.Errorf("expected entry %s to be %v, got %v", entry.Key, testMap[entry.Key], entry.Value)
}
}
}
func TestKeySet(t *testing.T) {
testMap := map[string]interface{}{
"one": 1,
"two": "two",
"three": &Person{Name: "Alice", Age: 30},
}
keys := KeySet(testMap)
str1, str2, str3 := "one", "two", "three"
expectedKeys := []*string{&str1, &str2, &str3}
if len(keys) != len(expectedKeys) {
t.Errorf("expected %d keys, got %d", len(expectedKeys), len(keys))
}
for _, key := range keys {
if !ArrContains(expectedKeys, key) {
t.Errorf("expected key %s to be in the array %v, but not", key, expectedKeys)
}
}
}
+45
View File
@@ -0,0 +1,45 @@
package dara
import (
"math"
"math/rand"
"reflect"
"time"
)
// Number is an interface that can be implemented by any numeric type
type Number interface{}
// toFloat64 converts a numeric value to float64 for comparison
func toFloat64(n Number) float64 {
v := reflect.ValueOf(n)
switch v.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return float64(v.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return float64(v.Uint())
case reflect.Float32, reflect.Float64:
return v.Float()
default:
panic("unsupported type")
}
}
// Floor returns the largest integer less than or equal to the input number as int
func Floor(n Number) int {
v := toFloat64(n)
floorValue := math.Floor(v)
return int(floorValue)
}
// Round returns the nearest integer to the input number as int
func Round(n Number) int {
v := toFloat64(n)
roundValue := math.Round(v)
return int(roundValue)
}
func Random() float64 {
rand.Seed(time.Now().UnixNano())
return rand.Float64()
}
+62
View File
@@ -0,0 +1,62 @@
package dara
import (
"testing"
)
// TestRandom tests the Random function to ensure it returns a value in the range [0.0, 1.0)
func TestRandom(t *testing.T) {
for i := 0; i < 100; i++ {
val := Random()
if val < 0.0 || val >= 1.0 {
t.Errorf("Random() = %v, want [0.0, 1.0)", val)
}
}
}
// TestFloor tests the Floor function with various numeric inputs
func TestFloor(t *testing.T) {
tests := []struct {
input Number
expected int
}{
{3.7, 3},
{-3.7, -4},
{0.9, 0},
{0.0, 0},
{-0.9, -1},
{int64(3), 3},
{int32(-3), -3},
}
for _, tt := range tests {
result := Floor(tt.input)
if result != tt.expected {
t.Errorf("Floor(%v) = %v, want %v", tt.input, result, tt.expected)
}
}
}
// TestRound tests the Round function with various numeric inputs
func TestRound(t *testing.T) {
tests := []struct {
input Number
expected int
}{
{3.7, 4},
{-3.7, -4},
{2.5, 3},
{2.4, 2},
{-2.5, -3},
{0.0, 0},
{int64(4), 4},
{int32(-4), -4},
}
for _, tt := range tests {
result := Round(tt.input)
if result != tt.expected {
t.Errorf("Round(%v) = %v, want %v", tt.input, result, tt.expected)
}
}
}
+588
View File
File diff suppressed because it is too large Load Diff
+485
View File
File diff suppressed because it is too large Load Diff
+373
View File
File diff suppressed because it is too large Load Diff
+619
View File
File diff suppressed because it is too large Load Diff
+215
View File
@@ -0,0 +1,215 @@
package dara
import (
"bufio"
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"strings"
)
type SSEEvent struct {
Id *string
Event *string
Data *string
Retry *int
}
func parseEvent(lines []string) *SSEEvent {
event := &SSEEvent{}
for _, line := range lines {
if strings.HasPrefix(line, "data:") {
var data string
if strings.HasPrefix(line, "data: ") {
data = strings.TrimPrefix(line, "data: ") + "\n"
} else {
data = strings.TrimPrefix(line, "data:") + "\n"
}
if event.Data == nil {
event.Data = new(string)
}
*event.Data += data
} else if strings.HasPrefix(line, "event:") {
var eventName string
if strings.HasPrefix(line, "event: ") {
eventName = strings.TrimPrefix(line, "event: ")
} else {
eventName = strings.TrimPrefix(line, "event:")
}
event.Event = &eventName
} else if strings.HasPrefix(line, "id:") {
var id string
if strings.HasPrefix(line, "id: ") {
id = strings.TrimPrefix(line, "id: ")
} else {
id = strings.TrimPrefix(line, "id:")
}
event.Id = &id
} else if strings.HasPrefix(line, "retry:") {
var retryStr string
if strings.HasPrefix(line, "retry: ") {
retryStr = strings.TrimPrefix(line, "retry: ")
} else {
retryStr = strings.TrimPrefix(line, "retry:")
}
var retry int
fmt.Sscanf(retryStr, "%d", &retry)
event.Retry = &retry
}
}
if event.Data != nil {
data := strings.TrimRight(*event.Data, "\n")
event.Data = &data
}
return event
}
func ReadAsBytes(body io.Reader) ([]byte, error) {
byt, err := ioutil.ReadAll(body)
if err != nil {
return nil, err
}
r, ok := body.(io.ReadCloser)
if ok {
r.Close()
}
return byt, nil
}
func ReadAsJSON(body io.Reader) (result interface{}, err error) {
byt, err := ioutil.ReadAll(body)
if err != nil {
return
}
if string(byt) == "" {
return
}
r, ok := body.(io.ReadCloser)
if ok {
r.Close()
}
d := json.NewDecoder(bytes.NewReader(byt))
d.UseNumber()
err = d.Decode(&result)
return
}
func ReadAsString(body io.Reader) (string, error) {
byt, err := ioutil.ReadAll(body)
if err != nil {
return "", err
}
r, ok := body.(io.ReadCloser)
if ok {
r.Close()
}
return string(byt), nil
}
func ReadAsSSE(body io.ReadCloser, eventChannel chan *SSEEvent, errorChannel chan error) {
go func() {
defer func() {
body.Close()
close(eventChannel)
}()
reader := bufio.NewReader(body)
var eventLines []string
for {
line, err := reader.ReadString('\n')
if err != nil {
if err == io.EOF {
// Handle the end of the stream and possibly pending event
if len(eventLines) > 0 {
event := parseEvent(eventLines)
eventChannel <- event
}
errorChannel <- nil
return
}
errorChannel <- err
return
}
line = strings.TrimRight(line, "\n")
if line == "" {
// End of an SSE event
if len(eventLines) > 0 {
event := parseEvent(eventLines)
eventChannel <- event
eventLines = []string{} // Reset for the next event
}
continue
}
eventLines = append(eventLines, line)
}
}()
}
func ReadAsSSEWithContext(ctx context.Context, body io.ReadCloser, eventChannel chan *SSEEvent, errorChannel chan error) {
go func() {
defer func() {
body.Close()
close(eventChannel)
}()
reader := bufio.NewReader(body)
var eventLines []string
for {
select {
case <-ctx.Done():
errorChannel <- ctx.Err()
return
default:
}
line, err := reader.ReadString('\n')
if err != nil {
if err == io.EOF {
// Handle the end of the stream and possibly pending event
if len(eventLines) > 0 {
event := parseEvent(eventLines)
select {
case eventChannel <- event:
case <-ctx.Done():
errorChannel <- ctx.Err()
return
}
}
errorChannel <- nil
return
}
errorChannel <- err
return
}
line = strings.TrimRight(line, "\n")
if line == "" {
// End of an SSE event
if len(eventLines) > 0 {
event := parseEvent(eventLines)
select {
case eventChannel <- event:
case <-ctx.Done():
errorChannel <- ctx.Err()
return
}
eventLines = []string{} // Reset for the next event
}
continue
}
eventLines = append(eventLines, line)
}
}()
}
+511
View File
File diff suppressed because it is too large Load Diff
+491
View File
File diff suppressed because it is too large Load Diff
+163
View File
@@ -0,0 +1,163 @@
package dara
import (
"testing"
"github.com/alibabacloud-go/tea/utils"
)
func Test_Trans(t *testing.T) {
str := String("tea")
strVal := StringValue(str)
utils.AssertEqual(t, "tea", strVal)
utils.AssertEqual(t, "", StringValue(nil))
strSlice := StringSlice([]string{"tea"})
strSliceVal := StringSliceValue(strSlice)
utils.AssertEqual(t, []string{"tea"}, strSliceVal)
utils.AssertNil(t, StringSlice(nil))
utils.AssertNil(t, StringSliceValue(nil))
b := Bool(true)
bVal := BoolValue(b)
utils.AssertEqual(t, true, bVal)
utils.AssertEqual(t, false, BoolValue(nil))
bSlice := BoolSlice([]bool{false})
bSliceVal := BoolSliceValue(bSlice)
utils.AssertEqual(t, []bool{false}, bSliceVal)
utils.AssertNil(t, BoolSlice(nil))
utils.AssertNil(t, BoolSliceValue(nil))
f64 := Float64(2.00)
f64Val := Float64Value(f64)
utils.AssertEqual(t, float64(2.00), f64Val)
utils.AssertEqual(t, float64(0), Float64Value(nil))
f32 := Float32(2.00)
f32Val := Float32Value(f32)
utils.AssertEqual(t, float32(2.00), f32Val)
utils.AssertEqual(t, float32(0), Float32Value(nil))
f64Slice := Float64Slice([]float64{2.00})
f64SliceVal := Float64ValueSlice(f64Slice)
utils.AssertEqual(t, []float64{2.00}, f64SliceVal)
utils.AssertNil(t, Float64Slice(nil))
utils.AssertNil(t, Float64ValueSlice(nil))
f32Slice := Float32Slice([]float32{2.00})
f32SliceVal := Float32ValueSlice(f32Slice)
utils.AssertEqual(t, []float32{2.00}, f32SliceVal)
utils.AssertNil(t, Float32Slice(nil))
utils.AssertNil(t, Float32ValueSlice(nil))
i := Int(1)
iVal := IntValue(i)
utils.AssertEqual(t, 1, iVal)
utils.AssertEqual(t, 0, IntValue(nil))
i8 := Int8(int8(1))
i8Val := Int8Value(i8)
utils.AssertEqual(t, int8(1), i8Val)
utils.AssertEqual(t, int8(0), Int8Value(nil))
i16 := Int16(int16(1))
i16Val := Int16Value(i16)
utils.AssertEqual(t, int16(1), i16Val)
utils.AssertEqual(t, int16(0), Int16Value(nil))
i32 := Int32(int32(1))
i32Val := Int32Value(i32)
utils.AssertEqual(t, int32(1), i32Val)
utils.AssertEqual(t, int32(0), Int32Value(nil))
i64 := Int64(int64(1))
i64Val := Int64Value(i64)
utils.AssertEqual(t, int64(1), i64Val)
utils.AssertEqual(t, int64(0), Int64Value(nil))
iSlice := IntSlice([]int{1})
iSliceVal := IntValueSlice(iSlice)
utils.AssertEqual(t, []int{1}, iSliceVal)
utils.AssertNil(t, IntSlice(nil))
utils.AssertNil(t, IntValueSlice(nil))
i8Slice := Int8Slice([]int8{1})
i8ValSlice := Int8ValueSlice(i8Slice)
utils.AssertEqual(t, []int8{1}, i8ValSlice)
utils.AssertNil(t, Int8Slice(nil))
utils.AssertNil(t, Int8ValueSlice(nil))
i16Slice := Int16Slice([]int16{1})
i16ValSlice := Int16ValueSlice(i16Slice)
utils.AssertEqual(t, []int16{1}, i16ValSlice)
utils.AssertNil(t, Int16Slice(nil))
utils.AssertNil(t, Int16ValueSlice(nil))
i32Slice := Int32Slice([]int32{1})
i32ValSlice := Int32ValueSlice(i32Slice)
utils.AssertEqual(t, []int32{1}, i32ValSlice)
utils.AssertNil(t, Int32Slice(nil))
utils.AssertNil(t, Int32ValueSlice(nil))
i64Slice := Int64Slice([]int64{1})
i64ValSlice := Int64ValueSlice(i64Slice)
utils.AssertEqual(t, []int64{1}, i64ValSlice)
utils.AssertNil(t, Int64Slice(nil))
utils.AssertNil(t, Int64ValueSlice(nil))
ui := Uint(1)
uiVal := UintValue(ui)
utils.AssertEqual(t, uint(1), uiVal)
utils.AssertEqual(t, uint(0), UintValue(nil))
ui8 := Uint8(uint8(1))
ui8Val := Uint8Value(ui8)
utils.AssertEqual(t, uint8(1), ui8Val)
utils.AssertEqual(t, uint8(0), Uint8Value(nil))
ui16 := Uint16(uint16(1))
ui16Val := Uint16Value(ui16)
utils.AssertEqual(t, uint16(1), ui16Val)
utils.AssertEqual(t, uint16(0), Uint16Value(nil))
ui32 := Uint32(uint32(1))
ui32Val := Uint32Value(ui32)
utils.AssertEqual(t, uint32(1), ui32Val)
utils.AssertEqual(t, uint32(0), Uint32Value(nil))
ui64 := Uint64(uint64(1))
ui64Val := Uint64Value(ui64)
utils.AssertEqual(t, uint64(1), ui64Val)
utils.AssertEqual(t, uint64(0), Uint64Value(nil))
uiSlice := UintSlice([]uint{1})
uiValSlice := UintValueSlice(uiSlice)
utils.AssertEqual(t, []uint{1}, uiValSlice)
utils.AssertNil(t, UintSlice(nil))
utils.AssertNil(t, UintValueSlice(nil))
ui8Slice := Uint8Slice([]uint8{1})
ui8ValSlice := Uint8ValueSlice(ui8Slice)
utils.AssertEqual(t, []uint8{1}, ui8ValSlice)
utils.AssertNil(t, Uint8Slice(nil))
utils.AssertNil(t, Uint8ValueSlice(nil))
ui16Slice := Uint16Slice([]uint16{1})
ui16ValSlice := Uint16ValueSlice(ui16Slice)
utils.AssertEqual(t, []uint16{1}, ui16ValSlice)
utils.AssertNil(t, Uint16Slice(nil))
utils.AssertNil(t, Uint16ValueSlice(nil))
ui32Slice := Uint32Slice([]uint32{1})
ui32ValSlice := Uint32ValueSlice(ui32Slice)
utils.AssertEqual(t, []uint32{1}, ui32ValSlice)
utils.AssertNil(t, Uint32Slice(nil))
utils.AssertNil(t, Uint32ValueSlice(nil))
ui64Slice := Uint64Slice([]uint64{1})
ui64ValSlice := Uint64ValueSlice(ui64Slice)
utils.AssertEqual(t, []uint64{1}, ui64ValSlice)
utils.AssertNil(t, Uint64Slice(nil))
utils.AssertNil(t, Uint64ValueSlice(nil))
}
+149
View File
@@ -0,0 +1,149 @@
package dara
import (
"fmt"
"net/url"
"strings"
)
// PortMap maps protocols to their corresponding ports.
var portMap = map[string]string{
"ftp": "21",
"gopher": "70",
"http": "80",
"https": "443",
"ws": "80",
"wss": "443",
}
// URL is a wrapper around the URL type.
type URL struct {
_url *url.URL
}
// NewURL constructs a new URL from a string.
func NewURL(str string) (*URL, error) {
parsedURL, err := url.Parse(str)
if err != nil {
return nil, err
}
return &URL{_url: parsedURL}, nil
}
// Path returns the path and query of the URL.
func (t *URL) Path() string {
if t._url.RawQuery == "" {
return t._url.Path
}
return t._url.Path + "?" + t._url.RawQuery
}
// Pathname returns the pathname of the URL.
func (t *URL) Pathname() string {
return t._url.Path
}
// Protocol returns the protocol of the URL.
func (t *URL) Protocol() string {
return strings.TrimSuffix(t._url.Scheme, ":")
}
// Hostname returns the hostname of the URL.
func (t *URL) Hostname() string {
return t._url.Hostname()
}
// Host returns the host (host:port) of the URL.
func (t *URL) Host() string {
return t._url.Host
}
// Port returns the port of the URL, or the default for the protocol if not specified.
func (t *URL) Port() string {
if p := t._url.Port(); p != "" {
return p
}
return portMap[t.Protocol()]
}
// Hash returns the hash of the URL without the #.
func (t *URL) Hash() string {
return strings.TrimPrefix(t._url.Fragment, "#")
}
// Search returns the search part of the URL without the ?.
func (t *URL) Search() string {
return strings.TrimPrefix(t._url.RawQuery, "?")
}
// Href returns the full URL.
func (t *URL) Href() string {
return t._url.String()
}
// Auth returns the username and password of the URL.
func (t *URL) Auth() string {
password := getPassword(t._url.User)
username := t._url.User.Username()
if username == "" && password == "" {
return ""
}
return fmt.Sprintf("%s:%s", username, password)
}
// getPassword retrieves the password from a URL.User.
func getPassword(user *url.Userinfo) string {
if password, ok := user.Password(); ok {
return password
}
return ""
}
// Parse constructs a new URL from a string.
func ParseURL(urlStr string) (*URL, error) {
return NewURL(urlStr)
}
// EncodeURL encodes a URL string.
func EncodeURL(urlStr string) string {
if urlStr == "" {
return ""
}
strs := strings.Split(urlStr, "/")
for i, v := range strs {
strs[i] = url.QueryEscape(v)
}
urlStr = strings.Join(strs, "/")
urlStr = strings.Replace(urlStr, "+", "%20", -1)
urlStr = strings.Replace(urlStr, "*", "%2A", -1)
urlStr = strings.Replace(urlStr, "%7E", "~", -1)
return urlStr
}
// PercentEncode encodes a string for use in URLs, replacing certain characters.
func PercentEncode(uri string) string {
if uri == "" {
return ""
}
uri = url.QueryEscape(uri)
uri = strings.Replace(uri, "+", "%20", -1)
uri = strings.Replace(uri, "*", "%2A", -1)
uri = strings.Replace(uri, "%7E", "~", -1)
return uri
}
// PathEncode encodes each segment of a path.
func PathEncode(path string) string {
if path == "" || path == "/" {
return path
}
strs := strings.Split(path, "/")
for i, v := range strs {
strs[i] = url.QueryEscape(v)
}
path = strings.Join(strs, "/")
path = strings.Replace(path, "+", "%20", -1)
path = strings.Replace(path, "*", "%2A", -1)
path = strings.Replace(path, "%7E", "~", -1)
return path
}
+255
View File
@@ -0,0 +1,255 @@
package dara
import (
"testing"
)
func TestNewURL(t *testing.T) {
tests := []struct {
urlString string
wantErr bool
}{
{"http://example.com", false},
{"ftp://user:pass@host:21/path", false},
{"://example.com", true}, // Invalid URL
}
for _, tt := range tests {
_, err := NewURL(tt.urlString)
if (err != nil) != tt.wantErr {
t.Errorf("NewURL(%q) error = %v, wantErr %v", tt.urlString, err, tt.wantErr)
}
}
}
func TestDaraURL_Path(t *testing.T) {
tests := []struct {
urlString string
want string
}{
{"http://example.com/path?query=1", "/path?query=1"},
{"https://example.com/", "/"},
}
for _, tt := range tests {
tu, _ := NewURL(tt.urlString)
if got := tu.Path(); got != tt.want {
t.Errorf("DaraURL.Path() = %v, want %v", got, tt.want)
}
}
}
func TestDaraURL_Pathname(t *testing.T) {
tests := []struct {
urlString string
want string
}{
{"http://example.com/path?query=1", "/path"},
{"https://example.com/another/path/", "/another/path/"},
}
for _, tt := range tests {
tu, _ := NewURL(tt.urlString)
if got := tu.Pathname(); got != tt.want {
t.Errorf("DaraURL.Pathname() = %v, want %v", got, tt.want)
}
}
}
func TestDaraURL_Protocol(t *testing.T) {
tests := []struct {
urlString string
want string
}{
{"http://example.com", "http"},
{"ftp://example.com", "ftp"},
}
for _, tt := range tests {
tu, _ := NewURL(tt.urlString)
if got := tu.Protocol(); got != tt.want {
t.Errorf("DaraURL.Protocol() = %v, want %v", got, tt.want)
}
}
}
func TestDaraURL_Hostname(t *testing.T) {
tests := []struct {
urlString string
want string
}{
{"http://example.com", "example.com"},
{"https://user@subdomain.example.com:443", "subdomain.example.com"},
}
for _, tt := range tests {
tu, _ := NewURL(tt.urlString)
if got := tu.Hostname(); got != tt.want {
t.Errorf("DaraURL.Hostname() = %v, want %v", got, tt.want)
}
}
}
func TestDaraURL_Host(t *testing.T) {
tests := []struct {
urlString string
want string
}{
{"http://example.com", "example.com"},
{"http://example.com:8080", "example.com:8080"},
}
for _, tt := range tests {
tu, _ := NewURL(tt.urlString)
if got := tu.Host(); got != tt.want {
t.Errorf("DaraURL.Host() = %v, want %v", got, tt.want)
}
}
}
func TestDaraURL_Port(t *testing.T) {
tests := []struct {
urlString string
want string
}{
{"http://example.com", "80"},
{"https://example.com", "443"},
{"ftp://example.com:21", "21"},
{"gopher://example.com", "70"},
{"ws://example.com", "80"},
{"wss://example.com", "443"},
{"http://example.com:8080", "8080"},
}
for _, tt := range tests {
tu, _ := NewURL(tt.urlString)
if got := tu.Port(); got != tt.want {
t.Errorf("DaraURL.Port() = %v, want %v", got, tt.want)
}
}
}
func TestDaraURL_Hash(t *testing.T) {
tests := []struct {
urlString string
want string
}{
{"http://example.com#section", "section"},
{"http://example.com", ""},
}
for _, tt := range tests {
tu, _ := NewURL(tt.urlString)
if got := tu.Hash(); got != tt.want {
t.Errorf("DaraURL.Hash() = %v, want %v", got, tt.want)
}
}
}
func TestDaraURL_Search(t *testing.T) {
tests := []struct {
urlString string
want string
}{
{"http://example.com?query=1", "query=1"},
{"http://example.com", ""},
}
for _, tt := range tests {
tu, _ := NewURL(tt.urlString)
if got := tu.Search(); got != tt.want {
t.Errorf("DaraURL.Search() = %v, want %v", got, tt.want)
}
}
}
func TestDaraURL_Href(t *testing.T) {
tests := []struct {
urlString string
want string
}{
{"http://example.com", "http://example.com"},
{"https://user:pass@host:443/path?query=1#section", "https://user:pass@host:443/path?query=1#section"},
}
for _, tt := range tests {
tu, _ := NewURL(tt.urlString)
if got := tu.Href(); got != tt.want {
t.Errorf("DaraURL.Href() = %v, want %v", got, tt.want)
}
}
}
func TestDaraURL_Auth(t *testing.T) {
tests := []struct {
urlString string
want string
}{
{"http://user:pass@example.com", "user:pass"},
{"http://user@example.com", "user:"},
{"http://example.com", ""},
}
for _, tt := range tests {
tu, _ := NewURL(tt.urlString)
if got := tu.Auth(); got != tt.want {
t.Errorf("DaraURL.Auth() = %v, want %v", got, tt.want)
}
}
}
func TestEncodeURL(t *testing.T) {
tests := []struct {
urlString string
want string
}{
{"hello world", "hello%20world"},
{"test*abcd++", "test%2Aabcd%2B%2B"},
{"", ""},
}
for _, tt := range tests {
got := EncodeURL(tt.urlString)
if got != tt.want {
t.Errorf("EncodeURL(%q) = %v, want %v", tt.urlString, got, tt.want)
}
}
}
func TestPercentEncode(t *testing.T) {
tests := []struct {
raw string
want string
}{
{"hello world", "hello%20world"},
{"hello*world", "hello%2Aworld"},
{"hello~world", "hello~world"},
{"", ""},
}
for _, tt := range tests {
got := PercentEncode(tt.raw)
if got != tt.want {
t.Errorf("PercentEncode(%q) = %v, want %v", tt.raw, got, tt.want)
}
}
}
func TestPathEncode(t *testing.T) {
tests := []struct {
path string
want string
}{
{"hello/world", "hello/world"},
{"hello world", "hello%20world"},
{"", ""},
{"/", "/"},
}
for _, tt := range tests {
got := PathEncode(tt.path)
if got != tt.want {
t.Errorf("PathEncode(%q) = %v, want %v", tt.path, got, tt.want)
}
}
}

Some files were not shown because too many files have changed in this diff Show More