aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSandro Jäckel <sandro.jaeckel@sap.com>2022-10-26 15:23:04 +0200
committerSandro Jäckel <sandro.jaeckel@sap.com>2022-10-28 12:55:16 +0200
commit8b38a040830109f19550e7329b82ec5caf76b321 (patch)
tree13ecc54219914102f69fb59e005a836a88f78f5c
parent4f67f4c0a59a850321b37a154efc9bde60539ca8 (diff)
downloadgo-schwift-8b38a040830109f19550e7329b82ec5caf76b321.tar.gz
Fix linting errors
-rw-r--r--account.go2
-rw-r--r--bulk.go12
-rw-r--r--container_iterator.go2
-rw-r--r--download.go7
-rw-r--r--field_time.go4
-rw-r--r--field_uint64.go4
-rw-r--r--gopherschwift/package.go3
-rw-r--r--largeobject.go5
-rw-r--r--largeobject_test.go8
-rw-r--r--object.go42
-rw-r--r--object_iterator.go4
-rw-r--r--request.go5
-rw-r--r--tests/account_test.go1
-rw-r--r--tests/bulk_upload_test.go4
-rw-r--r--tests/container_test.go2
-rw-r--r--tests/field_test.go4
-rw-r--r--tests/largeobject_test.go6
-rw-r--r--tests/object_test.go4
-rw-r--r--tests/shared_test.go16
-rwxr-xr-xutil/render_template.go4
20 files changed, 66 insertions, 73 deletions
diff --git a/account.go b/account.go
index 9db952e..e29561c 100644
--- a/account.go
+++ b/account.go
@@ -212,7 +212,7 @@ func (a *Account) Capabilities() (Capabilities, error) {
func (a *Account) RawCapabilities() ([]byte, error) {
//This method is the only one in Schwift that bypasses struct Request since
//the request URL is not below the endpoint URL.
- req, err := http.NewRequest("GET", a.baseURL+"info", nil)
+ req, err := http.NewRequest(http.MethodGet, a.baseURL+"info", http.NoBody)
if err != nil {
return nil, err
}
diff --git a/bulk.go b/bulk.go
index 3aa40db..1c4dd4a 100644
--- a/bulk.go
+++ b/bulk.go
@@ -142,7 +142,7 @@ func makeBulkObjectError(fullName string, statusCode int) BulkObjectError {
// The objects may be located in multiple containers, but they and the
// containers must all be located in the given account. (Otherwise,
// ErrAccountMismatch is returned.)
-func (a *Account) BulkDelete(objects []*Object, containers []*Container, opts *RequestOptions) (numDeleted int, numNotFound int, deleteError error) {
+func (a *Account) BulkDelete(objects []*Object, containers []*Container, opts *RequestOptions) (numDeleted, numNotFound int, deleteError error) {
//validate that all given objects are in this account
for _, obj := range objects {
if !a.IsEqualTo(obj.Container().Account()) {
@@ -202,12 +202,8 @@ func (a *Account) BulkDelete(objects []*Object, containers []*Container, opts *R
// Implementation of BulkDelete() for servers that *do not* support bulk
// deletion.
-func (a *Account) bulkDeleteSingle(objects []*Object, containers []*Container, opts *RequestOptions) (int, int, error) {
- var (
- numDeleted = 0
- numNotFound = 0
- errs []BulkObjectError
- )
+func (a *Account) bulkDeleteSingle(objects []*Object, containers []*Container, opts *RequestOptions) (numDeleted, numNotFound int, err error) {
+ var errs []BulkObjectError
handleSingleError := func(containerName, objectName string, err error) error {
if err == nil {
@@ -259,7 +255,7 @@ func (a *Account) bulkDeleteSingle(objects []*Object, containers []*Container, o
// Implementation of BulkDelete() for servers that *do* support bulk deletion.
// This function is called *after* chunking, so `len(names) <=
// account.Capabilities.BulkDelete.MaximumDeletesPerRequest`.
-func (a *Account) bulkDelete(names []string, opts *RequestOptions) (int, int, error) {
+func (a *Account) bulkDelete(names []string, opts *RequestOptions) (numDeleted, numNotFound int, err error) {
req := Request{
Method: "DELETE",
Body: strings.NewReader(strings.Join(names, "\n") + "\n"),
diff --git a/container_iterator.go b/container_iterator.go
index 6dd75c2..71f799d 100644
--- a/container_iterator.go
+++ b/container_iterator.go
@@ -126,7 +126,7 @@ func (i *ContainerIterator) NextPageDetailed(limit int) ([]ContainerInfo, error)
result[idx].LastModified, err = time.Parse(time.RFC3339Nano, data.LastModifiedStr+"Z")
if err != nil {
//this error is sufficiently obscure that we don't need to expose a type for it
- return nil, fmt.Errorf("Bad field containers[%d].last_modified: %s", idx, err.Error())
+ return nil, fmt.Errorf("bad field containers[%d].last_modified: %s", idx, err.Error())
}
}
diff --git a/download.go b/download.go
index 130df91..8367f3d 100644
--- a/download.go
+++ b/download.go
@@ -20,7 +20,6 @@ package schwift
import (
"io"
- "io/ioutil"
)
// DownloadedObject is returned by Object.Download(). It wraps the io.ReadCloser
@@ -31,7 +30,7 @@ import (
//
// //Do NOT do this!
// reader, err := obj.Download(nil).AsReadCloser()
-// bytes, err := ioutil.ReadAll(reader)
+// bytes, err := io.ReadAll(reader)
// err := reader.Close()
// str := string(bytes)
//
@@ -66,12 +65,12 @@ func (o DownloadedObject) AsByteSlice() ([]byte, error) {
if o.err != nil {
return nil, o.err
}
- slice, err := ioutil.ReadAll(o.r)
+ slice, err := io.ReadAll(o.r)
closeErr := o.r.Close()
if err == nil {
err = closeErr
}
- return slice, closeErr
+ return slice, err
}
// AsString collects the contents of this downloaded object into a string.
diff --git a/field_time.go b/field_time.go
index e5fe8cc..8c51f6b 100644
--- a/field_time.go
+++ b/field_time.go
@@ -162,9 +162,9 @@ func (f FieldUnixTimeReadonly) Exists() bool {
// Get returns the value for this header, or the zero value if there is no value
// (or if it is not a valid timestamp).
func (f FieldUnixTimeReadonly) Get() time.Time {
- return FieldUnixTime{f.h, f.k}.Get()
+ return FieldUnixTime(f).Get()
}
func (f FieldUnixTimeReadonly) validate() error {
- return FieldUnixTime{f.h, f.k}.validate()
+ return FieldUnixTime(f).validate()
}
diff --git a/field_uint64.go b/field_uint64.go
index d8e63a4..8e1a6c2 100644
--- a/field_uint64.go
+++ b/field_uint64.go
@@ -97,9 +97,9 @@ func (f FieldUint64Readonly) Exists() bool {
// Get returns the value for this header, or 0 if there is no value (or if it is
// not a valid uint64).
func (f FieldUint64Readonly) Get() uint64 {
- return FieldUint64{f.h, f.k}.Get()
+ return FieldUint64(f).Get()
}
func (f FieldUint64Readonly) validate() error {
- return FieldUint64{f.h, f.k}.validate()
+ return FieldUint64(f).validate()
}
diff --git a/gopherschwift/package.go b/gopherschwift/package.go
index adeee83..3c89074 100644
--- a/gopherschwift/package.go
+++ b/gopherschwift/package.go
@@ -41,7 +41,6 @@ package gopherschwift
import (
"io"
- "io/ioutil"
"net/http"
"github.com/gophercloud/gophercloud"
@@ -107,7 +106,7 @@ func (g *backend) do(req *http.Request, afterReauth bool) (*http.Response, error
//detect expired token
if resp.StatusCode == http.StatusUnauthorized && !afterReauth {
- _, err := io.Copy(ioutil.Discard, resp.Body)
+ _, err := io.Copy(io.Discard, resp.Body)
if err != nil {
return nil, err
}
diff --git a/largeobject.go b/largeobject.go
index 637c024..83deddf 100644
--- a/largeobject.go
+++ b/largeobject.go
@@ -20,7 +20,7 @@ package schwift
import (
"bytes"
- "crypto/md5"
+ "crypto/md5" //nolint:gosec // Etag uses md5
"encoding/base64"
"encoding/hex"
"encoding/json"
@@ -422,7 +422,6 @@ func (o *Object) AsNewLargeObject(sopts SegmentingOptions, topts *TruncateOption
}
case ErrNotLarge:
//not an error, continue down below
- err = nil
default:
return nil, err //unexpected error
}
@@ -686,7 +685,7 @@ func (lo *LargeObject) Append(contents io.Reader, segmentSizeBytes int64, opts *
tracker := lengthAndEtagTrackingReader{
Reader: segment,
- Hasher: md5.New(),
+ Hasher: md5.New(), //nolint:gosec // Etag uses md5
}
obj := lo.NextSegmentObject()
diff --git a/largeobject_test.go b/largeobject_test.go
index 724654a..e5b790b 100644
--- a/largeobject_test.go
+++ b/largeobject_test.go
@@ -20,7 +20,7 @@ package schwift
import (
"bytes"
- "io/ioutil"
+ "io"
"testing"
)
@@ -89,7 +89,7 @@ func TestSegmentingReader(t *testing.T) {
t.Errorf("expected segment %q, but NextSegment() returned nil", expected)
break
}
- actual, err := ioutil.ReadAll(segment)
+ actual, err := io.ReadAll(segment)
if err != nil {
t.Errorf("expected segment %q, but got read error %q", expected, err.Error())
break
@@ -101,8 +101,8 @@ func TestSegmentingReader(t *testing.T) {
segment := sr.NextSegment()
if segment != nil {
- actual, err := ioutil.ReadAll(segment)
- if err == nil {
+ actual, err := io.ReadAll(segment)
+ if err != nil {
t.Errorf("expected no more segments, but got segment producing read error %q", err.Error())
} else {
t.Errorf("expected no more segments, but got %q", string(actual))
diff --git a/object.go b/object.go
index 51ab3db..d9ecb8d 100644
--- a/object.go
+++ b/object.go
@@ -21,8 +21,8 @@ package schwift
import (
"bytes"
"crypto/hmac"
- "crypto/md5"
- "crypto/sha1"
+ "crypto/md5" //nolint:gosec // Etag uses md5
+ "crypto/sha1" //nolint:gosec // Used by swift
"encoding/hex"
"fmt"
"hash"
@@ -128,7 +128,7 @@ func (o *Object) fetchHeaders(opts *RequestOptions) (*ObjectHeaders, error) {
Options: opts,
//since Openstack LOVES to be inconsistent with everything (incl. itself),
//this returns 200 instead of 204
- ExpectStatusCodes: []int{200},
+ ExpectStatusCodes: []int{http.StatusOK},
DrainResponseBody: true,
}.Do(o.c.a.backend)
if err != nil {
@@ -151,7 +151,7 @@ func (o *Object) Update(headers ObjectHeaders, opts *RequestOptions) error {
ContainerName: o.c.name,
ObjectName: o.name,
Options: cloneRequestOptions(opts, headers.Headers),
- ExpectStatusCodes: []int{202},
+ ExpectStatusCodes: []int{http.StatusAccepted},
}.Do(o.c.a.backend)
if err == nil {
o.Invalidate()
@@ -223,11 +223,14 @@ func (o *Object) Upload(content io.Reader, opts *UploadOptions, ropts *RequestOp
var hasher hash.Hash
if !isManifestUpload {
- tryComputeEtag(content, hdr)
+ err := tryComputeEtag(content, hdr)
+ if err != nil {
+ return err
+ }
//could not compute Etag in advance -> need to check on the fly
if !hdr.Etag().Exists() {
- hasher = md5.New()
+ hasher = md5.New() //nolint:gosec // Etag uses md5
if content != nil {
content = io.TeeReader(content, hasher)
}
@@ -301,10 +304,11 @@ func tryComputeContentLength(content io.Reader) *uint64 {
return nil
}
-func tryComputeEtag(content io.Reader, headers ObjectHeaders) {
+//nolint:gosec // Etag uses md5
+func tryComputeEtag(content io.Reader, headers ObjectHeaders) error {
h := headers.Etag()
if h.Exists() {
- return
+ return nil
}
switch r := content.(type) {
case nil:
@@ -318,11 +322,19 @@ func tryComputeEtag(content io.Reader, headers ObjectHeaders) {
case io.ReadSeeker:
//bytes.Reader does not have such a method, but it is an io.Seeker, so we
//can read the entire thing and then seek back to where we started
- hash := md5.New()
- n, _ := io.Copy(hash, r)
- r.Seek(-n, io.SeekCurrent)
- h.Set(hex.EncodeToString(hash.Sum(nil)))
+ md5Hash := md5.New()
+ n, err := io.Copy(md5Hash, r)
+ if err != nil {
+ return err
+ }
+ _, err = r.Seek(-n, io.SeekCurrent)
+ if err != nil {
+ return err
+ }
+ h.Set(hex.EncodeToString(md5Hash.Sum(nil)))
}
+
+ return nil
}
// UploadFromWriter is a variant of Upload that can be used when the object's
@@ -400,7 +412,7 @@ func (o *Object) Delete(opts *DeleteOptions, ropts *RequestOptions) error {
ContainerName: o.c.name,
ObjectName: o.name,
Options: ropts,
- ExpectStatusCodes: []int{204},
+ ExpectStatusCodes: []int{http.StatusNoContent},
}.Do(o.c.a.backend)
if err == nil {
o.Invalidate()
@@ -439,7 +451,7 @@ func (o *Object) Download(opts *RequestOptions) DownloadedObject {
ContainerName: o.c.name,
ObjectName: o.name,
Options: opts,
- ExpectStatusCodes: []int{200},
+ ExpectStatusCodes: []int{http.StatusOK},
}.Do(o.c.a.backend)
var body io.ReadCloser
if err == nil {
@@ -490,7 +502,7 @@ func (o *Object) CopyTo(target *Object, opts *CopyOptions, ropts *RequestOptions
ContainerName: o.c.name,
ObjectName: o.name,
Options: ropts,
- ExpectStatusCodes: []int{201},
+ ExpectStatusCodes: []int{http.StatusCreated},
DrainResponseBody: true,
}.Do(o.c.a.backend)
if err == nil {
diff --git a/object_iterator.go b/object_iterator.go
index dcb8443..64b5551 100644
--- a/object_iterator.go
+++ b/object_iterator.go
@@ -155,13 +155,13 @@ func (i *ObjectIterator) NextPageDetailed(limit int) ([]ObjectInfo, error) {
result[idx].LastModified, err = time.Parse(time.RFC3339Nano, data.LastModifiedStr+"Z")
if err != nil {
//this error is sufficiently obscure that we don't need to expose a type for it
- return nil, fmt.Errorf("Bad field objects[%d].last_modified: %s", idx, err.Error())
+ return nil, fmt.Errorf("bad field objects[%d].last_modified: %s", idx, err.Error())
}
if data.SymlinkPath != "" {
match := symlinkPathRx.FindStringSubmatch(data.SymlinkPath)
if match == nil {
//like above
- return nil, fmt.Errorf("Bad field objects[%d].symlink_path: %q", idx, data.SymlinkPath)
+ return nil, fmt.Errorf("bad field objects[%d].symlink_path: %q", idx, data.SymlinkPath)
}
a := i.Container.a
if a.Name() != match[1] {
diff --git a/request.go b/request.go
index b789161..2e78f00 100644
--- a/request.go
+++ b/request.go
@@ -21,7 +21,6 @@ package schwift
import (
"context"
"io"
- "io/ioutil"
"net/http"
"net/url"
"strings"
@@ -169,7 +168,7 @@ func (r Request) Do(backend Backend) (*http.Response, error) {
}
func drainResponseBody(r *http.Response) error {
- _, err := io.Copy(ioutil.Discard, r.Body)
+ _, err := io.Copy(io.Discard, r.Body)
if err != nil {
return err
}
@@ -177,7 +176,7 @@ func drainResponseBody(r *http.Response) error {
}
func collectResponseBody(r *http.Response) ([]byte, error) {
- buf, err := ioutil.ReadAll(r.Body)
+ buf, err := io.ReadAll(r.Body)
if err != nil {
return nil, err
}
diff --git a/tests/account_test.go b/tests/account_test.go
index f5925f0..e4572ea 100644
--- a/tests/account_test.go
+++ b/tests/account_test.go
@@ -89,6 +89,5 @@ func TestAccountMetadata(t *testing.T) {
}
expectString(t, hdr.Metadata().Get("schwift-test1"), "")
expectString(t, hdr.Metadata().Get("schwift-test2"), "changed")
-
})
}
diff --git a/tests/bulk_upload_test.go b/tests/bulk_upload_test.go
index 6cbae42..d810cf5 100644
--- a/tests/bulk_upload_test.go
+++ b/tests/bulk_upload_test.go
@@ -62,7 +62,7 @@ func TestBulkUploadArchiveError(t *testing.T) {
)
expectInt(t, n, 0)
expectError(t, err, "400 Bad Request: Invalid Tar File: truncated header")
- bulkErr := err.(schwift.BulkError)
+ bulkErr := err.(schwift.BulkError) //nolint:errcheck
expectInt(t, bulkErr.StatusCode, 400)
expectString(t, bulkErr.OverallError, "Invalid Tar File: truncated header")
expectInt(t, len(bulkErr.ObjectErrors), 0)
@@ -87,7 +87,7 @@ func TestBulkUploadObjectError(t *testing.T) {
)
expectInt(t, n, 1)
expectError(t, err, "400 Bad Request (+1 object errors)")
- bulkErr := err.(schwift.BulkError)
+ bulkErr := err.(schwift.BulkError) //nolint:errcheck
expectInt(t, len(bulkErr.ObjectErrors), 1)
expectString(t, bulkErr.ObjectErrors[0].ContainerName, c.Name())
expectInt(t, bulkErr.ObjectErrors[0].StatusCode, 400)
diff --git a/tests/container_test.go b/tests/container_test.go
index 0efc4e6..eed4906 100644
--- a/tests/container_test.go
+++ b/tests/container_test.go
@@ -63,7 +63,6 @@ func TestContainerLifecycle(t *testing.T) {
func TestContainerUpdate(t *testing.T) {
testWithContainer(t, func(c *schwift.Container) {
-
hdr, err := c.Headers()
expectSuccess(t, err)
expectBool(t, hdr.ObjectCount().Exists(), true)
@@ -80,7 +79,6 @@ func TestContainerUpdate(t *testing.T) {
expectSuccess(t, err)
expectUint64(t, hdr.BytesUsedQuota().Get(), 42)
expectUint64(t, hdr.ObjectCountQuota().Get(), 23)
-
})
}
diff --git a/tests/field_test.go b/tests/field_test.go
index c8dcf7f..cd27c99 100644
--- a/tests/field_test.go
+++ b/tests/field_test.go
@@ -70,7 +70,7 @@ func TestFieldTimestamp(t *testing.T) {
expectBool(t, hdr.CreatedAt().Exists(), true)
actual := float64(hdr.CreatedAt().Get().UnixNano()) / 1e9
- expected, _ := strconv.ParseFloat(hdr.Headers["X-Timestamp"], 64)
+ expected, _ := strconv.ParseFloat(hdr.Headers["X-Timestamp"], 64) //nolint:errcheck
expectFloat64(t, actual, expected)
})
@@ -100,7 +100,7 @@ func TestFieldHTTPTimestamp(t *testing.T) {
expectBool(t, hdr.UpdatedAt().Exists(), true)
actual := hdr.UpdatedAt().Get()
- expected, _ := http.ParseTime(hdr.Get("Last-Modified"))
+ expected, _ := http.ParseTime(hdr.Get("Last-Modified")) //nolint:errcheck
expectInt64(t, actual.Unix(), expected.Unix())
})
diff --git a/tests/largeobject_test.go b/tests/largeobject_test.go
index b63ccd7..ed61033 100644
--- a/tests/largeobject_test.go
+++ b/tests/largeobject_test.go
@@ -37,7 +37,7 @@ func TestLargeObjectsBasic(t *testing.T) {
testWithContainer(t, func(c *schwift.Container) {
foreachLargeObjectStrategy(func(strategy schwift.LargeObjectStrategy, strategyStr string) {
obj := c.Object(strategyStr + "-largeobject")
- lo, err := obj.AsLargeObject()
+ _, err := obj.AsLargeObject()
expectError(t, err, schwift.ErrNotLarge.Error())
segment1 := getRandomSegmentContent(128)
@@ -46,7 +46,7 @@ func TestLargeObjectsBasic(t *testing.T) {
segment4 := getRandomSegmentContent(128)
//basic write example
- lo, err = obj.AsNewLargeObject(schwift.SegmentingOptions{
+ lo, err := obj.AsNewLargeObject(schwift.SegmentingOptions{
SegmentContainer: c,
SegmentPrefix: strategyStr + "-segments/",
Strategy: strategy,
@@ -169,7 +169,6 @@ func TestLargeObjectExpiration(t *testing.T) {
expectSuccess(t, err)
objectExpiration = hdr.ExpiresAt().Get().Format("2006-01-02 15:04:05 +00:00 MST")
expectString(t, objectExpiration, expirationTime.Format("2006-01-02 15:04:05 +00:00 MST"))
-
})
})
}
@@ -210,7 +209,6 @@ func TestTruncateDuringOverwrite(t *testing.T) {
expectObjectExistence(t, c.Object("segments/0000000000000001"), false)
expectObjectExistence(t, c.Object("segments/0000000000000002"), false)
-
})
})
}
diff --git a/tests/object_test.go b/tests/object_test.go
index 1575a31..2f22b24 100644
--- a/tests/object_test.go
+++ b/tests/object_test.go
@@ -21,7 +21,6 @@ package tests
import (
"bytes"
"io"
- "io/ioutil"
"net/http"
"strings"
"testing"
@@ -63,7 +62,6 @@ func TestObjectLifecycle(t *testing.T) {
func TestObjectUpload(t *testing.T) {
testWithContainer(t, func(c *schwift.Container) {
-
//test upload with bytes.Reader
obj := c.Object("upload1")
err := obj.Upload(bytes.NewReader(objectExampleContent), nil, nil)
@@ -152,7 +150,7 @@ func TestObjectDownload(t *testing.T) {
_, err = reader.Read(buf)
expectSuccess(t, err)
expectString(t, string(buf), string(objectExampleContent[4:8]))
- buf, err = ioutil.ReadAll(reader)
+ buf, err = io.ReadAll(reader)
expectSuccess(t, err)
expectString(t, string(buf), string(objectExampleContent[8:]))
})
diff --git a/tests/shared_test.go b/tests/shared_test.go
index 54934ff..d6271a6 100644
--- a/tests/shared_test.go
+++ b/tests/shared_test.go
@@ -19,7 +19,7 @@
package tests
import (
- "crypto/md5"
+ "crypto/md5" //nolint:gosec // Etag uses md5
"crypto/rand"
"encoding/hex"
"math"
@@ -107,7 +107,7 @@ func testWithContainer(t *testing.T, testCode func(c *schwift.Container)) {
////////////////////////////////////////////////////////////////////////////////
func etagOf(buf []byte) string {
- hash := md5.Sum(buf)
+ hash := md5.Sum(buf) //nolint:gosec // Etag uses md5
return hex.EncodeToString(hash[:])
}
@@ -124,7 +124,7 @@ func getRandomName() string {
return hex.EncodeToString(buf[:])
}
-func getRandomSegmentContent(length int) string {
+func getRandomSegmentContent(length int) string { //nolint:unparam
buf := make([]byte, length/2)
_, err := rand.Read(buf)
if err != nil {
@@ -177,17 +177,13 @@ func expectString(t *testing.T, actual, expected string) {
}
}
-func expectError(t *testing.T, actual error, expected string) (ok bool) {
+func expectError(t *testing.T, actual error, expected string) {
t.Helper()
if actual == nil {
t.Errorf("expected error %q, got no error\n", expected)
- return false
- }
- if expected != actual.Error() {
+ } else if expected != actual.Error() {
t.Errorf("expected error %q, got %q instead\n", expected, actual.Error())
- return false
}
- return true
}
func expectSuccess(t *testing.T, actual error) (ok bool) {
@@ -199,7 +195,7 @@ func expectSuccess(t *testing.T, actual error) (ok bool) {
return true
}
-func expectHeaders(t *testing.T, actual map[string]string, expected map[string]string) {
+func expectHeaders(t *testing.T, actual, expected map[string]string) {
t.Helper()
reported := make(map[string]bool)
diff --git a/util/render_template.go b/util/render_template.go
index 2898623..03a4628 100755
--- a/util/render_template.go
+++ b/util/render_template.go
@@ -23,14 +23,14 @@ package main
import (
"encoding/json"
"fmt"
- "io/ioutil"
+ "io"
"os"
"strings"
"text/template"
)
func main() {
- input, err := ioutil.ReadAll(os.Stdin)
+ input, err := io.ReadAll(os.Stdin)
failIfError(err)
sections := strings.SplitN(string(input), "\n---\n", 2)