mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2025-12-29 22:36:14 -06:00
fix up some stuffffff
This commit is contained in:
parent
6d3efd3511
commit
e12b81e3f1
34 changed files with 236 additions and 2484 deletions
3
go.mod
3
go.mod
|
|
@ -30,7 +30,6 @@ require (
|
||||||
github.com/uptrace/bun v1.0.5
|
github.com/uptrace/bun v1.0.5
|
||||||
github.com/uptrace/bun/dialect/pgdialect v1.0.5
|
github.com/uptrace/bun/dialect/pgdialect v1.0.5
|
||||||
github.com/uptrace/bun/dialect/sqlitedialect v1.0.5
|
github.com/uptrace/bun/dialect/sqlitedialect v1.0.5
|
||||||
github.com/uptrace/bun/extra/bundebug v1.0.5
|
|
||||||
github.com/urfave/cli/v2 v2.3.0
|
github.com/urfave/cli/v2 v2.3.0
|
||||||
github.com/wagslane/go-password-validator v0.3.0
|
github.com/wagslane/go-password-validator v0.3.0
|
||||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5
|
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5
|
||||||
|
|
@ -53,7 +52,6 @@ require (
|
||||||
github.com/dsoprea/go-photoshop-info-format v0.0.0-20200610045659-121dd752914d // indirect
|
github.com/dsoprea/go-photoshop-info-format v0.0.0-20200610045659-121dd752914d // indirect
|
||||||
github.com/dsoprea/go-png-image-structure v0.0.0-20210512210324-29b889a6093d // indirect
|
github.com/dsoprea/go-png-image-structure v0.0.0-20210512210324-29b889a6093d // indirect
|
||||||
github.com/dsoprea/go-utility v0.0.0-20200717064901-2fccff4aa15e // indirect
|
github.com/dsoprea/go-utility v0.0.0-20200717064901-2fccff4aa15e // indirect
|
||||||
github.com/fatih/color v1.10.0 // indirect
|
|
||||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||||
github.com/go-errors/errors v1.4.0 // indirect
|
github.com/go-errors/errors v1.4.0 // indirect
|
||||||
github.com/go-playground/locales v0.14.0 // indirect
|
github.com/go-playground/locales v0.14.0 // indirect
|
||||||
|
|
@ -77,7 +75,6 @@ require (
|
||||||
github.com/json-iterator/go v1.1.11 // indirect
|
github.com/json-iterator/go v1.1.11 // indirect
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||||
github.com/leodido/go-urn v1.2.1 // indirect
|
github.com/leodido/go-urn v1.2.1 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.8 // indirect
|
|
||||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.1 // indirect
|
github.com/modern-go/reflect2 v1.0.1 // indirect
|
||||||
|
|
|
||||||
6
go.sum
6
go.sum
|
|
@ -108,8 +108,6 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m
|
||||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8=
|
github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8=
|
||||||
github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
|
|
||||||
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
|
||||||
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
|
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
|
||||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
|
|
@ -337,8 +335,6 @@ github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2/go.mod h1:0KeJpeMD6o+O
|
||||||
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
||||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
|
||||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
|
||||||
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||||
|
|
@ -460,8 +456,6 @@ github.com/uptrace/bun/dialect/pgdialect v1.0.5 h1:mq694/aMvs7GwuTar9NIlCLQt/2u4
|
||||||
github.com/uptrace/bun/dialect/pgdialect v1.0.5/go.mod h1:MKWjO0PC20ris2oJ3dd6mI/762x24Cjwh8XmbqUhM8A=
|
github.com/uptrace/bun/dialect/pgdialect v1.0.5/go.mod h1:MKWjO0PC20ris2oJ3dd6mI/762x24Cjwh8XmbqUhM8A=
|
||||||
github.com/uptrace/bun/dialect/sqlitedialect v1.0.5 h1:6cIj31YVJr4vvA15C2v76soXL+7WtjFdV6WraApc3r0=
|
github.com/uptrace/bun/dialect/sqlitedialect v1.0.5 h1:6cIj31YVJr4vvA15C2v76soXL+7WtjFdV6WraApc3r0=
|
||||||
github.com/uptrace/bun/dialect/sqlitedialect v1.0.5/go.mod h1:NW2Gctc9ooQXGSD4kYSac2aiF49lo8YJ3ZAr93lH2p8=
|
github.com/uptrace/bun/dialect/sqlitedialect v1.0.5/go.mod h1:NW2Gctc9ooQXGSD4kYSac2aiF49lo8YJ3ZAr93lH2p8=
|
||||||
github.com/uptrace/bun/extra/bundebug v1.0.5 h1:g7ILRgoPXSKOQ6Wq+n9leF/ehGLzHmktBL33JIHmgq0=
|
|
||||||
github.com/uptrace/bun/extra/bundebug v1.0.5/go.mod h1:548Yazx7hlZDkLQyHBIz840rOspP2ceBVEwl/FJHubU=
|
|
||||||
github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
|
github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
|
||||||
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
||||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,9 @@
|
||||||
package account
|
package account
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
|
|
@ -107,17 +109,24 @@ func (m *Module) AccountUpdateCredentialsPATCHHandler(c *gin.Context) {
|
||||||
}
|
}
|
||||||
l.Tracef("retrieved account %+v", authed.Account.ID)
|
l.Tracef("retrieved account %+v", authed.Account.ID)
|
||||||
|
|
||||||
form := &model.UpdateCredentialsRequest{}
|
form, err := parseUpdateAccountForm(c)
|
||||||
if err := c.ShouldBind(&form); err != nil || form == nil {
|
if err != nil {
|
||||||
l.Debugf("could not parse form from request: %s", err)
|
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
l.Debugf("parsed request form %+v", form)
|
|
||||||
|
|
||||||
// if everything on the form is nil, then nothing has been set and we shouldn't continue
|
// if everything on the form is nil, then nothing has been set and we shouldn't continue
|
||||||
if form.Discoverable == nil && form.Bot == nil && form.DisplayName == nil && form.Note == nil && form.Avatar == nil && form.Header == nil && form.Locked == nil && form.Source == nil && form.FieldsAttributes == nil {
|
if form.Discoverable == nil &&
|
||||||
|
form.Bot == nil &&
|
||||||
|
form.DisplayName == nil &&
|
||||||
|
form.Note == nil &&
|
||||||
|
form.Avatar == nil &&
|
||||||
|
form.Header == nil &&
|
||||||
|
form.Locked == nil &&
|
||||||
|
form.Source.Privacy == nil &&
|
||||||
|
form.Source.Sensitive == nil &&
|
||||||
|
form.Source.Language == nil &&
|
||||||
|
form.FieldsAttributes == nil {
|
||||||
l.Debugf("could not parse form from request")
|
l.Debugf("could not parse form from request")
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "empty form submitted"})
|
c.JSON(http.StatusBadRequest, gin.H{"error": "empty form submitted"})
|
||||||
return
|
return
|
||||||
|
|
@ -133,3 +142,34 @@ func (m *Module) AccountUpdateCredentialsPATCHHandler(c *gin.Context) {
|
||||||
l.Tracef("conversion successful, returning OK and mastosensitive account %+v", acctSensitive)
|
l.Tracef("conversion successful, returning OK and mastosensitive account %+v", acctSensitive)
|
||||||
c.JSON(http.StatusOK, acctSensitive)
|
c.JSON(http.StatusOK, acctSensitive)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseUpdateAccountForm(c *gin.Context) (*model.UpdateCredentialsRequest, error) {
|
||||||
|
// parse main fields from request
|
||||||
|
form := &model.UpdateCredentialsRequest{
|
||||||
|
Source: &model.UpdateSource{},
|
||||||
|
}
|
||||||
|
if err := c.ShouldBind(&form); err != nil || form == nil {
|
||||||
|
return nil, fmt.Errorf("could not parse form from request: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse source field-by-field
|
||||||
|
sourceMap := c.PostFormMap("source")
|
||||||
|
|
||||||
|
if privacy, ok := sourceMap["privacy"]; ok {
|
||||||
|
form.Source.Privacy = &privacy
|
||||||
|
}
|
||||||
|
|
||||||
|
if sensitive, ok := sourceMap["sensitive"]; ok {
|
||||||
|
sensitiveBool, err := strconv.ParseBool(sensitive)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error parsing form source[sensitive]: %s", err)
|
||||||
|
}
|
||||||
|
form.Source.Sensitive = &sensitiveBool
|
||||||
|
}
|
||||||
|
|
||||||
|
if language, ok := sourceMap["language"]; ok {
|
||||||
|
form.Source.Language = &language
|
||||||
|
}
|
||||||
|
|
||||||
|
return form, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ package account_test
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
|
@ -51,7 +50,6 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandler()
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
bodyBytes := requestBody.Bytes()
|
bodyBytes := requestBody.Bytes()
|
||||||
fmt.Println(string(bodyBytes))
|
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
ctx := suite.newContext(recorder, http.MethodPatch, bodyBytes, account.UpdateCredentialsPath, w.FormDataContentType())
|
ctx := suite.newContext(recorder, http.MethodPatch, bodyBytes, account.UpdateCredentialsPath, w.FormDataContentType())
|
||||||
|
|
||||||
|
|
@ -68,7 +66,6 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandler()
|
||||||
// check the response
|
// check the response
|
||||||
b, err := ioutil.ReadAll(result.Body)
|
b, err := ioutil.ReadAll(result.Body)
|
||||||
assert.NoError(suite.T(), err)
|
assert.NoError(suite.T(), err)
|
||||||
fmt.Println(string(b))
|
|
||||||
|
|
||||||
// unmarshal the returned account
|
// unmarshal the returned account
|
||||||
apimodelAccount := &apimodel.Account{}
|
apimodelAccount := &apimodel.Account{}
|
||||||
|
|
@ -77,7 +74,7 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandler()
|
||||||
|
|
||||||
// check the returned api model account
|
// check the returned api model account
|
||||||
// fields should be updated
|
// fields should be updated
|
||||||
suite.Equal(newBio, apimodelAccount.Note)
|
suite.Equal("<p>this is my new bio read it and weep</p>", apimodelAccount.Note)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerGetAccountFirst() {
|
func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerGetAccountFirst() {
|
||||||
|
|
@ -114,7 +111,6 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerGet
|
||||||
// check the response
|
// check the response
|
||||||
b, err := ioutil.ReadAll(result.Body)
|
b, err := ioutil.ReadAll(result.Body)
|
||||||
assert.NoError(suite.T(), err)
|
assert.NoError(suite.T(), err)
|
||||||
fmt.Println(string(b))
|
|
||||||
|
|
||||||
// unmarshal the returned account
|
// unmarshal the returned account
|
||||||
apimodelAccount := &apimodel.Account{}
|
apimodelAccount := &apimodel.Account{}
|
||||||
|
|
@ -123,7 +119,7 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerGet
|
||||||
|
|
||||||
// check the returned api model account
|
// check the returned api model account
|
||||||
// fields should be updated
|
// fields should be updated
|
||||||
suite.Equal(newBio, apimodelAccount.Note)
|
suite.Equal("<p>this is my new bio read it and weep</p>", apimodelAccount.Note)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerTwoFields() {
|
func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerTwoFields() {
|
||||||
|
|
@ -140,7 +136,6 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerTwo
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
bodyBytes := requestBody.Bytes()
|
bodyBytes := requestBody.Bytes()
|
||||||
fmt.Println(string(bodyBytes))
|
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
ctx := suite.newContext(recorder, http.MethodPatch, bodyBytes, account.UpdateCredentialsPath, w.FormDataContentType())
|
ctx := suite.newContext(recorder, http.MethodPatch, bodyBytes, account.UpdateCredentialsPath, w.FormDataContentType())
|
||||||
|
|
||||||
|
|
@ -157,7 +152,6 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerTwo
|
||||||
// check the response
|
// check the response
|
||||||
b, err := ioutil.ReadAll(result.Body)
|
b, err := ioutil.ReadAll(result.Body)
|
||||||
assert.NoError(suite.T(), err)
|
assert.NoError(suite.T(), err)
|
||||||
fmt.Println(string(b))
|
|
||||||
|
|
||||||
// unmarshal the returned account
|
// unmarshal the returned account
|
||||||
apimodelAccount := &apimodel.Account{}
|
apimodelAccount := &apimodel.Account{}
|
||||||
|
|
@ -166,7 +160,7 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerTwo
|
||||||
|
|
||||||
// check the returned api model account
|
// check the returned api model account
|
||||||
// fields should be updated
|
// fields should be updated
|
||||||
suite.Equal(newBio, apimodelAccount.Note)
|
suite.Equal("<p>this is my new bio read it and weep</p>", apimodelAccount.Note)
|
||||||
suite.True(apimodelAccount.Locked)
|
suite.True(apimodelAccount.Locked)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -201,7 +195,6 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerWit
|
||||||
// check the response
|
// check the response
|
||||||
b, err := ioutil.ReadAll(result.Body)
|
b, err := ioutil.ReadAll(result.Body)
|
||||||
assert.NoError(suite.T(), err)
|
assert.NoError(suite.T(), err)
|
||||||
fmt.Println(string(b))
|
|
||||||
|
|
||||||
// unmarshal the returned account
|
// unmarshal the returned account
|
||||||
apimodelAccount := &apimodel.Account{}
|
apimodelAccount := &apimodel.Account{}
|
||||||
|
|
@ -246,6 +239,52 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerEmp
|
||||||
suite.Equal(`{"error":"empty form submitted"}`, string(b))
|
suite.Equal(`{"error":"empty form submitted"}`, string(b))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerUpdateSource() {
|
||||||
|
// set up the request
|
||||||
|
// we're updating the language of zork
|
||||||
|
newLanguage := "de"
|
||||||
|
requestBody, w, err := testrig.CreateMultipartFormData(
|
||||||
|
"", "",
|
||||||
|
map[string]string{
|
||||||
|
"source[privacy]": string(apimodel.VisibilityPrivate),
|
||||||
|
"source[language]": "de",
|
||||||
|
"source[sensitive]": "true",
|
||||||
|
"locked": "true",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
bodyBytes := requestBody.Bytes()
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
|
ctx := suite.newContext(recorder, http.MethodPatch, bodyBytes, account.UpdateCredentialsPath, w.FormDataContentType())
|
||||||
|
|
||||||
|
// call the handler
|
||||||
|
suite.accountModule.AccountUpdateCredentialsPATCHHandler(ctx)
|
||||||
|
|
||||||
|
// 1. we should have OK because our request was valid
|
||||||
|
suite.Equal(http.StatusOK, recorder.Code)
|
||||||
|
|
||||||
|
// 2. we should have no error message in the result body
|
||||||
|
result := recorder.Result()
|
||||||
|
defer result.Body.Close()
|
||||||
|
|
||||||
|
// check the response
|
||||||
|
b, err := ioutil.ReadAll(result.Body)
|
||||||
|
assert.NoError(suite.T(), err)
|
||||||
|
|
||||||
|
// unmarshal the returned account
|
||||||
|
apimodelAccount := &apimodel.Account{}
|
||||||
|
err = json.Unmarshal(b, apimodelAccount)
|
||||||
|
suite.NoError(err)
|
||||||
|
|
||||||
|
// check the returned api model account
|
||||||
|
// fields should be updated
|
||||||
|
suite.Equal(newLanguage, apimodelAccount.Source.Language)
|
||||||
|
suite.EqualValues(apimodel.VisibilityPrivate, apimodelAccount.Source.Privacy)
|
||||||
|
suite.True(apimodelAccount.Source.Sensitive)
|
||||||
|
suite.True(apimodelAccount.Locked)
|
||||||
|
}
|
||||||
|
|
||||||
func TestAccountUpdateTestSuite(t *testing.T) {
|
func TestAccountUpdateTestSuite(t *testing.T) {
|
||||||
suite.Run(t, new(AccountUpdateTestSuite))
|
suite.Run(t, new(AccountUpdateTestSuite))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -160,6 +160,7 @@ type StatusCreateRequest struct {
|
||||||
// - public
|
// - public
|
||||||
// - unlisted
|
// - unlisted
|
||||||
// - private
|
// - private
|
||||||
|
// - mutuals_only
|
||||||
// - direct
|
// - direct
|
||||||
type Visibility string
|
type Visibility string
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -66,10 +66,6 @@ type Basic interface {
|
||||||
// The given interface i will be set to the result of the query, whatever it is. Use a pointer or a slice.
|
// The given interface i will be set to the result of the query, whatever it is. Use a pointer or a slice.
|
||||||
UpdateByPrimaryKey(ctx context.Context, i interface{}) Error
|
UpdateByPrimaryKey(ctx context.Context, i interface{}) Error
|
||||||
|
|
||||||
// UpdateOneByPrimaryKey sets one column of interface, with the given key, to the given value.
|
|
||||||
// It uses the primary key of interface i to decide which row to update. This is usually the `id`.
|
|
||||||
UpdateOneByPrimaryKey(ctx context.Context, key string, value interface{}, i interface{}) Error
|
|
||||||
|
|
||||||
// UpdateWhere updates column key of interface i with the given value, where the given parameters apply.
|
// UpdateWhere updates column key of interface i with the given value, where the given parameters apply.
|
||||||
UpdateWhere(ctx context.Context, where []Where, key string, value interface{}, i interface{}) Error
|
UpdateWhere(ctx context.Context, where []Where, key string, value interface{}, i interface{}) Error
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/cache"
|
"github.com/superseriousbusiness/gotosocial/internal/cache"
|
||||||
|
|
@ -103,16 +102,15 @@ func (a *accountDB) getAccount(ctx context.Context, cacheGet func() (*gtsmodel.A
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *accountDB) UpdateAccount(ctx context.Context, account *gtsmodel.Account) (*gtsmodel.Account, db.Error) {
|
func (a *accountDB) UpdateAccount(ctx context.Context, account *gtsmodel.Account) (*gtsmodel.Account, db.Error) {
|
||||||
if strings.TrimSpace(account.ID) == "" {
|
// Update the account's last-updated
|
||||||
// TODO: we should not need this check here
|
|
||||||
return nil, errors.New("account had no ID")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the account's last-used
|
|
||||||
account.UpdatedAt = time.Now()
|
account.UpdatedAt = time.Now()
|
||||||
|
|
||||||
// Update the account model in the DB
|
// Update the account model in the DB
|
||||||
_, err := a.conn.NewUpdate().Model(account).WherePK().Exec(ctx)
|
_, err := a.conn.
|
||||||
|
NewUpdate().
|
||||||
|
Model(account).
|
||||||
|
WherePK().
|
||||||
|
Exec(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, a.conn.ProcessError(err)
|
return nil, a.conn.ProcessError(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -105,16 +105,6 @@ func (b *basicDB) UpdateByPrimaryKey(ctx context.Context, i interface{}) db.Erro
|
||||||
return b.conn.ProcessError(err)
|
return b.conn.ProcessError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *basicDB) UpdateOneByPrimaryKey(ctx context.Context, key string, value interface{}, i interface{}) db.Error {
|
|
||||||
q := b.conn.NewUpdate().
|
|
||||||
Model(i).
|
|
||||||
Set("? = ?", bun.Safe(key), value).
|
|
||||||
WherePK()
|
|
||||||
|
|
||||||
_, err := q.Exec(ctx)
|
|
||||||
return b.conn.ProcessError(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *basicDB) UpdateWhere(ctx context.Context, where []db.Where, key string, value interface{}, i interface{}) db.Error {
|
func (b *basicDB) UpdateWhere(ctx context.Context, where []db.Where, key string, value interface{}, i interface{}) db.Error {
|
||||||
q := b.conn.NewUpdate().Model(i)
|
q := b.conn.NewUpdate().Model(i)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -64,40 +64,6 @@ func (suite *BasicTestSuite) TestGetAllNotNull() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *BasicTestSuite) TestUpdateOneByPrimaryKeySetEmpty() {
|
|
||||||
testAccount := suite.testAccounts["local_account_1"]
|
|
||||||
|
|
||||||
// try removing the note from zork
|
|
||||||
err := suite.db.UpdateOneByPrimaryKey(context.Background(), "note", "", testAccount)
|
|
||||||
suite.NoError(err)
|
|
||||||
|
|
||||||
// get zork out of the database
|
|
||||||
dbAccount, err := suite.db.GetAccountByID(context.Background(), testAccount.ID)
|
|
||||||
suite.NoError(err)
|
|
||||||
suite.NotNil(dbAccount)
|
|
||||||
|
|
||||||
// note should be empty now
|
|
||||||
suite.Empty(dbAccount.Note)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (suite *BasicTestSuite) TestUpdateOneByPrimaryKeySetValue() {
|
|
||||||
testAccount := suite.testAccounts["local_account_1"]
|
|
||||||
|
|
||||||
note := "this is my new note :)"
|
|
||||||
|
|
||||||
// try updating the note on zork
|
|
||||||
err := suite.db.UpdateOneByPrimaryKey(context.Background(), "note", note, testAccount)
|
|
||||||
suite.NoError(err)
|
|
||||||
|
|
||||||
// get zork out of the database
|
|
||||||
dbAccount, err := suite.db.GetAccountByID(context.Background(), testAccount.ID)
|
|
||||||
suite.NoError(err)
|
|
||||||
suite.NotNil(dbAccount)
|
|
||||||
|
|
||||||
// note should be set now
|
|
||||||
suite.Equal(note, dbAccount.Note)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBasicTestSuite(t *testing.T) {
|
func TestBasicTestSuite(t *testing.T) {
|
||||||
suite.Run(t, new(BasicTestSuite))
|
suite.Run(t, new(BasicTestSuite))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -149,7 +149,7 @@ func NewBunDBService(ctx context.Context, c *config.Config, log *logrus.Logger)
|
||||||
|
|
||||||
if log.Level >= logrus.TraceLevel {
|
if log.Level >= logrus.TraceLevel {
|
||||||
// add a hook to just log queries and the time they take
|
// add a hook to just log queries and the time they take
|
||||||
conn.DB.AddQueryHook(&debugQueryHook{log: log})
|
conn.DB.AddQueryHook(newDebugQueryHook(log))
|
||||||
}
|
}
|
||||||
|
|
||||||
// actually *begin* the connection so that we can tell if the db is there and listening
|
// actually *begin* the connection so that we can tell if the db is there and listening
|
||||||
|
|
@ -407,7 +407,7 @@ func (ps *bunDBService) MentionStringsToMentions(ctx context.Context, targetAcco
|
||||||
return menchies, nil
|
return menchies, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ps *bunDBService) TagStringsToTags(ctx context.Context, tags []string, originAccountID string, statusID string) ([]*gtsmodel.Tag, error) {
|
func (ps *bunDBService) TagStringsToTags(ctx context.Context, tags []string, originAccountID string) ([]*gtsmodel.Tag, error) {
|
||||||
newTags := []*gtsmodel.Tag{}
|
newTags := []*gtsmodel.Tag{}
|
||||||
for _, t := range tags {
|
for _, t := range tags {
|
||||||
tag := >smodel.Tag{}
|
tag := >smodel.Tag{}
|
||||||
|
|
@ -443,7 +443,7 @@ func (ps *bunDBService) TagStringsToTags(ctx context.Context, tags []string, ori
|
||||||
return newTags, nil
|
return newTags, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ps *bunDBService) EmojiStringsToEmojis(ctx context.Context, emojis []string, originAccountID string, statusID string) ([]*gtsmodel.Emoji, error) {
|
func (ps *bunDBService) EmojiStringsToEmojis(ctx context.Context, emojis []string) ([]*gtsmodel.Emoji, error) {
|
||||||
newEmojis := []*gtsmodel.Emoji{}
|
newEmojis := []*gtsmodel.Emoji{}
|
||||||
for _, e := range emojis {
|
for _, e := range emojis {
|
||||||
emoji := >smodel.Emoji{}
|
emoji := >smodel.Emoji{}
|
||||||
|
|
|
||||||
|
|
@ -26,10 +26,13 @@ import (
|
||||||
"github.com/uptrace/bun"
|
"github.com/uptrace/bun"
|
||||||
)
|
)
|
||||||
|
|
||||||
// queryHook is just a wrapper for bun.QueryHook
|
func newDebugQueryHook(log *logrus.Logger) bun.QueryHook {
|
||||||
type queryHook bun.QueryHook
|
return &debugQueryHook{
|
||||||
|
log: log,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// debugQueryHook implements queryHook
|
// debugQueryHook implements bun.QueryHook
|
||||||
type debugQueryHook struct {
|
type debugQueryHook struct {
|
||||||
log *logrus.Logger
|
log *logrus.Logger
|
||||||
}
|
}
|
||||||
|
|
@ -41,7 +44,7 @@ func (q *debugQueryHook) BeforeQuery(ctx context.Context, event *bun.QueryEvent)
|
||||||
|
|
||||||
// AfterQuery logs the time taken to query, the operation (select, update, etc), and the query itself as translated by bun.
|
// AfterQuery logs the time taken to query, the operation (select, update, etc), and the query itself as translated by bun.
|
||||||
func (q *debugQueryHook) AfterQuery(ctx context.Context, event *bun.QueryEvent) {
|
func (q *debugQueryHook) AfterQuery(ctx context.Context, event *bun.QueryEvent) {
|
||||||
dur := time.Now().Sub(event.StartTime).Round(time.Microsecond)
|
dur := time.Since(event.StartTime).Round(time.Microsecond)
|
||||||
l := q.log.WithFields(logrus.Fields{
|
l := q.log.WithFields(logrus.Fields{
|
||||||
"queryTime": dur,
|
"queryTime": dur,
|
||||||
"operation": event.Operation(),
|
"operation": event.Operation(),
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ type DB interface {
|
||||||
//
|
//
|
||||||
// Note: this func doesn't/shouldn't do any manipulation of the tags in the DB, it's just for checking
|
// Note: this func doesn't/shouldn't do any manipulation of the tags in the DB, it's just for checking
|
||||||
// if they exist in the db already, and conveniently returning them, or creating new tag structs.
|
// if they exist in the db already, and conveniently returning them, or creating new tag structs.
|
||||||
TagStringsToTags(ctx context.Context, tags []string, originAccountID string, statusID string) ([]*gtsmodel.Tag, error)
|
TagStringsToTags(ctx context.Context, tags []string, originAccountID string) ([]*gtsmodel.Tag, error)
|
||||||
|
|
||||||
// EmojiStringsToEmojis takes a slice of deduplicated, lowercase emojis in the form ":emojiname:", which have been
|
// EmojiStringsToEmojis takes a slice of deduplicated, lowercase emojis in the form ":emojiname:", which have been
|
||||||
// used in a status. It takes the id of the account that wrote the status, and the id of the status itself, and then
|
// used in a status. It takes the id of the account that wrote the status, and the id of the status itself, and then
|
||||||
|
|
@ -72,5 +72,5 @@ type DB interface {
|
||||||
//
|
//
|
||||||
// Note: this func doesn't/shouldn't do any manipulation of the emoji in the DB, it's just for checking
|
// Note: this func doesn't/shouldn't do any manipulation of the emoji in the DB, it's just for checking
|
||||||
// if they exist in the db and conveniently returning them if they do.
|
// if they exist in the db and conveniently returning them if they do.
|
||||||
EmojiStringsToEmojis(ctx context.Context, emojis []string, originAccountID string, statusID string) ([]*gtsmodel.Emoji, error)
|
EmojiStringsToEmojis(ctx context.Context, emojis []string) ([]*gtsmodel.Emoji, error)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ import (
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/messages"
|
"github.com/superseriousbusiness/gotosocial/internal/messages"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/text"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
||||||
"github.com/superseriousbusiness/oauth2/v4"
|
"github.com/superseriousbusiness/oauth2/v4"
|
||||||
|
|
@ -83,6 +84,7 @@ type processor struct {
|
||||||
fromClientAPI chan messages.FromClientAPI
|
fromClientAPI chan messages.FromClientAPI
|
||||||
oauthServer oauth.Server
|
oauthServer oauth.Server
|
||||||
filter visibility.Filter
|
filter visibility.Filter
|
||||||
|
formatter text.Formatter
|
||||||
db db.DB
|
db db.DB
|
||||||
federator federation.Federator
|
federator federation.Federator
|
||||||
log *logrus.Logger
|
log *logrus.Logger
|
||||||
|
|
@ -97,6 +99,7 @@ func New(db db.DB, tc typeutils.TypeConverter, mediaHandler media.Handler, oauth
|
||||||
fromClientAPI: fromClientAPI,
|
fromClientAPI: fromClientAPI,
|
||||||
oauthServer: oauthServer,
|
oauthServer: oauthServer,
|
||||||
filter: visibility.NewFilter(db, log),
|
filter: visibility.NewFilter(db, log),
|
||||||
|
formatter: text.NewFormatter(config, db, log),
|
||||||
db: db,
|
db: db,
|
||||||
federator: federator,
|
federator: federator,
|
||||||
log: log,
|
log: log,
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ import (
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/messages"
|
"github.com/superseriousbusiness/gotosocial/internal/messages"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/text"
|
"github.com/superseriousbusiness/gotosocial/internal/text"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/validate"
|
"github.com/superseriousbusiness/gotosocial/internal/validate"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -39,35 +40,29 @@ func (p *processor) Update(ctx context.Context, account *gtsmodel.Account, form
|
||||||
l := p.log.WithField("func", "AccountUpdate")
|
l := p.log.WithField("func", "AccountUpdate")
|
||||||
|
|
||||||
if form.Discoverable != nil {
|
if form.Discoverable != nil {
|
||||||
if err := p.db.UpdateOneByPrimaryKey(ctx, "discoverable", *form.Discoverable, account); err != nil {
|
account.Discoverable = *form.Discoverable
|
||||||
return nil, fmt.Errorf("error updating discoverable: %s", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if form.Bot != nil {
|
if form.Bot != nil {
|
||||||
if err := p.db.UpdateOneByPrimaryKey(ctx, "bot", *form.Bot, account); err != nil {
|
account.Bot = *form.Bot
|
||||||
return nil, fmt.Errorf("error updating bot: %s", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if form.DisplayName != nil {
|
if form.DisplayName != nil {
|
||||||
if err := validate.DisplayName(*form.DisplayName); err != nil {
|
if err := validate.DisplayName(*form.DisplayName); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
displayName := text.RemoveHTML(*form.DisplayName) // no html allowed in display name
|
account.DisplayName = text.RemoveHTML(*form.DisplayName)
|
||||||
if err := p.db.UpdateOneByPrimaryKey(ctx, "display_name", displayName, account); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if form.Note != nil {
|
if form.Note != nil {
|
||||||
if err := validate.Note(*form.Note); err != nil {
|
if err := validate.Note(*form.Note); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
note := text.SanitizeHTML(*form.Note) // html OK in note but sanitize it
|
note, err := p.processNote(ctx, *form.Note, account.ID)
|
||||||
if err := p.db.UpdateOneByPrimaryKey(ctx, "note", note, account); err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
account.Note = note
|
||||||
}
|
}
|
||||||
|
|
||||||
if form.Avatar != nil && form.Avatar.Size != 0 {
|
if form.Avatar != nil && form.Avatar.Size != 0 {
|
||||||
|
|
@ -75,6 +70,8 @@ func (p *processor) Update(ctx context.Context, account *gtsmodel.Account, form
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
account.AvatarMediaAttachmentID = avatarInfo.ID
|
||||||
|
account.AvatarMediaAttachment = avatarInfo
|
||||||
l.Tracef("new avatar info for account %s is %+v", account.ID, avatarInfo)
|
l.Tracef("new avatar info for account %s is %+v", account.ID, avatarInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -83,13 +80,13 @@ func (p *processor) Update(ctx context.Context, account *gtsmodel.Account, form
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
account.HeaderMediaAttachmentID = headerInfo.ID
|
||||||
|
account.HeaderMediaAttachment = headerInfo
|
||||||
l.Tracef("new header info for account %s is %+v", account.ID, headerInfo)
|
l.Tracef("new header info for account %s is %+v", account.ID, headerInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
if form.Locked != nil {
|
if form.Locked != nil {
|
||||||
if err := p.db.UpdateOneByPrimaryKey(ctx, "locked", *form.Locked, account); err != nil {
|
account.Locked = *form.Locked
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if form.Source != nil {
|
if form.Source != nil {
|
||||||
|
|
@ -97,31 +94,25 @@ func (p *processor) Update(ctx context.Context, account *gtsmodel.Account, form
|
||||||
if err := validate.Language(*form.Source.Language); err != nil {
|
if err := validate.Language(*form.Source.Language); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := p.db.UpdateOneByPrimaryKey(ctx, "language", *form.Source.Language, account); err != nil {
|
account.Language = *form.Source.Language
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if form.Source.Sensitive != nil {
|
if form.Source.Sensitive != nil {
|
||||||
if err := p.db.UpdateOneByPrimaryKey(ctx, "locked", *form.Locked, account); err != nil {
|
account.Sensitive = *form.Source.Sensitive
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if form.Source.Privacy != nil {
|
if form.Source.Privacy != nil {
|
||||||
if err := validate.Privacy(*form.Source.Privacy); err != nil {
|
if err := validate.Privacy(*form.Source.Privacy); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := p.db.UpdateOneByPrimaryKey(ctx, "privacy", *form.Source.Privacy, account); err != nil {
|
privacy := p.tc.MastoVisToVis(apimodel.Visibility(*form.Source.Privacy))
|
||||||
return nil, err
|
account.Privacy = privacy
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// fetch the account with all updated values set
|
updatedAccount, err := p.db.UpdateAccount(ctx, account)
|
||||||
updatedAccount, err := p.db.GetAccountByID(ctx, account.ID)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not fetch updated account %s: %s", account.ID, err)
|
return nil, fmt.Errorf("could not update account %s: %s", account.ID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
p.fromClientAPI <- messages.FromClientAPI{
|
p.fromClientAPI <- messages.FromClientAPI{
|
||||||
|
|
@ -203,3 +194,27 @@ func (p *processor) UpdateHeader(ctx context.Context, header *multipart.FileHead
|
||||||
|
|
||||||
return headerInfo, f.Close()
|
return headerInfo, f.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *processor) processNote(ctx context.Context, note string, accountID string) (string, error) {
|
||||||
|
if note == "" {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
tagStrings := util.DeriveHashtagsFromText(note)
|
||||||
|
tags, err := p.db.TagStringsToTags(ctx, tagStrings, accountID)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
mentionStrings := util.DeriveMentionsFromText(note)
|
||||||
|
mentions, err := p.db.MentionStringsToMentions(ctx, mentionStrings, accountID, "")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: support emojis in account notes
|
||||||
|
// emojiStrings := util.DeriveEmojisFromText(note)
|
||||||
|
// emojis, err := p.db.EmojiStringsToEmojis(ctx, emojiStrings)
|
||||||
|
|
||||||
|
return p.formatter.FromPlain(ctx, note, mentions, tags), nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateSimple() {
|
||||||
|
|
||||||
locked := true
|
locked := true
|
||||||
displayName := "new display name"
|
displayName := "new display name"
|
||||||
note := ""
|
note := "#hello here i am!"
|
||||||
|
|
||||||
form := &apimodel.UpdateCredentialsRequest{
|
form := &apimodel.UpdateCredentialsRequest{
|
||||||
DisplayName: &displayName,
|
DisplayName: &displayName,
|
||||||
|
|
@ -52,7 +52,7 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateSimple() {
|
||||||
// fields on the profile should be updated
|
// fields on the profile should be updated
|
||||||
suite.True(apiAccount.Locked)
|
suite.True(apiAccount.Locked)
|
||||||
suite.Equal(displayName, apiAccount.DisplayName)
|
suite.Equal(displayName, apiAccount.DisplayName)
|
||||||
suite.Empty(apiAccount.Note)
|
suite.Equal(`<p><a href="http://localhost:8080/tags/hello" class="mention hashtag" rel="tag nofollow noreferrer noopener" target="_blank">#<span>hello</span></a> here i am!</p>`, apiAccount.Note)
|
||||||
|
|
||||||
// we should have an update in the client api channel
|
// we should have an update in the client api channel
|
||||||
msg := <-suite.fromClientAPIChan
|
msg := <-suite.fromClientAPIChan
|
||||||
|
|
@ -67,7 +67,50 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateSimple() {
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
suite.True(dbAccount.Locked)
|
suite.True(dbAccount.Locked)
|
||||||
suite.Equal(displayName, dbAccount.DisplayName)
|
suite.Equal(displayName, dbAccount.DisplayName)
|
||||||
suite.Empty(dbAccount.Note)
|
suite.Equal(`<p><a href="http://localhost:8080/tags/hello" class="mention hashtag" rel="tag nofollow noreferrer noopener" target="_blank">#<span>hello</span></a> here i am!</p>`, dbAccount.Note)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *AccountUpdateTestSuite) TestAccountUpdateWithMention() {
|
||||||
|
testAccount := suite.testAccounts["local_account_1"]
|
||||||
|
|
||||||
|
locked := true
|
||||||
|
displayName := "new display name"
|
||||||
|
note := `#hello here i am!
|
||||||
|
|
||||||
|
go check out @1happyturtle, they have a cool account!
|
||||||
|
`
|
||||||
|
noteExpected := `<p><a href="http://localhost:8080/tags/hello" class="mention hashtag" rel="tag nofollow noreferrer noopener" target="_blank">#<span>hello</span></a> here i am!<br><br>go check out <span class="h-card"><a href="http://localhost:8080/@1happyturtle" class="u-url mention" rel="nofollow noreferrer noopener" target="_blank">@<span>1happyturtle</span></a></span>, they have a cool account!</p>`
|
||||||
|
|
||||||
|
form := &apimodel.UpdateCredentialsRequest{
|
||||||
|
DisplayName: &displayName,
|
||||||
|
Locked: &locked,
|
||||||
|
Note: ¬e,
|
||||||
|
}
|
||||||
|
|
||||||
|
// should get no error from the update function, and an api model account returned
|
||||||
|
apiAccount, err := suite.accountProcessor.Update(context.Background(), testAccount, form)
|
||||||
|
suite.NoError(err)
|
||||||
|
suite.NotNil(apiAccount)
|
||||||
|
|
||||||
|
// fields on the profile should be updated
|
||||||
|
suite.True(apiAccount.Locked)
|
||||||
|
suite.Equal(displayName, apiAccount.DisplayName)
|
||||||
|
suite.Equal(noteExpected, apiAccount.Note)
|
||||||
|
|
||||||
|
// we should have an update in the client api channel
|
||||||
|
msg := <-suite.fromClientAPIChan
|
||||||
|
suite.Equal(ap.ActivityUpdate, msg.APActivityType)
|
||||||
|
suite.Equal(ap.ObjectProfile, msg.APObjectType)
|
||||||
|
suite.NotNil(msg.OriginAccount)
|
||||||
|
suite.Equal(testAccount.ID, msg.OriginAccount.ID)
|
||||||
|
suite.Nil(msg.TargetAccount)
|
||||||
|
|
||||||
|
// fields should be updated in the database as well
|
||||||
|
dbAccount, err := suite.db.GetAccountByID(context.Background(), testAccount.ID)
|
||||||
|
suite.NoError(err)
|
||||||
|
suite.True(dbAccount.Locked)
|
||||||
|
suite.Equal(displayName, dbAccount.DisplayName)
|
||||||
|
suite.Equal(noteExpected, dbAccount.Note)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAccountUpdateTestSuite(t *testing.T) {
|
func TestAccountUpdateTestSuite(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -192,7 +192,7 @@ func (p *processor) ProcessLanguage(ctx context.Context, form *apimodel.Advanced
|
||||||
|
|
||||||
func (p *processor) ProcessMentions(ctx context.Context, form *apimodel.AdvancedStatusCreateForm, accountID string, status *gtsmodel.Status) error {
|
func (p *processor) ProcessMentions(ctx context.Context, form *apimodel.AdvancedStatusCreateForm, accountID string, status *gtsmodel.Status) error {
|
||||||
menchies := []string{}
|
menchies := []string{}
|
||||||
gtsMenchies, err := p.db.MentionStringsToMentions(ctx, util.DeriveMentionsFromStatus(form.Status), accountID, status.ID)
|
gtsMenchies, err := p.db.MentionStringsToMentions(ctx, util.DeriveMentionsFromText(form.Status), accountID, status.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error generating mentions from status: %s", err)
|
return fmt.Errorf("error generating mentions from status: %s", err)
|
||||||
}
|
}
|
||||||
|
|
@ -217,7 +217,7 @@ func (p *processor) ProcessMentions(ctx context.Context, form *apimodel.Advanced
|
||||||
|
|
||||||
func (p *processor) ProcessTags(ctx context.Context, form *apimodel.AdvancedStatusCreateForm, accountID string, status *gtsmodel.Status) error {
|
func (p *processor) ProcessTags(ctx context.Context, form *apimodel.AdvancedStatusCreateForm, accountID string, status *gtsmodel.Status) error {
|
||||||
tags := []string{}
|
tags := []string{}
|
||||||
gtsTags, err := p.db.TagStringsToTags(ctx, util.DeriveHashtagsFromStatus(form.Status), accountID, status.ID)
|
gtsTags, err := p.db.TagStringsToTags(ctx, util.DeriveHashtagsFromText(form.Status), accountID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error generating hashtags from status: %s", err)
|
return fmt.Errorf("error generating hashtags from status: %s", err)
|
||||||
}
|
}
|
||||||
|
|
@ -236,7 +236,7 @@ func (p *processor) ProcessTags(ctx context.Context, form *apimodel.AdvancedStat
|
||||||
|
|
||||||
func (p *processor) ProcessEmojis(ctx context.Context, form *apimodel.AdvancedStatusCreateForm, accountID string, status *gtsmodel.Status) error {
|
func (p *processor) ProcessEmojis(ctx context.Context, form *apimodel.AdvancedStatusCreateForm, accountID string, status *gtsmodel.Status) error {
|
||||||
emojis := []string{}
|
emojis := []string{}
|
||||||
gtsEmojis, err := p.db.EmojiStringsToEmojis(ctx, util.DeriveEmojisFromStatus(form.Status), accountID, status.ID)
|
gtsEmojis, err := p.db.EmojiStringsToEmojis(ctx, util.DeriveEmojisFromText(form.Status))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error generating emojis from status: %s", err)
|
return fmt.Errorf("error generating emojis from status: %s", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,38 +25,38 @@ import (
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/regexes"
|
"github.com/superseriousbusiness/gotosocial/internal/regexes"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DeriveMentionsFromStatus takes a plaintext (ie., not html-formatted) status,
|
// DeriveMentionsFromText takes a plaintext (ie., not html-formatted) text,
|
||||||
// and applies a regex to it to return a deduplicated list of accounts
|
// and applies a regex to it to return a deduplicated list of accounts
|
||||||
// mentioned in that status.
|
// mentioned in that text.
|
||||||
//
|
//
|
||||||
// It will look for fully-qualified account names in the form "@user@example.org".
|
// It will look for fully-qualified account names in the form "@user@example.org".
|
||||||
// or the form "@username" for local users.
|
// or the form "@username" for local users.
|
||||||
func DeriveMentionsFromStatus(status string) []string {
|
func DeriveMentionsFromText(text string) []string {
|
||||||
mentionedAccounts := []string{}
|
mentionedAccounts := []string{}
|
||||||
for _, m := range regexes.MentionFinder.FindAllStringSubmatch(status, -1) {
|
for _, m := range regexes.MentionFinder.FindAllStringSubmatch(text, -1) {
|
||||||
mentionedAccounts = append(mentionedAccounts, m[1])
|
mentionedAccounts = append(mentionedAccounts, m[1])
|
||||||
}
|
}
|
||||||
return UniqueStrings(mentionedAccounts)
|
return UniqueStrings(mentionedAccounts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeriveHashtagsFromStatus takes a plaintext (ie., not html-formatted) status,
|
// DeriveHashtagsFromText takes a plaintext (ie., not html-formatted) text,
|
||||||
// and applies a regex to it to return a deduplicated list of hashtags
|
// and applies a regex to it to return a deduplicated list of hashtags
|
||||||
// used in that status, without the leading #. The case of the returned
|
// used in that text, without the leading #. The case of the returned
|
||||||
// tags will be lowered, for consistency.
|
// tags will be lowered, for consistency.
|
||||||
func DeriveHashtagsFromStatus(status string) []string {
|
func DeriveHashtagsFromText(text string) []string {
|
||||||
tags := []string{}
|
tags := []string{}
|
||||||
for _, m := range regexes.HashtagFinder.FindAllStringSubmatch(status, -1) {
|
for _, m := range regexes.HashtagFinder.FindAllStringSubmatch(text, -1) {
|
||||||
tags = append(tags, strings.TrimPrefix(m[1], "#"))
|
tags = append(tags, strings.TrimPrefix(m[1], "#"))
|
||||||
}
|
}
|
||||||
return UniqueStrings(tags)
|
return UniqueStrings(tags)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeriveEmojisFromStatus takes a plaintext (ie., not html-formatted) status,
|
// DeriveEmojisFromText takes a plaintext (ie., not html-formatted) text,
|
||||||
// and applies a regex to it to return a deduplicated list of emojis
|
// and applies a regex to it to return a deduplicated list of emojis
|
||||||
// used in that status, without the surround ::.
|
// used in that text, without the surrounding `::`
|
||||||
func DeriveEmojisFromStatus(status string) []string {
|
func DeriveEmojisFromText(text string) []string {
|
||||||
emojis := []string{}
|
emojis := []string{}
|
||||||
for _, m := range regexes.EmojiFinder.FindAllStringSubmatch(status, -1) {
|
for _, m := range regexes.EmojiFinder.FindAllStringSubmatch(text, -1) {
|
||||||
emojis = append(emojis, m[1])
|
emojis = append(emojis, m[1])
|
||||||
}
|
}
|
||||||
return UniqueStrings(emojis)
|
return UniqueStrings(emojis)
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ func (suite *StatusTestSuite) TestDeriveMentionsOK() {
|
||||||
|
|
||||||
`
|
`
|
||||||
|
|
||||||
menchies := util.DeriveMentionsFromStatus(statusText)
|
menchies := util.DeriveMentionsFromText(statusText)
|
||||||
assert.Len(suite.T(), menchies, 6)
|
assert.Len(suite.T(), menchies, 6)
|
||||||
assert.Equal(suite.T(), "@dumpsterqueer@example.org", menchies[0])
|
assert.Equal(suite.T(), "@dumpsterqueer@example.org", menchies[0])
|
||||||
assert.Equal(suite.T(), "@someone_else@testing.best-horse.com", menchies[1])
|
assert.Equal(suite.T(), "@someone_else@testing.best-horse.com", menchies[1])
|
||||||
|
|
@ -57,7 +57,7 @@ func (suite *StatusTestSuite) TestDeriveMentionsOK() {
|
||||||
|
|
||||||
func (suite *StatusTestSuite) TestDeriveMentionsEmpty() {
|
func (suite *StatusTestSuite) TestDeriveMentionsEmpty() {
|
||||||
statusText := ``
|
statusText := ``
|
||||||
menchies := util.DeriveMentionsFromStatus(statusText)
|
menchies := util.DeriveMentionsFromText(statusText)
|
||||||
assert.Len(suite.T(), menchies, 0)
|
assert.Len(suite.T(), menchies, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -74,7 +74,7 @@ func (suite *StatusTestSuite) TestDeriveHashtagsOK() {
|
||||||
|
|
||||||
#111111 thisalsoshouldn'twork#### ##`
|
#111111 thisalsoshouldn'twork#### ##`
|
||||||
|
|
||||||
tags := util.DeriveHashtagsFromStatus(statusText)
|
tags := util.DeriveHashtagsFromText(statusText)
|
||||||
assert.Len(suite.T(), tags, 5)
|
assert.Len(suite.T(), tags, 5)
|
||||||
assert.Equal(suite.T(), "testing123", tags[0])
|
assert.Equal(suite.T(), "testing123", tags[0])
|
||||||
assert.Equal(suite.T(), "also", tags[1])
|
assert.Equal(suite.T(), "also", tags[1])
|
||||||
|
|
@ -97,7 +97,7 @@ Here's some normal text with an :emoji: at the end
|
||||||
:underscores_ok_too:
|
:underscores_ok_too:
|
||||||
`
|
`
|
||||||
|
|
||||||
tags := util.DeriveEmojisFromStatus(statusText)
|
tags := util.DeriveEmojisFromText(statusText)
|
||||||
assert.Len(suite.T(), tags, 7)
|
assert.Len(suite.T(), tags, 7)
|
||||||
assert.Equal(suite.T(), "test", tags[0])
|
assert.Equal(suite.T(), "test", tags[0])
|
||||||
assert.Equal(suite.T(), "another", tags[1])
|
assert.Equal(suite.T(), "another", tags[1])
|
||||||
|
|
@ -115,9 +115,9 @@ func (suite *StatusTestSuite) TestDeriveMultiple() {
|
||||||
|
|
||||||
Text`
|
Text`
|
||||||
|
|
||||||
ms := util.DeriveMentionsFromStatus(statusText)
|
ms := util.DeriveMentionsFromText(statusText)
|
||||||
hs := util.DeriveHashtagsFromStatus(statusText)
|
hs := util.DeriveHashtagsFromText(statusText)
|
||||||
es := util.DeriveEmojisFromStatus(statusText)
|
es := util.DeriveEmojisFromText(statusText)
|
||||||
|
|
||||||
assert.Len(suite.T(), ms, 1)
|
assert.Len(suite.T(), ms, 1)
|
||||||
assert.Equal(suite.T(), "@foss_satan@fossbros-anonymous.io", ms[0])
|
assert.Equal(suite.T(), "@foss_satan@fossbros-anonymous.io", ms[0])
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/mail"
|
"net/mail"
|
||||||
|
|
||||||
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/regexes"
|
"github.com/superseriousbusiness/gotosocial/internal/regexes"
|
||||||
pwv "github.com/wagslane/go-password-validator"
|
pwv "github.com/wagslane/go-password-validator"
|
||||||
"golang.org/x/text/language"
|
"golang.org/x/text/language"
|
||||||
|
|
@ -126,8 +127,14 @@ func Note(note string) error {
|
||||||
|
|
||||||
// Privacy checks that the desired privacy setting is valid
|
// Privacy checks that the desired privacy setting is valid
|
||||||
func Privacy(privacy string) error {
|
func Privacy(privacy string) error {
|
||||||
// TODO: add some validation logic here -- length, characters, etc
|
if privacy == "" {
|
||||||
return nil
|
return fmt.Errorf("empty string for privacy not allowed")
|
||||||
|
}
|
||||||
|
switch apimodel.Visibility(privacy) {
|
||||||
|
case apimodel.VisibilityDirect, apimodel.VisibilityMutualsOnly, apimodel.VisibilityPrivate, apimodel.VisibilityPublic, apimodel.VisibilityUnlisted:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("privacy %s was not recognized", privacy)
|
||||||
}
|
}
|
||||||
|
|
||||||
// EmojiShortcode just runs the given shortcode through the regular expression
|
// EmojiShortcode just runs the given shortcode through the regular expression
|
||||||
|
|
|
||||||
20
vendor/github.com/fatih/color/LICENSE.md
generated
vendored
20
vendor/github.com/fatih/color/LICENSE.md
generated
vendored
|
|
@ -1,20 +0,0 @@
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2013 Fatih Arslan
|
|
||||||
|
|
||||||
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.
|
|
||||||
173
vendor/github.com/fatih/color/README.md
generated
vendored
173
vendor/github.com/fatih/color/README.md
generated
vendored
|
|
@ -1,173 +0,0 @@
|
||||||
# color [](https://github.com/fatih/color/actions) [](https://pkg.go.dev/github.com/fatih/color)
|
|
||||||
|
|
||||||
Color lets you use colorized outputs in terms of [ANSI Escape
|
|
||||||
Codes](http://en.wikipedia.org/wiki/ANSI_escape_code#Colors) in Go (Golang). It
|
|
||||||
has support for Windows too! The API can be used in several ways, pick one that
|
|
||||||
suits you.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
|
|
||||||
## Install
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go get github.com/fatih/color
|
|
||||||
```
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
### Standard colors
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Print with default helper functions
|
|
||||||
color.Cyan("Prints text in cyan.")
|
|
||||||
|
|
||||||
// A newline will be appended automatically
|
|
||||||
color.Blue("Prints %s in blue.", "text")
|
|
||||||
|
|
||||||
// These are using the default foreground colors
|
|
||||||
color.Red("We have red")
|
|
||||||
color.Magenta("And many others ..")
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
### Mix and reuse colors
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Create a new color object
|
|
||||||
c := color.New(color.FgCyan).Add(color.Underline)
|
|
||||||
c.Println("Prints cyan text with an underline.")
|
|
||||||
|
|
||||||
// Or just add them to New()
|
|
||||||
d := color.New(color.FgCyan, color.Bold)
|
|
||||||
d.Printf("This prints bold cyan %s\n", "too!.")
|
|
||||||
|
|
||||||
// Mix up foreground and background colors, create new mixes!
|
|
||||||
red := color.New(color.FgRed)
|
|
||||||
|
|
||||||
boldRed := red.Add(color.Bold)
|
|
||||||
boldRed.Println("This will print text in bold red.")
|
|
||||||
|
|
||||||
whiteBackground := red.Add(color.BgWhite)
|
|
||||||
whiteBackground.Println("Red text with white background.")
|
|
||||||
```
|
|
||||||
|
|
||||||
### Use your own output (io.Writer)
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Use your own io.Writer output
|
|
||||||
color.New(color.FgBlue).Fprintln(myWriter, "blue color!")
|
|
||||||
|
|
||||||
blue := color.New(color.FgBlue)
|
|
||||||
blue.Fprint(writer, "This will print text in blue.")
|
|
||||||
```
|
|
||||||
|
|
||||||
### Custom print functions (PrintFunc)
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Create a custom print function for convenience
|
|
||||||
red := color.New(color.FgRed).PrintfFunc()
|
|
||||||
red("Warning")
|
|
||||||
red("Error: %s", err)
|
|
||||||
|
|
||||||
// Mix up multiple attributes
|
|
||||||
notice := color.New(color.Bold, color.FgGreen).PrintlnFunc()
|
|
||||||
notice("Don't forget this...")
|
|
||||||
```
|
|
||||||
|
|
||||||
### Custom fprint functions (FprintFunc)
|
|
||||||
|
|
||||||
```go
|
|
||||||
blue := color.New(FgBlue).FprintfFunc()
|
|
||||||
blue(myWriter, "important notice: %s", stars)
|
|
||||||
|
|
||||||
// Mix up with multiple attributes
|
|
||||||
success := color.New(color.Bold, color.FgGreen).FprintlnFunc()
|
|
||||||
success(myWriter, "Don't forget this...")
|
|
||||||
```
|
|
||||||
|
|
||||||
### Insert into noncolor strings (SprintFunc)
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Create SprintXxx functions to mix strings with other non-colorized strings:
|
|
||||||
yellow := color.New(color.FgYellow).SprintFunc()
|
|
||||||
red := color.New(color.FgRed).SprintFunc()
|
|
||||||
fmt.Printf("This is a %s and this is %s.\n", yellow("warning"), red("error"))
|
|
||||||
|
|
||||||
info := color.New(color.FgWhite, color.BgGreen).SprintFunc()
|
|
||||||
fmt.Printf("This %s rocks!\n", info("package"))
|
|
||||||
|
|
||||||
// Use helper functions
|
|
||||||
fmt.Println("This", color.RedString("warning"), "should be not neglected.")
|
|
||||||
fmt.Printf("%v %v\n", color.GreenString("Info:"), "an important message.")
|
|
||||||
|
|
||||||
// Windows supported too! Just don't forget to change the output to color.Output
|
|
||||||
fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS"))
|
|
||||||
```
|
|
||||||
|
|
||||||
### Plug into existing code
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Use handy standard colors
|
|
||||||
color.Set(color.FgYellow)
|
|
||||||
|
|
||||||
fmt.Println("Existing text will now be in yellow")
|
|
||||||
fmt.Printf("This one %s\n", "too")
|
|
||||||
|
|
||||||
color.Unset() // Don't forget to unset
|
|
||||||
|
|
||||||
// You can mix up parameters
|
|
||||||
color.Set(color.FgMagenta, color.Bold)
|
|
||||||
defer color.Unset() // Use it in your function
|
|
||||||
|
|
||||||
fmt.Println("All text will now be bold magenta.")
|
|
||||||
```
|
|
||||||
|
|
||||||
### Disable/Enable color
|
|
||||||
|
|
||||||
There might be a case where you want to explicitly disable/enable color output. the
|
|
||||||
`go-isatty` package will automatically disable color output for non-tty output streams
|
|
||||||
(for example if the output were piped directly to `less`)
|
|
||||||
|
|
||||||
`Color` has support to disable/enable colors both globally and for single color
|
|
||||||
definitions. For example suppose you have a CLI app and a `--no-color` bool flag. You
|
|
||||||
can easily disable the color output with:
|
|
||||||
|
|
||||||
```go
|
|
||||||
|
|
||||||
var flagNoColor = flag.Bool("no-color", false, "Disable color output")
|
|
||||||
|
|
||||||
if *flagNoColor {
|
|
||||||
color.NoColor = true // disables colorized output
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
It also has support for single color definitions (local). You can
|
|
||||||
disable/enable color output on the fly:
|
|
||||||
|
|
||||||
```go
|
|
||||||
c := color.New(color.FgCyan)
|
|
||||||
c.Println("Prints cyan text")
|
|
||||||
|
|
||||||
c.DisableColor()
|
|
||||||
c.Println("This is printed without any color")
|
|
||||||
|
|
||||||
c.EnableColor()
|
|
||||||
c.Println("This prints again cyan...")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Todo
|
|
||||||
|
|
||||||
* Save/Return previous values
|
|
||||||
* Evaluate fmt.Formatter interface
|
|
||||||
|
|
||||||
|
|
||||||
## Credits
|
|
||||||
|
|
||||||
* [Fatih Arslan](https://github.com/fatih)
|
|
||||||
* Windows support via @mattn: [colorable](https://github.com/mattn/go-colorable)
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
The MIT License (MIT) - see [`LICENSE.md`](https://github.com/fatih/color/blob/master/LICENSE.md) for more details
|
|
||||||
|
|
||||||
603
vendor/github.com/fatih/color/color.go
generated
vendored
603
vendor/github.com/fatih/color/color.go
generated
vendored
|
|
@ -1,603 +0,0 @@
|
||||||
package color
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/mattn/go-colorable"
|
|
||||||
"github.com/mattn/go-isatty"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// NoColor defines if the output is colorized or not. It's dynamically set to
|
|
||||||
// false or true based on the stdout's file descriptor referring to a terminal
|
|
||||||
// or not. This is a global option and affects all colors. For more control
|
|
||||||
// over each color block use the methods DisableColor() individually.
|
|
||||||
NoColor = os.Getenv("TERM") == "dumb" ||
|
|
||||||
(!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd()))
|
|
||||||
|
|
||||||
// Output defines the standard output of the print functions. By default
|
|
||||||
// os.Stdout is used.
|
|
||||||
Output = colorable.NewColorableStdout()
|
|
||||||
|
|
||||||
// Error defines a color supporting writer for os.Stderr.
|
|
||||||
Error = colorable.NewColorableStderr()
|
|
||||||
|
|
||||||
// colorsCache is used to reduce the count of created Color objects and
|
|
||||||
// allows to reuse already created objects with required Attribute.
|
|
||||||
colorsCache = make(map[Attribute]*Color)
|
|
||||||
colorsCacheMu sync.Mutex // protects colorsCache
|
|
||||||
)
|
|
||||||
|
|
||||||
// Color defines a custom color object which is defined by SGR parameters.
|
|
||||||
type Color struct {
|
|
||||||
params []Attribute
|
|
||||||
noColor *bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attribute defines a single SGR Code
|
|
||||||
type Attribute int
|
|
||||||
|
|
||||||
const escape = "\x1b"
|
|
||||||
|
|
||||||
// Base attributes
|
|
||||||
const (
|
|
||||||
Reset Attribute = iota
|
|
||||||
Bold
|
|
||||||
Faint
|
|
||||||
Italic
|
|
||||||
Underline
|
|
||||||
BlinkSlow
|
|
||||||
BlinkRapid
|
|
||||||
ReverseVideo
|
|
||||||
Concealed
|
|
||||||
CrossedOut
|
|
||||||
)
|
|
||||||
|
|
||||||
// Foreground text colors
|
|
||||||
const (
|
|
||||||
FgBlack Attribute = iota + 30
|
|
||||||
FgRed
|
|
||||||
FgGreen
|
|
||||||
FgYellow
|
|
||||||
FgBlue
|
|
||||||
FgMagenta
|
|
||||||
FgCyan
|
|
||||||
FgWhite
|
|
||||||
)
|
|
||||||
|
|
||||||
// Foreground Hi-Intensity text colors
|
|
||||||
const (
|
|
||||||
FgHiBlack Attribute = iota + 90
|
|
||||||
FgHiRed
|
|
||||||
FgHiGreen
|
|
||||||
FgHiYellow
|
|
||||||
FgHiBlue
|
|
||||||
FgHiMagenta
|
|
||||||
FgHiCyan
|
|
||||||
FgHiWhite
|
|
||||||
)
|
|
||||||
|
|
||||||
// Background text colors
|
|
||||||
const (
|
|
||||||
BgBlack Attribute = iota + 40
|
|
||||||
BgRed
|
|
||||||
BgGreen
|
|
||||||
BgYellow
|
|
||||||
BgBlue
|
|
||||||
BgMagenta
|
|
||||||
BgCyan
|
|
||||||
BgWhite
|
|
||||||
)
|
|
||||||
|
|
||||||
// Background Hi-Intensity text colors
|
|
||||||
const (
|
|
||||||
BgHiBlack Attribute = iota + 100
|
|
||||||
BgHiRed
|
|
||||||
BgHiGreen
|
|
||||||
BgHiYellow
|
|
||||||
BgHiBlue
|
|
||||||
BgHiMagenta
|
|
||||||
BgHiCyan
|
|
||||||
BgHiWhite
|
|
||||||
)
|
|
||||||
|
|
||||||
// New returns a newly created color object.
|
|
||||||
func New(value ...Attribute) *Color {
|
|
||||||
c := &Color{params: make([]Attribute, 0)}
|
|
||||||
c.Add(value...)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set sets the given parameters immediately. It will change the color of
|
|
||||||
// output with the given SGR parameters until color.Unset() is called.
|
|
||||||
func Set(p ...Attribute) *Color {
|
|
||||||
c := New(p...)
|
|
||||||
c.Set()
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unset resets all escape attributes and clears the output. Usually should
|
|
||||||
// be called after Set().
|
|
||||||
func Unset() {
|
|
||||||
if NoColor {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintf(Output, "%s[%dm", escape, Reset)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set sets the SGR sequence.
|
|
||||||
func (c *Color) Set() *Color {
|
|
||||||
if c.isNoColorSet() {
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintf(Output, c.format())
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Color) unset() {
|
|
||||||
if c.isNoColorSet() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
Unset()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Color) setWriter(w io.Writer) *Color {
|
|
||||||
if c.isNoColorSet() {
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintf(w, c.format())
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Color) unsetWriter(w io.Writer) {
|
|
||||||
if c.isNoColorSet() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if NoColor {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintf(w, "%s[%dm", escape, Reset)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add is used to chain SGR parameters. Use as many as parameters to combine
|
|
||||||
// and create custom color objects. Example: Add(color.FgRed, color.Underline).
|
|
||||||
func (c *Color) Add(value ...Attribute) *Color {
|
|
||||||
c.params = append(c.params, value...)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Color) prepend(value Attribute) {
|
|
||||||
c.params = append(c.params, 0)
|
|
||||||
copy(c.params[1:], c.params[0:])
|
|
||||||
c.params[0] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fprint formats using the default formats for its operands and writes to w.
|
|
||||||
// Spaces are added between operands when neither is a string.
|
|
||||||
// It returns the number of bytes written and any write error encountered.
|
|
||||||
// On Windows, users should wrap w with colorable.NewColorable() if w is of
|
|
||||||
// type *os.File.
|
|
||||||
func (c *Color) Fprint(w io.Writer, a ...interface{}) (n int, err error) {
|
|
||||||
c.setWriter(w)
|
|
||||||
defer c.unsetWriter(w)
|
|
||||||
|
|
||||||
return fmt.Fprint(w, a...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print formats using the default formats for its operands and writes to
|
|
||||||
// standard output. Spaces are added between operands when neither is a
|
|
||||||
// string. It returns the number of bytes written and any write error
|
|
||||||
// encountered. This is the standard fmt.Print() method wrapped with the given
|
|
||||||
// color.
|
|
||||||
func (c *Color) Print(a ...interface{}) (n int, err error) {
|
|
||||||
c.Set()
|
|
||||||
defer c.unset()
|
|
||||||
|
|
||||||
return fmt.Fprint(Output, a...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fprintf formats according to a format specifier and writes to w.
|
|
||||||
// It returns the number of bytes written and any write error encountered.
|
|
||||||
// On Windows, users should wrap w with colorable.NewColorable() if w is of
|
|
||||||
// type *os.File.
|
|
||||||
func (c *Color) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
|
|
||||||
c.setWriter(w)
|
|
||||||
defer c.unsetWriter(w)
|
|
||||||
|
|
||||||
return fmt.Fprintf(w, format, a...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Printf formats according to a format specifier and writes to standard output.
|
|
||||||
// It returns the number of bytes written and any write error encountered.
|
|
||||||
// This is the standard fmt.Printf() method wrapped with the given color.
|
|
||||||
func (c *Color) Printf(format string, a ...interface{}) (n int, err error) {
|
|
||||||
c.Set()
|
|
||||||
defer c.unset()
|
|
||||||
|
|
||||||
return fmt.Fprintf(Output, format, a...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fprintln formats using the default formats for its operands and writes to w.
|
|
||||||
// Spaces are always added between operands and a newline is appended.
|
|
||||||
// On Windows, users should wrap w with colorable.NewColorable() if w is of
|
|
||||||
// type *os.File.
|
|
||||||
func (c *Color) Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
|
|
||||||
c.setWriter(w)
|
|
||||||
defer c.unsetWriter(w)
|
|
||||||
|
|
||||||
return fmt.Fprintln(w, a...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Println formats using the default formats for its operands and writes to
|
|
||||||
// standard output. Spaces are always added between operands and a newline is
|
|
||||||
// appended. It returns the number of bytes written and any write error
|
|
||||||
// encountered. This is the standard fmt.Print() method wrapped with the given
|
|
||||||
// color.
|
|
||||||
func (c *Color) Println(a ...interface{}) (n int, err error) {
|
|
||||||
c.Set()
|
|
||||||
defer c.unset()
|
|
||||||
|
|
||||||
return fmt.Fprintln(Output, a...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sprint is just like Print, but returns a string instead of printing it.
|
|
||||||
func (c *Color) Sprint(a ...interface{}) string {
|
|
||||||
return c.wrap(fmt.Sprint(a...))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sprintln is just like Println, but returns a string instead of printing it.
|
|
||||||
func (c *Color) Sprintln(a ...interface{}) string {
|
|
||||||
return c.wrap(fmt.Sprintln(a...))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sprintf is just like Printf, but returns a string instead of printing it.
|
|
||||||
func (c *Color) Sprintf(format string, a ...interface{}) string {
|
|
||||||
return c.wrap(fmt.Sprintf(format, a...))
|
|
||||||
}
|
|
||||||
|
|
||||||
// FprintFunc returns a new function that prints the passed arguments as
|
|
||||||
// colorized with color.Fprint().
|
|
||||||
func (c *Color) FprintFunc() func(w io.Writer, a ...interface{}) {
|
|
||||||
return func(w io.Writer, a ...interface{}) {
|
|
||||||
c.Fprint(w, a...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrintFunc returns a new function that prints the passed arguments as
|
|
||||||
// colorized with color.Print().
|
|
||||||
func (c *Color) PrintFunc() func(a ...interface{}) {
|
|
||||||
return func(a ...interface{}) {
|
|
||||||
c.Print(a...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FprintfFunc returns a new function that prints the passed arguments as
|
|
||||||
// colorized with color.Fprintf().
|
|
||||||
func (c *Color) FprintfFunc() func(w io.Writer, format string, a ...interface{}) {
|
|
||||||
return func(w io.Writer, format string, a ...interface{}) {
|
|
||||||
c.Fprintf(w, format, a...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrintfFunc returns a new function that prints the passed arguments as
|
|
||||||
// colorized with color.Printf().
|
|
||||||
func (c *Color) PrintfFunc() func(format string, a ...interface{}) {
|
|
||||||
return func(format string, a ...interface{}) {
|
|
||||||
c.Printf(format, a...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FprintlnFunc returns a new function that prints the passed arguments as
|
|
||||||
// colorized with color.Fprintln().
|
|
||||||
func (c *Color) FprintlnFunc() func(w io.Writer, a ...interface{}) {
|
|
||||||
return func(w io.Writer, a ...interface{}) {
|
|
||||||
c.Fprintln(w, a...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrintlnFunc returns a new function that prints the passed arguments as
|
|
||||||
// colorized with color.Println().
|
|
||||||
func (c *Color) PrintlnFunc() func(a ...interface{}) {
|
|
||||||
return func(a ...interface{}) {
|
|
||||||
c.Println(a...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SprintFunc returns a new function that returns colorized strings for the
|
|
||||||
// given arguments with fmt.Sprint(). Useful to put into or mix into other
|
|
||||||
// string. Windows users should use this in conjunction with color.Output, example:
|
|
||||||
//
|
|
||||||
// put := New(FgYellow).SprintFunc()
|
|
||||||
// fmt.Fprintf(color.Output, "This is a %s", put("warning"))
|
|
||||||
func (c *Color) SprintFunc() func(a ...interface{}) string {
|
|
||||||
return func(a ...interface{}) string {
|
|
||||||
return c.wrap(fmt.Sprint(a...))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SprintfFunc returns a new function that returns colorized strings for the
|
|
||||||
// given arguments with fmt.Sprintf(). Useful to put into or mix into other
|
|
||||||
// string. Windows users should use this in conjunction with color.Output.
|
|
||||||
func (c *Color) SprintfFunc() func(format string, a ...interface{}) string {
|
|
||||||
return func(format string, a ...interface{}) string {
|
|
||||||
return c.wrap(fmt.Sprintf(format, a...))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SprintlnFunc returns a new function that returns colorized strings for the
|
|
||||||
// given arguments with fmt.Sprintln(). Useful to put into or mix into other
|
|
||||||
// string. Windows users should use this in conjunction with color.Output.
|
|
||||||
func (c *Color) SprintlnFunc() func(a ...interface{}) string {
|
|
||||||
return func(a ...interface{}) string {
|
|
||||||
return c.wrap(fmt.Sprintln(a...))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// sequence returns a formatted SGR sequence to be plugged into a "\x1b[...m"
|
|
||||||
// an example output might be: "1;36" -> bold cyan
|
|
||||||
func (c *Color) sequence() string {
|
|
||||||
format := make([]string, len(c.params))
|
|
||||||
for i, v := range c.params {
|
|
||||||
format[i] = strconv.Itoa(int(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings.Join(format, ";")
|
|
||||||
}
|
|
||||||
|
|
||||||
// wrap wraps the s string with the colors attributes. The string is ready to
|
|
||||||
// be printed.
|
|
||||||
func (c *Color) wrap(s string) string {
|
|
||||||
if c.isNoColorSet() {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.format() + s + c.unformat()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Color) format() string {
|
|
||||||
return fmt.Sprintf("%s[%sm", escape, c.sequence())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Color) unformat() string {
|
|
||||||
return fmt.Sprintf("%s[%dm", escape, Reset)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DisableColor disables the color output. Useful to not change any existing
|
|
||||||
// code and still being able to output. Can be used for flags like
|
|
||||||
// "--no-color". To enable back use EnableColor() method.
|
|
||||||
func (c *Color) DisableColor() {
|
|
||||||
c.noColor = boolPtr(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
// EnableColor enables the color output. Use it in conjunction with
|
|
||||||
// DisableColor(). Otherwise this method has no side effects.
|
|
||||||
func (c *Color) EnableColor() {
|
|
||||||
c.noColor = boolPtr(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Color) isNoColorSet() bool {
|
|
||||||
// check first if we have user setted action
|
|
||||||
if c.noColor != nil {
|
|
||||||
return *c.noColor
|
|
||||||
}
|
|
||||||
|
|
||||||
// if not return the global option, which is disabled by default
|
|
||||||
return NoColor
|
|
||||||
}
|
|
||||||
|
|
||||||
// Equals returns a boolean value indicating whether two colors are equal.
|
|
||||||
func (c *Color) Equals(c2 *Color) bool {
|
|
||||||
if len(c.params) != len(c2.params) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, attr := range c.params {
|
|
||||||
if !c2.attrExists(attr) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Color) attrExists(a Attribute) bool {
|
|
||||||
for _, attr := range c.params {
|
|
||||||
if attr == a {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func boolPtr(v bool) *bool {
|
|
||||||
return &v
|
|
||||||
}
|
|
||||||
|
|
||||||
func getCachedColor(p Attribute) *Color {
|
|
||||||
colorsCacheMu.Lock()
|
|
||||||
defer colorsCacheMu.Unlock()
|
|
||||||
|
|
||||||
c, ok := colorsCache[p]
|
|
||||||
if !ok {
|
|
||||||
c = New(p)
|
|
||||||
colorsCache[p] = c
|
|
||||||
}
|
|
||||||
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
func colorPrint(format string, p Attribute, a ...interface{}) {
|
|
||||||
c := getCachedColor(p)
|
|
||||||
|
|
||||||
if !strings.HasSuffix(format, "\n") {
|
|
||||||
format += "\n"
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(a) == 0 {
|
|
||||||
c.Print(format)
|
|
||||||
} else {
|
|
||||||
c.Printf(format, a...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func colorString(format string, p Attribute, a ...interface{}) string {
|
|
||||||
c := getCachedColor(p)
|
|
||||||
|
|
||||||
if len(a) == 0 {
|
|
||||||
return c.SprintFunc()(format)
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.SprintfFunc()(format, a...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Black is a convenient helper function to print with black foreground. A
|
|
||||||
// newline is appended to format by default.
|
|
||||||
func Black(format string, a ...interface{}) { colorPrint(format, FgBlack, a...) }
|
|
||||||
|
|
||||||
// Red is a convenient helper function to print with red foreground. A
|
|
||||||
// newline is appended to format by default.
|
|
||||||
func Red(format string, a ...interface{}) { colorPrint(format, FgRed, a...) }
|
|
||||||
|
|
||||||
// Green is a convenient helper function to print with green foreground. A
|
|
||||||
// newline is appended to format by default.
|
|
||||||
func Green(format string, a ...interface{}) { colorPrint(format, FgGreen, a...) }
|
|
||||||
|
|
||||||
// Yellow is a convenient helper function to print with yellow foreground.
|
|
||||||
// A newline is appended to format by default.
|
|
||||||
func Yellow(format string, a ...interface{}) { colorPrint(format, FgYellow, a...) }
|
|
||||||
|
|
||||||
// Blue is a convenient helper function to print with blue foreground. A
|
|
||||||
// newline is appended to format by default.
|
|
||||||
func Blue(format string, a ...interface{}) { colorPrint(format, FgBlue, a...) }
|
|
||||||
|
|
||||||
// Magenta is a convenient helper function to print with magenta foreground.
|
|
||||||
// A newline is appended to format by default.
|
|
||||||
func Magenta(format string, a ...interface{}) { colorPrint(format, FgMagenta, a...) }
|
|
||||||
|
|
||||||
// Cyan is a convenient helper function to print with cyan foreground. A
|
|
||||||
// newline is appended to format by default.
|
|
||||||
func Cyan(format string, a ...interface{}) { colorPrint(format, FgCyan, a...) }
|
|
||||||
|
|
||||||
// White is a convenient helper function to print with white foreground. A
|
|
||||||
// newline is appended to format by default.
|
|
||||||
func White(format string, a ...interface{}) { colorPrint(format, FgWhite, a...) }
|
|
||||||
|
|
||||||
// BlackString is a convenient helper function to return a string with black
|
|
||||||
// foreground.
|
|
||||||
func BlackString(format string, a ...interface{}) string { return colorString(format, FgBlack, a...) }
|
|
||||||
|
|
||||||
// RedString is a convenient helper function to return a string with red
|
|
||||||
// foreground.
|
|
||||||
func RedString(format string, a ...interface{}) string { return colorString(format, FgRed, a...) }
|
|
||||||
|
|
||||||
// GreenString is a convenient helper function to return a string with green
|
|
||||||
// foreground.
|
|
||||||
func GreenString(format string, a ...interface{}) string { return colorString(format, FgGreen, a...) }
|
|
||||||
|
|
||||||
// YellowString is a convenient helper function to return a string with yellow
|
|
||||||
// foreground.
|
|
||||||
func YellowString(format string, a ...interface{}) string { return colorString(format, FgYellow, a...) }
|
|
||||||
|
|
||||||
// BlueString is a convenient helper function to return a string with blue
|
|
||||||
// foreground.
|
|
||||||
func BlueString(format string, a ...interface{}) string { return colorString(format, FgBlue, a...) }
|
|
||||||
|
|
||||||
// MagentaString is a convenient helper function to return a string with magenta
|
|
||||||
// foreground.
|
|
||||||
func MagentaString(format string, a ...interface{}) string {
|
|
||||||
return colorString(format, FgMagenta, a...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CyanString is a convenient helper function to return a string with cyan
|
|
||||||
// foreground.
|
|
||||||
func CyanString(format string, a ...interface{}) string { return colorString(format, FgCyan, a...) }
|
|
||||||
|
|
||||||
// WhiteString is a convenient helper function to return a string with white
|
|
||||||
// foreground.
|
|
||||||
func WhiteString(format string, a ...interface{}) string { return colorString(format, FgWhite, a...) }
|
|
||||||
|
|
||||||
// HiBlack is a convenient helper function to print with hi-intensity black foreground. A
|
|
||||||
// newline is appended to format by default.
|
|
||||||
func HiBlack(format string, a ...interface{}) { colorPrint(format, FgHiBlack, a...) }
|
|
||||||
|
|
||||||
// HiRed is a convenient helper function to print with hi-intensity red foreground. A
|
|
||||||
// newline is appended to format by default.
|
|
||||||
func HiRed(format string, a ...interface{}) { colorPrint(format, FgHiRed, a...) }
|
|
||||||
|
|
||||||
// HiGreen is a convenient helper function to print with hi-intensity green foreground. A
|
|
||||||
// newline is appended to format by default.
|
|
||||||
func HiGreen(format string, a ...interface{}) { colorPrint(format, FgHiGreen, a...) }
|
|
||||||
|
|
||||||
// HiYellow is a convenient helper function to print with hi-intensity yellow foreground.
|
|
||||||
// A newline is appended to format by default.
|
|
||||||
func HiYellow(format string, a ...interface{}) { colorPrint(format, FgHiYellow, a...) }
|
|
||||||
|
|
||||||
// HiBlue is a convenient helper function to print with hi-intensity blue foreground. A
|
|
||||||
// newline is appended to format by default.
|
|
||||||
func HiBlue(format string, a ...interface{}) { colorPrint(format, FgHiBlue, a...) }
|
|
||||||
|
|
||||||
// HiMagenta is a convenient helper function to print with hi-intensity magenta foreground.
|
|
||||||
// A newline is appended to format by default.
|
|
||||||
func HiMagenta(format string, a ...interface{}) { colorPrint(format, FgHiMagenta, a...) }
|
|
||||||
|
|
||||||
// HiCyan is a convenient helper function to print with hi-intensity cyan foreground. A
|
|
||||||
// newline is appended to format by default.
|
|
||||||
func HiCyan(format string, a ...interface{}) { colorPrint(format, FgHiCyan, a...) }
|
|
||||||
|
|
||||||
// HiWhite is a convenient helper function to print with hi-intensity white foreground. A
|
|
||||||
// newline is appended to format by default.
|
|
||||||
func HiWhite(format string, a ...interface{}) { colorPrint(format, FgHiWhite, a...) }
|
|
||||||
|
|
||||||
// HiBlackString is a convenient helper function to return a string with hi-intensity black
|
|
||||||
// foreground.
|
|
||||||
func HiBlackString(format string, a ...interface{}) string {
|
|
||||||
return colorString(format, FgHiBlack, a...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// HiRedString is a convenient helper function to return a string with hi-intensity red
|
|
||||||
// foreground.
|
|
||||||
func HiRedString(format string, a ...interface{}) string { return colorString(format, FgHiRed, a...) }
|
|
||||||
|
|
||||||
// HiGreenString is a convenient helper function to return a string with hi-intensity green
|
|
||||||
// foreground.
|
|
||||||
func HiGreenString(format string, a ...interface{}) string {
|
|
||||||
return colorString(format, FgHiGreen, a...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// HiYellowString is a convenient helper function to return a string with hi-intensity yellow
|
|
||||||
// foreground.
|
|
||||||
func HiYellowString(format string, a ...interface{}) string {
|
|
||||||
return colorString(format, FgHiYellow, a...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// HiBlueString is a convenient helper function to return a string with hi-intensity blue
|
|
||||||
// foreground.
|
|
||||||
func HiBlueString(format string, a ...interface{}) string { return colorString(format, FgHiBlue, a...) }
|
|
||||||
|
|
||||||
// HiMagentaString is a convenient helper function to return a string with hi-intensity magenta
|
|
||||||
// foreground.
|
|
||||||
func HiMagentaString(format string, a ...interface{}) string {
|
|
||||||
return colorString(format, FgHiMagenta, a...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// HiCyanString is a convenient helper function to return a string with hi-intensity cyan
|
|
||||||
// foreground.
|
|
||||||
func HiCyanString(format string, a ...interface{}) string { return colorString(format, FgHiCyan, a...) }
|
|
||||||
|
|
||||||
// HiWhiteString is a convenient helper function to return a string with hi-intensity white
|
|
||||||
// foreground.
|
|
||||||
func HiWhiteString(format string, a ...interface{}) string {
|
|
||||||
return colorString(format, FgHiWhite, a...)
|
|
||||||
}
|
|
||||||
133
vendor/github.com/fatih/color/doc.go
generated
vendored
133
vendor/github.com/fatih/color/doc.go
generated
vendored
|
|
@ -1,133 +0,0 @@
|
||||||
/*
|
|
||||||
Package color is an ANSI color package to output colorized or SGR defined
|
|
||||||
output to the standard output. The API can be used in several way, pick one
|
|
||||||
that suits you.
|
|
||||||
|
|
||||||
Use simple and default helper functions with predefined foreground colors:
|
|
||||||
|
|
||||||
color.Cyan("Prints text in cyan.")
|
|
||||||
|
|
||||||
// a newline will be appended automatically
|
|
||||||
color.Blue("Prints %s in blue.", "text")
|
|
||||||
|
|
||||||
// More default foreground colors..
|
|
||||||
color.Red("We have red")
|
|
||||||
color.Yellow("Yellow color too!")
|
|
||||||
color.Magenta("And many others ..")
|
|
||||||
|
|
||||||
// Hi-intensity colors
|
|
||||||
color.HiGreen("Bright green color.")
|
|
||||||
color.HiBlack("Bright black means gray..")
|
|
||||||
color.HiWhite("Shiny white color!")
|
|
||||||
|
|
||||||
However there are times where custom color mixes are required. Below are some
|
|
||||||
examples to create custom color objects and use the print functions of each
|
|
||||||
separate color object.
|
|
||||||
|
|
||||||
// Create a new color object
|
|
||||||
c := color.New(color.FgCyan).Add(color.Underline)
|
|
||||||
c.Println("Prints cyan text with an underline.")
|
|
||||||
|
|
||||||
// Or just add them to New()
|
|
||||||
d := color.New(color.FgCyan, color.Bold)
|
|
||||||
d.Printf("This prints bold cyan %s\n", "too!.")
|
|
||||||
|
|
||||||
|
|
||||||
// Mix up foreground and background colors, create new mixes!
|
|
||||||
red := color.New(color.FgRed)
|
|
||||||
|
|
||||||
boldRed := red.Add(color.Bold)
|
|
||||||
boldRed.Println("This will print text in bold red.")
|
|
||||||
|
|
||||||
whiteBackground := red.Add(color.BgWhite)
|
|
||||||
whiteBackground.Println("Red text with White background.")
|
|
||||||
|
|
||||||
// Use your own io.Writer output
|
|
||||||
color.New(color.FgBlue).Fprintln(myWriter, "blue color!")
|
|
||||||
|
|
||||||
blue := color.New(color.FgBlue)
|
|
||||||
blue.Fprint(myWriter, "This will print text in blue.")
|
|
||||||
|
|
||||||
You can create PrintXxx functions to simplify even more:
|
|
||||||
|
|
||||||
// Create a custom print function for convenient
|
|
||||||
red := color.New(color.FgRed).PrintfFunc()
|
|
||||||
red("warning")
|
|
||||||
red("error: %s", err)
|
|
||||||
|
|
||||||
// Mix up multiple attributes
|
|
||||||
notice := color.New(color.Bold, color.FgGreen).PrintlnFunc()
|
|
||||||
notice("don't forget this...")
|
|
||||||
|
|
||||||
You can also FprintXxx functions to pass your own io.Writer:
|
|
||||||
|
|
||||||
blue := color.New(FgBlue).FprintfFunc()
|
|
||||||
blue(myWriter, "important notice: %s", stars)
|
|
||||||
|
|
||||||
// Mix up with multiple attributes
|
|
||||||
success := color.New(color.Bold, color.FgGreen).FprintlnFunc()
|
|
||||||
success(myWriter, don't forget this...")
|
|
||||||
|
|
||||||
|
|
||||||
Or create SprintXxx functions to mix strings with other non-colorized strings:
|
|
||||||
|
|
||||||
yellow := New(FgYellow).SprintFunc()
|
|
||||||
red := New(FgRed).SprintFunc()
|
|
||||||
|
|
||||||
fmt.Printf("this is a %s and this is %s.\n", yellow("warning"), red("error"))
|
|
||||||
|
|
||||||
info := New(FgWhite, BgGreen).SprintFunc()
|
|
||||||
fmt.Printf("this %s rocks!\n", info("package"))
|
|
||||||
|
|
||||||
Windows support is enabled by default. All Print functions work as intended.
|
|
||||||
However only for color.SprintXXX functions, user should use fmt.FprintXXX and
|
|
||||||
set the output to color.Output:
|
|
||||||
|
|
||||||
fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS"))
|
|
||||||
|
|
||||||
info := New(FgWhite, BgGreen).SprintFunc()
|
|
||||||
fmt.Fprintf(color.Output, "this %s rocks!\n", info("package"))
|
|
||||||
|
|
||||||
Using with existing code is possible. Just use the Set() method to set the
|
|
||||||
standard output to the given parameters. That way a rewrite of an existing
|
|
||||||
code is not required.
|
|
||||||
|
|
||||||
// Use handy standard colors.
|
|
||||||
color.Set(color.FgYellow)
|
|
||||||
|
|
||||||
fmt.Println("Existing text will be now in Yellow")
|
|
||||||
fmt.Printf("This one %s\n", "too")
|
|
||||||
|
|
||||||
color.Unset() // don't forget to unset
|
|
||||||
|
|
||||||
// You can mix up parameters
|
|
||||||
color.Set(color.FgMagenta, color.Bold)
|
|
||||||
defer color.Unset() // use it in your function
|
|
||||||
|
|
||||||
fmt.Println("All text will be now bold magenta.")
|
|
||||||
|
|
||||||
There might be a case where you want to disable color output (for example to
|
|
||||||
pipe the standard output of your app to somewhere else). `Color` has support to
|
|
||||||
disable colors both globally and for single color definition. For example
|
|
||||||
suppose you have a CLI app and a `--no-color` bool flag. You can easily disable
|
|
||||||
the color output with:
|
|
||||||
|
|
||||||
var flagNoColor = flag.Bool("no-color", false, "Disable color output")
|
|
||||||
|
|
||||||
if *flagNoColor {
|
|
||||||
color.NoColor = true // disables colorized output
|
|
||||||
}
|
|
||||||
|
|
||||||
It also has support for single color definitions (local). You can
|
|
||||||
disable/enable color output on the fly:
|
|
||||||
|
|
||||||
c := color.New(color.FgCyan)
|
|
||||||
c.Println("Prints cyan text")
|
|
||||||
|
|
||||||
c.DisableColor()
|
|
||||||
c.Println("This is printed without any color")
|
|
||||||
|
|
||||||
c.EnableColor()
|
|
||||||
c.Println("This prints again cyan...")
|
|
||||||
*/
|
|
||||||
package color
|
|
||||||
15
vendor/github.com/mattn/go-colorable/.travis.yml
generated
vendored
15
vendor/github.com/mattn/go-colorable/.travis.yml
generated
vendored
|
|
@ -1,15 +0,0 @@
|
||||||
language: go
|
|
||||||
sudo: false
|
|
||||||
go:
|
|
||||||
- 1.13.x
|
|
||||||
- tip
|
|
||||||
|
|
||||||
before_install:
|
|
||||||
- go get -t -v ./...
|
|
||||||
|
|
||||||
script:
|
|
||||||
- ./go.test.sh
|
|
||||||
|
|
||||||
after_success:
|
|
||||||
- bash <(curl -s https://codecov.io/bash)
|
|
||||||
|
|
||||||
21
vendor/github.com/mattn/go-colorable/LICENSE
generated
vendored
21
vendor/github.com/mattn/go-colorable/LICENSE
generated
vendored
|
|
@ -1,21 +0,0 @@
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2016 Yasuhiro Matsumoto
|
|
||||||
|
|
||||||
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.
|
|
||||||
48
vendor/github.com/mattn/go-colorable/README.md
generated
vendored
48
vendor/github.com/mattn/go-colorable/README.md
generated
vendored
|
|
@ -1,48 +0,0 @@
|
||||||
# go-colorable
|
|
||||||
|
|
||||||
[](https://travis-ci.org/mattn/go-colorable)
|
|
||||||
[](https://codecov.io/gh/mattn/go-colorable)
|
|
||||||
[](http://godoc.org/github.com/mattn/go-colorable)
|
|
||||||
[](https://goreportcard.com/report/mattn/go-colorable)
|
|
||||||
|
|
||||||
Colorable writer for windows.
|
|
||||||
|
|
||||||
For example, most of logger packages doesn't show colors on windows. (I know we can do it with ansicon. But I don't want.)
|
|
||||||
This package is possible to handle escape sequence for ansi color on windows.
|
|
||||||
|
|
||||||
## Too Bad!
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
|
|
||||||
## So Good!
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```go
|
|
||||||
logrus.SetFormatter(&logrus.TextFormatter{ForceColors: true})
|
|
||||||
logrus.SetOutput(colorable.NewColorableStdout())
|
|
||||||
|
|
||||||
logrus.Info("succeeded")
|
|
||||||
logrus.Warn("not correct")
|
|
||||||
logrus.Error("something error")
|
|
||||||
logrus.Fatal("panic")
|
|
||||||
```
|
|
||||||
|
|
||||||
You can compile above code on non-windows OSs.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```
|
|
||||||
$ go get github.com/mattn/go-colorable
|
|
||||||
```
|
|
||||||
|
|
||||||
# License
|
|
||||||
|
|
||||||
MIT
|
|
||||||
|
|
||||||
# Author
|
|
||||||
|
|
||||||
Yasuhiro Matsumoto (a.k.a mattn)
|
|
||||||
37
vendor/github.com/mattn/go-colorable/colorable_appengine.go
generated
vendored
37
vendor/github.com/mattn/go-colorable/colorable_appengine.go
generated
vendored
|
|
@ -1,37 +0,0 @@
|
||||||
// +build appengine
|
|
||||||
|
|
||||||
package colorable
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
_ "github.com/mattn/go-isatty"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewColorable returns new instance of Writer which handles escape sequence.
|
|
||||||
func NewColorable(file *os.File) io.Writer {
|
|
||||||
if file == nil {
|
|
||||||
panic("nil passed instead of *os.File to NewColorable()")
|
|
||||||
}
|
|
||||||
|
|
||||||
return file
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewColorableStdout returns new instance of Writer which handles escape sequence for stdout.
|
|
||||||
func NewColorableStdout() io.Writer {
|
|
||||||
return os.Stdout
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewColorableStderr returns new instance of Writer which handles escape sequence for stderr.
|
|
||||||
func NewColorableStderr() io.Writer {
|
|
||||||
return os.Stderr
|
|
||||||
}
|
|
||||||
|
|
||||||
// EnableColorsStdout enable colors if possible.
|
|
||||||
func EnableColorsStdout(enabled *bool) func() {
|
|
||||||
if enabled != nil {
|
|
||||||
*enabled = true
|
|
||||||
}
|
|
||||||
return func() {}
|
|
||||||
}
|
|
||||||
38
vendor/github.com/mattn/go-colorable/colorable_others.go
generated
vendored
38
vendor/github.com/mattn/go-colorable/colorable_others.go
generated
vendored
|
|
@ -1,38 +0,0 @@
|
||||||
// +build !windows
|
|
||||||
// +build !appengine
|
|
||||||
|
|
||||||
package colorable
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
_ "github.com/mattn/go-isatty"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewColorable returns new instance of Writer which handles escape sequence.
|
|
||||||
func NewColorable(file *os.File) io.Writer {
|
|
||||||
if file == nil {
|
|
||||||
panic("nil passed instead of *os.File to NewColorable()")
|
|
||||||
}
|
|
||||||
|
|
||||||
return file
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewColorableStdout returns new instance of Writer which handles escape sequence for stdout.
|
|
||||||
func NewColorableStdout() io.Writer {
|
|
||||||
return os.Stdout
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewColorableStderr returns new instance of Writer which handles escape sequence for stderr.
|
|
||||||
func NewColorableStderr() io.Writer {
|
|
||||||
return os.Stderr
|
|
||||||
}
|
|
||||||
|
|
||||||
// EnableColorsStdout enable colors if possible.
|
|
||||||
func EnableColorsStdout(enabled *bool) func() {
|
|
||||||
if enabled != nil {
|
|
||||||
*enabled = true
|
|
||||||
}
|
|
||||||
return func() {}
|
|
||||||
}
|
|
||||||
1043
vendor/github.com/mattn/go-colorable/colorable_windows.go
generated
vendored
1043
vendor/github.com/mattn/go-colorable/colorable_windows.go
generated
vendored
File diff suppressed because it is too large
Load diff
12
vendor/github.com/mattn/go-colorable/go.test.sh
generated
vendored
12
vendor/github.com/mattn/go-colorable/go.test.sh
generated
vendored
|
|
@ -1,12 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
echo "" > coverage.txt
|
|
||||||
|
|
||||||
for d in $(go list ./... | grep -v vendor); do
|
|
||||||
go test -race -coverprofile=profile.out -covermode=atomic "$d"
|
|
||||||
if [ -f profile.out ]; then
|
|
||||||
cat profile.out >> coverage.txt
|
|
||||||
rm profile.out
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
55
vendor/github.com/mattn/go-colorable/noncolorable.go
generated
vendored
55
vendor/github.com/mattn/go-colorable/noncolorable.go
generated
vendored
|
|
@ -1,55 +0,0 @@
|
||||||
package colorable
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NonColorable holds writer but removes escape sequence.
|
|
||||||
type NonColorable struct {
|
|
||||||
out io.Writer
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewNonColorable returns new instance of Writer which removes escape sequence from Writer.
|
|
||||||
func NewNonColorable(w io.Writer) io.Writer {
|
|
||||||
return &NonColorable{out: w}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write writes data on console
|
|
||||||
func (w *NonColorable) Write(data []byte) (n int, err error) {
|
|
||||||
er := bytes.NewReader(data)
|
|
||||||
var bw [1]byte
|
|
||||||
loop:
|
|
||||||
for {
|
|
||||||
c1, err := er.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
break loop
|
|
||||||
}
|
|
||||||
if c1 != 0x1b {
|
|
||||||
bw[0] = c1
|
|
||||||
w.out.Write(bw[:])
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
c2, err := er.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
break loop
|
|
||||||
}
|
|
||||||
if c2 != 0x5b {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
var buf bytes.Buffer
|
|
||||||
for {
|
|
||||||
c, err := er.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
break loop
|
|
||||||
}
|
|
||||||
if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
buf.Write([]byte(string(c)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return len(data), nil
|
|
||||||
}
|
|
||||||
24
vendor/github.com/uptrace/bun/extra/bundebug/LICENSE
generated
vendored
24
vendor/github.com/uptrace/bun/extra/bundebug/LICENSE
generated
vendored
|
|
@ -1,24 +0,0 @@
|
||||||
Copyright (c) 2021 Vladimir Mihailenco. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are
|
|
||||||
met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the following disclaimer
|
|
||||||
in the documentation and/or other materials provided with the
|
|
||||||
distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
109
vendor/github.com/uptrace/bun/extra/bundebug/debug.go
generated
vendored
109
vendor/github.com/uptrace/bun/extra/bundebug/debug.go
generated
vendored
|
|
@ -1,109 +0,0 @@
|
||||||
package bundebug
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"database/sql"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/fatih/color"
|
|
||||||
|
|
||||||
"github.com/uptrace/bun"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ConfigOption func(*QueryHook)
|
|
||||||
|
|
||||||
func WithVerbose() ConfigOption {
|
|
||||||
return func(h *QueryHook) {
|
|
||||||
h.verbose = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type QueryHook struct {
|
|
||||||
verbose bool
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ bun.QueryHook = (*QueryHook)(nil)
|
|
||||||
|
|
||||||
func NewQueryHook(opts ...ConfigOption) *QueryHook {
|
|
||||||
h := new(QueryHook)
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(h)
|
|
||||||
}
|
|
||||||
return h
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *QueryHook) BeforeQuery(
|
|
||||||
ctx context.Context, event *bun.QueryEvent,
|
|
||||||
) context.Context {
|
|
||||||
return ctx
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *QueryHook) AfterQuery(ctx context.Context, event *bun.QueryEvent) {
|
|
||||||
if !h.verbose {
|
|
||||||
switch event.Err {
|
|
||||||
case nil, sql.ErrNoRows:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
now := time.Now()
|
|
||||||
dur := now.Sub(event.StartTime)
|
|
||||||
|
|
||||||
args := []interface{}{
|
|
||||||
"[bun]",
|
|
||||||
now.Format(" 15:04:05.000 "),
|
|
||||||
formatOperation(event),
|
|
||||||
fmt.Sprintf(" %10s ", dur.Round(time.Microsecond)),
|
|
||||||
event.Query,
|
|
||||||
}
|
|
||||||
|
|
||||||
if event.Err != nil {
|
|
||||||
typ := reflect.TypeOf(event.Err).String()
|
|
||||||
args = append(args,
|
|
||||||
"\t",
|
|
||||||
color.New(color.BgRed).Sprintf(" %s ", typ+": "+event.Err.Error()),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func formatOperation(event *bun.QueryEvent) string {
|
|
||||||
operation := event.Operation()
|
|
||||||
return operationColor(operation).Sprintf(" %-16s ", operation)
|
|
||||||
}
|
|
||||||
|
|
||||||
func eventOperation(event *bun.QueryEvent) string {
|
|
||||||
if event.QueryAppender != nil {
|
|
||||||
return event.QueryAppender.Operation()
|
|
||||||
}
|
|
||||||
return queryOperation(event.Query)
|
|
||||||
}
|
|
||||||
|
|
||||||
func queryOperation(name string) string {
|
|
||||||
if idx := strings.IndexByte(name, ' '); idx > 0 {
|
|
||||||
name = name[:idx]
|
|
||||||
}
|
|
||||||
if len(name) > 16 {
|
|
||||||
name = name[:16]
|
|
||||||
}
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
|
|
||||||
func operationColor(operation string) *color.Color {
|
|
||||||
switch operation {
|
|
||||||
case "SELECT":
|
|
||||||
return color.New(color.BgGreen, color.FgHiWhite)
|
|
||||||
case "INSERT":
|
|
||||||
return color.New(color.BgBlue, color.FgHiWhite)
|
|
||||||
case "UPDATE":
|
|
||||||
return color.New(color.BgYellow, color.FgHiBlack)
|
|
||||||
case "DELETE":
|
|
||||||
return color.New(color.BgMagenta, color.FgHiWhite)
|
|
||||||
default:
|
|
||||||
return color.New(color.BgWhite, color.FgHiBlack)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
9
vendor/modules.txt
vendored
9
vendor/modules.txt
vendored
|
|
@ -44,9 +44,6 @@ github.com/dsoprea/go-png-image-structure
|
||||||
# github.com/dsoprea/go-utility v0.0.0-20200717064901-2fccff4aa15e
|
# github.com/dsoprea/go-utility v0.0.0-20200717064901-2fccff4aa15e
|
||||||
## explicit; go 1.12
|
## explicit; go 1.12
|
||||||
github.com/dsoprea/go-utility/image
|
github.com/dsoprea/go-utility/image
|
||||||
# github.com/fatih/color v1.10.0
|
|
||||||
## explicit; go 1.13
|
|
||||||
github.com/fatih/color
|
|
||||||
# github.com/gin-contrib/cors v1.3.1
|
# github.com/gin-contrib/cors v1.3.1
|
||||||
## explicit; go 1.13
|
## explicit; go 1.13
|
||||||
github.com/gin-contrib/cors
|
github.com/gin-contrib/cors
|
||||||
|
|
@ -352,9 +349,6 @@ github.com/kballard/go-shellquote
|
||||||
# github.com/leodido/go-urn v1.2.1
|
# github.com/leodido/go-urn v1.2.1
|
||||||
## explicit; go 1.13
|
## explicit; go 1.13
|
||||||
github.com/leodido/go-urn
|
github.com/leodido/go-urn
|
||||||
# github.com/mattn/go-colorable v0.1.8
|
|
||||||
## explicit; go 1.13
|
|
||||||
github.com/mattn/go-colorable
|
|
||||||
# github.com/mattn/go-isatty v0.0.14
|
# github.com/mattn/go-isatty v0.0.14
|
||||||
## explicit; go 1.12
|
## explicit; go 1.12
|
||||||
github.com/mattn/go-isatty
|
github.com/mattn/go-isatty
|
||||||
|
|
@ -442,9 +436,6 @@ github.com/uptrace/bun/dialect/pgdialect
|
||||||
# github.com/uptrace/bun/dialect/sqlitedialect v1.0.5
|
# github.com/uptrace/bun/dialect/sqlitedialect v1.0.5
|
||||||
## explicit; go 1.16
|
## explicit; go 1.16
|
||||||
github.com/uptrace/bun/dialect/sqlitedialect
|
github.com/uptrace/bun/dialect/sqlitedialect
|
||||||
# github.com/uptrace/bun/extra/bundebug v1.0.5
|
|
||||||
## explicit; go 1.16
|
|
||||||
github.com/uptrace/bun/extra/bundebug
|
|
||||||
# github.com/urfave/cli/v2 v2.3.0
|
# github.com/urfave/cli/v2 v2.3.0
|
||||||
## explicit; go 1.11
|
## explicit; go 1.11
|
||||||
github.com/urfave/cli/v2
|
github.com/urfave/cli/v2
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue