commit
a701a93b81
|
@ -0,0 +1,2 @@
|
||||||
|
.idea/
|
||||||
|
/playground*
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2021 John Olheiser
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -0,0 +1,17 @@
|
||||||
|
# go-playground
|
||||||
|
|
||||||
|
A library for interacting with the [Go playground](https://play.golang.org).
|
||||||
|
|
||||||
|
This library only supports the `share` endpoint of the playground.
|
||||||
|
|
||||||
|
If you want to format your code, use `go fmt`.
|
||||||
|
|
||||||
|
If you want to evaluate some code, consider using [yaegi](https://github.com/traefik/yaegi).
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
`go install git.jojodev.com/golang/go-playground/cmd/playground@main`
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
[MIT](LICENSE)
|
|
@ -0,0 +1,81 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"git.jojodev.com/golang/go-playground"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
contents, err := readStdin()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if contents == "" {
|
||||||
|
contents, err = readFile()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if contents == "" {
|
||||||
|
fmt.Println("did not receive any data from stdin or a file")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
code, err := playground.Share(context.Background(), contents)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("could not get share URL: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(code.URL())
|
||||||
|
}
|
||||||
|
|
||||||
|
func readStdin() (string, error) {
|
||||||
|
fi, err := os.Stdin.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("could not stat stdin: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if fi.Mode()&os.ModeNamedPipe != 0 {
|
||||||
|
contents, err := io.ReadAll(os.Stdin)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("could not read from stdin: %w", err)
|
||||||
|
}
|
||||||
|
return string(contents), nil
|
||||||
|
}
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readFile() (string, error) {
|
||||||
|
if len(os.Args) == 1 {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
path := os.Args[1]
|
||||||
|
fi, err := os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("could not stat %s: %v", path, err)
|
||||||
|
}
|
||||||
|
if fi.IsDir() {
|
||||||
|
return "", errors.New("can not share a directory")
|
||||||
|
}
|
||||||
|
if fi.Size() > 500000 {
|
||||||
|
return "", errors.New("this library limits the share payload to ~500kb")
|
||||||
|
}
|
||||||
|
|
||||||
|
contents, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("could not read file at %s: %w", path, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(contents), nil
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
package playground
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/md5"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var shareURL = "https://play.golang.org/share"
|
||||||
|
|
||||||
|
var cache = make(map[string]Code)
|
||||||
|
|
||||||
|
// Code is a playground code
|
||||||
|
type Code string
|
||||||
|
|
||||||
|
func (c Code) URL() string {
|
||||||
|
return fmt.Sprintf("https://play.golang.org/p/%s", c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Share creates a share link from text
|
||||||
|
func Share(ctx context.Context, contents string) (Code, error) {
|
||||||
|
return share(ctx, contents)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShareFile creates a share link from a file
|
||||||
|
func ShareFile(ctx context.Context, path string) (Code, error) {
|
||||||
|
stat, err := os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("could not stat file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if stat.IsDir() {
|
||||||
|
return "", fmt.Errorf("%s is a directory, not a file", path)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.HasSuffix(stat.Name(), ".go") {
|
||||||
|
return "", fmt.Errorf("%s is not a Go file", path)
|
||||||
|
}
|
||||||
|
|
||||||
|
fi, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("could not open file: %w", err)
|
||||||
|
}
|
||||||
|
defer fi.Close()
|
||||||
|
|
||||||
|
contents, err := io.ReadAll(fi)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("could not read file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return share(ctx, string(contents))
|
||||||
|
}
|
||||||
|
|
||||||
|
func share(ctx context.Context, contents string) (Code, error) {
|
||||||
|
hash := fmt.Sprintf("%x", md5.Sum([]byte(contents)))
|
||||||
|
if link, ok := cache[hash]; ok {
|
||||||
|
return link, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodPost, shareURL, strings.NewReader(contents))
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("could not create request: %w", err)
|
||||||
|
}
|
||||||
|
req.Header.Set("User-Agent", "git.jojodev.com/golang/go-playground")
|
||||||
|
|
||||||
|
res, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("could not create share link: %w", err)
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
|
||||||
|
b, err := io.ReadAll(res.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("could not read response body: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cache[hash] = Code(b)
|
||||||
|
return cache[hash], nil
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
package playground
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestShare(t *testing.T) {
|
||||||
|
var resp string
|
||||||
|
contents := "test"
|
||||||
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
resp += "test-"
|
||||||
|
_, _ = w.Write([]byte(resp))
|
||||||
|
}))
|
||||||
|
shareURL = server.URL
|
||||||
|
|
||||||
|
link, err := Share(context.Background(), contents)
|
||||||
|
if err != nil {
|
||||||
|
t.Log(err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.EqualFold(link, resp) {
|
||||||
|
t.Logf("Expected `%s` but got `%s`\n", resp, link)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
link2, err := Share(context.Background(), contents)
|
||||||
|
if err != nil {
|
||||||
|
t.Log(err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.EqualFold(link2, "test-") {
|
||||||
|
t.Logf("Expected `test-` but got `%s`\n", link2)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue