This commit is contained in:
parent
ebd8c61956
commit
0d5e406f90
|
|
@ -3,8 +3,9 @@ package main
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/chzyer/readline"
|
||||
"log"
|
||||
|
||||
"github.com/chzyer/readline"
|
||||
"ucl.lmika.dev/repl"
|
||||
"ucl.lmika.dev/ucl"
|
||||
"ucl.lmika.dev/ucl/builtins"
|
||||
|
|
@ -27,6 +28,7 @@ func main() {
|
|||
ucl.WithModule(builtins.Lists()),
|
||||
ucl.WithModule(builtins.Time()),
|
||||
ucl.WithModule(builtins.Fns()),
|
||||
ucl.WithModule(builtins.URLs()),
|
||||
)
|
||||
ctx := context.Background()
|
||||
|
||||
|
|
|
|||
67
ucl/builtins/urls.go
Normal file
67
ucl/builtins/urls.go
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
package builtins
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"ucl.lmika.dev/ucl"
|
||||
)
|
||||
|
||||
func URLs() ucl.Module {
|
||||
return ucl.Module{
|
||||
Name: "urls",
|
||||
Builtins: map[string]ucl.BuiltinHandler{
|
||||
"fetch": urlsFetch,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func urlsFetch(ctx context.Context, args ucl.CallArgs) (any, error) {
|
||||
var url string
|
||||
|
||||
if err := args.Bind(&url); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if strings.HasPrefix(url, "http:") || strings.HasPrefix(url, "https:") {
|
||||
return urlFetchHTTP(ctx, url, args)
|
||||
}
|
||||
|
||||
return nil, errors.New("unsupported URL scheme")
|
||||
}
|
||||
|
||||
func urlFetchHTTP(ctx context.Context, url string, args ucl.CallArgs) (any, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return nil, fmt.Errorf("non-200 status code: %v", resp.StatusCode)
|
||||
}
|
||||
|
||||
// Do content negotiation
|
||||
contentType := resp.Header.Get("Content-Type")
|
||||
switch {
|
||||
case strings.HasPrefix(contentType, "text/"):
|
||||
bts, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO: honour encoding
|
||||
return string(bts), nil
|
||||
}
|
||||
|
||||
return nil, errors.New("unsupported content type: " + contentType)
|
||||
}
|
||||
37
ucl/builtins/urls_test.go
Normal file
37
ucl/builtins/urls_test.go
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
package builtins_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"ucl.lmika.dev/ucl"
|
||||
"ucl.lmika.dev/ucl/builtins"
|
||||
)
|
||||
|
||||
func TestURLs_Fetch_http(t *testing.T) {
|
||||
tests := []struct {
|
||||
desc string
|
||||
eval string
|
||||
want any
|
||||
wantErr bool
|
||||
}{
|
||||
{desc: "fetch 1", eval: `in (urls:fetch "https://www.example.com") "Example Domain"`, want: true},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.desc, func(t *testing.T) {
|
||||
inst := ucl.New(
|
||||
ucl.WithModule(builtins.Strs()),
|
||||
ucl.WithModule(builtins.URLs()),
|
||||
)
|
||||
res, err := inst.EvalString(context.Background(), tt.eval)
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tt.want, res)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue