@@ -24,11 +24,27 @@ | |||||
.am-pt-20{ padding-top: 2.0rem !important;} | .am-pt-20{ padding-top: 2.0rem !important;} | ||||
.am-pt-30{ padding-top: 3.0rem !important;} | .am-pt-30{ padding-top: 3.0rem !important;} | ||||
.am-pl-30{ padding-left: 3.0rem !important;} | .am-pl-30{ padding-left: 3.0rem !important;} | ||||
.am-pl-50{ padding-left: 5.0em !important;} | |||||
.am-ml-10{ margin-left: 1.0rem !important;} | |||||
.am-ml-30{ margin-left: 3.0rem !important;} | .am-ml-30{ margin-left: 3.0rem !important;} | ||||
.am-pr-30{ padding-right: 3.0rem !important;} | .am-pr-30{ padding-right: 3.0rem !important;} | ||||
.am-mr-30{ margin-right: 3.0rem !important;} | .am-mr-30{ margin-right: 3.0rem !important;} | ||||
.am-lh-18{ line-height: 1.8em;} | .am-lh-18{ line-height: 1.8em;} | ||||
.nowrap{ | |||||
white-space: nowrap; | |||||
overflow: hidden; | |||||
text-overflow: ellipsis; | |||||
} | |||||
.nowrap-2 { | |||||
max-height: 2.837em; | |||||
line-height: 1.4285em; | |||||
overflow: hidden; | |||||
display: -webkit-box; | |||||
-webkit-line-clamp: 2; | |||||
-webkit-box-orient: vertical; | |||||
} | |||||
.opacity5{ opacity:0.5;} | .opacity5{ opacity:0.5;} | ||||
.radius15{ border-radius:1.5rem !important; } | .radius15{ border-radius:1.5rem !important; } | ||||
.radius10{ border-radius:1.0rem !important; } | .radius10{ border-radius:1.0rem !important; } | ||||
@@ -42,12 +58,15 @@ | |||||
box-shadow: 0 2px 4px 0 rgba(34,36,38,.3); | box-shadow: 0 2px 4px 0 rgba(34,36,38,.3); | ||||
} | } | ||||
.ui.blue { | |||||
color: #0366d6; | |||||
} | |||||
.ui.secondary.hometop.segment{ | .ui.secondary.hometop.segment{ | ||||
background: #DFE9F0; | background: #DFE9F0; | ||||
padding-top: 0; | padding-top: 0; | ||||
border: none; | border: none; | ||||
margin-bottom: 90px; | |||||
margin-bottom: 11em; | |||||
} | } | ||||
.ui.secondary.hometop.segment #navbar{ | .ui.secondary.hometop.segment #navbar{ | ||||
z-index: 10; | z-index: 10; | ||||
@@ -68,6 +87,7 @@ | |||||
.homebanner{ | .homebanner{ | ||||
position: relative; | position: relative; | ||||
padding: 100px 32px 80px; | padding: 100px 32px 80px; | ||||
padding-bottom: 0; | |||||
z-index: 9; | z-index: 9; | ||||
} | } | ||||
.homebanner .ui.header .sub.header{ | .homebanner .ui.header .sub.header{ | ||||
@@ -75,10 +95,15 @@ | |||||
} | } | ||||
.bannerpic{ | .bannerpic{ | ||||
position: absolute; | position: absolute; | ||||
right: 50px; | |||||
right: 0px; | |||||
bottom: -64px; | bottom: -64px; | ||||
width: 560px; | width: 560px; | ||||
z-index: 6; | |||||
} | |||||
.homebanner .ui.button{ | |||||
font-weight: normal; | |||||
} | } | ||||
.ui[class*="very padded"].segment.i-code{ | .ui[class*="very padded"].segment.i-code{ | ||||
padding-left: 6.0rem; | padding-left: 6.0rem; | ||||
} | } | ||||
@@ -115,25 +140,141 @@ | |||||
.i-env .ui.cards>.card>.content{ | .i-env .ui.cards>.card>.content{ | ||||
border-top: none; | border-top: none; | ||||
} | } | ||||
.leftline01{ | |||||
#homenews{ | |||||
position: relative; | |||||
z-index: 9; | |||||
bottom: -6em; | |||||
} | |||||
#homenews > p{ | |||||
color: #BBBBBB; | |||||
margin-left: 2.3em; | |||||
} | |||||
.homenews{ | |||||
border-radius: 2em; | |||||
background-color: rgba(16, 16, 16, .9); | |||||
position: relative; | |||||
padding-left: 2.3em !important; | |||||
} | |||||
.homeorg, .homepro, .homemodel, .i-env{ | |||||
position: relative; | |||||
padding-bottom: 5em; | |||||
} | |||||
.homenews::before{ | |||||
content: ''; | |||||
position: absolute; | position: absolute; | ||||
left: 3.0rem; | |||||
left: 3em; | |||||
top: 0; | top: 0; | ||||
bottom: 0; | bottom: 0; | ||||
border-left: 2px solid #505559; | |||||
border-bottom: 2px solid #505559; | |||||
border-radius: 0 0 0 2.0rem; | |||||
width: 2.0rem; | |||||
background-color: rgba(105, 192, 255, .4); | |||||
width: 2px; | |||||
} | |||||
.homenews .time-since{ | |||||
padding-left: 1em; | |||||
color: #888888; | |||||
} | |||||
.homenews a{ | |||||
color: #69C0FF; | |||||
} | } | ||||
.leftline02{ | |||||
.homenews .ui.list>.item>.content{ | |||||
color: #E8E8E8; | |||||
line-height: 1.8em; | |||||
width: calc(100% - 3.25em) !important; | |||||
} | |||||
.homenews .ui.list>.item{ | |||||
padding: 0; | |||||
} | |||||
.newslist{ | |||||
height: 325px; | |||||
overflow: hidden; | |||||
} | |||||
.leftline01, .leftline03, .leftline04{ | |||||
position: absolute; | position: absolute; | ||||
left: 5rem; | |||||
top: calc(-5.0rem - 2px); | |||||
border-top: 2px solid #505559; | |||||
border-right: 2px solid #505559; | |||||
border-radius: 0 2.0rem 0 0; | |||||
width: 17.5rem; | |||||
height: 6.0rem; | |||||
width: 5em; | |||||
border-radius: 0 0 0 4.0em; | |||||
border-left: 2px solid #3291F8; | |||||
border-bottom: 2px solid #3291F8; | |||||
top: -5em; | |||||
bottom: 0; | |||||
left: 2em; | |||||
z-index: 6; | |||||
} | |||||
.leftline02, .leftline02-2 { | |||||
position: absolute; | |||||
left: 7.0em; | |||||
top: -2px; | |||||
bottom: 2px; | |||||
height: auto; | |||||
border-top: 2px solid #3291F8; | |||||
border-right: 2px solid #3291F8; | |||||
border-bottom: 2px solid #3291F8; | |||||
border-radius: 0 4.0em 4.0em 0; | |||||
width: 10em; | |||||
z-index: 6; | |||||
} | |||||
.leftline03{ | |||||
border-radius: 4.0em 0 0 0; | |||||
border-top: 2px solid #3291F8; | |||||
border-bottom:none; | |||||
top: -2.0em; | |||||
} | |||||
.leftline02-2{ | |||||
border-color: rgba(105, 192, 255, .4); | |||||
width: 7em; | |||||
z-index: 5; | |||||
} | |||||
.leftline04{ | |||||
border-radius: 0; | |||||
border-top: none; | |||||
border-bottom: none; | |||||
} | |||||
.homeorg-tit{ | |||||
padding-left: 5em !important; | |||||
} | |||||
.homeorg-tit::after{ | |||||
content: ''; | |||||
position: absolute; | |||||
width: 1.6em; | |||||
height: 1.6em; | |||||
background-color: #FFF; | |||||
border: 2px solid #3291F8; | |||||
left: 2.3em; | |||||
top: 1.3em; | |||||
border-radius: 1em; | |||||
z-index: 9; | |||||
} | |||||
.homeorg-list .card{ | |||||
background-image: linear-gradient(#FFF, #FFF 60%, #DFF0EF) !important; | |||||
box-shadow: none !important; | |||||
} | |||||
.homeorg-list .card .ui.small.header .content{ | |||||
width: calc(100% - 3.25em); | |||||
} | |||||
.homepro-list{ | |||||
position: relative; | |||||
z-index: 9; | |||||
padding: 1.0em 1.0em 3.0em; | |||||
overflow: hidden; | |||||
} | |||||
.homepro-list .ui.card{ | |||||
border-radius: 15px; | |||||
background-color: #FFF; | |||||
box-shadow: 0px 5px 10px 0px rgba(105, 192, 255, 30); | |||||
border: 1px solid rgba(105, 192, 255, 40); | |||||
min-height: 10.8em; | |||||
} | |||||
.homepro-list .ui.card>.content>.header{ | |||||
line-height: 40px !important; | |||||
} | |||||
.homepro-list .swiper-pagination-bullet-active{ | |||||
width: 40px; | |||||
border-radius: 4px; | |||||
} | |||||
.i-env > div{ | |||||
position: relative; | |||||
} | } | ||||
@media only screen and (max-width: 767px) { | @media only screen and (max-width: 767px) { | ||||
@@ -32,6 +32,7 @@ require ( | |||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible | github.com/dgrijalva/jwt-go v3.2.0+incompatible | ||||
github.com/dustin/go-humanize v1.0.0 | github.com/dustin/go-humanize v1.0.0 | ||||
github.com/editorconfig/editorconfig-core-go/v2 v2.1.1 | github.com/editorconfig/editorconfig-core-go/v2 v2.1.1 | ||||
github.com/elliotchance/orderedmap v1.4.0 | |||||
github.com/emirpasic/gods v1.12.0 | github.com/emirpasic/gods v1.12.0 | ||||
github.com/ethantkoenig/rupture v0.0.0-20180203182544-0a76f03a811a | github.com/ethantkoenig/rupture v0.0.0-20180203182544-0a76f03a811a | ||||
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 // indirect | github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 // indirect | ||||
@@ -100,7 +101,7 @@ require ( | |||||
github.com/sergi/go-diff v1.1.0 | github.com/sergi/go-diff v1.1.0 | ||||
github.com/shurcooL/httpfs v0.0.0-20190527155220-6a4d4a70508b // indirect | github.com/shurcooL/httpfs v0.0.0-20190527155220-6a4d4a70508b // indirect | ||||
github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd | github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd | ||||
github.com/stretchr/testify v1.4.0 | |||||
github.com/stretchr/testify v1.7.0 | |||||
github.com/tecbot/gorocksdb v0.0.0-20181010114359-8752a9433481 // indirect | github.com/tecbot/gorocksdb v0.0.0-20181010114359-8752a9433481 // indirect | ||||
github.com/tinylib/msgp v1.1.2 // indirect | github.com/tinylib/msgp v1.1.2 // indirect | ||||
github.com/tstranex/u2f v1.0.0 | github.com/tstranex/u2f v1.0.0 | ||||
@@ -180,6 +180,8 @@ github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFP | |||||
github.com/editorconfig/editorconfig-core-go/v2 v2.1.1 h1:mhPg/0hGebcpiiQLqJD2PWWyoHRLEdZ3sXKaEvT1EQU= | github.com/editorconfig/editorconfig-core-go/v2 v2.1.1 h1:mhPg/0hGebcpiiQLqJD2PWWyoHRLEdZ3sXKaEvT1EQU= | ||||
github.com/editorconfig/editorconfig-core-go/v2 v2.1.1/go.mod h1:/LuhWJiQ9Gvo1DhVpa4ssm5qeg8rrztdtI7j/iCie2k= | github.com/editorconfig/editorconfig-core-go/v2 v2.1.1/go.mod h1:/LuhWJiQ9Gvo1DhVpa4ssm5qeg8rrztdtI7j/iCie2k= | ||||
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= | github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= | ||||
github.com/elliotchance/orderedmap v1.4.0 h1:wZtfeEONCbx6in1CZyE6bELEt/vFayMvsxqI5SgsR+A= | |||||
github.com/elliotchance/orderedmap v1.4.0/go.mod h1:wsDwEaX5jEoyhbs7x93zk2H/qv0zwuhg4inXhDkYqys= | |||||
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= | github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= | ||||
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= | github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= | ||||
github.com/ethantkoenig/rupture v0.0.0-20180203182544-0a76f03a811a h1:M1bRpaZAn4GSsqu3hdK2R8H0AH9O6vqCTCbm2oAFGfE= | github.com/ethantkoenig/rupture v0.0.0-20180203182544-0a76f03a811a h1:M1bRpaZAn4GSsqu3hdK2R8H0AH9O6vqCTCbm2oAFGfE= | ||||
@@ -705,6 +707,8 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf | |||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= | ||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= | github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= | ||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= | ||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= | |||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | |||||
github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203 h1:QVqDTf3h2WHt08YuiTGPZLls0Wq99X9bWd0Q5ZSBesM= | github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203 h1:QVqDTf3h2WHt08YuiTGPZLls0Wq99X9bWd0Q5ZSBesM= | ||||
github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203/go.mod h1:oqN97ltKNihBbwlX8dLpwxCl3+HnXKV/R0e+sRLd9C8= | github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203/go.mod h1:oqN97ltKNihBbwlX8dLpwxCl3+HnXKV/R0e+sRLd9C8= | ||||
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= | github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= | ||||
@@ -1028,6 +1032,8 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | |||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= | gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= | ||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= | |||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | |||||
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= | grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= | ||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | ||||
honnef.co/go/tools v0.0.0-20180920025451-e3ad64cb4ed3/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | honnef.co/go/tools v0.0.0-20180920025451-e3ad64cb4ed3/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | ||||
@@ -346,6 +346,25 @@ func GetFeeds(opts GetFeedsOptions) ([]*Action, error) { | |||||
return actions, nil | return actions, nil | ||||
} | } | ||||
func GetLast20PublicFeeds() ([]*Action, error) { | |||||
cond := builder.NewCond() | |||||
cond = cond.And(builder.Eq{"is_private": false}) | |||||
cond = cond.And(builder.Eq{"is_deleted": false}) | |||||
actions := make([]*Action, 0, 20) | |||||
if err := x.Limit(30).Desc("id").Where(cond).Find(&actions); err != nil { | |||||
return nil, fmt.Errorf("Find: %v", err) | |||||
} | |||||
if err := ActionList(actions).LoadAttributes(); err != nil { | |||||
return nil, fmt.Errorf("LoadAttributes: %v", err) | |||||
} | |||||
return actions, nil | |||||
} | |||||
func GetUnTransformedActions() ([]*Action, error) { | func GetUnTransformedActions() ([]*Action, error) { | ||||
actions := make([]*Action, 0, 10) | actions := make([]*Action, 0, 10) | ||||
err := x.Where("op_type = ?", ActionCommitRepo). | err := x.Where("op_type = ?", ActionCommitRepo). | ||||
@@ -280,8 +280,9 @@ func notifyWatchers(e Engine, actions ...*Action) error { | |||||
// NotifyWatchers creates batch of actions for every watcher. | // NotifyWatchers creates batch of actions for every watcher. | ||||
func NotifyWatchers(actions ...*Action) error { | func NotifyWatchers(actions ...*Action) error { | ||||
error := notifyWatchers(x, actions...) | |||||
producer(actions...) | producer(actions...) | ||||
return notifyWatchers(x, actions...) | |||||
return error | |||||
} | } | ||||
func producer(actions ...*Action) { | func producer(actions ...*Action) { | ||||
@@ -220,6 +220,37 @@ show_only_public = Showing only public | |||||
issues.in_your_repos = In your repositories | issues.in_your_repos = In your repositories | ||||
contributors = Contributors | contributors = Contributors | ||||
page_title=Explore Better AI | |||||
page_small_title=OpenI AI development cooperation platform | |||||
page_description=The one-stop collaborative development environment for AI field provides AI development pipeline integrating code development, data management, model debugging, reasoning and evaluation | |||||
page_use=Use Now | |||||
page_only_dynamic=Show only open source project dynamics | |||||
page_recommend_org=Recommended organization | |||||
page_recommend_org_desc=These excellent organizations are using Qizhi AI to develop collaboration platforms; Your organization also wants to show here, | |||||
page_recommend_org_commit=Click here to submit | |||||
page_recommend_org_more=More organizations | |||||
page_recommend_repo=Recommended projects | |||||
page_recommend_repo_desc=Excellent AI project recommendation; Your project also wants to show here, | |||||
page_recommend_repo_commit=Click here to submit | |||||
page_recommend_repo_go=. Click here | |||||
page_recommend_repo_more=Project Square | |||||
page_dev_env=Collaborative development environment | |||||
page_dev_env_desc=The biggest difference between Qizhi AI collaborative development platform and traditional git platform is that it provides a collaborative development environment for AI development | |||||
page_dev_env_desc_title=Unified management of development elements | |||||
page_dev_env_desc_desc=The platform provides four elements of AI development: unified management of model code, data set, model and execution environment | |||||
page_dev_env_desc1_title=Data collaboration and sharing | |||||
page_dev_env_desc1_desc=By uploading data sets in the project, many project members cooperate to complete data preprocessing; You can also establish a better model with community developers by setting the data as a public dataset | |||||
page_dev_env_desc2_title=Model management and sharing | |||||
page_dev_env_desc2_desc=Associate the model with the code version, adjust the model in different ways based on the code history version, and save the results; The trained model can be open and shared, so that more people can use the model to test and give feedback | |||||
page_dev_env_desc3_title=One configuration, multiple use | |||||
page_dev_env_desc3_desc=Provide execution environment sharing, one-time configuration and multiple use, reduce the threshold of model development, and avoid spending repeated time configuring complex environments | |||||
page_dev_yunlao=PengCheng Cloudbrain open source collaboration | |||||
page_dev_yunlao_desc1=The platform has been connected with Pengcheng Cloudbrain and can use the rich computing resources of Pengcheng Cloudbrain to complete AI development tasks | |||||
page_dev_yunlao_desc2=Pengcheng Cloudbrain's existing AI computing power is 100p FLOPS@FP16 (billions of half precision floating-point calculations per second), the main hardware infrastructure is composed of GPU server equipped with NVIDIA Tesla V100 and Atlas 900 AI cluster equipped with Kunpeng and shengteng processors | |||||
page_dev_yunlao_desc3=Developers can freely choose the corresponding computing resources according to the use requirements, and can test the adaptability, performance and stability of the model in different hardware environments | |||||
page_dev_yunlao_desc4=If your model needs more computing resources, you can also apply separately | |||||
page_dev_yunlao_apply=Separate apply | |||||
[explore] | [explore] | ||||
repos = Repositories | repos = Repositories | ||||
select_repos = Select the project | select_repos = Select the project | ||||
@@ -2673,5 +2704,5 @@ foot.member_news = Member news | |||||
foot.industry_advisory = Industry Advisory | foot.industry_advisory = Industry Advisory | ||||
foot.help = help | foot.help = help | ||||
foot.copyright= Copyright: New Generation Artificial Intelligence Open Source Open Platform (OpenI) | foot.copyright= Copyright: New Generation Artificial Intelligence Open Source Open Platform (OpenI) | ||||
Platform_Tutorial=Platform Tutorial | |||||
foot.advice_feedback=advice feedback | |||||
Platform_Tutorial = Tutorial | |||||
foot.advice_feedback = Feedback |
@@ -222,6 +222,37 @@ issues.in_your_repos=属于该用户项目的 | |||||
contributors=贡献者 | contributors=贡献者 | ||||
page_title=探索更好的AI | |||||
page_small_title=启智AI开发协作平台 | |||||
page_description=面向AI领域的一站式协同开发环境,提供集代码开发、数据管理、模型调试、推理和评测为一体的AI开发流水线 | |||||
page_use=立即使用 | |||||
page_only_dynamic=仅展示开源项目动态 | |||||
page_recommend_org=推荐组织 | |||||
page_recommend_org_desc=这些优秀的组织正在使用启智AI开发协作平台;你的组织也想展示到这里, | |||||
page_recommend_org_commit=点此提交 | |||||
page_recommend_org_more=更多组织 | |||||
page_recommend_repo=推荐项目 | |||||
page_recommend_repo_desc=优秀的AI项目推荐;你的项目也想展示到这里, | |||||
page_recommend_repo_commit=点此提交 | |||||
page_recommend_repo_go=。进入 | |||||
page_recommend_repo_more=项目广场 | |||||
page_dev_env=协同开发环境 | |||||
page_dev_env_desc=启智AI协作开发平台与传统git平台最大的不同就在于提供了面向AI开发的协同开发环境 | |||||
page_dev_env_desc_title=开发要素统一管理 | |||||
page_dev_env_desc_desc=平台提供了AI开发四大要素:模型代码、数据集、模型和执行环境的统一管理 | |||||
page_dev_env_desc1_title=数据协同与共享 | |||||
page_dev_env_desc1_desc=通过在项目中上传数据集,项目成员多人协作完成数据预处理;也可以通过将数据设置为公有数据集,与社区开发者共同建立更好的模型 | |||||
page_dev_env_desc2_title=模型管理与共享 | |||||
page_dev_env_desc2_desc=将模型与代码版本建立关联,可以基于代码历史版本,使用不同的方式调整模型,并将结果保存下来;训练好的模型可以开放共享,让更多人的使用模型测试并提出反馈 | |||||
page_dev_env_desc3_title=一次配置,多次使用 | |||||
page_dev_env_desc3_desc=提供执行环境共享,一次配置,多次使用,降低模型开发门槛,避免花费重复的时间配置复杂的环境 | |||||
page_dev_yunlao=鹏城云脑开源协同 | |||||
page_dev_yunlao_desc1=平台已经与鹏城云脑打通,可以利用鹏城云脑的丰富算力资源,完成AI开发任务 | |||||
page_dev_yunlao_desc2=鹏城云脑现有AI算力100P FLOPS@FP16(每秒十亿亿次半精度浮点计算),主要硬件基础设施由搭载英伟达Tesla V100 的GPU服务器和搭载鲲鹏、昇腾处理器的Atlas 900 AI集群构成 | |||||
page_dev_yunlao_desc3=开发者可以根据使用需求,自由选择相应计算资源,可以测试模型在不同硬件环境下的适配能力、性能、稳定性等 | |||||
page_dev_yunlao_desc4=如果您的模型需要更多的计算资源,也可以单独申请 | |||||
page_dev_yunlao_apply=单独申请 | |||||
[explore] | [explore] | ||||
repos=项目 | repos=项目 | ||||
select_repos=精选项目 | select_repos=精选项目 | ||||
@@ -1446,7 +1477,7 @@ milestones.open_tab=%d 开启中 | |||||
milestones.close_tab=%d 已关闭 | milestones.close_tab=%d 已关闭 | ||||
milestones.closed=于 %s关闭 | milestones.closed=于 %s关闭 | ||||
milestones.no_due_date=暂无截止日期 | milestones.no_due_date=暂无截止日期 | ||||
milestones.open=开启中 | |||||
milestones.open=开启 | |||||
milestones.close=关闭 | milestones.close=关闭 | ||||
milestones.new_subheader=里程碑组织任务,合并请求和跟踪进度。 | milestones.new_subheader=里程碑组织任务,合并请求和跟踪进度。 | ||||
milestones.completeness=%d%% 完成 | milestones.completeness=%d%% 完成 | ||||
@@ -0,0 +1,448 @@ | |||||
var token; | |||||
if(isEmpty(token)){ | |||||
var meta = $("meta[name=_uid]"); | |||||
if(!isEmpty(meta)){ | |||||
token = meta.attr("content"); | |||||
console.log("token is uid:" + token); | |||||
} | |||||
} | |||||
var swiperNewMessage = new Swiper(".newslist", { | |||||
direction: "vertical", | |||||
slidesPerView: 10, | |||||
loop: true, | |||||
autoplay: { | |||||
delay: 2500, | |||||
disableOnInteraction: false, | |||||
}, | |||||
}); | |||||
var swiperRepo = new Swiper(".homepro-list", { | |||||
slidesPerView: 3, | |||||
slidesPerColumn: 2, | |||||
slidesPerColumnFill:'row', | |||||
spaceBetween: 30, | |||||
pagination: { | |||||
el: ".swiper-pagination", | |||||
clickable: true, | |||||
}, | |||||
autoplay: { | |||||
delay: 2500, | |||||
disableOnInteraction: false, | |||||
}, | |||||
}); | |||||
var output = document.getElementById("newmessage"); | |||||
console.log("document.location.host="+document.location.host); | |||||
console.log("document.URL="+document.URL); | |||||
var url = "ws://" + document.location.host + "/action/notification"; | |||||
if(document.location.host == "git.openi.org.cn" || document.URL.startsWith("https")){ | |||||
url = "wss://" + document.location.host + "/action/notification" | |||||
} | |||||
var socket = new WebSocket(url); | |||||
socket.onopen = function () { | |||||
console.log("message has connected."); | |||||
}; | |||||
var messageQueue = []; | |||||
var maxSize = 20; | |||||
var html =document.documentElement; | |||||
var lang = html.attributes["lang"] | |||||
var isZh = true; | |||||
if(lang != null && lang.nodeValue =="en-US" ){ | |||||
console.log("the language is " + lang.nodeValue); | |||||
isZh=false; | |||||
}else{ | |||||
console.log("default lang=zh"); | |||||
} | |||||
socket.onmessage = function (e) { | |||||
var data =JSON.parse(e.data) | |||||
console.log("recevie data=" + e.data) | |||||
var html = ""; | |||||
if (data != null){ | |||||
console.log("queue length=" + messageQueue.length); | |||||
if(messageQueue.length > maxSize){ | |||||
delete messageQueue[0]; | |||||
}else{ | |||||
messageQueue.push(data); | |||||
} | |||||
var currentTime = new Date().getTime(); | |||||
for(var i = 0; i < messageQueue.length; i++){ | |||||
var record = messageQueue[i]; | |||||
var recordPrefix = getMsg(record); | |||||
var actionName = getAction(record.OpType,isZh); | |||||
if(record.OpType == "6" || record.OpType == "10" || record.OpType == "12" || record.OpType == "13"){ | |||||
html += recordPrefix + actionName; | |||||
html += " <a href=\"" + getIssueLink(record) + "\" rel=\"nofollow\">" + getIssueText(record) + "</a>" | |||||
} | |||||
else if(record.OpType == "7" || record.OpType == "11" || record.OpType == "14" || record.OpType == "15" || record.OpType == "22" | |||||
|| record.OpType == "23"){ | |||||
html += recordPrefix + actionName; | |||||
html += " <a href=\"" + getPRLink(record) + "\" rel=\"nofollow\">" + getPRText(record) + "</a>" | |||||
} | |||||
else if(record.OpType == "1"){ | |||||
html += recordPrefix + actionName; | |||||
html += " <a href=\"" + getRepoLink(record) + "\" rel=\"nofollow\">" + getRepoLink(record) + "</a>" | |||||
} | |||||
else if(record.OpType == "9" || record.OpType == "5"){ | |||||
branch = "<a href=\"" + getRepoLink(record) + "/src/branch/" + record.RefName + "\" rel=\"nofollow\">" + record.RefName + "</a>" | |||||
actionName = actionName.replace("{branch}",branch); | |||||
html += recordPrefix + actionName; | |||||
html += " <a href=\"" + getRepoLink(record) + "\" rel=\"nofollow\">" + getRepoLink(record) + "</a>" | |||||
}else if(record.OpType == "17"){ | |||||
actionName = actionName.replace("{deleteBranchName}",record.RefName); | |||||
var repoLink = "<a href=\"" + getRepoLink(record) + "\" rel=\"nofollow\">" + getRepoLink(record) + "</a>" | |||||
actionName = actionName.replace("{repoName}",repoLink); | |||||
html += recordPrefix + actionName; | |||||
} | |||||
else if(record.OpType == "2"){ | |||||
actionName = actionName.replace("{oldRepoName}",record.Content); | |||||
html += recordPrefix + actionName; | |||||
html += " <a href=\"" + getRepoLink(record) + "\" rel=\"nofollow\">" + getRepoLink(record) + "</a>" | |||||
} | |||||
else{ | |||||
continue; | |||||
} | |||||
if(record.Repo != null){ | |||||
var time = getTime(record.CreatedUnix,currentTime); | |||||
html += " " + time; | |||||
} | |||||
html += "</div>"; | |||||
html += "</div>"; | |||||
} | |||||
/* | |||||
<div class="swiper-slide item"> | |||||
<img class="ui avatar image" src="/user/avatar/zhoupzh/-1" alt=""> | |||||
<div class="middle aligned content"> | |||||
<a href="/zhoupzh" title="">zhoupzh</a> 合并了合并请求 <a href="/OpenI/aiforge/pulls/1168" rel="nofollow">OpenI/aiforge#1168</a><span class="time-since">22 分钟前</span> | |||||
</div> | |||||
</div> | |||||
*/ | |||||
} | |||||
console.log("html=" + html) | |||||
output.innerHTML = html; | |||||
swiperNewMessage.updateSlides(); | |||||
swiperNewMessage.updateProgress(); | |||||
}; | |||||
function getMsg(record){ | |||||
var html =""; | |||||
html += "<div class=\"swiper-slide item\">"; | |||||
html += " <img class=\"ui avatar image\" src=\"/user/avatar/" + record.ActUser.Name + "/-1\" alt=\"\">" | |||||
html += " <div class=\"middle aligned content nowrap\">" | |||||
html += " <a href=\"/" + record.ActUser.Name + "\" title=\"\">" + record.ActUser.Name + "</a>" | |||||
return html; | |||||
} | |||||
function getRepoLink(record){ | |||||
return "/" + record.Repo.OwnerName + "/" + record.Repo.Name; | |||||
} | |||||
function getRepoLink(record){ | |||||
return record.Repo.OwnerName + "/" + record.Repo.Name; | |||||
} | |||||
function getTime(UpdatedUnix,currentTime){ | |||||
UpdatedUnix = UpdatedUnix; | |||||
currentTime = currentTime / 1000; | |||||
var timeEscSecond = currentTime - UpdatedUnix; | |||||
if( timeEscSecond < 0){ | |||||
timeEscSecond = 1; | |||||
} | |||||
console.log("currentTime=" + currentTime + " updateUnix=" + UpdatedUnix); | |||||
var hours= Math.floor(timeEscSecond / 3600); | |||||
//计算相差分钟数 | |||||
var leave2 = Math.floor(timeEscSecond % (3600)); //计算小时数后剩余的秒数 | |||||
var minutes= Math.floor(leave2 / 60);//计算相差分钟数 | |||||
var leave3=Math.floor(leave2 % 60); //计算分钟数后剩余的秒数 | |||||
var seconds= leave3; | |||||
if(hours == 0 && minutes == 0){ | |||||
return seconds + getRepoOrOrg(6,isZh); | |||||
}else{ | |||||
if(hours > 0){ | |||||
return hours + getRepoOrOrg(4,isZh); | |||||
}else{ | |||||
return minutes + getRepoOrOrg(5,isZh); | |||||
} | |||||
} | |||||
} | |||||
function getPRLink(record){ | |||||
return "/" + record.Repo.OwnerName + "/" + record.Repo.Name + "/pulls/" + getIssueId(record); | |||||
} | |||||
function getPRText(record){ | |||||
return record.Repo.OwnerName + "/" + record.Repo.Name + "#" + getIssueId(record); | |||||
} | |||||
function getIssueLink(record){ | |||||
return "/" + record.Repo.OwnerName + "/" + record.Repo.Name + "/issues/" + getIssueId(record); | |||||
} | |||||
function getIssueId(record){ | |||||
var Id = "1"; | |||||
if(!isEmpty(record.Comment) && !isEmpty(record.Comment.Issue)){ | |||||
Id = record.Comment.Issue.Index; | |||||
}else{ | |||||
if(!isEmpty(record.Content)){ | |||||
var content = record.Content; | |||||
var index = content.indexOf("|"); | |||||
if(index != -1){ | |||||
Id = content.substring(0,index); | |||||
} | |||||
} | |||||
} | |||||
return Id; | |||||
} | |||||
function getIssueText(record){ | |||||
return record.Repo.OwnerName + "/" + record.Repo.Name + "#" + getIssueId(record); | |||||
} | |||||
/* | |||||
ActionCreateRepo ActionType = iota + 1 // 1 | |||||
ActionRenameRepo // 2 | |||||
ActionStarRepo // 3 | |||||
ActionWatchRepo // 4 | |||||
ActionCommitRepo // 5 | |||||
ActionCreateIssue // 6 | |||||
ActionCreatePullRequest // 7 | |||||
ActionTransferRepo // 8 | |||||
ActionPushTag // 9 | |||||
ActionCommentIssue // 10 | |||||
ActionMergePullRequest // 11 | |||||
ActionCloseIssue // 12 | |||||
ActionReopenIssue // 13 | |||||
ActionClosePullRequest // 14 | |||||
ActionReopenPullRequest // 15 | |||||
ActionDeleteTag // 16 | |||||
ActionDeleteBranch // 17 | |||||
ActionMirrorSyncPush // 18 | |||||
ActionMirrorSyncCreate // 19 | |||||
ActionMirrorSyncDelete // 20 | |||||
ActionApprovePullRequest // 21 | |||||
ActionRejectPullRequest // 22 | |||||
ActionCommentPull // 23 | |||||
*/ | |||||
var actionNameZH={ | |||||
"1":"创建了项目", | |||||
"2":"重命名项目 {oldRepoName} 为", | |||||
"5":"推送了 {branch} 分支的代码到", | |||||
"6":"创建了任务", | |||||
"7":"创建了合并请求", | |||||
"9":"推送了 {branch} 分支的代码到", | |||||
"10":"评论了任务", | |||||
"11":"合并了合并请求", | |||||
"12":"关闭了任务", | |||||
"13":"重新开启了任务", | |||||
"14":"关闭了合并请求", | |||||
"15":"重新开启了合并请求", | |||||
"17":"从 {repoName} 删除分支 {deleteBranchName}", | |||||
"22":"拒绝了合并请求", | |||||
"23":"评论了合并请求" | |||||
}; | |||||
var actionNameEN={ | |||||
"1":" created repository", | |||||
"2":" renamed repository from {oldRepoName} to ", | |||||
"5":" pushed to {branch} at", | |||||
"6":" opened issue", | |||||
"7":" created pull request", | |||||
"9":" pushed to {branch} at", | |||||
"10":" commented on issue", | |||||
"11":" merged pull request", | |||||
"12":" closed issue", | |||||
"13":" reopened issue", | |||||
"14":" closed pull request", | |||||
"15":" reopened pull request", | |||||
"17":" deleted branch {deleteBranchName} from {repoName}", | |||||
"22":" rejected pull request", | |||||
"23":" commented on pull request" | |||||
}; | |||||
var repoAndOrgZH={ | |||||
"1":"项目", | |||||
"2":"成员", | |||||
"3":"团队", | |||||
"4":"小时前", | |||||
"5":"分钟前", | |||||
"6":"秒前" | |||||
}; | |||||
var repoAndOrgEN={ | |||||
"1":"repository", | |||||
"2":"Members ", | |||||
"3":"Teams", | |||||
"4":" hours ago", | |||||
"5":" minutes ago", | |||||
"6":" seconds ago" | |||||
}; | |||||
function getAction(opType,isZh){ | |||||
if(isZh){ | |||||
return actionNameZH[opType] | |||||
}else{ | |||||
return actionNameEN[opType] | |||||
} | |||||
} | |||||
queryRecommendData(); | |||||
function queryRecommendData(){ | |||||
$.ajax({ | |||||
type:"GET", | |||||
url:"/recommend/org", | |||||
headers: { | |||||
authorization:token, | |||||
}, | |||||
dataType:"json", | |||||
async:false, | |||||
success:function(json){ | |||||
console.log(json); | |||||
displayOrg(json); | |||||
}, | |||||
error:function(response) { | |||||
console.log(response); | |||||
} | |||||
}); | |||||
$.ajax({ | |||||
type:"GET", | |||||
url:"/recommend/repo", | |||||
headers: { | |||||
authorization:token, | |||||
}, | |||||
dataType:"json", | |||||
async:false, | |||||
success:function(json){ | |||||
console.log(json); | |||||
displayRepo(json); | |||||
}, | |||||
error:function(response) { | |||||
console.log(response); | |||||
} | |||||
}); | |||||
} | |||||
/* | |||||
<div class="swiper-slide"> | |||||
<div class="ui fluid card"> | |||||
<div class="content"> | |||||
<span class="right floated meta"> | |||||
<i class="star icon"></i>276 <i class="star icon"></i>32 | |||||
</span> | |||||
<img class="left floated mini ui image" src="/repo-avatars/278-a9f45e21b92b86dbf969c9f70dff1efc"> | |||||
<a class="header nowrap" href="/OpenI/aiforge">aiforge </a> | |||||
<div class="description nowrap-2"> | |||||
本项目是群体化方法与技术的开源实现案例,在基于Gitea的基础上,进一步支持社交化的协同开发、协同学习、协同研究等群体创新实践服务,特别是针对新一代人工智能技术特点,重点支持项目管理、git代码管理、大数据集存储管理与智能计算平台接入。 | |||||
</div> | |||||
<div class="ui tags nowrap am-mt-10"> | |||||
<a class="ui small label topic" href="/explore/repos?q=ai%e5%bc%80%e5%8f%91%e5%b7%a5%e5%85%b7&topic=">ai开发工具</a> | |||||
<a class="ui small label topic" href="/explore/repos?q=openi&topic=">openi</a> | |||||
<a class="ui small label topic" href="/explore/repos?q=golang&topic=">golang</a> | |||||
<a class="ui small label topic" href="/explore/repos?q=git&topic=">git</a> | |||||
<a class="ui small label topic" href="/explore/repos?q=pcl&topic=">pcl</a> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
*/ | |||||
function displayRepo(json){ | |||||
var orgRepo = document.getElementById("recommendrepo"); | |||||
var html = ""; | |||||
if (json != null && json.length > 0){ | |||||
for(var i = 0; i < json.length;i++){ | |||||
var record = json[i] | |||||
html += "<div class=\"swiper-slide\">"; | |||||
html += " <div class=\"ui fluid card\">"; | |||||
html += " <div class=\"content\">"; | |||||
html += " <span class=\"right floated meta\">"; | |||||
html += " <i class=\"ri-star-line\"></i>" + record["NumStars"] + "<i class=\"ri-git-branch-line am-ml-10\"></i>" + record["NumForks"]; | |||||
html += " </span>"; | |||||
html += " <img class=\"left floated mini ui image\" src=\"" + record["Avatar"] + "\">"; | |||||
html += " <a class=\"header nowrap\" href=\"/" + record["OwnerName"] + "/" + record["Name"] + "\">" + record["Name"] +"</a>"; | |||||
html += " <div class=\"description nowrap-2\">" + record["Description"] + " </div>"; | |||||
html += " <div class=\"ui tags nowrap am-mt-10\">" | |||||
if(record["Topics"] != null){ | |||||
for(var j = 0; j < record["Topics"].length; j++){ | |||||
topic = record["Topics"][j]; | |||||
url = "/explore/repos?q=" + (topic) + "&topic=" | |||||
html += "<a class=\"ui small label topic\" href=\"" + url + "\">" + topic + "</a>"; | |||||
} | |||||
} | |||||
html += " </div>"; | |||||
html += " </div>"; | |||||
html += " </div>"; | |||||
html += "</div>"; | |||||
} | |||||
} | |||||
orgRepo.innerHTML = html; | |||||
swiperRepo.updateSlides(); | |||||
swiperRepo.updateProgress(); | |||||
} | |||||
/** | |||||
* | |||||
* <div class="column"> | |||||
<div class="ui fluid card"> | |||||
<div class="content"> | |||||
<div class="ui small header"> | |||||
<img class="ui image" src="/user/avatar/OpenI/-1"> | |||||
<div class="content nowrap"> | |||||
<a href="/OpenI">OpenI</a> 启智社区 | |||||
<div class="sub header">39 项目 ・ 60 成员 ・ 23 团队</div> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
*/ | |||||
//var repoAndOrgZH = new Map([['1', "项目"], ['2', "成员"], ['3', "团队"]]); | |||||
//var repoAndOrgEN = new Map([['1', "Repository"], ['2', "Members"], ['3', "Teams"]]); | |||||
function getRepoOrOrg(key,isZhLang){ | |||||
if(isZhLang){ | |||||
return repoAndOrgZH[key]; | |||||
}else{ | |||||
return repoAndOrgEN[key]; | |||||
} | |||||
} | |||||
function displayOrg(json){ | |||||
var orgDiv = document.getElementById("recommendorg"); | |||||
var html = ""; | |||||
if (json != null && json.length > 0){ | |||||
for(var i = 0; i < json.length;i++){ | |||||
var record = json[i] | |||||
html += "<div class=\"column\">"; | |||||
html += " <a href=\"/" + record["Name"] + "\" class=\"ui fluid card\">"; | |||||
html += " <div class=\"content\">"; | |||||
html += " <div class=\"ui small header\">"; | |||||
html += " <img class=\"ui image\" src=\"" + record["Avatar"] + "\">"; | |||||
html += " <div class=\"content nowrap\">"; | |||||
html += " <span class=\"ui blue\">" + record["Name"] + "</span> " + record["FullName"]; | |||||
html += " <div class=\"sub header\">" + record["NumRepos"] +" " + getRepoOrOrg(1,isZh) + " ・ " + record["NumMembers"] +" " + getRepoOrOrg(2,isZh) + " ・ " + record["NumTeams"] + " " + getRepoOrOrg(3,isZh) + "</div>"; | |||||
html += " </div>"; | |||||
html += " </div>"; | |||||
html += " </div>"; | |||||
html += " </a>"; | |||||
html += "</div>"; | |||||
} | |||||
} | |||||
orgDiv.innerHTML = html; | |||||
} |
@@ -1,6 +1,8 @@ | |||||
package routers | package routers | ||||
import ( | import ( | ||||
"net/http" | |||||
"code.gitea.io/gitea/models" | "code.gitea.io/gitea/models" | ||||
"code.gitea.io/gitea/modules/context" | "code.gitea.io/gitea/modules/context" | ||||
"code.gitea.io/gitea/modules/log" | "code.gitea.io/gitea/modules/log" | ||||
@@ -11,6 +13,9 @@ import ( | |||||
var upgrader = websocket.Upgrader{ | var upgrader = websocket.Upgrader{ | ||||
ReadBufferSize: 1024, | ReadBufferSize: 1024, | ||||
WriteBufferSize: 1024, | WriteBufferSize: 1024, | ||||
CheckOrigin: func(r *http.Request) bool { | |||||
return true | |||||
}, | |||||
} | } | ||||
var SocketManager = socketwrap.NewClientsManager() | var SocketManager = socketwrap.NewClientsManager() | ||||
@@ -24,7 +29,7 @@ func ActionNotification(ctx *context.Context) { | |||||
} | } | ||||
client := &socketwrap.Client{Manager: SocketManager, Conn: conn, Send: make(chan *models.Action, 256)} | client := &socketwrap.Client{Manager: SocketManager, Conn: conn, Send: make(chan *models.Action, 256)} | ||||
WriteLastTenActionsIfHave(conn) | |||||
WriteLastActionsIfHave(conn) | |||||
client.Manager.Register <- client | client.Manager.Register <- client | ||||
@@ -32,22 +37,20 @@ func ActionNotification(ctx *context.Context) { | |||||
} | } | ||||
func WriteLastTenActionsIfHave(conn *websocket.Conn) { | |||||
socketwrap.LastTenActionsQueue.Mutex.RLock() | |||||
func WriteLastActionsIfHave(conn *websocket.Conn) { | |||||
socketwrap.LastActionsQueue.Mutex.RLock() | |||||
{ | { | ||||
size := socketwrap.LastTenActionsQueue.Queue.Len() | |||||
size := socketwrap.LastActionsQueue.Queue.Len() | |||||
if size > 0 { | if size > 0 { | ||||
tempE := socketwrap.LastTenActionsQueue.Queue.Front() | |||||
conn.WriteJSON(tempE) | |||||
tempE := socketwrap.LastActionsQueue.Queue.Front() | |||||
conn.WriteJSON(tempE.Value) | |||||
for i := 1; i < size; i++ { | for i := 1; i < size; i++ { | ||||
tempE = tempE.Next() | tempE = tempE.Next() | ||||
conn.WriteJSON(tempE) | |||||
conn.WriteJSON(tempE.Value) | |||||
} | } | ||||
} | } | ||||
} | } | ||||
socketwrap.LastTenActionsQueue.Mutex.RUnlock() | |||||
socketwrap.LastActionsQueue.Mutex.RUnlock() | |||||
} | } | ||||
@@ -44,9 +44,52 @@ const ( | |||||
func Home(ctx *context.Context) { | func Home(ctx *context.Context) { | ||||
ctx.Data["PageIsHome"] = true | ctx.Data["PageIsHome"] = true | ||||
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled | ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled | ||||
setRecommendURL(ctx) | |||||
ctx.HTML(200, tplHome) | ctx.HTML(200, tplHome) | ||||
} | } | ||||
func setRecommendURL(ctx *context.Context) { | |||||
addr := setting.RecommentRepoAddr[10:] | |||||
start := strings.Index(addr, "/") | |||||
end := strings.Index(addr, "raw") | |||||
if start != -1 && end != -1 { | |||||
ctx.Data["RecommendURL"] = addr[start:end] | |||||
} else { | |||||
ctx.Data["RecommendURL"] = setting.RecommentRepoAddr | |||||
} | |||||
ctx.Data["page_title"] = ctx.Tr("home.page_title") | |||||
ctx.Data["page_small_title"] = ctx.Tr("home.page_small_title") | |||||
ctx.Data["page_description"] = ctx.Tr("home.page_description") | |||||
ctx.Data["page_use"] = ctx.Tr("home.page_use") | |||||
ctx.Data["page_only_dynamic"] = ctx.Tr("home.page_only_dynamic") | |||||
ctx.Data["page_recommend_org"] = ctx.Tr("home.page_recommend_org") | |||||
ctx.Data["page_recommend_org_desc"] = ctx.Tr("home.page_recommend_org_desc") | |||||
ctx.Data["page_recommend_org_commit"] = ctx.Tr("home.page_recommend_org_commit") | |||||
ctx.Data["page_recommend_org_more"] = ctx.Tr("home.page_recommend_org_more") | |||||
ctx.Data["page_recommend_repo"] = ctx.Tr("home.page_recommend_repo") | |||||
ctx.Data["page_recommend_repo_desc"] = ctx.Tr("home.page_recommend_repo_desc") | |||||
ctx.Data["page_recommend_repo_commit"] = ctx.Tr("home.page_recommend_repo_commit") | |||||
ctx.Data["page_recommend_repo_go"] = ctx.Tr("home.page_recommend_repo_go") | |||||
ctx.Data["page_recommend_repo_more"] = ctx.Tr("home.page_recommend_repo_more") | |||||
ctx.Data["page_dev_env"] = ctx.Tr("home.page_dev_env") | |||||
ctx.Data["page_dev_env_desc"] = ctx.Tr("home.page_dev_env_desc") | |||||
ctx.Data["page_dev_env_desc_title"] = ctx.Tr("home.page_dev_env_desc_title") | |||||
ctx.Data["page_dev_env_desc_desc"] = ctx.Tr("home.page_dev_env_desc_desc") | |||||
ctx.Data["page_dev_env_desc1_title"] = ctx.Tr("home.page_dev_env_desc1_title") | |||||
ctx.Data["page_dev_env_desc1_desc"] = ctx.Tr("home.page_dev_env_desc1_desc") | |||||
ctx.Data["page_dev_env_desc2_title"] = ctx.Tr("home.page_dev_env_desc2_title") | |||||
ctx.Data["page_dev_env_desc2_desc"] = ctx.Tr("home.page_dev_env_desc2_desc") | |||||
ctx.Data["page_dev_env_desc3_title"] = ctx.Tr("home.page_dev_env_desc3_title") | |||||
ctx.Data["page_dev_env_desc3_desc"] = ctx.Tr("home.page_dev_env_desc3_desc") | |||||
ctx.Data["page_dev_yunlao"] = ctx.Tr("home.page_dev_yunlao") | |||||
ctx.Data["page_dev_yunlao_desc1"] = ctx.Tr("home.page_dev_yunlao_desc1") | |||||
ctx.Data["page_dev_yunlao_desc2"] = ctx.Tr("home.page_dev_yunlao_desc2") | |||||
ctx.Data["page_dev_yunlao_desc3"] = ctx.Tr("home.page_dev_yunlao_desc3") | |||||
ctx.Data["page_dev_yunlao_desc4"] = ctx.Tr("home.page_dev_yunlao_desc4") | |||||
ctx.Data["page_dev_yunlao_apply"] = ctx.Tr("home.page_dev_yunlao_apply") | |||||
} | |||||
func Dashboard(ctx *context.Context) { | func Dashboard(ctx *context.Context) { | ||||
if ctx.IsSigned { | if ctx.IsSigned { | ||||
if !ctx.User.IsActive && setting.Service.RegisterEmailConfirm { | if !ctx.User.IsActive && setting.Service.RegisterEmailConfirm { | ||||
@@ -77,7 +120,7 @@ func Dashboard(ctx *context.Context) { | |||||
ctx.Redirect(setting.AppSubURL + "/user/login") | ctx.Redirect(setting.AppSubURL + "/user/login") | ||||
return | return | ||||
} | } | ||||
setRecommendURL(ctx) | |||||
ctx.Data["PageIsHome"] = true | ctx.Data["PageIsHome"] = true | ||||
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled | ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled | ||||
ctx.HTML(200, tplHome) | ctx.HTML(200, tplHome) | ||||
@@ -513,43 +556,92 @@ func NotFound(ctx *context.Context) { | |||||
ctx.Data["Title"] = "Page Not Found" | ctx.Data["Title"] = "Page Not Found" | ||||
ctx.NotFound("home.NotFound", nil) | ctx.NotFound("home.NotFound", nil) | ||||
} | } | ||||
func RecommendOrgFromPromote(ctx *context.Context) { | func RecommendOrgFromPromote(ctx *context.Context) { | ||||
url := setting.RecommentRepoAddr + "organizations" | url := setting.RecommentRepoAddr + "organizations" | ||||
recommendFromPromote(ctx, url) | |||||
result, err := recommendFromPromote(url) | |||||
if err != nil { | |||||
ctx.ServerError("500", err) | |||||
return | |||||
} | |||||
resultOrg := make([]map[string]interface{}, 0) | |||||
for _, userName := range result { | |||||
user, err := models.GetUserByName(userName) | |||||
if err == nil { | |||||
userMap := make(map[string]interface{}) | |||||
userMap["Name"] = user.Name | |||||
userMap["Description"] = user.Description | |||||
userMap["FullName"] = user.FullName | |||||
userMap["ID"] = user.ID | |||||
userMap["Avatar"] = user.RelAvatarLink() | |||||
userMap["NumRepos"] = user.NumRepos | |||||
userMap["NumTeams"] = user.NumTeams | |||||
userMap["NumMembers"] = user.NumMembers | |||||
resultOrg = append(resultOrg, userMap) | |||||
} else { | |||||
log.Info("query user error," + err.Error()) | |||||
} | |||||
} | |||||
ctx.JSON(200, resultOrg) | |||||
} | } | ||||
func recommendFromPromote(ctx *context.Context, url string) { | |||||
func recommendFromPromote(url string) ([]string, error) { | |||||
resp, err := http.Get(url) | resp, err := http.Get(url) | ||||
if err != nil { | |||||
if err != nil || resp.StatusCode != 200 { | |||||
log.Info("Get organizations url error=" + err.Error()) | log.Info("Get organizations url error=" + err.Error()) | ||||
ctx.ServerError("QueryTrainJobList:", err) | |||||
return | |||||
return nil, err | |||||
} | } | ||||
bytes, err := ioutil.ReadAll(resp.Body) | bytes, err := ioutil.ReadAll(resp.Body) | ||||
resp.Body.Close() | resp.Body.Close() | ||||
if err != nil { | if err != nil { | ||||
log.Info("Get organizations url error=" + err.Error()) | log.Info("Get organizations url error=" + err.Error()) | ||||
ctx.ServerError("QueryTrainJobList:", err) | |||||
return | |||||
return nil, err | |||||
} | } | ||||
allLineStr := string(bytes) | allLineStr := string(bytes) | ||||
lines := strings.Split(allLineStr, "\n") | lines := strings.Split(allLineStr, "\n") | ||||
result := make([]string, len(lines)) | result := make([]string, len(lines)) | ||||
for i, line := range lines { | for i, line := range lines { | ||||
tmpIndex := strings.Index(line, ".") | |||||
log.Info("i=" + fmt.Sprint(i) + " line=" + line + " tmpIndex=" + fmt.Sprint(tmpIndex)) | |||||
if tmpIndex == -1 { | |||||
result[i] = strings.Trim(line, " ") | |||||
} else { | |||||
result[i] = strings.Trim(line[tmpIndex+1:], " ") | |||||
} | |||||
log.Info("i=" + fmt.Sprint(i) + " line=" + line) | |||||
result[i] = strings.Trim(line, " ") | |||||
} | } | ||||
ctx.JSON(http.StatusOK, result) | |||||
return result, nil | |||||
} | } | ||||
func RecommendRepoFromPromote(ctx *context.Context) { | func RecommendRepoFromPromote(ctx *context.Context) { | ||||
url := setting.RecommentRepoAddr + "projects" | url := setting.RecommentRepoAddr + "projects" | ||||
recommendFromPromote(ctx, url) | |||||
result, err := recommendFromPromote(url) | |||||
if err != nil { | |||||
ctx.ServerError("500", err) | |||||
return | |||||
} | |||||
resultRepo := make([]map[string]interface{}, 0) | |||||
//resultRepo := make([]*models.Repository, 0) | |||||
for _, repoName := range result { | |||||
tmpIndex := strings.Index(repoName, "/") | |||||
if tmpIndex == -1 { | |||||
log.Info("error repo name format.") | |||||
} else { | |||||
ownerName := strings.Trim(repoName[0:tmpIndex], " ") | |||||
repoName := strings.Trim(repoName[tmpIndex+1:], " ") | |||||
repo, err := models.GetRepositoryByOwnerAndName(ownerName, repoName) | |||||
if err == nil { | |||||
repoMap := make(map[string]interface{}) | |||||
repoMap["ID"] = fmt.Sprint(repo.ID) | |||||
repoMap["Name"] = repo.Name | |||||
repoMap["OwnerName"] = repo.OwnerName | |||||
repoMap["NumStars"] = repo.NumStars | |||||
repoMap["NumForks"] = repo.NumForks | |||||
repoMap["Description"] = repo.Description | |||||
repoMap["NumWatchs"] = repo.NumWatches | |||||
repoMap["Topics"] = repo.Topics | |||||
repoMap["Avatar"] = repo.RelAvatarLink() | |||||
resultRepo = append(resultRepo, repoMap) | |||||
} else { | |||||
log.Info("query repo error," + err.Error()) | |||||
} | |||||
} | |||||
} | |||||
ctx.JSON(200, resultRepo) | |||||
} | } |
@@ -219,7 +219,7 @@ func CloudBrainCreate(ctx *context.Context, form auth.CreateCloudBrainForm) { | |||||
gpuType = gpuInfo.Value | gpuType = gpuInfo.Value | ||||
} | } | ||||
} | } | ||||
downloadRateCode(repo, jobName, setting.BenchmarkOwner, setting.BrainScoreName, benchmarkPath, form.BenchmarkCategory, gpuType) | |||||
downloadRateCode(repo, jobName, setting.BenchmarkOwner, setting.BenchmarkName, benchmarkPath, form.BenchmarkCategory, gpuType) | |||||
uploadCodeToMinio(benchmarkPath+"/", jobName, cloudbrain.BenchMarkMountPath+"/") | uploadCodeToMinio(benchmarkPath+"/", jobName, cloudbrain.BenchMarkMountPath+"/") | ||||
} | } | ||||
@@ -14,6 +14,11 @@ type Client struct { | |||||
Send chan *models.Action | Send chan *models.Action | ||||
} | } | ||||
func (c *Client) Close() { | |||||
close(c.Send) | |||||
c.Conn.Close() | |||||
} | |||||
func (c *Client) WritePump() { | func (c *Client) WritePump() { | ||||
defer func() { | defer func() { | ||||
@@ -1,11 +1,17 @@ | |||||
package socketwrap | package socketwrap | ||||
import ( | import ( | ||||
"os" | |||||
"os/signal" | |||||
"syscall" | |||||
"code.gitea.io/gitea/models" | "code.gitea.io/gitea/models" | ||||
"code.gitea.io/gitea/modules/log" | |||||
"github.com/elliotchance/orderedmap" | |||||
) | ) | ||||
type ClientsManager struct { | type ClientsManager struct { | ||||
Clients map[*Client]bool | |||||
Clients *orderedmap.OrderedMap | |||||
Register chan *Client | Register chan *Client | ||||
Unregister chan *Client | Unregister chan *Client | ||||
} | } | ||||
@@ -14,32 +20,69 @@ func NewClientsManager() *ClientsManager { | |||||
return &ClientsManager{ | return &ClientsManager{ | ||||
Register: make(chan *Client), | Register: make(chan *Client), | ||||
Unregister: make(chan *Client), | Unregister: make(chan *Client), | ||||
Clients: make(map[*Client]bool), | |||||
Clients: orderedmap.NewOrderedMap(), | |||||
} | } | ||||
} | } | ||||
var LastTenActionsQueue = NewSyncQueue(10) | |||||
const MaxClients = 100 | |||||
var LastActionsQueue = NewSyncQueue(15) | |||||
func (h *ClientsManager) Run() { | func (h *ClientsManager) Run() { | ||||
initActionQueue() | |||||
sig := make(chan os.Signal, 1) | |||||
signal.Notify(sig, os.Interrupt, syscall.SIGTERM) | |||||
var signalsReceived uint | |||||
for { | for { | ||||
select { | select { | ||||
case client := <-h.Register: | case client := <-h.Register: | ||||
h.Clients[client] = true | |||||
h.Clients.Set(client, true) | |||||
if h.Clients.Len() > MaxClients { | |||||
h.Clients.Delete(h.Clients.Front().Key) | |||||
} | |||||
case client := <-h.Unregister: | case client := <-h.Unregister: | ||||
if _, ok := h.Clients[client]; ok { | |||||
delete(h.Clients, client) | |||||
if _, ok := h.Clients.Get(client); ok { | |||||
h.Clients.Delete(client) | |||||
close(client.Send) | close(client.Send) | ||||
} | } | ||||
case message := <-models.ActionChan: | case message := <-models.ActionChan: | ||||
LastTenActionsQueue.Push(message) | |||||
for client := range h.Clients { | |||||
LastActionsQueue.Push(message) | |||||
for _, client := range h.Clients.Keys() { | |||||
select { | select { | ||||
case client.Send <- message: | |||||
case client.(*Client).Send <- message: | |||||
default: | default: | ||||
close(client.Send) | |||||
delete(h.Clients, client) | |||||
close(client.(*Client).Send) | |||||
h.Clients.Delete(client) | |||||
} | } | ||||
} | } | ||||
case s := <-sig: | |||||
log.Info("received signal", s) | |||||
signalsReceived++ | |||||
if signalsReceived < 2 { | |||||
for _, client := range h.Clients.Keys() { | |||||
h.Clients.Delete(client) | |||||
client.(*Client).Close() | |||||
} | |||||
break | |||||
} | |||||
} | |||||
} | |||||
} | |||||
func initActionQueue() { | |||||
actions, err := models.GetLast20PublicFeeds() | |||||
if err == nil { | |||||
for i := len(actions) - 1; i >= 0; i-- { | |||||
user, err := models.GetUserByID(actions[i].UserID) | |||||
if err == nil { | |||||
if !user.IsOrganization() { | |||||
LastActionsQueue.Push(actions[i]) | |||||
} | |||||
} | |||||
} | } | ||||
} | } | ||||
@@ -26,12 +26,13 @@ | |||||
{{end}} | {{end}} | ||||
</div> | </div> | ||||
</div> | </div> | ||||
<a href="https://git.openi.org.cn/zeizei/OpenI_Learning" class="ui item" target="_blank">{{.i18n.Tr "custom.Platform_Tutorial"}}</a> | |||||
{{if .EnableSwagger}}<a href="/api/swagger" class="ui item">API</a>{{end}} | |||||
<a href="https://git.openi.org.cn/zeizei/OpenI_Learning" class=" item a_margin" target="_blank"><i class="ri-creative-commons-by-line footer_icon" ></i><p class="footer_icon">{{.i18n.Tr "custom.Platform_Tutorial"}}</p> </a> | |||||
{{if .EnableSwagger}}<a href="/api/swagger" class=" item a_margin"><i class="ri-exchange-line footer_icon" > </i><p class="footer_icon">API</p> </a>{{end}} | |||||
{{if .IsSigned}} | {{if .IsSigned}} | ||||
<a href="https://git.openi.org.cn/zeizei/OpenI_Learning/issues/new" class="ui item" target="_blank">{{.i18n.Tr "custom.foot.advice_feedback"}}</a> | |||||
<a href="https://git.openi.org.cn/zeizei/OpenI_Learning/issues/new" class=" item a_margin" target="_blank"><i class="ri-mail-send-line footer_icon"></i><p class="footer_icon">{{.i18n.Tr "custom.foot.advice_feedback"}}</p></a> | |||||
{{else}} | {{else}} | ||||
<a href="{{AppSubUrl}}/user/login" class="ui item">{{.i18n.Tr "custom.foot.advice_feedback"}}</a> | |||||
<a href="{{AppSubUrl}}/user/login" class=" item a_margin" ><i class="ri-mail-send-line footer_icon" ></i><p class="footer_icon">{{.i18n.Tr "custom.foot.advice_feedback"}}</p></a> | |||||
{{end}} | {{end}} | ||||
{{template "custom/extra_links_footer" .}} | {{template "custom/extra_links_footer" .}} | ||||
@@ -26,12 +26,12 @@ | |||||
{{end}} | {{end}} | ||||
</div> | </div> | ||||
</div> | </div> | ||||
<a href="https://git.openi.org.cn/zeizei/OpenI_Learning" class="ui item" target="_blank">{{.i18n.Tr "custom.Platform_Tutorial"}}</a> | |||||
{{if .EnableSwagger}}<a href="/api/swagger" class="ui item">API</a>{{end}} | |||||
<a href="https://git.openi.org.cn/zeizei/OpenI_Learning" class=" item a_margin" target="_blank"><i class="ri-creative-commons-by-line footer_icon" ></i><p class="footer_icon">{{.i18n.Tr "custom.Platform_Tutorial"}}</p> </a> | |||||
{{if .EnableSwagger}}<a href="/api/swagger" class=" item a_margin"><i class="ri-exchange-line footer_icon" > </i><p class="footer_icon">API</p> </a>{{end}} | |||||
{{if .IsSigned}} | {{if .IsSigned}} | ||||
<a href="https://git.openi.org.cn/zeizei/OpenI_Learning/issues/new" class="ui item" target="_blank">{{.i18n.Tr "custom.foot.advice_feedback"}}</a> | |||||
<a href="https://git.openi.org.cn/zeizei/OpenI_Learning/issues/new" class=" item a_margin" target="_blank"><i class="ri-mail-send-line footer_icon"></i><p class="footer_icon">{{.i18n.Tr "custom.foot.advice_feedback"}}</p></a> | |||||
{{else}} | {{else}} | ||||
<a href="{{AppSubUrl}}/user/login" class="ui item">{{.i18n.Tr "custom.foot.advice_feedback"}}</a> | |||||
<a href="{{AppSubUrl}}/user/login" class=" item a_margin" ><i class="ri-mail-send-line footer_icon" ></i><p class="footer_icon footer_icon">{{.i18n.Tr "custom.foot.advice_feedback"}}</p></a> | |||||
{{end}} | {{end}} | ||||
{{template "custom/extra_links_footer" .}} | {{template "custom/extra_links_footer" .}} | ||||
</div> | </div> | ||||
@@ -174,6 +174,7 @@ | |||||
{{else if ne DefaultTheme "gitea"}} | {{else if ne DefaultTheme "gitea"}} | ||||
<link rel="stylesheet" href="{{StaticUrlPrefix}}/css/theme-{{DefaultTheme}}.css?v={{MD5 AppVer}}"> | <link rel="stylesheet" href="{{StaticUrlPrefix}}/css/theme-{{DefaultTheme}}.css?v={{MD5 AppVer}}"> | ||||
{{end}} | {{end}} | ||||
<link rel="stylesheet" href="/RemixIcon_Fonts_v2.5.0/fonts/remixicon.css"> | |||||
{{template "custom/header" .}} | {{template "custom/header" .}} | ||||
<script> | <script> | ||||
@@ -186,7 +187,6 @@ var _hmt = _hmt || []; | |||||
})(); | })(); | ||||
</script> | </script> | ||||
<script src="/self/func.js" type="text/javascript"></script> | <script src="/self/func.js" type="text/javascript"></script> | ||||
</head> | </head> | ||||
<body> | <body> | ||||
{{template "custom/body_outer_pre" .}} | {{template "custom/body_outer_pre" .}} | ||||
@@ -206,7 +206,7 @@ var _hmt = _hmt || []; | |||||
{{.notice.Title}} | {{.notice.Title}} | ||||
</marquee> | </marquee> | ||||
</a> | </a> | ||||
<i class="icon icon-octicon x_icon" onclick="closeNoice()">{{svg "octicon-x" 16}}</i> | |||||
<i class="ri-close-fill x_icon" onclick="closeNoice()"></i> | |||||
</div> | </div> | ||||
{{end}} | {{end}} | ||||
{{/* | {{/* | ||||
@@ -231,7 +231,7 @@ var _hmt = _hmt || []; | |||||
}else{ | }else{ | ||||
isNewNotice=false; | isNewNotice=false; | ||||
} | } | ||||
if ("{{.notice.Visible}}"){ | |||||
if (JSON.parse("{{.notice.Visible}}")){ | |||||
if(isNewNotice){ | if(isNewNotice){ | ||||
document.getElementById("notic_content").style.display='block' | document.getElementById("notic_content").style.display='block' | ||||
}else{ | }else{ | ||||
@@ -174,6 +174,7 @@ | |||||
{{else if ne DefaultTheme "gitea"}} | {{else if ne DefaultTheme "gitea"}} | ||||
<link rel="stylesheet" href="{{StaticUrlPrefix}}/css/theme-{{DefaultTheme}}.css?v={{MD5 AppVer}}"> | <link rel="stylesheet" href="{{StaticUrlPrefix}}/css/theme-{{DefaultTheme}}.css?v={{MD5 AppVer}}"> | ||||
{{end}} | {{end}} | ||||
<link rel="stylesheet" href="/RemixIcon_Fonts_v2.5.0/fonts/remixicon.css"> | |||||
{{template "custom/header" .}} | {{template "custom/header" .}} | ||||
<script> | <script> | ||||
@@ -206,7 +207,7 @@ var _hmt = _hmt || []; | |||||
{{.notice.Title}} | {{.notice.Title}} | ||||
</marquee> | </marquee> | ||||
</a> | </a> | ||||
<i class="icon icon-octicon x_icon" onclick="closeNoice()">{{svg "octicon-x" 16}}</i> | |||||
<i class="ri-close-fill x_icon" onclick="closeNoice()"></i> | |||||
</div> | </div> | ||||
{{end}} | {{end}} | ||||
{{/* | {{/* | ||||
@@ -231,7 +232,7 @@ var _hmt = _hmt || []; | |||||
}else{ | }else{ | ||||
isNewNotice=false; | isNewNotice=false; | ||||
} | } | ||||
if ("{{.notice.Visible}}"){ | |||||
if (JSON.parse("{{.notice.Visible}}")){ | |||||
if(isNewNotice){ | if(isNewNotice){ | ||||
document.getElementById("notic_content").style.display='block' | document.getElementById("notic_content").style.display='block' | ||||
}else{ | }else{ | ||||
@@ -186,25 +186,71 @@ var _hmt = _hmt || []; | |||||
s.parentNode.insertBefore(hm, s); | s.parentNode.insertBefore(hm, s); | ||||
})(); | })(); | ||||
</script> | </script> | ||||
<script src="/self/func.js" type="text/javascript"></script> | |||||
<!--RemixIcon Fonts v2.5.0--> | |||||
<link rel="stylesheet" href="/RemixIcon_Fonts_v2.5.0/fonts/remixicon.css"> | |||||
<!-- Swiper --> | |||||
<link rel="stylesheet" href="/swiper/swiper-bundle.min.css"> | |||||
<script src="/swiper/swiper-bundle.min.js"></script> | |||||
</head> | </head> | ||||
<body> | <body> | ||||
{{template "custom/body_outer_pre" .}} | {{template "custom/body_outer_pre" .}} | ||||
<div class="full height"> | <div class="full height"> | ||||
<noscript>{{.i18n.Tr "enable_javascript"}}</noscript> | <noscript>{{.i18n.Tr "enable_javascript"}}</noscript> | ||||
<div class="ui vertical masthead secondary hometop segment"> | |||||
{{template "custom/body_inner_pre" .}} | |||||
{{if not .PageIsInstall}} | |||||
<div class="ui container"> | |||||
<div class="ui top secondary stackable main menu following bar"> | |||||
{{template "base/head_navbar_home" .}} | |||||
</div><!-- end bar --> | |||||
</div> | |||||
{{end}} | |||||
{{template "custom/body_inner_pre" .}} | |||||
{{if not .PageIsInstall}} | |||||
<div class="ui top secondary stackable main menu following bar dark"> | |||||
{{template "base/head_navbar" .}} | |||||
</div><!-- end bar --> | |||||
<div class="notic_content" id ="notic_content" > | |||||
<a href={{.notice.Link}} class="a_width"> | |||||
<marquee behavior="scroll" direction="left"> | |||||
{{.notice.Title}} | |||||
</marquee> | |||||
</a> | |||||
<i class="ri-close-fill x_icon" onclick="closeNoice()"></i> | |||||
</div> | |||||
{{end}} | |||||
{{/* | {{/* | ||||
</div> | |||||
</div> | </div> | ||||
</body> | </body> | ||||
</html> | </html> | ||||
*/}} | */}} | ||||
<script> | |||||
function closeNoice(){ | |||||
document.getElementById("notic_content").style.display='none' | |||||
localStorage.setItem("isCloseNotice",true) | |||||
} | |||||
function isShowNotice(){ | |||||
var current_notice = localStorage.getItem("notice") | |||||
if (current_notice != "{{.notice.CommitId}}"){ | |||||
localStorage.setItem('notice',"{{.notice.CommitId}}"); | |||||
isNewNotice=true; | |||||
localStorage.setItem("isCloseNotice",false) | |||||
}else{ | |||||
isNewNotice=false; | |||||
} | |||||
if (JSON.parse("{{.notice.Visible}}")){ | |||||
if(isNewNotice){ | |||||
document.getElementById("notic_content").style.display='block' | |||||
}else{ | |||||
isCloseNotice = localStorage.getItem("isCloseNotice") | |||||
if (JSON.parse(isCloseNotice)){ | |||||
document.getElementById("notic_content").style.display='none' | |||||
}else{ | |||||
document.getElementById("notic_content").style.display='block' | |||||
} | |||||
} | |||||
}else{ | |||||
document.getElementById("notic_content").style.display='none' | |||||
} | |||||
} | |||||
isShowNotice(); | |||||
</script> |
@@ -174,6 +174,7 @@ | |||||
{{else if ne DefaultTheme "gitea"}} | {{else if ne DefaultTheme "gitea"}} | ||||
<link rel="stylesheet" href="{{StaticUrlPrefix}}/css/theme-{{DefaultTheme}}.css?v={{MD5 AppVer}}"> | <link rel="stylesheet" href="{{StaticUrlPrefix}}/css/theme-{{DefaultTheme}}.css?v={{MD5 AppVer}}"> | ||||
{{end}} | {{end}} | ||||
<link rel="stylesheet" href="/RemixIcon_Fonts_v2.5.0/fonts/remixicon.css"> | |||||
{{template "custom/header" .}} | {{template "custom/header" .}} | ||||
<script> | <script> | ||||
@@ -232,7 +233,7 @@ var _hmt = _hmt || []; | |||||
}else{ | }else{ | ||||
isNewNotice=false; | isNewNotice=false; | ||||
} | } | ||||
if ("{{.notice.Visible}}"){ | |||||
if (JSON.parse("{{.notice.Visible}}")){ | |||||
if(isNewNotice){ | if(isNewNotice){ | ||||
document.getElementById("notic_content").style.display='block' | document.getElementById("notic_content").style.display='block' | ||||
}else{ | }else{ | ||||
@@ -1,7 +1,7 @@ | |||||
{{$paginationLink := .Page.GetParams}} | {{$paginationLink := .Page.GetParams}} | ||||
{{with .Page.Paginater}} | {{with .Page.Paginater}} | ||||
{{if gt .TotalPages 1}} | {{if gt .TotalPages 1}} | ||||
<div class="center page buttons"> | |||||
<div class="center page buttons" style="margin: 0px auto 15px"> | |||||
<div class="ui borderless pagination menu"> | <div class="ui borderless pagination menu"> | ||||
<a class="{{if .IsFirst}}disabled{{end}} item navigation" {{if not .IsFirst}}href="{{$.Link}}{{if $paginationLink}}?{{$paginationLink}}{{end}}"{{end}}><i class="angle double left icon"></i><span class="navigation_label"> {{$.i18n.Tr "admin.first_page"}}</span></a> | <a class="{{if .IsFirst}}disabled{{end}} item navigation" {{if not .IsFirst}}href="{{$.Link}}{{if $paginationLink}}?{{$paginationLink}}{{end}}"{{end}}><i class="angle double left icon"></i><span class="navigation_label"> {{$.i18n.Tr "admin.first_page"}}</span></a> | ||||
<a class="{{if not .HasPrevious}}disabled{{end}} item navigation" {{if .HasPrevious}}href="{{$.Link}}?page={{.Previous}}{{if $paginationLink}}&{{$paginationLink}}{{end}}"{{end}}> | <a class="{{if not .HasPrevious}}disabled{{end}} item navigation" {{if .HasPrevious}}href="{{$.Link}}?page={{.Previous}}{{if $paginationLink}}&{{$paginationLink}}{{end}}"{{end}}> | ||||
@@ -59,7 +59,7 @@ | |||||
</div> | </div> | ||||
<div class="swiper-slide"> | <div class="swiper-slide"> | ||||
<div class="ui card"> | <div class="ui card"> | ||||
<a class="image" href="https://git.openi.org.cn/TensorLayer"> | |||||
<a class="image" href="https://git.openi.org.cn/OpenI/TensorLayerX"> | |||||
<img src="/img/org-tensorlayer@2x-80.jpg" alt="TensorLayer" title="TensorLayer"> | <img src="/img/org-tensorlayer@2x-80.jpg" alt="TensorLayer" title="TensorLayer"> | ||||
</a> | </a> | ||||
</div> | </div> | ||||
@@ -1,221 +1,161 @@ | |||||
{{template "base/head_home" .}} | {{template "base/head_home" .}} | ||||
<div class="ui container homebanner"> | |||||
<h1 class="ui header"> | |||||
启智AI开发协作平台 | |||||
<div class="sub header"> | |||||
面向AI领域的一站式协同开发环境 | |||||
</div> | |||||
</h1> | |||||
<p class="am-lh-18">免费私有代码仓库,免费计算资源,大容量数据存储,<br>多类型硬件环境(GPU、NPU),AI开发流水线(开发-调试-训练-迭代)</p> | |||||
{{if .IsSigned}} | |||||
<a class="circular ui secondary button" href="{{AppSubUrl}}/dashboard">立即使用 <i class="right arrow icon"></i></a> | |||||
{{else}} | |||||
<a class="circular ui secondary button" href="{{AppSubUrl}}/user/login">立即使用 <i class="right arrow icon"></i></a> | |||||
{{end}} | |||||
<div class="bannerpic"><img class="ui fluid image" src="/img/gitopeni-index-01.svg"></div> | |||||
</div> | |||||
</div><!-- end segment --> | |||||
<div class="explore repositories"> | |||||
<div class="ui sticky container"> | |||||
<div class="ui secondary pointing fluid five item menu" style="background-color: #FFF;"> | |||||
<a class="item" href="#first">代码</a> | |||||
<a class="item" href="#second">数据</a> | |||||
<a class="item" href="#third">AI流水线</a> | |||||
<a class="item" href="#fourth">协同开发</a> | |||||
<a class="item" href="#fifth">云脑协同</a> | |||||
</div> | |||||
</div> | |||||
<div id="railContent"> | |||||
<a name="first"></a> | |||||
<div class="ui container am-mt-30 basic tab i-code active" style="position: relative;"> | |||||
<div class="ui inverted very padded segment radius15 am-pl-30"> | |||||
<div class="ui mobile reversed stackable grid am-pl-30"> | |||||
<div class="six wide column"> | |||||
<h2 class="ui huge blue header">代码管理</h2> | |||||
<p class="am-lh-18">在这里为你和你的团队创建项目,基于Git工具,提交记录或者回滚代码修改。<br> | |||||
不论是公开或者私有仓库,都可免费使用所有功能。<br> | |||||
尽情将你喜欢的代码都放在这里,仓库数量、存储容量不受限</p> | |||||
{{if .IsSigned}} | |||||
<a class="ui blue basic button am-mt-20" href="{{AppSubUrl}}/dashboard">立即使用 </a> | |||||
{{else}} | |||||
<a class="ui blue basic button am-mt-20" href="{{AppSubUrl}}/user/login">立即使用 </a> | |||||
{{end}} | |||||
</div> | |||||
<div class="ten wide column computer only i-code-pic am-pt-30"> | |||||
<img class="ui fluid rounded image am-shadow-2 am-mt-10" src="/img/i-code-pic.jpg" style="position: absolute;"> | |||||
<div class="ui vertical masthead secondary hometop segment"> | |||||
<div class="ui container" style="position: relative;"> | |||||
<div class="ui center homebanner"> | |||||
<h1 class="ui huge header"> | |||||
{{.page_title}} | |||||
<div class="sub header"> | |||||
{{.page_small_title}} | |||||
</div> | </div> | ||||
</div> | |||||
</h1> | |||||
<p class="ui am-lh-18">{{.page_description}}</p> | |||||
{{if .IsSigned}} | |||||
<a class="circular huge ui secondary button" href="{{AppSubUrl}}/dashboard">{{.page_use}} <i class="right arrow icon"></i></a> | |||||
{{else}} | |||||
<a class="circular huge ui secondary button" href="{{AppSubUrl}}/user/login">{{.page_use}} <i class="right arrow icon"></i></a> | |||||
{{end}} | |||||
</div> | </div> | ||||
<div class="am-mt-30 am-pt-30 am-pl-30"> | |||||
<div class="am-pl-30 am-pb-30"> | |||||
<h2 class="ui huge header am-bw">协作开发</h2> | |||||
<p class="am-lh-18">鼓励通过创建合并请求(PR)的方式,更好的进行团队协作<br> | |||||
代码评审让每一次的代码修改得以二次确认,提高代码质量<br> | |||||
创建并指派任务(Issue),让每一个任务的进展有迹可循,规范管理<br> | |||||
被合并的PR,可获得奖励积分;积分总额可以显示出你在项目中的贡献度,也许有一天会有人愿意为此付费<br> | |||||
</p> | |||||
</div> | |||||
</div> | |||||
<div class="leftline01"></div> | |||||
</div> | |||||
<a name="second"></a> | |||||
<div class="ui basic tab active am-mt-30 bg-gray am-pt-30"> | |||||
<div class="ui container i-data" style="position: relative;"> | |||||
<div class="leftline02"></div> | |||||
<div class="ui stackable grid"> | |||||
<div class="nine wide column"> | |||||
<img class="ui centered medium rounded large image" src="/img/gitopeni-index-02.svg"> | |||||
<div class="i-code-pic" style="margin-top: -4.0rem;"> | |||||
<img class="ui fluid rounded image am-shadow-2" src="/img/i-data-pic.jpg"> | |||||
<div class="bannerpic"><img class="ui fluid image" src="/img/gitopeni-index-01.svg"></div> | |||||
<div id="homenews" class="ui container"> | |||||
<p>* {{.page_only_dynamic}}</p> | |||||
<div class="ui grid"> | |||||
<div class="twelve wide tablet ten wide computer column homenews"> | |||||
<div class="newslist"> | |||||
<div class="ui mini aligned list swiper-wrapper" id="newmessage"> | |||||
</div> | |||||
</div> | </div> | ||||
</div> | </div> | ||||
<div class="seven wide column am-pt-30"> | |||||
<h2 class="ui huge blue header">数据集管理</h2> | |||||
<p class="am-lh-18"> | |||||
数据是重要的生产要素,AI开发更是离不开数据;<br> | |||||
- 数据与模型代码的协同,可在项目中上传关联的数据集;<br> | |||||
- 数据存储免费,不限制文件大小;<br> | |||||
- 数据可共享,标注清洗过的公开数据集,用户可设置为公有数据,供社区用户下载; | |||||
</p> | |||||
<div class="am-mt-30 am-pt-20"></div> | |||||
<h2 class="ui huge header">使用数据集</h2> | |||||
<p class="am-lh-18"> | |||||
数据集可以直接用于训练或者推理任务中<br> | |||||
创建任务时选择对应的数据集,启动任务执行环境(Docker)后,即可在Docker内访问到你的代码和数据,就像在你本地执行一样 | |||||
</p> | |||||
</div> | |||||
</div> | |||||
</div><!-- end homenews --> | |||||
</div> | |||||
</div><!-- end segment --> | |||||
<!--组织--> | |||||
<div class="ui container homeorg"> | |||||
<div class="ui stackable grid"> | |||||
<div class="sixteen wide tablet four wide computer column homeorg-tit"> | |||||
<h2>{{.page_recommend_org}}</h2> | |||||
<p><span class="ui text grey">{{.page_recommend_org_desc}}</span><a href="{{.RecommendURL}}">{{.page_recommend_org_commit}}</a></p> | |||||
<a href="{{AppSubUrl}}/explore/organizations" class="circular ui primary basic button">{{.page_recommend_org_more}} <i class="arrow circle right icon"></i></a> | |||||
</div> | |||||
<div class="sixteen wide tablet twelve wide computer column"> | |||||
<div class="ui stackable three column grid homeorg-list" id="recommendorg"> | |||||
</div> | </div> | ||||
</div> | </div> | ||||
</div> | |||||
</div> | |||||
<div class="leftline01"></div> | |||||
</div> | |||||
<!--项目--> | |||||
<div class="ui container homepro"> | |||||
<div class="leftline02"></div> | |||||
<div class="leftline02-2"></div> | |||||
<div class="ui center homepro-tit am-mb-20"> | |||||
<h2>{{.page_recommend_repo}}</h2> | |||||
<p><span class="ui text grey">{{.page_recommend_repo_desc}}</span><a href="{{.RecommendURL}}">{{.page_recommend_repo_commit}}</a>{{.page_recommend_repo_go}}<a href="{{AppSubUrl}}/explore/">{{.page_recommend_repo_more}}</a></p> | |||||
</div> | |||||
<a name="third"></a> | |||||
<div class="ui basic tab active container am-mt-30 am-pt-30"> | |||||
<h2 class="ui center aligned huge blue header am-pt-30"> | |||||
AI流水线 | |||||
<div class="sub header am-mt-10">提供集代码开发、数据管理、模型调试、推理和评测为一体的AI开发流水线</div> | |||||
</h2> | |||||
<div class="ui divider"></div> | |||||
<div class="ui centered grid"> | |||||
<div class="fourteen wide column"> | |||||
<div class="ui two column grid"> | |||||
<div class="column"> | |||||
<div class="ui small header">调试任务:</div> | |||||
<div class="ui bulleted list"> | |||||
<div class="item">配置模型运行环境;</div> | |||||
<div class="item">可在线编辑和调试模型代码,并将改动更新至代码仓库;</div> | |||||
<div class="item">基于编辑好的脚本,开展模型评测任务;</div> | |||||
</div> | |||||
</div> | |||||
<div class="column"> | |||||
<div class="ui small header">作业任务:</div> | |||||
<div class="ui bulleted list"> | |||||
<div class="item">利用已配置好的模型运行环境;</div> | |||||
<div class="item">基于编辑好的脚本,一键运行,开展模型训练或模型推理任务;</div> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
<div class="homepro-list"> | |||||
<div class="swiper-wrapper" id="recommendrepo"> | |||||
</div> | </div> | ||||
<div class="ui divider"></div> | |||||
<img class="ui centered image" src="/img/develop.svg"> | |||||
<div class="swiper-pagination"></div> | |||||
</div> | </div> | ||||
</div> | |||||
<a name="fourth"></a> | |||||
<div class="ui basic tab active container am-mt-30 am-pt-30 i-env"> | |||||
<h2 class="ui center aligned huge blue header am-pt-30"> | |||||
协同开发环境 | |||||
<div class="sub header am-mt-10">启智AI协作开发平台与传统git平台最大的不同就在于提供了面向AI开发的协同开发环境</div> | |||||
</h2> | |||||
<div class="ui four stackable cards am-mt-20"> | |||||
<div class="card"> | |||||
<div class="image"> | |||||
<img src="/img/i-pic-01.svg"> | |||||
<a name="fourth"></a> | |||||
<div class="ui container i-env"> | |||||
<div class="ui center am-pb-30"> | |||||
<div class="leftline03"></div> | |||||
<h2>{{.page_dev_env}}</h2> | |||||
<p><span class="ui text grey">{{.page_dev_env_desc}}</p> | |||||
</div> | |||||
<div class="ui four stackable cards"> | |||||
<div class="card"> | |||||
<div class="image"> | |||||
<img src="/img/i-pic-01.svg"> | |||||
</div> | |||||
<div class="content"> | |||||
<h3 class="ui centered small header">{{.page_dev_env_desc_title}}</h3> | |||||
<div class="description"> | |||||
{{.page_dev_env_desc_desc}} | |||||
</div> | </div> | ||||
<div class="content"> | |||||
<h3 class="ui centered header">开发要素统一管理</h3> | |||||
<div class="description"> | |||||
平台提供了AI开发四大要素:模型代码、数据集、模型和执行环境的统一管理 | |||||
</div> | |||||
</div> | |||||
</div> | |||||
<div class="card"> | |||||
<div class="image"> | |||||
<img src="/img/i-pic-02.svg"> | |||||
</div> | |||||
<div class="content"> | |||||
<h3 class="ui centered small header">{{.page_dev_env_desc1_title}}</h3> | |||||
<div class="description"> | |||||
{{.page_dev_env_desc1_desc}} | |||||
</div> | </div> | ||||
</div> | </div> | ||||
</div> | |||||
<div class="card"> | <div class="card"> | ||||
<div class="image"> | |||||
<img src="/img/i-pic-02.svg"> | |||||
</div> | |||||
<div class="content"> | |||||
<h3 class="ui centered header">数据协同与共享</h3> | |||||
<div class="description"> | |||||
通过在项目中上传数据集,项目成员多人协作完成数据预处理;也可以通过将数据设置为公有数据集,与社区开发者共同建立更好的模型 | |||||
</div> | |||||
</div> | |||||
<div class="image"> | |||||
<img src="/img/i-pic-03.svg"> | |||||
</div> | </div> | ||||
<div class="card"> | |||||
<div class="image"> | |||||
<img src="/img/i-pic-03.svg"> | |||||
</div> | |||||
<div class="content"> | |||||
<h3 class="ui centered header">模型管理与共享</h3> | |||||
<div class="description"> | |||||
将模型与代码版本建立关联,可以基于代码历史版本,使用不同的方式调整模型,并将结果保存下来;训练好的模型可以开放共享,让更多人的使用模型测试并提出反馈 | |||||
</div> | |||||
<div class="content"> | |||||
<h3 class="ui centered small header">{{.page_dev_env_desc2_title}}</h3> | |||||
<div class="description"> | |||||
{{.page_dev_env_desc2_desc}} | |||||
</div> | </div> | ||||
</div> | </div> | ||||
<div class="card"> | |||||
<div class="image"> | |||||
<img src="/img/i-pic-04.svg"> | |||||
</div> | |||||
<div class="content"> | |||||
<h3 class="ui centered header">一次配置,多次使用</h3> | |||||
<div class="description"> | |||||
提供执行环境共享,一次配置,多次使用,降低模型开发门槛,避免花费重复的时间配置复杂的环境 | |||||
</div> | |||||
</div> | |||||
<div class="card"> | |||||
<div class="image"> | |||||
<img src="/img/i-pic-04.svg"> | |||||
</div> | |||||
<div class="content"> | |||||
<h3 class="ui centered small header">{{.page_dev_env_desc3_title}}</h3> | |||||
<div class="description"> | |||||
{{.page_dev_env_desc3_desc}} | |||||
</div> | </div> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
</div> | |||||
<a name="fifth"></a> | |||||
<div class="ui basic tab active container am-mt-30 am-pt-30"> | |||||
<div class="ui very padded black inverted segment radius15"> | |||||
<div class="ui stackable grid"> | |||||
<div class="six wide column"> | |||||
<img class="ui centered large image" src="/img/i-yunnao.svg"> | |||||
</div> | |||||
<div class="ten wide column am-pt-30"> | |||||
<h2 class="ui huge blue header">鹏城云脑开源协同</h2> | |||||
<p class="am-lh-18"> | |||||
平台已经与鹏城云脑打通,可以利用鹏城云脑的丰富算力资源,完成AI开发任务<br> | |||||
鹏城云脑现有AI算力100P FLOPS@FP16(每秒十亿亿次半精度浮点计算),主要硬件基础设施由搭载英伟达Tesla V100 的GPU服务器和搭载鲲鹏、昇腾处理器的Atlas 900 AI集群构成<br> | |||||
开发者可以根据使用需求,自由选择相应计算资源,可以测试模型在不同硬件环境下的适配能力、性能、稳定性等<br> | |||||
如果您的模型需要更多的计算资源,也可以单独申请<br> | |||||
</p> | |||||
{{if .IsSigned}} | |||||
<a class="ui blue basic button am-mt-20" href="{{AppSubUrl}}/dashboard">立即使用 </a><a class="ui grey basic button am-mt-20" href="mailto:aiforge@openi.org.cn">单独申请</a> | |||||
{{else}} | |||||
<a class="ui blue basic button am-mt-20" href="{{AppSubUrl}}/user/login">立即使用 </a><a class="ui grey basic button am-mt-20" href="mailto:aiforge@openi.org.cn">单独申请</a> | |||||
{{end}} | |||||
</div> | |||||
<a name="fifth"></a> | |||||
<div class="ui container"> | |||||
<div class="ui very padded inverted segment radius15"> | |||||
<div class="ui stackable grid"> | |||||
<div class="six wide column"> | |||||
<img class="ui centered large image" src="/img/i-yunnao.svg"> | |||||
</div> | |||||
<div class="ten wide column am-pt-30"> | |||||
<h2 class="ui grey inverted header">{{.page_dev_yunlao}}</h2> | |||||
<p class="am-lh-18 ui text grey"> | |||||
{{.page_dev_yunlao_desc1}}<br> | |||||
{{.page_dev_yunlao_desc2}}<br> | |||||
{{.page_dev_yunlao_desc3}}<br> | |||||
{{.page_dev_yunlao_desc4}}<br> | |||||
</p> | |||||
{{if .IsSigned}} | |||||
<a class="ui blue basic button am-mt-20" href="{{AppSubUrl}}/dashboard">{{.page_use}}</a> | |||||
{{else}} | |||||
<a class="ui blue basic button am-mt-20" href="{{AppSubUrl}}/user/login">{{.page_use}}</a> | |||||
{{end}} | |||||
<a class="ui grey basic button am-mt-20" href="mailto:aiforge@openi.org.cn">{{.page_dev_yunlao_apply}}</a> | |||||
</div> | </div> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
<div class="am-mt-30"></div> | |||||
</div><!-- end railContent --> | |||||
</div> | |||||
<div class="am-mt-30"></div> | |||||
<script src="/self/js/jquery.min.js" type="text/javascript"></script> | |||||
<script src="/home/home.js?v={{MD5 AppVer}}" type="text/javascript"></script> | |||||
</div><!-- end explore --> | |||||
<script> | |||||
$('.menu .item') | |||||
.tab(); | |||||
$('.ui.sticky') | |||||
.sticky({ | |||||
context: '#railContent', | |||||
observeChanges: true, | |||||
}) | |||||
.sticky('refresh'); | |||||
</script> | |||||
{{template "base/footer" .}} | {{template "base/footer" .}} |
@@ -196,7 +196,7 @@ td, th { | |||||
<span> | <span> | ||||
<div style="float: right;"> | <div style="float: right;"> | ||||
{{$.CsrfTokenHtml}} | {{$.CsrfTokenHtml}} | ||||
{{if .CanModify}} | |||||
{{if and (.CanModify) (eq .Status "COMPLETED")}} | |||||
<a class="ti-action-menu-item" onclick="showcreate({{.}})">{{$.i18n.Tr "repo.modelarts.create_model"}}</a> | <a class="ti-action-menu-item" onclick="showcreate({{.}})">{{$.i18n.Tr "repo.modelarts.create_model"}}</a> | ||||
{{else}} | {{else}} | ||||
<a class="ti-action-menu-item disabled">{{$.i18n.Tr "repo.modelarts.create_model"}}</a> | <a class="ti-action-menu-item disabled">{{$.i18n.Tr "repo.modelarts.create_model"}}</a> | ||||
@@ -0,0 +1,12 @@ | |||||
root = true | |||||
[*] | |||||
charset = utf-8 | |||||
end_of_line = lf | |||||
insert_final_newline = true | |||||
trim_trailing_whitespace = true | |||||
[*.go] | |||||
indent_style = tab | |||||
indent_size = 4 | |||||
max_line_length = 80 |
@@ -0,0 +1 @@ | |||||
/.idea |
@@ -0,0 +1,9 @@ | |||||
language: go | |||||
go: | |||||
- 1.11.x | |||||
- 1.12.x | |||||
- master | |||||
script: | |||||
- env GO111MODULE=on go test |
@@ -0,0 +1,21 @@ | |||||
MIT License | |||||
Copyright (c) 2020 Elliot Chance | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in all | |||||
copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
SOFTWARE. |
@@ -0,0 +1,102 @@ | |||||
# 🔃 github.com/elliotchance/orderedmap [](https://godoc.org/github.com/elliotchance/orderedmap) [](https://travis-ci.org/elliotchance/orderedmap) | |||||
## Installation | |||||
```bash | |||||
go get -u github.com/elliotchance/orderedmap | |||||
``` | |||||
## Basic Usage | |||||
An `*OrderedMap` is a high performance ordered map that maintains amortized O(1) | |||||
for `Set`, `Get`, `Delete` and `Len`: | |||||
```go | |||||
m := orderedmap.NewOrderedMap() | |||||
m.Set("foo", "bar") | |||||
m.Set("qux", 1.23) | |||||
m.Set(123, true) | |||||
m.Delete("qux") | |||||
``` | |||||
Internally an `*OrderedMap` uses a combination of a map and linked list. | |||||
## Iterating | |||||
Be careful using `Keys()` as it will create a copy of all of the keys so it's | |||||
only suitable for a small number of items: | |||||
```go | |||||
for _, key := range m.Keys() { | |||||
value, _:= m.Get(key) | |||||
fmt.Println(key, value) | |||||
} | |||||
``` | |||||
For larger maps you should use `Front()` or `Back()` to iterate per element: | |||||
```go | |||||
// Iterate through all elements from oldest to newest: | |||||
for el := m.Front(); el != nil; el = el.Next() { | |||||
fmt.Println(el.Key, el.Value) | |||||
} | |||||
// You can also use Back and Prev to iterate in reverse: | |||||
for el := m.Back(); el != nil; el = el.Prev() { | |||||
fmt.Println(el.Key, el.Value) | |||||
} | |||||
``` | |||||
The iterator is safe to use bidirectionally, and will return `nil` once it goes | |||||
beyond the first or last item. | |||||
If the map is changing while the iteration is in-flight it may produce | |||||
unexpected behavior. | |||||
## Performance | |||||
CPU: Intel(R) Core(TM) i5-8250U CPU @ 1.60GHz | |||||
RAM: 8GB | |||||
System: Windows 10 | |||||
```shell | |||||
$go test -benchmem -run=^$ github.com/elliotchance/orderedmap -bench BenchmarkAll | |||||
``` | |||||
map[int]bool | |||||
| | map | orderedmap | | |||||
| ------- | ------------------- | ------------------- | | |||||
| set | 198 ns/op, 44 B/op | 722 ns/op, 211 B/op | | |||||
| get | 18 ns/op, 0 B/op | 37.3 ns/op, 0 B/op | | |||||
| delete | 888 ns/op, 211 B/op | 280 ns/op, 44 B/op | | |||||
| Iterate | 206 ns/op, 44 B/op | 693 ns/op, 259 B/op | | |||||
map[string]bool(PS : Use strconv.Itoa()) | |||||
| | map | orderedmap | | |||||
| ----------- | ------------------- | ----------------------- | | |||||
| set | 421 ns/op, 86 B/op | 1048 ns/op, 243 B/op | | |||||
| get | 81.1 ns/op, 2 B/op | 97.8 ns/op, 2 B/op | | |||||
| delete | 737 ns/op, 122 B/op | 1188 ns/op, 251 B/op | | |||||
| Iterate all | 14706 ns/op, 1 B/op | 52671 ns/op, 16391 B/op | | |||||
Big map[int]bool (10000000 keys) | |||||
| | map | orderedmap | | |||||
| ----------- | -------------------------------- | ------------------------------- | | |||||
| set all | 1.834559 s/op, 423.9470291 MB/op | 7.5564667 s/op, 1784.1483 MB/op | | |||||
| get all | 2.6367878 s/op, 423.9698 MB/op | 9.0232475 s/op, 1784.1086 MB/op | | |||||
| Iterate all | 1.9526784 s/op, 423.9042 MB/op | 8.2495265 s/op, 1936.7619 MB/op | | |||||
Big map[string]bool (10000000 keys) | |||||
| | map | orderedmap | | |||||
| ----------- | --------------------------------- | ----------------------------------- | | |||||
| set all | 4.8893923 s/op, 921.33435 MB/op | 10.4405527 s/op, 2089.0144 MB/op | | |||||
| get all | 7.122791 s/op, 997.3802643 MB/op | 13.2613692 s/op, 2165.09521 MB/op | | |||||
| Iterate all | 5.1688922 s/op, 921.4619293 MB/op | 12.6623711 s/op, 2241.5272064 MB/op | |
@@ -0,0 +1,33 @@ | |||||
package orderedmap | |||||
import "container/list" | |||||
type Element struct { | |||||
Key, Value interface{} | |||||
element *list.Element | |||||
} | |||||
func newElement(e *list.Element) *Element { | |||||
if e == nil { | |||||
return nil | |||||
} | |||||
element := e.Value.(*orderedMapElement) | |||||
return &Element{ | |||||
element: e, | |||||
Key: element.key, | |||||
Value: element.value, | |||||
} | |||||
} | |||||
// Next returns the next element, or nil if it finished. | |||||
func (e *Element) Next() *Element { | |||||
return newElement(e.element.Next()) | |||||
} | |||||
// Prev returns the previous element, or nil if it finished. | |||||
func (e *Element) Prev() *Element { | |||||
return newElement(e.element.Prev()) | |||||
} |
@@ -0,0 +1,5 @@ | |||||
module github.com/elliotchance/orderedmap | |||||
go 1.12 | |||||
require github.com/stretchr/testify v1.7.0 |
@@ -0,0 +1,15 @@ | |||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= | |||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | |||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | |||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | |||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | |||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= | |||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= | |||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= | |||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | |||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= | |||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | |||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= | |||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | |||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= | |||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
@@ -0,0 +1,150 @@ | |||||
package orderedmap | |||||
import "container/list" | |||||
type orderedMapElement struct { | |||||
key, value interface{} | |||||
} | |||||
type OrderedMap struct { | |||||
kv map[interface{}]*list.Element | |||||
ll *list.List | |||||
} | |||||
func NewOrderedMap() *OrderedMap { | |||||
return &OrderedMap{ | |||||
kv: make(map[interface{}]*list.Element), | |||||
ll: list.New(), | |||||
} | |||||
} | |||||
// Get returns the value for a key. If the key does not exist, the second return | |||||
// parameter will be false and the value will be nil. | |||||
func (m *OrderedMap) Get(key interface{}) (interface{}, bool) { | |||||
value, ok := m.kv[key] | |||||
if ok { | |||||
return value.Value.(*orderedMapElement).value, true | |||||
} | |||||
return nil, false | |||||
} | |||||
// Set will set (or replace) a value for a key. If the key was new, then true | |||||
// will be returned. The returned value will be false if the value was replaced | |||||
// (even if the value was the same). | |||||
func (m *OrderedMap) Set(key, value interface{}) bool { | |||||
_, didExist := m.kv[key] | |||||
if !didExist { | |||||
element := m.ll.PushBack(&orderedMapElement{key, value}) | |||||
m.kv[key] = element | |||||
} else { | |||||
m.kv[key].Value.(*orderedMapElement).value = value | |||||
} | |||||
return !didExist | |||||
} | |||||
// GetOrDefault returns the value for a key. If the key does not exist, returns | |||||
// the default value instead. | |||||
func (m *OrderedMap) GetOrDefault(key, defaultValue interface{}) interface{} { | |||||
if value, ok := m.kv[key]; ok { | |||||
return value.Value.(*orderedMapElement).value | |||||
} | |||||
return defaultValue | |||||
} | |||||
// GetElement returns the element for a key. If the key does not exist, the | |||||
// pointer will be nil. | |||||
func (m *OrderedMap) GetElement(key interface{}) *Element { | |||||
value, ok := m.kv[key] | |||||
if ok { | |||||
element := value.Value.(*orderedMapElement) | |||||
return &Element{ | |||||
element: value, | |||||
Key: element.key, | |||||
Value: element.value, | |||||
} | |||||
} | |||||
return nil | |||||
} | |||||
// Len returns the number of elements in the map. | |||||
func (m *OrderedMap) Len() int { | |||||
return len(m.kv) | |||||
} | |||||
// Keys returns all of the keys in the order they were inserted. If a key was | |||||
// replaced it will retain the same position. To ensure most recently set keys | |||||
// are always at the end you must always Delete before Set. | |||||
func (m *OrderedMap) Keys() (keys []interface{}) { | |||||
keys = make([]interface{}, m.Len()) | |||||
element := m.ll.Front() | |||||
for i := 0; element != nil; i++ { | |||||
keys[i] = element.Value.(*orderedMapElement).key | |||||
element = element.Next() | |||||
} | |||||
return keys | |||||
} | |||||
// Delete will remove a key from the map. It will return true if the key was | |||||
// removed (the key did exist). | |||||
func (m *OrderedMap) Delete(key interface{}) (didDelete bool) { | |||||
element, ok := m.kv[key] | |||||
if ok { | |||||
m.ll.Remove(element) | |||||
delete(m.kv, key) | |||||
} | |||||
return ok | |||||
} | |||||
// Front will return the element that is the first (oldest Set element). If | |||||
// there are no elements this will return nil. | |||||
func (m *OrderedMap) Front() *Element { | |||||
front := m.ll.Front() | |||||
if front == nil { | |||||
return nil | |||||
} | |||||
element := front.Value.(*orderedMapElement) | |||||
return &Element{ | |||||
element: front, | |||||
Key: element.key, | |||||
Value: element.value, | |||||
} | |||||
} | |||||
// Back will return the element that is the last (most recent Set element). If | |||||
// there are no elements this will return nil. | |||||
func (m *OrderedMap) Back() *Element { | |||||
back := m.ll.Back() | |||||
if back == nil { | |||||
return nil | |||||
} | |||||
element := back.Value.(*orderedMapElement) | |||||
return &Element{ | |||||
element: back, | |||||
Key: element.key, | |||||
Value: element.value, | |||||
} | |||||
} | |||||
// Copy returns a new OrderedMap with the same elements. | |||||
// Using Copy while there are concurrent writes may mangle the result. | |||||
func (m *OrderedMap) Copy() *OrderedMap { | |||||
m2 := NewOrderedMap() | |||||
for el := m.Front(); el != nil; el = el.Next() { | |||||
m2.Set(el.Key, el.Value) | |||||
} | |||||
return m2 | |||||
} |
@@ -1,6 +1,6 @@ | |||||
MIT License | MIT License | ||||
Copyright (c) 2012-2018 Mat Ryer and Tyler Bunnell | |||||
Copyright (c) 2012-2020 Mat Ryer, Tyler Bunnell and contributors. | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
of this software and associated documentation files (the "Software"), to deal | of this software and associated documentation files (the "Software"), to deal | ||||
@@ -0,0 +1,394 @@ | |||||
package assert | |||||
import ( | |||||
"fmt" | |||||
"reflect" | |||||
) | |||||
type CompareType int | |||||
const ( | |||||
compareLess CompareType = iota - 1 | |||||
compareEqual | |||||
compareGreater | |||||
) | |||||
var ( | |||||
intType = reflect.TypeOf(int(1)) | |||||
int8Type = reflect.TypeOf(int8(1)) | |||||
int16Type = reflect.TypeOf(int16(1)) | |||||
int32Type = reflect.TypeOf(int32(1)) | |||||
int64Type = reflect.TypeOf(int64(1)) | |||||
uintType = reflect.TypeOf(uint(1)) | |||||
uint8Type = reflect.TypeOf(uint8(1)) | |||||
uint16Type = reflect.TypeOf(uint16(1)) | |||||
uint32Type = reflect.TypeOf(uint32(1)) | |||||
uint64Type = reflect.TypeOf(uint64(1)) | |||||
float32Type = reflect.TypeOf(float32(1)) | |||||
float64Type = reflect.TypeOf(float64(1)) | |||||
stringType = reflect.TypeOf("") | |||||
) | |||||
func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { | |||||
obj1Value := reflect.ValueOf(obj1) | |||||
obj2Value := reflect.ValueOf(obj2) | |||||
// throughout this switch we try and avoid calling .Convert() if possible, | |||||
// as this has a pretty big performance impact | |||||
switch kind { | |||||
case reflect.Int: | |||||
{ | |||||
intobj1, ok := obj1.(int) | |||||
if !ok { | |||||
intobj1 = obj1Value.Convert(intType).Interface().(int) | |||||
} | |||||
intobj2, ok := obj2.(int) | |||||
if !ok { | |||||
intobj2 = obj2Value.Convert(intType).Interface().(int) | |||||
} | |||||
if intobj1 > intobj2 { | |||||
return compareGreater, true | |||||
} | |||||
if intobj1 == intobj2 { | |||||
return compareEqual, true | |||||
} | |||||
if intobj1 < intobj2 { | |||||
return compareLess, true | |||||
} | |||||
} | |||||
case reflect.Int8: | |||||
{ | |||||
int8obj1, ok := obj1.(int8) | |||||
if !ok { | |||||
int8obj1 = obj1Value.Convert(int8Type).Interface().(int8) | |||||
} | |||||
int8obj2, ok := obj2.(int8) | |||||
if !ok { | |||||
int8obj2 = obj2Value.Convert(int8Type).Interface().(int8) | |||||
} | |||||
if int8obj1 > int8obj2 { | |||||
return compareGreater, true | |||||
} | |||||
if int8obj1 == int8obj2 { | |||||
return compareEqual, true | |||||
} | |||||
if int8obj1 < int8obj2 { | |||||
return compareLess, true | |||||
} | |||||
} | |||||
case reflect.Int16: | |||||
{ | |||||
int16obj1, ok := obj1.(int16) | |||||
if !ok { | |||||
int16obj1 = obj1Value.Convert(int16Type).Interface().(int16) | |||||
} | |||||
int16obj2, ok := obj2.(int16) | |||||
if !ok { | |||||
int16obj2 = obj2Value.Convert(int16Type).Interface().(int16) | |||||
} | |||||
if int16obj1 > int16obj2 { | |||||
return compareGreater, true | |||||
} | |||||
if int16obj1 == int16obj2 { | |||||
return compareEqual, true | |||||
} | |||||
if int16obj1 < int16obj2 { | |||||
return compareLess, true | |||||
} | |||||
} | |||||
case reflect.Int32: | |||||
{ | |||||
int32obj1, ok := obj1.(int32) | |||||
if !ok { | |||||
int32obj1 = obj1Value.Convert(int32Type).Interface().(int32) | |||||
} | |||||
int32obj2, ok := obj2.(int32) | |||||
if !ok { | |||||
int32obj2 = obj2Value.Convert(int32Type).Interface().(int32) | |||||
} | |||||
if int32obj1 > int32obj2 { | |||||
return compareGreater, true | |||||
} | |||||
if int32obj1 == int32obj2 { | |||||
return compareEqual, true | |||||
} | |||||
if int32obj1 < int32obj2 { | |||||
return compareLess, true | |||||
} | |||||
} | |||||
case reflect.Int64: | |||||
{ | |||||
int64obj1, ok := obj1.(int64) | |||||
if !ok { | |||||
int64obj1 = obj1Value.Convert(int64Type).Interface().(int64) | |||||
} | |||||
int64obj2, ok := obj2.(int64) | |||||
if !ok { | |||||
int64obj2 = obj2Value.Convert(int64Type).Interface().(int64) | |||||
} | |||||
if int64obj1 > int64obj2 { | |||||
return compareGreater, true | |||||
} | |||||
if int64obj1 == int64obj2 { | |||||
return compareEqual, true | |||||
} | |||||
if int64obj1 < int64obj2 { | |||||
return compareLess, true | |||||
} | |||||
} | |||||
case reflect.Uint: | |||||
{ | |||||
uintobj1, ok := obj1.(uint) | |||||
if !ok { | |||||
uintobj1 = obj1Value.Convert(uintType).Interface().(uint) | |||||
} | |||||
uintobj2, ok := obj2.(uint) | |||||
if !ok { | |||||
uintobj2 = obj2Value.Convert(uintType).Interface().(uint) | |||||
} | |||||
if uintobj1 > uintobj2 { | |||||
return compareGreater, true | |||||
} | |||||
if uintobj1 == uintobj2 { | |||||
return compareEqual, true | |||||
} | |||||
if uintobj1 < uintobj2 { | |||||
return compareLess, true | |||||
} | |||||
} | |||||
case reflect.Uint8: | |||||
{ | |||||
uint8obj1, ok := obj1.(uint8) | |||||
if !ok { | |||||
uint8obj1 = obj1Value.Convert(uint8Type).Interface().(uint8) | |||||
} | |||||
uint8obj2, ok := obj2.(uint8) | |||||
if !ok { | |||||
uint8obj2 = obj2Value.Convert(uint8Type).Interface().(uint8) | |||||
} | |||||
if uint8obj1 > uint8obj2 { | |||||
return compareGreater, true | |||||
} | |||||
if uint8obj1 == uint8obj2 { | |||||
return compareEqual, true | |||||
} | |||||
if uint8obj1 < uint8obj2 { | |||||
return compareLess, true | |||||
} | |||||
} | |||||
case reflect.Uint16: | |||||
{ | |||||
uint16obj1, ok := obj1.(uint16) | |||||
if !ok { | |||||
uint16obj1 = obj1Value.Convert(uint16Type).Interface().(uint16) | |||||
} | |||||
uint16obj2, ok := obj2.(uint16) | |||||
if !ok { | |||||
uint16obj2 = obj2Value.Convert(uint16Type).Interface().(uint16) | |||||
} | |||||
if uint16obj1 > uint16obj2 { | |||||
return compareGreater, true | |||||
} | |||||
if uint16obj1 == uint16obj2 { | |||||
return compareEqual, true | |||||
} | |||||
if uint16obj1 < uint16obj2 { | |||||
return compareLess, true | |||||
} | |||||
} | |||||
case reflect.Uint32: | |||||
{ | |||||
uint32obj1, ok := obj1.(uint32) | |||||
if !ok { | |||||
uint32obj1 = obj1Value.Convert(uint32Type).Interface().(uint32) | |||||
} | |||||
uint32obj2, ok := obj2.(uint32) | |||||
if !ok { | |||||
uint32obj2 = obj2Value.Convert(uint32Type).Interface().(uint32) | |||||
} | |||||
if uint32obj1 > uint32obj2 { | |||||
return compareGreater, true | |||||
} | |||||
if uint32obj1 == uint32obj2 { | |||||
return compareEqual, true | |||||
} | |||||
if uint32obj1 < uint32obj2 { | |||||
return compareLess, true | |||||
} | |||||
} | |||||
case reflect.Uint64: | |||||
{ | |||||
uint64obj1, ok := obj1.(uint64) | |||||
if !ok { | |||||
uint64obj1 = obj1Value.Convert(uint64Type).Interface().(uint64) | |||||
} | |||||
uint64obj2, ok := obj2.(uint64) | |||||
if !ok { | |||||
uint64obj2 = obj2Value.Convert(uint64Type).Interface().(uint64) | |||||
} | |||||
if uint64obj1 > uint64obj2 { | |||||
return compareGreater, true | |||||
} | |||||
if uint64obj1 == uint64obj2 { | |||||
return compareEqual, true | |||||
} | |||||
if uint64obj1 < uint64obj2 { | |||||
return compareLess, true | |||||
} | |||||
} | |||||
case reflect.Float32: | |||||
{ | |||||
float32obj1, ok := obj1.(float32) | |||||
if !ok { | |||||
float32obj1 = obj1Value.Convert(float32Type).Interface().(float32) | |||||
} | |||||
float32obj2, ok := obj2.(float32) | |||||
if !ok { | |||||
float32obj2 = obj2Value.Convert(float32Type).Interface().(float32) | |||||
} | |||||
if float32obj1 > float32obj2 { | |||||
return compareGreater, true | |||||
} | |||||
if float32obj1 == float32obj2 { | |||||
return compareEqual, true | |||||
} | |||||
if float32obj1 < float32obj2 { | |||||
return compareLess, true | |||||
} | |||||
} | |||||
case reflect.Float64: | |||||
{ | |||||
float64obj1, ok := obj1.(float64) | |||||
if !ok { | |||||
float64obj1 = obj1Value.Convert(float64Type).Interface().(float64) | |||||
} | |||||
float64obj2, ok := obj2.(float64) | |||||
if !ok { | |||||
float64obj2 = obj2Value.Convert(float64Type).Interface().(float64) | |||||
} | |||||
if float64obj1 > float64obj2 { | |||||
return compareGreater, true | |||||
} | |||||
if float64obj1 == float64obj2 { | |||||
return compareEqual, true | |||||
} | |||||
if float64obj1 < float64obj2 { | |||||
return compareLess, true | |||||
} | |||||
} | |||||
case reflect.String: | |||||
{ | |||||
stringobj1, ok := obj1.(string) | |||||
if !ok { | |||||
stringobj1 = obj1Value.Convert(stringType).Interface().(string) | |||||
} | |||||
stringobj2, ok := obj2.(string) | |||||
if !ok { | |||||
stringobj2 = obj2Value.Convert(stringType).Interface().(string) | |||||
} | |||||
if stringobj1 > stringobj2 { | |||||
return compareGreater, true | |||||
} | |||||
if stringobj1 == stringobj2 { | |||||
return compareEqual, true | |||||
} | |||||
if stringobj1 < stringobj2 { | |||||
return compareLess, true | |||||
} | |||||
} | |||||
} | |||||
return compareEqual, false | |||||
} | |||||
// Greater asserts that the first element is greater than the second | |||||
// | |||||
// assert.Greater(t, 2, 1) | |||||
// assert.Greater(t, float64(2), float64(1)) | |||||
// assert.Greater(t, "b", "a") | |||||
func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { | |||||
return compareTwoValues(t, e1, e2, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs) | |||||
} | |||||
// GreaterOrEqual asserts that the first element is greater than or equal to the second | |||||
// | |||||
// assert.GreaterOrEqual(t, 2, 1) | |||||
// assert.GreaterOrEqual(t, 2, 2) | |||||
// assert.GreaterOrEqual(t, "b", "a") | |||||
// assert.GreaterOrEqual(t, "b", "b") | |||||
func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { | |||||
return compareTwoValues(t, e1, e2, []CompareType{compareGreater, compareEqual}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs) | |||||
} | |||||
// Less asserts that the first element is less than the second | |||||
// | |||||
// assert.Less(t, 1, 2) | |||||
// assert.Less(t, float64(1), float64(2)) | |||||
// assert.Less(t, "a", "b") | |||||
func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { | |||||
return compareTwoValues(t, e1, e2, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs) | |||||
} | |||||
// LessOrEqual asserts that the first element is less than or equal to the second | |||||
// | |||||
// assert.LessOrEqual(t, 1, 2) | |||||
// assert.LessOrEqual(t, 2, 2) | |||||
// assert.LessOrEqual(t, "a", "b") | |||||
// assert.LessOrEqual(t, "b", "b") | |||||
func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { | |||||
return compareTwoValues(t, e1, e2, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs) | |||||
} | |||||
// Positive asserts that the specified element is positive | |||||
// | |||||
// assert.Positive(t, 1) | |||||
// assert.Positive(t, 1.23) | |||||
func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { | |||||
zero := reflect.Zero(reflect.TypeOf(e)) | |||||
return compareTwoValues(t, e, zero.Interface(), []CompareType{compareGreater}, "\"%v\" is not positive", msgAndArgs) | |||||
} | |||||
// Negative asserts that the specified element is negative | |||||
// | |||||
// assert.Negative(t, -1) | |||||
// assert.Negative(t, -1.23) | |||||
func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { | |||||
zero := reflect.Zero(reflect.TypeOf(e)) | |||||
return compareTwoValues(t, e, zero.Interface(), []CompareType{compareLess}, "\"%v\" is not negative", msgAndArgs) | |||||
} | |||||
func compareTwoValues(t TestingT, e1 interface{}, e2 interface{}, allowedComparesResults []CompareType, failMessage string, msgAndArgs ...interface{}) bool { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
e1Kind := reflect.ValueOf(e1).Kind() | |||||
e2Kind := reflect.ValueOf(e2).Kind() | |||||
if e1Kind != e2Kind { | |||||
return Fail(t, "Elements should be the same type", msgAndArgs...) | |||||
} | |||||
compareResult, isComparable := compare(e1, e2, e1Kind) | |||||
if !isComparable { | |||||
return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...) | |||||
} | |||||
if !containsValue(allowedComparesResults, compareResult) { | |||||
return Fail(t, fmt.Sprintf(failMessage, e1, e2), msgAndArgs...) | |||||
} | |||||
return true | |||||
} | |||||
func containsValue(values []CompareType, value CompareType) bool { | |||||
for _, v := range values { | |||||
if v == value { | |||||
return true | |||||
} | |||||
} | |||||
return false | |||||
} |
@@ -32,7 +32,8 @@ func Containsf(t TestingT, s interface{}, contains interface{}, msg string, args | |||||
return Contains(t, s, contains, append([]interface{}{msg}, args...)...) | return Contains(t, s, contains, append([]interface{}{msg}, args...)...) | ||||
} | } | ||||
// DirExistsf checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. | |||||
// DirExistsf checks whether a directory exists in the given path. It also fails | |||||
// if the path is a file rather a directory or there is an error checking whether it exists. | |||||
func DirExistsf(t TestingT, path string, msg string, args ...interface{}) bool { | func DirExistsf(t TestingT, path string, msg string, args ...interface{}) bool { | ||||
if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
@@ -92,7 +93,7 @@ func EqualErrorf(t TestingT, theError error, errString string, msg string, args | |||||
// EqualValuesf asserts that two objects are equal or convertable to the same types | // EqualValuesf asserts that two objects are equal or convertable to the same types | ||||
// and equal. | // and equal. | ||||
// | // | ||||
// assert.EqualValuesf(t, uint32(123, "error message %s", "formatted"), int32(123)) | |||||
// assert.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted") | |||||
func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { | func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { | ||||
if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
@@ -113,6 +114,24 @@ func Errorf(t TestingT, err error, msg string, args ...interface{}) bool { | |||||
return Error(t, err, append([]interface{}{msg}, args...)...) | return Error(t, err, append([]interface{}{msg}, args...)...) | ||||
} | } | ||||
// ErrorAsf asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. | |||||
// This is a wrapper for errors.As. | |||||
func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...interface{}) bool { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return ErrorAs(t, err, target, append([]interface{}{msg}, args...)...) | |||||
} | |||||
// ErrorIsf asserts that at least one of the errors in err's chain matches target. | |||||
// This is a wrapper for errors.Is. | |||||
func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) bool { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return ErrorIs(t, err, target, append([]interface{}{msg}, args...)...) | |||||
} | |||||
// Eventuallyf asserts that given condition will be met in waitFor time, | // Eventuallyf asserts that given condition will be met in waitFor time, | ||||
// periodically checking target function each tick. | // periodically checking target function each tick. | ||||
// | // | ||||
@@ -126,7 +145,7 @@ func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick | |||||
// Exactlyf asserts that two objects are equal in value and type. | // Exactlyf asserts that two objects are equal in value and type. | ||||
// | // | ||||
// assert.Exactlyf(t, int32(123, "error message %s", "formatted"), int64(123)) | |||||
// assert.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted") | |||||
func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { | func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { | ||||
if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
@@ -160,7 +179,8 @@ func Falsef(t TestingT, value bool, msg string, args ...interface{}) bool { | |||||
return False(t, value, append([]interface{}{msg}, args...)...) | return False(t, value, append([]interface{}{msg}, args...)...) | ||||
} | } | ||||
// FileExistsf checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. | |||||
// FileExistsf checks whether a file exists in the given path. It also fails if | |||||
// the path points to a directory or there is an error when trying to check the file. | |||||
func FileExistsf(t TestingT, path string, msg string, args ...interface{}) bool { | func FileExistsf(t TestingT, path string, msg string, args ...interface{}) bool { | ||||
if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
@@ -171,7 +191,7 @@ func FileExistsf(t TestingT, path string, msg string, args ...interface{}) bool | |||||
// Greaterf asserts that the first element is greater than the second | // Greaterf asserts that the first element is greater than the second | ||||
// | // | ||||
// assert.Greaterf(t, 2, 1, "error message %s", "formatted") | // assert.Greaterf(t, 2, 1, "error message %s", "formatted") | ||||
// assert.Greaterf(t, float64(2, "error message %s", "formatted"), float64(1)) | |||||
// assert.Greaterf(t, float64(2), float64(1), "error message %s", "formatted") | |||||
// assert.Greaterf(t, "b", "a", "error message %s", "formatted") | // assert.Greaterf(t, "b", "a", "error message %s", "formatted") | ||||
func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { | func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { | ||||
if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
@@ -223,7 +243,7 @@ func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, u | |||||
// | // | ||||
// assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} | // assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} | ||||
// | // | ||||
// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false). | |||||
// Returns whether the assertion was successful (true) or not (false). | |||||
func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { | func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { | ||||
if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
@@ -235,7 +255,7 @@ func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, | |||||
// | // | ||||
// assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} | // assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} | ||||
// | // | ||||
// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false). | |||||
// Returns whether the assertion was successful (true) or not (false). | |||||
func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { | func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { | ||||
if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
@@ -243,6 +263,18 @@ func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url stri | |||||
return HTTPRedirect(t, handler, method, url, values, append([]interface{}{msg}, args...)...) | return HTTPRedirect(t, handler, method, url, values, append([]interface{}{msg}, args...)...) | ||||
} | } | ||||
// HTTPStatusCodef asserts that a specified handler returns a specified status code. | |||||
// | |||||
// assert.HTTPStatusCodef(t, myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") | |||||
// | |||||
// Returns whether the assertion was successful (true) or not (false). | |||||
func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) bool { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return HTTPStatusCode(t, handler, method, url, values, statuscode, append([]interface{}{msg}, args...)...) | |||||
} | |||||
// HTTPSuccessf asserts that a specified handler returns a success status code. | // HTTPSuccessf asserts that a specified handler returns a success status code. | ||||
// | // | ||||
// assert.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") | // assert.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") | ||||
@@ -257,7 +289,7 @@ func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url strin | |||||
// Implementsf asserts that an object is implemented by the specified interface. | // Implementsf asserts that an object is implemented by the specified interface. | ||||
// | // | ||||
// assert.Implementsf(t, (*MyInterface, "error message %s", "formatted")(nil), new(MyObject)) | |||||
// assert.Implementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted") | |||||
func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool { | func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool { | ||||
if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
@@ -267,7 +299,7 @@ func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, ms | |||||
// InDeltaf asserts that the two numerals are within delta of each other. | // InDeltaf asserts that the two numerals are within delta of each other. | ||||
// | // | ||||
// assert.InDeltaf(t, math.Pi, (22 / 7.0, "error message %s", "formatted"), 0.01) | |||||
// assert.InDeltaf(t, math.Pi, 22/7.0, 0.01, "error message %s", "formatted") | |||||
func InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { | func InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { | ||||
if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
@@ -307,6 +339,54 @@ func InEpsilonSlicef(t TestingT, expected interface{}, actual interface{}, epsil | |||||
return InEpsilonSlice(t, expected, actual, epsilon, append([]interface{}{msg}, args...)...) | return InEpsilonSlice(t, expected, actual, epsilon, append([]interface{}{msg}, args...)...) | ||||
} | } | ||||
// IsDecreasingf asserts that the collection is decreasing | |||||
// | |||||
// assert.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted") | |||||
// assert.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted") | |||||
// assert.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted") | |||||
func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return IsDecreasing(t, object, append([]interface{}{msg}, args...)...) | |||||
} | |||||
// IsIncreasingf asserts that the collection is increasing | |||||
// | |||||
// assert.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted") | |||||
// assert.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted") | |||||
// assert.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted") | |||||
func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return IsIncreasing(t, object, append([]interface{}{msg}, args...)...) | |||||
} | |||||
// IsNonDecreasingf asserts that the collection is not decreasing | |||||
// | |||||
// assert.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted") | |||||
// assert.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted") | |||||
// assert.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted") | |||||
func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return IsNonDecreasing(t, object, append([]interface{}{msg}, args...)...) | |||||
} | |||||
// IsNonIncreasingf asserts that the collection is not increasing | |||||
// | |||||
// assert.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted") | |||||
// assert.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted") | |||||
// assert.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted") | |||||
func IsNonIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return IsNonIncreasing(t, object, append([]interface{}{msg}, args...)...) | |||||
} | |||||
// IsTypef asserts that the specified objects are of the same type. | // IsTypef asserts that the specified objects are of the same type. | ||||
func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg string, args ...interface{}) bool { | func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg string, args ...interface{}) bool { | ||||
if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
@@ -325,14 +405,6 @@ func JSONEqf(t TestingT, expected string, actual string, msg string, args ...int | |||||
return JSONEq(t, expected, actual, append([]interface{}{msg}, args...)...) | return JSONEq(t, expected, actual, append([]interface{}{msg}, args...)...) | ||||
} | } | ||||
// YAMLEqf asserts that two YAML strings are equivalent. | |||||
func YAMLEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return YAMLEq(t, expected, actual, append([]interface{}{msg}, args...)...) | |||||
} | |||||
// Lenf asserts that the specified object has specific length. | // Lenf asserts that the specified object has specific length. | ||||
// Lenf also fails if the object has a type that len() not accept. | // Lenf also fails if the object has a type that len() not accept. | ||||
// | // | ||||
@@ -347,7 +419,7 @@ func Lenf(t TestingT, object interface{}, length int, msg string, args ...interf | |||||
// Lessf asserts that the first element is less than the second | // Lessf asserts that the first element is less than the second | ||||
// | // | ||||
// assert.Lessf(t, 1, 2, "error message %s", "formatted") | // assert.Lessf(t, 1, 2, "error message %s", "formatted") | ||||
// assert.Lessf(t, float64(1, "error message %s", "formatted"), float64(2)) | |||||
// assert.Lessf(t, float64(1), float64(2), "error message %s", "formatted") | |||||
// assert.Lessf(t, "a", "b", "error message %s", "formatted") | // assert.Lessf(t, "a", "b", "error message %s", "formatted") | ||||
func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { | func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { | ||||
if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
@@ -369,6 +441,28 @@ func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args . | |||||
return LessOrEqual(t, e1, e2, append([]interface{}{msg}, args...)...) | return LessOrEqual(t, e1, e2, append([]interface{}{msg}, args...)...) | ||||
} | } | ||||
// Negativef asserts that the specified element is negative | |||||
// | |||||
// assert.Negativef(t, -1, "error message %s", "formatted") | |||||
// assert.Negativef(t, -1.23, "error message %s", "formatted") | |||||
func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) bool { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return Negative(t, e, append([]interface{}{msg}, args...)...) | |||||
} | |||||
// Neverf asserts that the given condition doesn't satisfy in waitFor time, | |||||
// periodically checking the target function each tick. | |||||
// | |||||
// assert.Neverf(t, func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") | |||||
func Neverf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return Never(t, condition, waitFor, tick, append([]interface{}{msg}, args...)...) | |||||
} | |||||
// Nilf asserts that the specified object is nil. | // Nilf asserts that the specified object is nil. | ||||
// | // | ||||
// assert.Nilf(t, err, "error message %s", "formatted") | // assert.Nilf(t, err, "error message %s", "formatted") | ||||
@@ -379,6 +473,15 @@ func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) bool | |||||
return Nil(t, object, append([]interface{}{msg}, args...)...) | return Nil(t, object, append([]interface{}{msg}, args...)...) | ||||
} | } | ||||
// NoDirExistsf checks whether a directory does not exist in the given path. | |||||
// It fails if the path points to an existing _directory_ only. | |||||
func NoDirExistsf(t TestingT, path string, msg string, args ...interface{}) bool { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return NoDirExists(t, path, append([]interface{}{msg}, args...)...) | |||||
} | |||||
// NoErrorf asserts that a function returned no error (i.e. `nil`). | // NoErrorf asserts that a function returned no error (i.e. `nil`). | ||||
// | // | ||||
// actualObj, err := SomeFunction() | // actualObj, err := SomeFunction() | ||||
@@ -392,6 +495,15 @@ func NoErrorf(t TestingT, err error, msg string, args ...interface{}) bool { | |||||
return NoError(t, err, append([]interface{}{msg}, args...)...) | return NoError(t, err, append([]interface{}{msg}, args...)...) | ||||
} | } | ||||
// NoFileExistsf checks whether a file does not exist in a given path. It fails | |||||
// if the path points to an existing _file_ only. | |||||
func NoFileExistsf(t TestingT, path string, msg string, args ...interface{}) bool { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return NoFileExists(t, path, append([]interface{}{msg}, args...)...) | |||||
} | |||||
// NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the | // NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the | ||||
// specified substring or element. | // specified substring or element. | ||||
// | // | ||||
@@ -431,6 +543,25 @@ func NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string, | |||||
return NotEqual(t, expected, actual, append([]interface{}{msg}, args...)...) | return NotEqual(t, expected, actual, append([]interface{}{msg}, args...)...) | ||||
} | } | ||||
// NotEqualValuesf asserts that two objects are not equal even when converted to the same type | |||||
// | |||||
// assert.NotEqualValuesf(t, obj1, obj2, "error message %s", "formatted") | |||||
func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return NotEqualValues(t, expected, actual, append([]interface{}{msg}, args...)...) | |||||
} | |||||
// NotErrorIsf asserts that at none of the errors in err's chain matches target. | |||||
// This is a wrapper for errors.Is. | |||||
func NotErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) bool { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return NotErrorIs(t, err, target, append([]interface{}{msg}, args...)...) | |||||
} | |||||
// NotNilf asserts that the specified object is not nil. | // NotNilf asserts that the specified object is not nil. | ||||
// | // | ||||
// assert.NotNilf(t, err, "error message %s", "formatted") | // assert.NotNilf(t, err, "error message %s", "formatted") | ||||
@@ -453,7 +584,7 @@ func NotPanicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bo | |||||
// NotRegexpf asserts that a specified regexp does not match a string. | // NotRegexpf asserts that a specified regexp does not match a string. | ||||
// | // | ||||
// assert.NotRegexpf(t, regexp.MustCompile("starts", "error message %s", "formatted"), "it's starting") | |||||
// assert.NotRegexpf(t, regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") | |||||
// assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted") | // assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted") | ||||
func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool { | func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool { | ||||
if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
@@ -462,6 +593,19 @@ func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args .. | |||||
return NotRegexp(t, rx, str, append([]interface{}{msg}, args...)...) | return NotRegexp(t, rx, str, append([]interface{}{msg}, args...)...) | ||||
} | } | ||||
// NotSamef asserts that two pointers do not reference the same object. | |||||
// | |||||
// assert.NotSamef(t, ptr1, ptr2, "error message %s", "formatted") | |||||
// | |||||
// Both arguments must be pointer variables. Pointer variable sameness is | |||||
// determined based on the equality of both type and value. | |||||
func NotSamef(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return NotSame(t, expected, actual, append([]interface{}{msg}, args...)...) | |||||
} | |||||
// NotSubsetf asserts that the specified list(array, slice...) contains not all | // NotSubsetf asserts that the specified list(array, slice...) contains not all | ||||
// elements given in the specified subset(array, slice...). | // elements given in the specified subset(array, slice...). | ||||
// | // | ||||
@@ -491,6 +635,18 @@ func Panicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool | |||||
return Panics(t, f, append([]interface{}{msg}, args...)...) | return Panics(t, f, append([]interface{}{msg}, args...)...) | ||||
} | } | ||||
// PanicsWithErrorf asserts that the code inside the specified PanicTestFunc | |||||
// panics, and that the recovered panic value is an error that satisfies the | |||||
// EqualError comparison. | |||||
// | |||||
// assert.PanicsWithErrorf(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") | |||||
func PanicsWithErrorf(t TestingT, errString string, f PanicTestFunc, msg string, args ...interface{}) bool { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return PanicsWithError(t, errString, f, append([]interface{}{msg}, args...)...) | |||||
} | |||||
// PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that | // PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that | ||||
// the recovered panic value equals the expected panic value. | // the recovered panic value equals the expected panic value. | ||||
// | // | ||||
@@ -502,9 +658,20 @@ func PanicsWithValuef(t TestingT, expected interface{}, f PanicTestFunc, msg str | |||||
return PanicsWithValue(t, expected, f, append([]interface{}{msg}, args...)...) | return PanicsWithValue(t, expected, f, append([]interface{}{msg}, args...)...) | ||||
} | } | ||||
// Positivef asserts that the specified element is positive | |||||
// | |||||
// assert.Positivef(t, 1, "error message %s", "formatted") | |||||
// assert.Positivef(t, 1.23, "error message %s", "formatted") | |||||
func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) bool { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return Positive(t, e, append([]interface{}{msg}, args...)...) | |||||
} | |||||
// Regexpf asserts that a specified regexp matches a string. | // Regexpf asserts that a specified regexp matches a string. | ||||
// | // | ||||
// assert.Regexpf(t, regexp.MustCompile("start", "error message %s", "formatted"), "it's starting") | |||||
// assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") | |||||
// assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted") | // assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted") | ||||
func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool { | func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool { | ||||
if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
@@ -557,6 +724,14 @@ func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta tim | |||||
return WithinDuration(t, expected, actual, delta, append([]interface{}{msg}, args...)...) | return WithinDuration(t, expected, actual, delta, append([]interface{}{msg}, args...)...) | ||||
} | } | ||||
// YAMLEqf asserts that two YAML strings are equivalent. | |||||
func YAMLEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return YAMLEq(t, expected, actual, append([]interface{}{msg}, args...)...) | |||||
} | |||||
// Zerof asserts that i is the zero value for its type. | // Zerof asserts that i is the zero value for its type. | ||||
func Zerof(t TestingT, i interface{}, msg string, args ...interface{}) bool { | func Zerof(t TestingT, i interface{}, msg string, args ...interface{}) bool { | ||||
if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
@@ -53,7 +53,8 @@ func (a *Assertions) Containsf(s interface{}, contains interface{}, msg string, | |||||
return Containsf(a.t, s, contains, msg, args...) | return Containsf(a.t, s, contains, msg, args...) | ||||
} | } | ||||
// DirExists checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. | |||||
// DirExists checks whether a directory exists in the given path. It also fails | |||||
// if the path is a file rather a directory or there is an error checking whether it exists. | |||||
func (a *Assertions) DirExists(path string, msgAndArgs ...interface{}) bool { | func (a *Assertions) DirExists(path string, msgAndArgs ...interface{}) bool { | ||||
if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
@@ -61,7 +62,8 @@ func (a *Assertions) DirExists(path string, msgAndArgs ...interface{}) bool { | |||||
return DirExists(a.t, path, msgAndArgs...) | return DirExists(a.t, path, msgAndArgs...) | ||||
} | } | ||||
// DirExistsf checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. | |||||
// DirExistsf checks whether a directory exists in the given path. It also fails | |||||
// if the path is a file rather a directory or there is an error checking whether it exists. | |||||
func (a *Assertions) DirExistsf(path string, msg string, args ...interface{}) bool { | func (a *Assertions) DirExistsf(path string, msg string, args ...interface{}) bool { | ||||
if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
@@ -167,7 +169,7 @@ func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAn | |||||
// EqualValuesf asserts that two objects are equal or convertable to the same types | // EqualValuesf asserts that two objects are equal or convertable to the same types | ||||
// and equal. | // and equal. | ||||
// | // | ||||
// a.EqualValuesf(uint32(123, "error message %s", "formatted"), int32(123)) | |||||
// a.EqualValuesf(uint32(123), int32(123), "error message %s", "formatted") | |||||
func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { | func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { | ||||
if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
@@ -202,6 +204,42 @@ func (a *Assertions) Error(err error, msgAndArgs ...interface{}) bool { | |||||
return Error(a.t, err, msgAndArgs...) | return Error(a.t, err, msgAndArgs...) | ||||
} | } | ||||
// ErrorAs asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. | |||||
// This is a wrapper for errors.As. | |||||
func (a *Assertions) ErrorAs(err error, target interface{}, msgAndArgs ...interface{}) bool { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return ErrorAs(a.t, err, target, msgAndArgs...) | |||||
} | |||||
// ErrorAsf asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. | |||||
// This is a wrapper for errors.As. | |||||
func (a *Assertions) ErrorAsf(err error, target interface{}, msg string, args ...interface{}) bool { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return ErrorAsf(a.t, err, target, msg, args...) | |||||
} | |||||
// ErrorIs asserts that at least one of the errors in err's chain matches target. | |||||
// This is a wrapper for errors.Is. | |||||
func (a *Assertions) ErrorIs(err error, target error, msgAndArgs ...interface{}) bool { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return ErrorIs(a.t, err, target, msgAndArgs...) | |||||
} | |||||
// ErrorIsf asserts that at least one of the errors in err's chain matches target. | |||||
// This is a wrapper for errors.Is. | |||||
func (a *Assertions) ErrorIsf(err error, target error, msg string, args ...interface{}) bool { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return ErrorIsf(a.t, err, target, msg, args...) | |||||
} | |||||
// Errorf asserts that a function returned an error (i.e. not `nil`). | // Errorf asserts that a function returned an error (i.e. not `nil`). | ||||
// | // | ||||
// actualObj, err := SomeFunction() | // actualObj, err := SomeFunction() | ||||
@@ -249,7 +287,7 @@ func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArg | |||||
// Exactlyf asserts that two objects are equal in value and type. | // Exactlyf asserts that two objects are equal in value and type. | ||||
// | // | ||||
// a.Exactlyf(int32(123, "error message %s", "formatted"), int64(123)) | |||||
// a.Exactlyf(int32(123), int64(123), "error message %s", "formatted") | |||||
func (a *Assertions) Exactlyf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { | func (a *Assertions) Exactlyf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { | ||||
if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
@@ -309,7 +347,8 @@ func (a *Assertions) Falsef(value bool, msg string, args ...interface{}) bool { | |||||
return Falsef(a.t, value, msg, args...) | return Falsef(a.t, value, msg, args...) | ||||
} | } | ||||
// FileExists checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. | |||||
// FileExists checks whether a file exists in the given path. It also fails if | |||||
// the path points to a directory or there is an error when trying to check the file. | |||||
func (a *Assertions) FileExists(path string, msgAndArgs ...interface{}) bool { | func (a *Assertions) FileExists(path string, msgAndArgs ...interface{}) bool { | ||||
if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
@@ -317,7 +356,8 @@ func (a *Assertions) FileExists(path string, msgAndArgs ...interface{}) bool { | |||||
return FileExists(a.t, path, msgAndArgs...) | return FileExists(a.t, path, msgAndArgs...) | ||||
} | } | ||||
// FileExistsf checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. | |||||
// FileExistsf checks whether a file exists in the given path. It also fails if | |||||
// the path points to a directory or there is an error when trying to check the file. | |||||
func (a *Assertions) FileExistsf(path string, msg string, args ...interface{}) bool { | func (a *Assertions) FileExistsf(path string, msg string, args ...interface{}) bool { | ||||
if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
@@ -366,7 +406,7 @@ func (a *Assertions) GreaterOrEqualf(e1 interface{}, e2 interface{}, msg string, | |||||
// Greaterf asserts that the first element is greater than the second | // Greaterf asserts that the first element is greater than the second | ||||
// | // | ||||
// a.Greaterf(2, 1, "error message %s", "formatted") | // a.Greaterf(2, 1, "error message %s", "formatted") | ||||
// a.Greaterf(float64(2, "error message %s", "formatted"), float64(1)) | |||||
// a.Greaterf(float64(2), float64(1), "error message %s", "formatted") | |||||
// a.Greaterf("b", "a", "error message %s", "formatted") | // a.Greaterf("b", "a", "error message %s", "formatted") | ||||
func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { | func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { | ||||
if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
@@ -443,7 +483,7 @@ func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url stri | |||||
// | // | ||||
// a.HTTPErrorf(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} | // a.HTTPErrorf(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} | ||||
// | // | ||||
// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false). | |||||
// Returns whether the assertion was successful (true) or not (false). | |||||
func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { | func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { | ||||
if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
@@ -467,7 +507,7 @@ func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url s | |||||
// | // | ||||
// a.HTTPRedirectf(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} | // a.HTTPRedirectf(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} | ||||
// | // | ||||
// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false). | |||||
// Returns whether the assertion was successful (true) or not (false). | |||||
func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { | func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { | ||||
if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
@@ -475,6 +515,30 @@ func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url | |||||
return HTTPRedirectf(a.t, handler, method, url, values, msg, args...) | return HTTPRedirectf(a.t, handler, method, url, values, msg, args...) | ||||
} | } | ||||
// HTTPStatusCode asserts that a specified handler returns a specified status code. | |||||
// | |||||
// a.HTTPStatusCode(myHandler, "GET", "/notImplemented", nil, 501) | |||||
// | |||||
// Returns whether the assertion was successful (true) or not (false). | |||||
func (a *Assertions) HTTPStatusCode(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) bool { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return HTTPStatusCode(a.t, handler, method, url, values, statuscode, msgAndArgs...) | |||||
} | |||||
// HTTPStatusCodef asserts that a specified handler returns a specified status code. | |||||
// | |||||
// a.HTTPStatusCodef(myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") | |||||
// | |||||
// Returns whether the assertion was successful (true) or not (false). | |||||
func (a *Assertions) HTTPStatusCodef(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) bool { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return HTTPStatusCodef(a.t, handler, method, url, values, statuscode, msg, args...) | |||||
} | |||||
// HTTPSuccess asserts that a specified handler returns a success status code. | // HTTPSuccess asserts that a specified handler returns a success status code. | ||||
// | // | ||||
// a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil) | // a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil) | ||||
@@ -511,7 +575,7 @@ func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, | |||||
// Implementsf asserts that an object is implemented by the specified interface. | // Implementsf asserts that an object is implemented by the specified interface. | ||||
// | // | ||||
// a.Implementsf((*MyInterface, "error message %s", "formatted")(nil), new(MyObject)) | |||||
// a.Implementsf((*MyInterface)(nil), new(MyObject), "error message %s", "formatted") | |||||
func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool { | func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool { | ||||
if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
@@ -521,7 +585,7 @@ func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{} | |||||
// InDelta asserts that the two numerals are within delta of each other. | // InDelta asserts that the two numerals are within delta of each other. | ||||
// | // | ||||
// a.InDelta(math.Pi, (22 / 7.0), 0.01) | |||||
// a.InDelta(math.Pi, 22/7.0, 0.01) | |||||
func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { | func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { | ||||
if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
@@ -563,7 +627,7 @@ func (a *Assertions) InDeltaSlicef(expected interface{}, actual interface{}, del | |||||
// InDeltaf asserts that the two numerals are within delta of each other. | // InDeltaf asserts that the two numerals are within delta of each other. | ||||
// | // | ||||
// a.InDeltaf(math.Pi, (22 / 7.0, "error message %s", "formatted"), 0.01) | |||||
// a.InDeltaf(math.Pi, 22/7.0, 0.01, "error message %s", "formatted") | |||||
func (a *Assertions) InDeltaf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { | func (a *Assertions) InDeltaf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { | ||||
if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
@@ -603,6 +667,102 @@ func (a *Assertions) InEpsilonf(expected interface{}, actual interface{}, epsilo | |||||
return InEpsilonf(a.t, expected, actual, epsilon, msg, args...) | return InEpsilonf(a.t, expected, actual, epsilon, msg, args...) | ||||
} | } | ||||
// IsDecreasing asserts that the collection is decreasing | |||||
// | |||||
// a.IsDecreasing([]int{2, 1, 0}) | |||||
// a.IsDecreasing([]float{2, 1}) | |||||
// a.IsDecreasing([]string{"b", "a"}) | |||||
func (a *Assertions) IsDecreasing(object interface{}, msgAndArgs ...interface{}) bool { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return IsDecreasing(a.t, object, msgAndArgs...) | |||||
} | |||||
// IsDecreasingf asserts that the collection is decreasing | |||||
// | |||||
// a.IsDecreasingf([]int{2, 1, 0}, "error message %s", "formatted") | |||||
// a.IsDecreasingf([]float{2, 1}, "error message %s", "formatted") | |||||
// a.IsDecreasingf([]string{"b", "a"}, "error message %s", "formatted") | |||||
func (a *Assertions) IsDecreasingf(object interface{}, msg string, args ...interface{}) bool { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return IsDecreasingf(a.t, object, msg, args...) | |||||
} | |||||
// IsIncreasing asserts that the collection is increasing | |||||
// | |||||
// a.IsIncreasing([]int{1, 2, 3}) | |||||
// a.IsIncreasing([]float{1, 2}) | |||||
// a.IsIncreasing([]string{"a", "b"}) | |||||
func (a *Assertions) IsIncreasing(object interface{}, msgAndArgs ...interface{}) bool { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return IsIncreasing(a.t, object, msgAndArgs...) | |||||
} | |||||
// IsIncreasingf asserts that the collection is increasing | |||||
// | |||||
// a.IsIncreasingf([]int{1, 2, 3}, "error message %s", "formatted") | |||||
// a.IsIncreasingf([]float{1, 2}, "error message %s", "formatted") | |||||
// a.IsIncreasingf([]string{"a", "b"}, "error message %s", "formatted") | |||||
func (a *Assertions) IsIncreasingf(object interface{}, msg string, args ...interface{}) bool { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return IsIncreasingf(a.t, object, msg, args...) | |||||
} | |||||
// IsNonDecreasing asserts that the collection is not decreasing | |||||
// | |||||
// a.IsNonDecreasing([]int{1, 1, 2}) | |||||
// a.IsNonDecreasing([]float{1, 2}) | |||||
// a.IsNonDecreasing([]string{"a", "b"}) | |||||
func (a *Assertions) IsNonDecreasing(object interface{}, msgAndArgs ...interface{}) bool { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return IsNonDecreasing(a.t, object, msgAndArgs...) | |||||
} | |||||
// IsNonDecreasingf asserts that the collection is not decreasing | |||||
// | |||||
// a.IsNonDecreasingf([]int{1, 1, 2}, "error message %s", "formatted") | |||||
// a.IsNonDecreasingf([]float{1, 2}, "error message %s", "formatted") | |||||
// a.IsNonDecreasingf([]string{"a", "b"}, "error message %s", "formatted") | |||||
func (a *Assertions) IsNonDecreasingf(object interface{}, msg string, args ...interface{}) bool { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return IsNonDecreasingf(a.t, object, msg, args...) | |||||
} | |||||
// IsNonIncreasing asserts that the collection is not increasing | |||||
// | |||||
// a.IsNonIncreasing([]int{2, 1, 1}) | |||||
// a.IsNonIncreasing([]float{2, 1}) | |||||
// a.IsNonIncreasing([]string{"b", "a"}) | |||||
func (a *Assertions) IsNonIncreasing(object interface{}, msgAndArgs ...interface{}) bool { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return IsNonIncreasing(a.t, object, msgAndArgs...) | |||||
} | |||||
// IsNonIncreasingf asserts that the collection is not increasing | |||||
// | |||||
// a.IsNonIncreasingf([]int{2, 1, 1}, "error message %s", "formatted") | |||||
// a.IsNonIncreasingf([]float{2, 1}, "error message %s", "formatted") | |||||
// a.IsNonIncreasingf([]string{"b", "a"}, "error message %s", "formatted") | |||||
func (a *Assertions) IsNonIncreasingf(object interface{}, msg string, args ...interface{}) bool { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return IsNonIncreasingf(a.t, object, msg, args...) | |||||
} | |||||
// IsType asserts that the specified objects are of the same type. | // IsType asserts that the specified objects are of the same type. | ||||
func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool { | func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool { | ||||
if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
@@ -639,22 +799,6 @@ func (a *Assertions) JSONEqf(expected string, actual string, msg string, args .. | |||||
return JSONEqf(a.t, expected, actual, msg, args...) | return JSONEqf(a.t, expected, actual, msg, args...) | ||||
} | } | ||||
// YAMLEq asserts that two YAML strings are equivalent. | |||||
func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) bool { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return YAMLEq(a.t, expected, actual, msgAndArgs...) | |||||
} | |||||
// YAMLEqf asserts that two YAML strings are equivalent. | |||||
func (a *Assertions) YAMLEqf(expected string, actual string, msg string, args ...interface{}) bool { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return YAMLEqf(a.t, expected, actual, msg, args...) | |||||
} | |||||
// Len asserts that the specified object has specific length. | // Len asserts that the specified object has specific length. | ||||
// Len also fails if the object has a type that len() not accept. | // Len also fails if the object has a type that len() not accept. | ||||
// | // | ||||
@@ -718,7 +862,7 @@ func (a *Assertions) LessOrEqualf(e1 interface{}, e2 interface{}, msg string, ar | |||||
// Lessf asserts that the first element is less than the second | // Lessf asserts that the first element is less than the second | ||||
// | // | ||||
// a.Lessf(1, 2, "error message %s", "formatted") | // a.Lessf(1, 2, "error message %s", "formatted") | ||||
// a.Lessf(float64(1, "error message %s", "formatted"), float64(2)) | |||||
// a.Lessf(float64(1), float64(2), "error message %s", "formatted") | |||||
// a.Lessf("a", "b", "error message %s", "formatted") | // a.Lessf("a", "b", "error message %s", "formatted") | ||||
func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { | func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { | ||||
if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
@@ -727,6 +871,50 @@ func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...i | |||||
return Lessf(a.t, e1, e2, msg, args...) | return Lessf(a.t, e1, e2, msg, args...) | ||||
} | } | ||||
// Negative asserts that the specified element is negative | |||||
// | |||||
// a.Negative(-1) | |||||
// a.Negative(-1.23) | |||||
func (a *Assertions) Negative(e interface{}, msgAndArgs ...interface{}) bool { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return Negative(a.t, e, msgAndArgs...) | |||||
} | |||||
// Negativef asserts that the specified element is negative | |||||
// | |||||
// a.Negativef(-1, "error message %s", "formatted") | |||||
// a.Negativef(-1.23, "error message %s", "formatted") | |||||
func (a *Assertions) Negativef(e interface{}, msg string, args ...interface{}) bool { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return Negativef(a.t, e, msg, args...) | |||||
} | |||||
// Never asserts that the given condition doesn't satisfy in waitFor time, | |||||
// periodically checking the target function each tick. | |||||
// | |||||
// a.Never(func() bool { return false; }, time.Second, 10*time.Millisecond) | |||||
func (a *Assertions) Never(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return Never(a.t, condition, waitFor, tick, msgAndArgs...) | |||||
} | |||||
// Neverf asserts that the given condition doesn't satisfy in waitFor time, | |||||
// periodically checking the target function each tick. | |||||
// | |||||
// a.Neverf(func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") | |||||
func (a *Assertions) Neverf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return Neverf(a.t, condition, waitFor, tick, msg, args...) | |||||
} | |||||
// Nil asserts that the specified object is nil. | // Nil asserts that the specified object is nil. | ||||
// | // | ||||
// a.Nil(err) | // a.Nil(err) | ||||
@@ -747,6 +935,24 @@ func (a *Assertions) Nilf(object interface{}, msg string, args ...interface{}) b | |||||
return Nilf(a.t, object, msg, args...) | return Nilf(a.t, object, msg, args...) | ||||
} | } | ||||
// NoDirExists checks whether a directory does not exist in the given path. | |||||
// It fails if the path points to an existing _directory_ only. | |||||
func (a *Assertions) NoDirExists(path string, msgAndArgs ...interface{}) bool { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return NoDirExists(a.t, path, msgAndArgs...) | |||||
} | |||||
// NoDirExistsf checks whether a directory does not exist in the given path. | |||||
// It fails if the path points to an existing _directory_ only. | |||||
func (a *Assertions) NoDirExistsf(path string, msg string, args ...interface{}) bool { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return NoDirExistsf(a.t, path, msg, args...) | |||||
} | |||||
// NoError asserts that a function returned no error (i.e. `nil`). | // NoError asserts that a function returned no error (i.e. `nil`). | ||||
// | // | ||||
// actualObj, err := SomeFunction() | // actualObj, err := SomeFunction() | ||||
@@ -773,6 +979,24 @@ func (a *Assertions) NoErrorf(err error, msg string, args ...interface{}) bool { | |||||
return NoErrorf(a.t, err, msg, args...) | return NoErrorf(a.t, err, msg, args...) | ||||
} | } | ||||
// NoFileExists checks whether a file does not exist in a given path. It fails | |||||
// if the path points to an existing _file_ only. | |||||
func (a *Assertions) NoFileExists(path string, msgAndArgs ...interface{}) bool { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return NoFileExists(a.t, path, msgAndArgs...) | |||||
} | |||||
// NoFileExistsf checks whether a file does not exist in a given path. It fails | |||||
// if the path points to an existing _file_ only. | |||||
func (a *Assertions) NoFileExistsf(path string, msg string, args ...interface{}) bool { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return NoFileExistsf(a.t, path, msg, args...) | |||||
} | |||||
// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the | // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the | ||||
// specified substring or element. | // specified substring or element. | ||||
// | // | ||||
@@ -838,6 +1062,26 @@ func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndAr | |||||
return NotEqual(a.t, expected, actual, msgAndArgs...) | return NotEqual(a.t, expected, actual, msgAndArgs...) | ||||
} | } | ||||
// NotEqualValues asserts that two objects are not equal even when converted to the same type | |||||
// | |||||
// a.NotEqualValues(obj1, obj2) | |||||
func (a *Assertions) NotEqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return NotEqualValues(a.t, expected, actual, msgAndArgs...) | |||||
} | |||||
// NotEqualValuesf asserts that two objects are not equal even when converted to the same type | |||||
// | |||||
// a.NotEqualValuesf(obj1, obj2, "error message %s", "formatted") | |||||
func (a *Assertions) NotEqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return NotEqualValuesf(a.t, expected, actual, msg, args...) | |||||
} | |||||
// NotEqualf asserts that the specified values are NOT equal. | // NotEqualf asserts that the specified values are NOT equal. | ||||
// | // | ||||
// a.NotEqualf(obj1, obj2, "error message %s", "formatted") | // a.NotEqualf(obj1, obj2, "error message %s", "formatted") | ||||
@@ -851,6 +1095,24 @@ func (a *Assertions) NotEqualf(expected interface{}, actual interface{}, msg str | |||||
return NotEqualf(a.t, expected, actual, msg, args...) | return NotEqualf(a.t, expected, actual, msg, args...) | ||||
} | } | ||||
// NotErrorIs asserts that at none of the errors in err's chain matches target. | |||||
// This is a wrapper for errors.Is. | |||||
func (a *Assertions) NotErrorIs(err error, target error, msgAndArgs ...interface{}) bool { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return NotErrorIs(a.t, err, target, msgAndArgs...) | |||||
} | |||||
// NotErrorIsf asserts that at none of the errors in err's chain matches target. | |||||
// This is a wrapper for errors.Is. | |||||
func (a *Assertions) NotErrorIsf(err error, target error, msg string, args ...interface{}) bool { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return NotErrorIsf(a.t, err, target, msg, args...) | |||||
} | |||||
// NotNil asserts that the specified object is not nil. | // NotNil asserts that the specified object is not nil. | ||||
// | // | ||||
// a.NotNil(err) | // a.NotNil(err) | ||||
@@ -904,7 +1166,7 @@ func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...in | |||||
// NotRegexpf asserts that a specified regexp does not match a string. | // NotRegexpf asserts that a specified regexp does not match a string. | ||||
// | // | ||||
// a.NotRegexpf(regexp.MustCompile("starts", "error message %s", "formatted"), "it's starting") | |||||
// a.NotRegexpf(regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") | |||||
// a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted") | // a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted") | ||||
func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool { | func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool { | ||||
if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
@@ -913,6 +1175,32 @@ func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, arg | |||||
return NotRegexpf(a.t, rx, str, msg, args...) | return NotRegexpf(a.t, rx, str, msg, args...) | ||||
} | } | ||||
// NotSame asserts that two pointers do not reference the same object. | |||||
// | |||||
// a.NotSame(ptr1, ptr2) | |||||
// | |||||
// Both arguments must be pointer variables. Pointer variable sameness is | |||||
// determined based on the equality of both type and value. | |||||
func (a *Assertions) NotSame(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return NotSame(a.t, expected, actual, msgAndArgs...) | |||||
} | |||||
// NotSamef asserts that two pointers do not reference the same object. | |||||
// | |||||
// a.NotSamef(ptr1, ptr2, "error message %s", "formatted") | |||||
// | |||||
// Both arguments must be pointer variables. Pointer variable sameness is | |||||
// determined based on the equality of both type and value. | |||||
func (a *Assertions) NotSamef(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return NotSamef(a.t, expected, actual, msg, args...) | |||||
} | |||||
// NotSubset asserts that the specified list(array, slice...) contains not all | // NotSubset asserts that the specified list(array, slice...) contains not all | ||||
// elements given in the specified subset(array, slice...). | // elements given in the specified subset(array, slice...). | ||||
// | // | ||||
@@ -961,6 +1249,30 @@ func (a *Assertions) Panics(f PanicTestFunc, msgAndArgs ...interface{}) bool { | |||||
return Panics(a.t, f, msgAndArgs...) | return Panics(a.t, f, msgAndArgs...) | ||||
} | } | ||||
// PanicsWithError asserts that the code inside the specified PanicTestFunc | |||||
// panics, and that the recovered panic value is an error that satisfies the | |||||
// EqualError comparison. | |||||
// | |||||
// a.PanicsWithError("crazy error", func(){ GoCrazy() }) | |||||
func (a *Assertions) PanicsWithError(errString string, f PanicTestFunc, msgAndArgs ...interface{}) bool { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return PanicsWithError(a.t, errString, f, msgAndArgs...) | |||||
} | |||||
// PanicsWithErrorf asserts that the code inside the specified PanicTestFunc | |||||
// panics, and that the recovered panic value is an error that satisfies the | |||||
// EqualError comparison. | |||||
// | |||||
// a.PanicsWithErrorf("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") | |||||
func (a *Assertions) PanicsWithErrorf(errString string, f PanicTestFunc, msg string, args ...interface{}) bool { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return PanicsWithErrorf(a.t, errString, f, msg, args...) | |||||
} | |||||
// PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that | // PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that | ||||
// the recovered panic value equals the expected panic value. | // the recovered panic value equals the expected panic value. | ||||
// | // | ||||
@@ -993,6 +1305,28 @@ func (a *Assertions) Panicsf(f PanicTestFunc, msg string, args ...interface{}) b | |||||
return Panicsf(a.t, f, msg, args...) | return Panicsf(a.t, f, msg, args...) | ||||
} | } | ||||
// Positive asserts that the specified element is positive | |||||
// | |||||
// a.Positive(1) | |||||
// a.Positive(1.23) | |||||
func (a *Assertions) Positive(e interface{}, msgAndArgs ...interface{}) bool { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return Positive(a.t, e, msgAndArgs...) | |||||
} | |||||
// Positivef asserts that the specified element is positive | |||||
// | |||||
// a.Positivef(1, "error message %s", "formatted") | |||||
// a.Positivef(1.23, "error message %s", "formatted") | |||||
func (a *Assertions) Positivef(e interface{}, msg string, args ...interface{}) bool { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return Positivef(a.t, e, msg, args...) | |||||
} | |||||
// Regexp asserts that a specified regexp matches a string. | // Regexp asserts that a specified regexp matches a string. | ||||
// | // | ||||
// a.Regexp(regexp.MustCompile("start"), "it's starting") | // a.Regexp(regexp.MustCompile("start"), "it's starting") | ||||
@@ -1006,7 +1340,7 @@ func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...inter | |||||
// Regexpf asserts that a specified regexp matches a string. | // Regexpf asserts that a specified regexp matches a string. | ||||
// | // | ||||
// a.Regexpf(regexp.MustCompile("start", "error message %s", "formatted"), "it's starting") | |||||
// a.Regexpf(regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") | |||||
// a.Regexpf("start...$", "it's not starting", "error message %s", "formatted") | // a.Regexpf("start...$", "it's not starting", "error message %s", "formatted") | ||||
func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool { | func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool { | ||||
if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
@@ -1103,6 +1437,22 @@ func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta | |||||
return WithinDurationf(a.t, expected, actual, delta, msg, args...) | return WithinDurationf(a.t, expected, actual, delta, msg, args...) | ||||
} | } | ||||
// YAMLEq asserts that two YAML strings are equivalent. | |||||
func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) bool { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return YAMLEq(a.t, expected, actual, msgAndArgs...) | |||||
} | |||||
// YAMLEqf asserts that two YAML strings are equivalent. | |||||
func (a *Assertions) YAMLEqf(expected string, actual string, msg string, args ...interface{}) bool { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return YAMLEqf(a.t, expected, actual, msg, args...) | |||||
} | |||||
// Zero asserts that i is the zero value for its type. | // Zero asserts that i is the zero value for its type. | ||||
func (a *Assertions) Zero(i interface{}, msgAndArgs ...interface{}) bool { | func (a *Assertions) Zero(i interface{}, msgAndArgs ...interface{}) bool { | ||||
if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
@@ -5,305 +5,77 @@ import ( | |||||
"reflect" | "reflect" | ||||
) | ) | ||||
func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) { | |||||
switch kind { | |||||
case reflect.Int: | |||||
{ | |||||
intobj1 := obj1.(int) | |||||
intobj2 := obj2.(int) | |||||
if intobj1 > intobj2 { | |||||
return -1, true | |||||
} | |||||
if intobj1 == intobj2 { | |||||
return 0, true | |||||
} | |||||
if intobj1 < intobj2 { | |||||
return 1, true | |||||
} | |||||
} | |||||
case reflect.Int8: | |||||
{ | |||||
int8obj1 := obj1.(int8) | |||||
int8obj2 := obj2.(int8) | |||||
if int8obj1 > int8obj2 { | |||||
return -1, true | |||||
} | |||||
if int8obj1 == int8obj2 { | |||||
return 0, true | |||||
} | |||||
if int8obj1 < int8obj2 { | |||||
return 1, true | |||||
} | |||||
} | |||||
case reflect.Int16: | |||||
{ | |||||
int16obj1 := obj1.(int16) | |||||
int16obj2 := obj2.(int16) | |||||
if int16obj1 > int16obj2 { | |||||
return -1, true | |||||
} | |||||
if int16obj1 == int16obj2 { | |||||
return 0, true | |||||
} | |||||
if int16obj1 < int16obj2 { | |||||
return 1, true | |||||
} | |||||
} | |||||
case reflect.Int32: | |||||
{ | |||||
int32obj1 := obj1.(int32) | |||||
int32obj2 := obj2.(int32) | |||||
if int32obj1 > int32obj2 { | |||||
return -1, true | |||||
} | |||||
if int32obj1 == int32obj2 { | |||||
return 0, true | |||||
} | |||||
if int32obj1 < int32obj2 { | |||||
return 1, true | |||||
} | |||||
} | |||||
case reflect.Int64: | |||||
{ | |||||
int64obj1 := obj1.(int64) | |||||
int64obj2 := obj2.(int64) | |||||
if int64obj1 > int64obj2 { | |||||
return -1, true | |||||
} | |||||
if int64obj1 == int64obj2 { | |||||
return 0, true | |||||
} | |||||
if int64obj1 < int64obj2 { | |||||
return 1, true | |||||
} | |||||
} | |||||
case reflect.Uint: | |||||
{ | |||||
uintobj1 := obj1.(uint) | |||||
uintobj2 := obj2.(uint) | |||||
if uintobj1 > uintobj2 { | |||||
return -1, true | |||||
} | |||||
if uintobj1 == uintobj2 { | |||||
return 0, true | |||||
} | |||||
if uintobj1 < uintobj2 { | |||||
return 1, true | |||||
} | |||||
} | |||||
case reflect.Uint8: | |||||
{ | |||||
uint8obj1 := obj1.(uint8) | |||||
uint8obj2 := obj2.(uint8) | |||||
if uint8obj1 > uint8obj2 { | |||||
return -1, true | |||||
} | |||||
if uint8obj1 == uint8obj2 { | |||||
return 0, true | |||||
} | |||||
if uint8obj1 < uint8obj2 { | |||||
return 1, true | |||||
} | |||||
} | |||||
case reflect.Uint16: | |||||
{ | |||||
uint16obj1 := obj1.(uint16) | |||||
uint16obj2 := obj2.(uint16) | |||||
if uint16obj1 > uint16obj2 { | |||||
return -1, true | |||||
} | |||||
if uint16obj1 == uint16obj2 { | |||||
return 0, true | |||||
} | |||||
if uint16obj1 < uint16obj2 { | |||||
return 1, true | |||||
} | |||||
} | |||||
case reflect.Uint32: | |||||
{ | |||||
uint32obj1 := obj1.(uint32) | |||||
uint32obj2 := obj2.(uint32) | |||||
if uint32obj1 > uint32obj2 { | |||||
return -1, true | |||||
} | |||||
if uint32obj1 == uint32obj2 { | |||||
return 0, true | |||||
} | |||||
if uint32obj1 < uint32obj2 { | |||||
return 1, true | |||||
} | |||||
} | |||||
case reflect.Uint64: | |||||
{ | |||||
uint64obj1 := obj1.(uint64) | |||||
uint64obj2 := obj2.(uint64) | |||||
if uint64obj1 > uint64obj2 { | |||||
return -1, true | |||||
} | |||||
if uint64obj1 == uint64obj2 { | |||||
return 0, true | |||||
} | |||||
if uint64obj1 < uint64obj2 { | |||||
return 1, true | |||||
} | |||||
} | |||||
case reflect.Float32: | |||||
{ | |||||
float32obj1 := obj1.(float32) | |||||
float32obj2 := obj2.(float32) | |||||
if float32obj1 > float32obj2 { | |||||
return -1, true | |||||
} | |||||
if float32obj1 == float32obj2 { | |||||
return 0, true | |||||
} | |||||
if float32obj1 < float32obj2 { | |||||
return 1, true | |||||
} | |||||
} | |||||
case reflect.Float64: | |||||
{ | |||||
float64obj1 := obj1.(float64) | |||||
float64obj2 := obj2.(float64) | |||||
if float64obj1 > float64obj2 { | |||||
return -1, true | |||||
} | |||||
if float64obj1 == float64obj2 { | |||||
return 0, true | |||||
} | |||||
if float64obj1 < float64obj2 { | |||||
return 1, true | |||||
} | |||||
} | |||||
case reflect.String: | |||||
{ | |||||
stringobj1 := obj1.(string) | |||||
stringobj2 := obj2.(string) | |||||
if stringobj1 > stringobj2 { | |||||
return -1, true | |||||
} | |||||
if stringobj1 == stringobj2 { | |||||
return 0, true | |||||
} | |||||
if stringobj1 < stringobj2 { | |||||
return 1, true | |||||
} | |||||
} | |||||
} | |||||
return 0, false | |||||
} | |||||
// Greater asserts that the first element is greater than the second | |||||
// | |||||
// assert.Greater(t, 2, 1) | |||||
// assert.Greater(t, float64(2), float64(1)) | |||||
// assert.Greater(t, "b", "a") | |||||
func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
// isOrdered checks that collection contains orderable elements. | |||||
func isOrdered(t TestingT, object interface{}, allowedComparesResults []CompareType, failMessage string, msgAndArgs ...interface{}) bool { | |||||
objKind := reflect.TypeOf(object).Kind() | |||||
if objKind != reflect.Slice && objKind != reflect.Array { | |||||
return false | |||||
} | } | ||||
e1Kind := reflect.ValueOf(e1).Kind() | |||||
e2Kind := reflect.ValueOf(e2).Kind() | |||||
if e1Kind != e2Kind { | |||||
return Fail(t, "Elements should be the same type", msgAndArgs...) | |||||
} | |||||
objValue := reflect.ValueOf(object) | |||||
objLen := objValue.Len() | |||||
res, isComparable := compare(e1, e2, e1Kind) | |||||
if !isComparable { | |||||
return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...) | |||||
if objLen <= 1 { | |||||
return true | |||||
} | } | ||||
if res != -1 { | |||||
return Fail(t, fmt.Sprintf("\"%v\" is not greater than \"%v\"", e1, e2), msgAndArgs...) | |||||
} | |||||
value := objValue.Index(0) | |||||
valueInterface := value.Interface() | |||||
firstValueKind := value.Kind() | |||||
return true | |||||
} | |||||
for i := 1; i < objLen; i++ { | |||||
prevValue := value | |||||
prevValueInterface := valueInterface | |||||
// GreaterOrEqual asserts that the first element is greater than or equal to the second | |||||
// | |||||
// assert.GreaterOrEqual(t, 2, 1) | |||||
// assert.GreaterOrEqual(t, 2, 2) | |||||
// assert.GreaterOrEqual(t, "b", "a") | |||||
// assert.GreaterOrEqual(t, "b", "b") | |||||
func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
value = objValue.Index(i) | |||||
valueInterface = value.Interface() | |||||
e1Kind := reflect.ValueOf(e1).Kind() | |||||
e2Kind := reflect.ValueOf(e2).Kind() | |||||
if e1Kind != e2Kind { | |||||
return Fail(t, "Elements should be the same type", msgAndArgs...) | |||||
} | |||||
compareResult, isComparable := compare(prevValueInterface, valueInterface, firstValueKind) | |||||
res, isComparable := compare(e1, e2, e1Kind) | |||||
if !isComparable { | |||||
return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...) | |||||
} | |||||
if !isComparable { | |||||
return Fail(t, fmt.Sprintf("Can not compare type \"%s\" and \"%s\"", reflect.TypeOf(value), reflect.TypeOf(prevValue)), msgAndArgs...) | |||||
} | |||||
if res != -1 && res != 0 { | |||||
return Fail(t, fmt.Sprintf("\"%v\" is not greater than or equal to \"%v\"", e1, e2), msgAndArgs...) | |||||
if !containsValue(allowedComparesResults, compareResult) { | |||||
return Fail(t, fmt.Sprintf(failMessage, prevValue, value), msgAndArgs...) | |||||
} | |||||
} | } | ||||
return true | return true | ||||
} | } | ||||
// Less asserts that the first element is less than the second | |||||
// IsIncreasing asserts that the collection is increasing | |||||
// | // | ||||
// assert.Less(t, 1, 2) | |||||
// assert.Less(t, float64(1), float64(2)) | |||||
// assert.Less(t, "a", "b") | |||||
func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
e1Kind := reflect.ValueOf(e1).Kind() | |||||
e2Kind := reflect.ValueOf(e2).Kind() | |||||
if e1Kind != e2Kind { | |||||
return Fail(t, "Elements should be the same type", msgAndArgs...) | |||||
} | |||||
res, isComparable := compare(e1, e2, e1Kind) | |||||
if !isComparable { | |||||
return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...) | |||||
} | |||||
if res != 1 { | |||||
return Fail(t, fmt.Sprintf("\"%v\" is not less than \"%v\"", e1, e2), msgAndArgs...) | |||||
} | |||||
return true | |||||
// assert.IsIncreasing(t, []int{1, 2, 3}) | |||||
// assert.IsIncreasing(t, []float{1, 2}) | |||||
// assert.IsIncreasing(t, []string{"a", "b"}) | |||||
func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { | |||||
return isOrdered(t, object, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs) | |||||
} | } | ||||
// LessOrEqual asserts that the first element is less than or equal to the second | |||||
// IsNonIncreasing asserts that the collection is not increasing | |||||
// | // | ||||
// assert.LessOrEqual(t, 1, 2) | |||||
// assert.LessOrEqual(t, 2, 2) | |||||
// assert.LessOrEqual(t, "a", "b") | |||||
// assert.LessOrEqual(t, "b", "b") | |||||
func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
e1Kind := reflect.ValueOf(e1).Kind() | |||||
e2Kind := reflect.ValueOf(e2).Kind() | |||||
if e1Kind != e2Kind { | |||||
return Fail(t, "Elements should be the same type", msgAndArgs...) | |||||
} | |||||
res, isComparable := compare(e1, e2, e1Kind) | |||||
if !isComparable { | |||||
return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...) | |||||
} | |||||
// assert.IsNonIncreasing(t, []int{2, 1, 1}) | |||||
// assert.IsNonIncreasing(t, []float{2, 1}) | |||||
// assert.IsNonIncreasing(t, []string{"b", "a"}) | |||||
func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { | |||||
return isOrdered(t, object, []CompareType{compareEqual, compareGreater}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs) | |||||
} | |||||
if res != 1 && res != 0 { | |||||
return Fail(t, fmt.Sprintf("\"%v\" is not less than or equal to \"%v\"", e1, e2), msgAndArgs...) | |||||
} | |||||
// IsDecreasing asserts that the collection is decreasing | |||||
// | |||||
// assert.IsDecreasing(t, []int{2, 1, 0}) | |||||
// assert.IsDecreasing(t, []float{2, 1}) | |||||
// assert.IsDecreasing(t, []string{"b", "a"}) | |||||
func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { | |||||
return isOrdered(t, object, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs) | |||||
} | |||||
return true | |||||
// IsNonDecreasing asserts that the collection is not decreasing | |||||
// | |||||
// assert.IsNonDecreasing(t, []int{1, 1, 2}) | |||||
// assert.IsNonDecreasing(t, []float{1, 2}) | |||||
// assert.IsNonDecreasing(t, []string{"a", "b"}) | |||||
func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { | |||||
return isOrdered(t, object, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs) | |||||
} | } |
@@ -11,6 +11,7 @@ import ( | |||||
"reflect" | "reflect" | ||||
"regexp" | "regexp" | ||||
"runtime" | "runtime" | ||||
"runtime/debug" | |||||
"strings" | "strings" | ||||
"time" | "time" | ||||
"unicode" | "unicode" | ||||
@@ -18,10 +19,10 @@ import ( | |||||
"github.com/davecgh/go-spew/spew" | "github.com/davecgh/go-spew/spew" | ||||
"github.com/pmezard/go-difflib/difflib" | "github.com/pmezard/go-difflib/difflib" | ||||
yaml "gopkg.in/yaml.v2" | |||||
yaml "gopkg.in/yaml.v3" | |||||
) | ) | ||||
//go:generate go run ../_codegen/main.go -output-package=assert -template=assertion_format.go.tmpl | |||||
//go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=assert -template=assertion_format.go.tmpl" | |||||
// TestingT is an interface wrapper around *testing.T | // TestingT is an interface wrapper around *testing.T | ||||
type TestingT interface { | type TestingT interface { | ||||
@@ -44,7 +45,7 @@ type BoolAssertionFunc func(TestingT, bool, ...interface{}) bool | |||||
// for table driven tests. | // for table driven tests. | ||||
type ErrorAssertionFunc func(TestingT, error, ...interface{}) bool | type ErrorAssertionFunc func(TestingT, error, ...interface{}) bool | ||||
// Comparison a custom function that returns true on success and false on failure | |||||
// Comparison is a custom function that returns true on success and false on failure | |||||
type Comparison func() (success bool) | type Comparison func() (success bool) | ||||
/* | /* | ||||
@@ -103,11 +104,11 @@ the problem actually occurred in calling code.*/ | |||||
// failed. | // failed. | ||||
func CallerInfo() []string { | func CallerInfo() []string { | ||||
pc := uintptr(0) | |||||
file := "" | |||||
line := 0 | |||||
ok := false | |||||
name := "" | |||||
var pc uintptr | |||||
var ok bool | |||||
var file string | |||||
var line int | |||||
var name string | |||||
callers := []string{} | callers := []string{} | ||||
for i := 0; ; i++ { | for i := 0; ; i++ { | ||||
@@ -171,8 +172,8 @@ func isTest(name, prefix string) bool { | |||||
if len(name) == len(prefix) { // "Test" is ok | if len(name) == len(prefix) { // "Test" is ok | ||||
return true | return true | ||||
} | } | ||||
rune, _ := utf8.DecodeRuneInString(name[len(prefix):]) | |||||
return !unicode.IsLower(rune) | |||||
r, _ := utf8.DecodeRuneInString(name[len(prefix):]) | |||||
return !unicode.IsLower(r) | |||||
} | } | ||||
func messageFromMsgAndArgs(msgAndArgs ...interface{}) string { | func messageFromMsgAndArgs(msgAndArgs ...interface{}) string { | ||||
@@ -351,6 +352,19 @@ func Equal(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) | |||||
} | } | ||||
// validateEqualArgs checks whether provided arguments can be safely used in the | |||||
// Equal/NotEqual functions. | |||||
func validateEqualArgs(expected, actual interface{}) error { | |||||
if expected == nil && actual == nil { | |||||
return nil | |||||
} | |||||
if isFunction(expected) || isFunction(actual) { | |||||
return errors.New("cannot take func type as argument") | |||||
} | |||||
return nil | |||||
} | |||||
// Same asserts that two pointers reference the same object. | // Same asserts that two pointers reference the same object. | ||||
// | // | ||||
// assert.Same(t, ptr1, ptr2) | // assert.Same(t, ptr1, ptr2) | ||||
@@ -362,18 +376,7 @@ func Same(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) b | |||||
h.Helper() | h.Helper() | ||||
} | } | ||||
expectedPtr, actualPtr := reflect.ValueOf(expected), reflect.ValueOf(actual) | |||||
if expectedPtr.Kind() != reflect.Ptr || actualPtr.Kind() != reflect.Ptr { | |||||
return Fail(t, "Invalid operation: both arguments must be pointers", msgAndArgs...) | |||||
} | |||||
expectedType, actualType := reflect.TypeOf(expected), reflect.TypeOf(actual) | |||||
if expectedType != actualType { | |||||
return Fail(t, fmt.Sprintf("Pointer expected to be of type %v, but was %v", | |||||
expectedType, actualType), msgAndArgs...) | |||||
} | |||||
if expected != actual { | |||||
if !samePointers(expected, actual) { | |||||
return Fail(t, fmt.Sprintf("Not same: \n"+ | return Fail(t, fmt.Sprintf("Not same: \n"+ | ||||
"expected: %p %#v\n"+ | "expected: %p %#v\n"+ | ||||
"actual : %p %#v", expected, expected, actual, actual), msgAndArgs...) | "actual : %p %#v", expected, expected, actual, actual), msgAndArgs...) | ||||
@@ -382,6 +385,42 @@ func Same(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) b | |||||
return true | return true | ||||
} | } | ||||
// NotSame asserts that two pointers do not reference the same object. | |||||
// | |||||
// assert.NotSame(t, ptr1, ptr2) | |||||
// | |||||
// Both arguments must be pointer variables. Pointer variable sameness is | |||||
// determined based on the equality of both type and value. | |||||
func NotSame(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
if samePointers(expected, actual) { | |||||
return Fail(t, fmt.Sprintf( | |||||
"Expected and actual point to the same object: %p %#v", | |||||
expected, expected), msgAndArgs...) | |||||
} | |||||
return true | |||||
} | |||||
// samePointers compares two generic interface objects and returns whether | |||||
// they point to the same object | |||||
func samePointers(first, second interface{}) bool { | |||||
firstPtr, secondPtr := reflect.ValueOf(first), reflect.ValueOf(second) | |||||
if firstPtr.Kind() != reflect.Ptr || secondPtr.Kind() != reflect.Ptr { | |||||
return false | |||||
} | |||||
firstType, secondType := reflect.TypeOf(first), reflect.TypeOf(second) | |||||
if firstType != secondType { | |||||
return false | |||||
} | |||||
// compare pointer addresses | |||||
return first == second | |||||
} | |||||
// formatUnequalValues takes two values of arbitrary types and returns string | // formatUnequalValues takes two values of arbitrary types and returns string | ||||
// representations appropriate to be presented to the user. | // representations appropriate to be presented to the user. | ||||
// | // | ||||
@@ -390,12 +429,27 @@ func Same(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) b | |||||
// to a type conversion in the Go grammar. | // to a type conversion in the Go grammar. | ||||
func formatUnequalValues(expected, actual interface{}) (e string, a string) { | func formatUnequalValues(expected, actual interface{}) (e string, a string) { | ||||
if reflect.TypeOf(expected) != reflect.TypeOf(actual) { | if reflect.TypeOf(expected) != reflect.TypeOf(actual) { | ||||
return fmt.Sprintf("%T(%#v)", expected, expected), | |||||
fmt.Sprintf("%T(%#v)", actual, actual) | |||||
return fmt.Sprintf("%T(%s)", expected, truncatingFormat(expected)), | |||||
fmt.Sprintf("%T(%s)", actual, truncatingFormat(actual)) | |||||
} | |||||
switch expected.(type) { | |||||
case time.Duration: | |||||
return fmt.Sprintf("%v", expected), fmt.Sprintf("%v", actual) | |||||
} | } | ||||
return truncatingFormat(expected), truncatingFormat(actual) | |||||
} | |||||
return fmt.Sprintf("%#v", expected), | |||||
fmt.Sprintf("%#v", actual) | |||||
// truncatingFormat formats the data and truncates it if it's too long. | |||||
// | |||||
// This helps keep formatted error messages lines from exceeding the | |||||
// bufio.MaxScanTokenSize max line length that the go testing framework imposes. | |||||
func truncatingFormat(data interface{}) string { | |||||
value := fmt.Sprintf("%#v", data) | |||||
max := bufio.MaxScanTokenSize - 100 // Give us some space the type info too if needed. | |||||
if len(value) > max { | |||||
value = value[0:max] + "<... truncated>" | |||||
} | |||||
return value | |||||
} | } | ||||
// EqualValues asserts that two objects are equal or convertable to the same types | // EqualValues asserts that two objects are equal or convertable to the same types | ||||
@@ -442,12 +496,12 @@ func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{} | |||||
// | // | ||||
// assert.NotNil(t, err) | // assert.NotNil(t, err) | ||||
func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { | func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { | ||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
if !isNil(object) { | if !isNil(object) { | ||||
return true | return true | ||||
} | } | ||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return Fail(t, "Expected value not to be nil.", msgAndArgs...) | return Fail(t, "Expected value not to be nil.", msgAndArgs...) | ||||
} | } | ||||
@@ -488,12 +542,12 @@ func isNil(object interface{}) bool { | |||||
// | // | ||||
// assert.Nil(t, err) | // assert.Nil(t, err) | ||||
func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { | func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { | ||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
if isNil(object) { | if isNil(object) { | ||||
return true | return true | ||||
} | } | ||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return Fail(t, fmt.Sprintf("Expected nil, but got: %#v", object), msgAndArgs...) | return Fail(t, fmt.Sprintf("Expected nil, but got: %#v", object), msgAndArgs...) | ||||
} | } | ||||
@@ -530,12 +584,11 @@ func isEmpty(object interface{}) bool { | |||||
// | // | ||||
// assert.Empty(t, obj) | // assert.Empty(t, obj) | ||||
func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { | func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { | ||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
pass := isEmpty(object) | pass := isEmpty(object) | ||||
if !pass { | if !pass { | ||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
Fail(t, fmt.Sprintf("Should be empty, but was %v", object), msgAndArgs...) | Fail(t, fmt.Sprintf("Should be empty, but was %v", object), msgAndArgs...) | ||||
} | } | ||||
@@ -550,12 +603,11 @@ func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { | |||||
// assert.Equal(t, "two", obj[1]) | // assert.Equal(t, "two", obj[1]) | ||||
// } | // } | ||||
func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { | func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { | ||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
pass := !isEmpty(object) | pass := !isEmpty(object) | ||||
if !pass { | if !pass { | ||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
Fail(t, fmt.Sprintf("Should NOT be empty, but was %v", object), msgAndArgs...) | Fail(t, fmt.Sprintf("Should NOT be empty, but was %v", object), msgAndArgs...) | ||||
} | } | ||||
@@ -598,16 +650,10 @@ func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) | |||||
// | // | ||||
// assert.True(t, myBool) | // assert.True(t, myBool) | ||||
func True(t TestingT, value bool, msgAndArgs ...interface{}) bool { | func True(t TestingT, value bool, msgAndArgs ...interface{}) bool { | ||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
if h, ok := t.(interface { | |||||
Helper() | |||||
}); ok { | |||||
h.Helper() | |||||
} | |||||
if value != true { | |||||
if !value { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return Fail(t, "Should be true", msgAndArgs...) | return Fail(t, "Should be true", msgAndArgs...) | ||||
} | } | ||||
@@ -619,11 +665,10 @@ func True(t TestingT, value bool, msgAndArgs ...interface{}) bool { | |||||
// | // | ||||
// assert.False(t, myBool) | // assert.False(t, myBool) | ||||
func False(t TestingT, value bool, msgAndArgs ...interface{}) bool { | func False(t TestingT, value bool, msgAndArgs ...interface{}) bool { | ||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
if value != false { | |||||
if value { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return Fail(t, "Should be false", msgAndArgs...) | return Fail(t, "Should be false", msgAndArgs...) | ||||
} | } | ||||
@@ -654,6 +699,21 @@ func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{ | |||||
} | } | ||||
// NotEqualValues asserts that two objects are not equal even when converted to the same type | |||||
// | |||||
// assert.NotEqualValues(t, obj1, obj2) | |||||
func NotEqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
if ObjectsAreEqualValues(expected, actual) { | |||||
return Fail(t, fmt.Sprintf("Should not be: %#v\n", actual), msgAndArgs...) | |||||
} | |||||
return true | |||||
} | |||||
// containsElement try loop over the list check if the list includes the element. | // containsElement try loop over the list check if the list includes the element. | ||||
// return (false, false) if impossible. | // return (false, false) if impossible. | ||||
// return (true, false) if element was not found. | // return (true, false) if element was not found. | ||||
@@ -706,10 +766,10 @@ func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bo | |||||
ok, found := includeElement(s, contains) | ok, found := includeElement(s, contains) | ||||
if !ok { | if !ok { | ||||
return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", s), msgAndArgs...) | |||||
return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", s), msgAndArgs...) | |||||
} | } | ||||
if !found { | if !found { | ||||
return Fail(t, fmt.Sprintf("\"%s\" does not contain \"%s\"", s, contains), msgAndArgs...) | |||||
return Fail(t, fmt.Sprintf("%#v does not contain %#v", s, contains), msgAndArgs...) | |||||
} | } | ||||
return true | return true | ||||
@@ -840,27 +900,39 @@ func ElementsMatch(t TestingT, listA, listB interface{}, msgAndArgs ...interface | |||||
return true | return true | ||||
} | } | ||||
aKind := reflect.TypeOf(listA).Kind() | |||||
bKind := reflect.TypeOf(listB).Kind() | |||||
if !isList(t, listA, msgAndArgs...) || !isList(t, listB, msgAndArgs...) { | |||||
return false | |||||
} | |||||
extraA, extraB := diffLists(listA, listB) | |||||
if aKind != reflect.Array && aKind != reflect.Slice { | |||||
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", listA, aKind), msgAndArgs...) | |||||
if len(extraA) == 0 && len(extraB) == 0 { | |||||
return true | |||||
} | } | ||||
if bKind != reflect.Array && bKind != reflect.Slice { | |||||
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", listB, bKind), msgAndArgs...) | |||||
return Fail(t, formatListDiff(listA, listB, extraA, extraB), msgAndArgs...) | |||||
} | |||||
// isList checks that the provided value is array or slice. | |||||
func isList(t TestingT, list interface{}, msgAndArgs ...interface{}) (ok bool) { | |||||
kind := reflect.TypeOf(list).Kind() | |||||
if kind != reflect.Array && kind != reflect.Slice { | |||||
return Fail(t, fmt.Sprintf("%q has an unsupported type %s, expecting array or slice", list, kind), | |||||
msgAndArgs...) | |||||
} | } | ||||
return true | |||||
} | |||||
// diffLists diffs two arrays/slices and returns slices of elements that are only in A and only in B. | |||||
// If some element is present multiple times, each instance is counted separately (e.g. if something is 2x in A and | |||||
// 5x in B, it will be 0x in extraA and 3x in extraB). The order of items in both lists is ignored. | |||||
func diffLists(listA, listB interface{}) (extraA, extraB []interface{}) { | |||||
aValue := reflect.ValueOf(listA) | aValue := reflect.ValueOf(listA) | ||||
bValue := reflect.ValueOf(listB) | bValue := reflect.ValueOf(listB) | ||||
aLen := aValue.Len() | aLen := aValue.Len() | ||||
bLen := bValue.Len() | bLen := bValue.Len() | ||||
if aLen != bLen { | |||||
return Fail(t, fmt.Sprintf("lengths don't match: %d != %d", aLen, bLen), msgAndArgs...) | |||||
} | |||||
// Mark indexes in bValue that we already used | // Mark indexes in bValue that we already used | ||||
visited := make([]bool, bLen) | visited := make([]bool, bLen) | ||||
for i := 0; i < aLen; i++ { | for i := 0; i < aLen; i++ { | ||||
@@ -877,11 +949,38 @@ func ElementsMatch(t TestingT, listA, listB interface{}, msgAndArgs ...interface | |||||
} | } | ||||
} | } | ||||
if !found { | if !found { | ||||
return Fail(t, fmt.Sprintf("element %s appears more times in %s than in %s", element, aValue, bValue), msgAndArgs...) | |||||
extraA = append(extraA, element) | |||||
} | } | ||||
} | } | ||||
return true | |||||
for j := 0; j < bLen; j++ { | |||||
if visited[j] { | |||||
continue | |||||
} | |||||
extraB = append(extraB, bValue.Index(j).Interface()) | |||||
} | |||||
return | |||||
} | |||||
func formatListDiff(listA, listB interface{}, extraA, extraB []interface{}) string { | |||||
var msg bytes.Buffer | |||||
msg.WriteString("elements differ") | |||||
if len(extraA) > 0 { | |||||
msg.WriteString("\n\nextra elements in list A:\n") | |||||
msg.WriteString(spewConfig.Sdump(extraA)) | |||||
} | |||||
if len(extraB) > 0 { | |||||
msg.WriteString("\n\nextra elements in list B:\n") | |||||
msg.WriteString(spewConfig.Sdump(extraB)) | |||||
} | |||||
msg.WriteString("\n\nlistA:\n") | |||||
msg.WriteString(spewConfig.Sdump(listA)) | |||||
msg.WriteString("\n\nlistB:\n") | |||||
msg.WriteString(spewConfig.Sdump(listB)) | |||||
return msg.String() | |||||
} | } | ||||
// Condition uses a Comparison to assert a complex condition. | // Condition uses a Comparison to assert a complex condition. | ||||
@@ -901,15 +1000,17 @@ func Condition(t TestingT, comp Comparison, msgAndArgs ...interface{}) bool { | |||||
type PanicTestFunc func() | type PanicTestFunc func() | ||||
// didPanic returns true if the function passed to it panics. Otherwise, it returns false. | // didPanic returns true if the function passed to it panics. Otherwise, it returns false. | ||||
func didPanic(f PanicTestFunc) (bool, interface{}) { | |||||
func didPanic(f PanicTestFunc) (bool, interface{}, string) { | |||||
didPanic := false | didPanic := false | ||||
var message interface{} | var message interface{} | ||||
var stack string | |||||
func() { | func() { | ||||
defer func() { | defer func() { | ||||
if message = recover(); message != nil { | if message = recover(); message != nil { | ||||
didPanic = true | didPanic = true | ||||
stack = string(debug.Stack()) | |||||
} | } | ||||
}() | }() | ||||
@@ -918,7 +1019,7 @@ func didPanic(f PanicTestFunc) (bool, interface{}) { | |||||
}() | }() | ||||
return didPanic, message | |||||
return didPanic, message, stack | |||||
} | } | ||||
@@ -930,7 +1031,7 @@ func Panics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { | |||||
h.Helper() | h.Helper() | ||||
} | } | ||||
if funcDidPanic, panicValue := didPanic(f); !funcDidPanic { | |||||
if funcDidPanic, panicValue, _ := didPanic(f); !funcDidPanic { | |||||
return Fail(t, fmt.Sprintf("func %#v should panic\n\tPanic value:\t%#v", f, panicValue), msgAndArgs...) | return Fail(t, fmt.Sprintf("func %#v should panic\n\tPanic value:\t%#v", f, panicValue), msgAndArgs...) | ||||
} | } | ||||
@@ -946,12 +1047,34 @@ func PanicsWithValue(t TestingT, expected interface{}, f PanicTestFunc, msgAndAr | |||||
h.Helper() | h.Helper() | ||||
} | } | ||||
funcDidPanic, panicValue := didPanic(f) | |||||
funcDidPanic, panicValue, panickedStack := didPanic(f) | |||||
if !funcDidPanic { | if !funcDidPanic { | ||||
return Fail(t, fmt.Sprintf("func %#v should panic\n\tPanic value:\t%#v", f, panicValue), msgAndArgs...) | return Fail(t, fmt.Sprintf("func %#v should panic\n\tPanic value:\t%#v", f, panicValue), msgAndArgs...) | ||||
} | } | ||||
if panicValue != expected { | if panicValue != expected { | ||||
return Fail(t, fmt.Sprintf("func %#v should panic with value:\t%#v\n\tPanic value:\t%#v", f, expected, panicValue), msgAndArgs...) | |||||
return Fail(t, fmt.Sprintf("func %#v should panic with value:\t%#v\n\tPanic value:\t%#v\n\tPanic stack:\t%s", f, expected, panicValue, panickedStack), msgAndArgs...) | |||||
} | |||||
return true | |||||
} | |||||
// PanicsWithError asserts that the code inside the specified PanicTestFunc | |||||
// panics, and that the recovered panic value is an error that satisfies the | |||||
// EqualError comparison. | |||||
// | |||||
// assert.PanicsWithError(t, "crazy error", func(){ GoCrazy() }) | |||||
func PanicsWithError(t TestingT, errString string, f PanicTestFunc, msgAndArgs ...interface{}) bool { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
funcDidPanic, panicValue, panickedStack := didPanic(f) | |||||
if !funcDidPanic { | |||||
return Fail(t, fmt.Sprintf("func %#v should panic\n\tPanic value:\t%#v", f, panicValue), msgAndArgs...) | |||||
} | |||||
panicErr, ok := panicValue.(error) | |||||
if !ok || panicErr.Error() != errString { | |||||
return Fail(t, fmt.Sprintf("func %#v should panic with error message:\t%#v\n\tPanic value:\t%#v\n\tPanic stack:\t%s", f, errString, panicValue, panickedStack), msgAndArgs...) | |||||
} | } | ||||
return true | return true | ||||
@@ -965,8 +1088,8 @@ func NotPanics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { | |||||
h.Helper() | h.Helper() | ||||
} | } | ||||
if funcDidPanic, panicValue := didPanic(f); funcDidPanic { | |||||
return Fail(t, fmt.Sprintf("func %#v should not panic\n\tPanic value:\t%v", f, panicValue), msgAndArgs...) | |||||
if funcDidPanic, panicValue, panickedStack := didPanic(f); funcDidPanic { | |||||
return Fail(t, fmt.Sprintf("func %#v should not panic\n\tPanic value:\t%v\n\tPanic stack:\t%s", f, panicValue, panickedStack), msgAndArgs...) | |||||
} | } | ||||
return true | return true | ||||
@@ -993,6 +1116,8 @@ func toFloat(x interface{}) (float64, bool) { | |||||
xok := true | xok := true | ||||
switch xn := x.(type) { | switch xn := x.(type) { | ||||
case uint: | |||||
xf = float64(xn) | |||||
case uint8: | case uint8: | ||||
xf = float64(xn) | xf = float64(xn) | ||||
case uint16: | case uint16: | ||||
@@ -1014,7 +1139,7 @@ func toFloat(x interface{}) (float64, bool) { | |||||
case float32: | case float32: | ||||
xf = float64(xn) | xf = float64(xn) | ||||
case float64: | case float64: | ||||
xf = float64(xn) | |||||
xf = xn | |||||
case time.Duration: | case time.Duration: | ||||
xf = float64(xn) | xf = float64(xn) | ||||
default: | default: | ||||
@@ -1026,7 +1151,7 @@ func toFloat(x interface{}) (float64, bool) { | |||||
// InDelta asserts that the two numerals are within delta of each other. | // InDelta asserts that the two numerals are within delta of each other. | ||||
// | // | ||||
// assert.InDelta(t, math.Pi, (22 / 7.0), 0.01) | |||||
// assert.InDelta(t, math.Pi, 22/7.0, 0.01) | |||||
func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { | func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { | ||||
if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
@@ -1128,6 +1253,9 @@ func calcRelativeError(expected, actual interface{}) (float64, error) { | |||||
if !aok { | if !aok { | ||||
return 0, fmt.Errorf("expected value %q cannot be converted to float", expected) | return 0, fmt.Errorf("expected value %q cannot be converted to float", expected) | ||||
} | } | ||||
if math.IsNaN(af) { | |||||
return 0, errors.New("expected value must not be NaN") | |||||
} | |||||
if af == 0 { | if af == 0 { | ||||
return 0, fmt.Errorf("expected value must have a value other than zero to calculate the relative error") | return 0, fmt.Errorf("expected value must have a value other than zero to calculate the relative error") | ||||
} | } | ||||
@@ -1135,6 +1263,9 @@ func calcRelativeError(expected, actual interface{}) (float64, error) { | |||||
if !bok { | if !bok { | ||||
return 0, fmt.Errorf("actual value %q cannot be converted to float", actual) | return 0, fmt.Errorf("actual value %q cannot be converted to float", actual) | ||||
} | } | ||||
if math.IsNaN(bf) { | |||||
return 0, errors.New("actual value must not be NaN") | |||||
} | |||||
return math.Abs(af-bf) / math.Abs(af), nil | return math.Abs(af-bf) / math.Abs(af), nil | ||||
} | } | ||||
@@ -1144,6 +1275,9 @@ func InEpsilon(t TestingT, expected, actual interface{}, epsilon float64, msgAnd | |||||
if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
} | } | ||||
if math.IsNaN(epsilon) { | |||||
return Fail(t, "epsilon must not be NaN") | |||||
} | |||||
actualEpsilon, err := calcRelativeError(expected, actual) | actualEpsilon, err := calcRelativeError(expected, actual) | ||||
if err != nil { | if err != nil { | ||||
return Fail(t, err.Error(), msgAndArgs...) | return Fail(t, err.Error(), msgAndArgs...) | ||||
@@ -1191,10 +1325,10 @@ func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, m | |||||
// assert.Equal(t, expectedObj, actualObj) | // assert.Equal(t, expectedObj, actualObj) | ||||
// } | // } | ||||
func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool { | func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool { | ||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
if err != nil { | if err != nil { | ||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return Fail(t, fmt.Sprintf("Received unexpected error:\n%+v", err), msgAndArgs...) | return Fail(t, fmt.Sprintf("Received unexpected error:\n%+v", err), msgAndArgs...) | ||||
} | } | ||||
@@ -1208,11 +1342,10 @@ func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool { | |||||
// assert.Equal(t, expectedError, err) | // assert.Equal(t, expectedError, err) | ||||
// } | // } | ||||
func Error(t TestingT, err error, msgAndArgs ...interface{}) bool { | func Error(t TestingT, err error, msgAndArgs ...interface{}) bool { | ||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
if err == nil { | if err == nil { | ||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
return Fail(t, "An error is expected but got nil.", msgAndArgs...) | return Fail(t, "An error is expected but got nil.", msgAndArgs...) | ||||
} | } | ||||
@@ -1314,7 +1447,8 @@ func NotZero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool { | |||||
return true | return true | ||||
} | } | ||||
// FileExists checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. | |||||
// FileExists checks whether a file exists in the given path. It also fails if | |||||
// the path points to a directory or there is an error when trying to check the file. | |||||
func FileExists(t TestingT, path string, msgAndArgs ...interface{}) bool { | func FileExists(t TestingT, path string, msgAndArgs ...interface{}) bool { | ||||
if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
@@ -1332,7 +1466,24 @@ func FileExists(t TestingT, path string, msgAndArgs ...interface{}) bool { | |||||
return true | return true | ||||
} | } | ||||
// DirExists checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. | |||||
// NoFileExists checks whether a file does not exist in a given path. It fails | |||||
// if the path points to an existing _file_ only. | |||||
func NoFileExists(t TestingT, path string, msgAndArgs ...interface{}) bool { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
info, err := os.Lstat(path) | |||||
if err != nil { | |||||
return true | |||||
} | |||||
if info.IsDir() { | |||||
return true | |||||
} | |||||
return Fail(t, fmt.Sprintf("file %q exists", path), msgAndArgs...) | |||||
} | |||||
// DirExists checks whether a directory exists in the given path. It also fails | |||||
// if the path is a file rather a directory or there is an error checking whether it exists. | |||||
func DirExists(t TestingT, path string, msgAndArgs ...interface{}) bool { | func DirExists(t TestingT, path string, msgAndArgs ...interface{}) bool { | ||||
if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
@@ -1350,6 +1501,25 @@ func DirExists(t TestingT, path string, msgAndArgs ...interface{}) bool { | |||||
return true | return true | ||||
} | } | ||||
// NoDirExists checks whether a directory does not exist in the given path. | |||||
// It fails if the path points to an existing _directory_ only. | |||||
func NoDirExists(t TestingT, path string, msgAndArgs ...interface{}) bool { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
info, err := os.Lstat(path) | |||||
if err != nil { | |||||
if os.IsNotExist(err) { | |||||
return true | |||||
} | |||||
return true | |||||
} | |||||
if !info.IsDir() { | |||||
return true | |||||
} | |||||
return Fail(t, fmt.Sprintf("directory %q exists", path), msgAndArgs...) | |||||
} | |||||
// JSONEq asserts that two JSON strings are equivalent. | // JSONEq asserts that two JSON strings are equivalent. | ||||
// | // | ||||
// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) | // assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) | ||||
@@ -1439,15 +1609,6 @@ func diff(expected interface{}, actual interface{}) string { | |||||
return "\n\nDiff:\n" + diff | return "\n\nDiff:\n" + diff | ||||
} | } | ||||
// validateEqualArgs checks whether provided arguments can be safely used in the | |||||
// Equal/NotEqual functions. | |||||
func validateEqualArgs(expected, actual interface{}) error { | |||||
if isFunction(expected) || isFunction(actual) { | |||||
return errors.New("cannot take func type as argument") | |||||
} | |||||
return nil | |||||
} | |||||
func isFunction(arg interface{}) bool { | func isFunction(arg interface{}) bool { | ||||
if arg == nil { | if arg == nil { | ||||
return false | return false | ||||
@@ -1460,6 +1621,8 @@ var spewConfig = spew.ConfigState{ | |||||
DisablePointerAddresses: true, | DisablePointerAddresses: true, | ||||
DisableCapacities: true, | DisableCapacities: true, | ||||
SortKeys: true, | SortKeys: true, | ||||
DisableMethods: true, | |||||
MaxDepth: 10, | |||||
} | } | ||||
type tHelper interface { | type tHelper interface { | ||||
@@ -1475,24 +1638,137 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t | |||||
h.Helper() | h.Helper() | ||||
} | } | ||||
ch := make(chan bool, 1) | |||||
timer := time.NewTimer(waitFor) | timer := time.NewTimer(waitFor) | ||||
ticker := time.NewTicker(tick) | |||||
checkPassed := make(chan bool) | |||||
defer timer.Stop() | defer timer.Stop() | ||||
ticker := time.NewTicker(tick) | |||||
defer ticker.Stop() | defer ticker.Stop() | ||||
defer close(checkPassed) | |||||
for { | |||||
for tick := ticker.C; ; { | |||||
select { | select { | ||||
case <-timer.C: | case <-timer.C: | ||||
return Fail(t, "Condition never satisfied", msgAndArgs...) | return Fail(t, "Condition never satisfied", msgAndArgs...) | ||||
case result := <-checkPassed: | |||||
if result { | |||||
case <-tick: | |||||
tick = nil | |||||
go func() { ch <- condition() }() | |||||
case v := <-ch: | |||||
if v { | |||||
return true | return true | ||||
} | } | ||||
case <-ticker.C: | |||||
go func() { | |||||
checkPassed <- condition() | |||||
}() | |||||
tick = ticker.C | |||||
} | |||||
} | |||||
} | |||||
// Never asserts that the given condition doesn't satisfy in waitFor time, | |||||
// periodically checking the target function each tick. | |||||
// | |||||
// assert.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond) | |||||
func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
ch := make(chan bool, 1) | |||||
timer := time.NewTimer(waitFor) | |||||
defer timer.Stop() | |||||
ticker := time.NewTicker(tick) | |||||
defer ticker.Stop() | |||||
for tick := ticker.C; ; { | |||||
select { | |||||
case <-timer.C: | |||||
return true | |||||
case <-tick: | |||||
tick = nil | |||||
go func() { ch <- condition() }() | |||||
case v := <-ch: | |||||
if v { | |||||
return Fail(t, "Condition satisfied", msgAndArgs...) | |||||
} | |||||
tick = ticker.C | |||||
} | } | ||||
} | } | ||||
} | } | ||||
// ErrorIs asserts that at least one of the errors in err's chain matches target. | |||||
// This is a wrapper for errors.Is. | |||||
func ErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
if errors.Is(err, target) { | |||||
return true | |||||
} | |||||
var expectedText string | |||||
if target != nil { | |||||
expectedText = target.Error() | |||||
} | |||||
chain := buildErrorChainString(err) | |||||
return Fail(t, fmt.Sprintf("Target error should be in err chain:\n"+ | |||||
"expected: %q\n"+ | |||||
"in chain: %s", expectedText, chain, | |||||
), msgAndArgs...) | |||||
} | |||||
// NotErrorIs asserts that at none of the errors in err's chain matches target. | |||||
// This is a wrapper for errors.Is. | |||||
func NotErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
if !errors.Is(err, target) { | |||||
return true | |||||
} | |||||
var expectedText string | |||||
if target != nil { | |||||
expectedText = target.Error() | |||||
} | |||||
chain := buildErrorChainString(err) | |||||
return Fail(t, fmt.Sprintf("Target error should not be in err chain:\n"+ | |||||
"found: %q\n"+ | |||||
"in chain: %s", expectedText, chain, | |||||
), msgAndArgs...) | |||||
} | |||||
// ErrorAs asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. | |||||
// This is a wrapper for errors.As. | |||||
func ErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{}) bool { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
if errors.As(err, target) { | |||||
return true | |||||
} | |||||
chain := buildErrorChainString(err) | |||||
return Fail(t, fmt.Sprintf("Should be in error chain:\n"+ | |||||
"expected: %q\n"+ | |||||
"in chain: %s", target, chain, | |||||
), msgAndArgs...) | |||||
} | |||||
func buildErrorChainString(err error) string { | |||||
if err == nil { | |||||
return "" | |||||
} | |||||
e := errors.Unwrap(err) | |||||
chain := fmt.Sprintf("%q", err.Error()) | |||||
for e != nil { | |||||
chain += fmt.Sprintf("\n\t%q", e.Error()) | |||||
e = errors.Unwrap(e) | |||||
} | |||||
return chain | |||||
} |
@@ -13,4 +13,4 @@ func New(t TestingT) *Assertions { | |||||
} | } | ||||
} | } | ||||
//go:generate go run ../_codegen/main.go -output-package=assert -template=assertion_forward.go.tmpl -include-format-funcs | |||||
//go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=assert -template=assertion_forward.go.tmpl -include-format-funcs" |
@@ -33,7 +33,6 @@ func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, value | |||||
code, err := httpCode(handler, method, url, values) | code, err := httpCode(handler, method, url, values) | ||||
if err != nil { | if err != nil { | ||||
Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err)) | Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err)) | ||||
return false | |||||
} | } | ||||
isSuccessCode := code >= http.StatusOK && code <= http.StatusPartialContent | isSuccessCode := code >= http.StatusOK && code <= http.StatusPartialContent | ||||
@@ -56,7 +55,6 @@ func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, valu | |||||
code, err := httpCode(handler, method, url, values) | code, err := httpCode(handler, method, url, values) | ||||
if err != nil { | if err != nil { | ||||
Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err)) | Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err)) | ||||
return false | |||||
} | } | ||||
isRedirectCode := code >= http.StatusMultipleChoices && code <= http.StatusTemporaryRedirect | isRedirectCode := code >= http.StatusMultipleChoices && code <= http.StatusTemporaryRedirect | ||||
@@ -79,7 +77,6 @@ func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values | |||||
code, err := httpCode(handler, method, url, values) | code, err := httpCode(handler, method, url, values) | ||||
if err != nil { | if err != nil { | ||||
Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err)) | Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err)) | ||||
return false | |||||
} | } | ||||
isErrorCode := code >= http.StatusBadRequest | isErrorCode := code >= http.StatusBadRequest | ||||
@@ -90,6 +87,28 @@ func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values | |||||
return isErrorCode | return isErrorCode | ||||
} | } | ||||
// HTTPStatusCode asserts that a specified handler returns a specified status code. | |||||
// | |||||
// assert.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501) | |||||
// | |||||
// Returns whether the assertion was successful (true) or not (false). | |||||
func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) bool { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
code, err := httpCode(handler, method, url, values) | |||||
if err != nil { | |||||
Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err)) | |||||
} | |||||
successful := code == statuscode | |||||
if !successful { | |||||
Fail(t, fmt.Sprintf("Expected HTTP status code %d for %q but received %d", statuscode, url+"?"+values.Encode(), code)) | |||||
} | |||||
return successful | |||||
} | |||||
// HTTPBody is a helper that returns HTTP body of the response. It returns | // HTTPBody is a helper that returns HTTP body of the response. It returns | ||||
// empty string if building a new request fails. | // empty string if building a new request fails. | ||||
func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) string { | func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) string { | ||||
@@ -13,4 +13,4 @@ func New(t TestingT) *Assertions { | |||||
} | } | ||||
} | } | ||||
//go:generate go run ../_codegen/main.go -output-package=require -template=require_forward.go.tmpl -include-format-funcs | |||||
//go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=require -template=require_forward.go.tmpl -include-format-funcs" |
@@ -66,7 +66,8 @@ func Containsf(t TestingT, s interface{}, contains interface{}, msg string, args | |||||
t.FailNow() | t.FailNow() | ||||
} | } | ||||
// DirExists checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. | |||||
// DirExists checks whether a directory exists in the given path. It also fails | |||||
// if the path is a file rather a directory or there is an error checking whether it exists. | |||||
func DirExists(t TestingT, path string, msgAndArgs ...interface{}) { | func DirExists(t TestingT, path string, msgAndArgs ...interface{}) { | ||||
if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
@@ -77,7 +78,8 @@ func DirExists(t TestingT, path string, msgAndArgs ...interface{}) { | |||||
t.FailNow() | t.FailNow() | ||||
} | } | ||||
// DirExistsf checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. | |||||
// DirExistsf checks whether a directory exists in the given path. It also fails | |||||
// if the path is a file rather a directory or there is an error checking whether it exists. | |||||
func DirExistsf(t TestingT, path string, msg string, args ...interface{}) { | func DirExistsf(t TestingT, path string, msg string, args ...interface{}) { | ||||
if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
@@ -210,7 +212,7 @@ func EqualValues(t TestingT, expected interface{}, actual interface{}, msgAndArg | |||||
// EqualValuesf asserts that two objects are equal or convertable to the same types | // EqualValuesf asserts that two objects are equal or convertable to the same types | ||||
// and equal. | // and equal. | ||||
// | // | ||||
// assert.EqualValuesf(t, uint32(123, "error message %s", "formatted"), int32(123)) | |||||
// assert.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted") | |||||
func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { | func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { | ||||
if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
@@ -254,6 +256,54 @@ func Error(t TestingT, err error, msgAndArgs ...interface{}) { | |||||
t.FailNow() | t.FailNow() | ||||
} | } | ||||
// ErrorAs asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. | |||||
// This is a wrapper for errors.As. | |||||
func ErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{}) { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
if assert.ErrorAs(t, err, target, msgAndArgs...) { | |||||
return | |||||
} | |||||
t.FailNow() | |||||
} | |||||
// ErrorAsf asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. | |||||
// This is a wrapper for errors.As. | |||||
func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...interface{}) { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
if assert.ErrorAsf(t, err, target, msg, args...) { | |||||
return | |||||
} | |||||
t.FailNow() | |||||
} | |||||
// ErrorIs asserts that at least one of the errors in err's chain matches target. | |||||
// This is a wrapper for errors.Is. | |||||
func ErrorIs(t TestingT, err error, target error, msgAndArgs ...interface{}) { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
if assert.ErrorIs(t, err, target, msgAndArgs...) { | |||||
return | |||||
} | |||||
t.FailNow() | |||||
} | |||||
// ErrorIsf asserts that at least one of the errors in err's chain matches target. | |||||
// This is a wrapper for errors.Is. | |||||
func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
if assert.ErrorIsf(t, err, target, msg, args...) { | |||||
return | |||||
} | |||||
t.FailNow() | |||||
} | |||||
// Errorf asserts that a function returned an error (i.e. not `nil`). | // Errorf asserts that a function returned an error (i.e. not `nil`). | ||||
// | // | ||||
// actualObj, err := SomeFunction() | // actualObj, err := SomeFunction() | ||||
@@ -275,12 +325,12 @@ func Errorf(t TestingT, err error, msg string, args ...interface{}) { | |||||
// | // | ||||
// assert.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond) | // assert.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond) | ||||
func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { | func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { | ||||
if assert.Eventually(t, condition, waitFor, tick, msgAndArgs...) { | |||||
return | |||||
} | |||||
if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
} | } | ||||
if assert.Eventually(t, condition, waitFor, tick, msgAndArgs...) { | |||||
return | |||||
} | |||||
t.FailNow() | t.FailNow() | ||||
} | } | ||||
@@ -289,12 +339,12 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t | |||||
// | // | ||||
// assert.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") | // assert.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") | ||||
func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { | func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { | ||||
if assert.Eventuallyf(t, condition, waitFor, tick, msg, args...) { | |||||
return | |||||
} | |||||
if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
} | } | ||||
if assert.Eventuallyf(t, condition, waitFor, tick, msg, args...) { | |||||
return | |||||
} | |||||
t.FailNow() | t.FailNow() | ||||
} | } | ||||
@@ -313,7 +363,7 @@ func Exactly(t TestingT, expected interface{}, actual interface{}, msgAndArgs .. | |||||
// Exactlyf asserts that two objects are equal in value and type. | // Exactlyf asserts that two objects are equal in value and type. | ||||
// | // | ||||
// assert.Exactlyf(t, int32(123, "error message %s", "formatted"), int64(123)) | |||||
// assert.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted") | |||||
func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { | func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { | ||||
if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
@@ -394,7 +444,8 @@ func Falsef(t TestingT, value bool, msg string, args ...interface{}) { | |||||
t.FailNow() | t.FailNow() | ||||
} | } | ||||
// FileExists checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. | |||||
// FileExists checks whether a file exists in the given path. It also fails if | |||||
// the path points to a directory or there is an error when trying to check the file. | |||||
func FileExists(t TestingT, path string, msgAndArgs ...interface{}) { | func FileExists(t TestingT, path string, msgAndArgs ...interface{}) { | ||||
if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
@@ -405,7 +456,8 @@ func FileExists(t TestingT, path string, msgAndArgs ...interface{}) { | |||||
t.FailNow() | t.FailNow() | ||||
} | } | ||||
// FileExistsf checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. | |||||
// FileExistsf checks whether a file exists in the given path. It also fails if | |||||
// the path points to a directory or there is an error when trying to check the file. | |||||
func FileExistsf(t TestingT, path string, msg string, args ...interface{}) { | func FileExistsf(t TestingT, path string, msg string, args ...interface{}) { | ||||
if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
@@ -466,7 +518,7 @@ func GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, arg | |||||
// Greaterf asserts that the first element is greater than the second | // Greaterf asserts that the first element is greater than the second | ||||
// | // | ||||
// assert.Greaterf(t, 2, 1, "error message %s", "formatted") | // assert.Greaterf(t, 2, 1, "error message %s", "formatted") | ||||
// assert.Greaterf(t, float64(2, "error message %s", "formatted"), float64(1)) | |||||
// assert.Greaterf(t, float64(2), float64(1), "error message %s", "formatted") | |||||
// assert.Greaterf(t, "b", "a", "error message %s", "formatted") | // assert.Greaterf(t, "b", "a", "error message %s", "formatted") | ||||
func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { | func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { | ||||
if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
@@ -561,7 +613,7 @@ func HTTPError(t TestingT, handler http.HandlerFunc, method string, url string, | |||||
// | // | ||||
// assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} | // assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} | ||||
// | // | ||||
// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false). | |||||
// Returns whether the assertion was successful (true) or not (false). | |||||
func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { | func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { | ||||
if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
@@ -591,7 +643,7 @@ func HTTPRedirect(t TestingT, handler http.HandlerFunc, method string, url strin | |||||
// | // | ||||
// assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} | // assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} | ||||
// | // | ||||
// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false). | |||||
// Returns whether the assertion was successful (true) or not (false). | |||||
func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { | func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { | ||||
if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
@@ -602,6 +654,36 @@ func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url stri | |||||
t.FailNow() | t.FailNow() | ||||
} | } | ||||
// HTTPStatusCode asserts that a specified handler returns a specified status code. | |||||
// | |||||
// assert.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501) | |||||
// | |||||
// Returns whether the assertion was successful (true) or not (false). | |||||
func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
if assert.HTTPStatusCode(t, handler, method, url, values, statuscode, msgAndArgs...) { | |||||
return | |||||
} | |||||
t.FailNow() | |||||
} | |||||
// HTTPStatusCodef asserts that a specified handler returns a specified status code. | |||||
// | |||||
// assert.HTTPStatusCodef(t, myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") | |||||
// | |||||
// Returns whether the assertion was successful (true) or not (false). | |||||
func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
if assert.HTTPStatusCodef(t, handler, method, url, values, statuscode, msg, args...) { | |||||
return | |||||
} | |||||
t.FailNow() | |||||
} | |||||
// HTTPSuccess asserts that a specified handler returns a success status code. | // HTTPSuccess asserts that a specified handler returns a success status code. | ||||
// | // | ||||
// assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) | // assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) | ||||
@@ -647,7 +729,7 @@ func Implements(t TestingT, interfaceObject interface{}, object interface{}, msg | |||||
// Implementsf asserts that an object is implemented by the specified interface. | // Implementsf asserts that an object is implemented by the specified interface. | ||||
// | // | ||||
// assert.Implementsf(t, (*MyInterface, "error message %s", "formatted")(nil), new(MyObject)) | |||||
// assert.Implementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted") | |||||
func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) { | func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) { | ||||
if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
@@ -660,7 +742,7 @@ func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, ms | |||||
// InDelta asserts that the two numerals are within delta of each other. | // InDelta asserts that the two numerals are within delta of each other. | ||||
// | // | ||||
// assert.InDelta(t, math.Pi, (22 / 7.0), 0.01) | |||||
// assert.InDelta(t, math.Pi, 22/7.0, 0.01) | |||||
func InDelta(t TestingT, expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { | func InDelta(t TestingT, expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { | ||||
if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
@@ -717,7 +799,7 @@ func InDeltaSlicef(t TestingT, expected interface{}, actual interface{}, delta f | |||||
// InDeltaf asserts that the two numerals are within delta of each other. | // InDeltaf asserts that the two numerals are within delta of each other. | ||||
// | // | ||||
// assert.InDeltaf(t, math.Pi, (22 / 7.0, "error message %s", "formatted"), 0.01) | |||||
// assert.InDeltaf(t, math.Pi, 22/7.0, 0.01, "error message %s", "formatted") | |||||
func InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { | func InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { | ||||
if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
@@ -772,71 +854,169 @@ func InEpsilonf(t TestingT, expected interface{}, actual interface{}, epsilon fl | |||||
t.FailNow() | t.FailNow() | ||||
} | } | ||||
// IsType asserts that the specified objects are of the same type. | |||||
func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) { | |||||
// IsDecreasing asserts that the collection is decreasing | |||||
// | |||||
// assert.IsDecreasing(t, []int{2, 1, 0}) | |||||
// assert.IsDecreasing(t, []float{2, 1}) | |||||
// assert.IsDecreasing(t, []string{"b", "a"}) | |||||
func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { | |||||
if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
} | } | ||||
if assert.IsType(t, expectedType, object, msgAndArgs...) { | |||||
if assert.IsDecreasing(t, object, msgAndArgs...) { | |||||
return | return | ||||
} | } | ||||
t.FailNow() | t.FailNow() | ||||
} | } | ||||
// IsTypef asserts that the specified objects are of the same type. | |||||
func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg string, args ...interface{}) { | |||||
// IsDecreasingf asserts that the collection is decreasing | |||||
// | |||||
// assert.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted") | |||||
// assert.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted") | |||||
// assert.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted") | |||||
func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { | |||||
if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
} | } | ||||
if assert.IsTypef(t, expectedType, object, msg, args...) { | |||||
if assert.IsDecreasingf(t, object, msg, args...) { | |||||
return | return | ||||
} | } | ||||
t.FailNow() | t.FailNow() | ||||
} | } | ||||
// JSONEq asserts that two JSON strings are equivalent. | |||||
// IsIncreasing asserts that the collection is increasing | |||||
// | // | ||||
// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) | |||||
func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) { | |||||
// assert.IsIncreasing(t, []int{1, 2, 3}) | |||||
// assert.IsIncreasing(t, []float{1, 2}) | |||||
// assert.IsIncreasing(t, []string{"a", "b"}) | |||||
func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { | |||||
if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
} | } | ||||
if assert.JSONEq(t, expected, actual, msgAndArgs...) { | |||||
if assert.IsIncreasing(t, object, msgAndArgs...) { | |||||
return | return | ||||
} | } | ||||
t.FailNow() | t.FailNow() | ||||
} | } | ||||
// JSONEqf asserts that two JSON strings are equivalent. | |||||
// IsIncreasingf asserts that the collection is increasing | |||||
// | // | ||||
// assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") | |||||
func JSONEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) { | |||||
// assert.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted") | |||||
// assert.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted") | |||||
// assert.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted") | |||||
func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { | |||||
if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
} | } | ||||
if assert.JSONEqf(t, expected, actual, msg, args...) { | |||||
if assert.IsIncreasingf(t, object, msg, args...) { | |||||
return | return | ||||
} | } | ||||
t.FailNow() | t.FailNow() | ||||
} | } | ||||
// YAMLEq asserts that two YAML strings are equivalent. | |||||
func YAMLEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) { | |||||
// IsNonDecreasing asserts that the collection is not decreasing | |||||
// | |||||
// assert.IsNonDecreasing(t, []int{1, 1, 2}) | |||||
// assert.IsNonDecreasing(t, []float{1, 2}) | |||||
// assert.IsNonDecreasing(t, []string{"a", "b"}) | |||||
func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { | |||||
if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
} | } | ||||
if assert.YAMLEq(t, expected, actual, msgAndArgs...) { | |||||
if assert.IsNonDecreasing(t, object, msgAndArgs...) { | |||||
return | return | ||||
} | } | ||||
t.FailNow() | t.FailNow() | ||||
} | } | ||||
// YAMLEqf asserts that two YAML strings are equivalent. | |||||
func YAMLEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) { | |||||
// IsNonDecreasingf asserts that the collection is not decreasing | |||||
// | |||||
// assert.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted") | |||||
// assert.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted") | |||||
// assert.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted") | |||||
func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { | |||||
if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
} | } | ||||
if assert.YAMLEqf(t, expected, actual, msg, args...) { | |||||
if assert.IsNonDecreasingf(t, object, msg, args...) { | |||||
return | |||||
} | |||||
t.FailNow() | |||||
} | |||||
// IsNonIncreasing asserts that the collection is not increasing | |||||
// | |||||
// assert.IsNonIncreasing(t, []int{2, 1, 1}) | |||||
// assert.IsNonIncreasing(t, []float{2, 1}) | |||||
// assert.IsNonIncreasing(t, []string{"b", "a"}) | |||||
func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
if assert.IsNonIncreasing(t, object, msgAndArgs...) { | |||||
return | |||||
} | |||||
t.FailNow() | |||||
} | |||||
// IsNonIncreasingf asserts that the collection is not increasing | |||||
// | |||||
// assert.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted") | |||||
// assert.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted") | |||||
// assert.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted") | |||||
func IsNonIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
if assert.IsNonIncreasingf(t, object, msg, args...) { | |||||
return | |||||
} | |||||
t.FailNow() | |||||
} | |||||
// IsType asserts that the specified objects are of the same type. | |||||
func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
if assert.IsType(t, expectedType, object, msgAndArgs...) { | |||||
return | |||||
} | |||||
t.FailNow() | |||||
} | |||||
// IsTypef asserts that the specified objects are of the same type. | |||||
func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg string, args ...interface{}) { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
if assert.IsTypef(t, expectedType, object, msg, args...) { | |||||
return | |||||
} | |||||
t.FailNow() | |||||
} | |||||
// JSONEq asserts that two JSON strings are equivalent. | |||||
// | |||||
// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) | |||||
func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
if assert.JSONEq(t, expected, actual, msgAndArgs...) { | |||||
return | |||||
} | |||||
t.FailNow() | |||||
} | |||||
// JSONEqf asserts that two JSON strings are equivalent. | |||||
// | |||||
// assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") | |||||
func JSONEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
if assert.JSONEqf(t, expected, actual, msg, args...) { | |||||
return | return | ||||
} | } | ||||
t.FailNow() | t.FailNow() | ||||
@@ -920,7 +1100,7 @@ func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args . | |||||
// Lessf asserts that the first element is less than the second | // Lessf asserts that the first element is less than the second | ||||
// | // | ||||
// assert.Lessf(t, 1, 2, "error message %s", "formatted") | // assert.Lessf(t, 1, 2, "error message %s", "formatted") | ||||
// assert.Lessf(t, float64(1, "error message %s", "formatted"), float64(2)) | |||||
// assert.Lessf(t, float64(1), float64(2), "error message %s", "formatted") | |||||
// assert.Lessf(t, "a", "b", "error message %s", "formatted") | // assert.Lessf(t, "a", "b", "error message %s", "formatted") | ||||
func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { | func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { | ||||
if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
@@ -932,6 +1112,62 @@ func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...inter | |||||
t.FailNow() | t.FailNow() | ||||
} | } | ||||
// Negative asserts that the specified element is negative | |||||
// | |||||
// assert.Negative(t, -1) | |||||
// assert.Negative(t, -1.23) | |||||
func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
if assert.Negative(t, e, msgAndArgs...) { | |||||
return | |||||
} | |||||
t.FailNow() | |||||
} | |||||
// Negativef asserts that the specified element is negative | |||||
// | |||||
// assert.Negativef(t, -1, "error message %s", "formatted") | |||||
// assert.Negativef(t, -1.23, "error message %s", "formatted") | |||||
func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
if assert.Negativef(t, e, msg, args...) { | |||||
return | |||||
} | |||||
t.FailNow() | |||||
} | |||||
// Never asserts that the given condition doesn't satisfy in waitFor time, | |||||
// periodically checking the target function each tick. | |||||
// | |||||
// assert.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond) | |||||
func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
if assert.Never(t, condition, waitFor, tick, msgAndArgs...) { | |||||
return | |||||
} | |||||
t.FailNow() | |||||
} | |||||
// Neverf asserts that the given condition doesn't satisfy in waitFor time, | |||||
// periodically checking the target function each tick. | |||||
// | |||||
// assert.Neverf(t, func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") | |||||
func Neverf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
if assert.Neverf(t, condition, waitFor, tick, msg, args...) { | |||||
return | |||||
} | |||||
t.FailNow() | |||||
} | |||||
// Nil asserts that the specified object is nil. | // Nil asserts that the specified object is nil. | ||||
// | // | ||||
// assert.Nil(t, err) | // assert.Nil(t, err) | ||||
@@ -958,6 +1194,30 @@ func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) { | |||||
t.FailNow() | t.FailNow() | ||||
} | } | ||||
// NoDirExists checks whether a directory does not exist in the given path. | |||||
// It fails if the path points to an existing _directory_ only. | |||||
func NoDirExists(t TestingT, path string, msgAndArgs ...interface{}) { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
if assert.NoDirExists(t, path, msgAndArgs...) { | |||||
return | |||||
} | |||||
t.FailNow() | |||||
} | |||||
// NoDirExistsf checks whether a directory does not exist in the given path. | |||||
// It fails if the path points to an existing _directory_ only. | |||||
func NoDirExistsf(t TestingT, path string, msg string, args ...interface{}) { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
if assert.NoDirExistsf(t, path, msg, args...) { | |||||
return | |||||
} | |||||
t.FailNow() | |||||
} | |||||
// NoError asserts that a function returned no error (i.e. `nil`). | // NoError asserts that a function returned no error (i.e. `nil`). | ||||
// | // | ||||
// actualObj, err := SomeFunction() | // actualObj, err := SomeFunction() | ||||
@@ -990,6 +1250,30 @@ func NoErrorf(t TestingT, err error, msg string, args ...interface{}) { | |||||
t.FailNow() | t.FailNow() | ||||
} | } | ||||
// NoFileExists checks whether a file does not exist in a given path. It fails | |||||
// if the path points to an existing _file_ only. | |||||
func NoFileExists(t TestingT, path string, msgAndArgs ...interface{}) { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
if assert.NoFileExists(t, path, msgAndArgs...) { | |||||
return | |||||
} | |||||
t.FailNow() | |||||
} | |||||
// NoFileExistsf checks whether a file does not exist in a given path. It fails | |||||
// if the path points to an existing _file_ only. | |||||
func NoFileExistsf(t TestingT, path string, msg string, args ...interface{}) { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
if assert.NoFileExistsf(t, path, msg, args...) { | |||||
return | |||||
} | |||||
t.FailNow() | |||||
} | |||||
// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the | // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the | ||||
// specified substring or element. | // specified substring or element. | ||||
// | // | ||||
@@ -1070,6 +1354,32 @@ func NotEqual(t TestingT, expected interface{}, actual interface{}, msgAndArgs . | |||||
t.FailNow() | t.FailNow() | ||||
} | } | ||||
// NotEqualValues asserts that two objects are not equal even when converted to the same type | |||||
// | |||||
// assert.NotEqualValues(t, obj1, obj2) | |||||
func NotEqualValues(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
if assert.NotEqualValues(t, expected, actual, msgAndArgs...) { | |||||
return | |||||
} | |||||
t.FailNow() | |||||
} | |||||
// NotEqualValuesf asserts that two objects are not equal even when converted to the same type | |||||
// | |||||
// assert.NotEqualValuesf(t, obj1, obj2, "error message %s", "formatted") | |||||
func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
if assert.NotEqualValuesf(t, expected, actual, msg, args...) { | |||||
return | |||||
} | |||||
t.FailNow() | |||||
} | |||||
// NotEqualf asserts that the specified values are NOT equal. | // NotEqualf asserts that the specified values are NOT equal. | ||||
// | // | ||||
// assert.NotEqualf(t, obj1, obj2, "error message %s", "formatted") | // assert.NotEqualf(t, obj1, obj2, "error message %s", "formatted") | ||||
@@ -1086,6 +1396,30 @@ func NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string, | |||||
t.FailNow() | t.FailNow() | ||||
} | } | ||||
// NotErrorIs asserts that at none of the errors in err's chain matches target. | |||||
// This is a wrapper for errors.Is. | |||||
func NotErrorIs(t TestingT, err error, target error, msgAndArgs ...interface{}) { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
if assert.NotErrorIs(t, err, target, msgAndArgs...) { | |||||
return | |||||
} | |||||
t.FailNow() | |||||
} | |||||
// NotErrorIsf asserts that at none of the errors in err's chain matches target. | |||||
// This is a wrapper for errors.Is. | |||||
func NotErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
if assert.NotErrorIsf(t, err, target, msg, args...) { | |||||
return | |||||
} | |||||
t.FailNow() | |||||
} | |||||
// NotNil asserts that the specified object is not nil. | // NotNil asserts that the specified object is not nil. | ||||
// | // | ||||
// assert.NotNil(t, err) | // assert.NotNil(t, err) | ||||
@@ -1154,7 +1488,7 @@ func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interf | |||||
// NotRegexpf asserts that a specified regexp does not match a string. | // NotRegexpf asserts that a specified regexp does not match a string. | ||||
// | // | ||||
// assert.NotRegexpf(t, regexp.MustCompile("starts", "error message %s", "formatted"), "it's starting") | |||||
// assert.NotRegexpf(t, regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") | |||||
// assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted") | // assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted") | ||||
func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) { | func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) { | ||||
if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
@@ -1166,6 +1500,38 @@ func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args .. | |||||
t.FailNow() | t.FailNow() | ||||
} | } | ||||
// NotSame asserts that two pointers do not reference the same object. | |||||
// | |||||
// assert.NotSame(t, ptr1, ptr2) | |||||
// | |||||
// Both arguments must be pointer variables. Pointer variable sameness is | |||||
// determined based on the equality of both type and value. | |||||
func NotSame(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
if assert.NotSame(t, expected, actual, msgAndArgs...) { | |||||
return | |||||
} | |||||
t.FailNow() | |||||
} | |||||
// NotSamef asserts that two pointers do not reference the same object. | |||||
// | |||||
// assert.NotSamef(t, ptr1, ptr2, "error message %s", "formatted") | |||||
// | |||||
// Both arguments must be pointer variables. Pointer variable sameness is | |||||
// determined based on the equality of both type and value. | |||||
func NotSamef(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
if assert.NotSamef(t, expected, actual, msg, args...) { | |||||
return | |||||
} | |||||
t.FailNow() | |||||
} | |||||
// NotSubset asserts that the specified list(array, slice...) contains not all | // NotSubset asserts that the specified list(array, slice...) contains not all | ||||
// elements given in the specified subset(array, slice...). | // elements given in the specified subset(array, slice...). | ||||
// | // | ||||
@@ -1229,6 +1595,36 @@ func Panics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) { | |||||
t.FailNow() | t.FailNow() | ||||
} | } | ||||
// PanicsWithError asserts that the code inside the specified PanicTestFunc | |||||
// panics, and that the recovered panic value is an error that satisfies the | |||||
// EqualError comparison. | |||||
// | |||||
// assert.PanicsWithError(t, "crazy error", func(){ GoCrazy() }) | |||||
func PanicsWithError(t TestingT, errString string, f assert.PanicTestFunc, msgAndArgs ...interface{}) { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
if assert.PanicsWithError(t, errString, f, msgAndArgs...) { | |||||
return | |||||
} | |||||
t.FailNow() | |||||
} | |||||
// PanicsWithErrorf asserts that the code inside the specified PanicTestFunc | |||||
// panics, and that the recovered panic value is an error that satisfies the | |||||
// EqualError comparison. | |||||
// | |||||
// assert.PanicsWithErrorf(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") | |||||
func PanicsWithErrorf(t TestingT, errString string, f assert.PanicTestFunc, msg string, args ...interface{}) { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
if assert.PanicsWithErrorf(t, errString, f, msg, args...) { | |||||
return | |||||
} | |||||
t.FailNow() | |||||
} | |||||
// PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that | // PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that | ||||
// the recovered panic value equals the expected panic value. | // the recovered panic value equals the expected panic value. | ||||
// | // | ||||
@@ -1270,6 +1666,34 @@ func Panicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interface{} | |||||
t.FailNow() | t.FailNow() | ||||
} | } | ||||
// Positive asserts that the specified element is positive | |||||
// | |||||
// assert.Positive(t, 1) | |||||
// assert.Positive(t, 1.23) | |||||
func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
if assert.Positive(t, e, msgAndArgs...) { | |||||
return | |||||
} | |||||
t.FailNow() | |||||
} | |||||
// Positivef asserts that the specified element is positive | |||||
// | |||||
// assert.Positivef(t, 1, "error message %s", "formatted") | |||||
// assert.Positivef(t, 1.23, "error message %s", "formatted") | |||||
func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
if assert.Positivef(t, e, msg, args...) { | |||||
return | |||||
} | |||||
t.FailNow() | |||||
} | |||||
// Regexp asserts that a specified regexp matches a string. | // Regexp asserts that a specified regexp matches a string. | ||||
// | // | ||||
// assert.Regexp(t, regexp.MustCompile("start"), "it's starting") | // assert.Regexp(t, regexp.MustCompile("start"), "it's starting") | ||||
@@ -1286,7 +1710,7 @@ func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface | |||||
// Regexpf asserts that a specified regexp matches a string. | // Regexpf asserts that a specified regexp matches a string. | ||||
// | // | ||||
// assert.Regexpf(t, regexp.MustCompile("start", "error message %s", "formatted"), "it's starting") | |||||
// assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") | |||||
// assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted") | // assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted") | ||||
func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) { | func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) { | ||||
if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
@@ -1410,6 +1834,28 @@ func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta tim | |||||
t.FailNow() | t.FailNow() | ||||
} | } | ||||
// YAMLEq asserts that two YAML strings are equivalent. | |||||
func YAMLEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
if assert.YAMLEq(t, expected, actual, msgAndArgs...) { | |||||
return | |||||
} | |||||
t.FailNow() | |||||
} | |||||
// YAMLEqf asserts that two YAML strings are equivalent. | |||||
func YAMLEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) { | |||||
if h, ok := t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
if assert.YAMLEqf(t, expected, actual, msg, args...) { | |||||
return | |||||
} | |||||
t.FailNow() | |||||
} | |||||
// Zero asserts that i is the zero value for its type. | // Zero asserts that i is the zero value for its type. | ||||
func Zero(t TestingT, i interface{}, msgAndArgs ...interface{}) { | func Zero(t TestingT, i interface{}, msgAndArgs ...interface{}) { | ||||
if h, ok := t.(tHelper); ok { | if h, ok := t.(tHelper); ok { | ||||
@@ -54,7 +54,8 @@ func (a *Assertions) Containsf(s interface{}, contains interface{}, msg string, | |||||
Containsf(a.t, s, contains, msg, args...) | Containsf(a.t, s, contains, msg, args...) | ||||
} | } | ||||
// DirExists checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. | |||||
// DirExists checks whether a directory exists in the given path. It also fails | |||||
// if the path is a file rather a directory or there is an error checking whether it exists. | |||||
func (a *Assertions) DirExists(path string, msgAndArgs ...interface{}) { | func (a *Assertions) DirExists(path string, msgAndArgs ...interface{}) { | ||||
if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
@@ -62,7 +63,8 @@ func (a *Assertions) DirExists(path string, msgAndArgs ...interface{}) { | |||||
DirExists(a.t, path, msgAndArgs...) | DirExists(a.t, path, msgAndArgs...) | ||||
} | } | ||||
// DirExistsf checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. | |||||
// DirExistsf checks whether a directory exists in the given path. It also fails | |||||
// if the path is a file rather a directory or there is an error checking whether it exists. | |||||
func (a *Assertions) DirExistsf(path string, msg string, args ...interface{}) { | func (a *Assertions) DirExistsf(path string, msg string, args ...interface{}) { | ||||
if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
@@ -168,7 +170,7 @@ func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAn | |||||
// EqualValuesf asserts that two objects are equal or convertable to the same types | // EqualValuesf asserts that two objects are equal or convertable to the same types | ||||
// and equal. | // and equal. | ||||
// | // | ||||
// a.EqualValuesf(uint32(123, "error message %s", "formatted"), int32(123)) | |||||
// a.EqualValuesf(uint32(123), int32(123), "error message %s", "formatted") | |||||
func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) { | func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) { | ||||
if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
@@ -203,6 +205,42 @@ func (a *Assertions) Error(err error, msgAndArgs ...interface{}) { | |||||
Error(a.t, err, msgAndArgs...) | Error(a.t, err, msgAndArgs...) | ||||
} | } | ||||
// ErrorAs asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. | |||||
// This is a wrapper for errors.As. | |||||
func (a *Assertions) ErrorAs(err error, target interface{}, msgAndArgs ...interface{}) { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
ErrorAs(a.t, err, target, msgAndArgs...) | |||||
} | |||||
// ErrorAsf asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. | |||||
// This is a wrapper for errors.As. | |||||
func (a *Assertions) ErrorAsf(err error, target interface{}, msg string, args ...interface{}) { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
ErrorAsf(a.t, err, target, msg, args...) | |||||
} | |||||
// ErrorIs asserts that at least one of the errors in err's chain matches target. | |||||
// This is a wrapper for errors.Is. | |||||
func (a *Assertions) ErrorIs(err error, target error, msgAndArgs ...interface{}) { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
ErrorIs(a.t, err, target, msgAndArgs...) | |||||
} | |||||
// ErrorIsf asserts that at least one of the errors in err's chain matches target. | |||||
// This is a wrapper for errors.Is. | |||||
func (a *Assertions) ErrorIsf(err error, target error, msg string, args ...interface{}) { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
ErrorIsf(a.t, err, target, msg, args...) | |||||
} | |||||
// Errorf asserts that a function returned an error (i.e. not `nil`). | // Errorf asserts that a function returned an error (i.e. not `nil`). | ||||
// | // | ||||
// actualObj, err := SomeFunction() | // actualObj, err := SomeFunction() | ||||
@@ -250,7 +288,7 @@ func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArg | |||||
// Exactlyf asserts that two objects are equal in value and type. | // Exactlyf asserts that two objects are equal in value and type. | ||||
// | // | ||||
// a.Exactlyf(int32(123, "error message %s", "formatted"), int64(123)) | |||||
// a.Exactlyf(int32(123), int64(123), "error message %s", "formatted") | |||||
func (a *Assertions) Exactlyf(expected interface{}, actual interface{}, msg string, args ...interface{}) { | func (a *Assertions) Exactlyf(expected interface{}, actual interface{}, msg string, args ...interface{}) { | ||||
if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
@@ -310,7 +348,8 @@ func (a *Assertions) Falsef(value bool, msg string, args ...interface{}) { | |||||
Falsef(a.t, value, msg, args...) | Falsef(a.t, value, msg, args...) | ||||
} | } | ||||
// FileExists checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. | |||||
// FileExists checks whether a file exists in the given path. It also fails if | |||||
// the path points to a directory or there is an error when trying to check the file. | |||||
func (a *Assertions) FileExists(path string, msgAndArgs ...interface{}) { | func (a *Assertions) FileExists(path string, msgAndArgs ...interface{}) { | ||||
if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
@@ -318,7 +357,8 @@ func (a *Assertions) FileExists(path string, msgAndArgs ...interface{}) { | |||||
FileExists(a.t, path, msgAndArgs...) | FileExists(a.t, path, msgAndArgs...) | ||||
} | } | ||||
// FileExistsf checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. | |||||
// FileExistsf checks whether a file exists in the given path. It also fails if | |||||
// the path points to a directory or there is an error when trying to check the file. | |||||
func (a *Assertions) FileExistsf(path string, msg string, args ...interface{}) { | func (a *Assertions) FileExistsf(path string, msg string, args ...interface{}) { | ||||
if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
@@ -367,7 +407,7 @@ func (a *Assertions) GreaterOrEqualf(e1 interface{}, e2 interface{}, msg string, | |||||
// Greaterf asserts that the first element is greater than the second | // Greaterf asserts that the first element is greater than the second | ||||
// | // | ||||
// a.Greaterf(2, 1, "error message %s", "formatted") | // a.Greaterf(2, 1, "error message %s", "formatted") | ||||
// a.Greaterf(float64(2, "error message %s", "formatted"), float64(1)) | |||||
// a.Greaterf(float64(2), float64(1), "error message %s", "formatted") | |||||
// a.Greaterf("b", "a", "error message %s", "formatted") | // a.Greaterf("b", "a", "error message %s", "formatted") | ||||
func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { | func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { | ||||
if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
@@ -444,7 +484,7 @@ func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url stri | |||||
// | // | ||||
// a.HTTPErrorf(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} | // a.HTTPErrorf(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} | ||||
// | // | ||||
// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false). | |||||
// Returns whether the assertion was successful (true) or not (false). | |||||
func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { | func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { | ||||
if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
@@ -468,7 +508,7 @@ func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url s | |||||
// | // | ||||
// a.HTTPRedirectf(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} | // a.HTTPRedirectf(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} | ||||
// | // | ||||
// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false). | |||||
// Returns whether the assertion was successful (true) or not (false). | |||||
func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { | func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { | ||||
if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
@@ -476,6 +516,30 @@ func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url | |||||
HTTPRedirectf(a.t, handler, method, url, values, msg, args...) | HTTPRedirectf(a.t, handler, method, url, values, msg, args...) | ||||
} | } | ||||
// HTTPStatusCode asserts that a specified handler returns a specified status code. | |||||
// | |||||
// a.HTTPStatusCode(myHandler, "GET", "/notImplemented", nil, 501) | |||||
// | |||||
// Returns whether the assertion was successful (true) or not (false). | |||||
func (a *Assertions) HTTPStatusCode(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
HTTPStatusCode(a.t, handler, method, url, values, statuscode, msgAndArgs...) | |||||
} | |||||
// HTTPStatusCodef asserts that a specified handler returns a specified status code. | |||||
// | |||||
// a.HTTPStatusCodef(myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") | |||||
// | |||||
// Returns whether the assertion was successful (true) or not (false). | |||||
func (a *Assertions) HTTPStatusCodef(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
HTTPStatusCodef(a.t, handler, method, url, values, statuscode, msg, args...) | |||||
} | |||||
// HTTPSuccess asserts that a specified handler returns a success status code. | // HTTPSuccess asserts that a specified handler returns a success status code. | ||||
// | // | ||||
// a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil) | // a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil) | ||||
@@ -512,7 +576,7 @@ func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, | |||||
// Implementsf asserts that an object is implemented by the specified interface. | // Implementsf asserts that an object is implemented by the specified interface. | ||||
// | // | ||||
// a.Implementsf((*MyInterface, "error message %s", "formatted")(nil), new(MyObject)) | |||||
// a.Implementsf((*MyInterface)(nil), new(MyObject), "error message %s", "formatted") | |||||
func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) { | func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) { | ||||
if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
@@ -522,7 +586,7 @@ func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{} | |||||
// InDelta asserts that the two numerals are within delta of each other. | // InDelta asserts that the two numerals are within delta of each other. | ||||
// | // | ||||
// a.InDelta(math.Pi, (22 / 7.0), 0.01) | |||||
// a.InDelta(math.Pi, 22/7.0, 0.01) | |||||
func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { | func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { | ||||
if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
@@ -564,7 +628,7 @@ func (a *Assertions) InDeltaSlicef(expected interface{}, actual interface{}, del | |||||
// InDeltaf asserts that the two numerals are within delta of each other. | // InDeltaf asserts that the two numerals are within delta of each other. | ||||
// | // | ||||
// a.InDeltaf(math.Pi, (22 / 7.0, "error message %s", "formatted"), 0.01) | |||||
// a.InDeltaf(math.Pi, 22/7.0, 0.01, "error message %s", "formatted") | |||||
func (a *Assertions) InDeltaf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { | func (a *Assertions) InDeltaf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { | ||||
if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
h.Helper() | h.Helper() | ||||
@@ -604,6 +668,102 @@ func (a *Assertions) InEpsilonf(expected interface{}, actual interface{}, epsilo | |||||
InEpsilonf(a.t, expected, actual, epsilon, msg, args...) | InEpsilonf(a.t, expected, actual, epsilon, msg, args...) | ||||
} | } | ||||
// IsDecreasing asserts that the collection is decreasing | |||||
// | |||||
// a.IsDecreasing([]int{2, 1, 0}) | |||||
// a.IsDecreasing([]float{2, 1}) | |||||
// a.IsDecreasing([]string{"b", "a"}) | |||||
func (a *Assertions) IsDecreasing(object interface{}, msgAndArgs ...interface{}) { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
IsDecreasing(a.t, object, msgAndArgs...) | |||||
} | |||||
// IsDecreasingf asserts that the collection is decreasing | |||||
// | |||||
// a.IsDecreasingf([]int{2, 1, 0}, "error message %s", "formatted") | |||||
// a.IsDecreasingf([]float{2, 1}, "error message %s", "formatted") | |||||
// a.IsDecreasingf([]string{"b", "a"}, "error message %s", "formatted") | |||||
func (a *Assertions) IsDecreasingf(object interface{}, msg string, args ...interface{}) { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
IsDecreasingf(a.t, object, msg, args...) | |||||
} | |||||
// IsIncreasing asserts that the collection is increasing | |||||
// | |||||
// a.IsIncreasing([]int{1, 2, 3}) | |||||
// a.IsIncreasing([]float{1, 2}) | |||||
// a.IsIncreasing([]string{"a", "b"}) | |||||
func (a *Assertions) IsIncreasing(object interface{}, msgAndArgs ...interface{}) { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
IsIncreasing(a.t, object, msgAndArgs...) | |||||
} | |||||
// IsIncreasingf asserts that the collection is increasing | |||||
// | |||||
// a.IsIncreasingf([]int{1, 2, 3}, "error message %s", "formatted") | |||||
// a.IsIncreasingf([]float{1, 2}, "error message %s", "formatted") | |||||
// a.IsIncreasingf([]string{"a", "b"}, "error message %s", "formatted") | |||||
func (a *Assertions) IsIncreasingf(object interface{}, msg string, args ...interface{}) { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
IsIncreasingf(a.t, object, msg, args...) | |||||
} | |||||
// IsNonDecreasing asserts that the collection is not decreasing | |||||
// | |||||
// a.IsNonDecreasing([]int{1, 1, 2}) | |||||
// a.IsNonDecreasing([]float{1, 2}) | |||||
// a.IsNonDecreasing([]string{"a", "b"}) | |||||
func (a *Assertions) IsNonDecreasing(object interface{}, msgAndArgs ...interface{}) { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
IsNonDecreasing(a.t, object, msgAndArgs...) | |||||
} | |||||
// IsNonDecreasingf asserts that the collection is not decreasing | |||||
// | |||||
// a.IsNonDecreasingf([]int{1, 1, 2}, "error message %s", "formatted") | |||||
// a.IsNonDecreasingf([]float{1, 2}, "error message %s", "formatted") | |||||
// a.IsNonDecreasingf([]string{"a", "b"}, "error message %s", "formatted") | |||||
func (a *Assertions) IsNonDecreasingf(object interface{}, msg string, args ...interface{}) { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
IsNonDecreasingf(a.t, object, msg, args...) | |||||
} | |||||
// IsNonIncreasing asserts that the collection is not increasing | |||||
// | |||||
// a.IsNonIncreasing([]int{2, 1, 1}) | |||||
// a.IsNonIncreasing([]float{2, 1}) | |||||
// a.IsNonIncreasing([]string{"b", "a"}) | |||||
func (a *Assertions) IsNonIncreasing(object interface{}, msgAndArgs ...interface{}) { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
IsNonIncreasing(a.t, object, msgAndArgs...) | |||||
} | |||||
// IsNonIncreasingf asserts that the collection is not increasing | |||||
// | |||||
// a.IsNonIncreasingf([]int{2, 1, 1}, "error message %s", "formatted") | |||||
// a.IsNonIncreasingf([]float{2, 1}, "error message %s", "formatted") | |||||
// a.IsNonIncreasingf([]string{"b", "a"}, "error message %s", "formatted") | |||||
func (a *Assertions) IsNonIncreasingf(object interface{}, msg string, args ...interface{}) { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
IsNonIncreasingf(a.t, object, msg, args...) | |||||
} | |||||
// IsType asserts that the specified objects are of the same type. | // IsType asserts that the specified objects are of the same type. | ||||
func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) { | func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) { | ||||
if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
@@ -640,22 +800,6 @@ func (a *Assertions) JSONEqf(expected string, actual string, msg string, args .. | |||||
JSONEqf(a.t, expected, actual, msg, args...) | JSONEqf(a.t, expected, actual, msg, args...) | ||||
} | } | ||||
// YAMLEq asserts that two YAML strings are equivalent. | |||||
func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
YAMLEq(a.t, expected, actual, msgAndArgs...) | |||||
} | |||||
// YAMLEqf asserts that two YAML strings are equivalent. | |||||
func (a *Assertions) YAMLEqf(expected string, actual string, msg string, args ...interface{}) { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
YAMLEqf(a.t, expected, actual, msg, args...) | |||||
} | |||||
// Len asserts that the specified object has specific length. | // Len asserts that the specified object has specific length. | ||||
// Len also fails if the object has a type that len() not accept. | // Len also fails if the object has a type that len() not accept. | ||||
// | // | ||||
@@ -719,7 +863,7 @@ func (a *Assertions) LessOrEqualf(e1 interface{}, e2 interface{}, msg string, ar | |||||
// Lessf asserts that the first element is less than the second | // Lessf asserts that the first element is less than the second | ||||
// | // | ||||
// a.Lessf(1, 2, "error message %s", "formatted") | // a.Lessf(1, 2, "error message %s", "formatted") | ||||
// a.Lessf(float64(1, "error message %s", "formatted"), float64(2)) | |||||
// a.Lessf(float64(1), float64(2), "error message %s", "formatted") | |||||
// a.Lessf("a", "b", "error message %s", "formatted") | // a.Lessf("a", "b", "error message %s", "formatted") | ||||
func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { | func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { | ||||
if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
@@ -728,6 +872,50 @@ func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...i | |||||
Lessf(a.t, e1, e2, msg, args...) | Lessf(a.t, e1, e2, msg, args...) | ||||
} | } | ||||
// Negative asserts that the specified element is negative | |||||
// | |||||
// a.Negative(-1) | |||||
// a.Negative(-1.23) | |||||
func (a *Assertions) Negative(e interface{}, msgAndArgs ...interface{}) { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
Negative(a.t, e, msgAndArgs...) | |||||
} | |||||
// Negativef asserts that the specified element is negative | |||||
// | |||||
// a.Negativef(-1, "error message %s", "formatted") | |||||
// a.Negativef(-1.23, "error message %s", "formatted") | |||||
func (a *Assertions) Negativef(e interface{}, msg string, args ...interface{}) { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
Negativef(a.t, e, msg, args...) | |||||
} | |||||
// Never asserts that the given condition doesn't satisfy in waitFor time, | |||||
// periodically checking the target function each tick. | |||||
// | |||||
// a.Never(func() bool { return false; }, time.Second, 10*time.Millisecond) | |||||
func (a *Assertions) Never(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
Never(a.t, condition, waitFor, tick, msgAndArgs...) | |||||
} | |||||
// Neverf asserts that the given condition doesn't satisfy in waitFor time, | |||||
// periodically checking the target function each tick. | |||||
// | |||||
// a.Neverf(func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") | |||||
func (a *Assertions) Neverf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
Neverf(a.t, condition, waitFor, tick, msg, args...) | |||||
} | |||||
// Nil asserts that the specified object is nil. | // Nil asserts that the specified object is nil. | ||||
// | // | ||||
// a.Nil(err) | // a.Nil(err) | ||||
@@ -748,6 +936,24 @@ func (a *Assertions) Nilf(object interface{}, msg string, args ...interface{}) { | |||||
Nilf(a.t, object, msg, args...) | Nilf(a.t, object, msg, args...) | ||||
} | } | ||||
// NoDirExists checks whether a directory does not exist in the given path. | |||||
// It fails if the path points to an existing _directory_ only. | |||||
func (a *Assertions) NoDirExists(path string, msgAndArgs ...interface{}) { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
NoDirExists(a.t, path, msgAndArgs...) | |||||
} | |||||
// NoDirExistsf checks whether a directory does not exist in the given path. | |||||
// It fails if the path points to an existing _directory_ only. | |||||
func (a *Assertions) NoDirExistsf(path string, msg string, args ...interface{}) { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
NoDirExistsf(a.t, path, msg, args...) | |||||
} | |||||
// NoError asserts that a function returned no error (i.e. `nil`). | // NoError asserts that a function returned no error (i.e. `nil`). | ||||
// | // | ||||
// actualObj, err := SomeFunction() | // actualObj, err := SomeFunction() | ||||
@@ -774,6 +980,24 @@ func (a *Assertions) NoErrorf(err error, msg string, args ...interface{}) { | |||||
NoErrorf(a.t, err, msg, args...) | NoErrorf(a.t, err, msg, args...) | ||||
} | } | ||||
// NoFileExists checks whether a file does not exist in a given path. It fails | |||||
// if the path points to an existing _file_ only. | |||||
func (a *Assertions) NoFileExists(path string, msgAndArgs ...interface{}) { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
NoFileExists(a.t, path, msgAndArgs...) | |||||
} | |||||
// NoFileExistsf checks whether a file does not exist in a given path. It fails | |||||
// if the path points to an existing _file_ only. | |||||
func (a *Assertions) NoFileExistsf(path string, msg string, args ...interface{}) { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
NoFileExistsf(a.t, path, msg, args...) | |||||
} | |||||
// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the | // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the | ||||
// specified substring or element. | // specified substring or element. | ||||
// | // | ||||
@@ -839,6 +1063,26 @@ func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndAr | |||||
NotEqual(a.t, expected, actual, msgAndArgs...) | NotEqual(a.t, expected, actual, msgAndArgs...) | ||||
} | } | ||||
// NotEqualValues asserts that two objects are not equal even when converted to the same type | |||||
// | |||||
// a.NotEqualValues(obj1, obj2) | |||||
func (a *Assertions) NotEqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
NotEqualValues(a.t, expected, actual, msgAndArgs...) | |||||
} | |||||
// NotEqualValuesf asserts that two objects are not equal even when converted to the same type | |||||
// | |||||
// a.NotEqualValuesf(obj1, obj2, "error message %s", "formatted") | |||||
func (a *Assertions) NotEqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
NotEqualValuesf(a.t, expected, actual, msg, args...) | |||||
} | |||||
// NotEqualf asserts that the specified values are NOT equal. | // NotEqualf asserts that the specified values are NOT equal. | ||||
// | // | ||||
// a.NotEqualf(obj1, obj2, "error message %s", "formatted") | // a.NotEqualf(obj1, obj2, "error message %s", "formatted") | ||||
@@ -852,6 +1096,24 @@ func (a *Assertions) NotEqualf(expected interface{}, actual interface{}, msg str | |||||
NotEqualf(a.t, expected, actual, msg, args...) | NotEqualf(a.t, expected, actual, msg, args...) | ||||
} | } | ||||
// NotErrorIs asserts that at none of the errors in err's chain matches target. | |||||
// This is a wrapper for errors.Is. | |||||
func (a *Assertions) NotErrorIs(err error, target error, msgAndArgs ...interface{}) { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
NotErrorIs(a.t, err, target, msgAndArgs...) | |||||
} | |||||
// NotErrorIsf asserts that at none of the errors in err's chain matches target. | |||||
// This is a wrapper for errors.Is. | |||||
func (a *Assertions) NotErrorIsf(err error, target error, msg string, args ...interface{}) { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
NotErrorIsf(a.t, err, target, msg, args...) | |||||
} | |||||
// NotNil asserts that the specified object is not nil. | // NotNil asserts that the specified object is not nil. | ||||
// | // | ||||
// a.NotNil(err) | // a.NotNil(err) | ||||
@@ -905,7 +1167,7 @@ func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...in | |||||
// NotRegexpf asserts that a specified regexp does not match a string. | // NotRegexpf asserts that a specified regexp does not match a string. | ||||
// | // | ||||
// a.NotRegexpf(regexp.MustCompile("starts", "error message %s", "formatted"), "it's starting") | |||||
// a.NotRegexpf(regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") | |||||
// a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted") | // a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted") | ||||
func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, args ...interface{}) { | func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, args ...interface{}) { | ||||
if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
@@ -914,6 +1176,32 @@ func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, arg | |||||
NotRegexpf(a.t, rx, str, msg, args...) | NotRegexpf(a.t, rx, str, msg, args...) | ||||
} | } | ||||
// NotSame asserts that two pointers do not reference the same object. | |||||
// | |||||
// a.NotSame(ptr1, ptr2) | |||||
// | |||||
// Both arguments must be pointer variables. Pointer variable sameness is | |||||
// determined based on the equality of both type and value. | |||||
func (a *Assertions) NotSame(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
NotSame(a.t, expected, actual, msgAndArgs...) | |||||
} | |||||
// NotSamef asserts that two pointers do not reference the same object. | |||||
// | |||||
// a.NotSamef(ptr1, ptr2, "error message %s", "formatted") | |||||
// | |||||
// Both arguments must be pointer variables. Pointer variable sameness is | |||||
// determined based on the equality of both type and value. | |||||
func (a *Assertions) NotSamef(expected interface{}, actual interface{}, msg string, args ...interface{}) { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
NotSamef(a.t, expected, actual, msg, args...) | |||||
} | |||||
// NotSubset asserts that the specified list(array, slice...) contains not all | // NotSubset asserts that the specified list(array, slice...) contains not all | ||||
// elements given in the specified subset(array, slice...). | // elements given in the specified subset(array, slice...). | ||||
// | // | ||||
@@ -962,6 +1250,30 @@ func (a *Assertions) Panics(f assert.PanicTestFunc, msgAndArgs ...interface{}) { | |||||
Panics(a.t, f, msgAndArgs...) | Panics(a.t, f, msgAndArgs...) | ||||
} | } | ||||
// PanicsWithError asserts that the code inside the specified PanicTestFunc | |||||
// panics, and that the recovered panic value is an error that satisfies the | |||||
// EqualError comparison. | |||||
// | |||||
// a.PanicsWithError("crazy error", func(){ GoCrazy() }) | |||||
func (a *Assertions) PanicsWithError(errString string, f assert.PanicTestFunc, msgAndArgs ...interface{}) { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
PanicsWithError(a.t, errString, f, msgAndArgs...) | |||||
} | |||||
// PanicsWithErrorf asserts that the code inside the specified PanicTestFunc | |||||
// panics, and that the recovered panic value is an error that satisfies the | |||||
// EqualError comparison. | |||||
// | |||||
// a.PanicsWithErrorf("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") | |||||
func (a *Assertions) PanicsWithErrorf(errString string, f assert.PanicTestFunc, msg string, args ...interface{}) { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
PanicsWithErrorf(a.t, errString, f, msg, args...) | |||||
} | |||||
// PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that | // PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that | ||||
// the recovered panic value equals the expected panic value. | // the recovered panic value equals the expected panic value. | ||||
// | // | ||||
@@ -994,6 +1306,28 @@ func (a *Assertions) Panicsf(f assert.PanicTestFunc, msg string, args ...interfa | |||||
Panicsf(a.t, f, msg, args...) | Panicsf(a.t, f, msg, args...) | ||||
} | } | ||||
// Positive asserts that the specified element is positive | |||||
// | |||||
// a.Positive(1) | |||||
// a.Positive(1.23) | |||||
func (a *Assertions) Positive(e interface{}, msgAndArgs ...interface{}) { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
Positive(a.t, e, msgAndArgs...) | |||||
} | |||||
// Positivef asserts that the specified element is positive | |||||
// | |||||
// a.Positivef(1, "error message %s", "formatted") | |||||
// a.Positivef(1.23, "error message %s", "formatted") | |||||
func (a *Assertions) Positivef(e interface{}, msg string, args ...interface{}) { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
Positivef(a.t, e, msg, args...) | |||||
} | |||||
// Regexp asserts that a specified regexp matches a string. | // Regexp asserts that a specified regexp matches a string. | ||||
// | // | ||||
// a.Regexp(regexp.MustCompile("start"), "it's starting") | // a.Regexp(regexp.MustCompile("start"), "it's starting") | ||||
@@ -1007,7 +1341,7 @@ func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...inter | |||||
// Regexpf asserts that a specified regexp matches a string. | // Regexpf asserts that a specified regexp matches a string. | ||||
// | // | ||||
// a.Regexpf(regexp.MustCompile("start", "error message %s", "formatted"), "it's starting") | |||||
// a.Regexpf(regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") | |||||
// a.Regexpf("start...$", "it's not starting", "error message %s", "formatted") | // a.Regexpf("start...$", "it's not starting", "error message %s", "formatted") | ||||
func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args ...interface{}) { | func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args ...interface{}) { | ||||
if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
@@ -1104,6 +1438,22 @@ func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta | |||||
WithinDurationf(a.t, expected, actual, delta, msg, args...) | WithinDurationf(a.t, expected, actual, delta, msg, args...) | ||||
} | } | ||||
// YAMLEq asserts that two YAML strings are equivalent. | |||||
func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
YAMLEq(a.t, expected, actual, msgAndArgs...) | |||||
} | |||||
// YAMLEqf asserts that two YAML strings are equivalent. | |||||
func (a *Assertions) YAMLEqf(expected string, actual string, msg string, args ...interface{}) { | |||||
if h, ok := a.t.(tHelper); ok { | |||||
h.Helper() | |||||
} | |||||
YAMLEqf(a.t, expected, actual, msg, args...) | |||||
} | |||||
// Zero asserts that i is the zero value for its type. | // Zero asserts that i is the zero value for its type. | ||||
func (a *Assertions) Zero(i interface{}, msgAndArgs ...interface{}) { | func (a *Assertions) Zero(i interface{}, msgAndArgs ...interface{}) { | ||||
if h, ok := a.t.(tHelper); ok { | if h, ok := a.t.(tHelper); ok { | ||||
@@ -26,4 +26,4 @@ type BoolAssertionFunc func(TestingT, bool, ...interface{}) | |||||
// for table driven tests. | // for table driven tests. | ||||
type ErrorAssertionFunc func(TestingT, error, ...interface{}) | type ErrorAssertionFunc func(TestingT, error, ...interface{}) | ||||
//go:generate go run ../_codegen/main.go -output-package=require -template=require.go.tmpl -include-format-funcs | |||||
//go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=require -template=require.go.tmpl -include-format-funcs" |
@@ -0,0 +1,16 @@ | |||||
language: go | |||||
go: | |||||
- "1.4.x" | |||||
- "1.5.x" | |||||
- "1.6.x" | |||||
- "1.7.x" | |||||
- "1.8.x" | |||||
- "1.9.x" | |||||
- "1.10.x" | |||||
- "1.11.x" | |||||
- "1.12.x" | |||||
- "1.13.x" | |||||
- "tip" | |||||
go_import_path: gopkg.in/yaml.v3 |
@@ -0,0 +1,50 @@ | |||||
This project is covered by two different licenses: MIT and Apache. | |||||
#### MIT License #### | |||||
The following files were ported to Go from C files of libyaml, and thus | |||||
are still covered by their original MIT license, with the additional | |||||
copyright staring in 2011 when the project was ported over: | |||||
apic.go emitterc.go parserc.go readerc.go scannerc.go | |||||
writerc.go yamlh.go yamlprivateh.go | |||||
Copyright (c) 2006-2010 Kirill Simonov | |||||
Copyright (c) 2006-2011 Kirill Simonov | |||||
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. | |||||
### Apache License ### | |||||
All the remaining project files are covered by the Apache license: | |||||
Copyright (c) 2011-2019 Canonical Ltd | |||||
Licensed under the Apache License, Version 2.0 (the "License"); | |||||
you may not use this file except in compliance with the License. | |||||
You may obtain a copy of the License at | |||||
http://www.apache.org/licenses/LICENSE-2.0 | |||||
Unless required by applicable law or agreed to in writing, software | |||||
distributed under the License is distributed on an "AS IS" BASIS, | |||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
See the License for the specific language governing permissions and | |||||
limitations under the License. |
@@ -0,0 +1,13 @@ | |||||
Copyright 2011-2016 Canonical Ltd. | |||||
Licensed under the Apache License, Version 2.0 (the "License"); | |||||
you may not use this file except in compliance with the License. | |||||
You may obtain a copy of the License at | |||||
http://www.apache.org/licenses/LICENSE-2.0 | |||||
Unless required by applicable law or agreed to in writing, software | |||||
distributed under the License is distributed on an "AS IS" BASIS, | |||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
See the License for the specific language governing permissions and | |||||
limitations under the License. |
@@ -0,0 +1,150 @@ | |||||
# YAML support for the Go language | |||||
Introduction | |||||
------------ | |||||
The yaml package enables Go programs to comfortably encode and decode YAML | |||||
values. It was developed within [Canonical](https://www.canonical.com) as | |||||
part of the [juju](https://juju.ubuntu.com) project, and is based on a | |||||
pure Go port of the well-known [libyaml](http://pyyaml.org/wiki/LibYAML) | |||||
C library to parse and generate YAML data quickly and reliably. | |||||
Compatibility | |||||
------------- | |||||
The yaml package supports most of YAML 1.2, but preserves some behavior | |||||
from 1.1 for backwards compatibility. | |||||
Specifically, as of v3 of the yaml package: | |||||
- YAML 1.1 bools (_yes/no, on/off_) are supported as long as they are being | |||||
decoded into a typed bool value. Otherwise they behave as a string. Booleans | |||||
in YAML 1.2 are _true/false_ only. | |||||
- Octals encode and decode as _0777_ per YAML 1.1, rather than _0o777_ | |||||
as specified in YAML 1.2, because most parsers still use the old format. | |||||
Octals in the _0o777_ format are supported though, so new files work. | |||||
- Does not support base-60 floats. These are gone from YAML 1.2, and were | |||||
actually never supported by this package as it's clearly a poor choice. | |||||
and offers backwards | |||||
compatibility with YAML 1.1 in some cases. | |||||
1.2, including support for | |||||
anchors, tags, map merging, etc. Multi-document unmarshalling is not yet | |||||
implemented, and base-60 floats from YAML 1.1 are purposefully not | |||||
supported since they're a poor design and are gone in YAML 1.2. | |||||
Installation and usage | |||||
---------------------- | |||||
The import path for the package is *gopkg.in/yaml.v3*. | |||||
To install it, run: | |||||
go get gopkg.in/yaml.v3 | |||||
API documentation | |||||
----------------- | |||||
If opened in a browser, the import path itself leads to the API documentation: | |||||
- [https://gopkg.in/yaml.v3](https://gopkg.in/yaml.v3) | |||||
API stability | |||||
------------- | |||||
The package API for yaml v3 will remain stable as described in [gopkg.in](https://gopkg.in). | |||||
License | |||||
------- | |||||
The yaml package is licensed under the MIT and Apache License 2.0 licenses. | |||||
Please see the LICENSE file for details. | |||||
Example | |||||
------- | |||||
```Go | |||||
package main | |||||
import ( | |||||
"fmt" | |||||
"log" | |||||
"gopkg.in/yaml.v3" | |||||
) | |||||
var data = ` | |||||
a: Easy! | |||||
b: | |||||
c: 2 | |||||
d: [3, 4] | |||||
` | |||||
// Note: struct fields must be public in order for unmarshal to | |||||
// correctly populate the data. | |||||
type T struct { | |||||
A string | |||||
B struct { | |||||
RenamedC int `yaml:"c"` | |||||
D []int `yaml:",flow"` | |||||
} | |||||
} | |||||
func main() { | |||||
t := T{} | |||||
err := yaml.Unmarshal([]byte(data), &t) | |||||
if err != nil { | |||||
log.Fatalf("error: %v", err) | |||||
} | |||||
fmt.Printf("--- t:\n%v\n\n", t) | |||||
d, err := yaml.Marshal(&t) | |||||
if err != nil { | |||||
log.Fatalf("error: %v", err) | |||||
} | |||||
fmt.Printf("--- t dump:\n%s\n\n", string(d)) | |||||
m := make(map[interface{}]interface{}) | |||||
err = yaml.Unmarshal([]byte(data), &m) | |||||
if err != nil { | |||||
log.Fatalf("error: %v", err) | |||||
} | |||||
fmt.Printf("--- m:\n%v\n\n", m) | |||||
d, err = yaml.Marshal(&m) | |||||
if err != nil { | |||||
log.Fatalf("error: %v", err) | |||||
} | |||||
fmt.Printf("--- m dump:\n%s\n\n", string(d)) | |||||
} | |||||
``` | |||||
This example will generate the following output: | |||||
``` | |||||
--- t: | |||||
{Easy! {2 [3 4]}} | |||||
--- t dump: | |||||
a: Easy! | |||||
b: | |||||
c: 2 | |||||
d: [3, 4] | |||||
--- m: | |||||
map[a:Easy! b:map[c:2 d:[3 4]]] | |||||
--- m dump: | |||||
a: Easy! | |||||
b: | |||||
c: 2 | |||||
d: | |||||
- 3 | |||||
- 4 | |||||
``` | |||||
@@ -0,0 +1,746 @@ | |||||
// | |||||
// Copyright (c) 2011-2019 Canonical Ltd | |||||
// Copyright (c) 2006-2010 Kirill Simonov | |||||
// | |||||
// 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. | |||||
package yaml | |||||
import ( | |||||
"io" | |||||
) | |||||
func yaml_insert_token(parser *yaml_parser_t, pos int, token *yaml_token_t) { | |||||
//fmt.Println("yaml_insert_token", "pos:", pos, "typ:", token.typ, "head:", parser.tokens_head, "len:", len(parser.tokens)) | |||||
// Check if we can move the queue at the beginning of the buffer. | |||||
if parser.tokens_head > 0 && len(parser.tokens) == cap(parser.tokens) { | |||||
if parser.tokens_head != len(parser.tokens) { | |||||
copy(parser.tokens, parser.tokens[parser.tokens_head:]) | |||||
} | |||||
parser.tokens = parser.tokens[:len(parser.tokens)-parser.tokens_head] | |||||
parser.tokens_head = 0 | |||||
} | |||||
parser.tokens = append(parser.tokens, *token) | |||||
if pos < 0 { | |||||
return | |||||
} | |||||
copy(parser.tokens[parser.tokens_head+pos+1:], parser.tokens[parser.tokens_head+pos:]) | |||||
parser.tokens[parser.tokens_head+pos] = *token | |||||
} | |||||
// Create a new parser object. | |||||
func yaml_parser_initialize(parser *yaml_parser_t) bool { | |||||
*parser = yaml_parser_t{ | |||||
raw_buffer: make([]byte, 0, input_raw_buffer_size), | |||||
buffer: make([]byte, 0, input_buffer_size), | |||||
} | |||||
return true | |||||
} | |||||
// Destroy a parser object. | |||||
func yaml_parser_delete(parser *yaml_parser_t) { | |||||
*parser = yaml_parser_t{} | |||||
} | |||||
// String read handler. | |||||
func yaml_string_read_handler(parser *yaml_parser_t, buffer []byte) (n int, err error) { | |||||
if parser.input_pos == len(parser.input) { | |||||
return 0, io.EOF | |||||
} | |||||
n = copy(buffer, parser.input[parser.input_pos:]) | |||||
parser.input_pos += n | |||||
return n, nil | |||||
} | |||||
// Reader read handler. | |||||
func yaml_reader_read_handler(parser *yaml_parser_t, buffer []byte) (n int, err error) { | |||||
return parser.input_reader.Read(buffer) | |||||
} | |||||
// Set a string input. | |||||
func yaml_parser_set_input_string(parser *yaml_parser_t, input []byte) { | |||||
if parser.read_handler != nil { | |||||
panic("must set the input source only once") | |||||
} | |||||
parser.read_handler = yaml_string_read_handler | |||||
parser.input = input | |||||
parser.input_pos = 0 | |||||
} | |||||
// Set a file input. | |||||
func yaml_parser_set_input_reader(parser *yaml_parser_t, r io.Reader) { | |||||
if parser.read_handler != nil { | |||||
panic("must set the input source only once") | |||||
} | |||||
parser.read_handler = yaml_reader_read_handler | |||||
parser.input_reader = r | |||||
} | |||||
// Set the source encoding. | |||||
func yaml_parser_set_encoding(parser *yaml_parser_t, encoding yaml_encoding_t) { | |||||
if parser.encoding != yaml_ANY_ENCODING { | |||||
panic("must set the encoding only once") | |||||
} | |||||
parser.encoding = encoding | |||||
} | |||||
// Create a new emitter object. | |||||
func yaml_emitter_initialize(emitter *yaml_emitter_t) { | |||||
*emitter = yaml_emitter_t{ | |||||
buffer: make([]byte, output_buffer_size), | |||||
raw_buffer: make([]byte, 0, output_raw_buffer_size), | |||||
states: make([]yaml_emitter_state_t, 0, initial_stack_size), | |||||
events: make([]yaml_event_t, 0, initial_queue_size), | |||||
} | |||||
} | |||||
// Destroy an emitter object. | |||||
func yaml_emitter_delete(emitter *yaml_emitter_t) { | |||||
*emitter = yaml_emitter_t{} | |||||
} | |||||
// String write handler. | |||||
func yaml_string_write_handler(emitter *yaml_emitter_t, buffer []byte) error { | |||||
*emitter.output_buffer = append(*emitter.output_buffer, buffer...) | |||||
return nil | |||||
} | |||||
// yaml_writer_write_handler uses emitter.output_writer to write the | |||||
// emitted text. | |||||
func yaml_writer_write_handler(emitter *yaml_emitter_t, buffer []byte) error { | |||||
_, err := emitter.output_writer.Write(buffer) | |||||
return err | |||||
} | |||||
// Set a string output. | |||||
func yaml_emitter_set_output_string(emitter *yaml_emitter_t, output_buffer *[]byte) { | |||||
if emitter.write_handler != nil { | |||||
panic("must set the output target only once") | |||||
} | |||||
emitter.write_handler = yaml_string_write_handler | |||||
emitter.output_buffer = output_buffer | |||||
} | |||||
// Set a file output. | |||||
func yaml_emitter_set_output_writer(emitter *yaml_emitter_t, w io.Writer) { | |||||
if emitter.write_handler != nil { | |||||
panic("must set the output target only once") | |||||
} | |||||
emitter.write_handler = yaml_writer_write_handler | |||||
emitter.output_writer = w | |||||
} | |||||
// Set the output encoding. | |||||
func yaml_emitter_set_encoding(emitter *yaml_emitter_t, encoding yaml_encoding_t) { | |||||
if emitter.encoding != yaml_ANY_ENCODING { | |||||
panic("must set the output encoding only once") | |||||
} | |||||
emitter.encoding = encoding | |||||
} | |||||
// Set the canonical output style. | |||||
func yaml_emitter_set_canonical(emitter *yaml_emitter_t, canonical bool) { | |||||
emitter.canonical = canonical | |||||
} | |||||
// Set the indentation increment. | |||||
func yaml_emitter_set_indent(emitter *yaml_emitter_t, indent int) { | |||||
if indent < 2 || indent > 9 { | |||||
indent = 2 | |||||
} | |||||
emitter.best_indent = indent | |||||
} | |||||
// Set the preferred line width. | |||||
func yaml_emitter_set_width(emitter *yaml_emitter_t, width int) { | |||||
if width < 0 { | |||||
width = -1 | |||||
} | |||||
emitter.best_width = width | |||||
} | |||||
// Set if unescaped non-ASCII characters are allowed. | |||||
func yaml_emitter_set_unicode(emitter *yaml_emitter_t, unicode bool) { | |||||
emitter.unicode = unicode | |||||
} | |||||
// Set the preferred line break character. | |||||
func yaml_emitter_set_break(emitter *yaml_emitter_t, line_break yaml_break_t) { | |||||
emitter.line_break = line_break | |||||
} | |||||
///* | |||||
// * Destroy a token object. | |||||
// */ | |||||
// | |||||
//YAML_DECLARE(void) | |||||
//yaml_token_delete(yaml_token_t *token) | |||||
//{ | |||||
// assert(token); // Non-NULL token object expected. | |||||
// | |||||
// switch (token.type) | |||||
// { | |||||
// case YAML_TAG_DIRECTIVE_TOKEN: | |||||
// yaml_free(token.data.tag_directive.handle); | |||||
// yaml_free(token.data.tag_directive.prefix); | |||||
// break; | |||||
// | |||||
// case YAML_ALIAS_TOKEN: | |||||
// yaml_free(token.data.alias.value); | |||||
// break; | |||||
// | |||||
// case YAML_ANCHOR_TOKEN: | |||||
// yaml_free(token.data.anchor.value); | |||||
// break; | |||||
// | |||||
// case YAML_TAG_TOKEN: | |||||
// yaml_free(token.data.tag.handle); | |||||
// yaml_free(token.data.tag.suffix); | |||||
// break; | |||||
// | |||||
// case YAML_SCALAR_TOKEN: | |||||
// yaml_free(token.data.scalar.value); | |||||
// break; | |||||
// | |||||
// default: | |||||
// break; | |||||
// } | |||||
// | |||||
// memset(token, 0, sizeof(yaml_token_t)); | |||||
//} | |||||
// | |||||
///* | |||||
// * Check if a string is a valid UTF-8 sequence. | |||||
// * | |||||
// * Check 'reader.c' for more details on UTF-8 encoding. | |||||
// */ | |||||
// | |||||
//static int | |||||
//yaml_check_utf8(yaml_char_t *start, size_t length) | |||||
//{ | |||||
// yaml_char_t *end = start+length; | |||||
// yaml_char_t *pointer = start; | |||||
// | |||||
// while (pointer < end) { | |||||
// unsigned char octet; | |||||
// unsigned int width; | |||||
// unsigned int value; | |||||
// size_t k; | |||||
// | |||||
// octet = pointer[0]; | |||||
// width = (octet & 0x80) == 0x00 ? 1 : | |||||
// (octet & 0xE0) == 0xC0 ? 2 : | |||||
// (octet & 0xF0) == 0xE0 ? 3 : | |||||
// (octet & 0xF8) == 0xF0 ? 4 : 0; | |||||
// value = (octet & 0x80) == 0x00 ? octet & 0x7F : | |||||
// (octet & 0xE0) == 0xC0 ? octet & 0x1F : | |||||
// (octet & 0xF0) == 0xE0 ? octet & 0x0F : | |||||
// (octet & 0xF8) == 0xF0 ? octet & 0x07 : 0; | |||||
// if (!width) return 0; | |||||
// if (pointer+width > end) return 0; | |||||
// for (k = 1; k < width; k ++) { | |||||
// octet = pointer[k]; | |||||
// if ((octet & 0xC0) != 0x80) return 0; | |||||
// value = (value << 6) + (octet & 0x3F); | |||||
// } | |||||
// if (!((width == 1) || | |||||
// (width == 2 && value >= 0x80) || | |||||
// (width == 3 && value >= 0x800) || | |||||
// (width == 4 && value >= 0x10000))) return 0; | |||||
// | |||||
// pointer += width; | |||||
// } | |||||
// | |||||
// return 1; | |||||
//} | |||||
// | |||||
// Create STREAM-START. | |||||
func yaml_stream_start_event_initialize(event *yaml_event_t, encoding yaml_encoding_t) { | |||||
*event = yaml_event_t{ | |||||
typ: yaml_STREAM_START_EVENT, | |||||
encoding: encoding, | |||||
} | |||||
} | |||||
// Create STREAM-END. | |||||
func yaml_stream_end_event_initialize(event *yaml_event_t) { | |||||
*event = yaml_event_t{ | |||||
typ: yaml_STREAM_END_EVENT, | |||||
} | |||||
} | |||||
// Create DOCUMENT-START. | |||||
func yaml_document_start_event_initialize( | |||||
event *yaml_event_t, | |||||
version_directive *yaml_version_directive_t, | |||||
tag_directives []yaml_tag_directive_t, | |||||
implicit bool, | |||||
) { | |||||
*event = yaml_event_t{ | |||||
typ: yaml_DOCUMENT_START_EVENT, | |||||
version_directive: version_directive, | |||||
tag_directives: tag_directives, | |||||
implicit: implicit, | |||||
} | |||||
} | |||||
// Create DOCUMENT-END. | |||||
func yaml_document_end_event_initialize(event *yaml_event_t, implicit bool) { | |||||
*event = yaml_event_t{ | |||||
typ: yaml_DOCUMENT_END_EVENT, | |||||
implicit: implicit, | |||||
} | |||||
} | |||||
// Create ALIAS. | |||||
func yaml_alias_event_initialize(event *yaml_event_t, anchor []byte) bool { | |||||
*event = yaml_event_t{ | |||||
typ: yaml_ALIAS_EVENT, | |||||
anchor: anchor, | |||||
} | |||||
return true | |||||
} | |||||
// Create SCALAR. | |||||
func yaml_scalar_event_initialize(event *yaml_event_t, anchor, tag, value []byte, plain_implicit, quoted_implicit bool, style yaml_scalar_style_t) bool { | |||||
*event = yaml_event_t{ | |||||
typ: yaml_SCALAR_EVENT, | |||||
anchor: anchor, | |||||
tag: tag, | |||||
value: value, | |||||
implicit: plain_implicit, | |||||
quoted_implicit: quoted_implicit, | |||||
style: yaml_style_t(style), | |||||
} | |||||
return true | |||||
} | |||||
// Create SEQUENCE-START. | |||||
func yaml_sequence_start_event_initialize(event *yaml_event_t, anchor, tag []byte, implicit bool, style yaml_sequence_style_t) bool { | |||||
*event = yaml_event_t{ | |||||
typ: yaml_SEQUENCE_START_EVENT, | |||||
anchor: anchor, | |||||
tag: tag, | |||||
implicit: implicit, | |||||
style: yaml_style_t(style), | |||||
} | |||||
return true | |||||
} | |||||
// Create SEQUENCE-END. | |||||
func yaml_sequence_end_event_initialize(event *yaml_event_t) bool { | |||||
*event = yaml_event_t{ | |||||
typ: yaml_SEQUENCE_END_EVENT, | |||||
} | |||||
return true | |||||
} | |||||
// Create MAPPING-START. | |||||
func yaml_mapping_start_event_initialize(event *yaml_event_t, anchor, tag []byte, implicit bool, style yaml_mapping_style_t) { | |||||
*event = yaml_event_t{ | |||||
typ: yaml_MAPPING_START_EVENT, | |||||
anchor: anchor, | |||||
tag: tag, | |||||
implicit: implicit, | |||||
style: yaml_style_t(style), | |||||
} | |||||
} | |||||
// Create MAPPING-END. | |||||
func yaml_mapping_end_event_initialize(event *yaml_event_t) { | |||||
*event = yaml_event_t{ | |||||
typ: yaml_MAPPING_END_EVENT, | |||||
} | |||||
} | |||||
// Destroy an event object. | |||||
func yaml_event_delete(event *yaml_event_t) { | |||||
*event = yaml_event_t{} | |||||
} | |||||
///* | |||||
// * Create a document object. | |||||
// */ | |||||
// | |||||
//YAML_DECLARE(int) | |||||
//yaml_document_initialize(document *yaml_document_t, | |||||
// version_directive *yaml_version_directive_t, | |||||
// tag_directives_start *yaml_tag_directive_t, | |||||
// tag_directives_end *yaml_tag_directive_t, | |||||
// start_implicit int, end_implicit int) | |||||
//{ | |||||
// struct { | |||||
// error yaml_error_type_t | |||||
// } context | |||||
// struct { | |||||
// start *yaml_node_t | |||||
// end *yaml_node_t | |||||
// top *yaml_node_t | |||||
// } nodes = { NULL, NULL, NULL } | |||||
// version_directive_copy *yaml_version_directive_t = NULL | |||||
// struct { | |||||
// start *yaml_tag_directive_t | |||||
// end *yaml_tag_directive_t | |||||
// top *yaml_tag_directive_t | |||||
// } tag_directives_copy = { NULL, NULL, NULL } | |||||
// value yaml_tag_directive_t = { NULL, NULL } | |||||
// mark yaml_mark_t = { 0, 0, 0 } | |||||
// | |||||
// assert(document) // Non-NULL document object is expected. | |||||
// assert((tag_directives_start && tag_directives_end) || | |||||
// (tag_directives_start == tag_directives_end)) | |||||
// // Valid tag directives are expected. | |||||
// | |||||
// if (!STACK_INIT(&context, nodes, INITIAL_STACK_SIZE)) goto error | |||||
// | |||||
// if (version_directive) { | |||||
// version_directive_copy = yaml_malloc(sizeof(yaml_version_directive_t)) | |||||
// if (!version_directive_copy) goto error | |||||
// version_directive_copy.major = version_directive.major | |||||
// version_directive_copy.minor = version_directive.minor | |||||
// } | |||||
// | |||||
// if (tag_directives_start != tag_directives_end) { | |||||
// tag_directive *yaml_tag_directive_t | |||||
// if (!STACK_INIT(&context, tag_directives_copy, INITIAL_STACK_SIZE)) | |||||
// goto error | |||||
// for (tag_directive = tag_directives_start | |||||
// tag_directive != tag_directives_end; tag_directive ++) { | |||||
// assert(tag_directive.handle) | |||||
// assert(tag_directive.prefix) | |||||
// if (!yaml_check_utf8(tag_directive.handle, | |||||
// strlen((char *)tag_directive.handle))) | |||||
// goto error | |||||
// if (!yaml_check_utf8(tag_directive.prefix, | |||||
// strlen((char *)tag_directive.prefix))) | |||||
// goto error | |||||
// value.handle = yaml_strdup(tag_directive.handle) | |||||
// value.prefix = yaml_strdup(tag_directive.prefix) | |||||
// if (!value.handle || !value.prefix) goto error | |||||
// if (!PUSH(&context, tag_directives_copy, value)) | |||||
// goto error | |||||
// value.handle = NULL | |||||
// value.prefix = NULL | |||||
// } | |||||
// } | |||||
// | |||||
// DOCUMENT_INIT(*document, nodes.start, nodes.end, version_directive_copy, | |||||
// tag_directives_copy.start, tag_directives_copy.top, | |||||
// start_implicit, end_implicit, mark, mark) | |||||
// | |||||
// return 1 | |||||
// | |||||
//error: | |||||
// STACK_DEL(&context, nodes) | |||||
// yaml_free(version_directive_copy) | |||||
// while (!STACK_EMPTY(&context, tag_directives_copy)) { | |||||
// value yaml_tag_directive_t = POP(&context, tag_directives_copy) | |||||
// yaml_free(value.handle) | |||||
// yaml_free(value.prefix) | |||||
// } | |||||
// STACK_DEL(&context, tag_directives_copy) | |||||
// yaml_free(value.handle) | |||||
// yaml_free(value.prefix) | |||||
// | |||||
// return 0 | |||||
//} | |||||
// | |||||
///* | |||||
// * Destroy a document object. | |||||
// */ | |||||
// | |||||
//YAML_DECLARE(void) | |||||
//yaml_document_delete(document *yaml_document_t) | |||||
//{ | |||||
// struct { | |||||
// error yaml_error_type_t | |||||
// } context | |||||
// tag_directive *yaml_tag_directive_t | |||||
// | |||||
// context.error = YAML_NO_ERROR // Eliminate a compiler warning. | |||||
// | |||||
// assert(document) // Non-NULL document object is expected. | |||||
// | |||||
// while (!STACK_EMPTY(&context, document.nodes)) { | |||||
// node yaml_node_t = POP(&context, document.nodes) | |||||
// yaml_free(node.tag) | |||||
// switch (node.type) { | |||||
// case YAML_SCALAR_NODE: | |||||
// yaml_free(node.data.scalar.value) | |||||
// break | |||||
// case YAML_SEQUENCE_NODE: | |||||
// STACK_DEL(&context, node.data.sequence.items) | |||||
// break | |||||
// case YAML_MAPPING_NODE: | |||||
// STACK_DEL(&context, node.data.mapping.pairs) | |||||
// break | |||||
// default: | |||||
// assert(0) // Should not happen. | |||||
// } | |||||
// } | |||||
// STACK_DEL(&context, document.nodes) | |||||
// | |||||
// yaml_free(document.version_directive) | |||||
// for (tag_directive = document.tag_directives.start | |||||
// tag_directive != document.tag_directives.end | |||||
// tag_directive++) { | |||||
// yaml_free(tag_directive.handle) | |||||
// yaml_free(tag_directive.prefix) | |||||
// } | |||||
// yaml_free(document.tag_directives.start) | |||||
// | |||||
// memset(document, 0, sizeof(yaml_document_t)) | |||||
//} | |||||
// | |||||
///** | |||||
// * Get a document node. | |||||
// */ | |||||
// | |||||
//YAML_DECLARE(yaml_node_t *) | |||||
//yaml_document_get_node(document *yaml_document_t, index int) | |||||
//{ | |||||
// assert(document) // Non-NULL document object is expected. | |||||
// | |||||
// if (index > 0 && document.nodes.start + index <= document.nodes.top) { | |||||
// return document.nodes.start + index - 1 | |||||
// } | |||||
// return NULL | |||||
//} | |||||
// | |||||
///** | |||||
// * Get the root object. | |||||
// */ | |||||
// | |||||
//YAML_DECLARE(yaml_node_t *) | |||||
//yaml_document_get_root_node(document *yaml_document_t) | |||||
//{ | |||||
// assert(document) // Non-NULL document object is expected. | |||||
// | |||||
// if (document.nodes.top != document.nodes.start) { | |||||
// return document.nodes.start | |||||
// } | |||||
// return NULL | |||||
//} | |||||
// | |||||
///* | |||||
// * Add a scalar node to a document. | |||||
// */ | |||||
// | |||||
//YAML_DECLARE(int) | |||||
//yaml_document_add_scalar(document *yaml_document_t, | |||||
// tag *yaml_char_t, value *yaml_char_t, length int, | |||||
// style yaml_scalar_style_t) | |||||
//{ | |||||
// struct { | |||||
// error yaml_error_type_t | |||||
// } context | |||||
// mark yaml_mark_t = { 0, 0, 0 } | |||||
// tag_copy *yaml_char_t = NULL | |||||
// value_copy *yaml_char_t = NULL | |||||
// node yaml_node_t | |||||
// | |||||
// assert(document) // Non-NULL document object is expected. | |||||
// assert(value) // Non-NULL value is expected. | |||||
// | |||||
// if (!tag) { | |||||
// tag = (yaml_char_t *)YAML_DEFAULT_SCALAR_TAG | |||||
// } | |||||
// | |||||
// if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error | |||||
// tag_copy = yaml_strdup(tag) | |||||
// if (!tag_copy) goto error | |||||
// | |||||
// if (length < 0) { | |||||
// length = strlen((char *)value) | |||||
// } | |||||
// | |||||
// if (!yaml_check_utf8(value, length)) goto error | |||||
// value_copy = yaml_malloc(length+1) | |||||
// if (!value_copy) goto error | |||||
// memcpy(value_copy, value, length) | |||||
// value_copy[length] = '\0' | |||||
// | |||||
// SCALAR_NODE_INIT(node, tag_copy, value_copy, length, style, mark, mark) | |||||
// if (!PUSH(&context, document.nodes, node)) goto error | |||||
// | |||||
// return document.nodes.top - document.nodes.start | |||||
// | |||||
//error: | |||||
// yaml_free(tag_copy) | |||||
// yaml_free(value_copy) | |||||
// | |||||
// return 0 | |||||
//} | |||||
// | |||||
///* | |||||
// * Add a sequence node to a document. | |||||
// */ | |||||
// | |||||
//YAML_DECLARE(int) | |||||
//yaml_document_add_sequence(document *yaml_document_t, | |||||
// tag *yaml_char_t, style yaml_sequence_style_t) | |||||
//{ | |||||
// struct { | |||||
// error yaml_error_type_t | |||||
// } context | |||||
// mark yaml_mark_t = { 0, 0, 0 } | |||||
// tag_copy *yaml_char_t = NULL | |||||
// struct { | |||||
// start *yaml_node_item_t | |||||
// end *yaml_node_item_t | |||||
// top *yaml_node_item_t | |||||
// } items = { NULL, NULL, NULL } | |||||
// node yaml_node_t | |||||
// | |||||
// assert(document) // Non-NULL document object is expected. | |||||
// | |||||
// if (!tag) { | |||||
// tag = (yaml_char_t *)YAML_DEFAULT_SEQUENCE_TAG | |||||
// } | |||||
// | |||||
// if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error | |||||
// tag_copy = yaml_strdup(tag) | |||||
// if (!tag_copy) goto error | |||||
// | |||||
// if (!STACK_INIT(&context, items, INITIAL_STACK_SIZE)) goto error | |||||
// | |||||
// SEQUENCE_NODE_INIT(node, tag_copy, items.start, items.end, | |||||
// style, mark, mark) | |||||
// if (!PUSH(&context, document.nodes, node)) goto error | |||||
// | |||||
// return document.nodes.top - document.nodes.start | |||||
// | |||||
//error: | |||||
// STACK_DEL(&context, items) | |||||
// yaml_free(tag_copy) | |||||
// | |||||
// return 0 | |||||
//} | |||||
// | |||||
///* | |||||
// * Add a mapping node to a document. | |||||
// */ | |||||
// | |||||
//YAML_DECLARE(int) | |||||
//yaml_document_add_mapping(document *yaml_document_t, | |||||
// tag *yaml_char_t, style yaml_mapping_style_t) | |||||
//{ | |||||
// struct { | |||||
// error yaml_error_type_t | |||||
// } context | |||||
// mark yaml_mark_t = { 0, 0, 0 } | |||||
// tag_copy *yaml_char_t = NULL | |||||
// struct { | |||||
// start *yaml_node_pair_t | |||||
// end *yaml_node_pair_t | |||||
// top *yaml_node_pair_t | |||||
// } pairs = { NULL, NULL, NULL } | |||||
// node yaml_node_t | |||||
// | |||||
// assert(document) // Non-NULL document object is expected. | |||||
// | |||||
// if (!tag) { | |||||
// tag = (yaml_char_t *)YAML_DEFAULT_MAPPING_TAG | |||||
// } | |||||
// | |||||
// if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error | |||||
// tag_copy = yaml_strdup(tag) | |||||
// if (!tag_copy) goto error | |||||
// | |||||
// if (!STACK_INIT(&context, pairs, INITIAL_STACK_SIZE)) goto error | |||||
// | |||||
// MAPPING_NODE_INIT(node, tag_copy, pairs.start, pairs.end, | |||||
// style, mark, mark) | |||||
// if (!PUSH(&context, document.nodes, node)) goto error | |||||
// | |||||
// return document.nodes.top - document.nodes.start | |||||
// | |||||
//error: | |||||
// STACK_DEL(&context, pairs) | |||||
// yaml_free(tag_copy) | |||||
// | |||||
// return 0 | |||||
//} | |||||
// | |||||
///* | |||||
// * Append an item to a sequence node. | |||||
// */ | |||||
// | |||||
//YAML_DECLARE(int) | |||||
//yaml_document_append_sequence_item(document *yaml_document_t, | |||||
// sequence int, item int) | |||||
//{ | |||||
// struct { | |||||
// error yaml_error_type_t | |||||
// } context | |||||
// | |||||
// assert(document) // Non-NULL document is required. | |||||
// assert(sequence > 0 | |||||
// && document.nodes.start + sequence <= document.nodes.top) | |||||
// // Valid sequence id is required. | |||||
// assert(document.nodes.start[sequence-1].type == YAML_SEQUENCE_NODE) | |||||
// // A sequence node is required. | |||||
// assert(item > 0 && document.nodes.start + item <= document.nodes.top) | |||||
// // Valid item id is required. | |||||
// | |||||
// if (!PUSH(&context, | |||||
// document.nodes.start[sequence-1].data.sequence.items, item)) | |||||
// return 0 | |||||
// | |||||
// return 1 | |||||
//} | |||||
// | |||||
///* | |||||
// * Append a pair of a key and a value to a mapping node. | |||||
// */ | |||||
// | |||||
//YAML_DECLARE(int) | |||||
//yaml_document_append_mapping_pair(document *yaml_document_t, | |||||
// mapping int, key int, value int) | |||||
//{ | |||||
// struct { | |||||
// error yaml_error_type_t | |||||
// } context | |||||
// | |||||
// pair yaml_node_pair_t | |||||
// | |||||
// assert(document) // Non-NULL document is required. | |||||
// assert(mapping > 0 | |||||
// && document.nodes.start + mapping <= document.nodes.top) | |||||
// // Valid mapping id is required. | |||||
// assert(document.nodes.start[mapping-1].type == YAML_MAPPING_NODE) | |||||
// // A mapping node is required. | |||||
// assert(key > 0 && document.nodes.start + key <= document.nodes.top) | |||||
// // Valid key id is required. | |||||
// assert(value > 0 && document.nodes.start + value <= document.nodes.top) | |||||
// // Valid value id is required. | |||||
// | |||||
// pair.key = key | |||||
// pair.value = value | |||||
// | |||||
// if (!PUSH(&context, | |||||
// document.nodes.start[mapping-1].data.mapping.pairs, pair)) | |||||
// return 0 | |||||
// | |||||
// return 1 | |||||
//} | |||||
// | |||||
// |
@@ -0,0 +1,931 @@ | |||||
// | |||||
// Copyright (c) 2011-2019 Canonical Ltd | |||||
// | |||||
// Licensed under the Apache License, Version 2.0 (the "License"); | |||||
// you may not use this file except in compliance with the License. | |||||
// You may obtain a copy of the License at | |||||
// | |||||
// http://www.apache.org/licenses/LICENSE-2.0 | |||||
// | |||||
// Unless required by applicable law or agreed to in writing, software | |||||
// distributed under the License is distributed on an "AS IS" BASIS, | |||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
// See the License for the specific language governing permissions and | |||||
// limitations under the License. | |||||
package yaml | |||||
import ( | |||||
"encoding" | |||||
"encoding/base64" | |||||
"fmt" | |||||
"io" | |||||
"math" | |||||
"reflect" | |||||
"strconv" | |||||
"time" | |||||
) | |||||
// ---------------------------------------------------------------------------- | |||||
// Parser, produces a node tree out of a libyaml event stream. | |||||
type parser struct { | |||||
parser yaml_parser_t | |||||
event yaml_event_t | |||||
doc *Node | |||||
anchors map[string]*Node | |||||
doneInit bool | |||||
} | |||||
func newParser(b []byte) *parser { | |||||
p := parser{} | |||||
if !yaml_parser_initialize(&p.parser) { | |||||
panic("failed to initialize YAML emitter") | |||||
} | |||||
if len(b) == 0 { | |||||
b = []byte{'\n'} | |||||
} | |||||
yaml_parser_set_input_string(&p.parser, b) | |||||
return &p | |||||
} | |||||
func newParserFromReader(r io.Reader) *parser { | |||||
p := parser{} | |||||
if !yaml_parser_initialize(&p.parser) { | |||||
panic("failed to initialize YAML emitter") | |||||
} | |||||
yaml_parser_set_input_reader(&p.parser, r) | |||||
return &p | |||||
} | |||||
func (p *parser) init() { | |||||
if p.doneInit { | |||||
return | |||||
} | |||||
p.anchors = make(map[string]*Node) | |||||
p.expect(yaml_STREAM_START_EVENT) | |||||
p.doneInit = true | |||||
} | |||||
func (p *parser) destroy() { | |||||
if p.event.typ != yaml_NO_EVENT { | |||||
yaml_event_delete(&p.event) | |||||
} | |||||
yaml_parser_delete(&p.parser) | |||||
} | |||||
// expect consumes an event from the event stream and | |||||
// checks that it's of the expected type. | |||||
func (p *parser) expect(e yaml_event_type_t) { | |||||
if p.event.typ == yaml_NO_EVENT { | |||||
if !yaml_parser_parse(&p.parser, &p.event) { | |||||
p.fail() | |||||
} | |||||
} | |||||
if p.event.typ == yaml_STREAM_END_EVENT { | |||||
failf("attempted to go past the end of stream; corrupted value?") | |||||
} | |||||
if p.event.typ != e { | |||||
p.parser.problem = fmt.Sprintf("expected %s event but got %s", e, p.event.typ) | |||||
p.fail() | |||||
} | |||||
yaml_event_delete(&p.event) | |||||
p.event.typ = yaml_NO_EVENT | |||||
} | |||||
// peek peeks at the next event in the event stream, | |||||
// puts the results into p.event and returns the event type. | |||||
func (p *parser) peek() yaml_event_type_t { | |||||
if p.event.typ != yaml_NO_EVENT { | |||||
return p.event.typ | |||||
} | |||||
if !yaml_parser_parse(&p.parser, &p.event) { | |||||
p.fail() | |||||
} | |||||
return p.event.typ | |||||
} | |||||
func (p *parser) fail() { | |||||
var where string | |||||
var line int | |||||
if p.parser.problem_mark.line != 0 { | |||||
line = p.parser.problem_mark.line | |||||
// Scanner errors don't iterate line before returning error | |||||
if p.parser.error == yaml_SCANNER_ERROR { | |||||
line++ | |||||
} | |||||
} else if p.parser.context_mark.line != 0 { | |||||
line = p.parser.context_mark.line | |||||
} | |||||
if line != 0 { | |||||
where = "line " + strconv.Itoa(line) + ": " | |||||
} | |||||
var msg string | |||||
if len(p.parser.problem) > 0 { | |||||
msg = p.parser.problem | |||||
} else { | |||||
msg = "unknown problem parsing YAML content" | |||||
} | |||||
failf("%s%s", where, msg) | |||||
} | |||||
func (p *parser) anchor(n *Node, anchor []byte) { | |||||
if anchor != nil { | |||||
n.Anchor = string(anchor) | |||||
p.anchors[n.Anchor] = n | |||||
} | |||||
} | |||||
func (p *parser) parse() *Node { | |||||
p.init() | |||||
switch p.peek() { | |||||
case yaml_SCALAR_EVENT: | |||||
return p.scalar() | |||||
case yaml_ALIAS_EVENT: | |||||
return p.alias() | |||||
case yaml_MAPPING_START_EVENT: | |||||
return p.mapping() | |||||
case yaml_SEQUENCE_START_EVENT: | |||||
return p.sequence() | |||||
case yaml_DOCUMENT_START_EVENT: | |||||
return p.document() | |||||
case yaml_STREAM_END_EVENT: | |||||
// Happens when attempting to decode an empty buffer. | |||||
return nil | |||||
case yaml_TAIL_COMMENT_EVENT: | |||||
panic("internal error: unexpected tail comment event (please report)") | |||||
default: | |||||
panic("internal error: attempted to parse unknown event (please report): " + p.event.typ.String()) | |||||
} | |||||
} | |||||
func (p *parser) node(kind Kind, defaultTag, tag, value string) *Node { | |||||
var style Style | |||||
if tag != "" && tag != "!" { | |||||
tag = shortTag(tag) | |||||
style = TaggedStyle | |||||
} else if defaultTag != "" { | |||||
tag = defaultTag | |||||
} else if kind == ScalarNode { | |||||
tag, _ = resolve("", value) | |||||
} | |||||
return &Node{ | |||||
Kind: kind, | |||||
Tag: tag, | |||||
Value: value, | |||||
Style: style, | |||||
Line: p.event.start_mark.line + 1, | |||||
Column: p.event.start_mark.column + 1, | |||||
HeadComment: string(p.event.head_comment), | |||||
LineComment: string(p.event.line_comment), | |||||
FootComment: string(p.event.foot_comment), | |||||
} | |||||
} | |||||
func (p *parser) parseChild(parent *Node) *Node { | |||||
child := p.parse() | |||||
parent.Content = append(parent.Content, child) | |||||
return child | |||||
} | |||||
func (p *parser) document() *Node { | |||||
n := p.node(DocumentNode, "", "", "") | |||||
p.doc = n | |||||
p.expect(yaml_DOCUMENT_START_EVENT) | |||||
p.parseChild(n) | |||||
if p.peek() == yaml_DOCUMENT_END_EVENT { | |||||
n.FootComment = string(p.event.foot_comment) | |||||
} | |||||
p.expect(yaml_DOCUMENT_END_EVENT) | |||||
return n | |||||
} | |||||
func (p *parser) alias() *Node { | |||||
n := p.node(AliasNode, "", "", string(p.event.anchor)) | |||||
n.Alias = p.anchors[n.Value] | |||||
if n.Alias == nil { | |||||
failf("unknown anchor '%s' referenced", n.Value) | |||||
} | |||||
p.expect(yaml_ALIAS_EVENT) | |||||
return n | |||||
} | |||||
func (p *parser) scalar() *Node { | |||||
var parsedStyle = p.event.scalar_style() | |||||
var nodeStyle Style | |||||
switch { | |||||
case parsedStyle&yaml_DOUBLE_QUOTED_SCALAR_STYLE != 0: | |||||
nodeStyle = DoubleQuotedStyle | |||||
case parsedStyle&yaml_SINGLE_QUOTED_SCALAR_STYLE != 0: | |||||
nodeStyle = SingleQuotedStyle | |||||
case parsedStyle&yaml_LITERAL_SCALAR_STYLE != 0: | |||||
nodeStyle = LiteralStyle | |||||
case parsedStyle&yaml_FOLDED_SCALAR_STYLE != 0: | |||||
nodeStyle = FoldedStyle | |||||
} | |||||
var nodeValue = string(p.event.value) | |||||
var nodeTag = string(p.event.tag) | |||||
var defaultTag string | |||||
if nodeStyle == 0 { | |||||
if nodeValue == "<<" { | |||||
defaultTag = mergeTag | |||||
} | |||||
} else { | |||||
defaultTag = strTag | |||||
} | |||||
n := p.node(ScalarNode, defaultTag, nodeTag, nodeValue) | |||||
n.Style |= nodeStyle | |||||
p.anchor(n, p.event.anchor) | |||||
p.expect(yaml_SCALAR_EVENT) | |||||
return n | |||||
} | |||||
func (p *parser) sequence() *Node { | |||||
n := p.node(SequenceNode, seqTag, string(p.event.tag), "") | |||||
if p.event.sequence_style()&yaml_FLOW_SEQUENCE_STYLE != 0 { | |||||
n.Style |= FlowStyle | |||||
} | |||||
p.anchor(n, p.event.anchor) | |||||
p.expect(yaml_SEQUENCE_START_EVENT) | |||||
for p.peek() != yaml_SEQUENCE_END_EVENT { | |||||
p.parseChild(n) | |||||
} | |||||
n.LineComment = string(p.event.line_comment) | |||||
n.FootComment = string(p.event.foot_comment) | |||||
p.expect(yaml_SEQUENCE_END_EVENT) | |||||
return n | |||||
} | |||||
func (p *parser) mapping() *Node { | |||||
n := p.node(MappingNode, mapTag, string(p.event.tag), "") | |||||
block := true | |||||
if p.event.mapping_style()&yaml_FLOW_MAPPING_STYLE != 0 { | |||||
block = false | |||||
n.Style |= FlowStyle | |||||
} | |||||
p.anchor(n, p.event.anchor) | |||||
p.expect(yaml_MAPPING_START_EVENT) | |||||
for p.peek() != yaml_MAPPING_END_EVENT { | |||||
k := p.parseChild(n) | |||||
if block && k.FootComment != "" { | |||||
// Must be a foot comment for the prior value when being dedented. | |||||
if len(n.Content) > 2 { | |||||
n.Content[len(n.Content)-3].FootComment = k.FootComment | |||||
k.FootComment = "" | |||||
} | |||||
} | |||||
v := p.parseChild(n) | |||||
if k.FootComment == "" && v.FootComment != "" { | |||||
k.FootComment = v.FootComment | |||||
v.FootComment = "" | |||||
} | |||||
if p.peek() == yaml_TAIL_COMMENT_EVENT { | |||||
if k.FootComment == "" { | |||||
k.FootComment = string(p.event.foot_comment) | |||||
} | |||||
p.expect(yaml_TAIL_COMMENT_EVENT) | |||||
} | |||||
} | |||||
n.LineComment = string(p.event.line_comment) | |||||
n.FootComment = string(p.event.foot_comment) | |||||
if n.Style&FlowStyle == 0 && n.FootComment != "" && len(n.Content) > 1 { | |||||
n.Content[len(n.Content)-2].FootComment = n.FootComment | |||||
n.FootComment = "" | |||||
} | |||||
p.expect(yaml_MAPPING_END_EVENT) | |||||
return n | |||||
} | |||||
// ---------------------------------------------------------------------------- | |||||
// Decoder, unmarshals a node into a provided value. | |||||
type decoder struct { | |||||
doc *Node | |||||
aliases map[*Node]bool | |||||
terrors []string | |||||
stringMapType reflect.Type | |||||
generalMapType reflect.Type | |||||
knownFields bool | |||||
uniqueKeys bool | |||||
decodeCount int | |||||
aliasCount int | |||||
aliasDepth int | |||||
} | |||||
var ( | |||||
nodeType = reflect.TypeOf(Node{}) | |||||
durationType = reflect.TypeOf(time.Duration(0)) | |||||
stringMapType = reflect.TypeOf(map[string]interface{}{}) | |||||
generalMapType = reflect.TypeOf(map[interface{}]interface{}{}) | |||||
ifaceType = generalMapType.Elem() | |||||
timeType = reflect.TypeOf(time.Time{}) | |||||
ptrTimeType = reflect.TypeOf(&time.Time{}) | |||||
) | |||||
func newDecoder() *decoder { | |||||
d := &decoder{ | |||||
stringMapType: stringMapType, | |||||
generalMapType: generalMapType, | |||||
uniqueKeys: true, | |||||
} | |||||
d.aliases = make(map[*Node]bool) | |||||
return d | |||||
} | |||||
func (d *decoder) terror(n *Node, tag string, out reflect.Value) { | |||||
if n.Tag != "" { | |||||
tag = n.Tag | |||||
} | |||||
value := n.Value | |||||
if tag != seqTag && tag != mapTag { | |||||
if len(value) > 10 { | |||||
value = " `" + value[:7] + "...`" | |||||
} else { | |||||
value = " `" + value + "`" | |||||
} | |||||
} | |||||
d.terrors = append(d.terrors, fmt.Sprintf("line %d: cannot unmarshal %s%s into %s", n.Line, shortTag(tag), value, out.Type())) | |||||
} | |||||
func (d *decoder) callUnmarshaler(n *Node, u Unmarshaler) (good bool) { | |||||
err := u.UnmarshalYAML(n) | |||||
if e, ok := err.(*TypeError); ok { | |||||
d.terrors = append(d.terrors, e.Errors...) | |||||
return false | |||||
} | |||||
if err != nil { | |||||
fail(err) | |||||
} | |||||
return true | |||||
} | |||||
func (d *decoder) callObsoleteUnmarshaler(n *Node, u obsoleteUnmarshaler) (good bool) { | |||||
terrlen := len(d.terrors) | |||||
err := u.UnmarshalYAML(func(v interface{}) (err error) { | |||||
defer handleErr(&err) | |||||
d.unmarshal(n, reflect.ValueOf(v)) | |||||
if len(d.terrors) > terrlen { | |||||
issues := d.terrors[terrlen:] | |||||
d.terrors = d.terrors[:terrlen] | |||||
return &TypeError{issues} | |||||
} | |||||
return nil | |||||
}) | |||||
if e, ok := err.(*TypeError); ok { | |||||
d.terrors = append(d.terrors, e.Errors...) | |||||
return false | |||||
} | |||||
if err != nil { | |||||
fail(err) | |||||
} | |||||
return true | |||||
} | |||||
// d.prepare initializes and dereferences pointers and calls UnmarshalYAML | |||||
// if a value is found to implement it. | |||||
// It returns the initialized and dereferenced out value, whether | |||||
// unmarshalling was already done by UnmarshalYAML, and if so whether | |||||
// its types unmarshalled appropriately. | |||||
// | |||||
// If n holds a null value, prepare returns before doing anything. | |||||
func (d *decoder) prepare(n *Node, out reflect.Value) (newout reflect.Value, unmarshaled, good bool) { | |||||
if n.ShortTag() == nullTag { | |||||
return out, false, false | |||||
} | |||||
again := true | |||||
for again { | |||||
again = false | |||||
if out.Kind() == reflect.Ptr { | |||||
if out.IsNil() { | |||||
out.Set(reflect.New(out.Type().Elem())) | |||||
} | |||||
out = out.Elem() | |||||
again = true | |||||
} | |||||
if out.CanAddr() { | |||||
outi := out.Addr().Interface() | |||||
if u, ok := outi.(Unmarshaler); ok { | |||||
good = d.callUnmarshaler(n, u) | |||||
return out, true, good | |||||
} | |||||
if u, ok := outi.(obsoleteUnmarshaler); ok { | |||||
good = d.callObsoleteUnmarshaler(n, u) | |||||
return out, true, good | |||||
} | |||||
} | |||||
} | |||||
return out, false, false | |||||
} | |||||
func (d *decoder) fieldByIndex(n *Node, v reflect.Value, index []int) (field reflect.Value) { | |||||
if n.ShortTag() == nullTag { | |||||
return reflect.Value{} | |||||
} | |||||
for _, num := range index { | |||||
for { | |||||
if v.Kind() == reflect.Ptr { | |||||
if v.IsNil() { | |||||
v.Set(reflect.New(v.Type().Elem())) | |||||
} | |||||
v = v.Elem() | |||||
continue | |||||
} | |||||
break | |||||
} | |||||
v = v.Field(num) | |||||
} | |||||
return v | |||||
} | |||||
const ( | |||||
// 400,000 decode operations is ~500kb of dense object declarations, or | |||||
// ~5kb of dense object declarations with 10000% alias expansion | |||||
alias_ratio_range_low = 400000 | |||||
// 4,000,000 decode operations is ~5MB of dense object declarations, or | |||||
// ~4.5MB of dense object declarations with 10% alias expansion | |||||
alias_ratio_range_high = 4000000 | |||||
// alias_ratio_range is the range over which we scale allowed alias ratios | |||||
alias_ratio_range = float64(alias_ratio_range_high - alias_ratio_range_low) | |||||
) | |||||
func allowedAliasRatio(decodeCount int) float64 { | |||||
switch { | |||||
case decodeCount <= alias_ratio_range_low: | |||||
// allow 99% to come from alias expansion for small-to-medium documents | |||||
return 0.99 | |||||
case decodeCount >= alias_ratio_range_high: | |||||
// allow 10% to come from alias expansion for very large documents | |||||
return 0.10 | |||||
default: | |||||
// scale smoothly from 99% down to 10% over the range. | |||||
// this maps to 396,000 - 400,000 allowed alias-driven decodes over the range. | |||||
// 400,000 decode operations is ~100MB of allocations in worst-case scenarios (single-item maps). | |||||
return 0.99 - 0.89*(float64(decodeCount-alias_ratio_range_low)/alias_ratio_range) | |||||
} | |||||
} | |||||
func (d *decoder) unmarshal(n *Node, out reflect.Value) (good bool) { | |||||
d.decodeCount++ | |||||
if d.aliasDepth > 0 { | |||||
d.aliasCount++ | |||||
} | |||||
if d.aliasCount > 100 && d.decodeCount > 1000 && float64(d.aliasCount)/float64(d.decodeCount) > allowedAliasRatio(d.decodeCount) { | |||||
failf("document contains excessive aliasing") | |||||
} | |||||
if out.Type() == nodeType { | |||||
out.Set(reflect.ValueOf(n).Elem()) | |||||
return true | |||||
} | |||||
switch n.Kind { | |||||
case DocumentNode: | |||||
return d.document(n, out) | |||||
case AliasNode: | |||||
return d.alias(n, out) | |||||
} | |||||
out, unmarshaled, good := d.prepare(n, out) | |||||
if unmarshaled { | |||||
return good | |||||
} | |||||
switch n.Kind { | |||||
case ScalarNode: | |||||
good = d.scalar(n, out) | |||||
case MappingNode: | |||||
good = d.mapping(n, out) | |||||
case SequenceNode: | |||||
good = d.sequence(n, out) | |||||
default: | |||||
panic("internal error: unknown node kind: " + strconv.Itoa(int(n.Kind))) | |||||
} | |||||
return good | |||||
} | |||||
func (d *decoder) document(n *Node, out reflect.Value) (good bool) { | |||||
if len(n.Content) == 1 { | |||||
d.doc = n | |||||
d.unmarshal(n.Content[0], out) | |||||
return true | |||||
} | |||||
return false | |||||
} | |||||
func (d *decoder) alias(n *Node, out reflect.Value) (good bool) { | |||||
if d.aliases[n] { | |||||
// TODO this could actually be allowed in some circumstances. | |||||
failf("anchor '%s' value contains itself", n.Value) | |||||
} | |||||
d.aliases[n] = true | |||||
d.aliasDepth++ | |||||
good = d.unmarshal(n.Alias, out) | |||||
d.aliasDepth-- | |||||
delete(d.aliases, n) | |||||
return good | |||||
} | |||||
var zeroValue reflect.Value | |||||
func resetMap(out reflect.Value) { | |||||
for _, k := range out.MapKeys() { | |||||
out.SetMapIndex(k, zeroValue) | |||||
} | |||||
} | |||||
func (d *decoder) scalar(n *Node, out reflect.Value) bool { | |||||
var tag string | |||||
var resolved interface{} | |||||
if n.indicatedString() { | |||||
tag = strTag | |||||
resolved = n.Value | |||||
} else { | |||||
tag, resolved = resolve(n.Tag, n.Value) | |||||
if tag == binaryTag { | |||||
data, err := base64.StdEncoding.DecodeString(resolved.(string)) | |||||
if err != nil { | |||||
failf("!!binary value contains invalid base64 data") | |||||
} | |||||
resolved = string(data) | |||||
} | |||||
} | |||||
if resolved == nil { | |||||
if out.CanAddr() { | |||||
switch out.Kind() { | |||||
case reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice: | |||||
out.Set(reflect.Zero(out.Type())) | |||||
return true | |||||
} | |||||
} | |||||
return false | |||||
} | |||||
if resolvedv := reflect.ValueOf(resolved); out.Type() == resolvedv.Type() { | |||||
// We've resolved to exactly the type we want, so use that. | |||||
out.Set(resolvedv) | |||||
return true | |||||
} | |||||
// Perhaps we can use the value as a TextUnmarshaler to | |||||
// set its value. | |||||
if out.CanAddr() { | |||||
u, ok := out.Addr().Interface().(encoding.TextUnmarshaler) | |||||
if ok { | |||||
var text []byte | |||||
if tag == binaryTag { | |||||
text = []byte(resolved.(string)) | |||||
} else { | |||||
// We let any value be unmarshaled into TextUnmarshaler. | |||||
// That might be more lax than we'd like, but the | |||||
// TextUnmarshaler itself should bowl out any dubious values. | |||||
text = []byte(n.Value) | |||||
} | |||||
err := u.UnmarshalText(text) | |||||
if err != nil { | |||||
fail(err) | |||||
} | |||||
return true | |||||
} | |||||
} | |||||
switch out.Kind() { | |||||
case reflect.String: | |||||
if tag == binaryTag { | |||||
out.SetString(resolved.(string)) | |||||
return true | |||||
} | |||||
out.SetString(n.Value) | |||||
return true | |||||
case reflect.Interface: | |||||
out.Set(reflect.ValueOf(resolved)) | |||||
return true | |||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | |||||
// This used to work in v2, but it's very unfriendly. | |||||
isDuration := out.Type() == durationType | |||||
switch resolved := resolved.(type) { | |||||
case int: | |||||
if !isDuration && !out.OverflowInt(int64(resolved)) { | |||||
out.SetInt(int64(resolved)) | |||||
return true | |||||
} | |||||
case int64: | |||||
if !isDuration && !out.OverflowInt(resolved) { | |||||
out.SetInt(resolved) | |||||
return true | |||||
} | |||||
case uint64: | |||||
if !isDuration && resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) { | |||||
out.SetInt(int64(resolved)) | |||||
return true | |||||
} | |||||
case float64: | |||||
if !isDuration && resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) { | |||||
out.SetInt(int64(resolved)) | |||||
return true | |||||
} | |||||
case string: | |||||
if out.Type() == durationType { | |||||
d, err := time.ParseDuration(resolved) | |||||
if err == nil { | |||||
out.SetInt(int64(d)) | |||||
return true | |||||
} | |||||
} | |||||
} | |||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: | |||||
switch resolved := resolved.(type) { | |||||
case int: | |||||
if resolved >= 0 && !out.OverflowUint(uint64(resolved)) { | |||||
out.SetUint(uint64(resolved)) | |||||
return true | |||||
} | |||||
case int64: | |||||
if resolved >= 0 && !out.OverflowUint(uint64(resolved)) { | |||||
out.SetUint(uint64(resolved)) | |||||
return true | |||||
} | |||||
case uint64: | |||||
if !out.OverflowUint(uint64(resolved)) { | |||||
out.SetUint(uint64(resolved)) | |||||
return true | |||||
} | |||||
case float64: | |||||
if resolved <= math.MaxUint64 && !out.OverflowUint(uint64(resolved)) { | |||||
out.SetUint(uint64(resolved)) | |||||
return true | |||||
} | |||||
} | |||||
case reflect.Bool: | |||||
switch resolved := resolved.(type) { | |||||
case bool: | |||||
out.SetBool(resolved) | |||||
return true | |||||
case string: | |||||
// This offers some compatibility with the 1.1 spec (https://yaml.org/type/bool.html). | |||||
// It only works if explicitly attempting to unmarshal into a typed bool value. | |||||
switch resolved { | |||||
case "y", "Y", "yes", "Yes", "YES", "on", "On", "ON": | |||||
out.SetBool(true) | |||||
return true | |||||
case "n", "N", "no", "No", "NO", "off", "Off", "OFF": | |||||
out.SetBool(false) | |||||
return true | |||||
} | |||||
} | |||||
case reflect.Float32, reflect.Float64: | |||||
switch resolved := resolved.(type) { | |||||
case int: | |||||
out.SetFloat(float64(resolved)) | |||||
return true | |||||
case int64: | |||||
out.SetFloat(float64(resolved)) | |||||
return true | |||||
case uint64: | |||||
out.SetFloat(float64(resolved)) | |||||
return true | |||||
case float64: | |||||
out.SetFloat(resolved) | |||||
return true | |||||
} | |||||
case reflect.Struct: | |||||
if resolvedv := reflect.ValueOf(resolved); out.Type() == resolvedv.Type() { | |||||
out.Set(resolvedv) | |||||
return true | |||||
} | |||||
case reflect.Ptr: | |||||
panic("yaml internal error: please report the issue") | |||||
} | |||||
d.terror(n, tag, out) | |||||
return false | |||||
} | |||||
func settableValueOf(i interface{}) reflect.Value { | |||||
v := reflect.ValueOf(i) | |||||
sv := reflect.New(v.Type()).Elem() | |||||
sv.Set(v) | |||||
return sv | |||||
} | |||||
func (d *decoder) sequence(n *Node, out reflect.Value) (good bool) { | |||||
l := len(n.Content) | |||||
var iface reflect.Value | |||||
switch out.Kind() { | |||||
case reflect.Slice: | |||||
out.Set(reflect.MakeSlice(out.Type(), l, l)) | |||||
case reflect.Array: | |||||
if l != out.Len() { | |||||
failf("invalid array: want %d elements but got %d", out.Len(), l) | |||||
} | |||||
case reflect.Interface: | |||||
// No type hints. Will have to use a generic sequence. | |||||
iface = out | |||||
out = settableValueOf(make([]interface{}, l)) | |||||
default: | |||||
d.terror(n, seqTag, out) | |||||
return false | |||||
} | |||||
et := out.Type().Elem() | |||||
j := 0 | |||||
for i := 0; i < l; i++ { | |||||
e := reflect.New(et).Elem() | |||||
if ok := d.unmarshal(n.Content[i], e); ok { | |||||
out.Index(j).Set(e) | |||||
j++ | |||||
} | |||||
} | |||||
if out.Kind() != reflect.Array { | |||||
out.Set(out.Slice(0, j)) | |||||
} | |||||
if iface.IsValid() { | |||||
iface.Set(out) | |||||
} | |||||
return true | |||||
} | |||||
func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) { | |||||
l := len(n.Content) | |||||
if d.uniqueKeys { | |||||
nerrs := len(d.terrors) | |||||
for i := 0; i < l; i += 2 { | |||||
ni := n.Content[i] | |||||
for j := i + 2; j < l; j += 2 { | |||||
nj := n.Content[j] | |||||
if ni.Kind == nj.Kind && ni.Value == nj.Value { | |||||
d.terrors = append(d.terrors, fmt.Sprintf("line %d: mapping key %#v already defined at line %d", nj.Line, nj.Value, ni.Line)) | |||||
} | |||||
} | |||||
} | |||||
if len(d.terrors) > nerrs { | |||||
return false | |||||
} | |||||
} | |||||
switch out.Kind() { | |||||
case reflect.Struct: | |||||
return d.mappingStruct(n, out) | |||||
case reflect.Map: | |||||
// okay | |||||
case reflect.Interface: | |||||
iface := out | |||||
if isStringMap(n) { | |||||
out = reflect.MakeMap(d.stringMapType) | |||||
} else { | |||||
out = reflect.MakeMap(d.generalMapType) | |||||
} | |||||
iface.Set(out) | |||||
default: | |||||
d.terror(n, mapTag, out) | |||||
return false | |||||
} | |||||
outt := out.Type() | |||||
kt := outt.Key() | |||||
et := outt.Elem() | |||||
stringMapType := d.stringMapType | |||||
generalMapType := d.generalMapType | |||||
if outt.Elem() == ifaceType { | |||||
if outt.Key().Kind() == reflect.String { | |||||
d.stringMapType = outt | |||||
} else if outt.Key() == ifaceType { | |||||
d.generalMapType = outt | |||||
} | |||||
} | |||||
if out.IsNil() { | |||||
out.Set(reflect.MakeMap(outt)) | |||||
} | |||||
for i := 0; i < l; i += 2 { | |||||
if isMerge(n.Content[i]) { | |||||
d.merge(n.Content[i+1], out) | |||||
continue | |||||
} | |||||
k := reflect.New(kt).Elem() | |||||
if d.unmarshal(n.Content[i], k) { | |||||
kkind := k.Kind() | |||||
if kkind == reflect.Interface { | |||||
kkind = k.Elem().Kind() | |||||
} | |||||
if kkind == reflect.Map || kkind == reflect.Slice { | |||||
failf("invalid map key: %#v", k.Interface()) | |||||
} | |||||
e := reflect.New(et).Elem() | |||||
if d.unmarshal(n.Content[i+1], e) { | |||||
out.SetMapIndex(k, e) | |||||
} | |||||
} | |||||
} | |||||
d.stringMapType = stringMapType | |||||
d.generalMapType = generalMapType | |||||
return true | |||||
} | |||||
func isStringMap(n *Node) bool { | |||||
if n.Kind != MappingNode { | |||||
return false | |||||
} | |||||
l := len(n.Content) | |||||
for i := 0; i < l; i += 2 { | |||||
if n.Content[i].ShortTag() != strTag { | |||||
return false | |||||
} | |||||
} | |||||
return true | |||||
} | |||||
func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) { | |||||
sinfo, err := getStructInfo(out.Type()) | |||||
if err != nil { | |||||
panic(err) | |||||
} | |||||
var inlineMap reflect.Value | |||||
var elemType reflect.Type | |||||
if sinfo.InlineMap != -1 { | |||||
inlineMap = out.Field(sinfo.InlineMap) | |||||
inlineMap.Set(reflect.New(inlineMap.Type()).Elem()) | |||||
elemType = inlineMap.Type().Elem() | |||||
} | |||||
for _, index := range sinfo.InlineUnmarshalers { | |||||
field := d.fieldByIndex(n, out, index) | |||||
d.prepare(n, field) | |||||
} | |||||
var doneFields []bool | |||||
if d.uniqueKeys { | |||||
doneFields = make([]bool, len(sinfo.FieldsList)) | |||||
} | |||||
name := settableValueOf("") | |||||
l := len(n.Content) | |||||
for i := 0; i < l; i += 2 { | |||||
ni := n.Content[i] | |||||
if isMerge(ni) { | |||||
d.merge(n.Content[i+1], out) | |||||
continue | |||||
} | |||||
if !d.unmarshal(ni, name) { | |||||
continue | |||||
} | |||||
if info, ok := sinfo.FieldsMap[name.String()]; ok { | |||||
if d.uniqueKeys { | |||||
if doneFields[info.Id] { | |||||
d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s already set in type %s", ni.Line, name.String(), out.Type())) | |||||
continue | |||||
} | |||||
doneFields[info.Id] = true | |||||
} | |||||
var field reflect.Value | |||||
if info.Inline == nil { | |||||
field = out.Field(info.Num) | |||||
} else { | |||||
field = d.fieldByIndex(n, out, info.Inline) | |||||
} | |||||
d.unmarshal(n.Content[i+1], field) | |||||
} else if sinfo.InlineMap != -1 { | |||||
if inlineMap.IsNil() { | |||||
inlineMap.Set(reflect.MakeMap(inlineMap.Type())) | |||||
} | |||||
value := reflect.New(elemType).Elem() | |||||
d.unmarshal(n.Content[i+1], value) | |||||
inlineMap.SetMapIndex(name, value) | |||||
} else if d.knownFields { | |||||
d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s not found in type %s", ni.Line, name.String(), out.Type())) | |||||
} | |||||
} | |||||
return true | |||||
} | |||||
func failWantMap() { | |||||
failf("map merge requires map or sequence of maps as the value") | |||||
} | |||||
func (d *decoder) merge(n *Node, out reflect.Value) { | |||||
switch n.Kind { | |||||
case MappingNode: | |||||
d.unmarshal(n, out) | |||||
case AliasNode: | |||||
if n.Alias != nil && n.Alias.Kind != MappingNode { | |||||
failWantMap() | |||||
} | |||||
d.unmarshal(n, out) | |||||
case SequenceNode: | |||||
// Step backwards as earlier nodes take precedence. | |||||
for i := len(n.Content) - 1; i >= 0; i-- { | |||||
ni := n.Content[i] | |||||
if ni.Kind == AliasNode { | |||||
if ni.Alias != nil && ni.Alias.Kind != MappingNode { | |||||
failWantMap() | |||||
} | |||||
} else if ni.Kind != MappingNode { | |||||
failWantMap() | |||||
} | |||||
d.unmarshal(ni, out) | |||||
} | |||||
default: | |||||
failWantMap() | |||||
} | |||||
} | |||||
func isMerge(n *Node) bool { | |||||
return n.Kind == ScalarNode && n.Value == "<<" && (n.Tag == "" || n.Tag == "!" || shortTag(n.Tag) == mergeTag) | |||||
} |
@@ -0,0 +1,561 @@ | |||||
// | |||||
// Copyright (c) 2011-2019 Canonical Ltd | |||||
// | |||||
// Licensed under the Apache License, Version 2.0 (the "License"); | |||||
// you may not use this file except in compliance with the License. | |||||
// You may obtain a copy of the License at | |||||
// | |||||
// http://www.apache.org/licenses/LICENSE-2.0 | |||||
// | |||||
// Unless required by applicable law or agreed to in writing, software | |||||
// distributed under the License is distributed on an "AS IS" BASIS, | |||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
// See the License for the specific language governing permissions and | |||||
// limitations under the License. | |||||
package yaml | |||||
import ( | |||||
"encoding" | |||||
"fmt" | |||||
"io" | |||||
"reflect" | |||||
"regexp" | |||||
"sort" | |||||
"strconv" | |||||
"strings" | |||||
"time" | |||||
"unicode/utf8" | |||||
) | |||||
type encoder struct { | |||||
emitter yaml_emitter_t | |||||
event yaml_event_t | |||||
out []byte | |||||
flow bool | |||||
indent int | |||||
doneInit bool | |||||
} | |||||
func newEncoder() *encoder { | |||||
e := &encoder{} | |||||
yaml_emitter_initialize(&e.emitter) | |||||
yaml_emitter_set_output_string(&e.emitter, &e.out) | |||||
yaml_emitter_set_unicode(&e.emitter, true) | |||||
return e | |||||
} | |||||
func newEncoderWithWriter(w io.Writer) *encoder { | |||||
e := &encoder{} | |||||
yaml_emitter_initialize(&e.emitter) | |||||
yaml_emitter_set_output_writer(&e.emitter, w) | |||||
yaml_emitter_set_unicode(&e.emitter, true) | |||||
return e | |||||
} | |||||
func (e *encoder) init() { | |||||
if e.doneInit { | |||||
return | |||||
} | |||||
if e.indent == 0 { | |||||
e.indent = 4 | |||||
} | |||||
e.emitter.best_indent = e.indent | |||||
yaml_stream_start_event_initialize(&e.event, yaml_UTF8_ENCODING) | |||||
e.emit() | |||||
e.doneInit = true | |||||
} | |||||
func (e *encoder) finish() { | |||||
e.emitter.open_ended = false | |||||
yaml_stream_end_event_initialize(&e.event) | |||||
e.emit() | |||||
} | |||||
func (e *encoder) destroy() { | |||||
yaml_emitter_delete(&e.emitter) | |||||
} | |||||
func (e *encoder) emit() { | |||||
// This will internally delete the e.event value. | |||||
e.must(yaml_emitter_emit(&e.emitter, &e.event)) | |||||
} | |||||
func (e *encoder) must(ok bool) { | |||||
if !ok { | |||||
msg := e.emitter.problem | |||||
if msg == "" { | |||||
msg = "unknown problem generating YAML content" | |||||
} | |||||
failf("%s", msg) | |||||
} | |||||
} | |||||
func (e *encoder) marshalDoc(tag string, in reflect.Value) { | |||||
e.init() | |||||
var node *Node | |||||
if in.IsValid() { | |||||
node, _ = in.Interface().(*Node) | |||||
} | |||||
if node != nil && node.Kind == DocumentNode { | |||||
e.nodev(in) | |||||
} else { | |||||
yaml_document_start_event_initialize(&e.event, nil, nil, true) | |||||
e.emit() | |||||
e.marshal(tag, in) | |||||
yaml_document_end_event_initialize(&e.event, true) | |||||
e.emit() | |||||
} | |||||
} | |||||
func (e *encoder) marshal(tag string, in reflect.Value) { | |||||
tag = shortTag(tag) | |||||
if !in.IsValid() || in.Kind() == reflect.Ptr && in.IsNil() { | |||||
e.nilv() | |||||
return | |||||
} | |||||
iface := in.Interface() | |||||
switch value := iface.(type) { | |||||
case *Node: | |||||
e.nodev(in) | |||||
return | |||||
case time.Time: | |||||
e.timev(tag, in) | |||||
return | |||||
case *time.Time: | |||||
e.timev(tag, in.Elem()) | |||||
return | |||||
case time.Duration: | |||||
e.stringv(tag, reflect.ValueOf(value.String())) | |||||
return | |||||
case Marshaler: | |||||
v, err := value.MarshalYAML() | |||||
if err != nil { | |||||
fail(err) | |||||
} | |||||
if v == nil { | |||||
e.nilv() | |||||
return | |||||
} | |||||
e.marshal(tag, reflect.ValueOf(v)) | |||||
return | |||||
case encoding.TextMarshaler: | |||||
text, err := value.MarshalText() | |||||
if err != nil { | |||||
fail(err) | |||||
} | |||||
in = reflect.ValueOf(string(text)) | |||||
case nil: | |||||
e.nilv() | |||||
return | |||||
} | |||||
switch in.Kind() { | |||||
case reflect.Interface: | |||||
e.marshal(tag, in.Elem()) | |||||
case reflect.Map: | |||||
e.mapv(tag, in) | |||||
case reflect.Ptr: | |||||
e.marshal(tag, in.Elem()) | |||||
case reflect.Struct: | |||||
e.structv(tag, in) | |||||
case reflect.Slice, reflect.Array: | |||||
e.slicev(tag, in) | |||||
case reflect.String: | |||||
e.stringv(tag, in) | |||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | |||||
e.intv(tag, in) | |||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: | |||||
e.uintv(tag, in) | |||||
case reflect.Float32, reflect.Float64: | |||||
e.floatv(tag, in) | |||||
case reflect.Bool: | |||||
e.boolv(tag, in) | |||||
default: | |||||
panic("cannot marshal type: " + in.Type().String()) | |||||
} | |||||
} | |||||
func (e *encoder) mapv(tag string, in reflect.Value) { | |||||
e.mappingv(tag, func() { | |||||
keys := keyList(in.MapKeys()) | |||||
sort.Sort(keys) | |||||
for _, k := range keys { | |||||
e.marshal("", k) | |||||
e.marshal("", in.MapIndex(k)) | |||||
} | |||||
}) | |||||
} | |||||
func (e *encoder) fieldByIndex(v reflect.Value, index []int) (field reflect.Value) { | |||||
for _, num := range index { | |||||
for { | |||||
if v.Kind() == reflect.Ptr { | |||||
if v.IsNil() { | |||||
return reflect.Value{} | |||||
} | |||||
v = v.Elem() | |||||
continue | |||||
} | |||||
break | |||||
} | |||||
v = v.Field(num) | |||||
} | |||||
return v | |||||
} | |||||
func (e *encoder) structv(tag string, in reflect.Value) { | |||||
sinfo, err := getStructInfo(in.Type()) | |||||
if err != nil { | |||||
panic(err) | |||||
} | |||||
e.mappingv(tag, func() { | |||||
for _, info := range sinfo.FieldsList { | |||||
var value reflect.Value | |||||
if info.Inline == nil { | |||||
value = in.Field(info.Num) | |||||
} else { | |||||
value = e.fieldByIndex(in, info.Inline) | |||||
if !value.IsValid() { | |||||
continue | |||||
} | |||||
} | |||||
if info.OmitEmpty && isZero(value) { | |||||
continue | |||||
} | |||||
e.marshal("", reflect.ValueOf(info.Key)) | |||||
e.flow = info.Flow | |||||
e.marshal("", value) | |||||
} | |||||
if sinfo.InlineMap >= 0 { | |||||
m := in.Field(sinfo.InlineMap) | |||||
if m.Len() > 0 { | |||||
e.flow = false | |||||
keys := keyList(m.MapKeys()) | |||||
sort.Sort(keys) | |||||
for _, k := range keys { | |||||
if _, found := sinfo.FieldsMap[k.String()]; found { | |||||
panic(fmt.Sprintf("cannot have key %q in inlined map: conflicts with struct field", k.String())) | |||||
} | |||||
e.marshal("", k) | |||||
e.flow = false | |||||
e.marshal("", m.MapIndex(k)) | |||||
} | |||||
} | |||||
} | |||||
}) | |||||
} | |||||
func (e *encoder) mappingv(tag string, f func()) { | |||||
implicit := tag == "" | |||||
style := yaml_BLOCK_MAPPING_STYLE | |||||
if e.flow { | |||||
e.flow = false | |||||
style = yaml_FLOW_MAPPING_STYLE | |||||
} | |||||
yaml_mapping_start_event_initialize(&e.event, nil, []byte(tag), implicit, style) | |||||
e.emit() | |||||
f() | |||||
yaml_mapping_end_event_initialize(&e.event) | |||||
e.emit() | |||||
} | |||||
func (e *encoder) slicev(tag string, in reflect.Value) { | |||||
implicit := tag == "" | |||||
style := yaml_BLOCK_SEQUENCE_STYLE | |||||
if e.flow { | |||||
e.flow = false | |||||
style = yaml_FLOW_SEQUENCE_STYLE | |||||
} | |||||
e.must(yaml_sequence_start_event_initialize(&e.event, nil, []byte(tag), implicit, style)) | |||||
e.emit() | |||||
n := in.Len() | |||||
for i := 0; i < n; i++ { | |||||
e.marshal("", in.Index(i)) | |||||
} | |||||
e.must(yaml_sequence_end_event_initialize(&e.event)) | |||||
e.emit() | |||||
} | |||||
// isBase60 returns whether s is in base 60 notation as defined in YAML 1.1. | |||||
// | |||||
// The base 60 float notation in YAML 1.1 is a terrible idea and is unsupported | |||||
// in YAML 1.2 and by this package, but these should be marshalled quoted for | |||||
// the time being for compatibility with other parsers. | |||||
func isBase60Float(s string) (result bool) { | |||||
// Fast path. | |||||
if s == "" { | |||||
return false | |||||
} | |||||
c := s[0] | |||||
if !(c == '+' || c == '-' || c >= '0' && c <= '9') || strings.IndexByte(s, ':') < 0 { | |||||
return false | |||||
} | |||||
// Do the full match. | |||||
return base60float.MatchString(s) | |||||
} | |||||
// From http://yaml.org/type/float.html, except the regular expression there | |||||
// is bogus. In practice parsers do not enforce the "\.[0-9_]*" suffix. | |||||
var base60float = regexp.MustCompile(`^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+(?:\.[0-9_]*)?$`) | |||||
// isOldBool returns whether s is bool notation as defined in YAML 1.1. | |||||
// | |||||
// We continue to force strings that YAML 1.1 would interpret as booleans to be | |||||
// rendered as quotes strings so that the marshalled output valid for YAML 1.1 | |||||
// parsing. | |||||
func isOldBool(s string) (result bool) { | |||||
switch s { | |||||
case "y", "Y", "yes", "Yes", "YES", "on", "On", "ON", | |||||
"n", "N", "no", "No", "NO", "off", "Off", "OFF": | |||||
return true | |||||
default: | |||||
return false | |||||
} | |||||
} | |||||
func (e *encoder) stringv(tag string, in reflect.Value) { | |||||
var style yaml_scalar_style_t | |||||
s := in.String() | |||||
canUsePlain := true | |||||
switch { | |||||
case !utf8.ValidString(s): | |||||
if tag == binaryTag { | |||||
failf("explicitly tagged !!binary data must be base64-encoded") | |||||
} | |||||
if tag != "" { | |||||
failf("cannot marshal invalid UTF-8 data as %s", shortTag(tag)) | |||||
} | |||||
// It can't be encoded directly as YAML so use a binary tag | |||||
// and encode it as base64. | |||||
tag = binaryTag | |||||
s = encodeBase64(s) | |||||
case tag == "": | |||||
// Check to see if it would resolve to a specific | |||||
// tag when encoded unquoted. If it doesn't, | |||||
// there's no need to quote it. | |||||
rtag, _ := resolve("", s) | |||||
canUsePlain = rtag == strTag && !(isBase60Float(s) || isOldBool(s)) | |||||
} | |||||
// Note: it's possible for user code to emit invalid YAML | |||||
// if they explicitly specify a tag and a string containing | |||||
// text that's incompatible with that tag. | |||||
switch { | |||||
case strings.Contains(s, "\n"): | |||||
if e.flow { | |||||
style = yaml_DOUBLE_QUOTED_SCALAR_STYLE | |||||
} else { | |||||
style = yaml_LITERAL_SCALAR_STYLE | |||||
} | |||||
case canUsePlain: | |||||
style = yaml_PLAIN_SCALAR_STYLE | |||||
default: | |||||
style = yaml_DOUBLE_QUOTED_SCALAR_STYLE | |||||
} | |||||
e.emitScalar(s, "", tag, style, nil, nil, nil, nil) | |||||
} | |||||
func (e *encoder) boolv(tag string, in reflect.Value) { | |||||
var s string | |||||
if in.Bool() { | |||||
s = "true" | |||||
} else { | |||||
s = "false" | |||||
} | |||||
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) | |||||
} | |||||
func (e *encoder) intv(tag string, in reflect.Value) { | |||||
s := strconv.FormatInt(in.Int(), 10) | |||||
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) | |||||
} | |||||
func (e *encoder) uintv(tag string, in reflect.Value) { | |||||
s := strconv.FormatUint(in.Uint(), 10) | |||||
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) | |||||
} | |||||
func (e *encoder) timev(tag string, in reflect.Value) { | |||||
t := in.Interface().(time.Time) | |||||
s := t.Format(time.RFC3339Nano) | |||||
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) | |||||
} | |||||
func (e *encoder) floatv(tag string, in reflect.Value) { | |||||
// Issue #352: When formatting, use the precision of the underlying value | |||||
precision := 64 | |||||
if in.Kind() == reflect.Float32 { | |||||
precision = 32 | |||||
} | |||||
s := strconv.FormatFloat(in.Float(), 'g', -1, precision) | |||||
switch s { | |||||
case "+Inf": | |||||
s = ".inf" | |||||
case "-Inf": | |||||
s = "-.inf" | |||||
case "NaN": | |||||
s = ".nan" | |||||
} | |||||
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) | |||||
} | |||||
func (e *encoder) nilv() { | |||||
e.emitScalar("null", "", "", yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) | |||||
} | |||||
func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t, head, line, foot, tail []byte) { | |||||
// TODO Kill this function. Replace all initialize calls by their underlining Go literals. | |||||
implicit := tag == "" | |||||
if !implicit { | |||||
tag = longTag(tag) | |||||
} | |||||
e.must(yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(tag), []byte(value), implicit, implicit, style)) | |||||
e.event.head_comment = head | |||||
e.event.line_comment = line | |||||
e.event.foot_comment = foot | |||||
e.event.tail_comment = tail | |||||
e.emit() | |||||
} | |||||
func (e *encoder) nodev(in reflect.Value) { | |||||
e.node(in.Interface().(*Node), "") | |||||
} | |||||
func (e *encoder) node(node *Node, tail string) { | |||||
// If the tag was not explicitly requested, and dropping it won't change the | |||||
// implicit tag of the value, don't include it in the presentation. | |||||
var tag = node.Tag | |||||
var stag = shortTag(tag) | |||||
var rtag string | |||||
var forceQuoting bool | |||||
if tag != "" && node.Style&TaggedStyle == 0 { | |||||
if node.Kind == ScalarNode { | |||||
if stag == strTag && node.Style&(SingleQuotedStyle|DoubleQuotedStyle|LiteralStyle|FoldedStyle) != 0 { | |||||
tag = "" | |||||
} else { | |||||
rtag, _ = resolve("", node.Value) | |||||
if rtag == stag { | |||||
tag = "" | |||||
} else if stag == strTag { | |||||
tag = "" | |||||
forceQuoting = true | |||||
} | |||||
} | |||||
} else { | |||||
switch node.Kind { | |||||
case MappingNode: | |||||
rtag = mapTag | |||||
case SequenceNode: | |||||
rtag = seqTag | |||||
} | |||||
if rtag == stag { | |||||
tag = "" | |||||
} | |||||
} | |||||
} | |||||
switch node.Kind { | |||||
case DocumentNode: | |||||
yaml_document_start_event_initialize(&e.event, nil, nil, true) | |||||
e.event.head_comment = []byte(node.HeadComment) | |||||
e.emit() | |||||
for _, node := range node.Content { | |||||
e.node(node, "") | |||||
} | |||||
yaml_document_end_event_initialize(&e.event, true) | |||||
e.event.foot_comment = []byte(node.FootComment) | |||||
e.emit() | |||||
case SequenceNode: | |||||
style := yaml_BLOCK_SEQUENCE_STYLE | |||||
if node.Style&FlowStyle != 0 { | |||||
style = yaml_FLOW_SEQUENCE_STYLE | |||||
} | |||||
e.must(yaml_sequence_start_event_initialize(&e.event, []byte(node.Anchor), []byte(tag), tag == "", style)) | |||||
e.event.head_comment = []byte(node.HeadComment) | |||||
e.emit() | |||||
for _, node := range node.Content { | |||||
e.node(node, "") | |||||
} | |||||
e.must(yaml_sequence_end_event_initialize(&e.event)) | |||||
e.event.line_comment = []byte(node.LineComment) | |||||
e.event.foot_comment = []byte(node.FootComment) | |||||
e.emit() | |||||
case MappingNode: | |||||
style := yaml_BLOCK_MAPPING_STYLE | |||||
if node.Style&FlowStyle != 0 { | |||||
style = yaml_FLOW_MAPPING_STYLE | |||||
} | |||||
yaml_mapping_start_event_initialize(&e.event, []byte(node.Anchor), []byte(tag), tag == "", style) | |||||
e.event.tail_comment = []byte(tail) | |||||
e.event.head_comment = []byte(node.HeadComment) | |||||
e.emit() | |||||
// The tail logic below moves the foot comment of prior keys to the following key, | |||||
// since the value for each key may be a nested structure and the foot needs to be | |||||
// processed only the entirety of the value is streamed. The last tail is processed | |||||
// with the mapping end event. | |||||
var tail string | |||||
for i := 0; i+1 < len(node.Content); i += 2 { | |||||
k := node.Content[i] | |||||
foot := k.FootComment | |||||
if foot != "" { | |||||
kopy := *k | |||||
kopy.FootComment = "" | |||||
k = &kopy | |||||
} | |||||
e.node(k, tail) | |||||
tail = foot | |||||
v := node.Content[i+1] | |||||
e.node(v, "") | |||||
} | |||||
yaml_mapping_end_event_initialize(&e.event) | |||||
e.event.tail_comment = []byte(tail) | |||||
e.event.line_comment = []byte(node.LineComment) | |||||
e.event.foot_comment = []byte(node.FootComment) | |||||
e.emit() | |||||
case AliasNode: | |||||
yaml_alias_event_initialize(&e.event, []byte(node.Value)) | |||||
e.event.head_comment = []byte(node.HeadComment) | |||||
e.event.line_comment = []byte(node.LineComment) | |||||
e.event.foot_comment = []byte(node.FootComment) | |||||
e.emit() | |||||
case ScalarNode: | |||||
value := node.Value | |||||
if !utf8.ValidString(value) { | |||||
if tag == binaryTag { | |||||
failf("explicitly tagged !!binary data must be base64-encoded") | |||||
} | |||||
if tag != "" { | |||||
failf("cannot marshal invalid UTF-8 data as %s", shortTag(tag)) | |||||
} | |||||
// It can't be encoded directly as YAML so use a binary tag | |||||
// and encode it as base64. | |||||
tag = binaryTag | |||||
value = encodeBase64(value) | |||||
} | |||||
style := yaml_PLAIN_SCALAR_STYLE | |||||
switch { | |||||
case node.Style&DoubleQuotedStyle != 0: | |||||
style = yaml_DOUBLE_QUOTED_SCALAR_STYLE | |||||
case node.Style&SingleQuotedStyle != 0: | |||||
style = yaml_SINGLE_QUOTED_SCALAR_STYLE | |||||
case node.Style&LiteralStyle != 0: | |||||
style = yaml_LITERAL_SCALAR_STYLE | |||||
case node.Style&FoldedStyle != 0: | |||||
style = yaml_FOLDED_SCALAR_STYLE | |||||
case strings.Contains(value, "\n"): | |||||
style = yaml_LITERAL_SCALAR_STYLE | |||||
case forceQuoting: | |||||
style = yaml_DOUBLE_QUOTED_SCALAR_STYLE | |||||
} | |||||
e.emitScalar(value, node.Anchor, tag, style, []byte(node.HeadComment), []byte(node.LineComment), []byte(node.FootComment), []byte(tail)) | |||||
} | |||||
} |
@@ -0,0 +1,5 @@ | |||||
module "gopkg.in/yaml.v3" | |||||
require ( | |||||
"gopkg.in/check.v1" v0.0.0-20161208181325-20d25e280405 | |||||
) |
@@ -0,0 +1,434 @@ | |||||
// | |||||
// Copyright (c) 2011-2019 Canonical Ltd | |||||
// Copyright (c) 2006-2010 Kirill Simonov | |||||
// | |||||
// 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. | |||||
package yaml | |||||
import ( | |||||
"io" | |||||
) | |||||
// Set the reader error and return 0. | |||||
func yaml_parser_set_reader_error(parser *yaml_parser_t, problem string, offset int, value int) bool { | |||||
parser.error = yaml_READER_ERROR | |||||
parser.problem = problem | |||||
parser.problem_offset = offset | |||||
parser.problem_value = value | |||||
return false | |||||
} | |||||
// Byte order marks. | |||||
const ( | |||||
bom_UTF8 = "\xef\xbb\xbf" | |||||
bom_UTF16LE = "\xff\xfe" | |||||
bom_UTF16BE = "\xfe\xff" | |||||
) | |||||
// Determine the input stream encoding by checking the BOM symbol. If no BOM is | |||||
// found, the UTF-8 encoding is assumed. Return 1 on success, 0 on failure. | |||||
func yaml_parser_determine_encoding(parser *yaml_parser_t) bool { | |||||
// Ensure that we had enough bytes in the raw buffer. | |||||
for !parser.eof && len(parser.raw_buffer)-parser.raw_buffer_pos < 3 { | |||||
if !yaml_parser_update_raw_buffer(parser) { | |||||
return false | |||||
} | |||||
} | |||||
// Determine the encoding. | |||||
buf := parser.raw_buffer | |||||
pos := parser.raw_buffer_pos | |||||
avail := len(buf) - pos | |||||
if avail >= 2 && buf[pos] == bom_UTF16LE[0] && buf[pos+1] == bom_UTF16LE[1] { | |||||
parser.encoding = yaml_UTF16LE_ENCODING | |||||
parser.raw_buffer_pos += 2 | |||||
parser.offset += 2 | |||||
} else if avail >= 2 && buf[pos] == bom_UTF16BE[0] && buf[pos+1] == bom_UTF16BE[1] { | |||||
parser.encoding = yaml_UTF16BE_ENCODING | |||||
parser.raw_buffer_pos += 2 | |||||
parser.offset += 2 | |||||
} else if avail >= 3 && buf[pos] == bom_UTF8[0] && buf[pos+1] == bom_UTF8[1] && buf[pos+2] == bom_UTF8[2] { | |||||
parser.encoding = yaml_UTF8_ENCODING | |||||
parser.raw_buffer_pos += 3 | |||||
parser.offset += 3 | |||||
} else { | |||||
parser.encoding = yaml_UTF8_ENCODING | |||||
} | |||||
return true | |||||
} | |||||
// Update the raw buffer. | |||||
func yaml_parser_update_raw_buffer(parser *yaml_parser_t) bool { | |||||
size_read := 0 | |||||
// Return if the raw buffer is full. | |||||
if parser.raw_buffer_pos == 0 && len(parser.raw_buffer) == cap(parser.raw_buffer) { | |||||
return true | |||||
} | |||||
// Return on EOF. | |||||
if parser.eof { | |||||
return true | |||||
} | |||||
// Move the remaining bytes in the raw buffer to the beginning. | |||||
if parser.raw_buffer_pos > 0 && parser.raw_buffer_pos < len(parser.raw_buffer) { | |||||
copy(parser.raw_buffer, parser.raw_buffer[parser.raw_buffer_pos:]) | |||||
} | |||||
parser.raw_buffer = parser.raw_buffer[:len(parser.raw_buffer)-parser.raw_buffer_pos] | |||||
parser.raw_buffer_pos = 0 | |||||
// Call the read handler to fill the buffer. | |||||
size_read, err := parser.read_handler(parser, parser.raw_buffer[len(parser.raw_buffer):cap(parser.raw_buffer)]) | |||||
parser.raw_buffer = parser.raw_buffer[:len(parser.raw_buffer)+size_read] | |||||
if err == io.EOF { | |||||
parser.eof = true | |||||
} else if err != nil { | |||||
return yaml_parser_set_reader_error(parser, "input error: "+err.Error(), parser.offset, -1) | |||||
} | |||||
return true | |||||
} | |||||
// Ensure that the buffer contains at least `length` characters. | |||||
// Return true on success, false on failure. | |||||
// | |||||
// The length is supposed to be significantly less that the buffer size. | |||||
func yaml_parser_update_buffer(parser *yaml_parser_t, length int) bool { | |||||
if parser.read_handler == nil { | |||||
panic("read handler must be set") | |||||
} | |||||
// [Go] This function was changed to guarantee the requested length size at EOF. | |||||
// The fact we need to do this is pretty awful, but the description above implies | |||||
// for that to be the case, and there are tests | |||||
// If the EOF flag is set and the raw buffer is empty, do nothing. | |||||
if parser.eof && parser.raw_buffer_pos == len(parser.raw_buffer) { | |||||
// [Go] ACTUALLY! Read the documentation of this function above. | |||||
// This is just broken. To return true, we need to have the | |||||
// given length in the buffer. Not doing that means every single | |||||
// check that calls this function to make sure the buffer has a | |||||
// given length is Go) panicking; or C) accessing invalid memory. | |||||
//return true | |||||
} | |||||
// Return if the buffer contains enough characters. | |||||
if parser.unread >= length { | |||||
return true | |||||
} | |||||
// Determine the input encoding if it is not known yet. | |||||
if parser.encoding == yaml_ANY_ENCODING { | |||||
if !yaml_parser_determine_encoding(parser) { | |||||
return false | |||||
} | |||||
} | |||||
// Move the unread characters to the beginning of the buffer. | |||||
buffer_len := len(parser.buffer) | |||||
if parser.buffer_pos > 0 && parser.buffer_pos < buffer_len { | |||||
copy(parser.buffer, parser.buffer[parser.buffer_pos:]) | |||||
buffer_len -= parser.buffer_pos | |||||
parser.buffer_pos = 0 | |||||
} else if parser.buffer_pos == buffer_len { | |||||
buffer_len = 0 | |||||
parser.buffer_pos = 0 | |||||
} | |||||
// Open the whole buffer for writing, and cut it before returning. | |||||
parser.buffer = parser.buffer[:cap(parser.buffer)] | |||||
// Fill the buffer until it has enough characters. | |||||
first := true | |||||
for parser.unread < length { | |||||
// Fill the raw buffer if necessary. | |||||
if !first || parser.raw_buffer_pos == len(parser.raw_buffer) { | |||||
if !yaml_parser_update_raw_buffer(parser) { | |||||
parser.buffer = parser.buffer[:buffer_len] | |||||
return false | |||||
} | |||||
} | |||||
first = false | |||||
// Decode the raw buffer. | |||||
inner: | |||||
for parser.raw_buffer_pos != len(parser.raw_buffer) { | |||||
var value rune | |||||
var width int | |||||
raw_unread := len(parser.raw_buffer) - parser.raw_buffer_pos | |||||
// Decode the next character. | |||||
switch parser.encoding { | |||||
case yaml_UTF8_ENCODING: | |||||
// Decode a UTF-8 character. Check RFC 3629 | |||||
// (http://www.ietf.org/rfc/rfc3629.txt) for more details. | |||||
// | |||||
// The following table (taken from the RFC) is used for | |||||
// decoding. | |||||
// | |||||
// Char. number range | UTF-8 octet sequence | |||||
// (hexadecimal) | (binary) | |||||
// --------------------+------------------------------------ | |||||
// 0000 0000-0000 007F | 0xxxxxxx | |||||
// 0000 0080-0000 07FF | 110xxxxx 10xxxxxx | |||||
// 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx | |||||
// 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx | |||||
// | |||||
// Additionally, the characters in the range 0xD800-0xDFFF | |||||
// are prohibited as they are reserved for use with UTF-16 | |||||
// surrogate pairs. | |||||
// Determine the length of the UTF-8 sequence. | |||||
octet := parser.raw_buffer[parser.raw_buffer_pos] | |||||
switch { | |||||
case octet&0x80 == 0x00: | |||||
width = 1 | |||||
case octet&0xE0 == 0xC0: | |||||
width = 2 | |||||
case octet&0xF0 == 0xE0: | |||||
width = 3 | |||||
case octet&0xF8 == 0xF0: | |||||
width = 4 | |||||
default: | |||||
// The leading octet is invalid. | |||||
return yaml_parser_set_reader_error(parser, | |||||
"invalid leading UTF-8 octet", | |||||
parser.offset, int(octet)) | |||||
} | |||||
// Check if the raw buffer contains an incomplete character. | |||||
if width > raw_unread { | |||||
if parser.eof { | |||||
return yaml_parser_set_reader_error(parser, | |||||
"incomplete UTF-8 octet sequence", | |||||
parser.offset, -1) | |||||
} | |||||
break inner | |||||
} | |||||
// Decode the leading octet. | |||||
switch { | |||||
case octet&0x80 == 0x00: | |||||
value = rune(octet & 0x7F) | |||||
case octet&0xE0 == 0xC0: | |||||
value = rune(octet & 0x1F) | |||||
case octet&0xF0 == 0xE0: | |||||
value = rune(octet & 0x0F) | |||||
case octet&0xF8 == 0xF0: | |||||
value = rune(octet & 0x07) | |||||
default: | |||||
value = 0 | |||||
} | |||||
// Check and decode the trailing octets. | |||||
for k := 1; k < width; k++ { | |||||
octet = parser.raw_buffer[parser.raw_buffer_pos+k] | |||||
// Check if the octet is valid. | |||||
if (octet & 0xC0) != 0x80 { | |||||
return yaml_parser_set_reader_error(parser, | |||||
"invalid trailing UTF-8 octet", | |||||
parser.offset+k, int(octet)) | |||||
} | |||||
// Decode the octet. | |||||
value = (value << 6) + rune(octet&0x3F) | |||||
} | |||||
// Check the length of the sequence against the value. | |||||
switch { | |||||
case width == 1: | |||||
case width == 2 && value >= 0x80: | |||||
case width == 3 && value >= 0x800: | |||||
case width == 4 && value >= 0x10000: | |||||
default: | |||||
return yaml_parser_set_reader_error(parser, | |||||
"invalid length of a UTF-8 sequence", | |||||
parser.offset, -1) | |||||
} | |||||
// Check the range of the value. | |||||
if value >= 0xD800 && value <= 0xDFFF || value > 0x10FFFF { | |||||
return yaml_parser_set_reader_error(parser, | |||||
"invalid Unicode character", | |||||
parser.offset, int(value)) | |||||
} | |||||
case yaml_UTF16LE_ENCODING, yaml_UTF16BE_ENCODING: | |||||
var low, high int | |||||
if parser.encoding == yaml_UTF16LE_ENCODING { | |||||
low, high = 0, 1 | |||||
} else { | |||||
low, high = 1, 0 | |||||
} | |||||
// The UTF-16 encoding is not as simple as one might | |||||
// naively think. Check RFC 2781 | |||||
// (http://www.ietf.org/rfc/rfc2781.txt). | |||||
// | |||||
// Normally, two subsequent bytes describe a Unicode | |||||
// character. However a special technique (called a | |||||
// surrogate pair) is used for specifying character | |||||
// values larger than 0xFFFF. | |||||
// | |||||
// A surrogate pair consists of two pseudo-characters: | |||||
// high surrogate area (0xD800-0xDBFF) | |||||
// low surrogate area (0xDC00-0xDFFF) | |||||
// | |||||
// The following formulas are used for decoding | |||||
// and encoding characters using surrogate pairs: | |||||
// | |||||
// U = U' + 0x10000 (0x01 00 00 <= U <= 0x10 FF FF) | |||||
// U' = yyyyyyyyyyxxxxxxxxxx (0 <= U' <= 0x0F FF FF) | |||||
// W1 = 110110yyyyyyyyyy | |||||
// W2 = 110111xxxxxxxxxx | |||||
// | |||||
// where U is the character value, W1 is the high surrogate | |||||
// area, W2 is the low surrogate area. | |||||
// Check for incomplete UTF-16 character. | |||||
if raw_unread < 2 { | |||||
if parser.eof { | |||||
return yaml_parser_set_reader_error(parser, | |||||
"incomplete UTF-16 character", | |||||
parser.offset, -1) | |||||
} | |||||
break inner | |||||
} | |||||
// Get the character. | |||||
value = rune(parser.raw_buffer[parser.raw_buffer_pos+low]) + | |||||
(rune(parser.raw_buffer[parser.raw_buffer_pos+high]) << 8) | |||||
// Check for unexpected low surrogate area. | |||||
if value&0xFC00 == 0xDC00 { | |||||
return yaml_parser_set_reader_error(parser, | |||||
"unexpected low surrogate area", | |||||
parser.offset, int(value)) | |||||
} | |||||
// Check for a high surrogate area. | |||||
if value&0xFC00 == 0xD800 { | |||||
width = 4 | |||||
// Check for incomplete surrogate pair. | |||||
if raw_unread < 4 { | |||||
if parser.eof { | |||||
return yaml_parser_set_reader_error(parser, | |||||
"incomplete UTF-16 surrogate pair", | |||||
parser.offset, -1) | |||||
} | |||||
break inner | |||||
} | |||||
// Get the next character. | |||||
value2 := rune(parser.raw_buffer[parser.raw_buffer_pos+low+2]) + | |||||
(rune(parser.raw_buffer[parser.raw_buffer_pos+high+2]) << 8) | |||||
// Check for a low surrogate area. | |||||
if value2&0xFC00 != 0xDC00 { | |||||
return yaml_parser_set_reader_error(parser, | |||||
"expected low surrogate area", | |||||
parser.offset+2, int(value2)) | |||||
} | |||||
// Generate the value of the surrogate pair. | |||||
value = 0x10000 + ((value & 0x3FF) << 10) + (value2 & 0x3FF) | |||||
} else { | |||||
width = 2 | |||||
} | |||||
default: | |||||
panic("impossible") | |||||
} | |||||
// Check if the character is in the allowed range: | |||||
// #x9 | #xA | #xD | [#x20-#x7E] (8 bit) | |||||
// | #x85 | [#xA0-#xD7FF] | [#xE000-#xFFFD] (16 bit) | |||||
// | [#x10000-#x10FFFF] (32 bit) | |||||
switch { | |||||
case value == 0x09: | |||||
case value == 0x0A: | |||||
case value == 0x0D: | |||||
case value >= 0x20 && value <= 0x7E: | |||||
case value == 0x85: | |||||
case value >= 0xA0 && value <= 0xD7FF: | |||||
case value >= 0xE000 && value <= 0xFFFD: | |||||
case value >= 0x10000 && value <= 0x10FFFF: | |||||
default: | |||||
return yaml_parser_set_reader_error(parser, | |||||
"control characters are not allowed", | |||||
parser.offset, int(value)) | |||||
} | |||||
// Move the raw pointers. | |||||
parser.raw_buffer_pos += width | |||||
parser.offset += width | |||||
// Finally put the character into the buffer. | |||||
if value <= 0x7F { | |||||
// 0000 0000-0000 007F . 0xxxxxxx | |||||
parser.buffer[buffer_len+0] = byte(value) | |||||
buffer_len += 1 | |||||
} else if value <= 0x7FF { | |||||
// 0000 0080-0000 07FF . 110xxxxx 10xxxxxx | |||||
parser.buffer[buffer_len+0] = byte(0xC0 + (value >> 6)) | |||||
parser.buffer[buffer_len+1] = byte(0x80 + (value & 0x3F)) | |||||
buffer_len += 2 | |||||
} else if value <= 0xFFFF { | |||||
// 0000 0800-0000 FFFF . 1110xxxx 10xxxxxx 10xxxxxx | |||||
parser.buffer[buffer_len+0] = byte(0xE0 + (value >> 12)) | |||||
parser.buffer[buffer_len+1] = byte(0x80 + ((value >> 6) & 0x3F)) | |||||
parser.buffer[buffer_len+2] = byte(0x80 + (value & 0x3F)) | |||||
buffer_len += 3 | |||||
} else { | |||||
// 0001 0000-0010 FFFF . 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx | |||||
parser.buffer[buffer_len+0] = byte(0xF0 + (value >> 18)) | |||||
parser.buffer[buffer_len+1] = byte(0x80 + ((value >> 12) & 0x3F)) | |||||
parser.buffer[buffer_len+2] = byte(0x80 + ((value >> 6) & 0x3F)) | |||||
parser.buffer[buffer_len+3] = byte(0x80 + (value & 0x3F)) | |||||
buffer_len += 4 | |||||
} | |||||
parser.unread++ | |||||
} | |||||
// On EOF, put NUL into the buffer and return. | |||||
if parser.eof { | |||||
parser.buffer[buffer_len] = 0 | |||||
buffer_len++ | |||||
parser.unread++ | |||||
break | |||||
} | |||||
} | |||||
// [Go] Read the documentation of this function above. To return true, | |||||
// we need to have the given length in the buffer. Not doing that means | |||||
// every single check that calls this function to make sure the buffer | |||||
// has a given length is Go) panicking; or C) accessing invalid memory. | |||||
// This happens here due to the EOF above breaking early. | |||||
for buffer_len < length { | |||||
parser.buffer[buffer_len] = 0 | |||||
buffer_len++ | |||||
} | |||||
parser.buffer = parser.buffer[:buffer_len] | |||||
return true | |||||
} |
@@ -0,0 +1,326 @@ | |||||
// | |||||
// Copyright (c) 2011-2019 Canonical Ltd | |||||
// | |||||
// Licensed under the Apache License, Version 2.0 (the "License"); | |||||
// you may not use this file except in compliance with the License. | |||||
// You may obtain a copy of the License at | |||||
// | |||||
// http://www.apache.org/licenses/LICENSE-2.0 | |||||
// | |||||
// Unless required by applicable law or agreed to in writing, software | |||||
// distributed under the License is distributed on an "AS IS" BASIS, | |||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
// See the License for the specific language governing permissions and | |||||
// limitations under the License. | |||||
package yaml | |||||
import ( | |||||
"encoding/base64" | |||||
"math" | |||||
"regexp" | |||||
"strconv" | |||||
"strings" | |||||
"time" | |||||
) | |||||
type resolveMapItem struct { | |||||
value interface{} | |||||
tag string | |||||
} | |||||
var resolveTable = make([]byte, 256) | |||||
var resolveMap = make(map[string]resolveMapItem) | |||||
func init() { | |||||
t := resolveTable | |||||
t[int('+')] = 'S' // Sign | |||||
t[int('-')] = 'S' | |||||
for _, c := range "0123456789" { | |||||
t[int(c)] = 'D' // Digit | |||||
} | |||||
for _, c := range "yYnNtTfFoO~" { | |||||
t[int(c)] = 'M' // In map | |||||
} | |||||
t[int('.')] = '.' // Float (potentially in map) | |||||
var resolveMapList = []struct { | |||||
v interface{} | |||||
tag string | |||||
l []string | |||||
}{ | |||||
{true, boolTag, []string{"true", "True", "TRUE"}}, | |||||
{false, boolTag, []string{"false", "False", "FALSE"}}, | |||||
{nil, nullTag, []string{"", "~", "null", "Null", "NULL"}}, | |||||
{math.NaN(), floatTag, []string{".nan", ".NaN", ".NAN"}}, | |||||
{math.Inf(+1), floatTag, []string{".inf", ".Inf", ".INF"}}, | |||||
{math.Inf(+1), floatTag, []string{"+.inf", "+.Inf", "+.INF"}}, | |||||
{math.Inf(-1), floatTag, []string{"-.inf", "-.Inf", "-.INF"}}, | |||||
{"<<", mergeTag, []string{"<<"}}, | |||||
} | |||||
m := resolveMap | |||||
for _, item := range resolveMapList { | |||||
for _, s := range item.l { | |||||
m[s] = resolveMapItem{item.v, item.tag} | |||||
} | |||||
} | |||||
} | |||||
const ( | |||||
nullTag = "!!null" | |||||
boolTag = "!!bool" | |||||
strTag = "!!str" | |||||
intTag = "!!int" | |||||
floatTag = "!!float" | |||||
timestampTag = "!!timestamp" | |||||
seqTag = "!!seq" | |||||
mapTag = "!!map" | |||||
binaryTag = "!!binary" | |||||
mergeTag = "!!merge" | |||||
) | |||||
var longTags = make(map[string]string) | |||||
var shortTags = make(map[string]string) | |||||
func init() { | |||||
for _, stag := range []string{nullTag, boolTag, strTag, intTag, floatTag, timestampTag, seqTag, mapTag, binaryTag, mergeTag} { | |||||
ltag := longTag(stag) | |||||
longTags[stag] = ltag | |||||
shortTags[ltag] = stag | |||||
} | |||||
} | |||||
const longTagPrefix = "tag:yaml.org,2002:" | |||||
func shortTag(tag string) string { | |||||
if strings.HasPrefix(tag, longTagPrefix) { | |||||
if stag, ok := shortTags[tag]; ok { | |||||
return stag | |||||
} | |||||
return "!!" + tag[len(longTagPrefix):] | |||||
} | |||||
return tag | |||||
} | |||||
func longTag(tag string) string { | |||||
if strings.HasPrefix(tag, "!!") { | |||||
if ltag, ok := longTags[tag]; ok { | |||||
return ltag | |||||
} | |||||
return longTagPrefix + tag[2:] | |||||
} | |||||
return tag | |||||
} | |||||
func resolvableTag(tag string) bool { | |||||
switch tag { | |||||
case "", strTag, boolTag, intTag, floatTag, nullTag, timestampTag: | |||||
return true | |||||
} | |||||
return false | |||||
} | |||||
var yamlStyleFloat = regexp.MustCompile(`^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$`) | |||||
func resolve(tag string, in string) (rtag string, out interface{}) { | |||||
tag = shortTag(tag) | |||||
if !resolvableTag(tag) { | |||||
return tag, in | |||||
} | |||||
defer func() { | |||||
switch tag { | |||||
case "", rtag, strTag, binaryTag: | |||||
return | |||||
case floatTag: | |||||
if rtag == intTag { | |||||
switch v := out.(type) { | |||||
case int64: | |||||
rtag = floatTag | |||||
out = float64(v) | |||||
return | |||||
case int: | |||||
rtag = floatTag | |||||
out = float64(v) | |||||
return | |||||
} | |||||
} | |||||
} | |||||
failf("cannot decode %s `%s` as a %s", shortTag(rtag), in, shortTag(tag)) | |||||
}() | |||||
// Any data is accepted as a !!str or !!binary. | |||||
// Otherwise, the prefix is enough of a hint about what it might be. | |||||
hint := byte('N') | |||||
if in != "" { | |||||
hint = resolveTable[in[0]] | |||||
} | |||||
if hint != 0 && tag != strTag && tag != binaryTag { | |||||
// Handle things we can lookup in a map. | |||||
if item, ok := resolveMap[in]; ok { | |||||
return item.tag, item.value | |||||
} | |||||
// Base 60 floats are a bad idea, were dropped in YAML 1.2, and | |||||
// are purposefully unsupported here. They're still quoted on | |||||
// the way out for compatibility with other parser, though. | |||||
switch hint { | |||||
case 'M': | |||||
// We've already checked the map above. | |||||
case '.': | |||||
// Not in the map, so maybe a normal float. | |||||
floatv, err := strconv.ParseFloat(in, 64) | |||||
if err == nil { | |||||
return floatTag, floatv | |||||
} | |||||
case 'D', 'S': | |||||
// Int, float, or timestamp. | |||||
// Only try values as a timestamp if the value is unquoted or there's an explicit | |||||
// !!timestamp tag. | |||||
if tag == "" || tag == timestampTag { | |||||
t, ok := parseTimestamp(in) | |||||
if ok { | |||||
return timestampTag, t | |||||
} | |||||
} | |||||
plain := strings.Replace(in, "_", "", -1) | |||||
intv, err := strconv.ParseInt(plain, 0, 64) | |||||
if err == nil { | |||||
if intv == int64(int(intv)) { | |||||
return intTag, int(intv) | |||||
} else { | |||||
return intTag, intv | |||||
} | |||||
} | |||||
uintv, err := strconv.ParseUint(plain, 0, 64) | |||||
if err == nil { | |||||
return intTag, uintv | |||||
} | |||||
if yamlStyleFloat.MatchString(plain) { | |||||
floatv, err := strconv.ParseFloat(plain, 64) | |||||
if err == nil { | |||||
return floatTag, floatv | |||||
} | |||||
} | |||||
if strings.HasPrefix(plain, "0b") { | |||||
intv, err := strconv.ParseInt(plain[2:], 2, 64) | |||||
if err == nil { | |||||
if intv == int64(int(intv)) { | |||||
return intTag, int(intv) | |||||
} else { | |||||
return intTag, intv | |||||
} | |||||
} | |||||
uintv, err := strconv.ParseUint(plain[2:], 2, 64) | |||||
if err == nil { | |||||
return intTag, uintv | |||||
} | |||||
} else if strings.HasPrefix(plain, "-0b") { | |||||
intv, err := strconv.ParseInt("-"+plain[3:], 2, 64) | |||||
if err == nil { | |||||
if true || intv == int64(int(intv)) { | |||||
return intTag, int(intv) | |||||
} else { | |||||
return intTag, intv | |||||
} | |||||
} | |||||
} | |||||
// Octals as introduced in version 1.2 of the spec. | |||||
// Octals from the 1.1 spec, spelled as 0777, are still | |||||
// decoded by default in v3 as well for compatibility. | |||||
// May be dropped in v4 depending on how usage evolves. | |||||
if strings.HasPrefix(plain, "0o") { | |||||
intv, err := strconv.ParseInt(plain[2:], 8, 64) | |||||
if err == nil { | |||||
if intv == int64(int(intv)) { | |||||
return intTag, int(intv) | |||||
} else { | |||||
return intTag, intv | |||||
} | |||||
} | |||||
uintv, err := strconv.ParseUint(plain[2:], 8, 64) | |||||
if err == nil { | |||||
return intTag, uintv | |||||
} | |||||
} else if strings.HasPrefix(plain, "-0o") { | |||||
intv, err := strconv.ParseInt("-"+plain[3:], 8, 64) | |||||
if err == nil { | |||||
if true || intv == int64(int(intv)) { | |||||
return intTag, int(intv) | |||||
} else { | |||||
return intTag, intv | |||||
} | |||||
} | |||||
} | |||||
default: | |||||
panic("internal error: missing handler for resolver table: " + string(rune(hint)) + " (with " + in + ")") | |||||
} | |||||
} | |||||
return strTag, in | |||||
} | |||||
// encodeBase64 encodes s as base64 that is broken up into multiple lines | |||||
// as appropriate for the resulting length. | |||||
func encodeBase64(s string) string { | |||||
const lineLen = 70 | |||||
encLen := base64.StdEncoding.EncodedLen(len(s)) | |||||
lines := encLen/lineLen + 1 | |||||
buf := make([]byte, encLen*2+lines) | |||||
in := buf[0:encLen] | |||||
out := buf[encLen:] | |||||
base64.StdEncoding.Encode(in, []byte(s)) | |||||
k := 0 | |||||
for i := 0; i < len(in); i += lineLen { | |||||
j := i + lineLen | |||||
if j > len(in) { | |||||
j = len(in) | |||||
} | |||||
k += copy(out[k:], in[i:j]) | |||||
if lines > 1 { | |||||
out[k] = '\n' | |||||
k++ | |||||
} | |||||
} | |||||
return string(out[:k]) | |||||
} | |||||
// This is a subset of the formats allowed by the regular expression | |||||
// defined at http://yaml.org/type/timestamp.html. | |||||
var allowedTimestampFormats = []string{ | |||||
"2006-1-2T15:4:5.999999999Z07:00", // RCF3339Nano with short date fields. | |||||
"2006-1-2t15:4:5.999999999Z07:00", // RFC3339Nano with short date fields and lower-case "t". | |||||
"2006-1-2 15:4:5.999999999", // space separated with no time zone | |||||
"2006-1-2", // date only | |||||
// Notable exception: time.Parse cannot handle: "2001-12-14 21:59:43.10 -5" | |||||
// from the set of examples. | |||||
} | |||||
// parseTimestamp parses s as a timestamp string and | |||||
// returns the timestamp and reports whether it succeeded. | |||||
// Timestamp formats are defined at http://yaml.org/type/timestamp.html | |||||
func parseTimestamp(s string) (time.Time, bool) { | |||||
// TODO write code to check all the formats supported by | |||||
// http://yaml.org/type/timestamp.html instead of using time.Parse. | |||||
// Quick check: all date formats start with YYYY-. | |||||
i := 0 | |||||
for ; i < len(s); i++ { | |||||
if c := s[i]; c < '0' || c > '9' { | |||||
break | |||||
} | |||||
} | |||||
if i != 4 || i == len(s) || s[i] != '-' { | |||||
return time.Time{}, false | |||||
} | |||||
for _, format := range allowedTimestampFormats { | |||||
if t, err := time.Parse(format, s); err == nil { | |||||
return t, true | |||||
} | |||||
} | |||||
return time.Time{}, false | |||||
} |
@@ -0,0 +1,134 @@ | |||||
// | |||||
// Copyright (c) 2011-2019 Canonical Ltd | |||||
// | |||||
// Licensed under the Apache License, Version 2.0 (the "License"); | |||||
// you may not use this file except in compliance with the License. | |||||
// You may obtain a copy of the License at | |||||
// | |||||
// http://www.apache.org/licenses/LICENSE-2.0 | |||||
// | |||||
// Unless required by applicable law or agreed to in writing, software | |||||
// distributed under the License is distributed on an "AS IS" BASIS, | |||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
// See the License for the specific language governing permissions and | |||||
// limitations under the License. | |||||
package yaml | |||||
import ( | |||||
"reflect" | |||||
"unicode" | |||||
) | |||||
type keyList []reflect.Value | |||||
func (l keyList) Len() int { return len(l) } | |||||
func (l keyList) Swap(i, j int) { l[i], l[j] = l[j], l[i] } | |||||
func (l keyList) Less(i, j int) bool { | |||||
a := l[i] | |||||
b := l[j] | |||||
ak := a.Kind() | |||||
bk := b.Kind() | |||||
for (ak == reflect.Interface || ak == reflect.Ptr) && !a.IsNil() { | |||||
a = a.Elem() | |||||
ak = a.Kind() | |||||
} | |||||
for (bk == reflect.Interface || bk == reflect.Ptr) && !b.IsNil() { | |||||
b = b.Elem() | |||||
bk = b.Kind() | |||||
} | |||||
af, aok := keyFloat(a) | |||||
bf, bok := keyFloat(b) | |||||
if aok && bok { | |||||
if af != bf { | |||||
return af < bf | |||||
} | |||||
if ak != bk { | |||||
return ak < bk | |||||
} | |||||
return numLess(a, b) | |||||
} | |||||
if ak != reflect.String || bk != reflect.String { | |||||
return ak < bk | |||||
} | |||||
ar, br := []rune(a.String()), []rune(b.String()) | |||||
digits := false | |||||
for i := 0; i < len(ar) && i < len(br); i++ { | |||||
if ar[i] == br[i] { | |||||
digits = unicode.IsDigit(ar[i]) | |||||
continue | |||||
} | |||||
al := unicode.IsLetter(ar[i]) | |||||
bl := unicode.IsLetter(br[i]) | |||||
if al && bl { | |||||
return ar[i] < br[i] | |||||
} | |||||
if al || bl { | |||||
if digits { | |||||
return al | |||||
} else { | |||||
return bl | |||||
} | |||||
} | |||||
var ai, bi int | |||||
var an, bn int64 | |||||
if ar[i] == '0' || br[i] == '0' { | |||||
for j := i - 1; j >= 0 && unicode.IsDigit(ar[j]); j-- { | |||||
if ar[j] != '0' { | |||||
an = 1 | |||||
bn = 1 | |||||
break | |||||
} | |||||
} | |||||
} | |||||
for ai = i; ai < len(ar) && unicode.IsDigit(ar[ai]); ai++ { | |||||
an = an*10 + int64(ar[ai]-'0') | |||||
} | |||||
for bi = i; bi < len(br) && unicode.IsDigit(br[bi]); bi++ { | |||||
bn = bn*10 + int64(br[bi]-'0') | |||||
} | |||||
if an != bn { | |||||
return an < bn | |||||
} | |||||
if ai != bi { | |||||
return ai < bi | |||||
} | |||||
return ar[i] < br[i] | |||||
} | |||||
return len(ar) < len(br) | |||||
} | |||||
// keyFloat returns a float value for v if it is a number/bool | |||||
// and whether it is a number/bool or not. | |||||
func keyFloat(v reflect.Value) (f float64, ok bool) { | |||||
switch v.Kind() { | |||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | |||||
return float64(v.Int()), true | |||||
case reflect.Float32, reflect.Float64: | |||||
return v.Float(), true | |||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: | |||||
return float64(v.Uint()), true | |||||
case reflect.Bool: | |||||
if v.Bool() { | |||||
return 1, true | |||||
} | |||||
return 0, true | |||||
} | |||||
return 0, false | |||||
} | |||||
// numLess returns whether a < b. | |||||
// a and b must necessarily have the same kind. | |||||
func numLess(a, b reflect.Value) bool { | |||||
switch a.Kind() { | |||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | |||||
return a.Int() < b.Int() | |||||
case reflect.Float32, reflect.Float64: | |||||
return a.Float() < b.Float() | |||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: | |||||
return a.Uint() < b.Uint() | |||||
case reflect.Bool: | |||||
return !a.Bool() && b.Bool() | |||||
} | |||||
panic("not a number") | |||||
} |
@@ -0,0 +1,48 @@ | |||||
// | |||||
// Copyright (c) 2011-2019 Canonical Ltd | |||||
// Copyright (c) 2006-2010 Kirill Simonov | |||||
// | |||||
// 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. | |||||
package yaml | |||||
// Set the writer error and return false. | |||||
func yaml_emitter_set_writer_error(emitter *yaml_emitter_t, problem string) bool { | |||||
emitter.error = yaml_WRITER_ERROR | |||||
emitter.problem = problem | |||||
return false | |||||
} | |||||
// Flush the output buffer. | |||||
func yaml_emitter_flush(emitter *yaml_emitter_t) bool { | |||||
if emitter.write_handler == nil { | |||||
panic("write handler not set") | |||||
} | |||||
// Check if the buffer is empty. | |||||
if emitter.buffer_pos == 0 { | |||||
return true | |||||
} | |||||
if err := emitter.write_handler(emitter, emitter.buffer[:emitter.buffer_pos]); err != nil { | |||||
return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error()) | |||||
} | |||||
emitter.buffer_pos = 0 | |||||
return true | |||||
} |
@@ -0,0 +1,662 @@ | |||||
// | |||||
// Copyright (c) 2011-2019 Canonical Ltd | |||||
// | |||||
// Licensed under the Apache License, Version 2.0 (the "License"); | |||||
// you may not use this file except in compliance with the License. | |||||
// You may obtain a copy of the License at | |||||
// | |||||
// http://www.apache.org/licenses/LICENSE-2.0 | |||||
// | |||||
// Unless required by applicable law or agreed to in writing, software | |||||
// distributed under the License is distributed on an "AS IS" BASIS, | |||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
// See the License for the specific language governing permissions and | |||||
// limitations under the License. | |||||
// Package yaml implements YAML support for the Go language. | |||||
// | |||||
// Source code and other details for the project are available at GitHub: | |||||
// | |||||
// https://github.com/go-yaml/yaml | |||||
// | |||||
package yaml | |||||
import ( | |||||
"errors" | |||||
"fmt" | |||||
"io" | |||||
"reflect" | |||||
"strings" | |||||
"sync" | |||||
"unicode/utf8" | |||||
) | |||||
// The Unmarshaler interface may be implemented by types to customize their | |||||
// behavior when being unmarshaled from a YAML document. | |||||
type Unmarshaler interface { | |||||
UnmarshalYAML(value *Node) error | |||||
} | |||||
type obsoleteUnmarshaler interface { | |||||
UnmarshalYAML(unmarshal func(interface{}) error) error | |||||
} | |||||
// The Marshaler interface may be implemented by types to customize their | |||||
// behavior when being marshaled into a YAML document. The returned value | |||||
// is marshaled in place of the original value implementing Marshaler. | |||||
// | |||||
// If an error is returned by MarshalYAML, the marshaling procedure stops | |||||
// and returns with the provided error. | |||||
type Marshaler interface { | |||||
MarshalYAML() (interface{}, error) | |||||
} | |||||
// Unmarshal decodes the first document found within the in byte slice | |||||
// and assigns decoded values into the out value. | |||||
// | |||||
// Maps and pointers (to a struct, string, int, etc) are accepted as out | |||||
// values. If an internal pointer within a struct is not initialized, | |||||
// the yaml package will initialize it if necessary for unmarshalling | |||||
// the provided data. The out parameter must not be nil. | |||||
// | |||||
// The type of the decoded values should be compatible with the respective | |||||
// values in out. If one or more values cannot be decoded due to a type | |||||
// mismatches, decoding continues partially until the end of the YAML | |||||
// content, and a *yaml.TypeError is returned with details for all | |||||
// missed values. | |||||
// | |||||
// Struct fields are only unmarshalled if they are exported (have an | |||||
// upper case first letter), and are unmarshalled using the field name | |||||
// lowercased as the default key. Custom keys may be defined via the | |||||
// "yaml" name in the field tag: the content preceding the first comma | |||||
// is used as the key, and the following comma-separated options are | |||||
// used to tweak the marshalling process (see Marshal). | |||||
// Conflicting names result in a runtime error. | |||||
// | |||||
// For example: | |||||
// | |||||
// type T struct { | |||||
// F int `yaml:"a,omitempty"` | |||||
// B int | |||||
// } | |||||
// var t T | |||||
// yaml.Unmarshal([]byte("a: 1\nb: 2"), &t) | |||||
// | |||||
// See the documentation of Marshal for the format of tags and a list of | |||||
// supported tag options. | |||||
// | |||||
func Unmarshal(in []byte, out interface{}) (err error) { | |||||
return unmarshal(in, out, false) | |||||
} | |||||
// A Decorder reads and decodes YAML values from an input stream. | |||||
type Decoder struct { | |||||
parser *parser | |||||
knownFields bool | |||||
} | |||||
// NewDecoder returns a new decoder that reads from r. | |||||
// | |||||
// The decoder introduces its own buffering and may read | |||||
// data from r beyond the YAML values requested. | |||||
func NewDecoder(r io.Reader) *Decoder { | |||||
return &Decoder{ | |||||
parser: newParserFromReader(r), | |||||
} | |||||
} | |||||
// KnownFields ensures that the keys in decoded mappings to | |||||
// exist as fields in the struct being decoded into. | |||||
func (dec *Decoder) KnownFields(enable bool) { | |||||
dec.knownFields = enable | |||||
} | |||||
// Decode reads the next YAML-encoded value from its input | |||||
// and stores it in the value pointed to by v. | |||||
// | |||||
// See the documentation for Unmarshal for details about the | |||||
// conversion of YAML into a Go value. | |||||
func (dec *Decoder) Decode(v interface{}) (err error) { | |||||
d := newDecoder() | |||||
d.knownFields = dec.knownFields | |||||
defer handleErr(&err) | |||||
node := dec.parser.parse() | |||||
if node == nil { | |||||
return io.EOF | |||||
} | |||||
out := reflect.ValueOf(v) | |||||
if out.Kind() == reflect.Ptr && !out.IsNil() { | |||||
out = out.Elem() | |||||
} | |||||
d.unmarshal(node, out) | |||||
if len(d.terrors) > 0 { | |||||
return &TypeError{d.terrors} | |||||
} | |||||
return nil | |||||
} | |||||
// Decode decodes the node and stores its data into the value pointed to by v. | |||||
// | |||||
// See the documentation for Unmarshal for details about the | |||||
// conversion of YAML into a Go value. | |||||
func (n *Node) Decode(v interface{}) (err error) { | |||||
d := newDecoder() | |||||
defer handleErr(&err) | |||||
out := reflect.ValueOf(v) | |||||
if out.Kind() == reflect.Ptr && !out.IsNil() { | |||||
out = out.Elem() | |||||
} | |||||
d.unmarshal(n, out) | |||||
if len(d.terrors) > 0 { | |||||
return &TypeError{d.terrors} | |||||
} | |||||
return nil | |||||
} | |||||
func unmarshal(in []byte, out interface{}, strict bool) (err error) { | |||||
defer handleErr(&err) | |||||
d := newDecoder() | |||||
p := newParser(in) | |||||
defer p.destroy() | |||||
node := p.parse() | |||||
if node != nil { | |||||
v := reflect.ValueOf(out) | |||||
if v.Kind() == reflect.Ptr && !v.IsNil() { | |||||
v = v.Elem() | |||||
} | |||||
d.unmarshal(node, v) | |||||
} | |||||
if len(d.terrors) > 0 { | |||||
return &TypeError{d.terrors} | |||||
} | |||||
return nil | |||||
} | |||||
// Marshal serializes the value provided into a YAML document. The structure | |||||
// of the generated document will reflect the structure of the value itself. | |||||
// Maps and pointers (to struct, string, int, etc) are accepted as the in value. | |||||
// | |||||
// Struct fields are only marshalled if they are exported (have an upper case | |||||
// first letter), and are marshalled using the field name lowercased as the | |||||
// default key. Custom keys may be defined via the "yaml" name in the field | |||||
// tag: the content preceding the first comma is used as the key, and the | |||||
// following comma-separated options are used to tweak the marshalling process. | |||||
// Conflicting names result in a runtime error. | |||||
// | |||||
// The field tag format accepted is: | |||||
// | |||||
// `(...) yaml:"[<key>][,<flag1>[,<flag2>]]" (...)` | |||||
// | |||||
// The following flags are currently supported: | |||||
// | |||||
// omitempty Only include the field if it's not set to the zero | |||||
// value for the type or to empty slices or maps. | |||||
// Zero valued structs will be omitted if all their public | |||||
// fields are zero, unless they implement an IsZero | |||||
// method (see the IsZeroer interface type), in which | |||||
// case the field will be included if that method returns true. | |||||
// | |||||
// flow Marshal using a flow style (useful for structs, | |||||
// sequences and maps). | |||||
// | |||||
// inline Inline the field, which must be a struct or a map, | |||||
// causing all of its fields or keys to be processed as if | |||||
// they were part of the outer struct. For maps, keys must | |||||
// not conflict with the yaml keys of other struct fields. | |||||
// | |||||
// In addition, if the key is "-", the field is ignored. | |||||
// | |||||
// For example: | |||||
// | |||||
// type T struct { | |||||
// F int `yaml:"a,omitempty"` | |||||
// B int | |||||
// } | |||||
// yaml.Marshal(&T{B: 2}) // Returns "b: 2\n" | |||||
// yaml.Marshal(&T{F: 1}} // Returns "a: 1\nb: 0\n" | |||||
// | |||||
func Marshal(in interface{}) (out []byte, err error) { | |||||
defer handleErr(&err) | |||||
e := newEncoder() | |||||
defer e.destroy() | |||||
e.marshalDoc("", reflect.ValueOf(in)) | |||||
e.finish() | |||||
out = e.out | |||||
return | |||||
} | |||||
// An Encoder writes YAML values to an output stream. | |||||
type Encoder struct { | |||||
encoder *encoder | |||||
} | |||||
// NewEncoder returns a new encoder that writes to w. | |||||
// The Encoder should be closed after use to flush all data | |||||
// to w. | |||||
func NewEncoder(w io.Writer) *Encoder { | |||||
return &Encoder{ | |||||
encoder: newEncoderWithWriter(w), | |||||
} | |||||
} | |||||
// Encode writes the YAML encoding of v to the stream. | |||||
// If multiple items are encoded to the stream, the | |||||
// second and subsequent document will be preceded | |||||
// with a "---" document separator, but the first will not. | |||||
// | |||||
// See the documentation for Marshal for details about the conversion of Go | |||||
// values to YAML. | |||||
func (e *Encoder) Encode(v interface{}) (err error) { | |||||
defer handleErr(&err) | |||||
e.encoder.marshalDoc("", reflect.ValueOf(v)) | |||||
return nil | |||||
} | |||||
// SetIndent changes the used indentation used when encoding. | |||||
func (e *Encoder) SetIndent(spaces int) { | |||||
if spaces < 0 { | |||||
panic("yaml: cannot indent to a negative number of spaces") | |||||
} | |||||
e.encoder.indent = spaces | |||||
} | |||||
// Close closes the encoder by writing any remaining data. | |||||
// It does not write a stream terminating string "...". | |||||
func (e *Encoder) Close() (err error) { | |||||
defer handleErr(&err) | |||||
e.encoder.finish() | |||||
return nil | |||||
} | |||||
func handleErr(err *error) { | |||||
if v := recover(); v != nil { | |||||
if e, ok := v.(yamlError); ok { | |||||
*err = e.err | |||||
} else { | |||||
panic(v) | |||||
} | |||||
} | |||||
} | |||||
type yamlError struct { | |||||
err error | |||||
} | |||||
func fail(err error) { | |||||
panic(yamlError{err}) | |||||
} | |||||
func failf(format string, args ...interface{}) { | |||||
panic(yamlError{fmt.Errorf("yaml: "+format, args...)}) | |||||
} | |||||
// A TypeError is returned by Unmarshal when one or more fields in | |||||
// the YAML document cannot be properly decoded into the requested | |||||
// types. When this error is returned, the value is still | |||||
// unmarshaled partially. | |||||
type TypeError struct { | |||||
Errors []string | |||||
} | |||||
func (e *TypeError) Error() string { | |||||
return fmt.Sprintf("yaml: unmarshal errors:\n %s", strings.Join(e.Errors, "\n ")) | |||||
} | |||||
type Kind uint32 | |||||
const ( | |||||
DocumentNode Kind = 1 << iota | |||||
SequenceNode | |||||
MappingNode | |||||
ScalarNode | |||||
AliasNode | |||||
) | |||||
type Style uint32 | |||||
const ( | |||||
TaggedStyle Style = 1 << iota | |||||
DoubleQuotedStyle | |||||
SingleQuotedStyle | |||||
LiteralStyle | |||||
FoldedStyle | |||||
FlowStyle | |||||
) | |||||
// Node represents an element in the YAML document hierarchy. While documents | |||||
// are typically encoded and decoded into higher level types, such as structs | |||||
// and maps, Node is an intermediate representation that allows detailed | |||||
// control over the content being decoded or encoded. | |||||
// | |||||
// Values that make use of the Node type interact with the yaml package in the | |||||
// same way any other type would do, by encoding and decoding yaml data | |||||
// directly or indirectly into them. | |||||
// | |||||
// For example: | |||||
// | |||||
// var person struct { | |||||
// Name string | |||||
// Address yaml.Node | |||||
// } | |||||
// err := yaml.Unmarshal(data, &person) | |||||
// | |||||
// Or by itself: | |||||
// | |||||
// var person Node | |||||
// err := yaml.Unmarshal(data, &person) | |||||
// | |||||
type Node struct { | |||||
// Kind defines whether the node is a document, a mapping, a sequence, | |||||
// a scalar value, or an alias to another node. The specific data type of | |||||
// scalar nodes may be obtained via the ShortTag and LongTag methods. | |||||
Kind Kind | |||||
// Style allows customizing the apperance of the node in the tree. | |||||
Style Style | |||||
// Tag holds the YAML tag defining the data type for the value. | |||||
// When decoding, this field will always be set to the resolved tag, | |||||
// even when it wasn't explicitly provided in the YAML content. | |||||
// When encoding, if this field is unset the value type will be | |||||
// implied from the node properties, and if it is set, it will only | |||||
// be serialized into the representation if TaggedStyle is used or | |||||
// the implicit tag diverges from the provided one. | |||||
Tag string | |||||
// Value holds the unescaped and unquoted represenation of the value. | |||||
Value string | |||||
// Anchor holds the anchor name for this node, which allows aliases to point to it. | |||||
Anchor string | |||||
// Alias holds the node that this alias points to. Only valid when Kind is AliasNode. | |||||
Alias *Node | |||||
// Content holds contained nodes for documents, mappings, and sequences. | |||||
Content []*Node | |||||
// HeadComment holds any comments in the lines preceding the node and | |||||
// not separated by an empty line. | |||||
HeadComment string | |||||
// LineComment holds any comments at the end of the line where the node is in. | |||||
LineComment string | |||||
// FootComment holds any comments following the node and before empty lines. | |||||
FootComment string | |||||
// Line and Column hold the node position in the decoded YAML text. | |||||
// These fields are not respected when encoding the node. | |||||
Line int | |||||
Column int | |||||
} | |||||
// LongTag returns the long form of the tag that indicates the data type for | |||||
// the node. If the Tag field isn't explicitly defined, one will be computed | |||||
// based on the node properties. | |||||
func (n *Node) LongTag() string { | |||||
return longTag(n.ShortTag()) | |||||
} | |||||
// ShortTag returns the short form of the YAML tag that indicates data type for | |||||
// the node. If the Tag field isn't explicitly defined, one will be computed | |||||
// based on the node properties. | |||||
func (n *Node) ShortTag() string { | |||||
if n.indicatedString() { | |||||
return strTag | |||||
} | |||||
if n.Tag == "" || n.Tag == "!" { | |||||
switch n.Kind { | |||||
case MappingNode: | |||||
return mapTag | |||||
case SequenceNode: | |||||
return seqTag | |||||
case AliasNode: | |||||
if n.Alias != nil { | |||||
return n.Alias.ShortTag() | |||||
} | |||||
case ScalarNode: | |||||
tag, _ := resolve("", n.Value) | |||||
return tag | |||||
} | |||||
return "" | |||||
} | |||||
return shortTag(n.Tag) | |||||
} | |||||
func (n *Node) indicatedString() bool { | |||||
return n.Kind == ScalarNode && | |||||
(shortTag(n.Tag) == strTag || | |||||
(n.Tag == "" || n.Tag == "!") && n.Style&(SingleQuotedStyle|DoubleQuotedStyle|LiteralStyle|FoldedStyle) != 0) | |||||
} | |||||
// SetString is a convenience function that sets the node to a string value | |||||
// and defines its style in a pleasant way depending on its content. | |||||
func (n *Node) SetString(s string) { | |||||
n.Kind = ScalarNode | |||||
if utf8.ValidString(s) { | |||||
n.Value = s | |||||
n.Tag = strTag | |||||
} else { | |||||
n.Value = encodeBase64(s) | |||||
n.Tag = binaryTag | |||||
} | |||||
if strings.Contains(n.Value, "\n") { | |||||
n.Style = LiteralStyle | |||||
} | |||||
} | |||||
// -------------------------------------------------------------------------- | |||||
// Maintain a mapping of keys to structure field indexes | |||||
// The code in this section was copied from mgo/bson. | |||||
// structInfo holds details for the serialization of fields of | |||||
// a given struct. | |||||
type structInfo struct { | |||||
FieldsMap map[string]fieldInfo | |||||
FieldsList []fieldInfo | |||||
// InlineMap is the number of the field in the struct that | |||||
// contains an ,inline map, or -1 if there's none. | |||||
InlineMap int | |||||
// InlineUnmarshalers holds indexes to inlined fields that | |||||
// contain unmarshaler values. | |||||
InlineUnmarshalers [][]int | |||||
} | |||||
type fieldInfo struct { | |||||
Key string | |||||
Num int | |||||
OmitEmpty bool | |||||
Flow bool | |||||
// Id holds the unique field identifier, so we can cheaply | |||||
// check for field duplicates without maintaining an extra map. | |||||
Id int | |||||
// Inline holds the field index if the field is part of an inlined struct. | |||||
Inline []int | |||||
} | |||||
var structMap = make(map[reflect.Type]*structInfo) | |||||
var fieldMapMutex sync.RWMutex | |||||
var unmarshalerType reflect.Type | |||||
func init() { | |||||
var v Unmarshaler | |||||
unmarshalerType = reflect.ValueOf(&v).Elem().Type() | |||||
} | |||||
func getStructInfo(st reflect.Type) (*structInfo, error) { | |||||
fieldMapMutex.RLock() | |||||
sinfo, found := structMap[st] | |||||
fieldMapMutex.RUnlock() | |||||
if found { | |||||
return sinfo, nil | |||||
} | |||||
n := st.NumField() | |||||
fieldsMap := make(map[string]fieldInfo) | |||||
fieldsList := make([]fieldInfo, 0, n) | |||||
inlineMap := -1 | |||||
inlineUnmarshalers := [][]int(nil) | |||||
for i := 0; i != n; i++ { | |||||
field := st.Field(i) | |||||
if field.PkgPath != "" && !field.Anonymous { | |||||
continue // Private field | |||||
} | |||||
info := fieldInfo{Num: i} | |||||
tag := field.Tag.Get("yaml") | |||||
if tag == "" && strings.Index(string(field.Tag), ":") < 0 { | |||||
tag = string(field.Tag) | |||||
} | |||||
if tag == "-" { | |||||
continue | |||||
} | |||||
inline := false | |||||
fields := strings.Split(tag, ",") | |||||
if len(fields) > 1 { | |||||
for _, flag := range fields[1:] { | |||||
switch flag { | |||||
case "omitempty": | |||||
info.OmitEmpty = true | |||||
case "flow": | |||||
info.Flow = true | |||||
case "inline": | |||||
inline = true | |||||
default: | |||||
return nil, errors.New(fmt.Sprintf("unsupported flag %q in tag %q of type %s", flag, tag, st)) | |||||
} | |||||
} | |||||
tag = fields[0] | |||||
} | |||||
if inline { | |||||
switch field.Type.Kind() { | |||||
case reflect.Map: | |||||
if inlineMap >= 0 { | |||||
return nil, errors.New("multiple ,inline maps in struct " + st.String()) | |||||
} | |||||
if field.Type.Key() != reflect.TypeOf("") { | |||||
return nil, errors.New("option ,inline needs a map with string keys in struct " + st.String()) | |||||
} | |||||
inlineMap = info.Num | |||||
case reflect.Struct, reflect.Ptr: | |||||
ftype := field.Type | |||||
for ftype.Kind() == reflect.Ptr { | |||||
ftype = ftype.Elem() | |||||
} | |||||
if ftype.Kind() != reflect.Struct { | |||||
return nil, errors.New("option ,inline may only be used on a struct or map field") | |||||
} | |||||
if reflect.PtrTo(ftype).Implements(unmarshalerType) { | |||||
inlineUnmarshalers = append(inlineUnmarshalers, []int{i}) | |||||
} else { | |||||
sinfo, err := getStructInfo(ftype) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
for _, index := range sinfo.InlineUnmarshalers { | |||||
inlineUnmarshalers = append(inlineUnmarshalers, append([]int{i}, index...)) | |||||
} | |||||
for _, finfo := range sinfo.FieldsList { | |||||
if _, found := fieldsMap[finfo.Key]; found { | |||||
msg := "duplicated key '" + finfo.Key + "' in struct " + st.String() | |||||
return nil, errors.New(msg) | |||||
} | |||||
if finfo.Inline == nil { | |||||
finfo.Inline = []int{i, finfo.Num} | |||||
} else { | |||||
finfo.Inline = append([]int{i}, finfo.Inline...) | |||||
} | |||||
finfo.Id = len(fieldsList) | |||||
fieldsMap[finfo.Key] = finfo | |||||
fieldsList = append(fieldsList, finfo) | |||||
} | |||||
} | |||||
default: | |||||
return nil, errors.New("option ,inline may only be used on a struct or map field") | |||||
} | |||||
continue | |||||
} | |||||
if tag != "" { | |||||
info.Key = tag | |||||
} else { | |||||
info.Key = strings.ToLower(field.Name) | |||||
} | |||||
if _, found = fieldsMap[info.Key]; found { | |||||
msg := "duplicated key '" + info.Key + "' in struct " + st.String() | |||||
return nil, errors.New(msg) | |||||
} | |||||
info.Id = len(fieldsList) | |||||
fieldsList = append(fieldsList, info) | |||||
fieldsMap[info.Key] = info | |||||
} | |||||
sinfo = &structInfo{ | |||||
FieldsMap: fieldsMap, | |||||
FieldsList: fieldsList, | |||||
InlineMap: inlineMap, | |||||
InlineUnmarshalers: inlineUnmarshalers, | |||||
} | |||||
fieldMapMutex.Lock() | |||||
structMap[st] = sinfo | |||||
fieldMapMutex.Unlock() | |||||
return sinfo, nil | |||||
} | |||||
// IsZeroer is used to check whether an object is zero to | |||||
// determine whether it should be omitted when marshaling | |||||
// with the omitempty flag. One notable implementation | |||||
// is time.Time. | |||||
type IsZeroer interface { | |||||
IsZero() bool | |||||
} | |||||
func isZero(v reflect.Value) bool { | |||||
kind := v.Kind() | |||||
if z, ok := v.Interface().(IsZeroer); ok { | |||||
if (kind == reflect.Ptr || kind == reflect.Interface) && v.IsNil() { | |||||
return true | |||||
} | |||||
return z.IsZero() | |||||
} | |||||
switch kind { | |||||
case reflect.String: | |||||
return len(v.String()) == 0 | |||||
case reflect.Interface, reflect.Ptr: | |||||
return v.IsNil() | |||||
case reflect.Slice: | |||||
return v.Len() == 0 | |||||
case reflect.Map: | |||||
return v.Len() == 0 | |||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | |||||
return v.Int() == 0 | |||||
case reflect.Float32, reflect.Float64: | |||||
return v.Float() == 0 | |||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: | |||||
return v.Uint() == 0 | |||||
case reflect.Bool: | |||||
return !v.Bool() | |||||
case reflect.Struct: | |||||
vt := v.Type() | |||||
for i := v.NumField() - 1; i >= 0; i-- { | |||||
if vt.Field(i).PkgPath != "" { | |||||
continue // Private field | |||||
} | |||||
if !isZero(v.Field(i)) { | |||||
return false | |||||
} | |||||
} | |||||
return true | |||||
} | |||||
return false | |||||
} |
@@ -0,0 +1,805 @@ | |||||
// | |||||
// Copyright (c) 2011-2019 Canonical Ltd | |||||
// Copyright (c) 2006-2010 Kirill Simonov | |||||
// | |||||
// 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. | |||||
package yaml | |||||
import ( | |||||
"fmt" | |||||
"io" | |||||
) | |||||
// The version directive data. | |||||
type yaml_version_directive_t struct { | |||||
major int8 // The major version number. | |||||
minor int8 // The minor version number. | |||||
} | |||||
// The tag directive data. | |||||
type yaml_tag_directive_t struct { | |||||
handle []byte // The tag handle. | |||||
prefix []byte // The tag prefix. | |||||
} | |||||
type yaml_encoding_t int | |||||
// The stream encoding. | |||||
const ( | |||||
// Let the parser choose the encoding. | |||||
yaml_ANY_ENCODING yaml_encoding_t = iota | |||||
yaml_UTF8_ENCODING // The default UTF-8 encoding. | |||||
yaml_UTF16LE_ENCODING // The UTF-16-LE encoding with BOM. | |||||
yaml_UTF16BE_ENCODING // The UTF-16-BE encoding with BOM. | |||||
) | |||||
type yaml_break_t int | |||||
// Line break types. | |||||
const ( | |||||
// Let the parser choose the break type. | |||||
yaml_ANY_BREAK yaml_break_t = iota | |||||
yaml_CR_BREAK // Use CR for line breaks (Mac style). | |||||
yaml_LN_BREAK // Use LN for line breaks (Unix style). | |||||
yaml_CRLN_BREAK // Use CR LN for line breaks (DOS style). | |||||
) | |||||
type yaml_error_type_t int | |||||
// Many bad things could happen with the parser and emitter. | |||||
const ( | |||||
// No error is produced. | |||||
yaml_NO_ERROR yaml_error_type_t = iota | |||||
yaml_MEMORY_ERROR // Cannot allocate or reallocate a block of memory. | |||||
yaml_READER_ERROR // Cannot read or decode the input stream. | |||||
yaml_SCANNER_ERROR // Cannot scan the input stream. | |||||
yaml_PARSER_ERROR // Cannot parse the input stream. | |||||
yaml_COMPOSER_ERROR // Cannot compose a YAML document. | |||||
yaml_WRITER_ERROR // Cannot write to the output stream. | |||||
yaml_EMITTER_ERROR // Cannot emit a YAML stream. | |||||
) | |||||
// The pointer position. | |||||
type yaml_mark_t struct { | |||||
index int // The position index. | |||||
line int // The position line. | |||||
column int // The position column. | |||||
} | |||||
// Node Styles | |||||
type yaml_style_t int8 | |||||
type yaml_scalar_style_t yaml_style_t | |||||
// Scalar styles. | |||||
const ( | |||||
// Let the emitter choose the style. | |||||
yaml_ANY_SCALAR_STYLE yaml_scalar_style_t = 0 | |||||
yaml_PLAIN_SCALAR_STYLE yaml_scalar_style_t = 1 << iota // The plain scalar style. | |||||
yaml_SINGLE_QUOTED_SCALAR_STYLE // The single-quoted scalar style. | |||||
yaml_DOUBLE_QUOTED_SCALAR_STYLE // The double-quoted scalar style. | |||||
yaml_LITERAL_SCALAR_STYLE // The literal scalar style. | |||||
yaml_FOLDED_SCALAR_STYLE // The folded scalar style. | |||||
) | |||||
type yaml_sequence_style_t yaml_style_t | |||||
// Sequence styles. | |||||
const ( | |||||
// Let the emitter choose the style. | |||||
yaml_ANY_SEQUENCE_STYLE yaml_sequence_style_t = iota | |||||
yaml_BLOCK_SEQUENCE_STYLE // The block sequence style. | |||||
yaml_FLOW_SEQUENCE_STYLE // The flow sequence style. | |||||
) | |||||
type yaml_mapping_style_t yaml_style_t | |||||
// Mapping styles. | |||||
const ( | |||||
// Let the emitter choose the style. | |||||
yaml_ANY_MAPPING_STYLE yaml_mapping_style_t = iota | |||||
yaml_BLOCK_MAPPING_STYLE // The block mapping style. | |||||
yaml_FLOW_MAPPING_STYLE // The flow mapping style. | |||||
) | |||||
// Tokens | |||||
type yaml_token_type_t int | |||||
// Token types. | |||||
const ( | |||||
// An empty token. | |||||
yaml_NO_TOKEN yaml_token_type_t = iota | |||||
yaml_STREAM_START_TOKEN // A STREAM-START token. | |||||
yaml_STREAM_END_TOKEN // A STREAM-END token. | |||||
yaml_VERSION_DIRECTIVE_TOKEN // A VERSION-DIRECTIVE token. | |||||
yaml_TAG_DIRECTIVE_TOKEN // A TAG-DIRECTIVE token. | |||||
yaml_DOCUMENT_START_TOKEN // A DOCUMENT-START token. | |||||
yaml_DOCUMENT_END_TOKEN // A DOCUMENT-END token. | |||||
yaml_BLOCK_SEQUENCE_START_TOKEN // A BLOCK-SEQUENCE-START token. | |||||
yaml_BLOCK_MAPPING_START_TOKEN // A BLOCK-SEQUENCE-END token. | |||||
yaml_BLOCK_END_TOKEN // A BLOCK-END token. | |||||
yaml_FLOW_SEQUENCE_START_TOKEN // A FLOW-SEQUENCE-START token. | |||||
yaml_FLOW_SEQUENCE_END_TOKEN // A FLOW-SEQUENCE-END token. | |||||
yaml_FLOW_MAPPING_START_TOKEN // A FLOW-MAPPING-START token. | |||||
yaml_FLOW_MAPPING_END_TOKEN // A FLOW-MAPPING-END token. | |||||
yaml_BLOCK_ENTRY_TOKEN // A BLOCK-ENTRY token. | |||||
yaml_FLOW_ENTRY_TOKEN // A FLOW-ENTRY token. | |||||
yaml_KEY_TOKEN // A KEY token. | |||||
yaml_VALUE_TOKEN // A VALUE token. | |||||
yaml_ALIAS_TOKEN // An ALIAS token. | |||||
yaml_ANCHOR_TOKEN // An ANCHOR token. | |||||
yaml_TAG_TOKEN // A TAG token. | |||||
yaml_SCALAR_TOKEN // A SCALAR token. | |||||
) | |||||
func (tt yaml_token_type_t) String() string { | |||||
switch tt { | |||||
case yaml_NO_TOKEN: | |||||
return "yaml_NO_TOKEN" | |||||
case yaml_STREAM_START_TOKEN: | |||||
return "yaml_STREAM_START_TOKEN" | |||||
case yaml_STREAM_END_TOKEN: | |||||
return "yaml_STREAM_END_TOKEN" | |||||
case yaml_VERSION_DIRECTIVE_TOKEN: | |||||
return "yaml_VERSION_DIRECTIVE_TOKEN" | |||||
case yaml_TAG_DIRECTIVE_TOKEN: | |||||
return "yaml_TAG_DIRECTIVE_TOKEN" | |||||
case yaml_DOCUMENT_START_TOKEN: | |||||
return "yaml_DOCUMENT_START_TOKEN" | |||||
case yaml_DOCUMENT_END_TOKEN: | |||||
return "yaml_DOCUMENT_END_TOKEN" | |||||
case yaml_BLOCK_SEQUENCE_START_TOKEN: | |||||
return "yaml_BLOCK_SEQUENCE_START_TOKEN" | |||||
case yaml_BLOCK_MAPPING_START_TOKEN: | |||||
return "yaml_BLOCK_MAPPING_START_TOKEN" | |||||
case yaml_BLOCK_END_TOKEN: | |||||
return "yaml_BLOCK_END_TOKEN" | |||||
case yaml_FLOW_SEQUENCE_START_TOKEN: | |||||
return "yaml_FLOW_SEQUENCE_START_TOKEN" | |||||
case yaml_FLOW_SEQUENCE_END_TOKEN: | |||||
return "yaml_FLOW_SEQUENCE_END_TOKEN" | |||||
case yaml_FLOW_MAPPING_START_TOKEN: | |||||
return "yaml_FLOW_MAPPING_START_TOKEN" | |||||
case yaml_FLOW_MAPPING_END_TOKEN: | |||||
return "yaml_FLOW_MAPPING_END_TOKEN" | |||||
case yaml_BLOCK_ENTRY_TOKEN: | |||||
return "yaml_BLOCK_ENTRY_TOKEN" | |||||
case yaml_FLOW_ENTRY_TOKEN: | |||||
return "yaml_FLOW_ENTRY_TOKEN" | |||||
case yaml_KEY_TOKEN: | |||||
return "yaml_KEY_TOKEN" | |||||
case yaml_VALUE_TOKEN: | |||||
return "yaml_VALUE_TOKEN" | |||||
case yaml_ALIAS_TOKEN: | |||||
return "yaml_ALIAS_TOKEN" | |||||
case yaml_ANCHOR_TOKEN: | |||||
return "yaml_ANCHOR_TOKEN" | |||||
case yaml_TAG_TOKEN: | |||||
return "yaml_TAG_TOKEN" | |||||
case yaml_SCALAR_TOKEN: | |||||
return "yaml_SCALAR_TOKEN" | |||||
} | |||||
return "<unknown token>" | |||||
} | |||||
// The token structure. | |||||
type yaml_token_t struct { | |||||
// The token type. | |||||
typ yaml_token_type_t | |||||
// The start/end of the token. | |||||
start_mark, end_mark yaml_mark_t | |||||
// The stream encoding (for yaml_STREAM_START_TOKEN). | |||||
encoding yaml_encoding_t | |||||
// The alias/anchor/scalar value or tag/tag directive handle | |||||
// (for yaml_ALIAS_TOKEN, yaml_ANCHOR_TOKEN, yaml_SCALAR_TOKEN, yaml_TAG_TOKEN, yaml_TAG_DIRECTIVE_TOKEN). | |||||
value []byte | |||||
// The tag suffix (for yaml_TAG_TOKEN). | |||||
suffix []byte | |||||
// The tag directive prefix (for yaml_TAG_DIRECTIVE_TOKEN). | |||||
prefix []byte | |||||
// The scalar style (for yaml_SCALAR_TOKEN). | |||||
style yaml_scalar_style_t | |||||
// The version directive major/minor (for yaml_VERSION_DIRECTIVE_TOKEN). | |||||
major, minor int8 | |||||
} | |||||
// Events | |||||
type yaml_event_type_t int8 | |||||
// Event types. | |||||
const ( | |||||
// An empty event. | |||||
yaml_NO_EVENT yaml_event_type_t = iota | |||||
yaml_STREAM_START_EVENT // A STREAM-START event. | |||||
yaml_STREAM_END_EVENT // A STREAM-END event. | |||||
yaml_DOCUMENT_START_EVENT // A DOCUMENT-START event. | |||||
yaml_DOCUMENT_END_EVENT // A DOCUMENT-END event. | |||||
yaml_ALIAS_EVENT // An ALIAS event. | |||||
yaml_SCALAR_EVENT // A SCALAR event. | |||||
yaml_SEQUENCE_START_EVENT // A SEQUENCE-START event. | |||||
yaml_SEQUENCE_END_EVENT // A SEQUENCE-END event. | |||||
yaml_MAPPING_START_EVENT // A MAPPING-START event. | |||||
yaml_MAPPING_END_EVENT // A MAPPING-END event. | |||||
yaml_TAIL_COMMENT_EVENT | |||||
) | |||||
var eventStrings = []string{ | |||||
yaml_NO_EVENT: "none", | |||||
yaml_STREAM_START_EVENT: "stream start", | |||||
yaml_STREAM_END_EVENT: "stream end", | |||||
yaml_DOCUMENT_START_EVENT: "document start", | |||||
yaml_DOCUMENT_END_EVENT: "document end", | |||||
yaml_ALIAS_EVENT: "alias", | |||||
yaml_SCALAR_EVENT: "scalar", | |||||
yaml_SEQUENCE_START_EVENT: "sequence start", | |||||
yaml_SEQUENCE_END_EVENT: "sequence end", | |||||
yaml_MAPPING_START_EVENT: "mapping start", | |||||
yaml_MAPPING_END_EVENT: "mapping end", | |||||
yaml_TAIL_COMMENT_EVENT: "tail comment", | |||||
} | |||||
func (e yaml_event_type_t) String() string { | |||||
if e < 0 || int(e) >= len(eventStrings) { | |||||
return fmt.Sprintf("unknown event %d", e) | |||||
} | |||||
return eventStrings[e] | |||||
} | |||||
// The event structure. | |||||
type yaml_event_t struct { | |||||
// The event type. | |||||
typ yaml_event_type_t | |||||
// The start and end of the event. | |||||
start_mark, end_mark yaml_mark_t | |||||
// The document encoding (for yaml_STREAM_START_EVENT). | |||||
encoding yaml_encoding_t | |||||
// The version directive (for yaml_DOCUMENT_START_EVENT). | |||||
version_directive *yaml_version_directive_t | |||||
// The list of tag directives (for yaml_DOCUMENT_START_EVENT). | |||||
tag_directives []yaml_tag_directive_t | |||||
// The comments | |||||
head_comment []byte | |||||
line_comment []byte | |||||
foot_comment []byte | |||||
tail_comment []byte | |||||
// The anchor (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT, yaml_ALIAS_EVENT). | |||||
anchor []byte | |||||
// The tag (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT). | |||||
tag []byte | |||||
// The scalar value (for yaml_SCALAR_EVENT). | |||||
value []byte | |||||
// Is the document start/end indicator implicit, or the tag optional? | |||||
// (for yaml_DOCUMENT_START_EVENT, yaml_DOCUMENT_END_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT, yaml_SCALAR_EVENT). | |||||
implicit bool | |||||
// Is the tag optional for any non-plain style? (for yaml_SCALAR_EVENT). | |||||
quoted_implicit bool | |||||
// The style (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT). | |||||
style yaml_style_t | |||||
} | |||||
func (e *yaml_event_t) scalar_style() yaml_scalar_style_t { return yaml_scalar_style_t(e.style) } | |||||
func (e *yaml_event_t) sequence_style() yaml_sequence_style_t { return yaml_sequence_style_t(e.style) } | |||||
func (e *yaml_event_t) mapping_style() yaml_mapping_style_t { return yaml_mapping_style_t(e.style) } | |||||
// Nodes | |||||
const ( | |||||
yaml_NULL_TAG = "tag:yaml.org,2002:null" // The tag !!null with the only possible value: null. | |||||
yaml_BOOL_TAG = "tag:yaml.org,2002:bool" // The tag !!bool with the values: true and false. | |||||
yaml_STR_TAG = "tag:yaml.org,2002:str" // The tag !!str for string values. | |||||
yaml_INT_TAG = "tag:yaml.org,2002:int" // The tag !!int for integer values. | |||||
yaml_FLOAT_TAG = "tag:yaml.org,2002:float" // The tag !!float for float values. | |||||
yaml_TIMESTAMP_TAG = "tag:yaml.org,2002:timestamp" // The tag !!timestamp for date and time values. | |||||
yaml_SEQ_TAG = "tag:yaml.org,2002:seq" // The tag !!seq is used to denote sequences. | |||||
yaml_MAP_TAG = "tag:yaml.org,2002:map" // The tag !!map is used to denote mapping. | |||||
// Not in original libyaml. | |||||
yaml_BINARY_TAG = "tag:yaml.org,2002:binary" | |||||
yaml_MERGE_TAG = "tag:yaml.org,2002:merge" | |||||
yaml_DEFAULT_SCALAR_TAG = yaml_STR_TAG // The default scalar tag is !!str. | |||||
yaml_DEFAULT_SEQUENCE_TAG = yaml_SEQ_TAG // The default sequence tag is !!seq. | |||||
yaml_DEFAULT_MAPPING_TAG = yaml_MAP_TAG // The default mapping tag is !!map. | |||||
) | |||||
type yaml_node_type_t int | |||||
// Node types. | |||||
const ( | |||||
// An empty node. | |||||
yaml_NO_NODE yaml_node_type_t = iota | |||||
yaml_SCALAR_NODE // A scalar node. | |||||
yaml_SEQUENCE_NODE // A sequence node. | |||||
yaml_MAPPING_NODE // A mapping node. | |||||
) | |||||
// An element of a sequence node. | |||||
type yaml_node_item_t int | |||||
// An element of a mapping node. | |||||
type yaml_node_pair_t struct { | |||||
key int // The key of the element. | |||||
value int // The value of the element. | |||||
} | |||||
// The node structure. | |||||
type yaml_node_t struct { | |||||
typ yaml_node_type_t // The node type. | |||||
tag []byte // The node tag. | |||||
// The node data. | |||||
// The scalar parameters (for yaml_SCALAR_NODE). | |||||
scalar struct { | |||||
value []byte // The scalar value. | |||||
length int // The length of the scalar value. | |||||
style yaml_scalar_style_t // The scalar style. | |||||
} | |||||
// The sequence parameters (for YAML_SEQUENCE_NODE). | |||||
sequence struct { | |||||
items_data []yaml_node_item_t // The stack of sequence items. | |||||
style yaml_sequence_style_t // The sequence style. | |||||
} | |||||
// The mapping parameters (for yaml_MAPPING_NODE). | |||||
mapping struct { | |||||
pairs_data []yaml_node_pair_t // The stack of mapping pairs (key, value). | |||||
pairs_start *yaml_node_pair_t // The beginning of the stack. | |||||
pairs_end *yaml_node_pair_t // The end of the stack. | |||||
pairs_top *yaml_node_pair_t // The top of the stack. | |||||
style yaml_mapping_style_t // The mapping style. | |||||
} | |||||
start_mark yaml_mark_t // The beginning of the node. | |||||
end_mark yaml_mark_t // The end of the node. | |||||
} | |||||
// The document structure. | |||||
type yaml_document_t struct { | |||||
// The document nodes. | |||||
nodes []yaml_node_t | |||||
// The version directive. | |||||
version_directive *yaml_version_directive_t | |||||
// The list of tag directives. | |||||
tag_directives_data []yaml_tag_directive_t | |||||
tag_directives_start int // The beginning of the tag directives list. | |||||
tag_directives_end int // The end of the tag directives list. | |||||
start_implicit int // Is the document start indicator implicit? | |||||
end_implicit int // Is the document end indicator implicit? | |||||
// The start/end of the document. | |||||
start_mark, end_mark yaml_mark_t | |||||
} | |||||
// The prototype of a read handler. | |||||
// | |||||
// The read handler is called when the parser needs to read more bytes from the | |||||
// source. The handler should write not more than size bytes to the buffer. | |||||
// The number of written bytes should be set to the size_read variable. | |||||
// | |||||
// [in,out] data A pointer to an application data specified by | |||||
// yaml_parser_set_input(). | |||||
// [out] buffer The buffer to write the data from the source. | |||||
// [in] size The size of the buffer. | |||||
// [out] size_read The actual number of bytes read from the source. | |||||
// | |||||
// On success, the handler should return 1. If the handler failed, | |||||
// the returned value should be 0. On EOF, the handler should set the | |||||
// size_read to 0 and return 1. | |||||
type yaml_read_handler_t func(parser *yaml_parser_t, buffer []byte) (n int, err error) | |||||
// This structure holds information about a potential simple key. | |||||
type yaml_simple_key_t struct { | |||||
possible bool // Is a simple key possible? | |||||
required bool // Is a simple key required? | |||||
token_number int // The number of the token. | |||||
mark yaml_mark_t // The position mark. | |||||
} | |||||
// The states of the parser. | |||||
type yaml_parser_state_t int | |||||
const ( | |||||
yaml_PARSE_STREAM_START_STATE yaml_parser_state_t = iota | |||||
yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE // Expect the beginning of an implicit document. | |||||
yaml_PARSE_DOCUMENT_START_STATE // Expect DOCUMENT-START. | |||||
yaml_PARSE_DOCUMENT_CONTENT_STATE // Expect the content of a document. | |||||
yaml_PARSE_DOCUMENT_END_STATE // Expect DOCUMENT-END. | |||||
yaml_PARSE_BLOCK_NODE_STATE // Expect a block node. | |||||
yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE // Expect a block node or indentless sequence. | |||||
yaml_PARSE_FLOW_NODE_STATE // Expect a flow node. | |||||
yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE // Expect the first entry of a block sequence. | |||||
yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE // Expect an entry of a block sequence. | |||||
yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE // Expect an entry of an indentless sequence. | |||||
yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE // Expect the first key of a block mapping. | |||||
yaml_PARSE_BLOCK_MAPPING_KEY_STATE // Expect a block mapping key. | |||||
yaml_PARSE_BLOCK_MAPPING_VALUE_STATE // Expect a block mapping value. | |||||
yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE // Expect the first entry of a flow sequence. | |||||
yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE // Expect an entry of a flow sequence. | |||||
yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE // Expect a key of an ordered mapping. | |||||
yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE // Expect a value of an ordered mapping. | |||||
yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE // Expect the and of an ordered mapping entry. | |||||
yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE // Expect the first key of a flow mapping. | |||||
yaml_PARSE_FLOW_MAPPING_KEY_STATE // Expect a key of a flow mapping. | |||||
yaml_PARSE_FLOW_MAPPING_VALUE_STATE // Expect a value of a flow mapping. | |||||
yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE // Expect an empty value of a flow mapping. | |||||
yaml_PARSE_END_STATE // Expect nothing. | |||||
) | |||||
func (ps yaml_parser_state_t) String() string { | |||||
switch ps { | |||||
case yaml_PARSE_STREAM_START_STATE: | |||||
return "yaml_PARSE_STREAM_START_STATE" | |||||
case yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE: | |||||
return "yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE" | |||||
case yaml_PARSE_DOCUMENT_START_STATE: | |||||
return "yaml_PARSE_DOCUMENT_START_STATE" | |||||
case yaml_PARSE_DOCUMENT_CONTENT_STATE: | |||||
return "yaml_PARSE_DOCUMENT_CONTENT_STATE" | |||||
case yaml_PARSE_DOCUMENT_END_STATE: | |||||
return "yaml_PARSE_DOCUMENT_END_STATE" | |||||
case yaml_PARSE_BLOCK_NODE_STATE: | |||||
return "yaml_PARSE_BLOCK_NODE_STATE" | |||||
case yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE: | |||||
return "yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE" | |||||
case yaml_PARSE_FLOW_NODE_STATE: | |||||
return "yaml_PARSE_FLOW_NODE_STATE" | |||||
case yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE: | |||||
return "yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE" | |||||
case yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE: | |||||
return "yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE" | |||||
case yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE: | |||||
return "yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE" | |||||
case yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE: | |||||
return "yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE" | |||||
case yaml_PARSE_BLOCK_MAPPING_KEY_STATE: | |||||
return "yaml_PARSE_BLOCK_MAPPING_KEY_STATE" | |||||
case yaml_PARSE_BLOCK_MAPPING_VALUE_STATE: | |||||
return "yaml_PARSE_BLOCK_MAPPING_VALUE_STATE" | |||||
case yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE: | |||||
return "yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE" | |||||
case yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE: | |||||
return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE" | |||||
case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE: | |||||
return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE" | |||||
case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE: | |||||
return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE" | |||||
case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE: | |||||
return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE" | |||||
case yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE: | |||||
return "yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE" | |||||
case yaml_PARSE_FLOW_MAPPING_KEY_STATE: | |||||
return "yaml_PARSE_FLOW_MAPPING_KEY_STATE" | |||||
case yaml_PARSE_FLOW_MAPPING_VALUE_STATE: | |||||
return "yaml_PARSE_FLOW_MAPPING_VALUE_STATE" | |||||
case yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE: | |||||
return "yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE" | |||||
case yaml_PARSE_END_STATE: | |||||
return "yaml_PARSE_END_STATE" | |||||
} | |||||
return "<unknown parser state>" | |||||
} | |||||
// This structure holds aliases data. | |||||
type yaml_alias_data_t struct { | |||||
anchor []byte // The anchor. | |||||
index int // The node id. | |||||
mark yaml_mark_t // The anchor mark. | |||||
} | |||||
// The parser structure. | |||||
// | |||||
// All members are internal. Manage the structure using the | |||||
// yaml_parser_ family of functions. | |||||
type yaml_parser_t struct { | |||||
// Error handling | |||||
error yaml_error_type_t // Error type. | |||||
problem string // Error description. | |||||
// The byte about which the problem occurred. | |||||
problem_offset int | |||||
problem_value int | |||||
problem_mark yaml_mark_t | |||||
// The error context. | |||||
context string | |||||
context_mark yaml_mark_t | |||||
// Reader stuff | |||||
read_handler yaml_read_handler_t // Read handler. | |||||
input_reader io.Reader // File input data. | |||||
input []byte // String input data. | |||||
input_pos int | |||||
eof bool // EOF flag | |||||
buffer []byte // The working buffer. | |||||
buffer_pos int // The current position of the buffer. | |||||
unread int // The number of unread characters in the buffer. | |||||
newlines int // The number of line breaks since last non-break/non-blank character | |||||
raw_buffer []byte // The raw buffer. | |||||
raw_buffer_pos int // The current position of the buffer. | |||||
encoding yaml_encoding_t // The input encoding. | |||||
offset int // The offset of the current position (in bytes). | |||||
mark yaml_mark_t // The mark of the current position. | |||||
// Comments | |||||
head_comment []byte // The current head comments | |||||
line_comment []byte // The current line comments | |||||
foot_comment []byte // The current foot comments | |||||
tail_comment []byte // Foot comment that happens at the end of a block. | |||||
stem_comment []byte // Comment in item preceding a nested structure (list inside list item, etc) | |||||
comments []yaml_comment_t // The folded comments for all parsed tokens | |||||
comments_head int | |||||
// Scanner stuff | |||||
stream_start_produced bool // Have we started to scan the input stream? | |||||
stream_end_produced bool // Have we reached the end of the input stream? | |||||
flow_level int // The number of unclosed '[' and '{' indicators. | |||||
tokens []yaml_token_t // The tokens queue. | |||||
tokens_head int // The head of the tokens queue. | |||||
tokens_parsed int // The number of tokens fetched from the queue. | |||||
token_available bool // Does the tokens queue contain a token ready for dequeueing. | |||||
indent int // The current indentation level. | |||||
indents []int // The indentation levels stack. | |||||
simple_key_allowed bool // May a simple key occur at the current position? | |||||
simple_keys []yaml_simple_key_t // The stack of simple keys. | |||||
simple_keys_by_tok map[int]int // possible simple_key indexes indexed by token_number | |||||
// Parser stuff | |||||
state yaml_parser_state_t // The current parser state. | |||||
states []yaml_parser_state_t // The parser states stack. | |||||
marks []yaml_mark_t // The stack of marks. | |||||
tag_directives []yaml_tag_directive_t // The list of TAG directives. | |||||
// Dumper stuff | |||||
aliases []yaml_alias_data_t // The alias data. | |||||
document *yaml_document_t // The currently parsed document. | |||||
} | |||||
type yaml_comment_t struct { | |||||
scan_mark yaml_mark_t // Position where scanning for comments started | |||||
token_mark yaml_mark_t // Position after which tokens will be associated with this comment | |||||
start_mark yaml_mark_t // Position of '#' comment mark | |||||
end_mark yaml_mark_t // Position where comment terminated | |||||
head []byte | |||||
line []byte | |||||
foot []byte | |||||
} | |||||
// Emitter Definitions | |||||
// The prototype of a write handler. | |||||
// | |||||
// The write handler is called when the emitter needs to flush the accumulated | |||||
// characters to the output. The handler should write @a size bytes of the | |||||
// @a buffer to the output. | |||||
// | |||||
// @param[in,out] data A pointer to an application data specified by | |||||
// yaml_emitter_set_output(). | |||||
// @param[in] buffer The buffer with bytes to be written. | |||||
// @param[in] size The size of the buffer. | |||||
// | |||||
// @returns On success, the handler should return @c 1. If the handler failed, | |||||
// the returned value should be @c 0. | |||||
// | |||||
type yaml_write_handler_t func(emitter *yaml_emitter_t, buffer []byte) error | |||||
type yaml_emitter_state_t int | |||||
// The emitter states. | |||||
const ( | |||||
// Expect STREAM-START. | |||||
yaml_EMIT_STREAM_START_STATE yaml_emitter_state_t = iota | |||||
yaml_EMIT_FIRST_DOCUMENT_START_STATE // Expect the first DOCUMENT-START or STREAM-END. | |||||
yaml_EMIT_DOCUMENT_START_STATE // Expect DOCUMENT-START or STREAM-END. | |||||
yaml_EMIT_DOCUMENT_CONTENT_STATE // Expect the content of a document. | |||||
yaml_EMIT_DOCUMENT_END_STATE // Expect DOCUMENT-END. | |||||
yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE // Expect the first item of a flow sequence. | |||||
yaml_EMIT_FLOW_SEQUENCE_TRAIL_ITEM_STATE // Expect the next item of a flow sequence, with the comma already written out | |||||
yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE // Expect an item of a flow sequence. | |||||
yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE // Expect the first key of a flow mapping. | |||||
yaml_EMIT_FLOW_MAPPING_TRAIL_KEY_STATE // Expect the next key of a flow mapping, with the comma already written out | |||||
yaml_EMIT_FLOW_MAPPING_KEY_STATE // Expect a key of a flow mapping. | |||||
yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE // Expect a value for a simple key of a flow mapping. | |||||
yaml_EMIT_FLOW_MAPPING_VALUE_STATE // Expect a value of a flow mapping. | |||||
yaml_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE // Expect the first item of a block sequence. | |||||
yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE // Expect an item of a block sequence. | |||||
yaml_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE // Expect the first key of a block mapping. | |||||
yaml_EMIT_BLOCK_MAPPING_KEY_STATE // Expect the key of a block mapping. | |||||
yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE // Expect a value for a simple key of a block mapping. | |||||
yaml_EMIT_BLOCK_MAPPING_VALUE_STATE // Expect a value of a block mapping. | |||||
yaml_EMIT_END_STATE // Expect nothing. | |||||
) | |||||
// The emitter structure. | |||||
// | |||||
// All members are internal. Manage the structure using the @c yaml_emitter_ | |||||
// family of functions. | |||||
type yaml_emitter_t struct { | |||||
// Error handling | |||||
error yaml_error_type_t // Error type. | |||||
problem string // Error description. | |||||
// Writer stuff | |||||
write_handler yaml_write_handler_t // Write handler. | |||||
output_buffer *[]byte // String output data. | |||||
output_writer io.Writer // File output data. | |||||
buffer []byte // The working buffer. | |||||
buffer_pos int // The current position of the buffer. | |||||
raw_buffer []byte // The raw buffer. | |||||
raw_buffer_pos int // The current position of the buffer. | |||||
encoding yaml_encoding_t // The stream encoding. | |||||
// Emitter stuff | |||||
canonical bool // If the output is in the canonical style? | |||||
best_indent int // The number of indentation spaces. | |||||
best_width int // The preferred width of the output lines. | |||||
unicode bool // Allow unescaped non-ASCII characters? | |||||
line_break yaml_break_t // The preferred line break. | |||||
state yaml_emitter_state_t // The current emitter state. | |||||
states []yaml_emitter_state_t // The stack of states. | |||||
events []yaml_event_t // The event queue. | |||||
events_head int // The head of the event queue. | |||||
indents []int // The stack of indentation levels. | |||||
tag_directives []yaml_tag_directive_t // The list of tag directives. | |||||
indent int // The current indentation level. | |||||
flow_level int // The current flow level. | |||||
root_context bool // Is it the document root context? | |||||
sequence_context bool // Is it a sequence context? | |||||
mapping_context bool // Is it a mapping context? | |||||
simple_key_context bool // Is it a simple mapping key context? | |||||
line int // The current line. | |||||
column int // The current column. | |||||
whitespace bool // If the last character was a whitespace? | |||||
indention bool // If the last character was an indentation character (' ', '-', '?', ':')? | |||||
open_ended bool // If an explicit document end is required? | |||||
space_above bool // Is there's an empty line above? | |||||
foot_indent int // The indent used to write the foot comment above, or -1 if none. | |||||
// Anchor analysis. | |||||
anchor_data struct { | |||||
anchor []byte // The anchor value. | |||||
alias bool // Is it an alias? | |||||
} | |||||
// Tag analysis. | |||||
tag_data struct { | |||||
handle []byte // The tag handle. | |||||
suffix []byte // The tag suffix. | |||||
} | |||||
// Scalar analysis. | |||||
scalar_data struct { | |||||
value []byte // The scalar value. | |||||
multiline bool // Does the scalar contain line breaks? | |||||
flow_plain_allowed bool // Can the scalar be expessed in the flow plain style? | |||||
block_plain_allowed bool // Can the scalar be expressed in the block plain style? | |||||
single_quoted_allowed bool // Can the scalar be expressed in the single quoted style? | |||||
block_allowed bool // Can the scalar be expressed in the literal or folded styles? | |||||
style yaml_scalar_style_t // The output style. | |||||
} | |||||
// Comments | |||||
head_comment []byte | |||||
line_comment []byte | |||||
foot_comment []byte | |||||
tail_comment []byte | |||||
// Dumper stuff | |||||
opened bool // If the stream was already opened? | |||||
closed bool // If the stream was already closed? | |||||
// The information associated with the document nodes. | |||||
anchors *struct { | |||||
references int // The number of references. | |||||
anchor int // The anchor id. | |||||
serialized bool // If the node has been emitted? | |||||
} | |||||
last_anchor_id int // The last assigned anchor id. | |||||
document *yaml_document_t // The currently emitted document. | |||||
} |
@@ -0,0 +1,198 @@ | |||||
// | |||||
// Copyright (c) 2011-2019 Canonical Ltd | |||||
// Copyright (c) 2006-2010 Kirill Simonov | |||||
// | |||||
// 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. | |||||
package yaml | |||||
const ( | |||||
// The size of the input raw buffer. | |||||
input_raw_buffer_size = 512 | |||||
// The size of the input buffer. | |||||
// It should be possible to decode the whole raw buffer. | |||||
input_buffer_size = input_raw_buffer_size * 3 | |||||
// The size of the output buffer. | |||||
output_buffer_size = 128 | |||||
// The size of the output raw buffer. | |||||
// It should be possible to encode the whole output buffer. | |||||
output_raw_buffer_size = (output_buffer_size*2 + 2) | |||||
// The size of other stacks and queues. | |||||
initial_stack_size = 16 | |||||
initial_queue_size = 16 | |||||
initial_string_size = 16 | |||||
) | |||||
// Check if the character at the specified position is an alphabetical | |||||
// character, a digit, '_', or '-'. | |||||
func is_alpha(b []byte, i int) bool { | |||||
return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'Z' || b[i] >= 'a' && b[i] <= 'z' || b[i] == '_' || b[i] == '-' | |||||
} | |||||
// Check if the character at the specified position is a digit. | |||||
func is_digit(b []byte, i int) bool { | |||||
return b[i] >= '0' && b[i] <= '9' | |||||
} | |||||
// Get the value of a digit. | |||||
func as_digit(b []byte, i int) int { | |||||
return int(b[i]) - '0' | |||||
} | |||||
// Check if the character at the specified position is a hex-digit. | |||||
func is_hex(b []byte, i int) bool { | |||||
return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'F' || b[i] >= 'a' && b[i] <= 'f' | |||||
} | |||||
// Get the value of a hex-digit. | |||||
func as_hex(b []byte, i int) int { | |||||
bi := b[i] | |||||
if bi >= 'A' && bi <= 'F' { | |||||
return int(bi) - 'A' + 10 | |||||
} | |||||
if bi >= 'a' && bi <= 'f' { | |||||
return int(bi) - 'a' + 10 | |||||
} | |||||
return int(bi) - '0' | |||||
} | |||||
// Check if the character is ASCII. | |||||
func is_ascii(b []byte, i int) bool { | |||||
return b[i] <= 0x7F | |||||
} | |||||
// Check if the character at the start of the buffer can be printed unescaped. | |||||
func is_printable(b []byte, i int) bool { | |||||
return ((b[i] == 0x0A) || // . == #x0A | |||||
(b[i] >= 0x20 && b[i] <= 0x7E) || // #x20 <= . <= #x7E | |||||
(b[i] == 0xC2 && b[i+1] >= 0xA0) || // #0xA0 <= . <= #xD7FF | |||||
(b[i] > 0xC2 && b[i] < 0xED) || | |||||
(b[i] == 0xED && b[i+1] < 0xA0) || | |||||
(b[i] == 0xEE) || | |||||
(b[i] == 0xEF && // #xE000 <= . <= #xFFFD | |||||
!(b[i+1] == 0xBB && b[i+2] == 0xBF) && // && . != #xFEFF | |||||
!(b[i+1] == 0xBF && (b[i+2] == 0xBE || b[i+2] == 0xBF)))) | |||||
} | |||||
// Check if the character at the specified position is NUL. | |||||
func is_z(b []byte, i int) bool { | |||||
return b[i] == 0x00 | |||||
} | |||||
// Check if the beginning of the buffer is a BOM. | |||||
func is_bom(b []byte, i int) bool { | |||||
return b[0] == 0xEF && b[1] == 0xBB && b[2] == 0xBF | |||||
} | |||||
// Check if the character at the specified position is space. | |||||
func is_space(b []byte, i int) bool { | |||||
return b[i] == ' ' | |||||
} | |||||
// Check if the character at the specified position is tab. | |||||
func is_tab(b []byte, i int) bool { | |||||
return b[i] == '\t' | |||||
} | |||||
// Check if the character at the specified position is blank (space or tab). | |||||
func is_blank(b []byte, i int) bool { | |||||
//return is_space(b, i) || is_tab(b, i) | |||||
return b[i] == ' ' || b[i] == '\t' | |||||
} | |||||
// Check if the character at the specified position is a line break. | |||||
func is_break(b []byte, i int) bool { | |||||
return (b[i] == '\r' || // CR (#xD) | |||||
b[i] == '\n' || // LF (#xA) | |||||
b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) | |||||
b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) | |||||
b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9) // PS (#x2029) | |||||
} | |||||
func is_crlf(b []byte, i int) bool { | |||||
return b[i] == '\r' && b[i+1] == '\n' | |||||
} | |||||
// Check if the character is a line break or NUL. | |||||
func is_breakz(b []byte, i int) bool { | |||||
//return is_break(b, i) || is_z(b, i) | |||||
return ( | |||||
// is_break: | |||||
b[i] == '\r' || // CR (#xD) | |||||
b[i] == '\n' || // LF (#xA) | |||||
b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) | |||||
b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) | |||||
b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) | |||||
// is_z: | |||||
b[i] == 0) | |||||
} | |||||
// Check if the character is a line break, space, or NUL. | |||||
func is_spacez(b []byte, i int) bool { | |||||
//return is_space(b, i) || is_breakz(b, i) | |||||
return ( | |||||
// is_space: | |||||
b[i] == ' ' || | |||||
// is_breakz: | |||||
b[i] == '\r' || // CR (#xD) | |||||
b[i] == '\n' || // LF (#xA) | |||||
b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) | |||||
b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) | |||||
b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) | |||||
b[i] == 0) | |||||
} | |||||
// Check if the character is a line break, space, tab, or NUL. | |||||
func is_blankz(b []byte, i int) bool { | |||||
//return is_blank(b, i) || is_breakz(b, i) | |||||
return ( | |||||
// is_blank: | |||||
b[i] == ' ' || b[i] == '\t' || | |||||
// is_breakz: | |||||
b[i] == '\r' || // CR (#xD) | |||||
b[i] == '\n' || // LF (#xA) | |||||
b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) | |||||
b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) | |||||
b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) | |||||
b[i] == 0) | |||||
} | |||||
// Determine the width of the character. | |||||
func width(b byte) int { | |||||
// Don't replace these by a switch without first | |||||
// confirming that it is being inlined. | |||||
if b&0x80 == 0x00 { | |||||
return 1 | |||||
} | |||||
if b&0xE0 == 0xC0 { | |||||
return 2 | |||||
} | |||||
if b&0xF0 == 0xE0 { | |||||
return 3 | |||||
} | |||||
if b&0xF8 == 0xF0 { | |||||
return 4 | |||||
} | |||||
return 0 | |||||
} |
@@ -253,6 +253,9 @@ github.com/dustin/go-humanize | |||||
# github.com/editorconfig/editorconfig-core-go/v2 v2.1.1 | # github.com/editorconfig/editorconfig-core-go/v2 v2.1.1 | ||||
## explicit | ## explicit | ||||
github.com/editorconfig/editorconfig-core-go/v2 | github.com/editorconfig/editorconfig-core-go/v2 | ||||
# github.com/elliotchance/orderedmap v1.4.0 | |||||
## explicit | |||||
github.com/elliotchance/orderedmap | |||||
# github.com/emirpasic/gods v1.12.0 | # github.com/emirpasic/gods v1.12.0 | ||||
## explicit | ## explicit | ||||
github.com/emirpasic/gods/containers | github.com/emirpasic/gods/containers | ||||
@@ -753,7 +756,7 @@ github.com/spf13/viper | |||||
github.com/steveyen/gtreap | github.com/steveyen/gtreap | ||||
# github.com/streadway/amqp v0.0.0-20190214183023-884228600bc9 | # github.com/streadway/amqp v0.0.0-20190214183023-884228600bc9 | ||||
github.com/streadway/amqp | github.com/streadway/amqp | ||||
# github.com/stretchr/testify v1.4.0 | |||||
# github.com/stretchr/testify v1.7.0 | |||||
## explicit | ## explicit | ||||
github.com/stretchr/testify/assert | github.com/stretchr/testify/assert | ||||
github.com/stretchr/testify/require | github.com/stretchr/testify/require | ||||
@@ -1134,6 +1137,8 @@ gopkg.in/warnings.v0 | |||||
# gopkg.in/yaml.v2 v2.2.8 | # gopkg.in/yaml.v2 v2.2.8 | ||||
## explicit | ## explicit | ||||
gopkg.in/yaml.v2 | gopkg.in/yaml.v2 | ||||
# gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c | |||||
gopkg.in/yaml.v3 | |||||
# mvdan.cc/xurls/v2 v2.1.0 | # mvdan.cc/xurls/v2 v2.1.0 | ||||
## explicit | ## explicit | ||||
mvdan.cc/xurls/v2 | mvdan.cc/xurls/v2 | ||||
@@ -597,10 +597,22 @@ display: block; | |||||
background: #E5F4F4 | background: #E5F4F4 | ||||
} | } | ||||
.x_icon{ | .x_icon{ | ||||
float: right; | |||||
float: right!important; | |||||
margin-right: 15px !important; | margin-right: 15px !important; | ||||
color: rgb(0,0,0) !important; | |||||
font-size: 20px !important; | |||||
font-weight: bold !important; | |||||
} | } | ||||
.a_width{ | .a_width{ | ||||
width: 50%; | |||||
display:inline-block | |||||
width: 50% !important; | |||||
display:inline-block !important; | |||||
} | |||||
.footer_icon{ | |||||
display: inline-block !important; | |||||
vertical-align: middle !important; | |||||
margin-right:0.8em !important; | |||||
} | |||||
.a_margin{ | |||||
margin: 0px !important; | |||||
} | } |