Compare commits
73 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e6c4e6c568 | |||
| 9eb5a63923 | |||
| 027c7539de | |||
| 9b850cc440 | |||
| 6192dca562 | |||
| c032b7c3e5 | |||
| 171239d819 | |||
| 4f3624270b | |||
| 8d3b5385f0 | |||
| cf679a8bdd | |||
| 6af7becd53 | |||
| eb3f1e523f | |||
| b59ecf51be | |||
| 39fa3715e4 | |||
| 1a7a3b600b | |||
| 89d6a19474 | |||
| b8dbae11d9 | |||
| 8e3b4252f0 | |||
| bb7ae5fd3e | |||
| c1f8e32399 | |||
| d755295be2 | |||
| b81493c626 | |||
| 2b0e131d00 | |||
| e3922d2afb | |||
| 5ef2fcc54f | |||
| 121efa4413 | |||
| aa99f50b3f | |||
| cb3a42bc23 | |||
| 712238a5c4 | |||
| 07ca3b69ad | |||
| e158cc5dc5 | |||
| b46b464236 | |||
| 636d89faa7 | |||
| c4f78703d7 | |||
| 8a7c8512f8 | |||
| 02f393be3f | |||
| c87ce79386 | |||
| b9ace42a5b | |||
| 3a75a83be1 | |||
| af66437067 | |||
| e0a739a681 | |||
| d9ff2bfbeb | |||
| fc13b6ebee | |||
| 6355725204 | |||
| b6aa048c4a | |||
| 5da4c33a57 | |||
| 2a316b984d | |||
| fa780870a7 | |||
| 9144ca7f27 | |||
| 174725cf27 | |||
| 45df60e197 | |||
| ecd18128a4 | |||
| f16f5b0116 | |||
| 8291a17aca | |||
| 7ad16974b6 | |||
| 8918f7f891 | |||
| 29a23a75ba | |||
| d9c5d0857b | |||
| 852924e5df | |||
| 4c13582572 | |||
| d3a7dc916c | |||
| 8817d0e413 | |||
| da2886ab11 | |||
| 3618315947 | |||
| 28a4a7b309 | |||
| 4d8a89852b | |||
| 60574a9378 | |||
| 2a388c0108 | |||
| 268f040e48 | |||
| b56effc83d | |||
| f4dd763f32 | |||
| d369dc9ebb | |||
| 781d370215 |
@@ -0,0 +1,11 @@
|
||||
# To get started with Dependabot version updates, you'll need to specify which
|
||||
# package ecosystems to update and where the package manifests are located.
|
||||
# Please see the documentation for all configuration options:
|
||||
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "gomod" # See documentation for possible values
|
||||
directory: "/" # Location of package manifests
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
@@ -0,0 +1,38 @@
|
||||
name: Go CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
go: ["1.13", "1.14", "1.15", "1.16", "1.17", "1.18", "1.19", "1.20"]
|
||||
fail-fast: false
|
||||
|
||||
steps:
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{ matrix.go }}
|
||||
|
||||
- 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 ./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/
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.10.x
|
||||
- 1.11.x
|
||||
- 1.12.x
|
||||
|
||||
branches: # build only on these branches
|
||||
only:
|
||||
- master
|
||||
|
||||
install:
|
||||
- go get -u github.com/golang/dep/cmd/dep
|
||||
- dep ensure
|
||||
|
||||
notifications:
|
||||
webhooks: https://oapi.dingtalk.com/robot/send?access_token=096ed387df243a6d60835aadeccc47165f3813bc7cb81cdd0cfeadfd28e3acc1
|
||||
email: false
|
||||
on_success: change
|
||||
on_failure: always
|
||||
|
||||
script:
|
||||
- go vet ./tea/...
|
||||
- go vet ./utils/...
|
||||
- go test -race -coverprofile=coverage.txt -covermode=atomic ./tea/... ./utils/...
|
||||
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
Generated
-31
@@ -1,31 +0,0 @@
|
||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:95a5d3b703c5a42a670bae2643f87f9d1009051cf8657b628945fb4022ab4de0"
|
||||
name = "github.com/alibabacloud-go/debug"
|
||||
packages = ["debug"]
|
||||
pruneopts = "UT"
|
||||
revision = "9472017b5c6804c66e5d873fabd2a2a937b31e0b"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:3587bbb18e3d5299dc0020733e2839138afb0a29456ffd4073a3962bb9966556"
|
||||
name = "golang.org/x/net"
|
||||
packages = [
|
||||
"internal/socks",
|
||||
"proxy",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "72f939374954d0a1b2a1e27dcda950e2724dd5c1"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
input-imports = [
|
||||
"github.com/alibabacloud-go/debug/debug",
|
||||
"golang.org/x/net/proxy",
|
||||
]
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
# Gopkg.toml example
|
||||
#
|
||||
# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
|
||||
# for detailed Gopkg.toml documentation.
|
||||
#
|
||||
# required = ["github.com/user/thing/cmd/thing"]
|
||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project"
|
||||
# version = "1.0.0"
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project2"
|
||||
# branch = "dev"
|
||||
# source = "github.com/myfork/project2"
|
||||
#
|
||||
# [[override]]
|
||||
# name = "github.com/x/y"
|
||||
# version = "2.4.0"
|
||||
#
|
||||
# [prune]
|
||||
# non-go = false
|
||||
# go-tests = true
|
||||
# unused-packages = true
|
||||
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/alibabacloud-go/debug"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/net"
|
||||
|
||||
[prune]
|
||||
go-tests = true
|
||||
unused-packages = true
|
||||
@@ -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
|
||||
|
||||
+3
-3
@@ -2,11 +2,11 @@
|
||||
|
||||
<a href="https://badge.fury.io/gh/alibabacloud-go%2Ftea"><img src="https://badge.fury.io/gh/alibabacloud-go%2Ftea.svg" alt="Latest Stable Version"></a>
|
||||
<a href="https://codecov.io/gh/alibabacloud-go/tea"><img src="https://codecov.io/gh/alibabacloud-go/tea/branch/master/graph/badge.svg" alt="codecov"></a>
|
||||
<a href="https://travis-ci.org/alibabacloud-go/tea"><img src="https://travis-ci.org/alibabacloud-go/tea.svg?branch=master" alt="Travis Build Status"></a>
|
||||
[](https://github.com/alibabacloud-go/tea/actions/workflows/go.yml)
|
||||
|
||||
该项目用于支持TEA OpenAPI DSL。它是http请求的底层库.
|
||||
该项目用于支持 Darabonba OpenAPI DSL。它是http请求的底层库.
|
||||
|
||||
## 许可证
|
||||
[Apache-2.0](/LICENSE)
|
||||
|
||||
Copyright (c) 2009-present, Alibaba Cloud All rights reserved.
|
||||
Copyright (c) 2009-present, Alibaba Cloud All rights reserved.
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
<a href="https://badge.fury.io/gh/alibabacloud-go%2Ftea"><img src="https://badge.fury.io/gh/alibabacloud-go%2Ftea.svg" alt="Latest Stable Version"></a>
|
||||
<a href="https://codecov.io/gh/alibabacloud-go/tea"><img src="https://codecov.io/gh/alibabacloud-go/tea/branch/master/graph/badge.svg" alt="codecov"></a>
|
||||
<a href="https://travis-ci.org/alibabacloud-go/tea"><img src="https://travis-ci.org/alibabacloud-go/tea.svg?branch=master" alt="Travis Build Status"></a>
|
||||
[](https://github.com/alibabacloud-go/tea/actions/workflows/go.yml)
|
||||
|
||||
This project is used for support TEA OpenAPI DSL. It's a low-level library for http request.
|
||||
This project is used for support Darabonba OpenAPI DSL. It's a low-level library for http request.
|
||||
|
||||
## License
|
||||
[Apache-2.0](/LICENSE)
|
||||
|
||||
Copyright (c) 2009-present, Alibaba Cloud All rights reserved.
|
||||
Copyright (c) 2009-present, Alibaba Cloud All rights reserved.
|
||||
|
||||
+439
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+958
File diff suppressed because it is too large
Load Diff
+1197
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
+280
@@ -0,0 +1,280 @@
|
||||
package dara
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Model interface {
|
||||
Validate() error
|
||||
ToMap() map[string]interface{}
|
||||
copyWithouStream() Model
|
||||
}
|
||||
|
||||
func Validate(params interface{}) error {
|
||||
if params == nil {
|
||||
return nil
|
||||
}
|
||||
requestValue := reflect.ValueOf(params)
|
||||
if requestValue.IsNil() {
|
||||
return nil
|
||||
}
|
||||
err := validate(requestValue.Elem())
|
||||
return err
|
||||
}
|
||||
|
||||
// Verify whether the parameters meet the requirements
|
||||
func validate(dataValue reflect.Value) error {
|
||||
if strings.HasPrefix(dataValue.Type().String(), "*") { // Determines whether the input is a structure object or a pointer object
|
||||
if dataValue.IsNil() {
|
||||
return nil
|
||||
}
|
||||
dataValue = dataValue.Elem()
|
||||
}
|
||||
dataType := dataValue.Type()
|
||||
for i := 0; i < dataType.NumField(); i++ {
|
||||
field := dataType.Field(i)
|
||||
valueField := dataValue.Field(i)
|
||||
for _, value := range validateParams {
|
||||
err := validateParam(field, valueField, value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateParam(field reflect.StructField, valueField reflect.Value, tagName string) error {
|
||||
tag, containsTag := field.Tag.Lookup(tagName) // Take out the checked regular expression
|
||||
if containsTag && tagName == "require" {
|
||||
err := checkRequire(field, valueField)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if strings.HasPrefix(field.Type.String(), "[]") { // Verify the parameters of the array type
|
||||
err := validateSlice(field, valueField, containsTag, tag, tagName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if valueField.Kind() == reflect.Ptr { // Determines whether it is a pointer object
|
||||
err := validatePtr(field, valueField, containsTag, tag, tagName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateSlice(field reflect.StructField, valueField reflect.Value, containsregexpTag bool, tag, tagName string) error {
|
||||
if valueField.IsValid() && !valueField.IsNil() { // Determines whether the parameter has a value
|
||||
if containsregexpTag {
|
||||
if tagName == "maxItems" {
|
||||
err := checkMaxItems(field, valueField, tag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if tagName == "minItems" {
|
||||
err := checkMinItems(field, valueField, tag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for m := 0; m < valueField.Len(); m++ {
|
||||
elementValue := valueField.Index(m)
|
||||
if elementValue.Type().Kind() == reflect.Ptr { // Determines whether the child elements of an array are of a basic type
|
||||
err := validatePtr(field, elementValue, containsregexpTag, tag, tagName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validatePtr(field reflect.StructField, elementValue reflect.Value, containsregexpTag bool, tag, tagName string) error {
|
||||
if elementValue.IsNil() {
|
||||
return nil
|
||||
}
|
||||
if isFilterType(elementValue.Elem().Type().String(), basicTypes) {
|
||||
if containsregexpTag {
|
||||
if tagName == "pattern" {
|
||||
err := checkPattern(field, elementValue.Elem(), tag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if tagName == "maxLength" {
|
||||
err := checkMaxLength(field, elementValue.Elem(), tag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if tagName == "minLength" {
|
||||
err := checkMinLength(field, elementValue.Elem(), tag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if tagName == "maximum" {
|
||||
err := checkMaximum(field, elementValue.Elem(), tag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if tagName == "minimum" {
|
||||
err := checkMinimum(field, elementValue.Elem(), tag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err := validate(elementValue)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkRequire(field reflect.StructField, valueField reflect.Value) error {
|
||||
name, _ := field.Tag.Lookup("json")
|
||||
strs := strings.Split(name, ",")
|
||||
name = strs[0]
|
||||
if !valueField.IsNil() && valueField.IsValid() {
|
||||
return nil
|
||||
}
|
||||
return errors.New(name + " should be setted")
|
||||
}
|
||||
|
||||
func checkPattern(field reflect.StructField, valueField reflect.Value, tag string) error {
|
||||
if valueField.IsValid() && valueField.String() != "" {
|
||||
value := valueField.String()
|
||||
r, _ := regexp.Compile("^" + tag + "$")
|
||||
if match := r.MatchString(value); !match { // Determines whether the parameter value satisfies the regular expression or not, and throws an error
|
||||
return errors.New(value + " is not matched " + tag)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkMaxItems(field reflect.StructField, valueField reflect.Value, tag string) error {
|
||||
if valueField.IsValid() && valueField.String() != "" {
|
||||
maxItems, err := strconv.Atoi(tag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
length := valueField.Len()
|
||||
if maxItems < length {
|
||||
errMsg := fmt.Sprintf("The length of %s is %d which is more than %d", field.Name, length, maxItems)
|
||||
return errors.New(errMsg)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkMinItems(field reflect.StructField, valueField reflect.Value, tag string) error {
|
||||
if valueField.IsValid() {
|
||||
minItems, err := strconv.Atoi(tag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
length := valueField.Len()
|
||||
if minItems > length {
|
||||
errMsg := fmt.Sprintf("The length of %s is %d which is less than %d", field.Name, length, minItems)
|
||||
return errors.New(errMsg)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkMaxLength(field reflect.StructField, valueField reflect.Value, tag string) error {
|
||||
if valueField.IsValid() && valueField.String() != "" {
|
||||
maxLength, err := strconv.Atoi(tag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
length := valueField.Len()
|
||||
if valueField.Kind().String() == "string" {
|
||||
length = strings.Count(valueField.String(), "") - 1
|
||||
}
|
||||
if maxLength < length {
|
||||
errMsg := fmt.Sprintf("The length of %s is %d which is more than %d", field.Name, length, maxLength)
|
||||
return errors.New(errMsg)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkMinLength(field reflect.StructField, valueField reflect.Value, tag string) error {
|
||||
if valueField.IsValid() {
|
||||
minLength, err := strconv.Atoi(tag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
length := valueField.Len()
|
||||
if valueField.Kind().String() == "string" {
|
||||
length = strings.Count(valueField.String(), "") - 1
|
||||
}
|
||||
if minLength > length {
|
||||
errMsg := fmt.Sprintf("The length of %s is %d which is less than %d", field.Name, length, minLength)
|
||||
return errors.New(errMsg)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkMaximum(field reflect.StructField, valueField reflect.Value, tag string) error {
|
||||
if valueField.IsValid() && valueField.String() != "" {
|
||||
maximum, err := strconv.ParseFloat(tag, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
byt, _ := json.Marshal(valueField.Interface())
|
||||
num, err := strconv.ParseFloat(string(byt), 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maximum < num {
|
||||
errMsg := fmt.Sprintf("The size of %s is %f which is greater than %f", field.Name, num, maximum)
|
||||
return errors.New(errMsg)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkMinimum(field reflect.StructField, valueField reflect.Value, tag string) error {
|
||||
if valueField.IsValid() && valueField.String() != "" {
|
||||
minimum, err := strconv.ParseFloat(tag, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
byt, _ := json.Marshal(valueField.Interface())
|
||||
num, err := strconv.ParseFloat(string(byt), 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if minimum > num {
|
||||
errMsg := fmt.Sprintf("The size of %s is %f which is less than %f", field.Name, num, minimum)
|
||||
return errors.New(errMsg)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
+373
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+137
@@ -0,0 +1,137 @@
|
||||
package dara
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// 定义 Event 结构体
|
||||
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: ") {
|
||||
data := strings.TrimPrefix(line, "data: ") + "\n"
|
||||
if event.Data == nil {
|
||||
event.Data = new(string)
|
||||
}
|
||||
*event.Data += data
|
||||
} else if strings.HasPrefix(line, "event: ") {
|
||||
eventName := strings.TrimPrefix(line, "event: ")
|
||||
event.Event = &eventName
|
||||
} else if strings.HasPrefix(line, "id: ") {
|
||||
id := strings.TrimPrefix(line, "id: ")
|
||||
event.ID = &id
|
||||
} else if strings.HasPrefix(line, "retry: ") {
|
||||
var retry int
|
||||
fmt.Sscanf(strings.TrimPrefix(line, "retry: "), "%d", &retry)
|
||||
event.Retry = &retry
|
||||
}
|
||||
}
|
||||
// Remove last newline from data
|
||||
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)
|
||||
}
|
||||
}()
|
||||
|
||||
return
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user