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