Compare commits
29 Commits
darabonba2
...
timeout
| Author | SHA1 | Date | |
|---|---|---|---|
| cdfd54484c | |||
| af5f206bd8 | |||
| 5c6eb77b42 | |||
| 0f6d084c67 | |||
| df04276c08 | |||
| 622f315135 | |||
| 31de98dc53 | |||
| f93724f865 | |||
| fdbb29ef15 | |||
| a4d8c8f7f5 | |||
| 4820a881e8 | |||
| 6d4a89b84e | |||
| a17e6d71ba | |||
| 5379d4dfbb | |||
| 027c7539de | |||
| 6192dca562 | |||
| c032b7c3e5 | |||
| 171239d819 | |||
| 4f3624270b | |||
| 8d3b5385f0 | |||
| cf679a8bdd | |||
| 6af7becd53 | |||
| eb3f1e523f | |||
| b59ecf51be | |||
| 39fa3715e4 | |||
| 1a7a3b600b | |||
| 89d6a19474 | |||
| b8dbae11d9 | |||
| 8e3b4252f0 |
@@ -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
@@ -2,4 +2,5 @@ coverage.html
|
||||
coverage.txt
|
||||
coverage.out
|
||||
.DS_Store
|
||||
.history/
|
||||
.history/
|
||||
vendor/
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+1172
File diff suppressed because it is too large
Load Diff
+1579
File diff suppressed because it is too large
Load Diff
+162
@@ -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
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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)
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+49
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+373
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+215
@@ -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)
|
||||
}
|
||||
}()
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
+491
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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
|
||||
}
|
||||
@@ -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
Reference in New Issue
Block a user