* vendor update: go-gitlab to v0.31.0 * migrate client init to v0.31.0 * refactortags/v1.21.12.1
| @@ -53,9 +53,10 @@ require ( | |||
| github.com/gobwas/glob v0.2.3 | |||
| github.com/gogs/chardet v0.0.0-20191104214054-4b6791f73a28 | |||
| github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14 | |||
| github.com/golang/protobuf v1.3.4 // indirect | |||
| github.com/golang/protobuf v1.4.0 // indirect | |||
| github.com/google/go-github/v24 v24.0.1 | |||
| github.com/gorilla/context v1.1.1 | |||
| github.com/hashicorp/go-retryablehttp v0.6.6 // indirect | |||
| github.com/huandu/xstrings v1.3.0 | |||
| github.com/issue9/assert v1.3.2 // indirect | |||
| github.com/issue9/identicon v0.0.0-20160320065130-d36b54562f4c | |||
| @@ -104,17 +105,19 @@ require ( | |||
| github.com/unknwon/i18n v0.0.0-20190805065654-5c6446a380b6 | |||
| github.com/unknwon/paginater v0.0.0-20151104151617-7748a72e0141 | |||
| github.com/urfave/cli v1.20.0 | |||
| github.com/xanzy/go-gitlab v0.22.1 | |||
| github.com/xanzy/go-gitlab v0.31.0 | |||
| github.com/yohcop/openid-go v0.0.0-20160914080427-2c050d2dae53 | |||
| github.com/yuin/goldmark v1.1.25 | |||
| github.com/yuin/goldmark-meta v0.0.0-20191126180153-f0638e958b60 | |||
| go.etcd.io/bbolt v1.3.3 // indirect | |||
| golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 | |||
| golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e | |||
| golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 | |||
| golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d | |||
| golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd | |||
| golang.org/x/text v0.3.2 | |||
| golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 // indirect | |||
| golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224 | |||
| google.golang.org/appengine v1.6.5 // indirect | |||
| gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect | |||
| gopkg.in/asn1-ber.v1 v1.0.0-20150924051756-4e86f4367175 // indirect | |||
| gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df | |||
| @@ -286,8 +286,12 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y | |||
| github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | |||
| github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= | |||
| github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | |||
| github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk= | |||
| github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= | |||
| github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= | |||
| github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= | |||
| github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= | |||
| github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= | |||
| github.com/golang/protobuf v1.4.0 h1:oOuy+ugB+P/kBdUnG5QaMXSIyJ1q38wWSojYCb3z5VQ= | |||
| github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= | |||
| github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= | |||
| github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= | |||
| github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= | |||
| @@ -297,6 +301,8 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a | |||
| github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | |||
| github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= | |||
| github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | |||
| github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= | |||
| github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | |||
| github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= | |||
| github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= | |||
| github.com/google/go-github/v24 v24.0.1 h1:KCt1LjMJEey1qvPXxa9SjaWxwTsCWSq6p2Ju57UR4Q4= | |||
| @@ -337,6 +343,14 @@ github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoA | |||
| github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= | |||
| github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= | |||
| github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= | |||
| github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= | |||
| github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= | |||
| github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI= | |||
| github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= | |||
| github.com/hashicorp/go-retryablehttp v0.6.4 h1:BbgctKO892xEyOXnGiaAwIoSq1QZ/SS4AhjoAh9DnfY= | |||
| github.com/hashicorp/go-retryablehttp v0.6.4/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= | |||
| github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM= | |||
| github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= | |||
| github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= | |||
| github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= | |||
| github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= | |||
| @@ -608,8 +622,8 @@ github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw= | |||
| github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= | |||
| github.com/willf/bitset v1.1.10 h1:NotGKqX0KwQ72NUzqrjZq5ipPNDQex9lo3WpaS8L2sc= | |||
| github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= | |||
| github.com/xanzy/go-gitlab v0.22.1 h1:TVxgHmoa35jQL+9FCkG0nwPDxU9dQZXknBTDtGaSFno= | |||
| github.com/xanzy/go-gitlab v0.22.1/go.mod h1:t4Bmvnxj7k37S4Y17lfLx+nLqkf/oQwT2HagfWKv5Og= | |||
| github.com/xanzy/go-gitlab v0.31.0 h1:+nHztQuCXGSMluKe5Q9IRaPdz6tO8O0gMkQ0vqGpiBk= | |||
| github.com/xanzy/go-gitlab v0.31.0/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfDUseX11Ug= | |||
| github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70= | |||
| github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= | |||
| github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= | |||
| @@ -699,6 +713,8 @@ golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAG | |||
| golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | |||
| golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= | |||
| golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | |||
| golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw= | |||
| golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | |||
| golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | |||
| golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | |||
| golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | |||
| @@ -741,6 +757,10 @@ golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= | |||
| golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= | |||
| golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= | |||
| golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= | |||
| golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= | |||
| golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= | |||
| golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI= | |||
| golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= | |||
| golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | |||
| golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | |||
| golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | |||
| @@ -782,6 +802,8 @@ google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww | |||
| google.golang.org/appengine v1.6.2/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= | |||
| google.golang.org/appengine v1.6.4 h1:WiKh4+/eMB2HaY7QhCfW/R7MuRAoA8QMCSJA6jP5/fo= | |||
| google.golang.org/appengine v1.6.4/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= | |||
| google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= | |||
| google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= | |||
| google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= | |||
| google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= | |||
| google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= | |||
| @@ -795,6 +817,12 @@ google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZi | |||
| google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= | |||
| google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= | |||
| google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= | |||
| google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= | |||
| google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= | |||
| google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= | |||
| google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= | |||
| google.golang.org/protobuf v1.21.0 h1:qdOKuR/EIArgaWNjetjgTzgVTAZ+S/WXVrq9HW9zimw= | |||
| google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= | |||
| gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= | |||
| gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= | |||
| gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= | |||
| @@ -8,7 +8,6 @@ import ( | |||
| "context" | |||
| "errors" | |||
| "fmt" | |||
| "net/http" | |||
| "net/url" | |||
| "strings" | |||
| "time" | |||
| @@ -87,15 +86,13 @@ type GitlabDownloader struct { | |||
| // Use either a username/password, personal token entered into the username field, or anonymous/public access | |||
| // Note: Public access only allows very basic access | |||
| func NewGitlabDownloader(baseURL, repoPath, username, password string) *GitlabDownloader { | |||
| var client *http.Client | |||
| var gitlabClient *gitlab.Client | |||
| var err error | |||
| if username != "" { | |||
| if password == "" { | |||
| gitlabClient = gitlab.NewClient(client, username) | |||
| gitlabClient, err = gitlab.NewClient(username) | |||
| } else { | |||
| gitlabClient, err = gitlab.NewBasicAuthClient(client, baseURL, username, password) | |||
| gitlabClient, err = gitlab.NewBasicAuthClient(username, password, gitlab.WithBaseURL(baseURL)) | |||
| } | |||
| } | |||
| @@ -0,0 +1,324 @@ | |||
| // Copyright 2019 The Go Authors. All rights reserved. | |||
| // Use of this source code is governed by a BSD-style | |||
| // license that can be found in the LICENSE file. | |||
| package proto | |||
| import ( | |||
| "errors" | |||
| "fmt" | |||
| "google.golang.org/protobuf/encoding/prototext" | |||
| "google.golang.org/protobuf/encoding/protowire" | |||
| "google.golang.org/protobuf/runtime/protoimpl" | |||
| ) | |||
| const ( | |||
| WireVarint = 0 | |||
| WireFixed32 = 5 | |||
| WireFixed64 = 1 | |||
| WireBytes = 2 | |||
| WireStartGroup = 3 | |||
| WireEndGroup = 4 | |||
| ) | |||
| // EncodeVarint returns the varint encoded bytes of v. | |||
| func EncodeVarint(v uint64) []byte { | |||
| return protowire.AppendVarint(nil, v) | |||
| } | |||
| // SizeVarint returns the length of the varint encoded bytes of v. | |||
| // This is equal to len(EncodeVarint(v)). | |||
| func SizeVarint(v uint64) int { | |||
| return protowire.SizeVarint(v) | |||
| } | |||
| // DecodeVarint parses a varint encoded integer from b, returning the | |||
| // integer value and the length of the varint. | |||
| // It returns (0, 0) if there is a parse error. | |||
| func DecodeVarint(b []byte) (uint64, int) { | |||
| v, n := protowire.ConsumeVarint(b) | |||
| if n < 0 { | |||
| return 0, 0 | |||
| } | |||
| return v, n | |||
| } | |||
| // Buffer is a buffer for encoding and decoding the protobuf wire format. | |||
| // It may be reused between invocations to reduce memory usage. | |||
| type Buffer struct { | |||
| buf []byte | |||
| idx int | |||
| deterministic bool | |||
| } | |||
| // NewBuffer allocates a new Buffer initialized with buf, | |||
| // where the contents of buf are considered the unread portion of the buffer. | |||
| func NewBuffer(buf []byte) *Buffer { | |||
| return &Buffer{buf: buf} | |||
| } | |||
| // SetDeterministic specifies whether to use deterministic serialization. | |||
| // | |||
| // Deterministic serialization guarantees that for a given binary, equal | |||
| // messages will always be serialized to the same bytes. This implies: | |||
| // | |||
| // - Repeated serialization of a message will return the same bytes. | |||
| // - Different processes of the same binary (which may be executing on | |||
| // different machines) will serialize equal messages to the same bytes. | |||
| // | |||
| // Note that the deterministic serialization is NOT canonical across | |||
| // languages. It is not guaranteed to remain stable over time. It is unstable | |||
| // across different builds with schema changes due to unknown fields. | |||
| // Users who need canonical serialization (e.g., persistent storage in a | |||
| // canonical form, fingerprinting, etc.) should define their own | |||
| // canonicalization specification and implement their own serializer rather | |||
| // than relying on this API. | |||
| // | |||
| // If deterministic serialization is requested, map entries will be sorted | |||
| // by keys in lexographical order. This is an implementation detail and | |||
| // subject to change. | |||
| func (b *Buffer) SetDeterministic(deterministic bool) { | |||
| b.deterministic = deterministic | |||
| } | |||
| // SetBuf sets buf as the internal buffer, | |||
| // where the contents of buf are considered the unread portion of the buffer. | |||
| func (b *Buffer) SetBuf(buf []byte) { | |||
| b.buf = buf | |||
| b.idx = 0 | |||
| } | |||
| // Reset clears the internal buffer of all written and unread data. | |||
| func (b *Buffer) Reset() { | |||
| b.buf = b.buf[:0] | |||
| b.idx = 0 | |||
| } | |||
| // Bytes returns the internal buffer. | |||
| func (b *Buffer) Bytes() []byte { | |||
| return b.buf | |||
| } | |||
| // Unread returns the unread portion of the buffer. | |||
| func (b *Buffer) Unread() []byte { | |||
| return b.buf[b.idx:] | |||
| } | |||
| // Marshal appends the wire-format encoding of m to the buffer. | |||
| func (b *Buffer) Marshal(m Message) error { | |||
| var err error | |||
| b.buf, err = marshalAppend(b.buf, m, b.deterministic) | |||
| return err | |||
| } | |||
| // Unmarshal parses the wire-format message in the buffer and places the decoded results in m. | |||
| // | |||
| // Unlike proto.Unmarshal, this does not reset the message before starting to unmarshal. | |||
| func (b *Buffer) Unmarshal(m Message) error { | |||
| err := UnmarshalMerge(b.Unread(), m) | |||
| b.idx = len(b.buf) | |||
| return err | |||
| } | |||
| type unknownFields struct{ XXX_unrecognized protoimpl.UnknownFields } | |||
| func (m *unknownFields) String() string { panic("not implemented") } | |||
| func (m *unknownFields) Reset() { panic("not implemented") } | |||
| func (m *unknownFields) ProtoMessage() { panic("not implemented") } | |||
| // DebugPrint dumps the encoded bytes of b with a header and footer including s | |||
| // to stdout. This is only intended for debugging. | |||
| func (*Buffer) DebugPrint(s string, b []byte) { | |||
| m := MessageReflect(new(unknownFields)) | |||
| m.SetUnknown(b) | |||
| b, _ = prototext.MarshalOptions{AllowPartial: true, Indent: "\t"}.Marshal(m.Interface()) | |||
| fmt.Printf("==== %s ====\n%s==== %s ====\n", s, b, s) | |||
| } | |||
| // EncodeVarint appends an unsigned varint encoding to the buffer. | |||
| func (b *Buffer) EncodeVarint(v uint64) error { | |||
| b.buf = protowire.AppendVarint(b.buf, v) | |||
| return nil | |||
| } | |||
| // EncodeZigzag32 appends a 32-bit zig-zag varint encoding to the buffer. | |||
| func (b *Buffer) EncodeZigzag32(v uint64) error { | |||
| return b.EncodeVarint(uint64((uint32(v) << 1) ^ uint32((int32(v) >> 31)))) | |||
| } | |||
| // EncodeZigzag64 appends a 64-bit zig-zag varint encoding to the buffer. | |||
| func (b *Buffer) EncodeZigzag64(v uint64) error { | |||
| return b.EncodeVarint(uint64((uint64(v) << 1) ^ uint64((int64(v) >> 63)))) | |||
| } | |||
| // EncodeFixed32 appends a 32-bit little-endian integer to the buffer. | |||
| func (b *Buffer) EncodeFixed32(v uint64) error { | |||
| b.buf = protowire.AppendFixed32(b.buf, uint32(v)) | |||
| return nil | |||
| } | |||
| // EncodeFixed64 appends a 64-bit little-endian integer to the buffer. | |||
| func (b *Buffer) EncodeFixed64(v uint64) error { | |||
| b.buf = protowire.AppendFixed64(b.buf, uint64(v)) | |||
| return nil | |||
| } | |||
| // EncodeRawBytes appends a length-prefixed raw bytes to the buffer. | |||
| func (b *Buffer) EncodeRawBytes(v []byte) error { | |||
| b.buf = protowire.AppendBytes(b.buf, v) | |||
| return nil | |||
| } | |||
| // EncodeStringBytes appends a length-prefixed raw bytes to the buffer. | |||
| // It does not validate whether v contains valid UTF-8. | |||
| func (b *Buffer) EncodeStringBytes(v string) error { | |||
| b.buf = protowire.AppendString(b.buf, v) | |||
| return nil | |||
| } | |||
| // EncodeMessage appends a length-prefixed encoded message to the buffer. | |||
| func (b *Buffer) EncodeMessage(m Message) error { | |||
| var err error | |||
| b.buf = protowire.AppendVarint(b.buf, uint64(Size(m))) | |||
| b.buf, err = marshalAppend(b.buf, m, b.deterministic) | |||
| return err | |||
| } | |||
| // DecodeVarint consumes an encoded unsigned varint from the buffer. | |||
| func (b *Buffer) DecodeVarint() (uint64, error) { | |||
| v, n := protowire.ConsumeVarint(b.buf[b.idx:]) | |||
| if n < 0 { | |||
| return 0, protowire.ParseError(n) | |||
| } | |||
| b.idx += n | |||
| return uint64(v), nil | |||
| } | |||
| // DecodeZigzag32 consumes an encoded 32-bit zig-zag varint from the buffer. | |||
| func (b *Buffer) DecodeZigzag32() (uint64, error) { | |||
| v, err := b.DecodeVarint() | |||
| if err != nil { | |||
| return 0, err | |||
| } | |||
| return uint64((uint32(v) >> 1) ^ uint32((int32(v&1)<<31)>>31)), nil | |||
| } | |||
| // DecodeZigzag64 consumes an encoded 64-bit zig-zag varint from the buffer. | |||
| func (b *Buffer) DecodeZigzag64() (uint64, error) { | |||
| v, err := b.DecodeVarint() | |||
| if err != nil { | |||
| return 0, err | |||
| } | |||
| return uint64((uint64(v) >> 1) ^ uint64((int64(v&1)<<63)>>63)), nil | |||
| } | |||
| // DecodeFixed32 consumes a 32-bit little-endian integer from the buffer. | |||
| func (b *Buffer) DecodeFixed32() (uint64, error) { | |||
| v, n := protowire.ConsumeFixed32(b.buf[b.idx:]) | |||
| if n < 0 { | |||
| return 0, protowire.ParseError(n) | |||
| } | |||
| b.idx += n | |||
| return uint64(v), nil | |||
| } | |||
| // DecodeFixed64 consumes a 64-bit little-endian integer from the buffer. | |||
| func (b *Buffer) DecodeFixed64() (uint64, error) { | |||
| v, n := protowire.ConsumeFixed64(b.buf[b.idx:]) | |||
| if n < 0 { | |||
| return 0, protowire.ParseError(n) | |||
| } | |||
| b.idx += n | |||
| return uint64(v), nil | |||
| } | |||
| // DecodeRawBytes consumes a length-prefixed raw bytes from the buffer. | |||
| // If alloc is specified, it returns a copy the raw bytes | |||
| // rather than a sub-slice of the buffer. | |||
| func (b *Buffer) DecodeRawBytes(alloc bool) ([]byte, error) { | |||
| v, n := protowire.ConsumeBytes(b.buf[b.idx:]) | |||
| if n < 0 { | |||
| return nil, protowire.ParseError(n) | |||
| } | |||
| b.idx += n | |||
| if alloc { | |||
| v = append([]byte(nil), v...) | |||
| } | |||
| return v, nil | |||
| } | |||
| // DecodeStringBytes consumes a length-prefixed raw bytes from the buffer. | |||
| // It does not validate whether the raw bytes contain valid UTF-8. | |||
| func (b *Buffer) DecodeStringBytes() (string, error) { | |||
| v, n := protowire.ConsumeString(b.buf[b.idx:]) | |||
| if n < 0 { | |||
| return "", protowire.ParseError(n) | |||
| } | |||
| b.idx += n | |||
| return v, nil | |||
| } | |||
| // DecodeMessage consumes a length-prefixed message from the buffer. | |||
| // It does not reset m. | |||
| func (b *Buffer) DecodeMessage(m Message) error { | |||
| v, err := b.DecodeRawBytes(false) | |||
| if err != nil { | |||
| return err | |||
| } | |||
| return UnmarshalMerge(v, m) | |||
| } | |||
| // DecodeGroup consumes a message group from the buffer. | |||
| // It assumes that the start group marker has already been consumed and | |||
| // consumes all bytes until (and including the end group marker). | |||
| // It does not reset m. | |||
| func (b *Buffer) DecodeGroup(m Message) error { | |||
| v, n, err := consumeGroup(b.buf[b.idx:]) | |||
| if err != nil { | |||
| return err | |||
| } | |||
| b.idx += n | |||
| return UnmarshalMerge(v, m) | |||
| } | |||
| // consumeGroup parses b until it finds an end group marker, returning | |||
| // the raw bytes of the message (excluding the end group marker) and the | |||
| // the total length of the message (including the end group marker). | |||
| func consumeGroup(b []byte) ([]byte, int, error) { | |||
| b0 := b | |||
| depth := 1 // assume this follows a start group marker | |||
| for { | |||
| _, wtyp, tagLen := protowire.ConsumeTag(b) | |||
| if tagLen < 0 { | |||
| return nil, 0, protowire.ParseError(tagLen) | |||
| } | |||
| b = b[tagLen:] | |||
| var valLen int | |||
| switch wtyp { | |||
| case protowire.VarintType: | |||
| _, valLen = protowire.ConsumeVarint(b) | |||
| case protowire.Fixed32Type: | |||
| _, valLen = protowire.ConsumeFixed32(b) | |||
| case protowire.Fixed64Type: | |||
| _, valLen = protowire.ConsumeFixed64(b) | |||
| case protowire.BytesType: | |||
| _, valLen = protowire.ConsumeBytes(b) | |||
| case protowire.StartGroupType: | |||
| depth++ | |||
| case protowire.EndGroupType: | |||
| depth-- | |||
| default: | |||
| return nil, 0, errors.New("proto: cannot parse reserved wire type") | |||
| } | |||
| if valLen < 0 { | |||
| return nil, 0, protowire.ParseError(valLen) | |||
| } | |||
| b = b[valLen:] | |||
| if depth == 0 { | |||
| return b0[:len(b0)-len(b)-tagLen], len(b0) - len(b), nil | |||
| } | |||
| } | |||
| } | |||
| @@ -1,253 +0,0 @@ | |||
| // Go support for Protocol Buffers - Google's data interchange format | |||
| // | |||
| // Copyright 2011 The Go Authors. All rights reserved. | |||
| // https://github.com/golang/protobuf | |||
| // | |||
| // Redistribution and use in source and binary forms, with or without | |||
| // modification, are permitted provided that the following conditions are | |||
| // met: | |||
| // | |||
| // * Redistributions of source code must retain the above copyright | |||
| // notice, this list of conditions and the following disclaimer. | |||
| // * Redistributions in binary form must reproduce the above | |||
| // copyright notice, this list of conditions and the following disclaimer | |||
| // in the documentation and/or other materials provided with the | |||
| // distribution. | |||
| // * Neither the name of Google Inc. nor the names of its | |||
| // contributors may be used to endorse or promote products derived from | |||
| // this software without specific prior written permission. | |||
| // | |||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| // Protocol buffer deep copy and merge. | |||
| // TODO: RawMessage. | |||
| package proto | |||
| import ( | |||
| "fmt" | |||
| "log" | |||
| "reflect" | |||
| "strings" | |||
| ) | |||
| // Clone returns a deep copy of a protocol buffer. | |||
| func Clone(src Message) Message { | |||
| in := reflect.ValueOf(src) | |||
| if in.IsNil() { | |||
| return src | |||
| } | |||
| out := reflect.New(in.Type().Elem()) | |||
| dst := out.Interface().(Message) | |||
| Merge(dst, src) | |||
| return dst | |||
| } | |||
| // Merger is the interface representing objects that can merge messages of the same type. | |||
| type Merger interface { | |||
| // Merge merges src into this message. | |||
| // Required and optional fields that are set in src will be set to that value in dst. | |||
| // Elements of repeated fields will be appended. | |||
| // | |||
| // Merge may panic if called with a different argument type than the receiver. | |||
| Merge(src Message) | |||
| } | |||
| // generatedMerger is the custom merge method that generated protos will have. | |||
| // We must add this method since a generate Merge method will conflict with | |||
| // many existing protos that have a Merge data field already defined. | |||
| type generatedMerger interface { | |||
| XXX_Merge(src Message) | |||
| } | |||
| // Merge merges src into dst. | |||
| // Required and optional fields that are set in src will be set to that value in dst. | |||
| // Elements of repeated fields will be appended. | |||
| // Merge panics if src and dst are not the same type, or if dst is nil. | |||
| func Merge(dst, src Message) { | |||
| if m, ok := dst.(Merger); ok { | |||
| m.Merge(src) | |||
| return | |||
| } | |||
| in := reflect.ValueOf(src) | |||
| out := reflect.ValueOf(dst) | |||
| if out.IsNil() { | |||
| panic("proto: nil destination") | |||
| } | |||
| if in.Type() != out.Type() { | |||
| panic(fmt.Sprintf("proto.Merge(%T, %T) type mismatch", dst, src)) | |||
| } | |||
| if in.IsNil() { | |||
| return // Merge from nil src is a noop | |||
| } | |||
| if m, ok := dst.(generatedMerger); ok { | |||
| m.XXX_Merge(src) | |||
| return | |||
| } | |||
| mergeStruct(out.Elem(), in.Elem()) | |||
| } | |||
| func mergeStruct(out, in reflect.Value) { | |||
| sprop := GetProperties(in.Type()) | |||
| for i := 0; i < in.NumField(); i++ { | |||
| f := in.Type().Field(i) | |||
| if strings.HasPrefix(f.Name, "XXX_") { | |||
| continue | |||
| } | |||
| mergeAny(out.Field(i), in.Field(i), false, sprop.Prop[i]) | |||
| } | |||
| if emIn, err := extendable(in.Addr().Interface()); err == nil { | |||
| emOut, _ := extendable(out.Addr().Interface()) | |||
| mIn, muIn := emIn.extensionsRead() | |||
| if mIn != nil { | |||
| mOut := emOut.extensionsWrite() | |||
| muIn.Lock() | |||
| mergeExtension(mOut, mIn) | |||
| muIn.Unlock() | |||
| } | |||
| } | |||
| uf := in.FieldByName("XXX_unrecognized") | |||
| if !uf.IsValid() { | |||
| return | |||
| } | |||
| uin := uf.Bytes() | |||
| if len(uin) > 0 { | |||
| out.FieldByName("XXX_unrecognized").SetBytes(append([]byte(nil), uin...)) | |||
| } | |||
| } | |||
| // mergeAny performs a merge between two values of the same type. | |||
| // viaPtr indicates whether the values were indirected through a pointer (implying proto2). | |||
| // prop is set if this is a struct field (it may be nil). | |||
| func mergeAny(out, in reflect.Value, viaPtr bool, prop *Properties) { | |||
| if in.Type() == protoMessageType { | |||
| if !in.IsNil() { | |||
| if out.IsNil() { | |||
| out.Set(reflect.ValueOf(Clone(in.Interface().(Message)))) | |||
| } else { | |||
| Merge(out.Interface().(Message), in.Interface().(Message)) | |||
| } | |||
| } | |||
| return | |||
| } | |||
| switch in.Kind() { | |||
| case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int32, reflect.Int64, | |||
| reflect.String, reflect.Uint32, reflect.Uint64: | |||
| if !viaPtr && isProto3Zero(in) { | |||
| return | |||
| } | |||
| out.Set(in) | |||
| case reflect.Interface: | |||
| // Probably a oneof field; copy non-nil values. | |||
| if in.IsNil() { | |||
| return | |||
| } | |||
| // Allocate destination if it is not set, or set to a different type. | |||
| // Otherwise we will merge as normal. | |||
| if out.IsNil() || out.Elem().Type() != in.Elem().Type() { | |||
| out.Set(reflect.New(in.Elem().Elem().Type())) // interface -> *T -> T -> new(T) | |||
| } | |||
| mergeAny(out.Elem(), in.Elem(), false, nil) | |||
| case reflect.Map: | |||
| if in.Len() == 0 { | |||
| return | |||
| } | |||
| if out.IsNil() { | |||
| out.Set(reflect.MakeMap(in.Type())) | |||
| } | |||
| // For maps with value types of *T or []byte we need to deep copy each value. | |||
| elemKind := in.Type().Elem().Kind() | |||
| for _, key := range in.MapKeys() { | |||
| var val reflect.Value | |||
| switch elemKind { | |||
| case reflect.Ptr: | |||
| val = reflect.New(in.Type().Elem().Elem()) | |||
| mergeAny(val, in.MapIndex(key), false, nil) | |||
| case reflect.Slice: | |||
| val = in.MapIndex(key) | |||
| val = reflect.ValueOf(append([]byte{}, val.Bytes()...)) | |||
| default: | |||
| val = in.MapIndex(key) | |||
| } | |||
| out.SetMapIndex(key, val) | |||
| } | |||
| case reflect.Ptr: | |||
| if in.IsNil() { | |||
| return | |||
| } | |||
| if out.IsNil() { | |||
| out.Set(reflect.New(in.Elem().Type())) | |||
| } | |||
| mergeAny(out.Elem(), in.Elem(), true, nil) | |||
| case reflect.Slice: | |||
| if in.IsNil() { | |||
| return | |||
| } | |||
| if in.Type().Elem().Kind() == reflect.Uint8 { | |||
| // []byte is a scalar bytes field, not a repeated field. | |||
| // Edge case: if this is in a proto3 message, a zero length | |||
| // bytes field is considered the zero value, and should not | |||
| // be merged. | |||
| if prop != nil && prop.proto3 && in.Len() == 0 { | |||
| return | |||
| } | |||
| // Make a deep copy. | |||
| // Append to []byte{} instead of []byte(nil) so that we never end up | |||
| // with a nil result. | |||
| out.SetBytes(append([]byte{}, in.Bytes()...)) | |||
| return | |||
| } | |||
| n := in.Len() | |||
| if out.IsNil() { | |||
| out.Set(reflect.MakeSlice(in.Type(), 0, n)) | |||
| } | |||
| switch in.Type().Elem().Kind() { | |||
| case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int32, reflect.Int64, | |||
| reflect.String, reflect.Uint32, reflect.Uint64: | |||
| out.Set(reflect.AppendSlice(out, in)) | |||
| default: | |||
| for i := 0; i < n; i++ { | |||
| x := reflect.Indirect(reflect.New(in.Type().Elem())) | |||
| mergeAny(x, in.Index(i), false, nil) | |||
| out.Set(reflect.Append(out, x)) | |||
| } | |||
| } | |||
| case reflect.Struct: | |||
| mergeStruct(out, in) | |||
| default: | |||
| // unknown type, so not a protocol buffer | |||
| log.Printf("proto: don't know how to copy %v", in) | |||
| } | |||
| } | |||
| func mergeExtension(out, in map[int32]Extension) { | |||
| for extNum, eIn := range in { | |||
| eOut := Extension{desc: eIn.desc} | |||
| if eIn.value != nil { | |||
| v := reflect.New(reflect.TypeOf(eIn.value)).Elem() | |||
| mergeAny(v, reflect.ValueOf(eIn.value), false, nil) | |||
| eOut.value = v.Interface() | |||
| } | |||
| if eIn.enc != nil { | |||
| eOut.enc = make([]byte, len(eIn.enc)) | |||
| copy(eOut.enc, eIn.enc) | |||
| } | |||
| out[extNum] = eOut | |||
| } | |||
| } | |||
| @@ -1,427 +0,0 @@ | |||
| // Go support for Protocol Buffers - Google's data interchange format | |||
| // | |||
| // Copyright 2010 The Go Authors. All rights reserved. | |||
| // https://github.com/golang/protobuf | |||
| // | |||
| // Redistribution and use in source and binary forms, with or without | |||
| // modification, are permitted provided that the following conditions are | |||
| // met: | |||
| // | |||
| // * Redistributions of source code must retain the above copyright | |||
| // notice, this list of conditions and the following disclaimer. | |||
| // * Redistributions in binary form must reproduce the above | |||
| // copyright notice, this list of conditions and the following disclaimer | |||
| // in the documentation and/or other materials provided with the | |||
| // distribution. | |||
| // * Neither the name of Google Inc. nor the names of its | |||
| // contributors may be used to endorse or promote products derived from | |||
| // this software without specific prior written permission. | |||
| // | |||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| package proto | |||
| /* | |||
| * Routines for decoding protocol buffer data to construct in-memory representations. | |||
| */ | |||
| import ( | |||
| "errors" | |||
| "fmt" | |||
| "io" | |||
| ) | |||
| // errOverflow is returned when an integer is too large to be represented. | |||
| var errOverflow = errors.New("proto: integer overflow") | |||
| // ErrInternalBadWireType is returned by generated code when an incorrect | |||
| // wire type is encountered. It does not get returned to user code. | |||
| var ErrInternalBadWireType = errors.New("proto: internal error: bad wiretype for oneof") | |||
| // DecodeVarint reads a varint-encoded integer from the slice. | |||
| // It returns the integer and the number of bytes consumed, or | |||
| // zero if there is not enough. | |||
| // This is the format for the | |||
| // int32, int64, uint32, uint64, bool, and enum | |||
| // protocol buffer types. | |||
| func DecodeVarint(buf []byte) (x uint64, n int) { | |||
| for shift := uint(0); shift < 64; shift += 7 { | |||
| if n >= len(buf) { | |||
| return 0, 0 | |||
| } | |||
| b := uint64(buf[n]) | |||
| n++ | |||
| x |= (b & 0x7F) << shift | |||
| if (b & 0x80) == 0 { | |||
| return x, n | |||
| } | |||
| } | |||
| // The number is too large to represent in a 64-bit value. | |||
| return 0, 0 | |||
| } | |||
| func (p *Buffer) decodeVarintSlow() (x uint64, err error) { | |||
| i := p.index | |||
| l := len(p.buf) | |||
| for shift := uint(0); shift < 64; shift += 7 { | |||
| if i >= l { | |||
| err = io.ErrUnexpectedEOF | |||
| return | |||
| } | |||
| b := p.buf[i] | |||
| i++ | |||
| x |= (uint64(b) & 0x7F) << shift | |||
| if b < 0x80 { | |||
| p.index = i | |||
| return | |||
| } | |||
| } | |||
| // The number is too large to represent in a 64-bit value. | |||
| err = errOverflow | |||
| return | |||
| } | |||
| // DecodeVarint reads a varint-encoded integer from the Buffer. | |||
| // This is the format for the | |||
| // int32, int64, uint32, uint64, bool, and enum | |||
| // protocol buffer types. | |||
| func (p *Buffer) DecodeVarint() (x uint64, err error) { | |||
| i := p.index | |||
| buf := p.buf | |||
| if i >= len(buf) { | |||
| return 0, io.ErrUnexpectedEOF | |||
| } else if buf[i] < 0x80 { | |||
| p.index++ | |||
| return uint64(buf[i]), nil | |||
| } else if len(buf)-i < 10 { | |||
| return p.decodeVarintSlow() | |||
| } | |||
| var b uint64 | |||
| // we already checked the first byte | |||
| x = uint64(buf[i]) - 0x80 | |||
| i++ | |||
| b = uint64(buf[i]) | |||
| i++ | |||
| x += b << 7 | |||
| if b&0x80 == 0 { | |||
| goto done | |||
| } | |||
| x -= 0x80 << 7 | |||
| b = uint64(buf[i]) | |||
| i++ | |||
| x += b << 14 | |||
| if b&0x80 == 0 { | |||
| goto done | |||
| } | |||
| x -= 0x80 << 14 | |||
| b = uint64(buf[i]) | |||
| i++ | |||
| x += b << 21 | |||
| if b&0x80 == 0 { | |||
| goto done | |||
| } | |||
| x -= 0x80 << 21 | |||
| b = uint64(buf[i]) | |||
| i++ | |||
| x += b << 28 | |||
| if b&0x80 == 0 { | |||
| goto done | |||
| } | |||
| x -= 0x80 << 28 | |||
| b = uint64(buf[i]) | |||
| i++ | |||
| x += b << 35 | |||
| if b&0x80 == 0 { | |||
| goto done | |||
| } | |||
| x -= 0x80 << 35 | |||
| b = uint64(buf[i]) | |||
| i++ | |||
| x += b << 42 | |||
| if b&0x80 == 0 { | |||
| goto done | |||
| } | |||
| x -= 0x80 << 42 | |||
| b = uint64(buf[i]) | |||
| i++ | |||
| x += b << 49 | |||
| if b&0x80 == 0 { | |||
| goto done | |||
| } | |||
| x -= 0x80 << 49 | |||
| b = uint64(buf[i]) | |||
| i++ | |||
| x += b << 56 | |||
| if b&0x80 == 0 { | |||
| goto done | |||
| } | |||
| x -= 0x80 << 56 | |||
| b = uint64(buf[i]) | |||
| i++ | |||
| x += b << 63 | |||
| if b&0x80 == 0 { | |||
| goto done | |||
| } | |||
| return 0, errOverflow | |||
| done: | |||
| p.index = i | |||
| return x, nil | |||
| } | |||
| // DecodeFixed64 reads a 64-bit integer from the Buffer. | |||
| // This is the format for the | |||
| // fixed64, sfixed64, and double protocol buffer types. | |||
| func (p *Buffer) DecodeFixed64() (x uint64, err error) { | |||
| // x, err already 0 | |||
| i := p.index + 8 | |||
| if i < 0 || i > len(p.buf) { | |||
| err = io.ErrUnexpectedEOF | |||
| return | |||
| } | |||
| p.index = i | |||
| x = uint64(p.buf[i-8]) | |||
| x |= uint64(p.buf[i-7]) << 8 | |||
| x |= uint64(p.buf[i-6]) << 16 | |||
| x |= uint64(p.buf[i-5]) << 24 | |||
| x |= uint64(p.buf[i-4]) << 32 | |||
| x |= uint64(p.buf[i-3]) << 40 | |||
| x |= uint64(p.buf[i-2]) << 48 | |||
| x |= uint64(p.buf[i-1]) << 56 | |||
| return | |||
| } | |||
| // DecodeFixed32 reads a 32-bit integer from the Buffer. | |||
| // This is the format for the | |||
| // fixed32, sfixed32, and float protocol buffer types. | |||
| func (p *Buffer) DecodeFixed32() (x uint64, err error) { | |||
| // x, err already 0 | |||
| i := p.index + 4 | |||
| if i < 0 || i > len(p.buf) { | |||
| err = io.ErrUnexpectedEOF | |||
| return | |||
| } | |||
| p.index = i | |||
| x = uint64(p.buf[i-4]) | |||
| x |= uint64(p.buf[i-3]) << 8 | |||
| x |= uint64(p.buf[i-2]) << 16 | |||
| x |= uint64(p.buf[i-1]) << 24 | |||
| return | |||
| } | |||
| // DecodeZigzag64 reads a zigzag-encoded 64-bit integer | |||
| // from the Buffer. | |||
| // This is the format used for the sint64 protocol buffer type. | |||
| func (p *Buffer) DecodeZigzag64() (x uint64, err error) { | |||
| x, err = p.DecodeVarint() | |||
| if err != nil { | |||
| return | |||
| } | |||
| x = (x >> 1) ^ uint64((int64(x&1)<<63)>>63) | |||
| return | |||
| } | |||
| // DecodeZigzag32 reads a zigzag-encoded 32-bit integer | |||
| // from the Buffer. | |||
| // This is the format used for the sint32 protocol buffer type. | |||
| func (p *Buffer) DecodeZigzag32() (x uint64, err error) { | |||
| x, err = p.DecodeVarint() | |||
| if err != nil { | |||
| return | |||
| } | |||
| x = uint64((uint32(x) >> 1) ^ uint32((int32(x&1)<<31)>>31)) | |||
| return | |||
| } | |||
| // DecodeRawBytes reads a count-delimited byte buffer from the Buffer. | |||
| // This is the format used for the bytes protocol buffer | |||
| // type and for embedded messages. | |||
| func (p *Buffer) DecodeRawBytes(alloc bool) (buf []byte, err error) { | |||
| n, err := p.DecodeVarint() | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| nb := int(n) | |||
| if nb < 0 { | |||
| return nil, fmt.Errorf("proto: bad byte length %d", nb) | |||
| } | |||
| end := p.index + nb | |||
| if end < p.index || end > len(p.buf) { | |||
| return nil, io.ErrUnexpectedEOF | |||
| } | |||
| if !alloc { | |||
| // todo: check if can get more uses of alloc=false | |||
| buf = p.buf[p.index:end] | |||
| p.index += nb | |||
| return | |||
| } | |||
| buf = make([]byte, nb) | |||
| copy(buf, p.buf[p.index:]) | |||
| p.index += nb | |||
| return | |||
| } | |||
| // DecodeStringBytes reads an encoded string from the Buffer. | |||
| // This is the format used for the proto2 string type. | |||
| func (p *Buffer) DecodeStringBytes() (s string, err error) { | |||
| buf, err := p.DecodeRawBytes(false) | |||
| if err != nil { | |||
| return | |||
| } | |||
| return string(buf), nil | |||
| } | |||
| // Unmarshaler is the interface representing objects that can | |||
| // unmarshal themselves. The argument points to data that may be | |||
| // overwritten, so implementations should not keep references to the | |||
| // buffer. | |||
| // Unmarshal implementations should not clear the receiver. | |||
| // Any unmarshaled data should be merged into the receiver. | |||
| // Callers of Unmarshal that do not want to retain existing data | |||
| // should Reset the receiver before calling Unmarshal. | |||
| type Unmarshaler interface { | |||
| Unmarshal([]byte) error | |||
| } | |||
| // newUnmarshaler is the interface representing objects that can | |||
| // unmarshal themselves. The semantics are identical to Unmarshaler. | |||
| // | |||
| // This exists to support protoc-gen-go generated messages. | |||
| // The proto package will stop type-asserting to this interface in the future. | |||
| // | |||
| // DO NOT DEPEND ON THIS. | |||
| type newUnmarshaler interface { | |||
| XXX_Unmarshal([]byte) error | |||
| } | |||
| // Unmarshal parses the protocol buffer representation in buf and places the | |||
| // decoded result in pb. If the struct underlying pb does not match | |||
| // the data in buf, the results can be unpredictable. | |||
| // | |||
| // Unmarshal resets pb before starting to unmarshal, so any | |||
| // existing data in pb is always removed. Use UnmarshalMerge | |||
| // to preserve and append to existing data. | |||
| func Unmarshal(buf []byte, pb Message) error { | |||
| pb.Reset() | |||
| if u, ok := pb.(newUnmarshaler); ok { | |||
| return u.XXX_Unmarshal(buf) | |||
| } | |||
| if u, ok := pb.(Unmarshaler); ok { | |||
| return u.Unmarshal(buf) | |||
| } | |||
| return NewBuffer(buf).Unmarshal(pb) | |||
| } | |||
| // UnmarshalMerge parses the protocol buffer representation in buf and | |||
| // writes the decoded result to pb. If the struct underlying pb does not match | |||
| // the data in buf, the results can be unpredictable. | |||
| // | |||
| // UnmarshalMerge merges into existing data in pb. | |||
| // Most code should use Unmarshal instead. | |||
| func UnmarshalMerge(buf []byte, pb Message) error { | |||
| if u, ok := pb.(newUnmarshaler); ok { | |||
| return u.XXX_Unmarshal(buf) | |||
| } | |||
| if u, ok := pb.(Unmarshaler); ok { | |||
| // NOTE: The history of proto have unfortunately been inconsistent | |||
| // whether Unmarshaler should or should not implicitly clear itself. | |||
| // Some implementations do, most do not. | |||
| // Thus, calling this here may or may not do what people want. | |||
| // | |||
| // See https://github.com/golang/protobuf/issues/424 | |||
| return u.Unmarshal(buf) | |||
| } | |||
| return NewBuffer(buf).Unmarshal(pb) | |||
| } | |||
| // DecodeMessage reads a count-delimited message from the Buffer. | |||
| func (p *Buffer) DecodeMessage(pb Message) error { | |||
| enc, err := p.DecodeRawBytes(false) | |||
| if err != nil { | |||
| return err | |||
| } | |||
| return NewBuffer(enc).Unmarshal(pb) | |||
| } | |||
| // DecodeGroup reads a tag-delimited group from the Buffer. | |||
| // StartGroup tag is already consumed. This function consumes | |||
| // EndGroup tag. | |||
| func (p *Buffer) DecodeGroup(pb Message) error { | |||
| b := p.buf[p.index:] | |||
| x, y := findEndGroup(b) | |||
| if x < 0 { | |||
| return io.ErrUnexpectedEOF | |||
| } | |||
| err := Unmarshal(b[:x], pb) | |||
| p.index += y | |||
| return err | |||
| } | |||
| // Unmarshal parses the protocol buffer representation in the | |||
| // Buffer and places the decoded result in pb. If the struct | |||
| // underlying pb does not match the data in the buffer, the results can be | |||
| // unpredictable. | |||
| // | |||
| // Unlike proto.Unmarshal, this does not reset pb before starting to unmarshal. | |||
| func (p *Buffer) Unmarshal(pb Message) error { | |||
| // If the object can unmarshal itself, let it. | |||
| if u, ok := pb.(newUnmarshaler); ok { | |||
| err := u.XXX_Unmarshal(p.buf[p.index:]) | |||
| p.index = len(p.buf) | |||
| return err | |||
| } | |||
| if u, ok := pb.(Unmarshaler); ok { | |||
| // NOTE: The history of proto have unfortunately been inconsistent | |||
| // whether Unmarshaler should or should not implicitly clear itself. | |||
| // Some implementations do, most do not. | |||
| // Thus, calling this here may or may not do what people want. | |||
| // | |||
| // See https://github.com/golang/protobuf/issues/424 | |||
| err := u.Unmarshal(p.buf[p.index:]) | |||
| p.index = len(p.buf) | |||
| return err | |||
| } | |||
| // Slow workaround for messages that aren't Unmarshalers. | |||
| // This includes some hand-coded .pb.go files and | |||
| // bootstrap protos. | |||
| // TODO: fix all of those and then add Unmarshal to | |||
| // the Message interface. Then: | |||
| // The cast above and code below can be deleted. | |||
| // The old unmarshaler can be deleted. | |||
| // Clients can call Unmarshal directly (can already do that, actually). | |||
| var info InternalMessageInfo | |||
| err := info.Unmarshal(pb, p.buf[p.index:]) | |||
| p.index = len(p.buf) | |||
| return err | |||
| } | |||
| @@ -0,0 +1,63 @@ | |||
| // Copyright 2019 The Go Authors. All rights reserved. | |||
| // Use of this source code is governed by a BSD-style | |||
| // license that can be found in the LICENSE file. | |||
| package proto | |||
| import ( | |||
| "google.golang.org/protobuf/reflect/protoreflect" | |||
| ) | |||
| // SetDefaults sets unpopulated scalar fields to their default values. | |||
| // Fields within a oneof are not set even if they have a default value. | |||
| // SetDefaults is recursively called upon any populated message fields. | |||
| func SetDefaults(m Message) { | |||
| if m != nil { | |||
| setDefaults(MessageReflect(m)) | |||
| } | |||
| } | |||
| func setDefaults(m protoreflect.Message) { | |||
| fds := m.Descriptor().Fields() | |||
| for i := 0; i < fds.Len(); i++ { | |||
| fd := fds.Get(i) | |||
| if !m.Has(fd) { | |||
| if fd.HasDefault() && fd.ContainingOneof() == nil { | |||
| v := fd.Default() | |||
| if fd.Kind() == protoreflect.BytesKind { | |||
| v = protoreflect.ValueOf(append([]byte(nil), v.Bytes()...)) // copy the default bytes | |||
| } | |||
| m.Set(fd, v) | |||
| } | |||
| continue | |||
| } | |||
| } | |||
| m.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool { | |||
| switch { | |||
| // Handle singular message. | |||
| case fd.Cardinality() != protoreflect.Repeated: | |||
| if fd.Message() != nil { | |||
| setDefaults(m.Get(fd).Message()) | |||
| } | |||
| // Handle list of messages. | |||
| case fd.IsList(): | |||
| if fd.Message() != nil { | |||
| ls := m.Get(fd).List() | |||
| for i := 0; i < ls.Len(); i++ { | |||
| setDefaults(ls.Get(i).Message()) | |||
| } | |||
| } | |||
| // Handle map of messages. | |||
| case fd.IsMap(): | |||
| if fd.MapValue().Message() != nil { | |||
| ms := m.Get(fd).Map() | |||
| ms.Range(func(_ protoreflect.MapKey, v protoreflect.Value) bool { | |||
| setDefaults(v.Message()) | |||
| return true | |||
| }) | |||
| } | |||
| } | |||
| return true | |||
| }) | |||
| } | |||
| @@ -1,63 +1,92 @@ | |||
| // Go support for Protocol Buffers - Google's data interchange format | |||
| // | |||
| // Copyright 2018 The Go Authors. All rights reserved. | |||
| // https://github.com/golang/protobuf | |||
| // | |||
| // Redistribution and use in source and binary forms, with or without | |||
| // modification, are permitted provided that the following conditions are | |||
| // met: | |||
| // | |||
| // * Redistributions of source code must retain the above copyright | |||
| // notice, this list of conditions and the following disclaimer. | |||
| // * Redistributions in binary form must reproduce the above | |||
| // copyright notice, this list of conditions and the following disclaimer | |||
| // in the documentation and/or other materials provided with the | |||
| // distribution. | |||
| // * Neither the name of Google Inc. nor the names of its | |||
| // contributors may be used to endorse or promote products derived from | |||
| // this software without specific prior written permission. | |||
| // | |||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| // Copyright 2018 The Go Authors. All rights reserved. | |||
| // Use of this source code is governed by a BSD-style | |||
| // license that can be found in the LICENSE file. | |||
| package proto | |||
| import "errors" | |||
| import ( | |||
| "encoding/json" | |||
| "errors" | |||
| "fmt" | |||
| "strconv" | |||
| ) | |||
| // Deprecated: do not use. | |||
| var ( | |||
| // Deprecated: No longer returned. | |||
| ErrNil = errors.New("proto: Marshal called with nil") | |||
| // Deprecated: No longer returned. | |||
| ErrTooLarge = errors.New("proto: message encodes to over 2 GB") | |||
| // Deprecated: No longer returned. | |||
| ErrInternalBadWireType = errors.New("proto: internal error: bad wiretype for oneof") | |||
| ) | |||
| // Deprecated: Do not use. | |||
| type Stats struct{ Emalloc, Dmalloc, Encode, Decode, Chit, Cmiss, Size uint64 } | |||
| // Deprecated: do not use. | |||
| // Deprecated: Do not use. | |||
| func GetStats() Stats { return Stats{} } | |||
| // Deprecated: do not use. | |||
| // Deprecated: Do not use. | |||
| func MarshalMessageSet(interface{}) ([]byte, error) { | |||
| return nil, errors.New("proto: not implemented") | |||
| } | |||
| // Deprecated: do not use. | |||
| // Deprecated: Do not use. | |||
| func UnmarshalMessageSet([]byte, interface{}) error { | |||
| return errors.New("proto: not implemented") | |||
| } | |||
| // Deprecated: do not use. | |||
| // Deprecated: Do not use. | |||
| func MarshalMessageSetJSON(interface{}) ([]byte, error) { | |||
| return nil, errors.New("proto: not implemented") | |||
| } | |||
| // Deprecated: do not use. | |||
| // Deprecated: Do not use. | |||
| func UnmarshalMessageSetJSON([]byte, interface{}) error { | |||
| return errors.New("proto: not implemented") | |||
| } | |||
| // Deprecated: do not use. | |||
| // Deprecated: Do not use. | |||
| func RegisterMessageSetType(Message, int32, string) {} | |||
| // Deprecated: Do not use. | |||
| func EnumName(m map[int32]string, v int32) string { | |||
| s, ok := m[v] | |||
| if ok { | |||
| return s | |||
| } | |||
| return strconv.Itoa(int(v)) | |||
| } | |||
| // Deprecated: Do not use. | |||
| func UnmarshalJSONEnum(m map[string]int32, data []byte, enumName string) (int32, error) { | |||
| if data[0] == '"' { | |||
| // New style: enums are strings. | |||
| var repr string | |||
| if err := json.Unmarshal(data, &repr); err != nil { | |||
| return -1, err | |||
| } | |||
| val, ok := m[repr] | |||
| if !ok { | |||
| return 0, fmt.Errorf("unrecognized enum %s value %q", enumName, repr) | |||
| } | |||
| return val, nil | |||
| } | |||
| // Old style: enums are ints. | |||
| var val int32 | |||
| if err := json.Unmarshal(data, &val); err != nil { | |||
| return 0, fmt.Errorf("cannot unmarshal %#q into enum %s", data, enumName) | |||
| } | |||
| return val, nil | |||
| } | |||
| // Deprecated: Do not use. | |||
| type InternalMessageInfo struct{} | |||
| func (*InternalMessageInfo) DiscardUnknown(Message) { panic("not implemented") } | |||
| func (*InternalMessageInfo) Marshal([]byte, Message, bool) ([]byte, error) { panic("not implemented") } | |||
| func (*InternalMessageInfo) Merge(Message, Message) { panic("not implemented") } | |||
| func (*InternalMessageInfo) Size(Message) int { panic("not implemented") } | |||
| func (*InternalMessageInfo) Unmarshal(Message, []byte) error { panic("not implemented") } | |||
| @@ -1,48 +1,13 @@ | |||
| // Go support for Protocol Buffers - Google's data interchange format | |||
| // | |||
| // Copyright 2017 The Go Authors. All rights reserved. | |||
| // https://github.com/golang/protobuf | |||
| // | |||
| // Redistribution and use in source and binary forms, with or without | |||
| // modification, are permitted provided that the following conditions are | |||
| // met: | |||
| // | |||
| // * Redistributions of source code must retain the above copyright | |||
| // notice, this list of conditions and the following disclaimer. | |||
| // * Redistributions in binary form must reproduce the above | |||
| // copyright notice, this list of conditions and the following disclaimer | |||
| // in the documentation and/or other materials provided with the | |||
| // distribution. | |||
| // * Neither the name of Google Inc. nor the names of its | |||
| // contributors may be used to endorse or promote products derived from | |||
| // this software without specific prior written permission. | |||
| // | |||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| // Copyright 2019 The Go Authors. All rights reserved. | |||
| // Use of this source code is governed by a BSD-style | |||
| // license that can be found in the LICENSE file. | |||
| package proto | |||
| import ( | |||
| "fmt" | |||
| "reflect" | |||
| "strings" | |||
| "sync" | |||
| "sync/atomic" | |||
| "google.golang.org/protobuf/reflect/protoreflect" | |||
| ) | |||
| type generatedDiscarder interface { | |||
| XXX_DiscardUnknown() | |||
| } | |||
| // DiscardUnknown recursively discards all unknown fields from this message | |||
| // and all embedded messages. | |||
| // | |||
| @@ -51,300 +16,43 @@ type generatedDiscarder interface { | |||
| // marshal to be able to produce a message that continues to have those | |||
| // unrecognized fields. To avoid this, DiscardUnknown is used to | |||
| // explicitly clear the unknown fields after unmarshaling. | |||
| // | |||
| // For proto2 messages, the unknown fields of message extensions are only | |||
| // discarded from messages that have been accessed via GetExtension. | |||
| func DiscardUnknown(m Message) { | |||
| if m, ok := m.(generatedDiscarder); ok { | |||
| m.XXX_DiscardUnknown() | |||
| return | |||
| } | |||
| // TODO: Dynamically populate a InternalMessageInfo for legacy messages, | |||
| // but the master branch has no implementation for InternalMessageInfo, | |||
| // so it would be more work to replicate that approach. | |||
| discardLegacy(m) | |||
| } | |||
| // DiscardUnknown recursively discards all unknown fields. | |||
| func (a *InternalMessageInfo) DiscardUnknown(m Message) { | |||
| di := atomicLoadDiscardInfo(&a.discard) | |||
| if di == nil { | |||
| di = getDiscardInfo(reflect.TypeOf(m).Elem()) | |||
| atomicStoreDiscardInfo(&a.discard, di) | |||
| } | |||
| di.discard(toPointer(&m)) | |||
| } | |||
| type discardInfo struct { | |||
| typ reflect.Type | |||
| initialized int32 // 0: only typ is valid, 1: everything is valid | |||
| lock sync.Mutex | |||
| fields []discardFieldInfo | |||
| unrecognized field | |||
| } | |||
| type discardFieldInfo struct { | |||
| field field // Offset of field, guaranteed to be valid | |||
| discard func(src pointer) | |||
| } | |||
| var ( | |||
| discardInfoMap = map[reflect.Type]*discardInfo{} | |||
| discardInfoLock sync.Mutex | |||
| ) | |||
| func getDiscardInfo(t reflect.Type) *discardInfo { | |||
| discardInfoLock.Lock() | |||
| defer discardInfoLock.Unlock() | |||
| di := discardInfoMap[t] | |||
| if di == nil { | |||
| di = &discardInfo{typ: t} | |||
| discardInfoMap[t] = di | |||
| if m != nil { | |||
| discardUnknown(MessageReflect(m)) | |||
| } | |||
| return di | |||
| } | |||
| func (di *discardInfo) discard(src pointer) { | |||
| if src.isNil() { | |||
| return // Nothing to do. | |||
| } | |||
| if atomic.LoadInt32(&di.initialized) == 0 { | |||
| di.computeDiscardInfo() | |||
| } | |||
| for _, fi := range di.fields { | |||
| sfp := src.offset(fi.field) | |||
| fi.discard(sfp) | |||
| } | |||
| // For proto2 messages, only discard unknown fields in message extensions | |||
| // that have been accessed via GetExtension. | |||
| if em, err := extendable(src.asPointerTo(di.typ).Interface()); err == nil { | |||
| // Ignore lock since DiscardUnknown is not concurrency safe. | |||
| emm, _ := em.extensionsRead() | |||
| for _, mx := range emm { | |||
| if m, ok := mx.value.(Message); ok { | |||
| DiscardUnknown(m) | |||
| func discardUnknown(m protoreflect.Message) { | |||
| m.Range(func(fd protoreflect.FieldDescriptor, val protoreflect.Value) bool { | |||
| switch { | |||
| // Handle singular message. | |||
| case fd.Cardinality() != protoreflect.Repeated: | |||
| if fd.Message() != nil { | |||
| discardUnknown(m.Get(fd).Message()) | |||
| } | |||
| } | |||
| } | |||
| if di.unrecognized.IsValid() { | |||
| *src.offset(di.unrecognized).toBytes() = nil | |||
| } | |||
| } | |||
| func (di *discardInfo) computeDiscardInfo() { | |||
| di.lock.Lock() | |||
| defer di.lock.Unlock() | |||
| if di.initialized != 0 { | |||
| return | |||
| } | |||
| t := di.typ | |||
| n := t.NumField() | |||
| for i := 0; i < n; i++ { | |||
| f := t.Field(i) | |||
| if strings.HasPrefix(f.Name, "XXX_") { | |||
| continue | |||
| } | |||
| dfi := discardFieldInfo{field: toField(&f)} | |||
| tf := f.Type | |||
| // Unwrap tf to get its most basic type. | |||
| var isPointer, isSlice bool | |||
| if tf.Kind() == reflect.Slice && tf.Elem().Kind() != reflect.Uint8 { | |||
| isSlice = true | |||
| tf = tf.Elem() | |||
| } | |||
| if tf.Kind() == reflect.Ptr { | |||
| isPointer = true | |||
| tf = tf.Elem() | |||
| } | |||
| if isPointer && isSlice && tf.Kind() != reflect.Struct { | |||
| panic(fmt.Sprintf("%v.%s cannot be a slice of pointers to primitive types", t, f.Name)) | |||
| } | |||
| switch tf.Kind() { | |||
| case reflect.Struct: | |||
| switch { | |||
| case !isPointer: | |||
| panic(fmt.Sprintf("%v.%s cannot be a direct struct value", t, f.Name)) | |||
| case isSlice: // E.g., []*pb.T | |||
| di := getDiscardInfo(tf) | |||
| dfi.discard = func(src pointer) { | |||
| sps := src.getPointerSlice() | |||
| for _, sp := range sps { | |||
| if !sp.isNil() { | |||
| di.discard(sp) | |||
| } | |||
| } | |||
| } | |||
| default: // E.g., *pb.T | |||
| di := getDiscardInfo(tf) | |||
| dfi.discard = func(src pointer) { | |||
| sp := src.getPointer() | |||
| if !sp.isNil() { | |||
| di.discard(sp) | |||
| } | |||
| // Handle list of messages. | |||
| case fd.IsList(): | |||
| if fd.Message() != nil { | |||
| ls := m.Get(fd).List() | |||
| for i := 0; i < ls.Len(); i++ { | |||
| discardUnknown(ls.Get(i).Message()) | |||
| } | |||
| } | |||
| case reflect.Map: | |||
| switch { | |||
| case isPointer || isSlice: | |||
| panic(fmt.Sprintf("%v.%s cannot be a pointer to a map or a slice of map values", t, f.Name)) | |||
| default: // E.g., map[K]V | |||
| if tf.Elem().Kind() == reflect.Ptr { // Proto struct (e.g., *T) | |||
| dfi.discard = func(src pointer) { | |||
| sm := src.asPointerTo(tf).Elem() | |||
| if sm.Len() == 0 { | |||
| return | |||
| } | |||
| for _, key := range sm.MapKeys() { | |||
| val := sm.MapIndex(key) | |||
| DiscardUnknown(val.Interface().(Message)) | |||
| } | |||
| } | |||
| } else { | |||
| dfi.discard = func(pointer) {} // Noop | |||
| } | |||
| } | |||
| case reflect.Interface: | |||
| // Must be oneof field. | |||
| switch { | |||
| case isPointer || isSlice: | |||
| panic(fmt.Sprintf("%v.%s cannot be a pointer to a interface or a slice of interface values", t, f.Name)) | |||
| default: // E.g., interface{} | |||
| // TODO: Make this faster? | |||
| dfi.discard = func(src pointer) { | |||
| su := src.asPointerTo(tf).Elem() | |||
| if !su.IsNil() { | |||
| sv := su.Elem().Elem().Field(0) | |||
| if sv.Kind() == reflect.Ptr && sv.IsNil() { | |||
| return | |||
| } | |||
| switch sv.Type().Kind() { | |||
| case reflect.Ptr: // Proto struct (e.g., *T) | |||
| DiscardUnknown(sv.Interface().(Message)) | |||
| } | |||
| } | |||
| } | |||
| // Handle map of messages. | |||
| case fd.IsMap(): | |||
| if fd.MapValue().Message() != nil { | |||
| ms := m.Get(fd).Map() | |||
| ms.Range(func(_ protoreflect.MapKey, v protoreflect.Value) bool { | |||
| discardUnknown(v.Message()) | |||
| return true | |||
| }) | |||
| } | |||
| default: | |||
| continue | |||
| } | |||
| di.fields = append(di.fields, dfi) | |||
| } | |||
| di.unrecognized = invalidField | |||
| if f, ok := t.FieldByName("XXX_unrecognized"); ok { | |||
| if f.Type != reflect.TypeOf([]byte{}) { | |||
| panic("expected XXX_unrecognized to be of type []byte") | |||
| } | |||
| di.unrecognized = toField(&f) | |||
| } | |||
| atomic.StoreInt32(&di.initialized, 1) | |||
| } | |||
| func discardLegacy(m Message) { | |||
| v := reflect.ValueOf(m) | |||
| if v.Kind() != reflect.Ptr || v.IsNil() { | |||
| return | |||
| } | |||
| v = v.Elem() | |||
| if v.Kind() != reflect.Struct { | |||
| return | |||
| } | |||
| t := v.Type() | |||
| for i := 0; i < v.NumField(); i++ { | |||
| f := t.Field(i) | |||
| if strings.HasPrefix(f.Name, "XXX_") { | |||
| continue | |||
| } | |||
| vf := v.Field(i) | |||
| tf := f.Type | |||
| return true | |||
| }) | |||
| // Unwrap tf to get its most basic type. | |||
| var isPointer, isSlice bool | |||
| if tf.Kind() == reflect.Slice && tf.Elem().Kind() != reflect.Uint8 { | |||
| isSlice = true | |||
| tf = tf.Elem() | |||
| } | |||
| if tf.Kind() == reflect.Ptr { | |||
| isPointer = true | |||
| tf = tf.Elem() | |||
| } | |||
| if isPointer && isSlice && tf.Kind() != reflect.Struct { | |||
| panic(fmt.Sprintf("%T.%s cannot be a slice of pointers to primitive types", m, f.Name)) | |||
| } | |||
| switch tf.Kind() { | |||
| case reflect.Struct: | |||
| switch { | |||
| case !isPointer: | |||
| panic(fmt.Sprintf("%T.%s cannot be a direct struct value", m, f.Name)) | |||
| case isSlice: // E.g., []*pb.T | |||
| for j := 0; j < vf.Len(); j++ { | |||
| discardLegacy(vf.Index(j).Interface().(Message)) | |||
| } | |||
| default: // E.g., *pb.T | |||
| discardLegacy(vf.Interface().(Message)) | |||
| } | |||
| case reflect.Map: | |||
| switch { | |||
| case isPointer || isSlice: | |||
| panic(fmt.Sprintf("%T.%s cannot be a pointer to a map or a slice of map values", m, f.Name)) | |||
| default: // E.g., map[K]V | |||
| tv := vf.Type().Elem() | |||
| if tv.Kind() == reflect.Ptr && tv.Implements(protoMessageType) { // Proto struct (e.g., *T) | |||
| for _, key := range vf.MapKeys() { | |||
| val := vf.MapIndex(key) | |||
| discardLegacy(val.Interface().(Message)) | |||
| } | |||
| } | |||
| } | |||
| case reflect.Interface: | |||
| // Must be oneof field. | |||
| switch { | |||
| case isPointer || isSlice: | |||
| panic(fmt.Sprintf("%T.%s cannot be a pointer to a interface or a slice of interface values", m, f.Name)) | |||
| default: // E.g., test_proto.isCommunique_Union interface | |||
| if !vf.IsNil() && f.Tag.Get("protobuf_oneof") != "" { | |||
| vf = vf.Elem() // E.g., *test_proto.Communique_Msg | |||
| if !vf.IsNil() { | |||
| vf = vf.Elem() // E.g., test_proto.Communique_Msg | |||
| vf = vf.Field(0) // E.g., Proto struct (e.g., *T) or primitive value | |||
| if vf.Kind() == reflect.Ptr { | |||
| discardLegacy(vf.Interface().(Message)) | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| if vf := v.FieldByName("XXX_unrecognized"); vf.IsValid() { | |||
| if vf.Type() != reflect.TypeOf([]byte{}) { | |||
| panic("expected XXX_unrecognized to be of type []byte") | |||
| } | |||
| vf.Set(reflect.ValueOf([]byte(nil))) | |||
| } | |||
| // For proto2 messages, only discard unknown fields in message extensions | |||
| // that have been accessed via GetExtension. | |||
| if em, err := extendable(m); err == nil { | |||
| // Ignore lock since discardLegacy is not concurrency safe. | |||
| emm, _ := em.extensionsRead() | |||
| for _, mx := range emm { | |||
| if m, ok := mx.value.(Message); ok { | |||
| discardLegacy(m) | |||
| } | |||
| } | |||
| // Discard unknown fields. | |||
| if len(m.GetUnknown()) > 0 { | |||
| m.SetUnknown(nil) | |||
| } | |||
| } | |||
| @@ -1,203 +0,0 @@ | |||
| // Go support for Protocol Buffers - Google's data interchange format | |||
| // | |||
| // Copyright 2010 The Go Authors. All rights reserved. | |||
| // https://github.com/golang/protobuf | |||
| // | |||
| // Redistribution and use in source and binary forms, with or without | |||
| // modification, are permitted provided that the following conditions are | |||
| // met: | |||
| // | |||
| // * Redistributions of source code must retain the above copyright | |||
| // notice, this list of conditions and the following disclaimer. | |||
| // * Redistributions in binary form must reproduce the above | |||
| // copyright notice, this list of conditions and the following disclaimer | |||
| // in the documentation and/or other materials provided with the | |||
| // distribution. | |||
| // * Neither the name of Google Inc. nor the names of its | |||
| // contributors may be used to endorse or promote products derived from | |||
| // this software without specific prior written permission. | |||
| // | |||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| package proto | |||
| /* | |||
| * Routines for encoding data into the wire format for protocol buffers. | |||
| */ | |||
| import ( | |||
| "errors" | |||
| "reflect" | |||
| ) | |||
| var ( | |||
| // errRepeatedHasNil is the error returned if Marshal is called with | |||
| // a struct with a repeated field containing a nil element. | |||
| errRepeatedHasNil = errors.New("proto: repeated field has nil element") | |||
| // errOneofHasNil is the error returned if Marshal is called with | |||
| // a struct with a oneof field containing a nil element. | |||
| errOneofHasNil = errors.New("proto: oneof field has nil value") | |||
| // ErrNil is the error returned if Marshal is called with nil. | |||
| ErrNil = errors.New("proto: Marshal called with nil") | |||
| // ErrTooLarge is the error returned if Marshal is called with a | |||
| // message that encodes to >2GB. | |||
| ErrTooLarge = errors.New("proto: message encodes to over 2 GB") | |||
| ) | |||
| // The fundamental encoders that put bytes on the wire. | |||
| // Those that take integer types all accept uint64 and are | |||
| // therefore of type valueEncoder. | |||
| const maxVarintBytes = 10 // maximum length of a varint | |||
| // EncodeVarint returns the varint encoding of x. | |||
| // This is the format for the | |||
| // int32, int64, uint32, uint64, bool, and enum | |||
| // protocol buffer types. | |||
| // Not used by the package itself, but helpful to clients | |||
| // wishing to use the same encoding. | |||
| func EncodeVarint(x uint64) []byte { | |||
| var buf [maxVarintBytes]byte | |||
| var n int | |||
| for n = 0; x > 127; n++ { | |||
| buf[n] = 0x80 | uint8(x&0x7F) | |||
| x >>= 7 | |||
| } | |||
| buf[n] = uint8(x) | |||
| n++ | |||
| return buf[0:n] | |||
| } | |||
| // EncodeVarint writes a varint-encoded integer to the Buffer. | |||
| // This is the format for the | |||
| // int32, int64, uint32, uint64, bool, and enum | |||
| // protocol buffer types. | |||
| func (p *Buffer) EncodeVarint(x uint64) error { | |||
| for x >= 1<<7 { | |||
| p.buf = append(p.buf, uint8(x&0x7f|0x80)) | |||
| x >>= 7 | |||
| } | |||
| p.buf = append(p.buf, uint8(x)) | |||
| return nil | |||
| } | |||
| // SizeVarint returns the varint encoding size of an integer. | |||
| func SizeVarint(x uint64) int { | |||
| switch { | |||
| case x < 1<<7: | |||
| return 1 | |||
| case x < 1<<14: | |||
| return 2 | |||
| case x < 1<<21: | |||
| return 3 | |||
| case x < 1<<28: | |||
| return 4 | |||
| case x < 1<<35: | |||
| return 5 | |||
| case x < 1<<42: | |||
| return 6 | |||
| case x < 1<<49: | |||
| return 7 | |||
| case x < 1<<56: | |||
| return 8 | |||
| case x < 1<<63: | |||
| return 9 | |||
| } | |||
| return 10 | |||
| } | |||
| // EncodeFixed64 writes a 64-bit integer to the Buffer. | |||
| // This is the format for the | |||
| // fixed64, sfixed64, and double protocol buffer types. | |||
| func (p *Buffer) EncodeFixed64(x uint64) error { | |||
| p.buf = append(p.buf, | |||
| uint8(x), | |||
| uint8(x>>8), | |||
| uint8(x>>16), | |||
| uint8(x>>24), | |||
| uint8(x>>32), | |||
| uint8(x>>40), | |||
| uint8(x>>48), | |||
| uint8(x>>56)) | |||
| return nil | |||
| } | |||
| // EncodeFixed32 writes a 32-bit integer to the Buffer. | |||
| // This is the format for the | |||
| // fixed32, sfixed32, and float protocol buffer types. | |||
| func (p *Buffer) EncodeFixed32(x uint64) error { | |||
| p.buf = append(p.buf, | |||
| uint8(x), | |||
| uint8(x>>8), | |||
| uint8(x>>16), | |||
| uint8(x>>24)) | |||
| return nil | |||
| } | |||
| // EncodeZigzag64 writes a zigzag-encoded 64-bit integer | |||
| // to the Buffer. | |||
| // This is the format used for the sint64 protocol buffer type. | |||
| func (p *Buffer) EncodeZigzag64(x uint64) error { | |||
| // use signed number to get arithmetic right shift. | |||
| return p.EncodeVarint(uint64((x << 1) ^ uint64((int64(x) >> 63)))) | |||
| } | |||
| // EncodeZigzag32 writes a zigzag-encoded 32-bit integer | |||
| // to the Buffer. | |||
| // This is the format used for the sint32 protocol buffer type. | |||
| func (p *Buffer) EncodeZigzag32(x uint64) error { | |||
| // use signed number to get arithmetic right shift. | |||
| return p.EncodeVarint(uint64((uint32(x) << 1) ^ uint32((int32(x) >> 31)))) | |||
| } | |||
| // EncodeRawBytes writes a count-delimited byte buffer to the Buffer. | |||
| // This is the format used for the bytes protocol buffer | |||
| // type and for embedded messages. | |||
| func (p *Buffer) EncodeRawBytes(b []byte) error { | |||
| p.EncodeVarint(uint64(len(b))) | |||
| p.buf = append(p.buf, b...) | |||
| return nil | |||
| } | |||
| // EncodeStringBytes writes an encoded string to the Buffer. | |||
| // This is the format used for the proto2 string type. | |||
| func (p *Buffer) EncodeStringBytes(s string) error { | |||
| p.EncodeVarint(uint64(len(s))) | |||
| p.buf = append(p.buf, s...) | |||
| return nil | |||
| } | |||
| // Marshaler is the interface representing objects that can marshal themselves. | |||
| type Marshaler interface { | |||
| Marshal() ([]byte, error) | |||
| } | |||
| // EncodeMessage writes the protocol buffer to the Buffer, | |||
| // prefixed by a varint-encoded length. | |||
| func (p *Buffer) EncodeMessage(pb Message) error { | |||
| siz := Size(pb) | |||
| p.EncodeVarint(uint64(siz)) | |||
| return p.Marshal(pb) | |||
| } | |||
| // All protocol buffer fields are nillable, but be careful. | |||
| func isNil(v reflect.Value) bool { | |||
| switch v.Kind() { | |||
| case reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: | |||
| return v.IsNil() | |||
| } | |||
| return false | |||
| } | |||
| @@ -1,301 +0,0 @@ | |||
| // Go support for Protocol Buffers - Google's data interchange format | |||
| // | |||
| // Copyright 2011 The Go Authors. All rights reserved. | |||
| // https://github.com/golang/protobuf | |||
| // | |||
| // Redistribution and use in source and binary forms, with or without | |||
| // modification, are permitted provided that the following conditions are | |||
| // met: | |||
| // | |||
| // * Redistributions of source code must retain the above copyright | |||
| // notice, this list of conditions and the following disclaimer. | |||
| // * Redistributions in binary form must reproduce the above | |||
| // copyright notice, this list of conditions and the following disclaimer | |||
| // in the documentation and/or other materials provided with the | |||
| // distribution. | |||
| // * Neither the name of Google Inc. nor the names of its | |||
| // contributors may be used to endorse or promote products derived from | |||
| // this software without specific prior written permission. | |||
| // | |||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| // Protocol buffer comparison. | |||
| package proto | |||
| import ( | |||
| "bytes" | |||
| "log" | |||
| "reflect" | |||
| "strings" | |||
| ) | |||
| /* | |||
| Equal returns true iff protocol buffers a and b are equal. | |||
| The arguments must both be pointers to protocol buffer structs. | |||
| Equality is defined in this way: | |||
| - Two messages are equal iff they are the same type, | |||
| corresponding fields are equal, unknown field sets | |||
| are equal, and extensions sets are equal. | |||
| - Two set scalar fields are equal iff their values are equal. | |||
| If the fields are of a floating-point type, remember that | |||
| NaN != x for all x, including NaN. If the message is defined | |||
| in a proto3 .proto file, fields are not "set"; specifically, | |||
| zero length proto3 "bytes" fields are equal (nil == {}). | |||
| - Two repeated fields are equal iff their lengths are the same, | |||
| and their corresponding elements are equal. Note a "bytes" field, | |||
| although represented by []byte, is not a repeated field and the | |||
| rule for the scalar fields described above applies. | |||
| - Two unset fields are equal. | |||
| - Two unknown field sets are equal if their current | |||
| encoded state is equal. | |||
| - Two extension sets are equal iff they have corresponding | |||
| elements that are pairwise equal. | |||
| - Two map fields are equal iff their lengths are the same, | |||
| and they contain the same set of elements. Zero-length map | |||
| fields are equal. | |||
| - Every other combination of things are not equal. | |||
| The return value is undefined if a and b are not protocol buffers. | |||
| */ | |||
| func Equal(a, b Message) bool { | |||
| if a == nil || b == nil { | |||
| return a == b | |||
| } | |||
| v1, v2 := reflect.ValueOf(a), reflect.ValueOf(b) | |||
| if v1.Type() != v2.Type() { | |||
| return false | |||
| } | |||
| if v1.Kind() == reflect.Ptr { | |||
| if v1.IsNil() { | |||
| return v2.IsNil() | |||
| } | |||
| if v2.IsNil() { | |||
| return false | |||
| } | |||
| v1, v2 = v1.Elem(), v2.Elem() | |||
| } | |||
| if v1.Kind() != reflect.Struct { | |||
| return false | |||
| } | |||
| return equalStruct(v1, v2) | |||
| } | |||
| // v1 and v2 are known to have the same type. | |||
| func equalStruct(v1, v2 reflect.Value) bool { | |||
| sprop := GetProperties(v1.Type()) | |||
| for i := 0; i < v1.NumField(); i++ { | |||
| f := v1.Type().Field(i) | |||
| if strings.HasPrefix(f.Name, "XXX_") { | |||
| continue | |||
| } | |||
| f1, f2 := v1.Field(i), v2.Field(i) | |||
| if f.Type.Kind() == reflect.Ptr { | |||
| if n1, n2 := f1.IsNil(), f2.IsNil(); n1 && n2 { | |||
| // both unset | |||
| continue | |||
| } else if n1 != n2 { | |||
| // set/unset mismatch | |||
| return false | |||
| } | |||
| f1, f2 = f1.Elem(), f2.Elem() | |||
| } | |||
| if !equalAny(f1, f2, sprop.Prop[i]) { | |||
| return false | |||
| } | |||
| } | |||
| if em1 := v1.FieldByName("XXX_InternalExtensions"); em1.IsValid() { | |||
| em2 := v2.FieldByName("XXX_InternalExtensions") | |||
| if !equalExtensions(v1.Type(), em1.Interface().(XXX_InternalExtensions), em2.Interface().(XXX_InternalExtensions)) { | |||
| return false | |||
| } | |||
| } | |||
| if em1 := v1.FieldByName("XXX_extensions"); em1.IsValid() { | |||
| em2 := v2.FieldByName("XXX_extensions") | |||
| if !equalExtMap(v1.Type(), em1.Interface().(map[int32]Extension), em2.Interface().(map[int32]Extension)) { | |||
| return false | |||
| } | |||
| } | |||
| uf := v1.FieldByName("XXX_unrecognized") | |||
| if !uf.IsValid() { | |||
| return true | |||
| } | |||
| u1 := uf.Bytes() | |||
| u2 := v2.FieldByName("XXX_unrecognized").Bytes() | |||
| return bytes.Equal(u1, u2) | |||
| } | |||
| // v1 and v2 are known to have the same type. | |||
| // prop may be nil. | |||
| func equalAny(v1, v2 reflect.Value, prop *Properties) bool { | |||
| if v1.Type() == protoMessageType { | |||
| m1, _ := v1.Interface().(Message) | |||
| m2, _ := v2.Interface().(Message) | |||
| return Equal(m1, m2) | |||
| } | |||
| switch v1.Kind() { | |||
| case reflect.Bool: | |||
| return v1.Bool() == v2.Bool() | |||
| case reflect.Float32, reflect.Float64: | |||
| return v1.Float() == v2.Float() | |||
| case reflect.Int32, reflect.Int64: | |||
| return v1.Int() == v2.Int() | |||
| case reflect.Interface: | |||
| // Probably a oneof field; compare the inner values. | |||
| n1, n2 := v1.IsNil(), v2.IsNil() | |||
| if n1 || n2 { | |||
| return n1 == n2 | |||
| } | |||
| e1, e2 := v1.Elem(), v2.Elem() | |||
| if e1.Type() != e2.Type() { | |||
| return false | |||
| } | |||
| return equalAny(e1, e2, nil) | |||
| case reflect.Map: | |||
| if v1.Len() != v2.Len() { | |||
| return false | |||
| } | |||
| for _, key := range v1.MapKeys() { | |||
| val2 := v2.MapIndex(key) | |||
| if !val2.IsValid() { | |||
| // This key was not found in the second map. | |||
| return false | |||
| } | |||
| if !equalAny(v1.MapIndex(key), val2, nil) { | |||
| return false | |||
| } | |||
| } | |||
| return true | |||
| case reflect.Ptr: | |||
| // Maps may have nil values in them, so check for nil. | |||
| if v1.IsNil() && v2.IsNil() { | |||
| return true | |||
| } | |||
| if v1.IsNil() != v2.IsNil() { | |||
| return false | |||
| } | |||
| return equalAny(v1.Elem(), v2.Elem(), prop) | |||
| case reflect.Slice: | |||
| if v1.Type().Elem().Kind() == reflect.Uint8 { | |||
| // short circuit: []byte | |||
| // Edge case: if this is in a proto3 message, a zero length | |||
| // bytes field is considered the zero value. | |||
| if prop != nil && prop.proto3 && v1.Len() == 0 && v2.Len() == 0 { | |||
| return true | |||
| } | |||
| if v1.IsNil() != v2.IsNil() { | |||
| return false | |||
| } | |||
| return bytes.Equal(v1.Interface().([]byte), v2.Interface().([]byte)) | |||
| } | |||
| if v1.Len() != v2.Len() { | |||
| return false | |||
| } | |||
| for i := 0; i < v1.Len(); i++ { | |||
| if !equalAny(v1.Index(i), v2.Index(i), prop) { | |||
| return false | |||
| } | |||
| } | |||
| return true | |||
| case reflect.String: | |||
| return v1.Interface().(string) == v2.Interface().(string) | |||
| case reflect.Struct: | |||
| return equalStruct(v1, v2) | |||
| case reflect.Uint32, reflect.Uint64: | |||
| return v1.Uint() == v2.Uint() | |||
| } | |||
| // unknown type, so not a protocol buffer | |||
| log.Printf("proto: don't know how to compare %v", v1) | |||
| return false | |||
| } | |||
| // base is the struct type that the extensions are based on. | |||
| // x1 and x2 are InternalExtensions. | |||
| func equalExtensions(base reflect.Type, x1, x2 XXX_InternalExtensions) bool { | |||
| em1, _ := x1.extensionsRead() | |||
| em2, _ := x2.extensionsRead() | |||
| return equalExtMap(base, em1, em2) | |||
| } | |||
| func equalExtMap(base reflect.Type, em1, em2 map[int32]Extension) bool { | |||
| if len(em1) != len(em2) { | |||
| return false | |||
| } | |||
| for extNum, e1 := range em1 { | |||
| e2, ok := em2[extNum] | |||
| if !ok { | |||
| return false | |||
| } | |||
| m1 := extensionAsLegacyType(e1.value) | |||
| m2 := extensionAsLegacyType(e2.value) | |||
| if m1 == nil && m2 == nil { | |||
| // Both have only encoded form. | |||
| if bytes.Equal(e1.enc, e2.enc) { | |||
| continue | |||
| } | |||
| // The bytes are different, but the extensions might still be | |||
| // equal. We need to decode them to compare. | |||
| } | |||
| if m1 != nil && m2 != nil { | |||
| // Both are unencoded. | |||
| if !equalAny(reflect.ValueOf(m1), reflect.ValueOf(m2), nil) { | |||
| return false | |||
| } | |||
| continue | |||
| } | |||
| // At least one is encoded. To do a semantically correct comparison | |||
| // we need to unmarshal them first. | |||
| var desc *ExtensionDesc | |||
| if m := extensionMaps[base]; m != nil { | |||
| desc = m[extNum] | |||
| } | |||
| if desc == nil { | |||
| // If both have only encoded form and the bytes are the same, | |||
| // it is handled above. We get here when the bytes are different. | |||
| // We don't know how to decode it, so just compare them as byte | |||
| // slices. | |||
| log.Printf("proto: don't know how to compare extension %d of %v", extNum, base) | |||
| return false | |||
| } | |||
| var err error | |||
| if m1 == nil { | |||
| m1, err = decodeExtension(e1.enc, desc) | |||
| } | |||
| if m2 == nil && err == nil { | |||
| m2, err = decodeExtension(e2.enc, desc) | |||
| } | |||
| if err != nil { | |||
| // The encoded form is invalid. | |||
| log.Printf("proto: badly encoded extension %d of %v: %v", extNum, base, err) | |||
| return false | |||
| } | |||
| if !equalAny(reflect.ValueOf(m1), reflect.ValueOf(m2), nil) { | |||
| return false | |||
| } | |||
| } | |||
| return true | |||
| } | |||
| @@ -1,310 +1,111 @@ | |||
| // Go support for Protocol Buffers - Google's data interchange format | |||
| // | |||
| // Copyright 2010 The Go Authors. All rights reserved. | |||
| // https://github.com/golang/protobuf | |||
| // | |||
| // Redistribution and use in source and binary forms, with or without | |||
| // modification, are permitted provided that the following conditions are | |||
| // met: | |||
| // | |||
| // * Redistributions of source code must retain the above copyright | |||
| // notice, this list of conditions and the following disclaimer. | |||
| // * Redistributions in binary form must reproduce the above | |||
| // copyright notice, this list of conditions and the following disclaimer | |||
| // in the documentation and/or other materials provided with the | |||
| // distribution. | |||
| // * Neither the name of Google Inc. nor the names of its | |||
| // contributors may be used to endorse or promote products derived from | |||
| // this software without specific prior written permission. | |||
| // | |||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| // Copyright 2010 The Go Authors. All rights reserved. | |||
| // Use of this source code is governed by a BSD-style | |||
| // license that can be found in the LICENSE file. | |||
| package proto | |||
| /* | |||
| * Types and routines for supporting protocol buffer extensions. | |||
| */ | |||
| import ( | |||
| "errors" | |||
| "fmt" | |||
| "io" | |||
| "reflect" | |||
| "strconv" | |||
| "sync" | |||
| ) | |||
| // ErrMissingExtension is the error returned by GetExtension if the named extension is not in the message. | |||
| var ErrMissingExtension = errors.New("proto: missing extension") | |||
| // ExtensionRange represents a range of message extensions for a protocol buffer. | |||
| // Used in code generated by the protocol compiler. | |||
| type ExtensionRange struct { | |||
| Start, End int32 // both inclusive | |||
| } | |||
| // extendableProto is an interface implemented by any protocol buffer generated by the current | |||
| // proto compiler that may be extended. | |||
| type extendableProto interface { | |||
| Message | |||
| ExtensionRangeArray() []ExtensionRange | |||
| extensionsWrite() map[int32]Extension | |||
| extensionsRead() (map[int32]Extension, sync.Locker) | |||
| } | |||
| // extendableProtoV1 is an interface implemented by a protocol buffer generated by the previous | |||
| // version of the proto compiler that may be extended. | |||
| type extendableProtoV1 interface { | |||
| Message | |||
| ExtensionRangeArray() []ExtensionRange | |||
| ExtensionMap() map[int32]Extension | |||
| } | |||
| // extensionAdapter is a wrapper around extendableProtoV1 that implements extendableProto. | |||
| type extensionAdapter struct { | |||
| extendableProtoV1 | |||
| } | |||
| "google.golang.org/protobuf/encoding/protowire" | |||
| "google.golang.org/protobuf/proto" | |||
| "google.golang.org/protobuf/reflect/protoreflect" | |||
| "google.golang.org/protobuf/reflect/protoregistry" | |||
| "google.golang.org/protobuf/runtime/protoiface" | |||
| "google.golang.org/protobuf/runtime/protoimpl" | |||
| ) | |||
| func (e extensionAdapter) extensionsWrite() map[int32]Extension { | |||
| return e.ExtensionMap() | |||
| } | |||
| type ( | |||
| // ExtensionDesc represents an extension descriptor and | |||
| // is used to interact with an extension field in a message. | |||
| // | |||
| // Variables of this type are generated in code by protoc-gen-go. | |||
| ExtensionDesc = protoimpl.ExtensionInfo | |||
| func (e extensionAdapter) extensionsRead() (map[int32]Extension, sync.Locker) { | |||
| return e.ExtensionMap(), notLocker{} | |||
| } | |||
| // ExtensionRange represents a range of message extensions. | |||
| // Used in code generated by protoc-gen-go. | |||
| ExtensionRange = protoiface.ExtensionRangeV1 | |||
| // notLocker is a sync.Locker whose Lock and Unlock methods are nops. | |||
| type notLocker struct{} | |||
| // Deprecated: Do not use; this is an internal type. | |||
| Extension = protoimpl.ExtensionFieldV1 | |||
| func (n notLocker) Lock() {} | |||
| func (n notLocker) Unlock() {} | |||
| // Deprecated: Do not use; this is an internal type. | |||
| XXX_InternalExtensions = protoimpl.ExtensionFields | |||
| ) | |||
| // extendable returns the extendableProto interface for the given generated proto message. | |||
| // If the proto message has the old extension format, it returns a wrapper that implements | |||
| // the extendableProto interface. | |||
| func extendable(p interface{}) (extendableProto, error) { | |||
| switch p := p.(type) { | |||
| case extendableProto: | |||
| if isNilPtr(p) { | |||
| return nil, fmt.Errorf("proto: nil %T is not extendable", p) | |||
| } | |||
| return p, nil | |||
| case extendableProtoV1: | |||
| if isNilPtr(p) { | |||
| return nil, fmt.Errorf("proto: nil %T is not extendable", p) | |||
| } | |||
| return extensionAdapter{p}, nil | |||
| } | |||
| // Don't allocate a specific error containing %T: | |||
| // this is the hot path for Clone and MarshalText. | |||
| return nil, errNotExtendable | |||
| } | |||
| // ErrMissingExtension reports whether the extension was not present. | |||
| var ErrMissingExtension = errors.New("proto: missing extension") | |||
| var errNotExtendable = errors.New("proto: not an extendable proto.Message") | |||
| func isNilPtr(x interface{}) bool { | |||
| v := reflect.ValueOf(x) | |||
| return v.Kind() == reflect.Ptr && v.IsNil() | |||
| } | |||
| // XXX_InternalExtensions is an internal representation of proto extensions. | |||
| // | |||
| // Each generated message struct type embeds an anonymous XXX_InternalExtensions field, | |||
| // thus gaining the unexported 'extensions' method, which can be called only from the proto package. | |||
| // | |||
| // The methods of XXX_InternalExtensions are not concurrency safe in general, | |||
| // but calls to logically read-only methods such as has and get may be executed concurrently. | |||
| type XXX_InternalExtensions struct { | |||
| // The struct must be indirect so that if a user inadvertently copies a | |||
| // generated message and its embedded XXX_InternalExtensions, they | |||
| // avoid the mayhem of a copied mutex. | |||
| // | |||
| // The mutex serializes all logically read-only operations to p.extensionMap. | |||
| // It is up to the client to ensure that write operations to p.extensionMap are | |||
| // mutually exclusive with other accesses. | |||
| p *struct { | |||
| mu sync.Mutex | |||
| extensionMap map[int32]Extension | |||
| // HasExtension reports whether the extension field is present in m | |||
| // either as an explicitly populated field or as an unknown field. | |||
| func HasExtension(m Message, xt *ExtensionDesc) (has bool) { | |||
| mr := MessageReflect(m) | |||
| if mr == nil || !mr.IsValid() { | |||
| return false | |||
| } | |||
| } | |||
| // extensionsWrite returns the extension map, creating it on first use. | |||
| func (e *XXX_InternalExtensions) extensionsWrite() map[int32]Extension { | |||
| if e.p == nil { | |||
| e.p = new(struct { | |||
| mu sync.Mutex | |||
| extensionMap map[int32]Extension | |||
| // Check whether any populated known field matches the field number. | |||
| xtd := xt.TypeDescriptor() | |||
| if isValidExtension(mr.Descriptor(), xtd) { | |||
| has = mr.Has(xtd) | |||
| } else { | |||
| mr.Range(func(fd protoreflect.FieldDescriptor, _ protoreflect.Value) bool { | |||
| has = int32(fd.Number()) == xt.Field | |||
| return !has | |||
| }) | |||
| e.p.extensionMap = make(map[int32]Extension) | |||
| } | |||
| return e.p.extensionMap | |||
| } | |||
| // extensionsRead returns the extensions map for read-only use. It may be nil. | |||
| // The caller must hold the returned mutex's lock when accessing Elements within the map. | |||
| func (e *XXX_InternalExtensions) extensionsRead() (map[int32]Extension, sync.Locker) { | |||
| if e.p == nil { | |||
| return nil, nil | |||
| // Check whether any unknown field matches the field number. | |||
| for b := mr.GetUnknown(); !has && len(b) > 0; { | |||
| num, _, n := protowire.ConsumeField(b) | |||
| has = int32(num) == xt.Field | |||
| b = b[n:] | |||
| } | |||
| return e.p.extensionMap, &e.p.mu | |||
| } | |||
| // ExtensionDesc represents an extension specification. | |||
| // Used in generated code from the protocol compiler. | |||
| type ExtensionDesc struct { | |||
| ExtendedType Message // nil pointer to the type that is being extended | |||
| ExtensionType interface{} // nil pointer to the extension type | |||
| Field int32 // field number | |||
| Name string // fully-qualified name of extension, for text formatting | |||
| Tag string // protobuf tag style | |||
| Filename string // name of the file in which the extension is defined | |||
| } | |||
| func (ed *ExtensionDesc) repeated() bool { | |||
| t := reflect.TypeOf(ed.ExtensionType) | |||
| return t.Kind() == reflect.Slice && t.Elem().Kind() != reflect.Uint8 | |||
| } | |||
| // Extension represents an extension in a message. | |||
| type Extension struct { | |||
| // When an extension is stored in a message using SetExtension | |||
| // only desc and value are set. When the message is marshaled | |||
| // enc will be set to the encoded form of the message. | |||
| // | |||
| // When a message is unmarshaled and contains extensions, each | |||
| // extension will have only enc set. When such an extension is | |||
| // accessed using GetExtension (or GetExtensions) desc and value | |||
| // will be set. | |||
| desc *ExtensionDesc | |||
| // value is a concrete value for the extension field. Let the type of | |||
| // desc.ExtensionType be the "API type" and the type of Extension.value | |||
| // be the "storage type". The API type and storage type are the same except: | |||
| // * For scalars (except []byte), the API type uses *T, | |||
| // while the storage type uses T. | |||
| // * For repeated fields, the API type uses []T, while the storage type | |||
| // uses *[]T. | |||
| // | |||
| // The reason for the divergence is so that the storage type more naturally | |||
| // matches what is expected of when retrieving the values through the | |||
| // protobuf reflection APIs. | |||
| // | |||
| // The value may only be populated if desc is also populated. | |||
| value interface{} | |||
| // enc is the raw bytes for the extension field. | |||
| enc []byte | |||
| return has | |||
| } | |||
| // SetRawExtension is for testing only. | |||
| func SetRawExtension(base Message, id int32, b []byte) { | |||
| epb, err := extendable(base) | |||
| if err != nil { | |||
| // ClearExtension removes the the exntesion field from m | |||
| // either as an explicitly populated field or as an unknown field. | |||
| func ClearExtension(m Message, xt *ExtensionDesc) { | |||
| mr := MessageReflect(m) | |||
| if mr == nil || !mr.IsValid() { | |||
| return | |||
| } | |||
| extmap := epb.extensionsWrite() | |||
| extmap[id] = Extension{enc: b} | |||
| } | |||
| // isExtensionField returns true iff the given field number is in an extension range. | |||
| func isExtensionField(pb extendableProto, field int32) bool { | |||
| for _, er := range pb.ExtensionRangeArray() { | |||
| if er.Start <= field && field <= er.End { | |||
| xtd := xt.TypeDescriptor() | |||
| if isValidExtension(mr.Descriptor(), xtd) { | |||
| mr.Clear(xtd) | |||
| } else { | |||
| mr.Range(func(fd protoreflect.FieldDescriptor, _ protoreflect.Value) bool { | |||
| if int32(fd.Number()) == xt.Field { | |||
| mr.Clear(fd) | |||
| return false | |||
| } | |||
| return true | |||
| } | |||
| } | |||
| return false | |||
| } | |||
| // checkExtensionTypes checks that the given extension is valid for pb. | |||
| func checkExtensionTypes(pb extendableProto, extension *ExtensionDesc) error { | |||
| var pbi interface{} = pb | |||
| // Check the extended type. | |||
| if ea, ok := pbi.(extensionAdapter); ok { | |||
| pbi = ea.extendableProtoV1 | |||
| } | |||
| if a, b := reflect.TypeOf(pbi), reflect.TypeOf(extension.ExtendedType); a != b { | |||
| return fmt.Errorf("proto: bad extended type; %v does not extend %v", b, a) | |||
| } | |||
| // Check the range. | |||
| if !isExtensionField(pb, extension.Field) { | |||
| return errors.New("proto: bad extension number; not in declared ranges") | |||
| } | |||
| return nil | |||
| } | |||
| // extPropKey is sufficient to uniquely identify an extension. | |||
| type extPropKey struct { | |||
| base reflect.Type | |||
| field int32 | |||
| } | |||
| var extProp = struct { | |||
| sync.RWMutex | |||
| m map[extPropKey]*Properties | |||
| }{ | |||
| m: make(map[extPropKey]*Properties), | |||
| } | |||
| func extensionProperties(ed *ExtensionDesc) *Properties { | |||
| key := extPropKey{base: reflect.TypeOf(ed.ExtendedType), field: ed.Field} | |||
| extProp.RLock() | |||
| if prop, ok := extProp.m[key]; ok { | |||
| extProp.RUnlock() | |||
| return prop | |||
| } | |||
| extProp.RUnlock() | |||
| extProp.Lock() | |||
| defer extProp.Unlock() | |||
| // Check again. | |||
| if prop, ok := extProp.m[key]; ok { | |||
| return prop | |||
| } | |||
| prop := new(Properties) | |||
| prop.Init(reflect.TypeOf(ed.ExtensionType), "unknown_name", ed.Tag, nil) | |||
| extProp.m[key] = prop | |||
| return prop | |||
| } | |||
| // HasExtension returns whether the given extension is present in pb. | |||
| func HasExtension(pb Message, extension *ExtensionDesc) bool { | |||
| // TODO: Check types, field numbers, etc.? | |||
| epb, err := extendable(pb) | |||
| if err != nil { | |||
| return false | |||
| } | |||
| extmap, mu := epb.extensionsRead() | |||
| if extmap == nil { | |||
| return false | |||
| }) | |||
| } | |||
| mu.Lock() | |||
| _, ok := extmap[extension.Field] | |||
| mu.Unlock() | |||
| return ok | |||
| clearUnknown(mr, fieldNum(xt.Field)) | |||
| } | |||
| // ClearExtension removes the given extension from pb. | |||
| func ClearExtension(pb Message, extension *ExtensionDesc) { | |||
| epb, err := extendable(pb) | |||
| if err != nil { | |||
| // ClearAllExtensions clears all extensions from m. | |||
| // This includes populated fields and unknown fields in the extension range. | |||
| func ClearAllExtensions(m Message) { | |||
| mr := MessageReflect(m) | |||
| if mr == nil || !mr.IsValid() { | |||
| return | |||
| } | |||
| // TODO: Check types, field numbers, etc.? | |||
| extmap := epb.extensionsWrite() | |||
| delete(extmap, extension.Field) | |||
| mr.Range(func(fd protoreflect.FieldDescriptor, _ protoreflect.Value) bool { | |||
| if fd.IsExtension() { | |||
| mr.Clear(fd) | |||
| } | |||
| return true | |||
| }) | |||
| clearUnknown(mr, mr.Descriptor().ExtensionRanges()) | |||
| } | |||
| // GetExtension retrieves a proto2 extended field from pb. | |||
| @@ -314,294 +115,242 @@ func ClearExtension(pb Message, extension *ExtensionDesc) { | |||
| // If the field is not present, then the default value is returned (if one is specified), | |||
| // otherwise ErrMissingExtension is reported. | |||
| // | |||
| // If the descriptor is not type complete (i.e., ExtensionDesc.ExtensionType is nil), | |||
| // then GetExtension returns the raw encoded bytes of the field extension. | |||
| func GetExtension(pb Message, extension *ExtensionDesc) (interface{}, error) { | |||
| epb, err := extendable(pb) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| if extension.ExtendedType != nil { | |||
| // can only check type if this is a complete descriptor | |||
| if err := checkExtensionTypes(epb, extension); err != nil { | |||
| return nil, err | |||
| // If the descriptor is type incomplete (i.e., ExtensionDesc.ExtensionType is nil), | |||
| // then GetExtension returns the raw encoded bytes for the extension field. | |||
| func GetExtension(m Message, xt *ExtensionDesc) (interface{}, error) { | |||
| mr := MessageReflect(m) | |||
| if mr == nil || !mr.IsValid() || mr.Descriptor().ExtensionRanges().Len() == 0 { | |||
| return nil, errNotExtendable | |||
| } | |||
| // Retrieve the unknown fields for this extension field. | |||
| var bo protoreflect.RawFields | |||
| for bi := mr.GetUnknown(); len(bi) > 0; { | |||
| num, _, n := protowire.ConsumeField(bi) | |||
| if int32(num) == xt.Field { | |||
| bo = append(bo, bi[:n]...) | |||
| } | |||
| bi = bi[n:] | |||
| } | |||
| emap, mu := epb.extensionsRead() | |||
| if emap == nil { | |||
| return defaultExtensionValue(extension) | |||
| } | |||
| mu.Lock() | |||
| defer mu.Unlock() | |||
| e, ok := emap[extension.Field] | |||
| if !ok { | |||
| // defaultExtensionValue returns the default value or | |||
| // ErrMissingExtension if there is no default. | |||
| return defaultExtensionValue(extension) | |||
| } | |||
| if e.value != nil { | |||
| // Already decoded. Check the descriptor, though. | |||
| if e.desc != extension { | |||
| // This shouldn't happen. If it does, it means that | |||
| // GetExtension was called twice with two different | |||
| // descriptors with the same field number. | |||
| return nil, errors.New("proto: descriptor conflict") | |||
| } | |||
| return extensionAsLegacyType(e.value), nil | |||
| // For type incomplete descriptors, only retrieve the unknown fields. | |||
| if xt.ExtensionType == nil { | |||
| return []byte(bo), nil | |||
| } | |||
| if extension.ExtensionType == nil { | |||
| // incomplete descriptor | |||
| return e.enc, nil | |||
| // If the extension field only exists as unknown fields, unmarshal it. | |||
| // This is rarely done since proto.Unmarshal eagerly unmarshals extensions. | |||
| xtd := xt.TypeDescriptor() | |||
| if !isValidExtension(mr.Descriptor(), xtd) { | |||
| return nil, fmt.Errorf("proto: bad extended type; %T does not extend %T", xt.ExtendedType, m) | |||
| } | |||
| v, err := decodeExtension(e.enc, extension) | |||
| if err != nil { | |||
| return nil, err | |||
| if !mr.Has(xtd) && len(bo) > 0 { | |||
| m2 := mr.New() | |||
| if err := (proto.UnmarshalOptions{ | |||
| Resolver: extensionResolver{xt}, | |||
| }.Unmarshal(bo, m2.Interface())); err != nil { | |||
| return nil, err | |||
| } | |||
| if m2.Has(xtd) { | |||
| mr.Set(xtd, m2.Get(xtd)) | |||
| clearUnknown(mr, fieldNum(xt.Field)) | |||
| } | |||
| } | |||
| // Remember the decoded version and drop the encoded version. | |||
| // That way it is safe to mutate what we return. | |||
| e.value = extensionAsStorageType(v) | |||
| e.desc = extension | |||
| e.enc = nil | |||
| emap[extension.Field] = e | |||
| return extensionAsLegacyType(e.value), nil | |||
| } | |||
| // defaultExtensionValue returns the default value for extension. | |||
| // If no default for an extension is defined ErrMissingExtension is returned. | |||
| func defaultExtensionValue(extension *ExtensionDesc) (interface{}, error) { | |||
| if extension.ExtensionType == nil { | |||
| // incomplete descriptor, so no default | |||
| // Check whether the message has the extension field set or a default. | |||
| var pv protoreflect.Value | |||
| switch { | |||
| case mr.Has(xtd): | |||
| pv = mr.Get(xtd) | |||
| case xtd.HasDefault(): | |||
| pv = xtd.Default() | |||
| default: | |||
| return nil, ErrMissingExtension | |||
| } | |||
| t := reflect.TypeOf(extension.ExtensionType) | |||
| props := extensionProperties(extension) | |||
| sf, _, err := fieldDefault(t, props) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| if sf == nil || sf.value == nil { | |||
| // There is no default value. | |||
| return nil, ErrMissingExtension | |||
| v := xt.InterfaceOf(pv) | |||
| rv := reflect.ValueOf(v) | |||
| if isScalarKind(rv.Kind()) { | |||
| rv2 := reflect.New(rv.Type()) | |||
| rv2.Elem().Set(rv) | |||
| v = rv2.Interface() | |||
| } | |||
| return v, nil | |||
| } | |||
| if t.Kind() != reflect.Ptr { | |||
| // We do not need to return a Ptr, we can directly return sf.value. | |||
| return sf.value, nil | |||
| } | |||
| // extensionResolver is a custom extension resolver that stores a single | |||
| // extension type that takes precedence over the global registry. | |||
| type extensionResolver struct{ xt protoreflect.ExtensionType } | |||
| // We need to return an interface{} that is a pointer to sf.value. | |||
| value := reflect.New(t).Elem() | |||
| value.Set(reflect.New(value.Type().Elem())) | |||
| if sf.kind == reflect.Int32 { | |||
| // We may have an int32 or an enum, but the underlying data is int32. | |||
| // Since we can't set an int32 into a non int32 reflect.value directly | |||
| // set it as a int32. | |||
| value.Elem().SetInt(int64(sf.value.(int32))) | |||
| } else { | |||
| value.Elem().Set(reflect.ValueOf(sf.value)) | |||
| func (r extensionResolver) FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error) { | |||
| if xtd := r.xt.TypeDescriptor(); xtd.FullName() == field { | |||
| return r.xt, nil | |||
| } | |||
| return value.Interface(), nil | |||
| return protoregistry.GlobalTypes.FindExtensionByName(field) | |||
| } | |||
| // decodeExtension decodes an extension encoded in b. | |||
| func decodeExtension(b []byte, extension *ExtensionDesc) (interface{}, error) { | |||
| t := reflect.TypeOf(extension.ExtensionType) | |||
| unmarshal := typeUnmarshaler(t, extension.Tag) | |||
| // t is a pointer to a struct, pointer to basic type or a slice. | |||
| // Allocate space to store the pointer/slice. | |||
| value := reflect.New(t).Elem() | |||
| var err error | |||
| for { | |||
| x, n := decodeVarint(b) | |||
| if n == 0 { | |||
| return nil, io.ErrUnexpectedEOF | |||
| } | |||
| b = b[n:] | |||
| wire := int(x) & 7 | |||
| b, err = unmarshal(b, valToPointer(value.Addr()), wire) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| if len(b) == 0 { | |||
| break | |||
| } | |||
| func (r extensionResolver) FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error) { | |||
| if xtd := r.xt.TypeDescriptor(); xtd.ContainingMessage().FullName() == message && xtd.Number() == field { | |||
| return r.xt, nil | |||
| } | |||
| return value.Interface(), nil | |||
| return protoregistry.GlobalTypes.FindExtensionByNumber(message, field) | |||
| } | |||
| // GetExtensions returns a slice of the extensions present in pb that are also listed in es. | |||
| // The returned slice has the same length as es; missing extensions will appear as nil elements. | |||
| func GetExtensions(pb Message, es []*ExtensionDesc) (extensions []interface{}, err error) { | |||
| epb, err := extendable(pb) | |||
| if err != nil { | |||
| return nil, err | |||
| // GetExtensions returns a list of the extensions values present in m, | |||
| // corresponding with the provided list of extension descriptors, xts. | |||
| // If an extension is missing in m, the corresponding value is nil. | |||
| func GetExtensions(m Message, xts []*ExtensionDesc) ([]interface{}, error) { | |||
| mr := MessageReflect(m) | |||
| if mr == nil || !mr.IsValid() { | |||
| return nil, errNotExtendable | |||
| } | |||
| extensions = make([]interface{}, len(es)) | |||
| for i, e := range es { | |||
| extensions[i], err = GetExtension(epb, e) | |||
| if err == ErrMissingExtension { | |||
| err = nil | |||
| } | |||
| vs := make([]interface{}, len(xts)) | |||
| for i, xt := range xts { | |||
| v, err := GetExtension(m, xt) | |||
| if err != nil { | |||
| return | |||
| if err == ErrMissingExtension { | |||
| continue | |||
| } | |||
| return vs, err | |||
| } | |||
| vs[i] = v | |||
| } | |||
| return | |||
| return vs, nil | |||
| } | |||
| // ExtensionDescs returns a new slice containing pb's extension descriptors, in undefined order. | |||
| // For non-registered extensions, ExtensionDescs returns an incomplete descriptor containing | |||
| // just the Field field, which defines the extension's field number. | |||
| func ExtensionDescs(pb Message) ([]*ExtensionDesc, error) { | |||
| epb, err := extendable(pb) | |||
| if err != nil { | |||
| return nil, err | |||
| // SetExtension sets an extension field in m to the provided value. | |||
| func SetExtension(m Message, xt *ExtensionDesc, v interface{}) error { | |||
| mr := MessageReflect(m) | |||
| if mr == nil || !mr.IsValid() || mr.Descriptor().ExtensionRanges().Len() == 0 { | |||
| return errNotExtendable | |||
| } | |||
| registeredExtensions := RegisteredExtensions(pb) | |||
| emap, mu := epb.extensionsRead() | |||
| if emap == nil { | |||
| return nil, nil | |||
| rv := reflect.ValueOf(v) | |||
| if reflect.TypeOf(v) != reflect.TypeOf(xt.ExtensionType) { | |||
| return fmt.Errorf("proto: bad extension value type. got: %T, want: %T", v, xt.ExtensionType) | |||
| } | |||
| mu.Lock() | |||
| defer mu.Unlock() | |||
| extensions := make([]*ExtensionDesc, 0, len(emap)) | |||
| for extid, e := range emap { | |||
| desc := e.desc | |||
| if desc == nil { | |||
| desc = registeredExtensions[extid] | |||
| if desc == nil { | |||
| desc = &ExtensionDesc{Field: extid} | |||
| } | |||
| if rv.Kind() == reflect.Ptr { | |||
| if rv.IsNil() { | |||
| return fmt.Errorf("proto: SetExtension called with nil value of type %T", v) | |||
| } | |||
| if isScalarKind(rv.Elem().Kind()) { | |||
| v = rv.Elem().Interface() | |||
| } | |||
| extensions = append(extensions, desc) | |||
| } | |||
| return extensions, nil | |||
| } | |||
| // SetExtension sets the specified extension of pb to the specified value. | |||
| func SetExtension(pb Message, extension *ExtensionDesc, value interface{}) error { | |||
| epb, err := extendable(pb) | |||
| if err != nil { | |||
| return err | |||
| } | |||
| if err := checkExtensionTypes(epb, extension); err != nil { | |||
| return err | |||
| } | |||
| typ := reflect.TypeOf(extension.ExtensionType) | |||
| if typ != reflect.TypeOf(value) { | |||
| return fmt.Errorf("proto: bad extension value type. got: %T, want: %T", value, extension.ExtensionType) | |||
| xtd := xt.TypeDescriptor() | |||
| if !isValidExtension(mr.Descriptor(), xtd) { | |||
| return fmt.Errorf("proto: bad extended type; %T does not extend %T", xt.ExtendedType, m) | |||
| } | |||
| // nil extension values need to be caught early, because the | |||
| // encoder can't distinguish an ErrNil due to a nil extension | |||
| // from an ErrNil due to a missing field. Extensions are | |||
| // always optional, so the encoder would just swallow the error | |||
| // and drop all the extensions from the encoded message. | |||
| if reflect.ValueOf(value).IsNil() { | |||
| return fmt.Errorf("proto: SetExtension called with nil value of type %T", value) | |||
| } | |||
| extmap := epb.extensionsWrite() | |||
| extmap[extension.Field] = Extension{desc: extension, value: extensionAsStorageType(value)} | |||
| mr.Set(xtd, xt.ValueOf(v)) | |||
| clearUnknown(mr, fieldNum(xt.Field)) | |||
| return nil | |||
| } | |||
| // ClearAllExtensions clears all extensions from pb. | |||
| func ClearAllExtensions(pb Message) { | |||
| epb, err := extendable(pb) | |||
| if err != nil { | |||
| // SetRawExtension inserts b into the unknown fields of m. | |||
| // | |||
| // Deprecated: Use Message.ProtoReflect.SetUnknown instead. | |||
| func SetRawExtension(m Message, fnum int32, b []byte) { | |||
| mr := MessageReflect(m) | |||
| if mr == nil || !mr.IsValid() { | |||
| return | |||
| } | |||
| m := epb.extensionsWrite() | |||
| for k := range m { | |||
| delete(m, k) | |||
| // Verify that the raw field is valid. | |||
| for b0 := b; len(b0) > 0; { | |||
| num, _, n := protowire.ConsumeField(b0) | |||
| if int32(num) != fnum { | |||
| panic(fmt.Sprintf("mismatching field number: got %d, want %d", num, fnum)) | |||
| } | |||
| b0 = b0[n:] | |||
| } | |||
| } | |||
| // A global registry of extensions. | |||
| // The generated code will register the generated descriptors by calling RegisterExtension. | |||
| ClearExtension(m, &ExtensionDesc{Field: fnum}) | |||
| mr.SetUnknown(append(mr.GetUnknown(), b...)) | |||
| } | |||
| var extensionMaps = make(map[reflect.Type]map[int32]*ExtensionDesc) | |||
| // ExtensionDescs returns a list of extension descriptors found in m, | |||
| // containing descriptors for both populated extension fields in m and | |||
| // also unknown fields of m that are in the extension range. | |||
| // For the later case, an type incomplete descriptor is provided where only | |||
| // the ExtensionDesc.Field field is populated. | |||
| // The order of the extension descriptors is undefined. | |||
| func ExtensionDescs(m Message) ([]*ExtensionDesc, error) { | |||
| mr := MessageReflect(m) | |||
| if mr == nil || !mr.IsValid() || mr.Descriptor().ExtensionRanges().Len() == 0 { | |||
| return nil, errNotExtendable | |||
| } | |||
| // RegisterExtension is called from the generated code. | |||
| func RegisterExtension(desc *ExtensionDesc) { | |||
| st := reflect.TypeOf(desc.ExtendedType).Elem() | |||
| m := extensionMaps[st] | |||
| if m == nil { | |||
| m = make(map[int32]*ExtensionDesc) | |||
| extensionMaps[st] = m | |||
| // Collect a set of known extension descriptors. | |||
| extDescs := make(map[protoreflect.FieldNumber]*ExtensionDesc) | |||
| mr.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool { | |||
| if fd.IsExtension() { | |||
| xt := fd.(protoreflect.ExtensionTypeDescriptor) | |||
| if xd, ok := xt.Type().(*ExtensionDesc); ok { | |||
| extDescs[fd.Number()] = xd | |||
| } | |||
| } | |||
| return true | |||
| }) | |||
| // Collect a set of unknown extension descriptors. | |||
| extRanges := mr.Descriptor().ExtensionRanges() | |||
| for b := mr.GetUnknown(); len(b) > 0; { | |||
| num, _, n := protowire.ConsumeField(b) | |||
| if extRanges.Has(num) && extDescs[num] == nil { | |||
| extDescs[num] = nil | |||
| } | |||
| b = b[n:] | |||
| } | |||
| if _, ok := m[desc.Field]; ok { | |||
| panic("proto: duplicate extension registered: " + st.String() + " " + strconv.Itoa(int(desc.Field))) | |||
| // Transpose the set of descriptors into a list. | |||
| var xts []*ExtensionDesc | |||
| for num, xt := range extDescs { | |||
| if xt == nil { | |||
| xt = &ExtensionDesc{Field: int32(num)} | |||
| } | |||
| xts = append(xts, xt) | |||
| } | |||
| m[desc.Field] = desc | |||
| return xts, nil | |||
| } | |||
| // RegisteredExtensions returns a map of the registered extensions of a | |||
| // protocol buffer struct, indexed by the extension number. | |||
| // The argument pb should be a nil pointer to the struct type. | |||
| func RegisteredExtensions(pb Message) map[int32]*ExtensionDesc { | |||
| return extensionMaps[reflect.TypeOf(pb).Elem()] | |||
| // isValidExtension reports whether xtd is a valid extension descriptor for md. | |||
| func isValidExtension(md protoreflect.MessageDescriptor, xtd protoreflect.ExtensionTypeDescriptor) bool { | |||
| return xtd.ContainingMessage() == md && md.ExtensionRanges().Has(xtd.Number()) | |||
| } | |||
| // extensionAsLegacyType converts an value in the storage type as the API type. | |||
| // See Extension.value. | |||
| func extensionAsLegacyType(v interface{}) interface{} { | |||
| switch rv := reflect.ValueOf(v); rv.Kind() { | |||
| // isScalarKind reports whether k is a protobuf scalar kind (except bytes). | |||
| // This function exists for historical reasons since the representation of | |||
| // scalars differs between v1 and v2, where v1 uses *T and v2 uses T. | |||
| func isScalarKind(k reflect.Kind) bool { | |||
| switch k { | |||
| case reflect.Bool, reflect.Int32, reflect.Int64, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64, reflect.String: | |||
| // Represent primitive types as a pointer to the value. | |||
| rv2 := reflect.New(rv.Type()) | |||
| rv2.Elem().Set(rv) | |||
| v = rv2.Interface() | |||
| case reflect.Ptr: | |||
| // Represent slice types as the value itself. | |||
| switch rv.Type().Elem().Kind() { | |||
| case reflect.Slice: | |||
| if rv.IsNil() { | |||
| v = reflect.Zero(rv.Type().Elem()).Interface() | |||
| } else { | |||
| v = rv.Elem().Interface() | |||
| } | |||
| } | |||
| return true | |||
| default: | |||
| return false | |||
| } | |||
| return v | |||
| } | |||
| // extensionAsStorageType converts an value in the API type as the storage type. | |||
| // See Extension.value. | |||
| func extensionAsStorageType(v interface{}) interface{} { | |||
| switch rv := reflect.ValueOf(v); rv.Kind() { | |||
| case reflect.Ptr: | |||
| // Represent slice types as the value itself. | |||
| switch rv.Type().Elem().Kind() { | |||
| case reflect.Bool, reflect.Int32, reflect.Int64, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64, reflect.String: | |||
| if rv.IsNil() { | |||
| v = reflect.Zero(rv.Type().Elem()).Interface() | |||
| } else { | |||
| v = rv.Elem().Interface() | |||
| } | |||
| } | |||
| case reflect.Slice: | |||
| // Represent slice types as a pointer to the value. | |||
| if rv.Type().Elem().Kind() != reflect.Uint8 { | |||
| rv2 := reflect.New(rv.Type()) | |||
| rv2.Elem().Set(rv) | |||
| v = rv2.Interface() | |||
| // clearUnknown removes unknown fields from m where remover.Has reports true. | |||
| func clearUnknown(m protoreflect.Message, remover interface { | |||
| Has(protoreflect.FieldNumber) bool | |||
| }) { | |||
| var bo protoreflect.RawFields | |||
| for bi := m.GetUnknown(); len(bi) > 0; { | |||
| num, _, n := protowire.ConsumeField(bi) | |||
| if !remover.Has(num) { | |||
| bo = append(bo, bi[:n]...) | |||
| } | |||
| bi = bi[n:] | |||
| } | |||
| return v | |||
| if bi := m.GetUnknown(); len(bi) != len(bo) { | |||
| m.SetUnknown(bo) | |||
| } | |||
| } | |||
| type fieldNum protoreflect.FieldNumber | |||
| func (n1 fieldNum) Has(n2 protoreflect.FieldNumber) bool { | |||
| return protoreflect.FieldNumber(n1) == n2 | |||
| } | |||
| @@ -1,965 +0,0 @@ | |||
| // Go support for Protocol Buffers - Google's data interchange format | |||
| // | |||
| // Copyright 2010 The Go Authors. All rights reserved. | |||
| // https://github.com/golang/protobuf | |||
| // | |||
| // Redistribution and use in source and binary forms, with or without | |||
| // modification, are permitted provided that the following conditions are | |||
| // met: | |||
| // | |||
| // * Redistributions of source code must retain the above copyright | |||
| // notice, this list of conditions and the following disclaimer. | |||
| // * Redistributions in binary form must reproduce the above | |||
| // copyright notice, this list of conditions and the following disclaimer | |||
| // in the documentation and/or other materials provided with the | |||
| // distribution. | |||
| // * Neither the name of Google Inc. nor the names of its | |||
| // contributors may be used to endorse or promote products derived from | |||
| // this software without specific prior written permission. | |||
| // | |||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| /* | |||
| Package proto converts data structures to and from the wire format of | |||
| protocol buffers. It works in concert with the Go source code generated | |||
| for .proto files by the protocol compiler. | |||
| A summary of the properties of the protocol buffer interface | |||
| for a protocol buffer variable v: | |||
| - Names are turned from camel_case to CamelCase for export. | |||
| - There are no methods on v to set fields; just treat | |||
| them as structure fields. | |||
| - There are getters that return a field's value if set, | |||
| and return the field's default value if unset. | |||
| The getters work even if the receiver is a nil message. | |||
| - The zero value for a struct is its correct initialization state. | |||
| All desired fields must be set before marshaling. | |||
| - A Reset() method will restore a protobuf struct to its zero state. | |||
| - Non-repeated fields are pointers to the values; nil means unset. | |||
| That is, optional or required field int32 f becomes F *int32. | |||
| - Repeated fields are slices. | |||
| - Helper functions are available to aid the setting of fields. | |||
| msg.Foo = proto.String("hello") // set field | |||
| - Constants are defined to hold the default values of all fields that | |||
| have them. They have the form Default_StructName_FieldName. | |||
| Because the getter methods handle defaulted values, | |||
| direct use of these constants should be rare. | |||
| - Enums are given type names and maps from names to values. | |||
| Enum values are prefixed by the enclosing message's name, or by the | |||
| enum's type name if it is a top-level enum. Enum types have a String | |||
| method, and a Enum method to assist in message construction. | |||
| - Nested messages, groups and enums have type names prefixed with the name of | |||
| the surrounding message type. | |||
| - Extensions are given descriptor names that start with E_, | |||
| followed by an underscore-delimited list of the nested messages | |||
| that contain it (if any) followed by the CamelCased name of the | |||
| extension field itself. HasExtension, ClearExtension, GetExtension | |||
| and SetExtension are functions for manipulating extensions. | |||
| - Oneof field sets are given a single field in their message, | |||
| with distinguished wrapper types for each possible field value. | |||
| - Marshal and Unmarshal are functions to encode and decode the wire format. | |||
| When the .proto file specifies `syntax="proto3"`, there are some differences: | |||
| - Non-repeated fields of non-message type are values instead of pointers. | |||
| - Enum types do not get an Enum method. | |||
| The simplest way to describe this is to see an example. | |||
| Given file test.proto, containing | |||
| package example; | |||
| enum FOO { X = 17; } | |||
| message Test { | |||
| required string label = 1; | |||
| optional int32 type = 2 [default=77]; | |||
| repeated int64 reps = 3; | |||
| optional group OptionalGroup = 4 { | |||
| required string RequiredField = 5; | |||
| } | |||
| oneof union { | |||
| int32 number = 6; | |||
| string name = 7; | |||
| } | |||
| } | |||
| The resulting file, test.pb.go, is: | |||
| package example | |||
| import proto "github.com/golang/protobuf/proto" | |||
| import math "math" | |||
| type FOO int32 | |||
| const ( | |||
| FOO_X FOO = 17 | |||
| ) | |||
| var FOO_name = map[int32]string{ | |||
| 17: "X", | |||
| } | |||
| var FOO_value = map[string]int32{ | |||
| "X": 17, | |||
| } | |||
| func (x FOO) Enum() *FOO { | |||
| p := new(FOO) | |||
| *p = x | |||
| return p | |||
| } | |||
| func (x FOO) String() string { | |||
| return proto.EnumName(FOO_name, int32(x)) | |||
| } | |||
| func (x *FOO) UnmarshalJSON(data []byte) error { | |||
| value, err := proto.UnmarshalJSONEnum(FOO_value, data) | |||
| if err != nil { | |||
| return err | |||
| } | |||
| *x = FOO(value) | |||
| return nil | |||
| } | |||
| type Test struct { | |||
| Label *string `protobuf:"bytes,1,req,name=label" json:"label,omitempty"` | |||
| Type *int32 `protobuf:"varint,2,opt,name=type,def=77" json:"type,omitempty"` | |||
| Reps []int64 `protobuf:"varint,3,rep,name=reps" json:"reps,omitempty"` | |||
| Optionalgroup *Test_OptionalGroup `protobuf:"group,4,opt,name=OptionalGroup" json:"optionalgroup,omitempty"` | |||
| // Types that are valid to be assigned to Union: | |||
| // *Test_Number | |||
| // *Test_Name | |||
| Union isTest_Union `protobuf_oneof:"union"` | |||
| XXX_unrecognized []byte `json:"-"` | |||
| } | |||
| func (m *Test) Reset() { *m = Test{} } | |||
| func (m *Test) String() string { return proto.CompactTextString(m) } | |||
| func (*Test) ProtoMessage() {} | |||
| type isTest_Union interface { | |||
| isTest_Union() | |||
| } | |||
| type Test_Number struct { | |||
| Number int32 `protobuf:"varint,6,opt,name=number"` | |||
| } | |||
| type Test_Name struct { | |||
| Name string `protobuf:"bytes,7,opt,name=name"` | |||
| } | |||
| func (*Test_Number) isTest_Union() {} | |||
| func (*Test_Name) isTest_Union() {} | |||
| func (m *Test) GetUnion() isTest_Union { | |||
| if m != nil { | |||
| return m.Union | |||
| } | |||
| return nil | |||
| } | |||
| const Default_Test_Type int32 = 77 | |||
| func (m *Test) GetLabel() string { | |||
| if m != nil && m.Label != nil { | |||
| return *m.Label | |||
| } | |||
| return "" | |||
| } | |||
| func (m *Test) GetType() int32 { | |||
| if m != nil && m.Type != nil { | |||
| return *m.Type | |||
| } | |||
| return Default_Test_Type | |||
| } | |||
| func (m *Test) GetOptionalgroup() *Test_OptionalGroup { | |||
| if m != nil { | |||
| return m.Optionalgroup | |||
| } | |||
| return nil | |||
| } | |||
| type Test_OptionalGroup struct { | |||
| RequiredField *string `protobuf:"bytes,5,req" json:"RequiredField,omitempty"` | |||
| } | |||
| func (m *Test_OptionalGroup) Reset() { *m = Test_OptionalGroup{} } | |||
| func (m *Test_OptionalGroup) String() string { return proto.CompactTextString(m) } | |||
| func (m *Test_OptionalGroup) GetRequiredField() string { | |||
| if m != nil && m.RequiredField != nil { | |||
| return *m.RequiredField | |||
| } | |||
| return "" | |||
| } | |||
| func (m *Test) GetNumber() int32 { | |||
| if x, ok := m.GetUnion().(*Test_Number); ok { | |||
| return x.Number | |||
| } | |||
| return 0 | |||
| } | |||
| func (m *Test) GetName() string { | |||
| if x, ok := m.GetUnion().(*Test_Name); ok { | |||
| return x.Name | |||
| } | |||
| return "" | |||
| } | |||
| func init() { | |||
| proto.RegisterEnum("example.FOO", FOO_name, FOO_value) | |||
| } | |||
| To create and play with a Test object: | |||
| package main | |||
| import ( | |||
| "log" | |||
| "github.com/golang/protobuf/proto" | |||
| pb "./example.pb" | |||
| ) | |||
| func main() { | |||
| test := &pb.Test{ | |||
| Label: proto.String("hello"), | |||
| Type: proto.Int32(17), | |||
| Reps: []int64{1, 2, 3}, | |||
| Optionalgroup: &pb.Test_OptionalGroup{ | |||
| RequiredField: proto.String("good bye"), | |||
| }, | |||
| Union: &pb.Test_Name{"fred"}, | |||
| } | |||
| data, err := proto.Marshal(test) | |||
| if err != nil { | |||
| log.Fatal("marshaling error: ", err) | |||
| } | |||
| newTest := &pb.Test{} | |||
| err = proto.Unmarshal(data, newTest) | |||
| if err != nil { | |||
| log.Fatal("unmarshaling error: ", err) | |||
| } | |||
| // Now test and newTest contain the same data. | |||
| if test.GetLabel() != newTest.GetLabel() { | |||
| log.Fatalf("data mismatch %q != %q", test.GetLabel(), newTest.GetLabel()) | |||
| } | |||
| // Use a type switch to determine which oneof was set. | |||
| switch u := test.Union.(type) { | |||
| case *pb.Test_Number: // u.Number contains the number. | |||
| case *pb.Test_Name: // u.Name contains the string. | |||
| } | |||
| // etc. | |||
| } | |||
| */ | |||
| package proto | |||
| import ( | |||
| "encoding/json" | |||
| "fmt" | |||
| "log" | |||
| "reflect" | |||
| "sort" | |||
| "strconv" | |||
| "sync" | |||
| ) | |||
| // RequiredNotSetError is an error type returned by either Marshal or Unmarshal. | |||
| // Marshal reports this when a required field is not initialized. | |||
| // Unmarshal reports this when a required field is missing from the wire data. | |||
| type RequiredNotSetError struct{ field string } | |||
| func (e *RequiredNotSetError) Error() string { | |||
| if e.field == "" { | |||
| return fmt.Sprintf("proto: required field not set") | |||
| } | |||
| return fmt.Sprintf("proto: required field %q not set", e.field) | |||
| } | |||
| func (e *RequiredNotSetError) RequiredNotSet() bool { | |||
| return true | |||
| } | |||
| type invalidUTF8Error struct{ field string } | |||
| func (e *invalidUTF8Error) Error() string { | |||
| if e.field == "" { | |||
| return "proto: invalid UTF-8 detected" | |||
| } | |||
| return fmt.Sprintf("proto: field %q contains invalid UTF-8", e.field) | |||
| } | |||
| func (e *invalidUTF8Error) InvalidUTF8() bool { | |||
| return true | |||
| } | |||
| // errInvalidUTF8 is a sentinel error to identify fields with invalid UTF-8. | |||
| // This error should not be exposed to the external API as such errors should | |||
| // be recreated with the field information. | |||
| var errInvalidUTF8 = &invalidUTF8Error{} | |||
| // isNonFatal reports whether the error is either a RequiredNotSet error | |||
| // or a InvalidUTF8 error. | |||
| func isNonFatal(err error) bool { | |||
| if re, ok := err.(interface{ RequiredNotSet() bool }); ok && re.RequiredNotSet() { | |||
| return true | |||
| } | |||
| if re, ok := err.(interface{ InvalidUTF8() bool }); ok && re.InvalidUTF8() { | |||
| return true | |||
| } | |||
| return false | |||
| } | |||
| type nonFatal struct{ E error } | |||
| // Merge merges err into nf and reports whether it was successful. | |||
| // Otherwise it returns false for any fatal non-nil errors. | |||
| func (nf *nonFatal) Merge(err error) (ok bool) { | |||
| if err == nil { | |||
| return true // not an error | |||
| } | |||
| if !isNonFatal(err) { | |||
| return false // fatal error | |||
| } | |||
| if nf.E == nil { | |||
| nf.E = err // store first instance of non-fatal error | |||
| } | |||
| return true | |||
| } | |||
| // Message is implemented by generated protocol buffer messages. | |||
| type Message interface { | |||
| Reset() | |||
| String() string | |||
| ProtoMessage() | |||
| } | |||
| // A Buffer is a buffer manager for marshaling and unmarshaling | |||
| // protocol buffers. It may be reused between invocations to | |||
| // reduce memory usage. It is not necessary to use a Buffer; | |||
| // the global functions Marshal and Unmarshal create a | |||
| // temporary Buffer and are fine for most applications. | |||
| type Buffer struct { | |||
| buf []byte // encode/decode byte stream | |||
| index int // read point | |||
| deterministic bool | |||
| } | |||
| // NewBuffer allocates a new Buffer and initializes its internal data to | |||
| // the contents of the argument slice. | |||
| func NewBuffer(e []byte) *Buffer { | |||
| return &Buffer{buf: e} | |||
| } | |||
| // Reset resets the Buffer, ready for marshaling a new protocol buffer. | |||
| func (p *Buffer) Reset() { | |||
| p.buf = p.buf[0:0] // for reading/writing | |||
| p.index = 0 // for reading | |||
| } | |||
| // SetBuf replaces the internal buffer with the slice, | |||
| // ready for unmarshaling the contents of the slice. | |||
| func (p *Buffer) SetBuf(s []byte) { | |||
| p.buf = s | |||
| p.index = 0 | |||
| } | |||
| // Bytes returns the contents of the Buffer. | |||
| func (p *Buffer) Bytes() []byte { return p.buf } | |||
| // SetDeterministic sets whether to use deterministic serialization. | |||
| // | |||
| // Deterministic serialization guarantees that for a given binary, equal | |||
| // messages will always be serialized to the same bytes. This implies: | |||
| // | |||
| // - Repeated serialization of a message will return the same bytes. | |||
| // - Different processes of the same binary (which may be executing on | |||
| // different machines) will serialize equal messages to the same bytes. | |||
| // | |||
| // Note that the deterministic serialization is NOT canonical across | |||
| // languages. It is not guaranteed to remain stable over time. It is unstable | |||
| // across different builds with schema changes due to unknown fields. | |||
| // Users who need canonical serialization (e.g., persistent storage in a | |||
| // canonical form, fingerprinting, etc.) should define their own | |||
| // canonicalization specification and implement their own serializer rather | |||
| // than relying on this API. | |||
| // | |||
| // If deterministic serialization is requested, map entries will be sorted | |||
| // by keys in lexicographical order. This is an implementation detail and | |||
| // subject to change. | |||
| func (p *Buffer) SetDeterministic(deterministic bool) { | |||
| p.deterministic = deterministic | |||
| } | |||
| /* | |||
| * Helper routines for simplifying the creation of optional fields of basic type. | |||
| */ | |||
| // Bool is a helper routine that allocates a new bool value | |||
| // to store v and returns a pointer to it. | |||
| func Bool(v bool) *bool { | |||
| return &v | |||
| } | |||
| // Int32 is a helper routine that allocates a new int32 value | |||
| // to store v and returns a pointer to it. | |||
| func Int32(v int32) *int32 { | |||
| return &v | |||
| } | |||
| // Int is a helper routine that allocates a new int32 value | |||
| // to store v and returns a pointer to it, but unlike Int32 | |||
| // its argument value is an int. | |||
| func Int(v int) *int32 { | |||
| p := new(int32) | |||
| *p = int32(v) | |||
| return p | |||
| } | |||
| // Int64 is a helper routine that allocates a new int64 value | |||
| // to store v and returns a pointer to it. | |||
| func Int64(v int64) *int64 { | |||
| return &v | |||
| } | |||
| // Float32 is a helper routine that allocates a new float32 value | |||
| // to store v and returns a pointer to it. | |||
| func Float32(v float32) *float32 { | |||
| return &v | |||
| } | |||
| // Float64 is a helper routine that allocates a new float64 value | |||
| // to store v and returns a pointer to it. | |||
| func Float64(v float64) *float64 { | |||
| return &v | |||
| } | |||
| // Uint32 is a helper routine that allocates a new uint32 value | |||
| // to store v and returns a pointer to it. | |||
| func Uint32(v uint32) *uint32 { | |||
| return &v | |||
| } | |||
| // Uint64 is a helper routine that allocates a new uint64 value | |||
| // to store v and returns a pointer to it. | |||
| func Uint64(v uint64) *uint64 { | |||
| return &v | |||
| } | |||
| // String is a helper routine that allocates a new string value | |||
| // to store v and returns a pointer to it. | |||
| func String(v string) *string { | |||
| return &v | |||
| } | |||
| // EnumName is a helper function to simplify printing protocol buffer enums | |||
| // by name. Given an enum map and a value, it returns a useful string. | |||
| func EnumName(m map[int32]string, v int32) string { | |||
| s, ok := m[v] | |||
| if ok { | |||
| return s | |||
| } | |||
| return strconv.Itoa(int(v)) | |||
| } | |||
| // UnmarshalJSONEnum is a helper function to simplify recovering enum int values | |||
| // from their JSON-encoded representation. Given a map from the enum's symbolic | |||
| // names to its int values, and a byte buffer containing the JSON-encoded | |||
| // value, it returns an int32 that can be cast to the enum type by the caller. | |||
| // | |||
| // The function can deal with both JSON representations, numeric and symbolic. | |||
| func UnmarshalJSONEnum(m map[string]int32, data []byte, enumName string) (int32, error) { | |||
| if data[0] == '"' { | |||
| // New style: enums are strings. | |||
| var repr string | |||
| if err := json.Unmarshal(data, &repr); err != nil { | |||
| return -1, err | |||
| } | |||
| val, ok := m[repr] | |||
| if !ok { | |||
| return 0, fmt.Errorf("unrecognized enum %s value %q", enumName, repr) | |||
| } | |||
| return val, nil | |||
| } | |||
| // Old style: enums are ints. | |||
| var val int32 | |||
| if err := json.Unmarshal(data, &val); err != nil { | |||
| return 0, fmt.Errorf("cannot unmarshal %#q into enum %s", data, enumName) | |||
| } | |||
| return val, nil | |||
| } | |||
| // DebugPrint dumps the encoded data in b in a debugging format with a header | |||
| // including the string s. Used in testing but made available for general debugging. | |||
| func (p *Buffer) DebugPrint(s string, b []byte) { | |||
| var u uint64 | |||
| obuf := p.buf | |||
| index := p.index | |||
| p.buf = b | |||
| p.index = 0 | |||
| depth := 0 | |||
| fmt.Printf("\n--- %s ---\n", s) | |||
| out: | |||
| for { | |||
| for i := 0; i < depth; i++ { | |||
| fmt.Print(" ") | |||
| } | |||
| index := p.index | |||
| if index == len(p.buf) { | |||
| break | |||
| } | |||
| op, err := p.DecodeVarint() | |||
| if err != nil { | |||
| fmt.Printf("%3d: fetching op err %v\n", index, err) | |||
| break out | |||
| } | |||
| tag := op >> 3 | |||
| wire := op & 7 | |||
| switch wire { | |||
| default: | |||
| fmt.Printf("%3d: t=%3d unknown wire=%d\n", | |||
| index, tag, wire) | |||
| break out | |||
| case WireBytes: | |||
| var r []byte | |||
| r, err = p.DecodeRawBytes(false) | |||
| if err != nil { | |||
| break out | |||
| } | |||
| fmt.Printf("%3d: t=%3d bytes [%d]", index, tag, len(r)) | |||
| if len(r) <= 6 { | |||
| for i := 0; i < len(r); i++ { | |||
| fmt.Printf(" %.2x", r[i]) | |||
| } | |||
| } else { | |||
| for i := 0; i < 3; i++ { | |||
| fmt.Printf(" %.2x", r[i]) | |||
| } | |||
| fmt.Printf(" ..") | |||
| for i := len(r) - 3; i < len(r); i++ { | |||
| fmt.Printf(" %.2x", r[i]) | |||
| } | |||
| } | |||
| fmt.Printf("\n") | |||
| case WireFixed32: | |||
| u, err = p.DecodeFixed32() | |||
| if err != nil { | |||
| fmt.Printf("%3d: t=%3d fix32 err %v\n", index, tag, err) | |||
| break out | |||
| } | |||
| fmt.Printf("%3d: t=%3d fix32 %d\n", index, tag, u) | |||
| case WireFixed64: | |||
| u, err = p.DecodeFixed64() | |||
| if err != nil { | |||
| fmt.Printf("%3d: t=%3d fix64 err %v\n", index, tag, err) | |||
| break out | |||
| } | |||
| fmt.Printf("%3d: t=%3d fix64 %d\n", index, tag, u) | |||
| case WireVarint: | |||
| u, err = p.DecodeVarint() | |||
| if err != nil { | |||
| fmt.Printf("%3d: t=%3d varint err %v\n", index, tag, err) | |||
| break out | |||
| } | |||
| fmt.Printf("%3d: t=%3d varint %d\n", index, tag, u) | |||
| case WireStartGroup: | |||
| fmt.Printf("%3d: t=%3d start\n", index, tag) | |||
| depth++ | |||
| case WireEndGroup: | |||
| depth-- | |||
| fmt.Printf("%3d: t=%3d end\n", index, tag) | |||
| } | |||
| } | |||
| if depth != 0 { | |||
| fmt.Printf("%3d: start-end not balanced %d\n", p.index, depth) | |||
| } | |||
| fmt.Printf("\n") | |||
| p.buf = obuf | |||
| p.index = index | |||
| } | |||
| // SetDefaults sets unset protocol buffer fields to their default values. | |||
| // It only modifies fields that are both unset and have defined defaults. | |||
| // It recursively sets default values in any non-nil sub-messages. | |||
| func SetDefaults(pb Message) { | |||
| setDefaults(reflect.ValueOf(pb), true, false) | |||
| } | |||
| // v is a pointer to a struct. | |||
| func setDefaults(v reflect.Value, recur, zeros bool) { | |||
| v = v.Elem() | |||
| defaultMu.RLock() | |||
| dm, ok := defaults[v.Type()] | |||
| defaultMu.RUnlock() | |||
| if !ok { | |||
| dm = buildDefaultMessage(v.Type()) | |||
| defaultMu.Lock() | |||
| defaults[v.Type()] = dm | |||
| defaultMu.Unlock() | |||
| } | |||
| for _, sf := range dm.scalars { | |||
| f := v.Field(sf.index) | |||
| if !f.IsNil() { | |||
| // field already set | |||
| continue | |||
| } | |||
| dv := sf.value | |||
| if dv == nil && !zeros { | |||
| // no explicit default, and don't want to set zeros | |||
| continue | |||
| } | |||
| fptr := f.Addr().Interface() // **T | |||
| // TODO: Consider batching the allocations we do here. | |||
| switch sf.kind { | |||
| case reflect.Bool: | |||
| b := new(bool) | |||
| if dv != nil { | |||
| *b = dv.(bool) | |||
| } | |||
| *(fptr.(**bool)) = b | |||
| case reflect.Float32: | |||
| f := new(float32) | |||
| if dv != nil { | |||
| *f = dv.(float32) | |||
| } | |||
| *(fptr.(**float32)) = f | |||
| case reflect.Float64: | |||
| f := new(float64) | |||
| if dv != nil { | |||
| *f = dv.(float64) | |||
| } | |||
| *(fptr.(**float64)) = f | |||
| case reflect.Int32: | |||
| // might be an enum | |||
| if ft := f.Type(); ft != int32PtrType { | |||
| // enum | |||
| f.Set(reflect.New(ft.Elem())) | |||
| if dv != nil { | |||
| f.Elem().SetInt(int64(dv.(int32))) | |||
| } | |||
| } else { | |||
| // int32 field | |||
| i := new(int32) | |||
| if dv != nil { | |||
| *i = dv.(int32) | |||
| } | |||
| *(fptr.(**int32)) = i | |||
| } | |||
| case reflect.Int64: | |||
| i := new(int64) | |||
| if dv != nil { | |||
| *i = dv.(int64) | |||
| } | |||
| *(fptr.(**int64)) = i | |||
| case reflect.String: | |||
| s := new(string) | |||
| if dv != nil { | |||
| *s = dv.(string) | |||
| } | |||
| *(fptr.(**string)) = s | |||
| case reflect.Uint8: | |||
| // exceptional case: []byte | |||
| var b []byte | |||
| if dv != nil { | |||
| db := dv.([]byte) | |||
| b = make([]byte, len(db)) | |||
| copy(b, db) | |||
| } else { | |||
| b = []byte{} | |||
| } | |||
| *(fptr.(*[]byte)) = b | |||
| case reflect.Uint32: | |||
| u := new(uint32) | |||
| if dv != nil { | |||
| *u = dv.(uint32) | |||
| } | |||
| *(fptr.(**uint32)) = u | |||
| case reflect.Uint64: | |||
| u := new(uint64) | |||
| if dv != nil { | |||
| *u = dv.(uint64) | |||
| } | |||
| *(fptr.(**uint64)) = u | |||
| default: | |||
| log.Printf("proto: can't set default for field %v (sf.kind=%v)", f, sf.kind) | |||
| } | |||
| } | |||
| for _, ni := range dm.nested { | |||
| f := v.Field(ni) | |||
| // f is *T or []*T or map[T]*T | |||
| switch f.Kind() { | |||
| case reflect.Ptr: | |||
| if f.IsNil() { | |||
| continue | |||
| } | |||
| setDefaults(f, recur, zeros) | |||
| case reflect.Slice: | |||
| for i := 0; i < f.Len(); i++ { | |||
| e := f.Index(i) | |||
| if e.IsNil() { | |||
| continue | |||
| } | |||
| setDefaults(e, recur, zeros) | |||
| } | |||
| case reflect.Map: | |||
| for _, k := range f.MapKeys() { | |||
| e := f.MapIndex(k) | |||
| if e.IsNil() { | |||
| continue | |||
| } | |||
| setDefaults(e, recur, zeros) | |||
| } | |||
| } | |||
| } | |||
| } | |||
| var ( | |||
| // defaults maps a protocol buffer struct type to a slice of the fields, | |||
| // with its scalar fields set to their proto-declared non-zero default values. | |||
| defaultMu sync.RWMutex | |||
| defaults = make(map[reflect.Type]defaultMessage) | |||
| int32PtrType = reflect.TypeOf((*int32)(nil)) | |||
| ) | |||
| // defaultMessage represents information about the default values of a message. | |||
| type defaultMessage struct { | |||
| scalars []scalarField | |||
| nested []int // struct field index of nested messages | |||
| } | |||
| type scalarField struct { | |||
| index int // struct field index | |||
| kind reflect.Kind // element type (the T in *T or []T) | |||
| value interface{} // the proto-declared default value, or nil | |||
| } | |||
| // t is a struct type. | |||
| func buildDefaultMessage(t reflect.Type) (dm defaultMessage) { | |||
| sprop := GetProperties(t) | |||
| for _, prop := range sprop.Prop { | |||
| fi, ok := sprop.decoderTags.get(prop.Tag) | |||
| if !ok { | |||
| // XXX_unrecognized | |||
| continue | |||
| } | |||
| ft := t.Field(fi).Type | |||
| sf, nested, err := fieldDefault(ft, prop) | |||
| switch { | |||
| case err != nil: | |||
| log.Print(err) | |||
| case nested: | |||
| dm.nested = append(dm.nested, fi) | |||
| case sf != nil: | |||
| sf.index = fi | |||
| dm.scalars = append(dm.scalars, *sf) | |||
| } | |||
| } | |||
| return dm | |||
| } | |||
| // fieldDefault returns the scalarField for field type ft. | |||
| // sf will be nil if the field can not have a default. | |||
| // nestedMessage will be true if this is a nested message. | |||
| // Note that sf.index is not set on return. | |||
| func fieldDefault(ft reflect.Type, prop *Properties) (sf *scalarField, nestedMessage bool, err error) { | |||
| var canHaveDefault bool | |||
| switch ft.Kind() { | |||
| case reflect.Ptr: | |||
| if ft.Elem().Kind() == reflect.Struct { | |||
| nestedMessage = true | |||
| } else { | |||
| canHaveDefault = true // proto2 scalar field | |||
| } | |||
| case reflect.Slice: | |||
| switch ft.Elem().Kind() { | |||
| case reflect.Ptr: | |||
| nestedMessage = true // repeated message | |||
| case reflect.Uint8: | |||
| canHaveDefault = true // bytes field | |||
| } | |||
| case reflect.Map: | |||
| if ft.Elem().Kind() == reflect.Ptr { | |||
| nestedMessage = true // map with message values | |||
| } | |||
| } | |||
| if !canHaveDefault { | |||
| if nestedMessage { | |||
| return nil, true, nil | |||
| } | |||
| return nil, false, nil | |||
| } | |||
| // We now know that ft is a pointer or slice. | |||
| sf = &scalarField{kind: ft.Elem().Kind()} | |||
| // scalar fields without defaults | |||
| if !prop.HasDefault { | |||
| return sf, false, nil | |||
| } | |||
| // a scalar field: either *T or []byte | |||
| switch ft.Elem().Kind() { | |||
| case reflect.Bool: | |||
| x, err := strconv.ParseBool(prop.Default) | |||
| if err != nil { | |||
| return nil, false, fmt.Errorf("proto: bad default bool %q: %v", prop.Default, err) | |||
| } | |||
| sf.value = x | |||
| case reflect.Float32: | |||
| x, err := strconv.ParseFloat(prop.Default, 32) | |||
| if err != nil { | |||
| return nil, false, fmt.Errorf("proto: bad default float32 %q: %v", prop.Default, err) | |||
| } | |||
| sf.value = float32(x) | |||
| case reflect.Float64: | |||
| x, err := strconv.ParseFloat(prop.Default, 64) | |||
| if err != nil { | |||
| return nil, false, fmt.Errorf("proto: bad default float64 %q: %v", prop.Default, err) | |||
| } | |||
| sf.value = x | |||
| case reflect.Int32: | |||
| x, err := strconv.ParseInt(prop.Default, 10, 32) | |||
| if err != nil { | |||
| return nil, false, fmt.Errorf("proto: bad default int32 %q: %v", prop.Default, err) | |||
| } | |||
| sf.value = int32(x) | |||
| case reflect.Int64: | |||
| x, err := strconv.ParseInt(prop.Default, 10, 64) | |||
| if err != nil { | |||
| return nil, false, fmt.Errorf("proto: bad default int64 %q: %v", prop.Default, err) | |||
| } | |||
| sf.value = x | |||
| case reflect.String: | |||
| sf.value = prop.Default | |||
| case reflect.Uint8: | |||
| // []byte (not *uint8) | |||
| sf.value = []byte(prop.Default) | |||
| case reflect.Uint32: | |||
| x, err := strconv.ParseUint(prop.Default, 10, 32) | |||
| if err != nil { | |||
| return nil, false, fmt.Errorf("proto: bad default uint32 %q: %v", prop.Default, err) | |||
| } | |||
| sf.value = uint32(x) | |||
| case reflect.Uint64: | |||
| x, err := strconv.ParseUint(prop.Default, 10, 64) | |||
| if err != nil { | |||
| return nil, false, fmt.Errorf("proto: bad default uint64 %q: %v", prop.Default, err) | |||
| } | |||
| sf.value = x | |||
| default: | |||
| return nil, false, fmt.Errorf("proto: unhandled def kind %v", ft.Elem().Kind()) | |||
| } | |||
| return sf, false, nil | |||
| } | |||
| // mapKeys returns a sort.Interface to be used for sorting the map keys. | |||
| // Map fields may have key types of non-float scalars, strings and enums. | |||
| func mapKeys(vs []reflect.Value) sort.Interface { | |||
| s := mapKeySorter{vs: vs} | |||
| // Type specialization per https://developers.google.com/protocol-buffers/docs/proto#maps. | |||
| if len(vs) == 0 { | |||
| return s | |||
| } | |||
| switch vs[0].Kind() { | |||
| case reflect.Int32, reflect.Int64: | |||
| s.less = func(a, b reflect.Value) bool { return a.Int() < b.Int() } | |||
| case reflect.Uint32, reflect.Uint64: | |||
| s.less = func(a, b reflect.Value) bool { return a.Uint() < b.Uint() } | |||
| case reflect.Bool: | |||
| s.less = func(a, b reflect.Value) bool { return !a.Bool() && b.Bool() } // false < true | |||
| case reflect.String: | |||
| s.less = func(a, b reflect.Value) bool { return a.String() < b.String() } | |||
| default: | |||
| panic(fmt.Sprintf("unsupported map key type: %v", vs[0].Kind())) | |||
| } | |||
| return s | |||
| } | |||
| type mapKeySorter struct { | |||
| vs []reflect.Value | |||
| less func(a, b reflect.Value) bool | |||
| } | |||
| func (s mapKeySorter) Len() int { return len(s.vs) } | |||
| func (s mapKeySorter) Swap(i, j int) { s.vs[i], s.vs[j] = s.vs[j], s.vs[i] } | |||
| func (s mapKeySorter) Less(i, j int) bool { | |||
| return s.less(s.vs[i], s.vs[j]) | |||
| } | |||
| // isProto3Zero reports whether v is a zero proto3 value. | |||
| func isProto3Zero(v reflect.Value) bool { | |||
| switch v.Kind() { | |||
| case reflect.Bool: | |||
| return !v.Bool() | |||
| case reflect.Int32, reflect.Int64: | |||
| return v.Int() == 0 | |||
| case reflect.Uint32, reflect.Uint64: | |||
| return v.Uint() == 0 | |||
| case reflect.Float32, reflect.Float64: | |||
| return v.Float() == 0 | |||
| case reflect.String: | |||
| return v.String() == "" | |||
| } | |||
| return false | |||
| } | |||
| const ( | |||
| // ProtoPackageIsVersion3 is referenced from generated protocol buffer files | |||
| // to assert that that code is compatible with this version of the proto package. | |||
| ProtoPackageIsVersion3 = true | |||
| // ProtoPackageIsVersion2 is referenced from generated protocol buffer files | |||
| // to assert that that code is compatible with this version of the proto package. | |||
| ProtoPackageIsVersion2 = true | |||
| // ProtoPackageIsVersion1 is referenced from generated protocol buffer files | |||
| // to assert that that code is compatible with this version of the proto package. | |||
| ProtoPackageIsVersion1 = true | |||
| ) | |||
| // InternalMessageInfo is a type used internally by generated .pb.go files. | |||
| // This type is not intended to be used by non-generated code. | |||
| // This type is not subject to any compatibility guarantee. | |||
| type InternalMessageInfo struct { | |||
| marshal *marshalInfo | |||
| unmarshal *unmarshalInfo | |||
| merge *mergeInfo | |||
| discard *discardInfo | |||
| } | |||
| @@ -1,181 +0,0 @@ | |||
| // Go support for Protocol Buffers - Google's data interchange format | |||
| // | |||
| // Copyright 2010 The Go Authors. All rights reserved. | |||
| // https://github.com/golang/protobuf | |||
| // | |||
| // Redistribution and use in source and binary forms, with or without | |||
| // modification, are permitted provided that the following conditions are | |||
| // met: | |||
| // | |||
| // * Redistributions of source code must retain the above copyright | |||
| // notice, this list of conditions and the following disclaimer. | |||
| // * Redistributions in binary form must reproduce the above | |||
| // copyright notice, this list of conditions and the following disclaimer | |||
| // in the documentation and/or other materials provided with the | |||
| // distribution. | |||
| // * Neither the name of Google Inc. nor the names of its | |||
| // contributors may be used to endorse or promote products derived from | |||
| // this software without specific prior written permission. | |||
| // | |||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| package proto | |||
| /* | |||
| * Support for message sets. | |||
| */ | |||
| import ( | |||
| "errors" | |||
| ) | |||
| // errNoMessageTypeID occurs when a protocol buffer does not have a message type ID. | |||
| // A message type ID is required for storing a protocol buffer in a message set. | |||
| var errNoMessageTypeID = errors.New("proto does not have a message type ID") | |||
| // The first two types (_MessageSet_Item and messageSet) | |||
| // model what the protocol compiler produces for the following protocol message: | |||
| // message MessageSet { | |||
| // repeated group Item = 1 { | |||
| // required int32 type_id = 2; | |||
| // required string message = 3; | |||
| // }; | |||
| // } | |||
| // That is the MessageSet wire format. We can't use a proto to generate these | |||
| // because that would introduce a circular dependency between it and this package. | |||
| type _MessageSet_Item struct { | |||
| TypeId *int32 `protobuf:"varint,2,req,name=type_id"` | |||
| Message []byte `protobuf:"bytes,3,req,name=message"` | |||
| } | |||
| type messageSet struct { | |||
| Item []*_MessageSet_Item `protobuf:"group,1,rep"` | |||
| XXX_unrecognized []byte | |||
| // TODO: caching? | |||
| } | |||
| // Make sure messageSet is a Message. | |||
| var _ Message = (*messageSet)(nil) | |||
| // messageTypeIder is an interface satisfied by a protocol buffer type | |||
| // that may be stored in a MessageSet. | |||
| type messageTypeIder interface { | |||
| MessageTypeId() int32 | |||
| } | |||
| func (ms *messageSet) find(pb Message) *_MessageSet_Item { | |||
| mti, ok := pb.(messageTypeIder) | |||
| if !ok { | |||
| return nil | |||
| } | |||
| id := mti.MessageTypeId() | |||
| for _, item := range ms.Item { | |||
| if *item.TypeId == id { | |||
| return item | |||
| } | |||
| } | |||
| return nil | |||
| } | |||
| func (ms *messageSet) Has(pb Message) bool { | |||
| return ms.find(pb) != nil | |||
| } | |||
| func (ms *messageSet) Unmarshal(pb Message) error { | |||
| if item := ms.find(pb); item != nil { | |||
| return Unmarshal(item.Message, pb) | |||
| } | |||
| if _, ok := pb.(messageTypeIder); !ok { | |||
| return errNoMessageTypeID | |||
| } | |||
| return nil // TODO: return error instead? | |||
| } | |||
| func (ms *messageSet) Marshal(pb Message) error { | |||
| msg, err := Marshal(pb) | |||
| if err != nil { | |||
| return err | |||
| } | |||
| if item := ms.find(pb); item != nil { | |||
| // reuse existing item | |||
| item.Message = msg | |||
| return nil | |||
| } | |||
| mti, ok := pb.(messageTypeIder) | |||
| if !ok { | |||
| return errNoMessageTypeID | |||
| } | |||
| mtid := mti.MessageTypeId() | |||
| ms.Item = append(ms.Item, &_MessageSet_Item{ | |||
| TypeId: &mtid, | |||
| Message: msg, | |||
| }) | |||
| return nil | |||
| } | |||
| func (ms *messageSet) Reset() { *ms = messageSet{} } | |||
| func (ms *messageSet) String() string { return CompactTextString(ms) } | |||
| func (*messageSet) ProtoMessage() {} | |||
| // Support for the message_set_wire_format message option. | |||
| func skipVarint(buf []byte) []byte { | |||
| i := 0 | |||
| for ; buf[i]&0x80 != 0; i++ { | |||
| } | |||
| return buf[i+1:] | |||
| } | |||
| // unmarshalMessageSet decodes the extension map encoded in buf in the message set wire format. | |||
| // It is called by Unmarshal methods on protocol buffer messages with the message_set_wire_format option. | |||
| func unmarshalMessageSet(buf []byte, exts interface{}) error { | |||
| var m map[int32]Extension | |||
| switch exts := exts.(type) { | |||
| case *XXX_InternalExtensions: | |||
| m = exts.extensionsWrite() | |||
| case map[int32]Extension: | |||
| m = exts | |||
| default: | |||
| return errors.New("proto: not an extension map") | |||
| } | |||
| ms := new(messageSet) | |||
| if err := Unmarshal(buf, ms); err != nil { | |||
| return err | |||
| } | |||
| for _, item := range ms.Item { | |||
| id := *item.TypeId | |||
| msg := item.Message | |||
| // Restore wire type and field number varint, plus length varint. | |||
| // Be careful to preserve duplicate items. | |||
| b := EncodeVarint(uint64(id)<<3 | WireBytes) | |||
| if ext, ok := m[id]; ok { | |||
| // Existing data; rip off the tag and length varint | |||
| // so we join the new data correctly. | |||
| // We can assume that ext.enc is set because we are unmarshaling. | |||
| o := ext.enc[len(b):] // skip wire type and field number | |||
| _, n := DecodeVarint(o) // calculate length of length varint | |||
| o = o[n:] // skip length varint | |||
| msg = append(o, msg...) // join old data and new data | |||
| } | |||
| b = append(b, EncodeVarint(uint64(len(msg)))...) | |||
| b = append(b, msg...) | |||
| m[id] = Extension{enc: b} | |||
| } | |||
| return nil | |||
| } | |||
| @@ -1,360 +0,0 @@ | |||
| // Go support for Protocol Buffers - Google's data interchange format | |||
| // | |||
| // Copyright 2012 The Go Authors. All rights reserved. | |||
| // https://github.com/golang/protobuf | |||
| // | |||
| // Redistribution and use in source and binary forms, with or without | |||
| // modification, are permitted provided that the following conditions are | |||
| // met: | |||
| // | |||
| // * Redistributions of source code must retain the above copyright | |||
| // notice, this list of conditions and the following disclaimer. | |||
| // * Redistributions in binary form must reproduce the above | |||
| // copyright notice, this list of conditions and the following disclaimer | |||
| // in the documentation and/or other materials provided with the | |||
| // distribution. | |||
| // * Neither the name of Google Inc. nor the names of its | |||
| // contributors may be used to endorse or promote products derived from | |||
| // this software without specific prior written permission. | |||
| // | |||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| // +build purego appengine js | |||
| // This file contains an implementation of proto field accesses using package reflect. | |||
| // It is slower than the code in pointer_unsafe.go but it avoids package unsafe and can | |||
| // be used on App Engine. | |||
| package proto | |||
| import ( | |||
| "reflect" | |||
| "sync" | |||
| ) | |||
| const unsafeAllowed = false | |||
| // A field identifies a field in a struct, accessible from a pointer. | |||
| // In this implementation, a field is identified by the sequence of field indices | |||
| // passed to reflect's FieldByIndex. | |||
| type field []int | |||
| // toField returns a field equivalent to the given reflect field. | |||
| func toField(f *reflect.StructField) field { | |||
| return f.Index | |||
| } | |||
| // invalidField is an invalid field identifier. | |||
| var invalidField = field(nil) | |||
| // zeroField is a noop when calling pointer.offset. | |||
| var zeroField = field([]int{}) | |||
| // IsValid reports whether the field identifier is valid. | |||
| func (f field) IsValid() bool { return f != nil } | |||
| // The pointer type is for the table-driven decoder. | |||
| // The implementation here uses a reflect.Value of pointer type to | |||
| // create a generic pointer. In pointer_unsafe.go we use unsafe | |||
| // instead of reflect to implement the same (but faster) interface. | |||
| type pointer struct { | |||
| v reflect.Value | |||
| } | |||
| // toPointer converts an interface of pointer type to a pointer | |||
| // that points to the same target. | |||
| func toPointer(i *Message) pointer { | |||
| return pointer{v: reflect.ValueOf(*i)} | |||
| } | |||
| // toAddrPointer converts an interface to a pointer that points to | |||
| // the interface data. | |||
| func toAddrPointer(i *interface{}, isptr, deref bool) pointer { | |||
| v := reflect.ValueOf(*i) | |||
| u := reflect.New(v.Type()) | |||
| u.Elem().Set(v) | |||
| if deref { | |||
| u = u.Elem() | |||
| } | |||
| return pointer{v: u} | |||
| } | |||
| // valToPointer converts v to a pointer. v must be of pointer type. | |||
| func valToPointer(v reflect.Value) pointer { | |||
| return pointer{v: v} | |||
| } | |||
| // offset converts from a pointer to a structure to a pointer to | |||
| // one of its fields. | |||
| func (p pointer) offset(f field) pointer { | |||
| return pointer{v: p.v.Elem().FieldByIndex(f).Addr()} | |||
| } | |||
| func (p pointer) isNil() bool { | |||
| return p.v.IsNil() | |||
| } | |||
| // grow updates the slice s in place to make it one element longer. | |||
| // s must be addressable. | |||
| // Returns the (addressable) new element. | |||
| func grow(s reflect.Value) reflect.Value { | |||
| n, m := s.Len(), s.Cap() | |||
| if n < m { | |||
| s.SetLen(n + 1) | |||
| } else { | |||
| s.Set(reflect.Append(s, reflect.Zero(s.Type().Elem()))) | |||
| } | |||
| return s.Index(n) | |||
| } | |||
| func (p pointer) toInt64() *int64 { | |||
| return p.v.Interface().(*int64) | |||
| } | |||
| func (p pointer) toInt64Ptr() **int64 { | |||
| return p.v.Interface().(**int64) | |||
| } | |||
| func (p pointer) toInt64Slice() *[]int64 { | |||
| return p.v.Interface().(*[]int64) | |||
| } | |||
| var int32ptr = reflect.TypeOf((*int32)(nil)) | |||
| func (p pointer) toInt32() *int32 { | |||
| return p.v.Convert(int32ptr).Interface().(*int32) | |||
| } | |||
| // The toInt32Ptr/Slice methods don't work because of enums. | |||
| // Instead, we must use set/get methods for the int32ptr/slice case. | |||
| /* | |||
| func (p pointer) toInt32Ptr() **int32 { | |||
| return p.v.Interface().(**int32) | |||
| } | |||
| func (p pointer) toInt32Slice() *[]int32 { | |||
| return p.v.Interface().(*[]int32) | |||
| } | |||
| */ | |||
| func (p pointer) getInt32Ptr() *int32 { | |||
| if p.v.Type().Elem().Elem() == reflect.TypeOf(int32(0)) { | |||
| // raw int32 type | |||
| return p.v.Elem().Interface().(*int32) | |||
| } | |||
| // an enum | |||
| return p.v.Elem().Convert(int32PtrType).Interface().(*int32) | |||
| } | |||
| func (p pointer) setInt32Ptr(v int32) { | |||
| // Allocate value in a *int32. Possibly convert that to a *enum. | |||
| // Then assign it to a **int32 or **enum. | |||
| // Note: we can convert *int32 to *enum, but we can't convert | |||
| // **int32 to **enum! | |||
| p.v.Elem().Set(reflect.ValueOf(&v).Convert(p.v.Type().Elem())) | |||
| } | |||
| // getInt32Slice copies []int32 from p as a new slice. | |||
| // This behavior differs from the implementation in pointer_unsafe.go. | |||
| func (p pointer) getInt32Slice() []int32 { | |||
| if p.v.Type().Elem().Elem() == reflect.TypeOf(int32(0)) { | |||
| // raw int32 type | |||
| return p.v.Elem().Interface().([]int32) | |||
| } | |||
| // an enum | |||
| // Allocate a []int32, then assign []enum's values into it. | |||
| // Note: we can't convert []enum to []int32. | |||
| slice := p.v.Elem() | |||
| s := make([]int32, slice.Len()) | |||
| for i := 0; i < slice.Len(); i++ { | |||
| s[i] = int32(slice.Index(i).Int()) | |||
| } | |||
| return s | |||
| } | |||
| // setInt32Slice copies []int32 into p as a new slice. | |||
| // This behavior differs from the implementation in pointer_unsafe.go. | |||
| func (p pointer) setInt32Slice(v []int32) { | |||
| if p.v.Type().Elem().Elem() == reflect.TypeOf(int32(0)) { | |||
| // raw int32 type | |||
| p.v.Elem().Set(reflect.ValueOf(v)) | |||
| return | |||
| } | |||
| // an enum | |||
| // Allocate a []enum, then assign []int32's values into it. | |||
| // Note: we can't convert []enum to []int32. | |||
| slice := reflect.MakeSlice(p.v.Type().Elem(), len(v), cap(v)) | |||
| for i, x := range v { | |||
| slice.Index(i).SetInt(int64(x)) | |||
| } | |||
| p.v.Elem().Set(slice) | |||
| } | |||
| func (p pointer) appendInt32Slice(v int32) { | |||
| grow(p.v.Elem()).SetInt(int64(v)) | |||
| } | |||
| func (p pointer) toUint64() *uint64 { | |||
| return p.v.Interface().(*uint64) | |||
| } | |||
| func (p pointer) toUint64Ptr() **uint64 { | |||
| return p.v.Interface().(**uint64) | |||
| } | |||
| func (p pointer) toUint64Slice() *[]uint64 { | |||
| return p.v.Interface().(*[]uint64) | |||
| } | |||
| func (p pointer) toUint32() *uint32 { | |||
| return p.v.Interface().(*uint32) | |||
| } | |||
| func (p pointer) toUint32Ptr() **uint32 { | |||
| return p.v.Interface().(**uint32) | |||
| } | |||
| func (p pointer) toUint32Slice() *[]uint32 { | |||
| return p.v.Interface().(*[]uint32) | |||
| } | |||
| func (p pointer) toBool() *bool { | |||
| return p.v.Interface().(*bool) | |||
| } | |||
| func (p pointer) toBoolPtr() **bool { | |||
| return p.v.Interface().(**bool) | |||
| } | |||
| func (p pointer) toBoolSlice() *[]bool { | |||
| return p.v.Interface().(*[]bool) | |||
| } | |||
| func (p pointer) toFloat64() *float64 { | |||
| return p.v.Interface().(*float64) | |||
| } | |||
| func (p pointer) toFloat64Ptr() **float64 { | |||
| return p.v.Interface().(**float64) | |||
| } | |||
| func (p pointer) toFloat64Slice() *[]float64 { | |||
| return p.v.Interface().(*[]float64) | |||
| } | |||
| func (p pointer) toFloat32() *float32 { | |||
| return p.v.Interface().(*float32) | |||
| } | |||
| func (p pointer) toFloat32Ptr() **float32 { | |||
| return p.v.Interface().(**float32) | |||
| } | |||
| func (p pointer) toFloat32Slice() *[]float32 { | |||
| return p.v.Interface().(*[]float32) | |||
| } | |||
| func (p pointer) toString() *string { | |||
| return p.v.Interface().(*string) | |||
| } | |||
| func (p pointer) toStringPtr() **string { | |||
| return p.v.Interface().(**string) | |||
| } | |||
| func (p pointer) toStringSlice() *[]string { | |||
| return p.v.Interface().(*[]string) | |||
| } | |||
| func (p pointer) toBytes() *[]byte { | |||
| return p.v.Interface().(*[]byte) | |||
| } | |||
| func (p pointer) toBytesSlice() *[][]byte { | |||
| return p.v.Interface().(*[][]byte) | |||
| } | |||
| func (p pointer) toExtensions() *XXX_InternalExtensions { | |||
| return p.v.Interface().(*XXX_InternalExtensions) | |||
| } | |||
| func (p pointer) toOldExtensions() *map[int32]Extension { | |||
| return p.v.Interface().(*map[int32]Extension) | |||
| } | |||
| func (p pointer) getPointer() pointer { | |||
| return pointer{v: p.v.Elem()} | |||
| } | |||
| func (p pointer) setPointer(q pointer) { | |||
| p.v.Elem().Set(q.v) | |||
| } | |||
| func (p pointer) appendPointer(q pointer) { | |||
| grow(p.v.Elem()).Set(q.v) | |||
| } | |||
| // getPointerSlice copies []*T from p as a new []pointer. | |||
| // This behavior differs from the implementation in pointer_unsafe.go. | |||
| func (p pointer) getPointerSlice() []pointer { | |||
| if p.v.IsNil() { | |||
| return nil | |||
| } | |||
| n := p.v.Elem().Len() | |||
| s := make([]pointer, n) | |||
| for i := 0; i < n; i++ { | |||
| s[i] = pointer{v: p.v.Elem().Index(i)} | |||
| } | |||
| return s | |||
| } | |||
| // setPointerSlice copies []pointer into p as a new []*T. | |||
| // This behavior differs from the implementation in pointer_unsafe.go. | |||
| func (p pointer) setPointerSlice(v []pointer) { | |||
| if v == nil { | |||
| p.v.Elem().Set(reflect.New(p.v.Elem().Type()).Elem()) | |||
| return | |||
| } | |||
| s := reflect.MakeSlice(p.v.Elem().Type(), 0, len(v)) | |||
| for _, p := range v { | |||
| s = reflect.Append(s, p.v) | |||
| } | |||
| p.v.Elem().Set(s) | |||
| } | |||
| // getInterfacePointer returns a pointer that points to the | |||
| // interface data of the interface pointed by p. | |||
| func (p pointer) getInterfacePointer() pointer { | |||
| if p.v.Elem().IsNil() { | |||
| return pointer{v: p.v.Elem()} | |||
| } | |||
| return pointer{v: p.v.Elem().Elem().Elem().Field(0).Addr()} // *interface -> interface -> *struct -> struct | |||
| } | |||
| func (p pointer) asPointerTo(t reflect.Type) reflect.Value { | |||
| // TODO: check that p.v.Type().Elem() == t? | |||
| return p.v | |||
| } | |||
| func atomicLoadUnmarshalInfo(p **unmarshalInfo) *unmarshalInfo { | |||
| atomicLock.Lock() | |||
| defer atomicLock.Unlock() | |||
| return *p | |||
| } | |||
| func atomicStoreUnmarshalInfo(p **unmarshalInfo, v *unmarshalInfo) { | |||
| atomicLock.Lock() | |||
| defer atomicLock.Unlock() | |||
| *p = v | |||
| } | |||
| func atomicLoadMarshalInfo(p **marshalInfo) *marshalInfo { | |||
| atomicLock.Lock() | |||
| defer atomicLock.Unlock() | |||
| return *p | |||
| } | |||
| func atomicStoreMarshalInfo(p **marshalInfo, v *marshalInfo) { | |||
| atomicLock.Lock() | |||
| defer atomicLock.Unlock() | |||
| *p = v | |||
| } | |||
| func atomicLoadMergeInfo(p **mergeInfo) *mergeInfo { | |||
| atomicLock.Lock() | |||
| defer atomicLock.Unlock() | |||
| return *p | |||
| } | |||
| func atomicStoreMergeInfo(p **mergeInfo, v *mergeInfo) { | |||
| atomicLock.Lock() | |||
| defer atomicLock.Unlock() | |||
| *p = v | |||
| } | |||
| func atomicLoadDiscardInfo(p **discardInfo) *discardInfo { | |||
| atomicLock.Lock() | |||
| defer atomicLock.Unlock() | |||
| return *p | |||
| } | |||
| func atomicStoreDiscardInfo(p **discardInfo, v *discardInfo) { | |||
| atomicLock.Lock() | |||
| defer atomicLock.Unlock() | |||
| *p = v | |||
| } | |||
| var atomicLock sync.Mutex | |||
| @@ -1,313 +0,0 @@ | |||
| // Go support for Protocol Buffers - Google's data interchange format | |||
| // | |||
| // Copyright 2012 The Go Authors. All rights reserved. | |||
| // https://github.com/golang/protobuf | |||
| // | |||
| // Redistribution and use in source and binary forms, with or without | |||
| // modification, are permitted provided that the following conditions are | |||
| // met: | |||
| // | |||
| // * Redistributions of source code must retain the above copyright | |||
| // notice, this list of conditions and the following disclaimer. | |||
| // * Redistributions in binary form must reproduce the above | |||
| // copyright notice, this list of conditions and the following disclaimer | |||
| // in the documentation and/or other materials provided with the | |||
| // distribution. | |||
| // * Neither the name of Google Inc. nor the names of its | |||
| // contributors may be used to endorse or promote products derived from | |||
| // this software without specific prior written permission. | |||
| // | |||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| // +build !purego,!appengine,!js | |||
| // This file contains the implementation of the proto field accesses using package unsafe. | |||
| package proto | |||
| import ( | |||
| "reflect" | |||
| "sync/atomic" | |||
| "unsafe" | |||
| ) | |||
| const unsafeAllowed = true | |||
| // A field identifies a field in a struct, accessible from a pointer. | |||
| // In this implementation, a field is identified by its byte offset from the start of the struct. | |||
| type field uintptr | |||
| // toField returns a field equivalent to the given reflect field. | |||
| func toField(f *reflect.StructField) field { | |||
| return field(f.Offset) | |||
| } | |||
| // invalidField is an invalid field identifier. | |||
| const invalidField = ^field(0) | |||
| // zeroField is a noop when calling pointer.offset. | |||
| const zeroField = field(0) | |||
| // IsValid reports whether the field identifier is valid. | |||
| func (f field) IsValid() bool { | |||
| return f != invalidField | |||
| } | |||
| // The pointer type below is for the new table-driven encoder/decoder. | |||
| // The implementation here uses unsafe.Pointer to create a generic pointer. | |||
| // In pointer_reflect.go we use reflect instead of unsafe to implement | |||
| // the same (but slower) interface. | |||
| type pointer struct { | |||
| p unsafe.Pointer | |||
| } | |||
| // size of pointer | |||
| var ptrSize = unsafe.Sizeof(uintptr(0)) | |||
| // toPointer converts an interface of pointer type to a pointer | |||
| // that points to the same target. | |||
| func toPointer(i *Message) pointer { | |||
| // Super-tricky - read pointer out of data word of interface value. | |||
| // Saves ~25ns over the equivalent: | |||
| // return valToPointer(reflect.ValueOf(*i)) | |||
| return pointer{p: (*[2]unsafe.Pointer)(unsafe.Pointer(i))[1]} | |||
| } | |||
| // toAddrPointer converts an interface to a pointer that points to | |||
| // the interface data. | |||
| func toAddrPointer(i *interface{}, isptr, deref bool) (p pointer) { | |||
| // Super-tricky - read or get the address of data word of interface value. | |||
| if isptr { | |||
| // The interface is of pointer type, thus it is a direct interface. | |||
| // The data word is the pointer data itself. We take its address. | |||
| p = pointer{p: unsafe.Pointer(uintptr(unsafe.Pointer(i)) + ptrSize)} | |||
| } else { | |||
| // The interface is not of pointer type. The data word is the pointer | |||
| // to the data. | |||
| p = pointer{p: (*[2]unsafe.Pointer)(unsafe.Pointer(i))[1]} | |||
| } | |||
| if deref { | |||
| p.p = *(*unsafe.Pointer)(p.p) | |||
| } | |||
| return p | |||
| } | |||
| // valToPointer converts v to a pointer. v must be of pointer type. | |||
| func valToPointer(v reflect.Value) pointer { | |||
| return pointer{p: unsafe.Pointer(v.Pointer())} | |||
| } | |||
| // offset converts from a pointer to a structure to a pointer to | |||
| // one of its fields. | |||
| func (p pointer) offset(f field) pointer { | |||
| // For safety, we should panic if !f.IsValid, however calling panic causes | |||
| // this to no longer be inlineable, which is a serious performance cost. | |||
| /* | |||
| if !f.IsValid() { | |||
| panic("invalid field") | |||
| } | |||
| */ | |||
| return pointer{p: unsafe.Pointer(uintptr(p.p) + uintptr(f))} | |||
| } | |||
| func (p pointer) isNil() bool { | |||
| return p.p == nil | |||
| } | |||
| func (p pointer) toInt64() *int64 { | |||
| return (*int64)(p.p) | |||
| } | |||
| func (p pointer) toInt64Ptr() **int64 { | |||
| return (**int64)(p.p) | |||
| } | |||
| func (p pointer) toInt64Slice() *[]int64 { | |||
| return (*[]int64)(p.p) | |||
| } | |||
| func (p pointer) toInt32() *int32 { | |||
| return (*int32)(p.p) | |||
| } | |||
| // See pointer_reflect.go for why toInt32Ptr/Slice doesn't exist. | |||
| /* | |||
| func (p pointer) toInt32Ptr() **int32 { | |||
| return (**int32)(p.p) | |||
| } | |||
| func (p pointer) toInt32Slice() *[]int32 { | |||
| return (*[]int32)(p.p) | |||
| } | |||
| */ | |||
| func (p pointer) getInt32Ptr() *int32 { | |||
| return *(**int32)(p.p) | |||
| } | |||
| func (p pointer) setInt32Ptr(v int32) { | |||
| *(**int32)(p.p) = &v | |||
| } | |||
| // getInt32Slice loads a []int32 from p. | |||
| // The value returned is aliased with the original slice. | |||
| // This behavior differs from the implementation in pointer_reflect.go. | |||
| func (p pointer) getInt32Slice() []int32 { | |||
| return *(*[]int32)(p.p) | |||
| } | |||
| // setInt32Slice stores a []int32 to p. | |||
| // The value set is aliased with the input slice. | |||
| // This behavior differs from the implementation in pointer_reflect.go. | |||
| func (p pointer) setInt32Slice(v []int32) { | |||
| *(*[]int32)(p.p) = v | |||
| } | |||
| // TODO: Can we get rid of appendInt32Slice and use setInt32Slice instead? | |||
| func (p pointer) appendInt32Slice(v int32) { | |||
| s := (*[]int32)(p.p) | |||
| *s = append(*s, v) | |||
| } | |||
| func (p pointer) toUint64() *uint64 { | |||
| return (*uint64)(p.p) | |||
| } | |||
| func (p pointer) toUint64Ptr() **uint64 { | |||
| return (**uint64)(p.p) | |||
| } | |||
| func (p pointer) toUint64Slice() *[]uint64 { | |||
| return (*[]uint64)(p.p) | |||
| } | |||
| func (p pointer) toUint32() *uint32 { | |||
| return (*uint32)(p.p) | |||
| } | |||
| func (p pointer) toUint32Ptr() **uint32 { | |||
| return (**uint32)(p.p) | |||
| } | |||
| func (p pointer) toUint32Slice() *[]uint32 { | |||
| return (*[]uint32)(p.p) | |||
| } | |||
| func (p pointer) toBool() *bool { | |||
| return (*bool)(p.p) | |||
| } | |||
| func (p pointer) toBoolPtr() **bool { | |||
| return (**bool)(p.p) | |||
| } | |||
| func (p pointer) toBoolSlice() *[]bool { | |||
| return (*[]bool)(p.p) | |||
| } | |||
| func (p pointer) toFloat64() *float64 { | |||
| return (*float64)(p.p) | |||
| } | |||
| func (p pointer) toFloat64Ptr() **float64 { | |||
| return (**float64)(p.p) | |||
| } | |||
| func (p pointer) toFloat64Slice() *[]float64 { | |||
| return (*[]float64)(p.p) | |||
| } | |||
| func (p pointer) toFloat32() *float32 { | |||
| return (*float32)(p.p) | |||
| } | |||
| func (p pointer) toFloat32Ptr() **float32 { | |||
| return (**float32)(p.p) | |||
| } | |||
| func (p pointer) toFloat32Slice() *[]float32 { | |||
| return (*[]float32)(p.p) | |||
| } | |||
| func (p pointer) toString() *string { | |||
| return (*string)(p.p) | |||
| } | |||
| func (p pointer) toStringPtr() **string { | |||
| return (**string)(p.p) | |||
| } | |||
| func (p pointer) toStringSlice() *[]string { | |||
| return (*[]string)(p.p) | |||
| } | |||
| func (p pointer) toBytes() *[]byte { | |||
| return (*[]byte)(p.p) | |||
| } | |||
| func (p pointer) toBytesSlice() *[][]byte { | |||
| return (*[][]byte)(p.p) | |||
| } | |||
| func (p pointer) toExtensions() *XXX_InternalExtensions { | |||
| return (*XXX_InternalExtensions)(p.p) | |||
| } | |||
| func (p pointer) toOldExtensions() *map[int32]Extension { | |||
| return (*map[int32]Extension)(p.p) | |||
| } | |||
| // getPointerSlice loads []*T from p as a []pointer. | |||
| // The value returned is aliased with the original slice. | |||
| // This behavior differs from the implementation in pointer_reflect.go. | |||
| func (p pointer) getPointerSlice() []pointer { | |||
| // Super-tricky - p should point to a []*T where T is a | |||
| // message type. We load it as []pointer. | |||
| return *(*[]pointer)(p.p) | |||
| } | |||
| // setPointerSlice stores []pointer into p as a []*T. | |||
| // The value set is aliased with the input slice. | |||
| // This behavior differs from the implementation in pointer_reflect.go. | |||
| func (p pointer) setPointerSlice(v []pointer) { | |||
| // Super-tricky - p should point to a []*T where T is a | |||
| // message type. We store it as []pointer. | |||
| *(*[]pointer)(p.p) = v | |||
| } | |||
| // getPointer loads the pointer at p and returns it. | |||
| func (p pointer) getPointer() pointer { | |||
| return pointer{p: *(*unsafe.Pointer)(p.p)} | |||
| } | |||
| // setPointer stores the pointer q at p. | |||
| func (p pointer) setPointer(q pointer) { | |||
| *(*unsafe.Pointer)(p.p) = q.p | |||
| } | |||
| // append q to the slice pointed to by p. | |||
| func (p pointer) appendPointer(q pointer) { | |||
| s := (*[]unsafe.Pointer)(p.p) | |||
| *s = append(*s, q.p) | |||
| } | |||
| // getInterfacePointer returns a pointer that points to the | |||
| // interface data of the interface pointed by p. | |||
| func (p pointer) getInterfacePointer() pointer { | |||
| // Super-tricky - read pointer out of data word of interface value. | |||
| return pointer{p: (*(*[2]unsafe.Pointer)(p.p))[1]} | |||
| } | |||
| // asPointerTo returns a reflect.Value that is a pointer to an | |||
| // object of type t stored at p. | |||
| func (p pointer) asPointerTo(t reflect.Type) reflect.Value { | |||
| return reflect.NewAt(t, p.p) | |||
| } | |||
| func atomicLoadUnmarshalInfo(p **unmarshalInfo) *unmarshalInfo { | |||
| return (*unmarshalInfo)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(p)))) | |||
| } | |||
| func atomicStoreUnmarshalInfo(p **unmarshalInfo, v *unmarshalInfo) { | |||
| atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(p)), unsafe.Pointer(v)) | |||
| } | |||
| func atomicLoadMarshalInfo(p **marshalInfo) *marshalInfo { | |||
| return (*marshalInfo)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(p)))) | |||
| } | |||
| func atomicStoreMarshalInfo(p **marshalInfo, v *marshalInfo) { | |||
| atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(p)), unsafe.Pointer(v)) | |||
| } | |||
| func atomicLoadMergeInfo(p **mergeInfo) *mergeInfo { | |||
| return (*mergeInfo)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(p)))) | |||
| } | |||
| func atomicStoreMergeInfo(p **mergeInfo, v *mergeInfo) { | |||
| atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(p)), unsafe.Pointer(v)) | |||
| } | |||
| func atomicLoadDiscardInfo(p **discardInfo) *discardInfo { | |||
| return (*discardInfo)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(p)))) | |||
| } | |||
| func atomicStoreDiscardInfo(p **discardInfo, v *discardInfo) { | |||
| atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(p)), unsafe.Pointer(v)) | |||
| } | |||
| @@ -1,162 +1,104 @@ | |||
| // Go support for Protocol Buffers - Google's data interchange format | |||
| // | |||
| // Copyright 2010 The Go Authors. All rights reserved. | |||
| // https://github.com/golang/protobuf | |||
| // | |||
| // Redistribution and use in source and binary forms, with or without | |||
| // modification, are permitted provided that the following conditions are | |||
| // met: | |||
| // | |||
| // * Redistributions of source code must retain the above copyright | |||
| // notice, this list of conditions and the following disclaimer. | |||
| // * Redistributions in binary form must reproduce the above | |||
| // copyright notice, this list of conditions and the following disclaimer | |||
| // in the documentation and/or other materials provided with the | |||
| // distribution. | |||
| // * Neither the name of Google Inc. nor the names of its | |||
| // contributors may be used to endorse or promote products derived from | |||
| // this software without specific prior written permission. | |||
| // | |||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| // Copyright 2010 The Go Authors. All rights reserved. | |||
| // Use of this source code is governed by a BSD-style | |||
| // license that can be found in the LICENSE file. | |||
| package proto | |||
| /* | |||
| * Routines for encoding data into the wire format for protocol buffers. | |||
| */ | |||
| import ( | |||
| "fmt" | |||
| "log" | |||
| "reflect" | |||
| "sort" | |||
| "strconv" | |||
| "strings" | |||
| "sync" | |||
| ) | |||
| const debug bool = false | |||
| // Constants that identify the encoding of a value on the wire. | |||
| const ( | |||
| WireVarint = 0 | |||
| WireFixed64 = 1 | |||
| WireBytes = 2 | |||
| WireStartGroup = 3 | |||
| WireEndGroup = 4 | |||
| WireFixed32 = 5 | |||
| "google.golang.org/protobuf/reflect/protoreflect" | |||
| "google.golang.org/protobuf/runtime/protoimpl" | |||
| ) | |||
| // tagMap is an optimization over map[int]int for typical protocol buffer | |||
| // use-cases. Encoded protocol buffers are often in tag order with small tag | |||
| // numbers. | |||
| type tagMap struct { | |||
| fastTags []int | |||
| slowTags map[int]int | |||
| } | |||
| // tagMapFastLimit is the upper bound on the tag number that will be stored in | |||
| // the tagMap slice rather than its map. | |||
| const tagMapFastLimit = 1024 | |||
| func (p *tagMap) get(t int) (int, bool) { | |||
| if t > 0 && t < tagMapFastLimit { | |||
| if t >= len(p.fastTags) { | |||
| return 0, false | |||
| } | |||
| fi := p.fastTags[t] | |||
| return fi, fi >= 0 | |||
| } | |||
| fi, ok := p.slowTags[t] | |||
| return fi, ok | |||
| } | |||
| func (p *tagMap) put(t int, fi int) { | |||
| if t > 0 && t < tagMapFastLimit { | |||
| for len(p.fastTags) < t+1 { | |||
| p.fastTags = append(p.fastTags, -1) | |||
| } | |||
| p.fastTags[t] = fi | |||
| return | |||
| } | |||
| if p.slowTags == nil { | |||
| p.slowTags = make(map[int]int) | |||
| } | |||
| p.slowTags[t] = fi | |||
| } | |||
| // StructProperties represents properties for all the fields of a struct. | |||
| // decoderTags and decoderOrigNames should only be used by the decoder. | |||
| // StructProperties represents protocol buffer type information for a | |||
| // generated protobuf message in the open-struct API. | |||
| // | |||
| // Deprecated: Do not use. | |||
| type StructProperties struct { | |||
| Prop []*Properties // properties for each field | |||
| reqCount int // required count | |||
| decoderTags tagMap // map from proto tag to struct field number | |||
| decoderOrigNames map[string]int // map from original name to struct field number | |||
| order []int // list of struct field numbers in tag order | |||
| // Prop are the properties for each field. | |||
| // | |||
| // Fields belonging to a oneof are stored in OneofTypes instead, with a | |||
| // single Properties representing the parent oneof held here. | |||
| // | |||
| // The order of Prop matches the order of fields in the Go struct. | |||
| // Struct fields that are not related to protobufs have a "XXX_" prefix | |||
| // in the Properties.Name and must be ignored by the user. | |||
| Prop []*Properties | |||
| // OneofTypes contains information about the oneof fields in this message. | |||
| // It is keyed by the original name of a field. | |||
| // It is keyed by the protobuf field name. | |||
| OneofTypes map[string]*OneofProperties | |||
| } | |||
| // OneofProperties represents information about a specific field in a oneof. | |||
| type OneofProperties struct { | |||
| Type reflect.Type // pointer to generated struct type for this oneof field | |||
| Field int // struct field number of the containing oneof in the message | |||
| Prop *Properties | |||
| } | |||
| // Implement the sorting interface so we can sort the fields in tag order, as recommended by the spec. | |||
| // See encode.go, (*Buffer).enc_struct. | |||
| func (sp *StructProperties) Len() int { return len(sp.order) } | |||
| func (sp *StructProperties) Less(i, j int) bool { | |||
| return sp.Prop[sp.order[i]].Tag < sp.Prop[sp.order[j]].Tag | |||
| } | |||
| func (sp *StructProperties) Swap(i, j int) { sp.order[i], sp.order[j] = sp.order[j], sp.order[i] } | |||
| // Properties represents the protocol-specific behavior of a single struct field. | |||
| // Properties represents the type information for a protobuf message field. | |||
| // | |||
| // Deprecated: Do not use. | |||
| type Properties struct { | |||
| Name string // name of the field, for error messages | |||
| OrigName string // original name before protocol compiler (always set) | |||
| JSONName string // name to use for JSON; determined by protoc | |||
| Wire string | |||
| // Name is a placeholder name with little meaningful semantic value. | |||
| // If the name has an "XXX_" prefix, the entire Properties must be ignored. | |||
| Name string | |||
| // OrigName is the protobuf field name or oneof name. | |||
| OrigName string | |||
| // JSONName is the JSON name for the protobuf field. | |||
| JSONName string | |||
| // Enum is a placeholder name for enums. | |||
| // For historical reasons, this is neither the Go name for the enum, | |||
| // nor the protobuf name for the enum. | |||
| Enum string // Deprecated: Do not use. | |||
| // Weak contains the full name of the weakly referenced message. | |||
| Weak string | |||
| // Wire is a string representation of the wire type. | |||
| Wire string | |||
| // WireType is the protobuf wire type for the field. | |||
| WireType int | |||
| Tag int | |||
| // Tag is the protobuf field number. | |||
| Tag int | |||
| // Required reports whether this is a required field. | |||
| Required bool | |||
| // Optional reports whether this is a optional field. | |||
| Optional bool | |||
| // Repeated reports whether this is a repeated field. | |||
| Repeated bool | |||
| Packed bool // relevant for repeated primitives only | |||
| Enum string // set for enum types only | |||
| proto3 bool // whether this is known to be a proto3 field | |||
| oneof bool // whether this is a oneof field | |||
| Default string // default value | |||
| HasDefault bool // whether an explicit default was provided | |||
| stype reflect.Type // set for struct types only | |||
| sprop *StructProperties // set for struct types only | |||
| // Packed reports whether this is a packed repeated field of scalars. | |||
| Packed bool | |||
| // Proto3 reports whether this field operates under the proto3 syntax. | |||
| Proto3 bool | |||
| // Oneof reports whether this field belongs within a oneof. | |||
| Oneof bool | |||
| // Default is the default value in string form. | |||
| Default string | |||
| // HasDefault reports whether the field has a default value. | |||
| HasDefault bool | |||
| // MapKeyProp is the properties for the key field for a map field. | |||
| MapKeyProp *Properties | |||
| // MapValProp is the properties for the value field for a map field. | |||
| MapValProp *Properties | |||
| } | |||
| mtype reflect.Type // set for map types only | |||
| MapKeyProp *Properties // set for map types only | |||
| MapValProp *Properties // set for map types only | |||
| // OneofProperties represents the type information for a protobuf oneof. | |||
| // | |||
| // Deprecated: Do not use. | |||
| type OneofProperties struct { | |||
| // Type is a pointer to the generated wrapper type for the field value. | |||
| // This is nil for messages that are not in the open-struct API. | |||
| Type reflect.Type | |||
| // Field is the index into StructProperties.Prop for the containing oneof. | |||
| Field int | |||
| // Prop is the properties for the field. | |||
| Prop *Properties | |||
| } | |||
| // String formats the properties in the protobuf struct field tag style. | |||
| func (p *Properties) String() string { | |||
| s := p.Wire | |||
| s += "," | |||
| s += strconv.Itoa(p.Tag) | |||
| s += "," + strconv.Itoa(p.Tag) | |||
| if p.Required { | |||
| s += ",req" | |||
| } | |||
| @@ -170,18 +112,21 @@ func (p *Properties) String() string { | |||
| s += ",packed" | |||
| } | |||
| s += ",name=" + p.OrigName | |||
| if p.JSONName != p.OrigName { | |||
| if p.JSONName != "" { | |||
| s += ",json=" + p.JSONName | |||
| } | |||
| if p.proto3 { | |||
| if len(p.Enum) > 0 { | |||
| s += ",enum=" + p.Enum | |||
| } | |||
| if len(p.Weak) > 0 { | |||
| s += ",weak=" + p.Weak | |||
| } | |||
| if p.Proto3 { | |||
| s += ",proto3" | |||
| } | |||
| if p.oneof { | |||
| if p.Oneof { | |||
| s += ",oneof" | |||
| } | |||
| if len(p.Enum) > 0 { | |||
| s += ",enum=" + p.Enum | |||
| } | |||
| if p.HasDefault { | |||
| s += ",def=" + p.Default | |||
| } | |||
| @@ -189,356 +134,173 @@ func (p *Properties) String() string { | |||
| } | |||
| // Parse populates p by parsing a string in the protobuf struct field tag style. | |||
| func (p *Properties) Parse(s string) { | |||
| // "bytes,49,opt,name=foo,def=hello!" | |||
| fields := strings.Split(s, ",") // breaks def=, but handled below. | |||
| if len(fields) < 2 { | |||
| log.Printf("proto: tag has too few fields: %q", s) | |||
| return | |||
| } | |||
| p.Wire = fields[0] | |||
| switch p.Wire { | |||
| case "varint": | |||
| p.WireType = WireVarint | |||
| case "fixed32": | |||
| p.WireType = WireFixed32 | |||
| case "fixed64": | |||
| p.WireType = WireFixed64 | |||
| case "zigzag32": | |||
| p.WireType = WireVarint | |||
| case "zigzag64": | |||
| p.WireType = WireVarint | |||
| case "bytes", "group": | |||
| p.WireType = WireBytes | |||
| // no numeric converter for non-numeric types | |||
| default: | |||
| log.Printf("proto: tag has unknown wire type: %q", s) | |||
| return | |||
| } | |||
| var err error | |||
| p.Tag, err = strconv.Atoi(fields[1]) | |||
| if err != nil { | |||
| return | |||
| } | |||
| outer: | |||
| for i := 2; i < len(fields); i++ { | |||
| f := fields[i] | |||
| switch { | |||
| case f == "req": | |||
| p.Required = true | |||
| case f == "opt": | |||
| func (p *Properties) Parse(tag string) { | |||
| // For example: "bytes,49,opt,name=foo,def=hello!" | |||
| for len(tag) > 0 { | |||
| i := strings.IndexByte(tag, ',') | |||
| if i < 0 { | |||
| i = len(tag) | |||
| } | |||
| switch s := tag[:i]; { | |||
| case strings.HasPrefix(s, "name="): | |||
| p.OrigName = s[len("name="):] | |||
| case strings.HasPrefix(s, "json="): | |||
| p.JSONName = s[len("json="):] | |||
| case strings.HasPrefix(s, "enum="): | |||
| p.Enum = s[len("enum="):] | |||
| case strings.HasPrefix(s, "weak="): | |||
| p.Weak = s[len("weak="):] | |||
| case strings.Trim(s, "0123456789") == "": | |||
| n, _ := strconv.ParseUint(s, 10, 32) | |||
| p.Tag = int(n) | |||
| case s == "opt": | |||
| p.Optional = true | |||
| case f == "rep": | |||
| case s == "req": | |||
| p.Required = true | |||
| case s == "rep": | |||
| p.Repeated = true | |||
| case f == "packed": | |||
| case s == "varint" || s == "zigzag32" || s == "zigzag64": | |||
| p.Wire = s | |||
| p.WireType = WireVarint | |||
| case s == "fixed32": | |||
| p.Wire = s | |||
| p.WireType = WireFixed32 | |||
| case s == "fixed64": | |||
| p.Wire = s | |||
| p.WireType = WireFixed64 | |||
| case s == "bytes": | |||
| p.Wire = s | |||
| p.WireType = WireBytes | |||
| case s == "group": | |||
| p.Wire = s | |||
| p.WireType = WireStartGroup | |||
| case s == "packed": | |||
| p.Packed = true | |||
| case strings.HasPrefix(f, "name="): | |||
| p.OrigName = f[5:] | |||
| case strings.HasPrefix(f, "json="): | |||
| p.JSONName = f[5:] | |||
| case strings.HasPrefix(f, "enum="): | |||
| p.Enum = f[5:] | |||
| case f == "proto3": | |||
| p.proto3 = true | |||
| case f == "oneof": | |||
| p.oneof = true | |||
| case strings.HasPrefix(f, "def="): | |||
| case s == "proto3": | |||
| p.Proto3 = true | |||
| case s == "oneof": | |||
| p.Oneof = true | |||
| case strings.HasPrefix(s, "def="): | |||
| // The default tag is special in that everything afterwards is the | |||
| // default regardless of the presence of commas. | |||
| p.HasDefault = true | |||
| p.Default = f[4:] // rest of string | |||
| if i+1 < len(fields) { | |||
| // Commas aren't escaped, and def is always last. | |||
| p.Default += "," + strings.Join(fields[i+1:], ",") | |||
| break outer | |||
| } | |||
| } | |||
| } | |||
| } | |||
| var protoMessageType = reflect.TypeOf((*Message)(nil)).Elem() | |||
| // setFieldProps initializes the field properties for submessages and maps. | |||
| func (p *Properties) setFieldProps(typ reflect.Type, f *reflect.StructField, lockGetProp bool) { | |||
| switch t1 := typ; t1.Kind() { | |||
| case reflect.Ptr: | |||
| if t1.Elem().Kind() == reflect.Struct { | |||
| p.stype = t1.Elem() | |||
| } | |||
| case reflect.Slice: | |||
| if t2 := t1.Elem(); t2.Kind() == reflect.Ptr && t2.Elem().Kind() == reflect.Struct { | |||
| p.stype = t2.Elem() | |||
| } | |||
| case reflect.Map: | |||
| p.mtype = t1 | |||
| p.MapKeyProp = &Properties{} | |||
| p.MapKeyProp.init(reflect.PtrTo(p.mtype.Key()), "Key", f.Tag.Get("protobuf_key"), nil, lockGetProp) | |||
| p.MapValProp = &Properties{} | |||
| vtype := p.mtype.Elem() | |||
| if vtype.Kind() != reflect.Ptr && vtype.Kind() != reflect.Slice { | |||
| // The value type is not a message (*T) or bytes ([]byte), | |||
| // so we need encoders for the pointer to this type. | |||
| vtype = reflect.PtrTo(vtype) | |||
| } | |||
| p.MapValProp.init(vtype, "Value", f.Tag.Get("protobuf_val"), nil, lockGetProp) | |||
| } | |||
| if p.stype != nil { | |||
| if lockGetProp { | |||
| p.sprop = GetProperties(p.stype) | |||
| } else { | |||
| p.sprop = getPropertiesLocked(p.stype) | |||
| p.Default, i = tag[len("def="):], len(tag) | |||
| } | |||
| tag = strings.TrimPrefix(tag[i:], ",") | |||
| } | |||
| } | |||
| var ( | |||
| marshalerType = reflect.TypeOf((*Marshaler)(nil)).Elem() | |||
| ) | |||
| // Init populates the properties from a protocol buffer struct tag. | |||
| // | |||
| // Deprecated: Do not use. | |||
| func (p *Properties) Init(typ reflect.Type, name, tag string, f *reflect.StructField) { | |||
| p.init(typ, name, tag, f, true) | |||
| } | |||
| func (p *Properties) init(typ reflect.Type, name, tag string, f *reflect.StructField, lockGetProp bool) { | |||
| // "bytes,49,opt,def=hello!" | |||
| p.Name = name | |||
| p.OrigName = name | |||
| if tag == "" { | |||
| return | |||
| } | |||
| p.Parse(tag) | |||
| p.setFieldProps(typ, f, lockGetProp) | |||
| if typ != nil && typ.Kind() == reflect.Map { | |||
| p.MapKeyProp = new(Properties) | |||
| p.MapKeyProp.Init(nil, "Key", f.Tag.Get("protobuf_key"), nil) | |||
| p.MapValProp = new(Properties) | |||
| p.MapValProp.Init(nil, "Value", f.Tag.Get("protobuf_val"), nil) | |||
| } | |||
| } | |||
| var ( | |||
| propertiesMu sync.RWMutex | |||
| propertiesMap = make(map[reflect.Type]*StructProperties) | |||
| ) | |||
| var propertiesCache sync.Map // map[reflect.Type]*StructProperties | |||
| // GetProperties returns the list of properties for the type represented by t. | |||
| // t must represent a generated struct type of a protocol message. | |||
| // GetProperties returns the list of properties for the type represented by t, | |||
| // which must be a generated protocol buffer message in the open-struct API, | |||
| // where protobuf message fields are represented by exported Go struct fields. | |||
| // | |||
| // Deprecated: Use protobuf reflection instead. | |||
| func GetProperties(t reflect.Type) *StructProperties { | |||
| if t.Kind() != reflect.Struct { | |||
| panic("proto: type must have kind struct") | |||
| } | |||
| // Most calls to GetProperties in a long-running program will be | |||
| // retrieving details for types we have seen before. | |||
| propertiesMu.RLock() | |||
| sprop, ok := propertiesMap[t] | |||
| propertiesMu.RUnlock() | |||
| if ok { | |||
| return sprop | |||
| if p, ok := propertiesCache.Load(t); ok { | |||
| return p.(*StructProperties) | |||
| } | |||
| propertiesMu.Lock() | |||
| sprop = getPropertiesLocked(t) | |||
| propertiesMu.Unlock() | |||
| return sprop | |||
| p, _ := propertiesCache.LoadOrStore(t, newProperties(t)) | |||
| return p.(*StructProperties) | |||
| } | |||
| type ( | |||
| oneofFuncsIface interface { | |||
| XXX_OneofFuncs() (func(Message, *Buffer) error, func(Message, int, int, *Buffer) (bool, error), func(Message) int, []interface{}) | |||
| } | |||
| oneofWrappersIface interface { | |||
| XXX_OneofWrappers() []interface{} | |||
| } | |||
| ) | |||
| // getPropertiesLocked requires that propertiesMu is held. | |||
| func getPropertiesLocked(t reflect.Type) *StructProperties { | |||
| if prop, ok := propertiesMap[t]; ok { | |||
| return prop | |||
| func newProperties(t reflect.Type) *StructProperties { | |||
| if t.Kind() != reflect.Struct { | |||
| panic(fmt.Sprintf("%v is not a generated message in the open-struct API", t)) | |||
| } | |||
| var hasOneof bool | |||
| prop := new(StructProperties) | |||
| // in case of recursive protos, fill this in now. | |||
| propertiesMap[t] = prop | |||
| // build properties | |||
| prop.Prop = make([]*Properties, t.NumField()) | |||
| prop.order = make([]int, t.NumField()) | |||
| // Construct a list of properties for each field in the struct. | |||
| for i := 0; i < t.NumField(); i++ { | |||
| f := t.Field(i) | |||
| p := new(Properties) | |||
| name := f.Name | |||
| p.init(f.Type, name, f.Tag.Get("protobuf"), &f, false) | |||
| f := t.Field(i) | |||
| tagField := f.Tag.Get("protobuf") | |||
| p.Init(f.Type, f.Name, tagField, &f) | |||
| oneof := f.Tag.Get("protobuf_oneof") // special case | |||
| if oneof != "" { | |||
| // Oneof fields don't use the traditional protobuf tag. | |||
| p.OrigName = oneof | |||
| tagOneof := f.Tag.Get("protobuf_oneof") | |||
| if tagOneof != "" { | |||
| hasOneof = true | |||
| p.OrigName = tagOneof | |||
| } | |||
| prop.Prop[i] = p | |||
| prop.order[i] = i | |||
| if debug { | |||
| print(i, " ", f.Name, " ", t.String(), " ") | |||
| if p.Tag > 0 { | |||
| print(p.String()) | |||
| } | |||
| print("\n") | |||
| // Rename unrelated struct fields with the "XXX_" prefix since so much | |||
| // user code simply checks for this to exclude special fields. | |||
| if tagField == "" && tagOneof == "" && !strings.HasPrefix(p.Name, "XXX_") { | |||
| p.Name = "XXX_" + p.Name | |||
| p.OrigName = "XXX_" + p.OrigName | |||
| } else if p.Weak != "" { | |||
| p.Name = p.OrigName // avoid possible "XXX_" prefix on weak field | |||
| } | |||
| prop.Prop = append(prop.Prop, p) | |||
| } | |||
| // Re-order prop.order. | |||
| sort.Sort(prop) | |||
| // Construct a mapping of oneof field names to properties. | |||
| if hasOneof { | |||
| var oneofWrappers []interface{} | |||
| if fn, ok := reflect.PtrTo(t).MethodByName("XXX_OneofFuncs"); ok { | |||
| oneofWrappers = fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))})[3].Interface().([]interface{}) | |||
| } | |||
| if fn, ok := reflect.PtrTo(t).MethodByName("XXX_OneofWrappers"); ok { | |||
| oneofWrappers = fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))})[0].Interface().([]interface{}) | |||
| } | |||
| if m, ok := reflect.Zero(reflect.PtrTo(t)).Interface().(protoreflect.ProtoMessage); ok { | |||
| if m, ok := m.ProtoReflect().(interface{ ProtoMessageInfo() *protoimpl.MessageInfo }); ok { | |||
| oneofWrappers = m.ProtoMessageInfo().OneofWrappers | |||
| } | |||
| } | |||
| var oots []interface{} | |||
| switch m := reflect.Zero(reflect.PtrTo(t)).Interface().(type) { | |||
| case oneofFuncsIface: | |||
| _, _, _, oots = m.XXX_OneofFuncs() | |||
| case oneofWrappersIface: | |||
| oots = m.XXX_OneofWrappers() | |||
| } | |||
| if len(oots) > 0 { | |||
| // Interpret oneof metadata. | |||
| prop.OneofTypes = make(map[string]*OneofProperties) | |||
| for _, oot := range oots { | |||
| oop := &OneofProperties{ | |||
| Type: reflect.ValueOf(oot).Type(), // *T | |||
| for _, wrapper := range oneofWrappers { | |||
| p := &OneofProperties{ | |||
| Type: reflect.ValueOf(wrapper).Type(), // *T | |||
| Prop: new(Properties), | |||
| } | |||
| sft := oop.Type.Elem().Field(0) | |||
| oop.Prop.Name = sft.Name | |||
| oop.Prop.Parse(sft.Tag.Get("protobuf")) | |||
| // There will be exactly one interface field that | |||
| // this new value is assignable to. | |||
| for i := 0; i < t.NumField(); i++ { | |||
| f := t.Field(i) | |||
| if f.Type.Kind() != reflect.Interface { | |||
| continue | |||
| f := p.Type.Elem().Field(0) | |||
| p.Prop.Name = f.Name | |||
| p.Prop.Parse(f.Tag.Get("protobuf")) | |||
| // Determine the struct field that contains this oneof. | |||
| // Each wrapper is assignable to exactly one parent field. | |||
| var foundOneof bool | |||
| for i := 0; i < t.NumField() && !foundOneof; i++ { | |||
| if p.Type.AssignableTo(t.Field(i).Type) { | |||
| p.Field = i | |||
| foundOneof = true | |||
| } | |||
| if !oop.Type.AssignableTo(f.Type) { | |||
| continue | |||
| } | |||
| oop.Field = i | |||
| break | |||
| } | |||
| prop.OneofTypes[oop.Prop.OrigName] = oop | |||
| } | |||
| } | |||
| // build required counts | |||
| // build tags | |||
| reqCount := 0 | |||
| prop.decoderOrigNames = make(map[string]int) | |||
| for i, p := range prop.Prop { | |||
| if strings.HasPrefix(p.Name, "XXX_") { | |||
| // Internal fields should not appear in tags/origNames maps. | |||
| // They are handled specially when encoding and decoding. | |||
| continue | |||
| } | |||
| if p.Required { | |||
| reqCount++ | |||
| if !foundOneof { | |||
| panic(fmt.Sprintf("%v is not a generated message in the open-struct API", t)) | |||
| } | |||
| prop.OneofTypes[p.Prop.OrigName] = p | |||
| } | |||
| prop.decoderTags.put(p.Tag, i) | |||
| prop.decoderOrigNames[p.OrigName] = i | |||
| } | |||
| prop.reqCount = reqCount | |||
| return prop | |||
| } | |||
| // A global registry of enum types. | |||
| // The generated code will register the generated maps by calling RegisterEnum. | |||
| var enumValueMaps = make(map[string]map[string]int32) | |||
| // RegisterEnum is called from the generated code to install the enum descriptor | |||
| // maps into the global table to aid parsing text format protocol buffers. | |||
| func RegisterEnum(typeName string, unusedNameMap map[int32]string, valueMap map[string]int32) { | |||
| if _, ok := enumValueMaps[typeName]; ok { | |||
| panic("proto: duplicate enum registered: " + typeName) | |||
| } | |||
| enumValueMaps[typeName] = valueMap | |||
| } | |||
| // EnumValueMap returns the mapping from names to integers of the | |||
| // enum type enumType, or a nil if not found. | |||
| func EnumValueMap(enumType string) map[string]int32 { | |||
| return enumValueMaps[enumType] | |||
| } | |||
| // A registry of all linked message types. | |||
| // The string is a fully-qualified proto name ("pkg.Message"). | |||
| var ( | |||
| protoTypedNils = make(map[string]Message) // a map from proto names to typed nil pointers | |||
| protoMapTypes = make(map[string]reflect.Type) // a map from proto names to map types | |||
| revProtoTypes = make(map[reflect.Type]string) | |||
| ) | |||
| // RegisterType is called from generated code and maps from the fully qualified | |||
| // proto name to the type (pointer to struct) of the protocol buffer. | |||
| func RegisterType(x Message, name string) { | |||
| if _, ok := protoTypedNils[name]; ok { | |||
| // TODO: Some day, make this a panic. | |||
| log.Printf("proto: duplicate proto type registered: %s", name) | |||
| return | |||
| } | |||
| t := reflect.TypeOf(x) | |||
| if v := reflect.ValueOf(x); v.Kind() == reflect.Ptr && v.Pointer() == 0 { | |||
| // Generated code always calls RegisterType with nil x. | |||
| // This check is just for extra safety. | |||
| protoTypedNils[name] = x | |||
| } else { | |||
| protoTypedNils[name] = reflect.Zero(t).Interface().(Message) | |||
| } | |||
| revProtoTypes[t] = name | |||
| } | |||
| // RegisterMapType is called from generated code and maps from the fully qualified | |||
| // proto name to the native map type of the proto map definition. | |||
| func RegisterMapType(x interface{}, name string) { | |||
| if reflect.TypeOf(x).Kind() != reflect.Map { | |||
| panic(fmt.Sprintf("RegisterMapType(%T, %q); want map", x, name)) | |||
| } | |||
| if _, ok := protoMapTypes[name]; ok { | |||
| log.Printf("proto: duplicate proto type registered: %s", name) | |||
| return | |||
| } | |||
| t := reflect.TypeOf(x) | |||
| protoMapTypes[name] = t | |||
| revProtoTypes[t] = name | |||
| } | |||
| // MessageName returns the fully-qualified proto name for the given message type. | |||
| func MessageName(x Message) string { | |||
| type xname interface { | |||
| XXX_MessageName() string | |||
| } | |||
| if m, ok := x.(xname); ok { | |||
| return m.XXX_MessageName() | |||
| } | |||
| return revProtoTypes[reflect.TypeOf(x)] | |||
| } | |||
| // MessageType returns the message type (pointer to struct) for a named message. | |||
| // The type is not guaranteed to implement proto.Message if the name refers to a | |||
| // map entry. | |||
| func MessageType(name string) reflect.Type { | |||
| if t, ok := protoTypedNils[name]; ok { | |||
| return reflect.TypeOf(t) | |||
| } | |||
| return protoMapTypes[name] | |||
| } | |||
| // A registry of all linked proto files. | |||
| var ( | |||
| protoFiles = make(map[string][]byte) // file name => fileDescriptor | |||
| ) | |||
| // RegisterFile is called from generated code and maps from the | |||
| // full file name of a .proto file to its compressed FileDescriptorProto. | |||
| func RegisterFile(filename string, fileDescriptor []byte) { | |||
| protoFiles[filename] = fileDescriptor | |||
| } | |||
| // FileDescriptor returns the compressed FileDescriptorProto for a .proto file. | |||
| func FileDescriptor(filename string) []byte { return protoFiles[filename] } | |||
| func (sp *StructProperties) Len() int { return len(sp.Prop) } | |||
| func (sp *StructProperties) Less(i, j int) bool { return false } | |||
| func (sp *StructProperties) Swap(i, j int) { return } | |||
| @@ -0,0 +1,167 @@ | |||
| // Copyright 2019 The Go Authors. All rights reserved. | |||
| // Use of this source code is governed by a BSD-style | |||
| // license that can be found in the LICENSE file. | |||
| // Package proto provides functionality for handling protocol buffer messages. | |||
| // In particular, it provides marshaling and unmarshaling between a protobuf | |||
| // message and the binary wire format. | |||
| // | |||
| // See https://developers.google.com/protocol-buffers/docs/gotutorial for | |||
| // more information. | |||
| // | |||
| // Deprecated: Use the "google.golang.org/protobuf/proto" package instead. | |||
| package proto | |||
| import ( | |||
| protoV2 "google.golang.org/protobuf/proto" | |||
| "google.golang.org/protobuf/reflect/protoreflect" | |||
| "google.golang.org/protobuf/runtime/protoiface" | |||
| "google.golang.org/protobuf/runtime/protoimpl" | |||
| ) | |||
| const ( | |||
| ProtoPackageIsVersion1 = true | |||
| ProtoPackageIsVersion2 = true | |||
| ProtoPackageIsVersion3 = true | |||
| ProtoPackageIsVersion4 = true | |||
| ) | |||
| // GeneratedEnum is any enum type generated by protoc-gen-go | |||
| // which is a named int32 kind. | |||
| // This type exists for documentation purposes. | |||
| type GeneratedEnum interface{} | |||
| // GeneratedMessage is any message type generated by protoc-gen-go | |||
| // which is a pointer to a named struct kind. | |||
| // This type exists for documentation purposes. | |||
| type GeneratedMessage interface{} | |||
| // Message is a protocol buffer message. | |||
| // | |||
| // This is the v1 version of the message interface and is marginally better | |||
| // than an empty interface as it lacks any method to programatically interact | |||
| // with the contents of the message. | |||
| // | |||
| // A v2 message is declared in "google.golang.org/protobuf/proto".Message and | |||
| // exposes protobuf reflection as a first-class feature of the interface. | |||
| // | |||
| // To convert a v1 message to a v2 message, use the MessageV2 function. | |||
| // To convert a v2 message to a v1 message, use the MessageV1 function. | |||
| type Message = protoiface.MessageV1 | |||
| // MessageV1 converts either a v1 or v2 message to a v1 message. | |||
| // It returns nil if m is nil. | |||
| func MessageV1(m GeneratedMessage) protoiface.MessageV1 { | |||
| return protoimpl.X.ProtoMessageV1Of(m) | |||
| } | |||
| // MessageV2 converts either a v1 or v2 message to a v2 message. | |||
| // It returns nil if m is nil. | |||
| func MessageV2(m GeneratedMessage) protoV2.Message { | |||
| return protoimpl.X.ProtoMessageV2Of(m) | |||
| } | |||
| // MessageReflect returns a reflective view for a message. | |||
| // It returns nil if m is nil. | |||
| func MessageReflect(m Message) protoreflect.Message { | |||
| return protoimpl.X.MessageOf(m) | |||
| } | |||
| // Marshaler is implemented by messages that can marshal themselves. | |||
| // This interface is used by the following functions: Size, Marshal, | |||
| // Buffer.Marshal, and Buffer.EncodeMessage. | |||
| // | |||
| // Deprecated: Do not implement. | |||
| type Marshaler interface { | |||
| // Marshal formats the encoded bytes of the message. | |||
| // It should be deterministic and emit valid protobuf wire data. | |||
| // The caller takes ownership of the returned buffer. | |||
| Marshal() ([]byte, error) | |||
| } | |||
| // Unmarshaler is implemented by messages that can unmarshal themselves. | |||
| // This interface is used by the following functions: Unmarshal, UnmarshalMerge, | |||
| // Buffer.Unmarshal, Buffer.DecodeMessage, and Buffer.DecodeGroup. | |||
| // | |||
| // Deprecated: Do not implement. | |||
| type Unmarshaler interface { | |||
| // Unmarshal parses the encoded bytes of the protobuf wire input. | |||
| // The provided buffer is only valid for during method call. | |||
| // It should not reset the receiver message. | |||
| Unmarshal([]byte) error | |||
| } | |||
| // Merger is implemented by messages that can merge themselves. | |||
| // This interface is used by the following functions: Clone and Merge. | |||
| // | |||
| // Deprecated: Do not implement. | |||
| type Merger interface { | |||
| // Merge merges the contents of src into the receiver message. | |||
| // It clones all data structures in src such that it aliases no mutable | |||
| // memory referenced by src. | |||
| Merge(src Message) | |||
| } | |||
| // RequiredNotSetError is an error type returned when | |||
| // marshaling or unmarshaling a message with missing required fields. | |||
| type RequiredNotSetError struct { | |||
| err error | |||
| } | |||
| func (e *RequiredNotSetError) Error() string { | |||
| if e.err != nil { | |||
| return e.err.Error() | |||
| } | |||
| return "proto: required field not set" | |||
| } | |||
| func (e *RequiredNotSetError) RequiredNotSet() bool { | |||
| return true | |||
| } | |||
| func checkRequiredNotSet(m protoV2.Message) error { | |||
| if err := protoV2.CheckInitialized(m); err != nil { | |||
| return &RequiredNotSetError{err: err} | |||
| } | |||
| return nil | |||
| } | |||
| // Clone returns a deep copy of src. | |||
| func Clone(src Message) Message { | |||
| return MessageV1(protoV2.Clone(MessageV2(src))) | |||
| } | |||
| // Merge merges src into dst, which must be messages of the same type. | |||
| // | |||
| // Populated scalar fields in src are copied to dst, while populated | |||
| // singular messages in src are merged into dst by recursively calling Merge. | |||
| // The elements of every list field in src is appended to the corresponded | |||
| // list fields in dst. The entries of every map field in src is copied into | |||
| // the corresponding map field in dst, possibly replacing existing entries. | |||
| // The unknown fields of src are appended to the unknown fields of dst. | |||
| func Merge(dst, src Message) { | |||
| protoV2.Merge(MessageV2(dst), MessageV2(src)) | |||
| } | |||
| // Equal reports whether two messages are equal. | |||
| // If two messages marshal to the same bytes under deterministic serialization, | |||
| // then Equal is guaranteed to report true. | |||
| // | |||
| // Two messages are equal if they are the same protobuf message type, | |||
| // have the same set of populated known and extension field values, | |||
| // and the same set of unknown fields values. | |||
| // | |||
| // Scalar values are compared with the equivalent of the == operator in Go, | |||
| // except bytes values which are compared using bytes.Equal and | |||
| // floating point values which specially treat NaNs as equal. | |||
| // Message values are compared by recursively calling Equal. | |||
| // Lists are equal if each element value is also equal. | |||
| // Maps are equal if they have the same set of keys, where the pair of values | |||
| // for each key is also equal. | |||
| func Equal(x, y Message) bool { | |||
| return protoV2.Equal(MessageV2(x), MessageV2(y)) | |||
| } | |||
| func isMessageSet(md protoreflect.MessageDescriptor) bool { | |||
| ms, ok := md.(interface{ IsMessageSet() bool }) | |||
| return ok && ms.IsMessageSet() | |||
| } | |||
| @@ -0,0 +1,323 @@ | |||
| // Copyright 2019 The Go Authors. All rights reserved. | |||
| // Use of this source code is governed by a BSD-style | |||
| // license that can be found in the LICENSE file. | |||
| package proto | |||
| import ( | |||
| "bytes" | |||
| "compress/gzip" | |||
| "fmt" | |||
| "io/ioutil" | |||
| "reflect" | |||
| "strings" | |||
| "sync" | |||
| "google.golang.org/protobuf/reflect/protoreflect" | |||
| "google.golang.org/protobuf/reflect/protoregistry" | |||
| "google.golang.org/protobuf/runtime/protoimpl" | |||
| ) | |||
| // filePath is the path to the proto source file. | |||
| type filePath = string // e.g., "google/protobuf/descriptor.proto" | |||
| // fileDescGZIP is the compressed contents of the encoded FileDescriptorProto. | |||
| type fileDescGZIP = []byte | |||
| var fileCache sync.Map // map[filePath]fileDescGZIP | |||
| // RegisterFile is called from generated code to register the compressed | |||
| // FileDescriptorProto with the file path for a proto source file. | |||
| // | |||
| // Deprecated: Use protoregistry.GlobalFiles.Register instead. | |||
| func RegisterFile(s filePath, d fileDescGZIP) { | |||
| // Decompress the descriptor. | |||
| zr, err := gzip.NewReader(bytes.NewReader(d)) | |||
| if err != nil { | |||
| panic(fmt.Sprintf("proto: invalid compressed file descriptor: %v", err)) | |||
| } | |||
| b, err := ioutil.ReadAll(zr) | |||
| if err != nil { | |||
| panic(fmt.Sprintf("proto: invalid compressed file descriptor: %v", err)) | |||
| } | |||
| // Construct a protoreflect.FileDescriptor from the raw descriptor. | |||
| // Note that DescBuilder.Build automatically registers the constructed | |||
| // file descriptor with the v2 registry. | |||
| protoimpl.DescBuilder{RawDescriptor: b}.Build() | |||
| // Locally cache the raw descriptor form for the file. | |||
| fileCache.Store(s, d) | |||
| } | |||
| // FileDescriptor returns the compressed FileDescriptorProto given the file path | |||
| // for a proto source file. It returns nil if not found. | |||
| // | |||
| // Deprecated: Use protoregistry.GlobalFiles.RangeFilesByPath instead. | |||
| func FileDescriptor(s filePath) fileDescGZIP { | |||
| if v, ok := fileCache.Load(s); ok { | |||
| return v.(fileDescGZIP) | |||
| } | |||
| // Find the descriptor in the v2 registry. | |||
| var b []byte | |||
| if fd, _ := protoregistry.GlobalFiles.FindFileByPath(s); fd != nil { | |||
| if fd, ok := fd.(interface{ ProtoLegacyRawDesc() []byte }); ok { | |||
| b = fd.ProtoLegacyRawDesc() | |||
| } else { | |||
| // TODO: Use protodesc.ToFileDescriptorProto to construct | |||
| // a descriptorpb.FileDescriptorProto and marshal it. | |||
| // However, doing so causes the proto package to have a dependency | |||
| // on descriptorpb, leading to cyclic dependency issues. | |||
| } | |||
| } | |||
| // Locally cache the raw descriptor form for the file. | |||
| if len(b) > 0 { | |||
| v, _ := fileCache.LoadOrStore(s, protoimpl.X.CompressGZIP(b)) | |||
| return v.(fileDescGZIP) | |||
| } | |||
| return nil | |||
| } | |||
| // enumName is the name of an enum. For historical reasons, the enum name is | |||
| // neither the full Go name nor the full protobuf name of the enum. | |||
| // The name is the dot-separated combination of just the proto package that the | |||
| // enum is declared within followed by the Go type name of the generated enum. | |||
| type enumName = string // e.g., "my.proto.package.GoMessage_GoEnum" | |||
| // enumsByName maps enum values by name to their numeric counterpart. | |||
| type enumsByName = map[string]int32 | |||
| // enumsByNumber maps enum values by number to their name counterpart. | |||
| type enumsByNumber = map[int32]string | |||
| var enumCache sync.Map // map[enumName]enumsByName | |||
| var numFilesCache sync.Map // map[protoreflect.FullName]int | |||
| // RegisterEnum is called from the generated code to register the mapping of | |||
| // enum value names to enum numbers for the enum identified by s. | |||
| // | |||
| // Deprecated: Use protoregistry.GlobalTypes.Register instead. | |||
| func RegisterEnum(s enumName, _ enumsByNumber, m enumsByName) { | |||
| if _, ok := enumCache.Load(s); ok { | |||
| panic("proto: duplicate enum registered: " + s) | |||
| } | |||
| enumCache.Store(s, m) | |||
| // This does not forward registration to the v2 registry since this API | |||
| // lacks sufficient information to construct a complete v2 enum descriptor. | |||
| } | |||
| // EnumValueMap returns the mapping from enum value names to enum numbers for | |||
| // the enum of the given name. It returns nil if not found. | |||
| // | |||
| // Deprecated: Use protoregistry.GlobalTypes.FindEnumByName instead. | |||
| func EnumValueMap(s enumName) enumsByName { | |||
| if v, ok := enumCache.Load(s); ok { | |||
| return v.(enumsByName) | |||
| } | |||
| // Check whether the cache is stale. If the number of files in the current | |||
| // package differs, then it means that some enums may have been recently | |||
| // registered upstream that we do not know about. | |||
| var protoPkg protoreflect.FullName | |||
| if i := strings.LastIndexByte(s, '.'); i >= 0 { | |||
| protoPkg = protoreflect.FullName(s[:i]) | |||
| } | |||
| v, _ := numFilesCache.Load(protoPkg) | |||
| numFiles, _ := v.(int) | |||
| if protoregistry.GlobalFiles.NumFilesByPackage(protoPkg) == numFiles { | |||
| return nil // cache is up-to-date; was not found earlier | |||
| } | |||
| // Update the enum cache for all enums declared in the given proto package. | |||
| numFiles = 0 | |||
| protoregistry.GlobalFiles.RangeFilesByPackage(protoPkg, func(fd protoreflect.FileDescriptor) bool { | |||
| walkEnums(fd, func(ed protoreflect.EnumDescriptor) { | |||
| name := protoimpl.X.LegacyEnumName(ed) | |||
| if _, ok := enumCache.Load(name); !ok { | |||
| m := make(enumsByName) | |||
| evs := ed.Values() | |||
| for i := evs.Len() - 1; i >= 0; i-- { | |||
| ev := evs.Get(i) | |||
| m[string(ev.Name())] = int32(ev.Number()) | |||
| } | |||
| enumCache.LoadOrStore(name, m) | |||
| } | |||
| }) | |||
| numFiles++ | |||
| return true | |||
| }) | |||
| numFilesCache.Store(protoPkg, numFiles) | |||
| // Check cache again for enum map. | |||
| if v, ok := enumCache.Load(s); ok { | |||
| return v.(enumsByName) | |||
| } | |||
| return nil | |||
| } | |||
| // walkEnums recursively walks all enums declared in d. | |||
| func walkEnums(d interface { | |||
| Enums() protoreflect.EnumDescriptors | |||
| Messages() protoreflect.MessageDescriptors | |||
| }, f func(protoreflect.EnumDescriptor)) { | |||
| eds := d.Enums() | |||
| for i := eds.Len() - 1; i >= 0; i-- { | |||
| f(eds.Get(i)) | |||
| } | |||
| mds := d.Messages() | |||
| for i := mds.Len() - 1; i >= 0; i-- { | |||
| walkEnums(mds.Get(i), f) | |||
| } | |||
| } | |||
| // messageName is the full name of protobuf message. | |||
| type messageName = string | |||
| var messageTypeCache sync.Map // map[messageName]reflect.Type | |||
| // RegisterType is called from generated code to register the message Go type | |||
| // for a message of the given name. | |||
| // | |||
| // Deprecated: Use protoregistry.GlobalTypes.Register instead. | |||
| func RegisterType(m Message, s messageName) { | |||
| mt := protoimpl.X.LegacyMessageTypeOf(m, protoreflect.FullName(s)) | |||
| if err := protoregistry.GlobalTypes.RegisterMessage(mt); err != nil { | |||
| panic(err) | |||
| } | |||
| messageTypeCache.Store(s, reflect.TypeOf(m)) | |||
| } | |||
| // RegisterMapType is called from generated code to register the Go map type | |||
| // for a protobuf message representing a map entry. | |||
| // | |||
| // Deprecated: Do not use. | |||
| func RegisterMapType(m interface{}, s messageName) { | |||
| t := reflect.TypeOf(m) | |||
| if t.Kind() != reflect.Map { | |||
| panic(fmt.Sprintf("invalid map kind: %v", t)) | |||
| } | |||
| if _, ok := messageTypeCache.Load(s); ok { | |||
| panic(fmt.Errorf("proto: duplicate proto message registered: %s", s)) | |||
| } | |||
| messageTypeCache.Store(s, t) | |||
| } | |||
| // MessageType returns the message type for a named message. | |||
| // It returns nil if not found. | |||
| // | |||
| // Deprecated: Use protoregistry.GlobalTypes.FindMessageByName instead. | |||
| func MessageType(s messageName) reflect.Type { | |||
| if v, ok := messageTypeCache.Load(s); ok { | |||
| return v.(reflect.Type) | |||
| } | |||
| // Derive the message type from the v2 registry. | |||
| var t reflect.Type | |||
| if mt, _ := protoregistry.GlobalTypes.FindMessageByName(protoreflect.FullName(s)); mt != nil { | |||
| t = messageGoType(mt) | |||
| } | |||
| // If we could not get a concrete type, it is possible that it is a | |||
| // pseudo-message for a map entry. | |||
| if t == nil { | |||
| d, _ := protoregistry.GlobalFiles.FindDescriptorByName(protoreflect.FullName(s)) | |||
| if md, _ := d.(protoreflect.MessageDescriptor); md != nil && md.IsMapEntry() { | |||
| kt := goTypeForField(md.Fields().ByNumber(1)) | |||
| vt := goTypeForField(md.Fields().ByNumber(2)) | |||
| t = reflect.MapOf(kt, vt) | |||
| } | |||
| } | |||
| // Locally cache the message type for the given name. | |||
| if t != nil { | |||
| v, _ := messageTypeCache.LoadOrStore(s, t) | |||
| return v.(reflect.Type) | |||
| } | |||
| return nil | |||
| } | |||
| func goTypeForField(fd protoreflect.FieldDescriptor) reflect.Type { | |||
| switch k := fd.Kind(); k { | |||
| case protoreflect.EnumKind: | |||
| if et, _ := protoregistry.GlobalTypes.FindEnumByName(fd.Enum().FullName()); et != nil { | |||
| return enumGoType(et) | |||
| } | |||
| return reflect.TypeOf(protoreflect.EnumNumber(0)) | |||
| case protoreflect.MessageKind, protoreflect.GroupKind: | |||
| if mt, _ := protoregistry.GlobalTypes.FindMessageByName(fd.Message().FullName()); mt != nil { | |||
| return messageGoType(mt) | |||
| } | |||
| return reflect.TypeOf((*protoreflect.Message)(nil)).Elem() | |||
| default: | |||
| return reflect.TypeOf(fd.Default().Interface()) | |||
| } | |||
| } | |||
| func enumGoType(et protoreflect.EnumType) reflect.Type { | |||
| return reflect.TypeOf(et.New(0)) | |||
| } | |||
| func messageGoType(mt protoreflect.MessageType) reflect.Type { | |||
| return reflect.TypeOf(MessageV1(mt.Zero().Interface())) | |||
| } | |||
| // MessageName returns the full protobuf name for the given message type. | |||
| // | |||
| // Deprecated: Use protoreflect.MessageDescriptor.FullName instead. | |||
| func MessageName(m Message) messageName { | |||
| if m == nil { | |||
| return "" | |||
| } | |||
| if m, ok := m.(interface{ XXX_MessageName() messageName }); ok { | |||
| return m.XXX_MessageName() | |||
| } | |||
| return messageName(protoimpl.X.MessageDescriptorOf(m).FullName()) | |||
| } | |||
| // RegisterExtension is called from the generated code to register | |||
| // the extension descriptor. | |||
| // | |||
| // Deprecated: Use protoregistry.GlobalTypes.Register instead. | |||
| func RegisterExtension(d *ExtensionDesc) { | |||
| if err := protoregistry.GlobalTypes.RegisterExtension(d); err != nil { | |||
| panic(err) | |||
| } | |||
| } | |||
| type extensionsByNumber = map[int32]*ExtensionDesc | |||
| var extensionCache sync.Map // map[messageName]extensionsByNumber | |||
| // RegisteredExtensions returns a map of the registered extensions for the | |||
| // provided protobuf message, indexed by the extension field number. | |||
| // | |||
| // Deprecated: Use protoregistry.GlobalTypes.RangeExtensionsByMessage instead. | |||
| func RegisteredExtensions(m Message) extensionsByNumber { | |||
| // Check whether the cache is stale. If the number of extensions for | |||
| // the given message differs, then it means that some extensions were | |||
| // recently registered upstream that we do not know about. | |||
| s := MessageName(m) | |||
| v, _ := extensionCache.Load(s) | |||
| xs, _ := v.(extensionsByNumber) | |||
| if protoregistry.GlobalTypes.NumExtensionsByMessage(protoreflect.FullName(s)) == len(xs) { | |||
| return xs // cache is up-to-date | |||
| } | |||
| // Cache is stale, re-compute the extensions map. | |||
| xs = make(extensionsByNumber) | |||
| protoregistry.GlobalTypes.RangeExtensionsByMessage(protoreflect.FullName(s), func(xt protoreflect.ExtensionType) bool { | |||
| if xd, ok := xt.(*ExtensionDesc); ok { | |||
| xs[int32(xt.TypeDescriptor().Number())] = xd | |||
| } else { | |||
| // TODO: This implies that the protoreflect.ExtensionType is a | |||
| // custom type not generated by protoc-gen-go. We could try and | |||
| // convert the type to an ExtensionDesc. | |||
| } | |||
| return true | |||
| }) | |||
| extensionCache.Store(s, xs) | |||
| return xs | |||
| } | |||
| @@ -1,654 +0,0 @@ | |||
| // Go support for Protocol Buffers - Google's data interchange format | |||
| // | |||
| // Copyright 2016 The Go Authors. All rights reserved. | |||
| // https://github.com/golang/protobuf | |||
| // | |||
| // Redistribution and use in source and binary forms, with or without | |||
| // modification, are permitted provided that the following conditions are | |||
| // met: | |||
| // | |||
| // * Redistributions of source code must retain the above copyright | |||
| // notice, this list of conditions and the following disclaimer. | |||
| // * Redistributions in binary form must reproduce the above | |||
| // copyright notice, this list of conditions and the following disclaimer | |||
| // in the documentation and/or other materials provided with the | |||
| // distribution. | |||
| // * Neither the name of Google Inc. nor the names of its | |||
| // contributors may be used to endorse or promote products derived from | |||
| // this software without specific prior written permission. | |||
| // | |||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| package proto | |||
| import ( | |||
| "fmt" | |||
| "reflect" | |||
| "strings" | |||
| "sync" | |||
| "sync/atomic" | |||
| ) | |||
| // Merge merges the src message into dst. | |||
| // This assumes that dst and src of the same type and are non-nil. | |||
| func (a *InternalMessageInfo) Merge(dst, src Message) { | |||
| mi := atomicLoadMergeInfo(&a.merge) | |||
| if mi == nil { | |||
| mi = getMergeInfo(reflect.TypeOf(dst).Elem()) | |||
| atomicStoreMergeInfo(&a.merge, mi) | |||
| } | |||
| mi.merge(toPointer(&dst), toPointer(&src)) | |||
| } | |||
| type mergeInfo struct { | |||
| typ reflect.Type | |||
| initialized int32 // 0: only typ is valid, 1: everything is valid | |||
| lock sync.Mutex | |||
| fields []mergeFieldInfo | |||
| unrecognized field // Offset of XXX_unrecognized | |||
| } | |||
| type mergeFieldInfo struct { | |||
| field field // Offset of field, guaranteed to be valid | |||
| // isPointer reports whether the value in the field is a pointer. | |||
| // This is true for the following situations: | |||
| // * Pointer to struct | |||
| // * Pointer to basic type (proto2 only) | |||
| // * Slice (first value in slice header is a pointer) | |||
| // * String (first value in string header is a pointer) | |||
| isPointer bool | |||
| // basicWidth reports the width of the field assuming that it is directly | |||
| // embedded in the struct (as is the case for basic types in proto3). | |||
| // The possible values are: | |||
| // 0: invalid | |||
| // 1: bool | |||
| // 4: int32, uint32, float32 | |||
| // 8: int64, uint64, float64 | |||
| basicWidth int | |||
| // Where dst and src are pointers to the types being merged. | |||
| merge func(dst, src pointer) | |||
| } | |||
| var ( | |||
| mergeInfoMap = map[reflect.Type]*mergeInfo{} | |||
| mergeInfoLock sync.Mutex | |||
| ) | |||
| func getMergeInfo(t reflect.Type) *mergeInfo { | |||
| mergeInfoLock.Lock() | |||
| defer mergeInfoLock.Unlock() | |||
| mi := mergeInfoMap[t] | |||
| if mi == nil { | |||
| mi = &mergeInfo{typ: t} | |||
| mergeInfoMap[t] = mi | |||
| } | |||
| return mi | |||
| } | |||
| // merge merges src into dst assuming they are both of type *mi.typ. | |||
| func (mi *mergeInfo) merge(dst, src pointer) { | |||
| if dst.isNil() { | |||
| panic("proto: nil destination") | |||
| } | |||
| if src.isNil() { | |||
| return // Nothing to do. | |||
| } | |||
| if atomic.LoadInt32(&mi.initialized) == 0 { | |||
| mi.computeMergeInfo() | |||
| } | |||
| for _, fi := range mi.fields { | |||
| sfp := src.offset(fi.field) | |||
| // As an optimization, we can avoid the merge function call cost | |||
| // if we know for sure that the source will have no effect | |||
| // by checking if it is the zero value. | |||
| if unsafeAllowed { | |||
| if fi.isPointer && sfp.getPointer().isNil() { // Could be slice or string | |||
| continue | |||
| } | |||
| if fi.basicWidth > 0 { | |||
| switch { | |||
| case fi.basicWidth == 1 && !*sfp.toBool(): | |||
| continue | |||
| case fi.basicWidth == 4 && *sfp.toUint32() == 0: | |||
| continue | |||
| case fi.basicWidth == 8 && *sfp.toUint64() == 0: | |||
| continue | |||
| } | |||
| } | |||
| } | |||
| dfp := dst.offset(fi.field) | |||
| fi.merge(dfp, sfp) | |||
| } | |||
| // TODO: Make this faster? | |||
| out := dst.asPointerTo(mi.typ).Elem() | |||
| in := src.asPointerTo(mi.typ).Elem() | |||
| if emIn, err := extendable(in.Addr().Interface()); err == nil { | |||
| emOut, _ := extendable(out.Addr().Interface()) | |||
| mIn, muIn := emIn.extensionsRead() | |||
| if mIn != nil { | |||
| mOut := emOut.extensionsWrite() | |||
| muIn.Lock() | |||
| mergeExtension(mOut, mIn) | |||
| muIn.Unlock() | |||
| } | |||
| } | |||
| if mi.unrecognized.IsValid() { | |||
| if b := *src.offset(mi.unrecognized).toBytes(); len(b) > 0 { | |||
| *dst.offset(mi.unrecognized).toBytes() = append([]byte(nil), b...) | |||
| } | |||
| } | |||
| } | |||
| func (mi *mergeInfo) computeMergeInfo() { | |||
| mi.lock.Lock() | |||
| defer mi.lock.Unlock() | |||
| if mi.initialized != 0 { | |||
| return | |||
| } | |||
| t := mi.typ | |||
| n := t.NumField() | |||
| props := GetProperties(t) | |||
| for i := 0; i < n; i++ { | |||
| f := t.Field(i) | |||
| if strings.HasPrefix(f.Name, "XXX_") { | |||
| continue | |||
| } | |||
| mfi := mergeFieldInfo{field: toField(&f)} | |||
| tf := f.Type | |||
| // As an optimization, we can avoid the merge function call cost | |||
| // if we know for sure that the source will have no effect | |||
| // by checking if it is the zero value. | |||
| if unsafeAllowed { | |||
| switch tf.Kind() { | |||
| case reflect.Ptr, reflect.Slice, reflect.String: | |||
| // As a special case, we assume slices and strings are pointers | |||
| // since we know that the first field in the SliceSlice or | |||
| // StringHeader is a data pointer. | |||
| mfi.isPointer = true | |||
| case reflect.Bool: | |||
| mfi.basicWidth = 1 | |||
| case reflect.Int32, reflect.Uint32, reflect.Float32: | |||
| mfi.basicWidth = 4 | |||
| case reflect.Int64, reflect.Uint64, reflect.Float64: | |||
| mfi.basicWidth = 8 | |||
| } | |||
| } | |||
| // Unwrap tf to get at its most basic type. | |||
| var isPointer, isSlice bool | |||
| if tf.Kind() == reflect.Slice && tf.Elem().Kind() != reflect.Uint8 { | |||
| isSlice = true | |||
| tf = tf.Elem() | |||
| } | |||
| if tf.Kind() == reflect.Ptr { | |||
| isPointer = true | |||
| tf = tf.Elem() | |||
| } | |||
| if isPointer && isSlice && tf.Kind() != reflect.Struct { | |||
| panic("both pointer and slice for basic type in " + tf.Name()) | |||
| } | |||
| switch tf.Kind() { | |||
| case reflect.Int32: | |||
| switch { | |||
| case isSlice: // E.g., []int32 | |||
| mfi.merge = func(dst, src pointer) { | |||
| // NOTE: toInt32Slice is not defined (see pointer_reflect.go). | |||
| /* | |||
| sfsp := src.toInt32Slice() | |||
| if *sfsp != nil { | |||
| dfsp := dst.toInt32Slice() | |||
| *dfsp = append(*dfsp, *sfsp...) | |||
| if *dfsp == nil { | |||
| *dfsp = []int64{} | |||
| } | |||
| } | |||
| */ | |||
| sfs := src.getInt32Slice() | |||
| if sfs != nil { | |||
| dfs := dst.getInt32Slice() | |||
| dfs = append(dfs, sfs...) | |||
| if dfs == nil { | |||
| dfs = []int32{} | |||
| } | |||
| dst.setInt32Slice(dfs) | |||
| } | |||
| } | |||
| case isPointer: // E.g., *int32 | |||
| mfi.merge = func(dst, src pointer) { | |||
| // NOTE: toInt32Ptr is not defined (see pointer_reflect.go). | |||
| /* | |||
| sfpp := src.toInt32Ptr() | |||
| if *sfpp != nil { | |||
| dfpp := dst.toInt32Ptr() | |||
| if *dfpp == nil { | |||
| *dfpp = Int32(**sfpp) | |||
| } else { | |||
| **dfpp = **sfpp | |||
| } | |||
| } | |||
| */ | |||
| sfp := src.getInt32Ptr() | |||
| if sfp != nil { | |||
| dfp := dst.getInt32Ptr() | |||
| if dfp == nil { | |||
| dst.setInt32Ptr(*sfp) | |||
| } else { | |||
| *dfp = *sfp | |||
| } | |||
| } | |||
| } | |||
| default: // E.g., int32 | |||
| mfi.merge = func(dst, src pointer) { | |||
| if v := *src.toInt32(); v != 0 { | |||
| *dst.toInt32() = v | |||
| } | |||
| } | |||
| } | |||
| case reflect.Int64: | |||
| switch { | |||
| case isSlice: // E.g., []int64 | |||
| mfi.merge = func(dst, src pointer) { | |||
| sfsp := src.toInt64Slice() | |||
| if *sfsp != nil { | |||
| dfsp := dst.toInt64Slice() | |||
| *dfsp = append(*dfsp, *sfsp...) | |||
| if *dfsp == nil { | |||
| *dfsp = []int64{} | |||
| } | |||
| } | |||
| } | |||
| case isPointer: // E.g., *int64 | |||
| mfi.merge = func(dst, src pointer) { | |||
| sfpp := src.toInt64Ptr() | |||
| if *sfpp != nil { | |||
| dfpp := dst.toInt64Ptr() | |||
| if *dfpp == nil { | |||
| *dfpp = Int64(**sfpp) | |||
| } else { | |||
| **dfpp = **sfpp | |||
| } | |||
| } | |||
| } | |||
| default: // E.g., int64 | |||
| mfi.merge = func(dst, src pointer) { | |||
| if v := *src.toInt64(); v != 0 { | |||
| *dst.toInt64() = v | |||
| } | |||
| } | |||
| } | |||
| case reflect.Uint32: | |||
| switch { | |||
| case isSlice: // E.g., []uint32 | |||
| mfi.merge = func(dst, src pointer) { | |||
| sfsp := src.toUint32Slice() | |||
| if *sfsp != nil { | |||
| dfsp := dst.toUint32Slice() | |||
| *dfsp = append(*dfsp, *sfsp...) | |||
| if *dfsp == nil { | |||
| *dfsp = []uint32{} | |||
| } | |||
| } | |||
| } | |||
| case isPointer: // E.g., *uint32 | |||
| mfi.merge = func(dst, src pointer) { | |||
| sfpp := src.toUint32Ptr() | |||
| if *sfpp != nil { | |||
| dfpp := dst.toUint32Ptr() | |||
| if *dfpp == nil { | |||
| *dfpp = Uint32(**sfpp) | |||
| } else { | |||
| **dfpp = **sfpp | |||
| } | |||
| } | |||
| } | |||
| default: // E.g., uint32 | |||
| mfi.merge = func(dst, src pointer) { | |||
| if v := *src.toUint32(); v != 0 { | |||
| *dst.toUint32() = v | |||
| } | |||
| } | |||
| } | |||
| case reflect.Uint64: | |||
| switch { | |||
| case isSlice: // E.g., []uint64 | |||
| mfi.merge = func(dst, src pointer) { | |||
| sfsp := src.toUint64Slice() | |||
| if *sfsp != nil { | |||
| dfsp := dst.toUint64Slice() | |||
| *dfsp = append(*dfsp, *sfsp...) | |||
| if *dfsp == nil { | |||
| *dfsp = []uint64{} | |||
| } | |||
| } | |||
| } | |||
| case isPointer: // E.g., *uint64 | |||
| mfi.merge = func(dst, src pointer) { | |||
| sfpp := src.toUint64Ptr() | |||
| if *sfpp != nil { | |||
| dfpp := dst.toUint64Ptr() | |||
| if *dfpp == nil { | |||
| *dfpp = Uint64(**sfpp) | |||
| } else { | |||
| **dfpp = **sfpp | |||
| } | |||
| } | |||
| } | |||
| default: // E.g., uint64 | |||
| mfi.merge = func(dst, src pointer) { | |||
| if v := *src.toUint64(); v != 0 { | |||
| *dst.toUint64() = v | |||
| } | |||
| } | |||
| } | |||
| case reflect.Float32: | |||
| switch { | |||
| case isSlice: // E.g., []float32 | |||
| mfi.merge = func(dst, src pointer) { | |||
| sfsp := src.toFloat32Slice() | |||
| if *sfsp != nil { | |||
| dfsp := dst.toFloat32Slice() | |||
| *dfsp = append(*dfsp, *sfsp...) | |||
| if *dfsp == nil { | |||
| *dfsp = []float32{} | |||
| } | |||
| } | |||
| } | |||
| case isPointer: // E.g., *float32 | |||
| mfi.merge = func(dst, src pointer) { | |||
| sfpp := src.toFloat32Ptr() | |||
| if *sfpp != nil { | |||
| dfpp := dst.toFloat32Ptr() | |||
| if *dfpp == nil { | |||
| *dfpp = Float32(**sfpp) | |||
| } else { | |||
| **dfpp = **sfpp | |||
| } | |||
| } | |||
| } | |||
| default: // E.g., float32 | |||
| mfi.merge = func(dst, src pointer) { | |||
| if v := *src.toFloat32(); v != 0 { | |||
| *dst.toFloat32() = v | |||
| } | |||
| } | |||
| } | |||
| case reflect.Float64: | |||
| switch { | |||
| case isSlice: // E.g., []float64 | |||
| mfi.merge = func(dst, src pointer) { | |||
| sfsp := src.toFloat64Slice() | |||
| if *sfsp != nil { | |||
| dfsp := dst.toFloat64Slice() | |||
| *dfsp = append(*dfsp, *sfsp...) | |||
| if *dfsp == nil { | |||
| *dfsp = []float64{} | |||
| } | |||
| } | |||
| } | |||
| case isPointer: // E.g., *float64 | |||
| mfi.merge = func(dst, src pointer) { | |||
| sfpp := src.toFloat64Ptr() | |||
| if *sfpp != nil { | |||
| dfpp := dst.toFloat64Ptr() | |||
| if *dfpp == nil { | |||
| *dfpp = Float64(**sfpp) | |||
| } else { | |||
| **dfpp = **sfpp | |||
| } | |||
| } | |||
| } | |||
| default: // E.g., float64 | |||
| mfi.merge = func(dst, src pointer) { | |||
| if v := *src.toFloat64(); v != 0 { | |||
| *dst.toFloat64() = v | |||
| } | |||
| } | |||
| } | |||
| case reflect.Bool: | |||
| switch { | |||
| case isSlice: // E.g., []bool | |||
| mfi.merge = func(dst, src pointer) { | |||
| sfsp := src.toBoolSlice() | |||
| if *sfsp != nil { | |||
| dfsp := dst.toBoolSlice() | |||
| *dfsp = append(*dfsp, *sfsp...) | |||
| if *dfsp == nil { | |||
| *dfsp = []bool{} | |||
| } | |||
| } | |||
| } | |||
| case isPointer: // E.g., *bool | |||
| mfi.merge = func(dst, src pointer) { | |||
| sfpp := src.toBoolPtr() | |||
| if *sfpp != nil { | |||
| dfpp := dst.toBoolPtr() | |||
| if *dfpp == nil { | |||
| *dfpp = Bool(**sfpp) | |||
| } else { | |||
| **dfpp = **sfpp | |||
| } | |||
| } | |||
| } | |||
| default: // E.g., bool | |||
| mfi.merge = func(dst, src pointer) { | |||
| if v := *src.toBool(); v { | |||
| *dst.toBool() = v | |||
| } | |||
| } | |||
| } | |||
| case reflect.String: | |||
| switch { | |||
| case isSlice: // E.g., []string | |||
| mfi.merge = func(dst, src pointer) { | |||
| sfsp := src.toStringSlice() | |||
| if *sfsp != nil { | |||
| dfsp := dst.toStringSlice() | |||
| *dfsp = append(*dfsp, *sfsp...) | |||
| if *dfsp == nil { | |||
| *dfsp = []string{} | |||
| } | |||
| } | |||
| } | |||
| case isPointer: // E.g., *string | |||
| mfi.merge = func(dst, src pointer) { | |||
| sfpp := src.toStringPtr() | |||
| if *sfpp != nil { | |||
| dfpp := dst.toStringPtr() | |||
| if *dfpp == nil { | |||
| *dfpp = String(**sfpp) | |||
| } else { | |||
| **dfpp = **sfpp | |||
| } | |||
| } | |||
| } | |||
| default: // E.g., string | |||
| mfi.merge = func(dst, src pointer) { | |||
| if v := *src.toString(); v != "" { | |||
| *dst.toString() = v | |||
| } | |||
| } | |||
| } | |||
| case reflect.Slice: | |||
| isProto3 := props.Prop[i].proto3 | |||
| switch { | |||
| case isPointer: | |||
| panic("bad pointer in byte slice case in " + tf.Name()) | |||
| case tf.Elem().Kind() != reflect.Uint8: | |||
| panic("bad element kind in byte slice case in " + tf.Name()) | |||
| case isSlice: // E.g., [][]byte | |||
| mfi.merge = func(dst, src pointer) { | |||
| sbsp := src.toBytesSlice() | |||
| if *sbsp != nil { | |||
| dbsp := dst.toBytesSlice() | |||
| for _, sb := range *sbsp { | |||
| if sb == nil { | |||
| *dbsp = append(*dbsp, nil) | |||
| } else { | |||
| *dbsp = append(*dbsp, append([]byte{}, sb...)) | |||
| } | |||
| } | |||
| if *dbsp == nil { | |||
| *dbsp = [][]byte{} | |||
| } | |||
| } | |||
| } | |||
| default: // E.g., []byte | |||
| mfi.merge = func(dst, src pointer) { | |||
| sbp := src.toBytes() | |||
| if *sbp != nil { | |||
| dbp := dst.toBytes() | |||
| if !isProto3 || len(*sbp) > 0 { | |||
| *dbp = append([]byte{}, *sbp...) | |||
| } | |||
| } | |||
| } | |||
| } | |||
| case reflect.Struct: | |||
| switch { | |||
| case !isPointer: | |||
| panic(fmt.Sprintf("message field %s without pointer", tf)) | |||
| case isSlice: // E.g., []*pb.T | |||
| mi := getMergeInfo(tf) | |||
| mfi.merge = func(dst, src pointer) { | |||
| sps := src.getPointerSlice() | |||
| if sps != nil { | |||
| dps := dst.getPointerSlice() | |||
| for _, sp := range sps { | |||
| var dp pointer | |||
| if !sp.isNil() { | |||
| dp = valToPointer(reflect.New(tf)) | |||
| mi.merge(dp, sp) | |||
| } | |||
| dps = append(dps, dp) | |||
| } | |||
| if dps == nil { | |||
| dps = []pointer{} | |||
| } | |||
| dst.setPointerSlice(dps) | |||
| } | |||
| } | |||
| default: // E.g., *pb.T | |||
| mi := getMergeInfo(tf) | |||
| mfi.merge = func(dst, src pointer) { | |||
| sp := src.getPointer() | |||
| if !sp.isNil() { | |||
| dp := dst.getPointer() | |||
| if dp.isNil() { | |||
| dp = valToPointer(reflect.New(tf)) | |||
| dst.setPointer(dp) | |||
| } | |||
| mi.merge(dp, sp) | |||
| } | |||
| } | |||
| } | |||
| case reflect.Map: | |||
| switch { | |||
| case isPointer || isSlice: | |||
| panic("bad pointer or slice in map case in " + tf.Name()) | |||
| default: // E.g., map[K]V | |||
| mfi.merge = func(dst, src pointer) { | |||
| sm := src.asPointerTo(tf).Elem() | |||
| if sm.Len() == 0 { | |||
| return | |||
| } | |||
| dm := dst.asPointerTo(tf).Elem() | |||
| if dm.IsNil() { | |||
| dm.Set(reflect.MakeMap(tf)) | |||
| } | |||
| switch tf.Elem().Kind() { | |||
| case reflect.Ptr: // Proto struct (e.g., *T) | |||
| for _, key := range sm.MapKeys() { | |||
| val := sm.MapIndex(key) | |||
| val = reflect.ValueOf(Clone(val.Interface().(Message))) | |||
| dm.SetMapIndex(key, val) | |||
| } | |||
| case reflect.Slice: // E.g. Bytes type (e.g., []byte) | |||
| for _, key := range sm.MapKeys() { | |||
| val := sm.MapIndex(key) | |||
| val = reflect.ValueOf(append([]byte{}, val.Bytes()...)) | |||
| dm.SetMapIndex(key, val) | |||
| } | |||
| default: // Basic type (e.g., string) | |||
| for _, key := range sm.MapKeys() { | |||
| val := sm.MapIndex(key) | |||
| dm.SetMapIndex(key, val) | |||
| } | |||
| } | |||
| } | |||
| } | |||
| case reflect.Interface: | |||
| // Must be oneof field. | |||
| switch { | |||
| case isPointer || isSlice: | |||
| panic("bad pointer or slice in interface case in " + tf.Name()) | |||
| default: // E.g., interface{} | |||
| // TODO: Make this faster? | |||
| mfi.merge = func(dst, src pointer) { | |||
| su := src.asPointerTo(tf).Elem() | |||
| if !su.IsNil() { | |||
| du := dst.asPointerTo(tf).Elem() | |||
| typ := su.Elem().Type() | |||
| if du.IsNil() || du.Elem().Type() != typ { | |||
| du.Set(reflect.New(typ.Elem())) // Initialize interface if empty | |||
| } | |||
| sv := su.Elem().Elem().Field(0) | |||
| if sv.Kind() == reflect.Ptr && sv.IsNil() { | |||
| return | |||
| } | |||
| dv := du.Elem().Elem().Field(0) | |||
| if dv.Kind() == reflect.Ptr && dv.IsNil() { | |||
| dv.Set(reflect.New(sv.Type().Elem())) // Initialize proto message if empty | |||
| } | |||
| switch sv.Type().Kind() { | |||
| case reflect.Ptr: // Proto struct (e.g., *T) | |||
| Merge(dv.Interface().(Message), sv.Interface().(Message)) | |||
| case reflect.Slice: // E.g. Bytes type (e.g., []byte) | |||
| dv.Set(reflect.ValueOf(append([]byte{}, sv.Bytes()...))) | |||
| default: // Basic type (e.g., string) | |||
| dv.Set(sv) | |||
| } | |||
| } | |||
| } | |||
| } | |||
| default: | |||
| panic(fmt.Sprintf("merger not found for type:%s", tf)) | |||
| } | |||
| mi.fields = append(mi.fields, mfi) | |||
| } | |||
| mi.unrecognized = invalidField | |||
| if f, ok := t.FieldByName("XXX_unrecognized"); ok { | |||
| if f.Type != reflect.TypeOf([]byte{}) { | |||
| panic("expected XXX_unrecognized to be of type []byte") | |||
| } | |||
| mi.unrecognized = toField(&f) | |||
| } | |||
| atomic.StoreInt32(&mi.initialized, 1) | |||
| } | |||
| @@ -1,845 +0,0 @@ | |||
| // Go support for Protocol Buffers - Google's data interchange format | |||
| // | |||
| // Copyright 2010 The Go Authors. All rights reserved. | |||
| // https://github.com/golang/protobuf | |||
| // | |||
| // Redistribution and use in source and binary forms, with or without | |||
| // modification, are permitted provided that the following conditions are | |||
| // met: | |||
| // | |||
| // * Redistributions of source code must retain the above copyright | |||
| // notice, this list of conditions and the following disclaimer. | |||
| // * Redistributions in binary form must reproduce the above | |||
| // copyright notice, this list of conditions and the following disclaimer | |||
| // in the documentation and/or other materials provided with the | |||
| // distribution. | |||
| // * Neither the name of Google Inc. nor the names of its | |||
| // contributors may be used to endorse or promote products derived from | |||
| // this software without specific prior written permission. | |||
| // | |||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| package proto | |||
| // Functions for writing the text protocol buffer format. | |||
| import ( | |||
| "bufio" | |||
| "bytes" | |||
| "encoding" | |||
| "errors" | |||
| "fmt" | |||
| "io" | |||
| "log" | |||
| "math" | |||
| "reflect" | |||
| "sort" | |||
| "strings" | |||
| ) | |||
| var ( | |||
| newline = []byte("\n") | |||
| spaces = []byte(" ") | |||
| endBraceNewline = []byte("}\n") | |||
| backslashN = []byte{'\\', 'n'} | |||
| backslashR = []byte{'\\', 'r'} | |||
| backslashT = []byte{'\\', 't'} | |||
| backslashDQ = []byte{'\\', '"'} | |||
| backslashBS = []byte{'\\', '\\'} | |||
| posInf = []byte("inf") | |||
| negInf = []byte("-inf") | |||
| nan = []byte("nan") | |||
| ) | |||
| type writer interface { | |||
| io.Writer | |||
| WriteByte(byte) error | |||
| } | |||
| // textWriter is an io.Writer that tracks its indentation level. | |||
| type textWriter struct { | |||
| ind int | |||
| complete bool // if the current position is a complete line | |||
| compact bool // whether to write out as a one-liner | |||
| w writer | |||
| } | |||
| func (w *textWriter) WriteString(s string) (n int, err error) { | |||
| if !strings.Contains(s, "\n") { | |||
| if !w.compact && w.complete { | |||
| w.writeIndent() | |||
| } | |||
| w.complete = false | |||
| return io.WriteString(w.w, s) | |||
| } | |||
| // WriteString is typically called without newlines, so this | |||
| // codepath and its copy are rare. We copy to avoid | |||
| // duplicating all of Write's logic here. | |||
| return w.Write([]byte(s)) | |||
| } | |||
| func (w *textWriter) Write(p []byte) (n int, err error) { | |||
| newlines := bytes.Count(p, newline) | |||
| if newlines == 0 { | |||
| if !w.compact && w.complete { | |||
| w.writeIndent() | |||
| } | |||
| n, err = w.w.Write(p) | |||
| w.complete = false | |||
| return n, err | |||
| } | |||
| frags := bytes.SplitN(p, newline, newlines+1) | |||
| if w.compact { | |||
| for i, frag := range frags { | |||
| if i > 0 { | |||
| if err := w.w.WriteByte(' '); err != nil { | |||
| return n, err | |||
| } | |||
| n++ | |||
| } | |||
| nn, err := w.w.Write(frag) | |||
| n += nn | |||
| if err != nil { | |||
| return n, err | |||
| } | |||
| } | |||
| return n, nil | |||
| } | |||
| for i, frag := range frags { | |||
| if w.complete { | |||
| w.writeIndent() | |||
| } | |||
| nn, err := w.w.Write(frag) | |||
| n += nn | |||
| if err != nil { | |||
| return n, err | |||
| } | |||
| if i+1 < len(frags) { | |||
| if err := w.w.WriteByte('\n'); err != nil { | |||
| return n, err | |||
| } | |||
| n++ | |||
| } | |||
| } | |||
| w.complete = len(frags[len(frags)-1]) == 0 | |||
| return n, nil | |||
| } | |||
| func (w *textWriter) WriteByte(c byte) error { | |||
| if w.compact && c == '\n' { | |||
| c = ' ' | |||
| } | |||
| if !w.compact && w.complete { | |||
| w.writeIndent() | |||
| } | |||
| err := w.w.WriteByte(c) | |||
| w.complete = c == '\n' | |||
| return err | |||
| } | |||
| func (w *textWriter) indent() { w.ind++ } | |||
| func (w *textWriter) unindent() { | |||
| if w.ind == 0 { | |||
| log.Print("proto: textWriter unindented too far") | |||
| return | |||
| } | |||
| w.ind-- | |||
| } | |||
| func writeName(w *textWriter, props *Properties) error { | |||
| if _, err := w.WriteString(props.OrigName); err != nil { | |||
| return err | |||
| } | |||
| if props.Wire != "group" { | |||
| return w.WriteByte(':') | |||
| } | |||
| return nil | |||
| } | |||
| func requiresQuotes(u string) bool { | |||
| // When type URL contains any characters except [0-9A-Za-z./\-]*, it must be quoted. | |||
| for _, ch := range u { | |||
| switch { | |||
| case ch == '.' || ch == '/' || ch == '_': | |||
| continue | |||
| case '0' <= ch && ch <= '9': | |||
| continue | |||
| case 'A' <= ch && ch <= 'Z': | |||
| continue | |||
| case 'a' <= ch && ch <= 'z': | |||
| continue | |||
| default: | |||
| return true | |||
| } | |||
| } | |||
| return false | |||
| } | |||
| // isAny reports whether sv is a google.protobuf.Any message | |||
| func isAny(sv reflect.Value) bool { | |||
| type wkt interface { | |||
| XXX_WellKnownType() string | |||
| } | |||
| t, ok := sv.Addr().Interface().(wkt) | |||
| return ok && t.XXX_WellKnownType() == "Any" | |||
| } | |||
| // writeProto3Any writes an expanded google.protobuf.Any message. | |||
| // | |||
| // It returns (false, nil) if sv value can't be unmarshaled (e.g. because | |||
| // required messages are not linked in). | |||
| // | |||
| // It returns (true, error) when sv was written in expanded format or an error | |||
| // was encountered. | |||
| func (tm *TextMarshaler) writeProto3Any(w *textWriter, sv reflect.Value) (bool, error) { | |||
| turl := sv.FieldByName("TypeUrl") | |||
| val := sv.FieldByName("Value") | |||
| if !turl.IsValid() || !val.IsValid() { | |||
| return true, errors.New("proto: invalid google.protobuf.Any message") | |||
| } | |||
| b, ok := val.Interface().([]byte) | |||
| if !ok { | |||
| return true, errors.New("proto: invalid google.protobuf.Any message") | |||
| } | |||
| parts := strings.Split(turl.String(), "/") | |||
| mt := MessageType(parts[len(parts)-1]) | |||
| if mt == nil { | |||
| return false, nil | |||
| } | |||
| m := reflect.New(mt.Elem()) | |||
| if err := Unmarshal(b, m.Interface().(Message)); err != nil { | |||
| return false, nil | |||
| } | |||
| w.Write([]byte("[")) | |||
| u := turl.String() | |||
| if requiresQuotes(u) { | |||
| writeString(w, u) | |||
| } else { | |||
| w.Write([]byte(u)) | |||
| } | |||
| if w.compact { | |||
| w.Write([]byte("]:<")) | |||
| } else { | |||
| w.Write([]byte("]: <\n")) | |||
| w.ind++ | |||
| } | |||
| if err := tm.writeStruct(w, m.Elem()); err != nil { | |||
| return true, err | |||
| } | |||
| if w.compact { | |||
| w.Write([]byte("> ")) | |||
| } else { | |||
| w.ind-- | |||
| w.Write([]byte(">\n")) | |||
| } | |||
| return true, nil | |||
| } | |||
| func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error { | |||
| if tm.ExpandAny && isAny(sv) { | |||
| if canExpand, err := tm.writeProto3Any(w, sv); canExpand { | |||
| return err | |||
| } | |||
| } | |||
| st := sv.Type() | |||
| sprops := GetProperties(st) | |||
| for i := 0; i < sv.NumField(); i++ { | |||
| fv := sv.Field(i) | |||
| props := sprops.Prop[i] | |||
| name := st.Field(i).Name | |||
| if name == "XXX_NoUnkeyedLiteral" { | |||
| continue | |||
| } | |||
| if strings.HasPrefix(name, "XXX_") { | |||
| // There are two XXX_ fields: | |||
| // XXX_unrecognized []byte | |||
| // XXX_extensions map[int32]proto.Extension | |||
| // The first is handled here; | |||
| // the second is handled at the bottom of this function. | |||
| if name == "XXX_unrecognized" && !fv.IsNil() { | |||
| if err := writeUnknownStruct(w, fv.Interface().([]byte)); err != nil { | |||
| return err | |||
| } | |||
| } | |||
| continue | |||
| } | |||
| if fv.Kind() == reflect.Ptr && fv.IsNil() { | |||
| // Field not filled in. This could be an optional field or | |||
| // a required field that wasn't filled in. Either way, there | |||
| // isn't anything we can show for it. | |||
| continue | |||
| } | |||
| if fv.Kind() == reflect.Slice && fv.IsNil() { | |||
| // Repeated field that is empty, or a bytes field that is unused. | |||
| continue | |||
| } | |||
| if props.Repeated && fv.Kind() == reflect.Slice { | |||
| // Repeated field. | |||
| for j := 0; j < fv.Len(); j++ { | |||
| if err := writeName(w, props); err != nil { | |||
| return err | |||
| } | |||
| if !w.compact { | |||
| if err := w.WriteByte(' '); err != nil { | |||
| return err | |||
| } | |||
| } | |||
| v := fv.Index(j) | |||
| if v.Kind() == reflect.Ptr && v.IsNil() { | |||
| // A nil message in a repeated field is not valid, | |||
| // but we can handle that more gracefully than panicking. | |||
| if _, err := w.Write([]byte("<nil>\n")); err != nil { | |||
| return err | |||
| } | |||
| continue | |||
| } | |||
| if err := tm.writeAny(w, v, props); err != nil { | |||
| return err | |||
| } | |||
| if err := w.WriteByte('\n'); err != nil { | |||
| return err | |||
| } | |||
| } | |||
| continue | |||
| } | |||
| if fv.Kind() == reflect.Map { | |||
| // Map fields are rendered as a repeated struct with key/value fields. | |||
| keys := fv.MapKeys() | |||
| sort.Sort(mapKeys(keys)) | |||
| for _, key := range keys { | |||
| val := fv.MapIndex(key) | |||
| if err := writeName(w, props); err != nil { | |||
| return err | |||
| } | |||
| if !w.compact { | |||
| if err := w.WriteByte(' '); err != nil { | |||
| return err | |||
| } | |||
| } | |||
| // open struct | |||
| if err := w.WriteByte('<'); err != nil { | |||
| return err | |||
| } | |||
| if !w.compact { | |||
| if err := w.WriteByte('\n'); err != nil { | |||
| return err | |||
| } | |||
| } | |||
| w.indent() | |||
| // key | |||
| if _, err := w.WriteString("key:"); err != nil { | |||
| return err | |||
| } | |||
| if !w.compact { | |||
| if err := w.WriteByte(' '); err != nil { | |||
| return err | |||
| } | |||
| } | |||
| if err := tm.writeAny(w, key, props.MapKeyProp); err != nil { | |||
| return err | |||
| } | |||
| if err := w.WriteByte('\n'); err != nil { | |||
| return err | |||
| } | |||
| // nil values aren't legal, but we can avoid panicking because of them. | |||
| if val.Kind() != reflect.Ptr || !val.IsNil() { | |||
| // value | |||
| if _, err := w.WriteString("value:"); err != nil { | |||
| return err | |||
| } | |||
| if !w.compact { | |||
| if err := w.WriteByte(' '); err != nil { | |||
| return err | |||
| } | |||
| } | |||
| if err := tm.writeAny(w, val, props.MapValProp); err != nil { | |||
| return err | |||
| } | |||
| if err := w.WriteByte('\n'); err != nil { | |||
| return err | |||
| } | |||
| } | |||
| // close struct | |||
| w.unindent() | |||
| if err := w.WriteByte('>'); err != nil { | |||
| return err | |||
| } | |||
| if err := w.WriteByte('\n'); err != nil { | |||
| return err | |||
| } | |||
| } | |||
| continue | |||
| } | |||
| if props.proto3 && fv.Kind() == reflect.Slice && fv.Len() == 0 { | |||
| // empty bytes field | |||
| continue | |||
| } | |||
| if fv.Kind() != reflect.Ptr && fv.Kind() != reflect.Slice { | |||
| // proto3 non-repeated scalar field; skip if zero value | |||
| if isProto3Zero(fv) { | |||
| continue | |||
| } | |||
| } | |||
| if fv.Kind() == reflect.Interface { | |||
| // Check if it is a oneof. | |||
| if st.Field(i).Tag.Get("protobuf_oneof") != "" { | |||
| // fv is nil, or holds a pointer to generated struct. | |||
| // That generated struct has exactly one field, | |||
| // which has a protobuf struct tag. | |||
| if fv.IsNil() { | |||
| continue | |||
| } | |||
| inner := fv.Elem().Elem() // interface -> *T -> T | |||
| tag := inner.Type().Field(0).Tag.Get("protobuf") | |||
| props = new(Properties) // Overwrite the outer props var, but not its pointee. | |||
| props.Parse(tag) | |||
| // Write the value in the oneof, not the oneof itself. | |||
| fv = inner.Field(0) | |||
| // Special case to cope with malformed messages gracefully: | |||
| // If the value in the oneof is a nil pointer, don't panic | |||
| // in writeAny. | |||
| if fv.Kind() == reflect.Ptr && fv.IsNil() { | |||
| // Use errors.New so writeAny won't render quotes. | |||
| msg := errors.New("/* nil */") | |||
| fv = reflect.ValueOf(&msg).Elem() | |||
| } | |||
| } | |||
| } | |||
| if err := writeName(w, props); err != nil { | |||
| return err | |||
| } | |||
| if !w.compact { | |||
| if err := w.WriteByte(' '); err != nil { | |||
| return err | |||
| } | |||
| } | |||
| // Enums have a String method, so writeAny will work fine. | |||
| if err := tm.writeAny(w, fv, props); err != nil { | |||
| return err | |||
| } | |||
| if err := w.WriteByte('\n'); err != nil { | |||
| return err | |||
| } | |||
| } | |||
| // Extensions (the XXX_extensions field). | |||
| pv := sv.Addr() | |||
| if _, err := extendable(pv.Interface()); err == nil { | |||
| if err := tm.writeExtensions(w, pv); err != nil { | |||
| return err | |||
| } | |||
| } | |||
| return nil | |||
| } | |||
| var textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem() | |||
| // writeAny writes an arbitrary field. | |||
| func (tm *TextMarshaler) writeAny(w *textWriter, v reflect.Value, props *Properties) error { | |||
| v = reflect.Indirect(v) | |||
| // Floats have special cases. | |||
| if v.Kind() == reflect.Float32 || v.Kind() == reflect.Float64 { | |||
| x := v.Float() | |||
| var b []byte | |||
| switch { | |||
| case math.IsInf(x, 1): | |||
| b = posInf | |||
| case math.IsInf(x, -1): | |||
| b = negInf | |||
| case math.IsNaN(x): | |||
| b = nan | |||
| } | |||
| if b != nil { | |||
| _, err := w.Write(b) | |||
| return err | |||
| } | |||
| // Other values are handled below. | |||
| } | |||
| // We don't attempt to serialise every possible value type; only those | |||
| // that can occur in protocol buffers. | |||
| switch v.Kind() { | |||
| case reflect.Slice: | |||
| // Should only be a []byte; repeated fields are handled in writeStruct. | |||
| if err := writeString(w, string(v.Bytes())); err != nil { | |||
| return err | |||
| } | |||
| case reflect.String: | |||
| if err := writeString(w, v.String()); err != nil { | |||
| return err | |||
| } | |||
| case reflect.Struct: | |||
| // Required/optional group/message. | |||
| var bra, ket byte = '<', '>' | |||
| if props != nil && props.Wire == "group" { | |||
| bra, ket = '{', '}' | |||
| } | |||
| if err := w.WriteByte(bra); err != nil { | |||
| return err | |||
| } | |||
| if !w.compact { | |||
| if err := w.WriteByte('\n'); err != nil { | |||
| return err | |||
| } | |||
| } | |||
| w.indent() | |||
| if v.CanAddr() { | |||
| // Calling v.Interface on a struct causes the reflect package to | |||
| // copy the entire struct. This is racy with the new Marshaler | |||
| // since we atomically update the XXX_sizecache. | |||
| // | |||
| // Thus, we retrieve a pointer to the struct if possible to avoid | |||
| // a race since v.Interface on the pointer doesn't copy the struct. | |||
| // | |||
| // If v is not addressable, then we are not worried about a race | |||
| // since it implies that the binary Marshaler cannot possibly be | |||
| // mutating this value. | |||
| v = v.Addr() | |||
| } | |||
| if v.Type().Implements(textMarshalerType) { | |||
| text, err := v.Interface().(encoding.TextMarshaler).MarshalText() | |||
| if err != nil { | |||
| return err | |||
| } | |||
| if _, err = w.Write(text); err != nil { | |||
| return err | |||
| } | |||
| } else { | |||
| if v.Kind() == reflect.Ptr { | |||
| v = v.Elem() | |||
| } | |||
| if err := tm.writeStruct(w, v); err != nil { | |||
| return err | |||
| } | |||
| } | |||
| w.unindent() | |||
| if err := w.WriteByte(ket); err != nil { | |||
| return err | |||
| } | |||
| default: | |||
| _, err := fmt.Fprint(w, v.Interface()) | |||
| return err | |||
| } | |||
| return nil | |||
| } | |||
| // equivalent to C's isprint. | |||
| func isprint(c byte) bool { | |||
| return c >= 0x20 && c < 0x7f | |||
| } | |||
| // writeString writes a string in the protocol buffer text format. | |||
| // It is similar to strconv.Quote except we don't use Go escape sequences, | |||
| // we treat the string as a byte sequence, and we use octal escapes. | |||
| // These differences are to maintain interoperability with the other | |||
| // languages' implementations of the text format. | |||
| func writeString(w *textWriter, s string) error { | |||
| // use WriteByte here to get any needed indent | |||
| if err := w.WriteByte('"'); err != nil { | |||
| return err | |||
| } | |||
| // Loop over the bytes, not the runes. | |||
| for i := 0; i < len(s); i++ { | |||
| var err error | |||
| // Divergence from C++: we don't escape apostrophes. | |||
| // There's no need to escape them, and the C++ parser | |||
| // copes with a naked apostrophe. | |||
| switch c := s[i]; c { | |||
| case '\n': | |||
| _, err = w.w.Write(backslashN) | |||
| case '\r': | |||
| _, err = w.w.Write(backslashR) | |||
| case '\t': | |||
| _, err = w.w.Write(backslashT) | |||
| case '"': | |||
| _, err = w.w.Write(backslashDQ) | |||
| case '\\': | |||
| _, err = w.w.Write(backslashBS) | |||
| default: | |||
| if isprint(c) { | |||
| err = w.w.WriteByte(c) | |||
| } else { | |||
| _, err = fmt.Fprintf(w.w, "\\%03o", c) | |||
| } | |||
| } | |||
| if err != nil { | |||
| return err | |||
| } | |||
| } | |||
| return w.WriteByte('"') | |||
| } | |||
| func writeUnknownStruct(w *textWriter, data []byte) (err error) { | |||
| if !w.compact { | |||
| if _, err := fmt.Fprintf(w, "/* %d unknown bytes */\n", len(data)); err != nil { | |||
| return err | |||
| } | |||
| } | |||
| b := NewBuffer(data) | |||
| for b.index < len(b.buf) { | |||
| x, err := b.DecodeVarint() | |||
| if err != nil { | |||
| _, err := fmt.Fprintf(w, "/* %v */\n", err) | |||
| return err | |||
| } | |||
| wire, tag := x&7, x>>3 | |||
| if wire == WireEndGroup { | |||
| w.unindent() | |||
| if _, err := w.Write(endBraceNewline); err != nil { | |||
| return err | |||
| } | |||
| continue | |||
| } | |||
| if _, err := fmt.Fprint(w, tag); err != nil { | |||
| return err | |||
| } | |||
| if wire != WireStartGroup { | |||
| if err := w.WriteByte(':'); err != nil { | |||
| return err | |||
| } | |||
| } | |||
| if !w.compact || wire == WireStartGroup { | |||
| if err := w.WriteByte(' '); err != nil { | |||
| return err | |||
| } | |||
| } | |||
| switch wire { | |||
| case WireBytes: | |||
| buf, e := b.DecodeRawBytes(false) | |||
| if e == nil { | |||
| _, err = fmt.Fprintf(w, "%q", buf) | |||
| } else { | |||
| _, err = fmt.Fprintf(w, "/* %v */", e) | |||
| } | |||
| case WireFixed32: | |||
| x, err = b.DecodeFixed32() | |||
| err = writeUnknownInt(w, x, err) | |||
| case WireFixed64: | |||
| x, err = b.DecodeFixed64() | |||
| err = writeUnknownInt(w, x, err) | |||
| case WireStartGroup: | |||
| err = w.WriteByte('{') | |||
| w.indent() | |||
| case WireVarint: | |||
| x, err = b.DecodeVarint() | |||
| err = writeUnknownInt(w, x, err) | |||
| default: | |||
| _, err = fmt.Fprintf(w, "/* unknown wire type %d */", wire) | |||
| } | |||
| if err != nil { | |||
| return err | |||
| } | |||
| if err = w.WriteByte('\n'); err != nil { | |||
| return err | |||
| } | |||
| } | |||
| return nil | |||
| } | |||
| func writeUnknownInt(w *textWriter, x uint64, err error) error { | |||
| if err == nil { | |||
| _, err = fmt.Fprint(w, x) | |||
| } else { | |||
| _, err = fmt.Fprintf(w, "/* %v */", err) | |||
| } | |||
| return err | |||
| } | |||
| type int32Slice []int32 | |||
| func (s int32Slice) Len() int { return len(s) } | |||
| func (s int32Slice) Less(i, j int) bool { return s[i] < s[j] } | |||
| func (s int32Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } | |||
| // writeExtensions writes all the extensions in pv. | |||
| // pv is assumed to be a pointer to a protocol message struct that is extendable. | |||
| func (tm *TextMarshaler) writeExtensions(w *textWriter, pv reflect.Value) error { | |||
| emap := extensionMaps[pv.Type().Elem()] | |||
| ep, _ := extendable(pv.Interface()) | |||
| // Order the extensions by ID. | |||
| // This isn't strictly necessary, but it will give us | |||
| // canonical output, which will also make testing easier. | |||
| m, mu := ep.extensionsRead() | |||
| if m == nil { | |||
| return nil | |||
| } | |||
| mu.Lock() | |||
| ids := make([]int32, 0, len(m)) | |||
| for id := range m { | |||
| ids = append(ids, id) | |||
| } | |||
| sort.Sort(int32Slice(ids)) | |||
| mu.Unlock() | |||
| for _, extNum := range ids { | |||
| ext := m[extNum] | |||
| var desc *ExtensionDesc | |||
| if emap != nil { | |||
| desc = emap[extNum] | |||
| } | |||
| if desc == nil { | |||
| // Unknown extension. | |||
| if err := writeUnknownStruct(w, ext.enc); err != nil { | |||
| return err | |||
| } | |||
| continue | |||
| } | |||
| pb, err := GetExtension(ep, desc) | |||
| if err != nil { | |||
| return fmt.Errorf("failed getting extension: %v", err) | |||
| } | |||
| // Repeated extensions will appear as a slice. | |||
| if !desc.repeated() { | |||
| if err := tm.writeExtension(w, desc.Name, pb); err != nil { | |||
| return err | |||
| } | |||
| } else { | |||
| v := reflect.ValueOf(pb) | |||
| for i := 0; i < v.Len(); i++ { | |||
| if err := tm.writeExtension(w, desc.Name, v.Index(i).Interface()); err != nil { | |||
| return err | |||
| } | |||
| } | |||
| } | |||
| } | |||
| return nil | |||
| } | |||
| func (tm *TextMarshaler) writeExtension(w *textWriter, name string, pb interface{}) error { | |||
| if _, err := fmt.Fprintf(w, "[%s]:", name); err != nil { | |||
| return err | |||
| } | |||
| if !w.compact { | |||
| if err := w.WriteByte(' '); err != nil { | |||
| return err | |||
| } | |||
| } | |||
| if err := tm.writeAny(w, reflect.ValueOf(pb), nil); err != nil { | |||
| return err | |||
| } | |||
| if err := w.WriteByte('\n'); err != nil { | |||
| return err | |||
| } | |||
| return nil | |||
| } | |||
| func (w *textWriter) writeIndent() { | |||
| if !w.complete { | |||
| return | |||
| } | |||
| remain := w.ind * 2 | |||
| for remain > 0 { | |||
| n := remain | |||
| if n > len(spaces) { | |||
| n = len(spaces) | |||
| } | |||
| w.w.Write(spaces[:n]) | |||
| remain -= n | |||
| } | |||
| w.complete = false | |||
| } | |||
| // TextMarshaler is a configurable text format marshaler. | |||
| type TextMarshaler struct { | |||
| Compact bool // use compact text format (one line). | |||
| ExpandAny bool // expand google.protobuf.Any messages of known types | |||
| } | |||
| // Marshal writes a given protocol buffer in text format. | |||
| // The only errors returned are from w. | |||
| func (tm *TextMarshaler) Marshal(w io.Writer, pb Message) error { | |||
| val := reflect.ValueOf(pb) | |||
| if pb == nil || val.IsNil() { | |||
| w.Write([]byte("<nil>")) | |||
| return nil | |||
| } | |||
| var bw *bufio.Writer | |||
| ww, ok := w.(writer) | |||
| if !ok { | |||
| bw = bufio.NewWriter(w) | |||
| ww = bw | |||
| } | |||
| aw := &textWriter{ | |||
| w: ww, | |||
| complete: true, | |||
| compact: tm.Compact, | |||
| } | |||
| if etm, ok := pb.(encoding.TextMarshaler); ok { | |||
| text, err := etm.MarshalText() | |||
| if err != nil { | |||
| return err | |||
| } | |||
| if _, err = aw.Write(text); err != nil { | |||
| return err | |||
| } | |||
| if bw != nil { | |||
| return bw.Flush() | |||
| } | |||
| return nil | |||
| } | |||
| // Dereference the received pointer so we don't have outer < and >. | |||
| v := reflect.Indirect(val) | |||
| if err := tm.writeStruct(aw, v); err != nil { | |||
| return err | |||
| } | |||
| if bw != nil { | |||
| return bw.Flush() | |||
| } | |||
| return nil | |||
| } | |||
| // Text is the same as Marshal, but returns the string directly. | |||
| func (tm *TextMarshaler) Text(pb Message) string { | |||
| var buf bytes.Buffer | |||
| tm.Marshal(&buf, pb) | |||
| return buf.String() | |||
| } | |||
| var ( | |||
| defaultTextMarshaler = TextMarshaler{} | |||
| compactTextMarshaler = TextMarshaler{Compact: true} | |||
| ) | |||
| // TODO: consider removing some of the Marshal functions below. | |||
| // MarshalText writes a given protocol buffer in text format. | |||
| // The only errors returned are from w. | |||
| func MarshalText(w io.Writer, pb Message) error { return defaultTextMarshaler.Marshal(w, pb) } | |||
| // MarshalTextString is the same as MarshalText, but returns the string directly. | |||
| func MarshalTextString(pb Message) string { return defaultTextMarshaler.Text(pb) } | |||
| // CompactText writes a given protocol buffer in compact text format (one line). | |||
| func CompactText(w io.Writer, pb Message) error { return compactTextMarshaler.Marshal(w, pb) } | |||
| // CompactTextString is the same as CompactText, but returns the string directly. | |||
| func CompactTextString(pb Message) string { return compactTextMarshaler.Text(pb) } | |||
| @@ -0,0 +1,801 @@ | |||
| // Copyright 2010 The Go Authors. All rights reserved. | |||
| // Use of this source code is governed by a BSD-style | |||
| // license that can be found in the LICENSE file. | |||
| package proto | |||
| import ( | |||
| "encoding" | |||
| "errors" | |||
| "fmt" | |||
| "reflect" | |||
| "strconv" | |||
| "strings" | |||
| "unicode/utf8" | |||
| "google.golang.org/protobuf/encoding/prototext" | |||
| protoV2 "google.golang.org/protobuf/proto" | |||
| "google.golang.org/protobuf/reflect/protoreflect" | |||
| "google.golang.org/protobuf/reflect/protoregistry" | |||
| ) | |||
| const wrapTextUnmarshalV2 = false | |||
| // ParseError is returned by UnmarshalText. | |||
| type ParseError struct { | |||
| Message string | |||
| // Deprecated: Do not use. | |||
| Line, Offset int | |||
| } | |||
| func (e *ParseError) Error() string { | |||
| if wrapTextUnmarshalV2 { | |||
| return e.Message | |||
| } | |||
| if e.Line == 1 { | |||
| return fmt.Sprintf("line 1.%d: %v", e.Offset, e.Message) | |||
| } | |||
| return fmt.Sprintf("line %d: %v", e.Line, e.Message) | |||
| } | |||
| // UnmarshalText parses a proto text formatted string into m. | |||
| func UnmarshalText(s string, m Message) error { | |||
| if u, ok := m.(encoding.TextUnmarshaler); ok { | |||
| return u.UnmarshalText([]byte(s)) | |||
| } | |||
| m.Reset() | |||
| mi := MessageV2(m) | |||
| if wrapTextUnmarshalV2 { | |||
| err := prototext.UnmarshalOptions{ | |||
| AllowPartial: true, | |||
| }.Unmarshal([]byte(s), mi) | |||
| if err != nil { | |||
| return &ParseError{Message: err.Error()} | |||
| } | |||
| return checkRequiredNotSet(mi) | |||
| } else { | |||
| if err := newTextParser(s).unmarshalMessage(mi.ProtoReflect(), ""); err != nil { | |||
| return err | |||
| } | |||
| return checkRequiredNotSet(mi) | |||
| } | |||
| } | |||
| type textParser struct { | |||
| s string // remaining input | |||
| done bool // whether the parsing is finished (success or error) | |||
| backed bool // whether back() was called | |||
| offset, line int | |||
| cur token | |||
| } | |||
| type token struct { | |||
| value string | |||
| err *ParseError | |||
| line int // line number | |||
| offset int // byte number from start of input, not start of line | |||
| unquoted string // the unquoted version of value, if it was a quoted string | |||
| } | |||
| func newTextParser(s string) *textParser { | |||
| p := new(textParser) | |||
| p.s = s | |||
| p.line = 1 | |||
| p.cur.line = 1 | |||
| return p | |||
| } | |||
| func (p *textParser) unmarshalMessage(m protoreflect.Message, terminator string) (err error) { | |||
| md := m.Descriptor() | |||
| fds := md.Fields() | |||
| // A struct is a sequence of "name: value", terminated by one of | |||
| // '>' or '}', or the end of the input. A name may also be | |||
| // "[extension]" or "[type/url]". | |||
| // | |||
| // The whole struct can also be an expanded Any message, like: | |||
| // [type/url] < ... struct contents ... > | |||
| seen := make(map[protoreflect.FieldNumber]bool) | |||
| for { | |||
| tok := p.next() | |||
| if tok.err != nil { | |||
| return tok.err | |||
| } | |||
| if tok.value == terminator { | |||
| break | |||
| } | |||
| if tok.value == "[" { | |||
| if err := p.unmarshalExtensionOrAny(m, seen); err != nil { | |||
| return err | |||
| } | |||
| continue | |||
| } | |||
| // This is a normal, non-extension field. | |||
| name := protoreflect.Name(tok.value) | |||
| fd := fds.ByName(name) | |||
| switch { | |||
| case fd == nil: | |||
| gd := fds.ByName(protoreflect.Name(strings.ToLower(string(name)))) | |||
| if gd != nil && gd.Kind() == protoreflect.GroupKind && gd.Message().Name() == name { | |||
| fd = gd | |||
| } | |||
| case fd.Kind() == protoreflect.GroupKind && fd.Message().Name() != name: | |||
| fd = nil | |||
| case fd.IsWeak() && fd.Message().IsPlaceholder(): | |||
| fd = nil | |||
| } | |||
| if fd == nil { | |||
| typeName := string(md.FullName()) | |||
| if m, ok := m.Interface().(Message); ok { | |||
| t := reflect.TypeOf(m) | |||
| if t.Kind() == reflect.Ptr { | |||
| typeName = t.Elem().String() | |||
| } | |||
| } | |||
| return p.errorf("unknown field name %q in %v", name, typeName) | |||
| } | |||
| if od := fd.ContainingOneof(); od != nil && m.WhichOneof(od) != nil { | |||
| return p.errorf("field '%s' would overwrite already parsed oneof '%s'", name, od.Name()) | |||
| } | |||
| if fd.Cardinality() != protoreflect.Repeated && seen[fd.Number()] { | |||
| return p.errorf("non-repeated field %q was repeated", fd.Name()) | |||
| } | |||
| seen[fd.Number()] = true | |||
| // Consume any colon. | |||
| if err := p.checkForColon(fd); err != nil { | |||
| return err | |||
| } | |||
| // Parse into the field. | |||
| v := m.Get(fd) | |||
| if !m.Has(fd) && (fd.IsList() || fd.IsMap() || fd.Message() != nil) { | |||
| v = m.Mutable(fd) | |||
| } | |||
| if v, err = p.unmarshalValue(v, fd); err != nil { | |||
| return err | |||
| } | |||
| m.Set(fd, v) | |||
| if err := p.consumeOptionalSeparator(); err != nil { | |||
| return err | |||
| } | |||
| } | |||
| return nil | |||
| } | |||
| func (p *textParser) unmarshalExtensionOrAny(m protoreflect.Message, seen map[protoreflect.FieldNumber]bool) error { | |||
| name, err := p.consumeExtensionOrAnyName() | |||
| if err != nil { | |||
| return err | |||
| } | |||
| // If it contains a slash, it's an Any type URL. | |||
| if slashIdx := strings.LastIndex(name, "/"); slashIdx >= 0 { | |||
| tok := p.next() | |||
| if tok.err != nil { | |||
| return tok.err | |||
| } | |||
| // consume an optional colon | |||
| if tok.value == ":" { | |||
| tok = p.next() | |||
| if tok.err != nil { | |||
| return tok.err | |||
| } | |||
| } | |||
| var terminator string | |||
| switch tok.value { | |||
| case "<": | |||
| terminator = ">" | |||
| case "{": | |||
| terminator = "}" | |||
| default: | |||
| return p.errorf("expected '{' or '<', found %q", tok.value) | |||
| } | |||
| mt, err := protoregistry.GlobalTypes.FindMessageByURL(name) | |||
| if err != nil { | |||
| return p.errorf("unrecognized message %q in google.protobuf.Any", name[slashIdx+len("/"):]) | |||
| } | |||
| m2 := mt.New() | |||
| if err := p.unmarshalMessage(m2, terminator); err != nil { | |||
| return err | |||
| } | |||
| b, err := protoV2.Marshal(m2.Interface()) | |||
| if err != nil { | |||
| return p.errorf("failed to marshal message of type %q: %v", name[slashIdx+len("/"):], err) | |||
| } | |||
| urlFD := m.Descriptor().Fields().ByName("type_url") | |||
| valFD := m.Descriptor().Fields().ByName("value") | |||
| if seen[urlFD.Number()] { | |||
| return p.errorf("Any message unpacked multiple times, or %q already set", urlFD.Name()) | |||
| } | |||
| if seen[valFD.Number()] { | |||
| return p.errorf("Any message unpacked multiple times, or %q already set", valFD.Name()) | |||
| } | |||
| m.Set(urlFD, protoreflect.ValueOfString(name)) | |||
| m.Set(valFD, protoreflect.ValueOfBytes(b)) | |||
| seen[urlFD.Number()] = true | |||
| seen[valFD.Number()] = true | |||
| return nil | |||
| } | |||
| xname := protoreflect.FullName(name) | |||
| xt, _ := protoregistry.GlobalTypes.FindExtensionByName(xname) | |||
| if xt == nil && isMessageSet(m.Descriptor()) { | |||
| xt, _ = protoregistry.GlobalTypes.FindExtensionByName(xname.Append("message_set_extension")) | |||
| } | |||
| if xt == nil { | |||
| return p.errorf("unrecognized extension %q", name) | |||
| } | |||
| fd := xt.TypeDescriptor() | |||
| if fd.ContainingMessage().FullName() != m.Descriptor().FullName() { | |||
| return p.errorf("extension field %q does not extend message %q", name, m.Descriptor().FullName()) | |||
| } | |||
| if err := p.checkForColon(fd); err != nil { | |||
| return err | |||
| } | |||
| v := m.Get(fd) | |||
| if !m.Has(fd) && (fd.IsList() || fd.IsMap() || fd.Message() != nil) { | |||
| v = m.Mutable(fd) | |||
| } | |||
| v, err = p.unmarshalValue(v, fd) | |||
| if err != nil { | |||
| return err | |||
| } | |||
| m.Set(fd, v) | |||
| return p.consumeOptionalSeparator() | |||
| } | |||
| func (p *textParser) unmarshalValue(v protoreflect.Value, fd protoreflect.FieldDescriptor) (protoreflect.Value, error) { | |||
| tok := p.next() | |||
| if tok.err != nil { | |||
| return v, tok.err | |||
| } | |||
| if tok.value == "" { | |||
| return v, p.errorf("unexpected EOF") | |||
| } | |||
| switch { | |||
| case fd.IsList(): | |||
| lv := v.List() | |||
| var err error | |||
| if tok.value == "[" { | |||
| // Repeated field with list notation, like [1,2,3]. | |||
| for { | |||
| vv := lv.NewElement() | |||
| vv, err = p.unmarshalSingularValue(vv, fd) | |||
| if err != nil { | |||
| return v, err | |||
| } | |||
| lv.Append(vv) | |||
| tok := p.next() | |||
| if tok.err != nil { | |||
| return v, tok.err | |||
| } | |||
| if tok.value == "]" { | |||
| break | |||
| } | |||
| if tok.value != "," { | |||
| return v, p.errorf("Expected ']' or ',' found %q", tok.value) | |||
| } | |||
| } | |||
| return v, nil | |||
| } | |||
| // One value of the repeated field. | |||
| p.back() | |||
| vv := lv.NewElement() | |||
| vv, err = p.unmarshalSingularValue(vv, fd) | |||
| if err != nil { | |||
| return v, err | |||
| } | |||
| lv.Append(vv) | |||
| return v, nil | |||
| case fd.IsMap(): | |||
| // The map entry should be this sequence of tokens: | |||
| // < key : KEY value : VALUE > | |||
| // However, implementations may omit key or value, and technically | |||
| // we should support them in any order. | |||
| var terminator string | |||
| switch tok.value { | |||
| case "<": | |||
| terminator = ">" | |||
| case "{": | |||
| terminator = "}" | |||
| default: | |||
| return v, p.errorf("expected '{' or '<', found %q", tok.value) | |||
| } | |||
| keyFD := fd.MapKey() | |||
| valFD := fd.MapValue() | |||
| mv := v.Map() | |||
| kv := keyFD.Default() | |||
| vv := mv.NewValue() | |||
| for { | |||
| tok := p.next() | |||
| if tok.err != nil { | |||
| return v, tok.err | |||
| } | |||
| if tok.value == terminator { | |||
| break | |||
| } | |||
| var err error | |||
| switch tok.value { | |||
| case "key": | |||
| if err := p.consumeToken(":"); err != nil { | |||
| return v, err | |||
| } | |||
| if kv, err = p.unmarshalSingularValue(kv, keyFD); err != nil { | |||
| return v, err | |||
| } | |||
| if err := p.consumeOptionalSeparator(); err != nil { | |||
| return v, err | |||
| } | |||
| case "value": | |||
| if err := p.checkForColon(valFD); err != nil { | |||
| return v, err | |||
| } | |||
| if vv, err = p.unmarshalSingularValue(vv, valFD); err != nil { | |||
| return v, err | |||
| } | |||
| if err := p.consumeOptionalSeparator(); err != nil { | |||
| return v, err | |||
| } | |||
| default: | |||
| p.back() | |||
| return v, p.errorf(`expected "key", "value", or %q, found %q`, terminator, tok.value) | |||
| } | |||
| } | |||
| mv.Set(kv.MapKey(), vv) | |||
| return v, nil | |||
| default: | |||
| p.back() | |||
| return p.unmarshalSingularValue(v, fd) | |||
| } | |||
| } | |||
| func (p *textParser) unmarshalSingularValue(v protoreflect.Value, fd protoreflect.FieldDescriptor) (protoreflect.Value, error) { | |||
| tok := p.next() | |||
| if tok.err != nil { | |||
| return v, tok.err | |||
| } | |||
| if tok.value == "" { | |||
| return v, p.errorf("unexpected EOF") | |||
| } | |||
| switch fd.Kind() { | |||
| case protoreflect.BoolKind: | |||
| switch tok.value { | |||
| case "true", "1", "t", "True": | |||
| return protoreflect.ValueOfBool(true), nil | |||
| case "false", "0", "f", "False": | |||
| return protoreflect.ValueOfBool(false), nil | |||
| } | |||
| case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind: | |||
| if x, err := strconv.ParseInt(tok.value, 0, 32); err == nil { | |||
| return protoreflect.ValueOfInt32(int32(x)), nil | |||
| } | |||
| // The C++ parser accepts large positive hex numbers that uses | |||
| // two's complement arithmetic to represent negative numbers. | |||
| // This feature is here for backwards compatibility with C++. | |||
| if strings.HasPrefix(tok.value, "0x") { | |||
| if x, err := strconv.ParseUint(tok.value, 0, 32); err == nil { | |||
| return protoreflect.ValueOfInt32(int32(-(int64(^x) + 1))), nil | |||
| } | |||
| } | |||
| case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind: | |||
| if x, err := strconv.ParseInt(tok.value, 0, 64); err == nil { | |||
| return protoreflect.ValueOfInt64(int64(x)), nil | |||
| } | |||
| // The C++ parser accepts large positive hex numbers that uses | |||
| // two's complement arithmetic to represent negative numbers. | |||
| // This feature is here for backwards compatibility with C++. | |||
| if strings.HasPrefix(tok.value, "0x") { | |||
| if x, err := strconv.ParseUint(tok.value, 0, 64); err == nil { | |||
| return protoreflect.ValueOfInt64(int64(-(int64(^x) + 1))), nil | |||
| } | |||
| } | |||
| case protoreflect.Uint32Kind, protoreflect.Fixed32Kind: | |||
| if x, err := strconv.ParseUint(tok.value, 0, 32); err == nil { | |||
| return protoreflect.ValueOfUint32(uint32(x)), nil | |||
| } | |||
| case protoreflect.Uint64Kind, protoreflect.Fixed64Kind: | |||
| if x, err := strconv.ParseUint(tok.value, 0, 64); err == nil { | |||
| return protoreflect.ValueOfUint64(uint64(x)), nil | |||
| } | |||
| case protoreflect.FloatKind: | |||
| // Ignore 'f' for compatibility with output generated by C++, | |||
| // but don't remove 'f' when the value is "-inf" or "inf". | |||
| v := tok.value | |||
| if strings.HasSuffix(v, "f") && v != "-inf" && v != "inf" { | |||
| v = v[:len(v)-len("f")] | |||
| } | |||
| if x, err := strconv.ParseFloat(v, 32); err == nil { | |||
| return protoreflect.ValueOfFloat32(float32(x)), nil | |||
| } | |||
| case protoreflect.DoubleKind: | |||
| // Ignore 'f' for compatibility with output generated by C++, | |||
| // but don't remove 'f' when the value is "-inf" or "inf". | |||
| v := tok.value | |||
| if strings.HasSuffix(v, "f") && v != "-inf" && v != "inf" { | |||
| v = v[:len(v)-len("f")] | |||
| } | |||
| if x, err := strconv.ParseFloat(v, 64); err == nil { | |||
| return protoreflect.ValueOfFloat64(float64(x)), nil | |||
| } | |||
| case protoreflect.StringKind: | |||
| if isQuote(tok.value[0]) { | |||
| return protoreflect.ValueOfString(tok.unquoted), nil | |||
| } | |||
| case protoreflect.BytesKind: | |||
| if isQuote(tok.value[0]) { | |||
| return protoreflect.ValueOfBytes([]byte(tok.unquoted)), nil | |||
| } | |||
| case protoreflect.EnumKind: | |||
| if x, err := strconv.ParseInt(tok.value, 0, 32); err == nil { | |||
| return protoreflect.ValueOfEnum(protoreflect.EnumNumber(x)), nil | |||
| } | |||
| vd := fd.Enum().Values().ByName(protoreflect.Name(tok.value)) | |||
| if vd != nil { | |||
| return protoreflect.ValueOfEnum(vd.Number()), nil | |||
| } | |||
| case protoreflect.MessageKind, protoreflect.GroupKind: | |||
| var terminator string | |||
| switch tok.value { | |||
| case "{": | |||
| terminator = "}" | |||
| case "<": | |||
| terminator = ">" | |||
| default: | |||
| return v, p.errorf("expected '{' or '<', found %q", tok.value) | |||
| } | |||
| err := p.unmarshalMessage(v.Message(), terminator) | |||
| return v, err | |||
| default: | |||
| panic(fmt.Sprintf("invalid kind %v", fd.Kind())) | |||
| } | |||
| return v, p.errorf("invalid %v: %v", fd.Kind(), tok.value) | |||
| } | |||
| // Consume a ':' from the input stream (if the next token is a colon), | |||
| // returning an error if a colon is needed but not present. | |||
| func (p *textParser) checkForColon(fd protoreflect.FieldDescriptor) *ParseError { | |||
| tok := p.next() | |||
| if tok.err != nil { | |||
| return tok.err | |||
| } | |||
| if tok.value != ":" { | |||
| if fd.Message() == nil { | |||
| return p.errorf("expected ':', found %q", tok.value) | |||
| } | |||
| p.back() | |||
| } | |||
| return nil | |||
| } | |||
| // consumeExtensionOrAnyName consumes an extension name or an Any type URL and | |||
| // the following ']'. It returns the name or URL consumed. | |||
| func (p *textParser) consumeExtensionOrAnyName() (string, error) { | |||
| tok := p.next() | |||
| if tok.err != nil { | |||
| return "", tok.err | |||
| } | |||
| // If extension name or type url is quoted, it's a single token. | |||
| if len(tok.value) > 2 && isQuote(tok.value[0]) && tok.value[len(tok.value)-1] == tok.value[0] { | |||
| name, err := unquoteC(tok.value[1:len(tok.value)-1], rune(tok.value[0])) | |||
| if err != nil { | |||
| return "", err | |||
| } | |||
| return name, p.consumeToken("]") | |||
| } | |||
| // Consume everything up to "]" | |||
| var parts []string | |||
| for tok.value != "]" { | |||
| parts = append(parts, tok.value) | |||
| tok = p.next() | |||
| if tok.err != nil { | |||
| return "", p.errorf("unrecognized type_url or extension name: %s", tok.err) | |||
| } | |||
| if p.done && tok.value != "]" { | |||
| return "", p.errorf("unclosed type_url or extension name") | |||
| } | |||
| } | |||
| return strings.Join(parts, ""), nil | |||
| } | |||
| // consumeOptionalSeparator consumes an optional semicolon or comma. | |||
| // It is used in unmarshalMessage to provide backward compatibility. | |||
| func (p *textParser) consumeOptionalSeparator() error { | |||
| tok := p.next() | |||
| if tok.err != nil { | |||
| return tok.err | |||
| } | |||
| if tok.value != ";" && tok.value != "," { | |||
| p.back() | |||
| } | |||
| return nil | |||
| } | |||
| func (p *textParser) errorf(format string, a ...interface{}) *ParseError { | |||
| pe := &ParseError{fmt.Sprintf(format, a...), p.cur.line, p.cur.offset} | |||
| p.cur.err = pe | |||
| p.done = true | |||
| return pe | |||
| } | |||
| func (p *textParser) skipWhitespace() { | |||
| i := 0 | |||
| for i < len(p.s) && (isWhitespace(p.s[i]) || p.s[i] == '#') { | |||
| if p.s[i] == '#' { | |||
| // comment; skip to end of line or input | |||
| for i < len(p.s) && p.s[i] != '\n' { | |||
| i++ | |||
| } | |||
| if i == len(p.s) { | |||
| break | |||
| } | |||
| } | |||
| if p.s[i] == '\n' { | |||
| p.line++ | |||
| } | |||
| i++ | |||
| } | |||
| p.offset += i | |||
| p.s = p.s[i:len(p.s)] | |||
| if len(p.s) == 0 { | |||
| p.done = true | |||
| } | |||
| } | |||
| func (p *textParser) advance() { | |||
| // Skip whitespace | |||
| p.skipWhitespace() | |||
| if p.done { | |||
| return | |||
| } | |||
| // Start of non-whitespace | |||
| p.cur.err = nil | |||
| p.cur.offset, p.cur.line = p.offset, p.line | |||
| p.cur.unquoted = "" | |||
| switch p.s[0] { | |||
| case '<', '>', '{', '}', ':', '[', ']', ';', ',', '/': | |||
| // Single symbol | |||
| p.cur.value, p.s = p.s[0:1], p.s[1:len(p.s)] | |||
| case '"', '\'': | |||
| // Quoted string | |||
| i := 1 | |||
| for i < len(p.s) && p.s[i] != p.s[0] && p.s[i] != '\n' { | |||
| if p.s[i] == '\\' && i+1 < len(p.s) { | |||
| // skip escaped char | |||
| i++ | |||
| } | |||
| i++ | |||
| } | |||
| if i >= len(p.s) || p.s[i] != p.s[0] { | |||
| p.errorf("unmatched quote") | |||
| return | |||
| } | |||
| unq, err := unquoteC(p.s[1:i], rune(p.s[0])) | |||
| if err != nil { | |||
| p.errorf("invalid quoted string %s: %v", p.s[0:i+1], err) | |||
| return | |||
| } | |||
| p.cur.value, p.s = p.s[0:i+1], p.s[i+1:len(p.s)] | |||
| p.cur.unquoted = unq | |||
| default: | |||
| i := 0 | |||
| for i < len(p.s) && isIdentOrNumberChar(p.s[i]) { | |||
| i++ | |||
| } | |||
| if i == 0 { | |||
| p.errorf("unexpected byte %#x", p.s[0]) | |||
| return | |||
| } | |||
| p.cur.value, p.s = p.s[0:i], p.s[i:len(p.s)] | |||
| } | |||
| p.offset += len(p.cur.value) | |||
| } | |||
| // Back off the parser by one token. Can only be done between calls to next(). | |||
| // It makes the next advance() a no-op. | |||
| func (p *textParser) back() { p.backed = true } | |||
| // Advances the parser and returns the new current token. | |||
| func (p *textParser) next() *token { | |||
| if p.backed || p.done { | |||
| p.backed = false | |||
| return &p.cur | |||
| } | |||
| p.advance() | |||
| if p.done { | |||
| p.cur.value = "" | |||
| } else if len(p.cur.value) > 0 && isQuote(p.cur.value[0]) { | |||
| // Look for multiple quoted strings separated by whitespace, | |||
| // and concatenate them. | |||
| cat := p.cur | |||
| for { | |||
| p.skipWhitespace() | |||
| if p.done || !isQuote(p.s[0]) { | |||
| break | |||
| } | |||
| p.advance() | |||
| if p.cur.err != nil { | |||
| return &p.cur | |||
| } | |||
| cat.value += " " + p.cur.value | |||
| cat.unquoted += p.cur.unquoted | |||
| } | |||
| p.done = false // parser may have seen EOF, but we want to return cat | |||
| p.cur = cat | |||
| } | |||
| return &p.cur | |||
| } | |||
| func (p *textParser) consumeToken(s string) error { | |||
| tok := p.next() | |||
| if tok.err != nil { | |||
| return tok.err | |||
| } | |||
| if tok.value != s { | |||
| p.back() | |||
| return p.errorf("expected %q, found %q", s, tok.value) | |||
| } | |||
| return nil | |||
| } | |||
| var errBadUTF8 = errors.New("proto: bad UTF-8") | |||
| func unquoteC(s string, quote rune) (string, error) { | |||
| // This is based on C++'s tokenizer.cc. | |||
| // Despite its name, this is *not* parsing C syntax. | |||
| // For instance, "\0" is an invalid quoted string. | |||
| // Avoid allocation in trivial cases. | |||
| simple := true | |||
| for _, r := range s { | |||
| if r == '\\' || r == quote { | |||
| simple = false | |||
| break | |||
| } | |||
| } | |||
| if simple { | |||
| return s, nil | |||
| } | |||
| buf := make([]byte, 0, 3*len(s)/2) | |||
| for len(s) > 0 { | |||
| r, n := utf8.DecodeRuneInString(s) | |||
| if r == utf8.RuneError && n == 1 { | |||
| return "", errBadUTF8 | |||
| } | |||
| s = s[n:] | |||
| if r != '\\' { | |||
| if r < utf8.RuneSelf { | |||
| buf = append(buf, byte(r)) | |||
| } else { | |||
| buf = append(buf, string(r)...) | |||
| } | |||
| continue | |||
| } | |||
| ch, tail, err := unescape(s) | |||
| if err != nil { | |||
| return "", err | |||
| } | |||
| buf = append(buf, ch...) | |||
| s = tail | |||
| } | |||
| return string(buf), nil | |||
| } | |||
| func unescape(s string) (ch string, tail string, err error) { | |||
| r, n := utf8.DecodeRuneInString(s) | |||
| if r == utf8.RuneError && n == 1 { | |||
| return "", "", errBadUTF8 | |||
| } | |||
| s = s[n:] | |||
| switch r { | |||
| case 'a': | |||
| return "\a", s, nil | |||
| case 'b': | |||
| return "\b", s, nil | |||
| case 'f': | |||
| return "\f", s, nil | |||
| case 'n': | |||
| return "\n", s, nil | |||
| case 'r': | |||
| return "\r", s, nil | |||
| case 't': | |||
| return "\t", s, nil | |||
| case 'v': | |||
| return "\v", s, nil | |||
| case '?': | |||
| return "?", s, nil // trigraph workaround | |||
| case '\'', '"', '\\': | |||
| return string(r), s, nil | |||
| case '0', '1', '2', '3', '4', '5', '6', '7': | |||
| if len(s) < 2 { | |||
| return "", "", fmt.Errorf(`\%c requires 2 following digits`, r) | |||
| } | |||
| ss := string(r) + s[:2] | |||
| s = s[2:] | |||
| i, err := strconv.ParseUint(ss, 8, 8) | |||
| if err != nil { | |||
| return "", "", fmt.Errorf(`\%s contains non-octal digits`, ss) | |||
| } | |||
| return string([]byte{byte(i)}), s, nil | |||
| case 'x', 'X', 'u', 'U': | |||
| var n int | |||
| switch r { | |||
| case 'x', 'X': | |||
| n = 2 | |||
| case 'u': | |||
| n = 4 | |||
| case 'U': | |||
| n = 8 | |||
| } | |||
| if len(s) < n { | |||
| return "", "", fmt.Errorf(`\%c requires %d following digits`, r, n) | |||
| } | |||
| ss := s[:n] | |||
| s = s[n:] | |||
| i, err := strconv.ParseUint(ss, 16, 64) | |||
| if err != nil { | |||
| return "", "", fmt.Errorf(`\%c%s contains non-hexadecimal digits`, r, ss) | |||
| } | |||
| if r == 'x' || r == 'X' { | |||
| return string([]byte{byte(i)}), s, nil | |||
| } | |||
| if i > utf8.MaxRune { | |||
| return "", "", fmt.Errorf(`\%c%s is not a valid Unicode code point`, r, ss) | |||
| } | |||
| return string(i), s, nil | |||
| } | |||
| return "", "", fmt.Errorf(`unknown escape \%c`, r) | |||
| } | |||
| func isIdentOrNumberChar(c byte) bool { | |||
| switch { | |||
| case 'A' <= c && c <= 'Z', 'a' <= c && c <= 'z': | |||
| return true | |||
| case '0' <= c && c <= '9': | |||
| return true | |||
| } | |||
| switch c { | |||
| case '-', '+', '.', '_': | |||
| return true | |||
| } | |||
| return false | |||
| } | |||
| func isWhitespace(c byte) bool { | |||
| switch c { | |||
| case ' ', '\t', '\n', '\r': | |||
| return true | |||
| } | |||
| return false | |||
| } | |||
| func isQuote(c byte) bool { | |||
| switch c { | |||
| case '"', '\'': | |||
| return true | |||
| } | |||
| return false | |||
| } | |||
| @@ -0,0 +1,560 @@ | |||
| // Copyright 2010 The Go Authors. All rights reserved. | |||
| // Use of this source code is governed by a BSD-style | |||
| // license that can be found in the LICENSE file. | |||
| package proto | |||
| import ( | |||
| "bytes" | |||
| "encoding" | |||
| "fmt" | |||
| "io" | |||
| "math" | |||
| "sort" | |||
| "strings" | |||
| "google.golang.org/protobuf/encoding/prototext" | |||
| "google.golang.org/protobuf/encoding/protowire" | |||
| "google.golang.org/protobuf/proto" | |||
| "google.golang.org/protobuf/reflect/protoreflect" | |||
| "google.golang.org/protobuf/reflect/protoregistry" | |||
| ) | |||
| const wrapTextMarshalV2 = false | |||
| // TextMarshaler is a configurable text format marshaler. | |||
| type TextMarshaler struct { | |||
| Compact bool // use compact text format (one line) | |||
| ExpandAny bool // expand google.protobuf.Any messages of known types | |||
| } | |||
| // Marshal writes the proto text format of m to w. | |||
| func (tm *TextMarshaler) Marshal(w io.Writer, m Message) error { | |||
| b, err := tm.marshal(m) | |||
| if len(b) > 0 { | |||
| if _, err := w.Write(b); err != nil { | |||
| return err | |||
| } | |||
| } | |||
| return err | |||
| } | |||
| // Text returns a proto text formatted string of m. | |||
| func (tm *TextMarshaler) Text(m Message) string { | |||
| b, _ := tm.marshal(m) | |||
| return string(b) | |||
| } | |||
| func (tm *TextMarshaler) marshal(m Message) ([]byte, error) { | |||
| mr := MessageReflect(m) | |||
| if mr == nil || !mr.IsValid() { | |||
| return []byte("<nil>"), nil | |||
| } | |||
| if wrapTextMarshalV2 { | |||
| if m, ok := m.(encoding.TextMarshaler); ok { | |||
| return m.MarshalText() | |||
| } | |||
| opts := prototext.MarshalOptions{ | |||
| AllowPartial: true, | |||
| EmitUnknown: true, | |||
| } | |||
| if !tm.Compact { | |||
| opts.Indent = " " | |||
| } | |||
| if !tm.ExpandAny { | |||
| opts.Resolver = (*protoregistry.Types)(nil) | |||
| } | |||
| return opts.Marshal(mr.Interface()) | |||
| } else { | |||
| w := &textWriter{ | |||
| compact: tm.Compact, | |||
| expandAny: tm.ExpandAny, | |||
| complete: true, | |||
| } | |||
| if m, ok := m.(encoding.TextMarshaler); ok { | |||
| b, err := m.MarshalText() | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| w.Write(b) | |||
| return w.buf, nil | |||
| } | |||
| err := w.writeMessage(mr) | |||
| return w.buf, err | |||
| } | |||
| } | |||
| var ( | |||
| defaultTextMarshaler = TextMarshaler{} | |||
| compactTextMarshaler = TextMarshaler{Compact: true} | |||
| ) | |||
| // MarshalText writes the proto text format of m to w. | |||
| func MarshalText(w io.Writer, pb Message) error { return defaultTextMarshaler.Marshal(w, pb) } | |||
| // MarshalTextString returns a proto text formatted string of m. | |||
| func MarshalTextString(pb Message) string { return defaultTextMarshaler.Text(pb) } | |||
| // CompactText writes the compact proto text format of m to w. | |||
| func CompactText(w io.Writer, pb Message) error { return compactTextMarshaler.Marshal(w, pb) } | |||
| // CompactTextString returns a compact proto text formatted string of m. | |||
| func CompactTextString(pb Message) string { return compactTextMarshaler.Text(pb) } | |||
| var ( | |||
| newline = []byte("\n") | |||
| endBraceNewline = []byte("}\n") | |||
| posInf = []byte("inf") | |||
| negInf = []byte("-inf") | |||
| nan = []byte("nan") | |||
| ) | |||
| // textWriter is an io.Writer that tracks its indentation level. | |||
| type textWriter struct { | |||
| compact bool // same as TextMarshaler.Compact | |||
| expandAny bool // same as TextMarshaler.ExpandAny | |||
| complete bool // whether the current position is a complete line | |||
| indent int // indentation level; never negative | |||
| buf []byte | |||
| } | |||
| func (w *textWriter) Write(p []byte) (n int, _ error) { | |||
| newlines := bytes.Count(p, newline) | |||
| if newlines == 0 { | |||
| if !w.compact && w.complete { | |||
| w.writeIndent() | |||
| } | |||
| w.buf = append(w.buf, p...) | |||
| w.complete = false | |||
| return len(p), nil | |||
| } | |||
| frags := bytes.SplitN(p, newline, newlines+1) | |||
| if w.compact { | |||
| for i, frag := range frags { | |||
| if i > 0 { | |||
| w.buf = append(w.buf, ' ') | |||
| n++ | |||
| } | |||
| w.buf = append(w.buf, frag...) | |||
| n += len(frag) | |||
| } | |||
| return n, nil | |||
| } | |||
| for i, frag := range frags { | |||
| if w.complete { | |||
| w.writeIndent() | |||
| } | |||
| w.buf = append(w.buf, frag...) | |||
| n += len(frag) | |||
| if i+1 < len(frags) { | |||
| w.buf = append(w.buf, '\n') | |||
| n++ | |||
| } | |||
| } | |||
| w.complete = len(frags[len(frags)-1]) == 0 | |||
| return n, nil | |||
| } | |||
| func (w *textWriter) WriteByte(c byte) error { | |||
| if w.compact && c == '\n' { | |||
| c = ' ' | |||
| } | |||
| if !w.compact && w.complete { | |||
| w.writeIndent() | |||
| } | |||
| w.buf = append(w.buf, c) | |||
| w.complete = c == '\n' | |||
| return nil | |||
| } | |||
| func (w *textWriter) writeName(fd protoreflect.FieldDescriptor) { | |||
| if !w.compact && w.complete { | |||
| w.writeIndent() | |||
| } | |||
| w.complete = false | |||
| if fd.Kind() != protoreflect.GroupKind { | |||
| w.buf = append(w.buf, fd.Name()...) | |||
| w.WriteByte(':') | |||
| } else { | |||
| // Use message type name for group field name. | |||
| w.buf = append(w.buf, fd.Message().Name()...) | |||
| } | |||
| if !w.compact { | |||
| w.WriteByte(' ') | |||
| } | |||
| } | |||
| func requiresQuotes(u string) bool { | |||
| // When type URL contains any characters except [0-9A-Za-z./\-]*, it must be quoted. | |||
| for _, ch := range u { | |||
| switch { | |||
| case ch == '.' || ch == '/' || ch == '_': | |||
| continue | |||
| case '0' <= ch && ch <= '9': | |||
| continue | |||
| case 'A' <= ch && ch <= 'Z': | |||
| continue | |||
| case 'a' <= ch && ch <= 'z': | |||
| continue | |||
| default: | |||
| return true | |||
| } | |||
| } | |||
| return false | |||
| } | |||
| // writeProto3Any writes an expanded google.protobuf.Any message. | |||
| // | |||
| // It returns (false, nil) if sv value can't be unmarshaled (e.g. because | |||
| // required messages are not linked in). | |||
| // | |||
| // It returns (true, error) when sv was written in expanded format or an error | |||
| // was encountered. | |||
| func (w *textWriter) writeProto3Any(m protoreflect.Message) (bool, error) { | |||
| md := m.Descriptor() | |||
| fdURL := md.Fields().ByName("type_url") | |||
| fdVal := md.Fields().ByName("value") | |||
| url := m.Get(fdURL).String() | |||
| mt, err := protoregistry.GlobalTypes.FindMessageByURL(url) | |||
| if err != nil { | |||
| return false, nil | |||
| } | |||
| b := m.Get(fdVal).Bytes() | |||
| m2 := mt.New() | |||
| if err := proto.Unmarshal(b, m2.Interface()); err != nil { | |||
| return false, nil | |||
| } | |||
| w.Write([]byte("[")) | |||
| if requiresQuotes(url) { | |||
| w.writeQuotedString(url) | |||
| } else { | |||
| w.Write([]byte(url)) | |||
| } | |||
| if w.compact { | |||
| w.Write([]byte("]:<")) | |||
| } else { | |||
| w.Write([]byte("]: <\n")) | |||
| w.indent++ | |||
| } | |||
| if err := w.writeMessage(m2); err != nil { | |||
| return true, err | |||
| } | |||
| if w.compact { | |||
| w.Write([]byte("> ")) | |||
| } else { | |||
| w.indent-- | |||
| w.Write([]byte(">\n")) | |||
| } | |||
| return true, nil | |||
| } | |||
| func (w *textWriter) writeMessage(m protoreflect.Message) error { | |||
| md := m.Descriptor() | |||
| if w.expandAny && md.FullName() == "google.protobuf.Any" { | |||
| if canExpand, err := w.writeProto3Any(m); canExpand { | |||
| return err | |||
| } | |||
| } | |||
| fds := md.Fields() | |||
| for i := 0; i < fds.Len(); { | |||
| fd := fds.Get(i) | |||
| if od := fd.ContainingOneof(); od != nil { | |||
| fd = m.WhichOneof(od) | |||
| i += od.Fields().Len() | |||
| } else { | |||
| i++ | |||
| } | |||
| if fd == nil || !m.Has(fd) { | |||
| continue | |||
| } | |||
| switch { | |||
| case fd.IsList(): | |||
| lv := m.Get(fd).List() | |||
| for j := 0; j < lv.Len(); j++ { | |||
| w.writeName(fd) | |||
| v := lv.Get(j) | |||
| if err := w.writeSingularValue(v, fd); err != nil { | |||
| return err | |||
| } | |||
| w.WriteByte('\n') | |||
| } | |||
| case fd.IsMap(): | |||
| kfd := fd.MapKey() | |||
| vfd := fd.MapValue() | |||
| mv := m.Get(fd).Map() | |||
| type entry struct{ key, val protoreflect.Value } | |||
| var entries []entry | |||
| mv.Range(func(k protoreflect.MapKey, v protoreflect.Value) bool { | |||
| entries = append(entries, entry{k.Value(), v}) | |||
| return true | |||
| }) | |||
| sort.Slice(entries, func(i, j int) bool { | |||
| switch kfd.Kind() { | |||
| case protoreflect.BoolKind: | |||
| return !entries[i].key.Bool() && entries[j].key.Bool() | |||
| case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind, protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind: | |||
| return entries[i].key.Int() < entries[j].key.Int() | |||
| case protoreflect.Uint32Kind, protoreflect.Fixed32Kind, protoreflect.Uint64Kind, protoreflect.Fixed64Kind: | |||
| return entries[i].key.Uint() < entries[j].key.Uint() | |||
| case protoreflect.StringKind: | |||
| return entries[i].key.String() < entries[j].key.String() | |||
| default: | |||
| panic("invalid kind") | |||
| } | |||
| }) | |||
| for _, entry := range entries { | |||
| w.writeName(fd) | |||
| w.WriteByte('<') | |||
| if !w.compact { | |||
| w.WriteByte('\n') | |||
| } | |||
| w.indent++ | |||
| w.writeName(kfd) | |||
| if err := w.writeSingularValue(entry.key, kfd); err != nil { | |||
| return err | |||
| } | |||
| w.WriteByte('\n') | |||
| w.writeName(vfd) | |||
| if err := w.writeSingularValue(entry.val, vfd); err != nil { | |||
| return err | |||
| } | |||
| w.WriteByte('\n') | |||
| w.indent-- | |||
| w.WriteByte('>') | |||
| w.WriteByte('\n') | |||
| } | |||
| default: | |||
| w.writeName(fd) | |||
| if err := w.writeSingularValue(m.Get(fd), fd); err != nil { | |||
| return err | |||
| } | |||
| w.WriteByte('\n') | |||
| } | |||
| } | |||
| if b := m.GetUnknown(); len(b) > 0 { | |||
| w.writeUnknownFields(b) | |||
| } | |||
| return w.writeExtensions(m) | |||
| } | |||
| func (w *textWriter) writeSingularValue(v protoreflect.Value, fd protoreflect.FieldDescriptor) error { | |||
| switch fd.Kind() { | |||
| case protoreflect.FloatKind, protoreflect.DoubleKind: | |||
| switch vf := v.Float(); { | |||
| case math.IsInf(vf, +1): | |||
| w.Write(posInf) | |||
| case math.IsInf(vf, -1): | |||
| w.Write(negInf) | |||
| case math.IsNaN(vf): | |||
| w.Write(nan) | |||
| default: | |||
| fmt.Fprint(w, v.Interface()) | |||
| } | |||
| case protoreflect.StringKind: | |||
| // NOTE: This does not validate UTF-8 for historical reasons. | |||
| w.writeQuotedString(string(v.String())) | |||
| case protoreflect.BytesKind: | |||
| w.writeQuotedString(string(v.Bytes())) | |||
| case protoreflect.MessageKind, protoreflect.GroupKind: | |||
| var bra, ket byte = '<', '>' | |||
| if fd.Kind() == protoreflect.GroupKind { | |||
| bra, ket = '{', '}' | |||
| } | |||
| w.WriteByte(bra) | |||
| if !w.compact { | |||
| w.WriteByte('\n') | |||
| } | |||
| w.indent++ | |||
| m := v.Message() | |||
| if m2, ok := m.Interface().(encoding.TextMarshaler); ok { | |||
| b, err := m2.MarshalText() | |||
| if err != nil { | |||
| return err | |||
| } | |||
| w.Write(b) | |||
| } else { | |||
| w.writeMessage(m) | |||
| } | |||
| w.indent-- | |||
| w.WriteByte(ket) | |||
| case protoreflect.EnumKind: | |||
| if ev := fd.Enum().Values().ByNumber(v.Enum()); ev != nil { | |||
| fmt.Fprint(w, ev.Name()) | |||
| } else { | |||
| fmt.Fprint(w, v.Enum()) | |||
| } | |||
| default: | |||
| fmt.Fprint(w, v.Interface()) | |||
| } | |||
| return nil | |||
| } | |||
| // writeQuotedString writes a quoted string in the protocol buffer text format. | |||
| func (w *textWriter) writeQuotedString(s string) { | |||
| w.WriteByte('"') | |||
| for i := 0; i < len(s); i++ { | |||
| switch c := s[i]; c { | |||
| case '\n': | |||
| w.buf = append(w.buf, `\n`...) | |||
| case '\r': | |||
| w.buf = append(w.buf, `\r`...) | |||
| case '\t': | |||
| w.buf = append(w.buf, `\t`...) | |||
| case '"': | |||
| w.buf = append(w.buf, `\"`...) | |||
| case '\\': | |||
| w.buf = append(w.buf, `\\`...) | |||
| default: | |||
| if isPrint := c >= 0x20 && c < 0x7f; isPrint { | |||
| w.buf = append(w.buf, c) | |||
| } else { | |||
| w.buf = append(w.buf, fmt.Sprintf(`\%03o`, c)...) | |||
| } | |||
| } | |||
| } | |||
| w.WriteByte('"') | |||
| } | |||
| func (w *textWriter) writeUnknownFields(b []byte) { | |||
| if !w.compact { | |||
| fmt.Fprintf(w, "/* %d unknown bytes */\n", len(b)) | |||
| } | |||
| for len(b) > 0 { | |||
| num, wtyp, n := protowire.ConsumeTag(b) | |||
| if n < 0 { | |||
| return | |||
| } | |||
| b = b[n:] | |||
| if wtyp == protowire.EndGroupType { | |||
| w.indent-- | |||
| w.Write(endBraceNewline) | |||
| continue | |||
| } | |||
| fmt.Fprint(w, num) | |||
| if wtyp != protowire.StartGroupType { | |||
| w.WriteByte(':') | |||
| } | |||
| if !w.compact || wtyp == protowire.StartGroupType { | |||
| w.WriteByte(' ') | |||
| } | |||
| switch wtyp { | |||
| case protowire.VarintType: | |||
| v, n := protowire.ConsumeVarint(b) | |||
| if n < 0 { | |||
| return | |||
| } | |||
| b = b[n:] | |||
| fmt.Fprint(w, v) | |||
| case protowire.Fixed32Type: | |||
| v, n := protowire.ConsumeFixed32(b) | |||
| if n < 0 { | |||
| return | |||
| } | |||
| b = b[n:] | |||
| fmt.Fprint(w, v) | |||
| case protowire.Fixed64Type: | |||
| v, n := protowire.ConsumeFixed64(b) | |||
| if n < 0 { | |||
| return | |||
| } | |||
| b = b[n:] | |||
| fmt.Fprint(w, v) | |||
| case protowire.BytesType: | |||
| v, n := protowire.ConsumeBytes(b) | |||
| if n < 0 { | |||
| return | |||
| } | |||
| b = b[n:] | |||
| fmt.Fprintf(w, "%q", v) | |||
| case protowire.StartGroupType: | |||
| w.WriteByte('{') | |||
| w.indent++ | |||
| default: | |||
| fmt.Fprintf(w, "/* unknown wire type %d */", wtyp) | |||
| } | |||
| w.WriteByte('\n') | |||
| } | |||
| } | |||
| // writeExtensions writes all the extensions in m. | |||
| func (w *textWriter) writeExtensions(m protoreflect.Message) error { | |||
| md := m.Descriptor() | |||
| if md.ExtensionRanges().Len() == 0 { | |||
| return nil | |||
| } | |||
| type ext struct { | |||
| desc protoreflect.FieldDescriptor | |||
| val protoreflect.Value | |||
| } | |||
| var exts []ext | |||
| m.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool { | |||
| if fd.IsExtension() { | |||
| exts = append(exts, ext{fd, v}) | |||
| } | |||
| return true | |||
| }) | |||
| sort.Slice(exts, func(i, j int) bool { | |||
| return exts[i].desc.Number() < exts[j].desc.Number() | |||
| }) | |||
| for _, ext := range exts { | |||
| // For message set, use the name of the message as the extension name. | |||
| name := string(ext.desc.FullName()) | |||
| if isMessageSet(ext.desc.ContainingMessage()) { | |||
| name = strings.TrimSuffix(name, ".message_set_extension") | |||
| } | |||
| if !ext.desc.IsList() { | |||
| if err := w.writeSingularExtension(name, ext.val, ext.desc); err != nil { | |||
| return err | |||
| } | |||
| } else { | |||
| lv := ext.val.List() | |||
| for i := 0; i < lv.Len(); i++ { | |||
| if err := w.writeSingularExtension(name, lv.Get(i), ext.desc); err != nil { | |||
| return err | |||
| } | |||
| } | |||
| } | |||
| } | |||
| return nil | |||
| } | |||
| func (w *textWriter) writeSingularExtension(name string, v protoreflect.Value, fd protoreflect.FieldDescriptor) error { | |||
| fmt.Fprintf(w, "[%s]:", name) | |||
| if !w.compact { | |||
| w.WriteByte(' ') | |||
| } | |||
| if err := w.writeSingularValue(v, fd); err != nil { | |||
| return err | |||
| } | |||
| w.WriteByte('\n') | |||
| return nil | |||
| } | |||
| func (w *textWriter) writeIndent() { | |||
| if !w.complete { | |||
| return | |||
| } | |||
| for i := 0; i < w.indent*2; i++ { | |||
| w.buf = append(w.buf, ' ') | |||
| } | |||
| w.complete = false | |||
| } | |||
| @@ -1,880 +0,0 @@ | |||
| // Go support for Protocol Buffers - Google's data interchange format | |||
| // | |||
| // Copyright 2010 The Go Authors. All rights reserved. | |||
| // https://github.com/golang/protobuf | |||
| // | |||
| // Redistribution and use in source and binary forms, with or without | |||
| // modification, are permitted provided that the following conditions are | |||
| // met: | |||
| // | |||
| // * Redistributions of source code must retain the above copyright | |||
| // notice, this list of conditions and the following disclaimer. | |||
| // * Redistributions in binary form must reproduce the above | |||
| // copyright notice, this list of conditions and the following disclaimer | |||
| // in the documentation and/or other materials provided with the | |||
| // distribution. | |||
| // * Neither the name of Google Inc. nor the names of its | |||
| // contributors may be used to endorse or promote products derived from | |||
| // this software without specific prior written permission. | |||
| // | |||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| package proto | |||
| // Functions for parsing the Text protocol buffer format. | |||
| // TODO: message sets. | |||
| import ( | |||
| "encoding" | |||
| "errors" | |||
| "fmt" | |||
| "reflect" | |||
| "strconv" | |||
| "strings" | |||
| "unicode/utf8" | |||
| ) | |||
| // Error string emitted when deserializing Any and fields are already set | |||
| const anyRepeatedlyUnpacked = "Any message unpacked multiple times, or %q already set" | |||
| type ParseError struct { | |||
| Message string | |||
| Line int // 1-based line number | |||
| Offset int // 0-based byte offset from start of input | |||
| } | |||
| func (p *ParseError) Error() string { | |||
| if p.Line == 1 { | |||
| // show offset only for first line | |||
| return fmt.Sprintf("line 1.%d: %v", p.Offset, p.Message) | |||
| } | |||
| return fmt.Sprintf("line %d: %v", p.Line, p.Message) | |||
| } | |||
| type token struct { | |||
| value string | |||
| err *ParseError | |||
| line int // line number | |||
| offset int // byte number from start of input, not start of line | |||
| unquoted string // the unquoted version of value, if it was a quoted string | |||
| } | |||
| func (t *token) String() string { | |||
| if t.err == nil { | |||
| return fmt.Sprintf("%q (line=%d, offset=%d)", t.value, t.line, t.offset) | |||
| } | |||
| return fmt.Sprintf("parse error: %v", t.err) | |||
| } | |||
| type textParser struct { | |||
| s string // remaining input | |||
| done bool // whether the parsing is finished (success or error) | |||
| backed bool // whether back() was called | |||
| offset, line int | |||
| cur token | |||
| } | |||
| func newTextParser(s string) *textParser { | |||
| p := new(textParser) | |||
| p.s = s | |||
| p.line = 1 | |||
| p.cur.line = 1 | |||
| return p | |||
| } | |||
| func (p *textParser) errorf(format string, a ...interface{}) *ParseError { | |||
| pe := &ParseError{fmt.Sprintf(format, a...), p.cur.line, p.cur.offset} | |||
| p.cur.err = pe | |||
| p.done = true | |||
| return pe | |||
| } | |||
| // Numbers and identifiers are matched by [-+._A-Za-z0-9] | |||
| func isIdentOrNumberChar(c byte) bool { | |||
| switch { | |||
| case 'A' <= c && c <= 'Z', 'a' <= c && c <= 'z': | |||
| return true | |||
| case '0' <= c && c <= '9': | |||
| return true | |||
| } | |||
| switch c { | |||
| case '-', '+', '.', '_': | |||
| return true | |||
| } | |||
| return false | |||
| } | |||
| func isWhitespace(c byte) bool { | |||
| switch c { | |||
| case ' ', '\t', '\n', '\r': | |||
| return true | |||
| } | |||
| return false | |||
| } | |||
| func isQuote(c byte) bool { | |||
| switch c { | |||
| case '"', '\'': | |||
| return true | |||
| } | |||
| return false | |||
| } | |||
| func (p *textParser) skipWhitespace() { | |||
| i := 0 | |||
| for i < len(p.s) && (isWhitespace(p.s[i]) || p.s[i] == '#') { | |||
| if p.s[i] == '#' { | |||
| // comment; skip to end of line or input | |||
| for i < len(p.s) && p.s[i] != '\n' { | |||
| i++ | |||
| } | |||
| if i == len(p.s) { | |||
| break | |||
| } | |||
| } | |||
| if p.s[i] == '\n' { | |||
| p.line++ | |||
| } | |||
| i++ | |||
| } | |||
| p.offset += i | |||
| p.s = p.s[i:len(p.s)] | |||
| if len(p.s) == 0 { | |||
| p.done = true | |||
| } | |||
| } | |||
| func (p *textParser) advance() { | |||
| // Skip whitespace | |||
| p.skipWhitespace() | |||
| if p.done { | |||
| return | |||
| } | |||
| // Start of non-whitespace | |||
| p.cur.err = nil | |||
| p.cur.offset, p.cur.line = p.offset, p.line | |||
| p.cur.unquoted = "" | |||
| switch p.s[0] { | |||
| case '<', '>', '{', '}', ':', '[', ']', ';', ',', '/': | |||
| // Single symbol | |||
| p.cur.value, p.s = p.s[0:1], p.s[1:len(p.s)] | |||
| case '"', '\'': | |||
| // Quoted string | |||
| i := 1 | |||
| for i < len(p.s) && p.s[i] != p.s[0] && p.s[i] != '\n' { | |||
| if p.s[i] == '\\' && i+1 < len(p.s) { | |||
| // skip escaped char | |||
| i++ | |||
| } | |||
| i++ | |||
| } | |||
| if i >= len(p.s) || p.s[i] != p.s[0] { | |||
| p.errorf("unmatched quote") | |||
| return | |||
| } | |||
| unq, err := unquoteC(p.s[1:i], rune(p.s[0])) | |||
| if err != nil { | |||
| p.errorf("invalid quoted string %s: %v", p.s[0:i+1], err) | |||
| return | |||
| } | |||
| p.cur.value, p.s = p.s[0:i+1], p.s[i+1:len(p.s)] | |||
| p.cur.unquoted = unq | |||
| default: | |||
| i := 0 | |||
| for i < len(p.s) && isIdentOrNumberChar(p.s[i]) { | |||
| i++ | |||
| } | |||
| if i == 0 { | |||
| p.errorf("unexpected byte %#x", p.s[0]) | |||
| return | |||
| } | |||
| p.cur.value, p.s = p.s[0:i], p.s[i:len(p.s)] | |||
| } | |||
| p.offset += len(p.cur.value) | |||
| } | |||
| var ( | |||
| errBadUTF8 = errors.New("proto: bad UTF-8") | |||
| ) | |||
| func unquoteC(s string, quote rune) (string, error) { | |||
| // This is based on C++'s tokenizer.cc. | |||
| // Despite its name, this is *not* parsing C syntax. | |||
| // For instance, "\0" is an invalid quoted string. | |||
| // Avoid allocation in trivial cases. | |||
| simple := true | |||
| for _, r := range s { | |||
| if r == '\\' || r == quote { | |||
| simple = false | |||
| break | |||
| } | |||
| } | |||
| if simple { | |||
| return s, nil | |||
| } | |||
| buf := make([]byte, 0, 3*len(s)/2) | |||
| for len(s) > 0 { | |||
| r, n := utf8.DecodeRuneInString(s) | |||
| if r == utf8.RuneError && n == 1 { | |||
| return "", errBadUTF8 | |||
| } | |||
| s = s[n:] | |||
| if r != '\\' { | |||
| if r < utf8.RuneSelf { | |||
| buf = append(buf, byte(r)) | |||
| } else { | |||
| buf = append(buf, string(r)...) | |||
| } | |||
| continue | |||
| } | |||
| ch, tail, err := unescape(s) | |||
| if err != nil { | |||
| return "", err | |||
| } | |||
| buf = append(buf, ch...) | |||
| s = tail | |||
| } | |||
| return string(buf), nil | |||
| } | |||
| func unescape(s string) (ch string, tail string, err error) { | |||
| r, n := utf8.DecodeRuneInString(s) | |||
| if r == utf8.RuneError && n == 1 { | |||
| return "", "", errBadUTF8 | |||
| } | |||
| s = s[n:] | |||
| switch r { | |||
| case 'a': | |||
| return "\a", s, nil | |||
| case 'b': | |||
| return "\b", s, nil | |||
| case 'f': | |||
| return "\f", s, nil | |||
| case 'n': | |||
| return "\n", s, nil | |||
| case 'r': | |||
| return "\r", s, nil | |||
| case 't': | |||
| return "\t", s, nil | |||
| case 'v': | |||
| return "\v", s, nil | |||
| case '?': | |||
| return "?", s, nil // trigraph workaround | |||
| case '\'', '"', '\\': | |||
| return string(r), s, nil | |||
| case '0', '1', '2', '3', '4', '5', '6', '7': | |||
| if len(s) < 2 { | |||
| return "", "", fmt.Errorf(`\%c requires 2 following digits`, r) | |||
| } | |||
| ss := string(r) + s[:2] | |||
| s = s[2:] | |||
| i, err := strconv.ParseUint(ss, 8, 8) | |||
| if err != nil { | |||
| return "", "", fmt.Errorf(`\%s contains non-octal digits`, ss) | |||
| } | |||
| return string([]byte{byte(i)}), s, nil | |||
| case 'x', 'X', 'u', 'U': | |||
| var n int | |||
| switch r { | |||
| case 'x', 'X': | |||
| n = 2 | |||
| case 'u': | |||
| n = 4 | |||
| case 'U': | |||
| n = 8 | |||
| } | |||
| if len(s) < n { | |||
| return "", "", fmt.Errorf(`\%c requires %d following digits`, r, n) | |||
| } | |||
| ss := s[:n] | |||
| s = s[n:] | |||
| i, err := strconv.ParseUint(ss, 16, 64) | |||
| if err != nil { | |||
| return "", "", fmt.Errorf(`\%c%s contains non-hexadecimal digits`, r, ss) | |||
| } | |||
| if r == 'x' || r == 'X' { | |||
| return string([]byte{byte(i)}), s, nil | |||
| } | |||
| if i > utf8.MaxRune { | |||
| return "", "", fmt.Errorf(`\%c%s is not a valid Unicode code point`, r, ss) | |||
| } | |||
| return string(i), s, nil | |||
| } | |||
| return "", "", fmt.Errorf(`unknown escape \%c`, r) | |||
| } | |||
| // Back off the parser by one token. Can only be done between calls to next(). | |||
| // It makes the next advance() a no-op. | |||
| func (p *textParser) back() { p.backed = true } | |||
| // Advances the parser and returns the new current token. | |||
| func (p *textParser) next() *token { | |||
| if p.backed || p.done { | |||
| p.backed = false | |||
| return &p.cur | |||
| } | |||
| p.advance() | |||
| if p.done { | |||
| p.cur.value = "" | |||
| } else if len(p.cur.value) > 0 && isQuote(p.cur.value[0]) { | |||
| // Look for multiple quoted strings separated by whitespace, | |||
| // and concatenate them. | |||
| cat := p.cur | |||
| for { | |||
| p.skipWhitespace() | |||
| if p.done || !isQuote(p.s[0]) { | |||
| break | |||
| } | |||
| p.advance() | |||
| if p.cur.err != nil { | |||
| return &p.cur | |||
| } | |||
| cat.value += " " + p.cur.value | |||
| cat.unquoted += p.cur.unquoted | |||
| } | |||
| p.done = false // parser may have seen EOF, but we want to return cat | |||
| p.cur = cat | |||
| } | |||
| return &p.cur | |||
| } | |||
| func (p *textParser) consumeToken(s string) error { | |||
| tok := p.next() | |||
| if tok.err != nil { | |||
| return tok.err | |||
| } | |||
| if tok.value != s { | |||
| p.back() | |||
| return p.errorf("expected %q, found %q", s, tok.value) | |||
| } | |||
| return nil | |||
| } | |||
| // Return a RequiredNotSetError indicating which required field was not set. | |||
| func (p *textParser) missingRequiredFieldError(sv reflect.Value) *RequiredNotSetError { | |||
| st := sv.Type() | |||
| sprops := GetProperties(st) | |||
| for i := 0; i < st.NumField(); i++ { | |||
| if !isNil(sv.Field(i)) { | |||
| continue | |||
| } | |||
| props := sprops.Prop[i] | |||
| if props.Required { | |||
| return &RequiredNotSetError{fmt.Sprintf("%v.%v", st, props.OrigName)} | |||
| } | |||
| } | |||
| return &RequiredNotSetError{fmt.Sprintf("%v.<unknown field name>", st)} // should not happen | |||
| } | |||
| // Returns the index in the struct for the named field, as well as the parsed tag properties. | |||
| func structFieldByName(sprops *StructProperties, name string) (int, *Properties, bool) { | |||
| i, ok := sprops.decoderOrigNames[name] | |||
| if ok { | |||
| return i, sprops.Prop[i], true | |||
| } | |||
| return -1, nil, false | |||
| } | |||
| // Consume a ':' from the input stream (if the next token is a colon), | |||
| // returning an error if a colon is needed but not present. | |||
| func (p *textParser) checkForColon(props *Properties, typ reflect.Type) *ParseError { | |||
| tok := p.next() | |||
| if tok.err != nil { | |||
| return tok.err | |||
| } | |||
| if tok.value != ":" { | |||
| // Colon is optional when the field is a group or message. | |||
| needColon := true | |||
| switch props.Wire { | |||
| case "group": | |||
| needColon = false | |||
| case "bytes": | |||
| // A "bytes" field is either a message, a string, or a repeated field; | |||
| // those three become *T, *string and []T respectively, so we can check for | |||
| // this field being a pointer to a non-string. | |||
| if typ.Kind() == reflect.Ptr { | |||
| // *T or *string | |||
| if typ.Elem().Kind() == reflect.String { | |||
| break | |||
| } | |||
| } else if typ.Kind() == reflect.Slice { | |||
| // []T or []*T | |||
| if typ.Elem().Kind() != reflect.Ptr { | |||
| break | |||
| } | |||
| } else if typ.Kind() == reflect.String { | |||
| // The proto3 exception is for a string field, | |||
| // which requires a colon. | |||
| break | |||
| } | |||
| needColon = false | |||
| } | |||
| if needColon { | |||
| return p.errorf("expected ':', found %q", tok.value) | |||
| } | |||
| p.back() | |||
| } | |||
| return nil | |||
| } | |||
| func (p *textParser) readStruct(sv reflect.Value, terminator string) error { | |||
| st := sv.Type() | |||
| sprops := GetProperties(st) | |||
| reqCount := sprops.reqCount | |||
| var reqFieldErr error | |||
| fieldSet := make(map[string]bool) | |||
| // A struct is a sequence of "name: value", terminated by one of | |||
| // '>' or '}', or the end of the input. A name may also be | |||
| // "[extension]" or "[type/url]". | |||
| // | |||
| // The whole struct can also be an expanded Any message, like: | |||
| // [type/url] < ... struct contents ... > | |||
| for { | |||
| tok := p.next() | |||
| if tok.err != nil { | |||
| return tok.err | |||
| } | |||
| if tok.value == terminator { | |||
| break | |||
| } | |||
| if tok.value == "[" { | |||
| // Looks like an extension or an Any. | |||
| // | |||
| // TODO: Check whether we need to handle | |||
| // namespace rooted names (e.g. ".something.Foo"). | |||
| extName, err := p.consumeExtName() | |||
| if err != nil { | |||
| return err | |||
| } | |||
| if s := strings.LastIndex(extName, "/"); s >= 0 { | |||
| // If it contains a slash, it's an Any type URL. | |||
| messageName := extName[s+1:] | |||
| mt := MessageType(messageName) | |||
| if mt == nil { | |||
| return p.errorf("unrecognized message %q in google.protobuf.Any", messageName) | |||
| } | |||
| tok = p.next() | |||
| if tok.err != nil { | |||
| return tok.err | |||
| } | |||
| // consume an optional colon | |||
| if tok.value == ":" { | |||
| tok = p.next() | |||
| if tok.err != nil { | |||
| return tok.err | |||
| } | |||
| } | |||
| var terminator string | |||
| switch tok.value { | |||
| case "<": | |||
| terminator = ">" | |||
| case "{": | |||
| terminator = "}" | |||
| default: | |||
| return p.errorf("expected '{' or '<', found %q", tok.value) | |||
| } | |||
| v := reflect.New(mt.Elem()) | |||
| if pe := p.readStruct(v.Elem(), terminator); pe != nil { | |||
| return pe | |||
| } | |||
| b, err := Marshal(v.Interface().(Message)) | |||
| if err != nil { | |||
| return p.errorf("failed to marshal message of type %q: %v", messageName, err) | |||
| } | |||
| if fieldSet["type_url"] { | |||
| return p.errorf(anyRepeatedlyUnpacked, "type_url") | |||
| } | |||
| if fieldSet["value"] { | |||
| return p.errorf(anyRepeatedlyUnpacked, "value") | |||
| } | |||
| sv.FieldByName("TypeUrl").SetString(extName) | |||
| sv.FieldByName("Value").SetBytes(b) | |||
| fieldSet["type_url"] = true | |||
| fieldSet["value"] = true | |||
| continue | |||
| } | |||
| var desc *ExtensionDesc | |||
| // This could be faster, but it's functional. | |||
| // TODO: Do something smarter than a linear scan. | |||
| for _, d := range RegisteredExtensions(reflect.New(st).Interface().(Message)) { | |||
| if d.Name == extName { | |||
| desc = d | |||
| break | |||
| } | |||
| } | |||
| if desc == nil { | |||
| return p.errorf("unrecognized extension %q", extName) | |||
| } | |||
| props := &Properties{} | |||
| props.Parse(desc.Tag) | |||
| typ := reflect.TypeOf(desc.ExtensionType) | |||
| if err := p.checkForColon(props, typ); err != nil { | |||
| return err | |||
| } | |||
| rep := desc.repeated() | |||
| // Read the extension structure, and set it in | |||
| // the value we're constructing. | |||
| var ext reflect.Value | |||
| if !rep { | |||
| ext = reflect.New(typ).Elem() | |||
| } else { | |||
| ext = reflect.New(typ.Elem()).Elem() | |||
| } | |||
| if err := p.readAny(ext, props); err != nil { | |||
| if _, ok := err.(*RequiredNotSetError); !ok { | |||
| return err | |||
| } | |||
| reqFieldErr = err | |||
| } | |||
| ep := sv.Addr().Interface().(Message) | |||
| if !rep { | |||
| SetExtension(ep, desc, ext.Interface()) | |||
| } else { | |||
| old, err := GetExtension(ep, desc) | |||
| var sl reflect.Value | |||
| if err == nil { | |||
| sl = reflect.ValueOf(old) // existing slice | |||
| } else { | |||
| sl = reflect.MakeSlice(typ, 0, 1) | |||
| } | |||
| sl = reflect.Append(sl, ext) | |||
| SetExtension(ep, desc, sl.Interface()) | |||
| } | |||
| if err := p.consumeOptionalSeparator(); err != nil { | |||
| return err | |||
| } | |||
| continue | |||
| } | |||
| // This is a normal, non-extension field. | |||
| name := tok.value | |||
| var dst reflect.Value | |||
| fi, props, ok := structFieldByName(sprops, name) | |||
| if ok { | |||
| dst = sv.Field(fi) | |||
| } else if oop, ok := sprops.OneofTypes[name]; ok { | |||
| // It is a oneof. | |||
| props = oop.Prop | |||
| nv := reflect.New(oop.Type.Elem()) | |||
| dst = nv.Elem().Field(0) | |||
| field := sv.Field(oop.Field) | |||
| if !field.IsNil() { | |||
| return p.errorf("field '%s' would overwrite already parsed oneof '%s'", name, sv.Type().Field(oop.Field).Name) | |||
| } | |||
| field.Set(nv) | |||
| } | |||
| if !dst.IsValid() { | |||
| return p.errorf("unknown field name %q in %v", name, st) | |||
| } | |||
| if dst.Kind() == reflect.Map { | |||
| // Consume any colon. | |||
| if err := p.checkForColon(props, dst.Type()); err != nil { | |||
| return err | |||
| } | |||
| // Construct the map if it doesn't already exist. | |||
| if dst.IsNil() { | |||
| dst.Set(reflect.MakeMap(dst.Type())) | |||
| } | |||
| key := reflect.New(dst.Type().Key()).Elem() | |||
| val := reflect.New(dst.Type().Elem()).Elem() | |||
| // The map entry should be this sequence of tokens: | |||
| // < key : KEY value : VALUE > | |||
| // However, implementations may omit key or value, and technically | |||
| // we should support them in any order. See b/28924776 for a time | |||
| // this went wrong. | |||
| tok := p.next() | |||
| var terminator string | |||
| switch tok.value { | |||
| case "<": | |||
| terminator = ">" | |||
| case "{": | |||
| terminator = "}" | |||
| default: | |||
| return p.errorf("expected '{' or '<', found %q", tok.value) | |||
| } | |||
| for { | |||
| tok := p.next() | |||
| if tok.err != nil { | |||
| return tok.err | |||
| } | |||
| if tok.value == terminator { | |||
| break | |||
| } | |||
| switch tok.value { | |||
| case "key": | |||
| if err := p.consumeToken(":"); err != nil { | |||
| return err | |||
| } | |||
| if err := p.readAny(key, props.MapKeyProp); err != nil { | |||
| return err | |||
| } | |||
| if err := p.consumeOptionalSeparator(); err != nil { | |||
| return err | |||
| } | |||
| case "value": | |||
| if err := p.checkForColon(props.MapValProp, dst.Type().Elem()); err != nil { | |||
| return err | |||
| } | |||
| if err := p.readAny(val, props.MapValProp); err != nil { | |||
| return err | |||
| } | |||
| if err := p.consumeOptionalSeparator(); err != nil { | |||
| return err | |||
| } | |||
| default: | |||
| p.back() | |||
| return p.errorf(`expected "key", "value", or %q, found %q`, terminator, tok.value) | |||
| } | |||
| } | |||
| dst.SetMapIndex(key, val) | |||
| continue | |||
| } | |||
| // Check that it's not already set if it's not a repeated field. | |||
| if !props.Repeated && fieldSet[name] { | |||
| return p.errorf("non-repeated field %q was repeated", name) | |||
| } | |||
| if err := p.checkForColon(props, dst.Type()); err != nil { | |||
| return err | |||
| } | |||
| // Parse into the field. | |||
| fieldSet[name] = true | |||
| if err := p.readAny(dst, props); err != nil { | |||
| if _, ok := err.(*RequiredNotSetError); !ok { | |||
| return err | |||
| } | |||
| reqFieldErr = err | |||
| } | |||
| if props.Required { | |||
| reqCount-- | |||
| } | |||
| if err := p.consumeOptionalSeparator(); err != nil { | |||
| return err | |||
| } | |||
| } | |||
| if reqCount > 0 { | |||
| return p.missingRequiredFieldError(sv) | |||
| } | |||
| return reqFieldErr | |||
| } | |||
| // consumeExtName consumes extension name or expanded Any type URL and the | |||
| // following ']'. It returns the name or URL consumed. | |||
| func (p *textParser) consumeExtName() (string, error) { | |||
| tok := p.next() | |||
| if tok.err != nil { | |||
| return "", tok.err | |||
| } | |||
| // If extension name or type url is quoted, it's a single token. | |||
| if len(tok.value) > 2 && isQuote(tok.value[0]) && tok.value[len(tok.value)-1] == tok.value[0] { | |||
| name, err := unquoteC(tok.value[1:len(tok.value)-1], rune(tok.value[0])) | |||
| if err != nil { | |||
| return "", err | |||
| } | |||
| return name, p.consumeToken("]") | |||
| } | |||
| // Consume everything up to "]" | |||
| var parts []string | |||
| for tok.value != "]" { | |||
| parts = append(parts, tok.value) | |||
| tok = p.next() | |||
| if tok.err != nil { | |||
| return "", p.errorf("unrecognized type_url or extension name: %s", tok.err) | |||
| } | |||
| if p.done && tok.value != "]" { | |||
| return "", p.errorf("unclosed type_url or extension name") | |||
| } | |||
| } | |||
| return strings.Join(parts, ""), nil | |||
| } | |||
| // consumeOptionalSeparator consumes an optional semicolon or comma. | |||
| // It is used in readStruct to provide backward compatibility. | |||
| func (p *textParser) consumeOptionalSeparator() error { | |||
| tok := p.next() | |||
| if tok.err != nil { | |||
| return tok.err | |||
| } | |||
| if tok.value != ";" && tok.value != "," { | |||
| p.back() | |||
| } | |||
| return nil | |||
| } | |||
| func (p *textParser) readAny(v reflect.Value, props *Properties) error { | |||
| tok := p.next() | |||
| if tok.err != nil { | |||
| return tok.err | |||
| } | |||
| if tok.value == "" { | |||
| return p.errorf("unexpected EOF") | |||
| } | |||
| switch fv := v; fv.Kind() { | |||
| case reflect.Slice: | |||
| at := v.Type() | |||
| if at.Elem().Kind() == reflect.Uint8 { | |||
| // Special case for []byte | |||
| if tok.value[0] != '"' && tok.value[0] != '\'' { | |||
| // Deliberately written out here, as the error after | |||
| // this switch statement would write "invalid []byte: ...", | |||
| // which is not as user-friendly. | |||
| return p.errorf("invalid string: %v", tok.value) | |||
| } | |||
| bytes := []byte(tok.unquoted) | |||
| fv.Set(reflect.ValueOf(bytes)) | |||
| return nil | |||
| } | |||
| // Repeated field. | |||
| if tok.value == "[" { | |||
| // Repeated field with list notation, like [1,2,3]. | |||
| for { | |||
| fv.Set(reflect.Append(fv, reflect.New(at.Elem()).Elem())) | |||
| err := p.readAny(fv.Index(fv.Len()-1), props) | |||
| if err != nil { | |||
| return err | |||
| } | |||
| tok := p.next() | |||
| if tok.err != nil { | |||
| return tok.err | |||
| } | |||
| if tok.value == "]" { | |||
| break | |||
| } | |||
| if tok.value != "," { | |||
| return p.errorf("Expected ']' or ',' found %q", tok.value) | |||
| } | |||
| } | |||
| return nil | |||
| } | |||
| // One value of the repeated field. | |||
| p.back() | |||
| fv.Set(reflect.Append(fv, reflect.New(at.Elem()).Elem())) | |||
| return p.readAny(fv.Index(fv.Len()-1), props) | |||
| case reflect.Bool: | |||
| // true/1/t/True or false/f/0/False. | |||
| switch tok.value { | |||
| case "true", "1", "t", "True": | |||
| fv.SetBool(true) | |||
| return nil | |||
| case "false", "0", "f", "False": | |||
| fv.SetBool(false) | |||
| return nil | |||
| } | |||
| case reflect.Float32, reflect.Float64: | |||
| v := tok.value | |||
| // Ignore 'f' for compatibility with output generated by C++, but don't | |||
| // remove 'f' when the value is "-inf" or "inf". | |||
| if strings.HasSuffix(v, "f") && tok.value != "-inf" && tok.value != "inf" { | |||
| v = v[:len(v)-1] | |||
| } | |||
| if f, err := strconv.ParseFloat(v, fv.Type().Bits()); err == nil { | |||
| fv.SetFloat(f) | |||
| return nil | |||
| } | |||
| case reflect.Int32: | |||
| if x, err := strconv.ParseInt(tok.value, 0, 32); err == nil { | |||
| fv.SetInt(x) | |||
| return nil | |||
| } | |||
| if len(props.Enum) == 0 { | |||
| break | |||
| } | |||
| m, ok := enumValueMaps[props.Enum] | |||
| if !ok { | |||
| break | |||
| } | |||
| x, ok := m[tok.value] | |||
| if !ok { | |||
| break | |||
| } | |||
| fv.SetInt(int64(x)) | |||
| return nil | |||
| case reflect.Int64: | |||
| if x, err := strconv.ParseInt(tok.value, 0, 64); err == nil { | |||
| fv.SetInt(x) | |||
| return nil | |||
| } | |||
| case reflect.Ptr: | |||
| // A basic field (indirected through pointer), or a repeated message/group | |||
| p.back() | |||
| fv.Set(reflect.New(fv.Type().Elem())) | |||
| return p.readAny(fv.Elem(), props) | |||
| case reflect.String: | |||
| if tok.value[0] == '"' || tok.value[0] == '\'' { | |||
| fv.SetString(tok.unquoted) | |||
| return nil | |||
| } | |||
| case reflect.Struct: | |||
| var terminator string | |||
| switch tok.value { | |||
| case "{": | |||
| terminator = "}" | |||
| case "<": | |||
| terminator = ">" | |||
| default: | |||
| return p.errorf("expected '{' or '<', found %q", tok.value) | |||
| } | |||
| // TODO: Handle nested messages which implement encoding.TextUnmarshaler. | |||
| return p.readStruct(fv, terminator) | |||
| case reflect.Uint32: | |||
| if x, err := strconv.ParseUint(tok.value, 0, 32); err == nil { | |||
| fv.SetUint(uint64(x)) | |||
| return nil | |||
| } | |||
| case reflect.Uint64: | |||
| if x, err := strconv.ParseUint(tok.value, 0, 64); err == nil { | |||
| fv.SetUint(x) | |||
| return nil | |||
| } | |||
| } | |||
| return p.errorf("invalid %v: %v", v.Type(), tok.value) | |||
| } | |||
| // UnmarshalText reads a protocol buffer in Text format. UnmarshalText resets pb | |||
| // before starting to unmarshal, so any existing data in pb is always removed. | |||
| // If a required field is not set and no other error occurs, | |||
| // UnmarshalText returns *RequiredNotSetError. | |||
| func UnmarshalText(s string, pb Message) error { | |||
| if um, ok := pb.(encoding.TextUnmarshaler); ok { | |||
| return um.UnmarshalText([]byte(s)) | |||
| } | |||
| pb.Reset() | |||
| v := reflect.ValueOf(pb) | |||
| return newTextParser(s).readStruct(v.Elem(), "") | |||
| } | |||
| @@ -0,0 +1,78 @@ | |||
| // Copyright 2019 The Go Authors. All rights reserved. | |||
| // Use of this source code is governed by a BSD-style | |||
| // license that can be found in the LICENSE file. | |||
| package proto | |||
| import ( | |||
| protoV2 "google.golang.org/protobuf/proto" | |||
| "google.golang.org/protobuf/runtime/protoiface" | |||
| ) | |||
| // Size returns the size in bytes of the wire-format encoding of m. | |||
| func Size(m Message) int { | |||
| if m == nil { | |||
| return 0 | |||
| } | |||
| mi := MessageV2(m) | |||
| return protoV2.Size(mi) | |||
| } | |||
| // Marshal returns the wire-format encoding of m. | |||
| func Marshal(m Message) ([]byte, error) { | |||
| b, err := marshalAppend(nil, m, false) | |||
| if b == nil { | |||
| b = zeroBytes | |||
| } | |||
| return b, err | |||
| } | |||
| var zeroBytes = make([]byte, 0, 0) | |||
| func marshalAppend(buf []byte, m Message, deterministic bool) ([]byte, error) { | |||
| if m == nil { | |||
| return nil, ErrNil | |||
| } | |||
| mi := MessageV2(m) | |||
| nbuf, err := protoV2.MarshalOptions{ | |||
| Deterministic: deterministic, | |||
| AllowPartial: true, | |||
| }.MarshalAppend(buf, mi) | |||
| if err != nil { | |||
| return buf, err | |||
| } | |||
| if len(buf) == len(nbuf) { | |||
| if !mi.ProtoReflect().IsValid() { | |||
| return buf, ErrNil | |||
| } | |||
| } | |||
| return nbuf, checkRequiredNotSet(mi) | |||
| } | |||
| // Unmarshal parses a wire-format message in b and places the decoded results in m. | |||
| // | |||
| // Unmarshal resets m before starting to unmarshal, so any existing data in m is always | |||
| // removed. Use UnmarshalMerge to preserve and append to existing data. | |||
| func Unmarshal(b []byte, m Message) error { | |||
| m.Reset() | |||
| return UnmarshalMerge(b, m) | |||
| } | |||
| // UnmarshalMerge parses a wire-format message in b and places the decoded results in m. | |||
| func UnmarshalMerge(b []byte, m Message) error { | |||
| mi := MessageV2(m) | |||
| out, err := protoV2.UnmarshalOptions{ | |||
| AllowPartial: true, | |||
| Merge: true, | |||
| }.UnmarshalState(protoiface.UnmarshalInput{ | |||
| Buf: b, | |||
| Message: mi.ProtoReflect(), | |||
| }) | |||
| if err != nil { | |||
| return err | |||
| } | |||
| if out.Flags&protoiface.UnmarshalInitialized > 0 { | |||
| return nil | |||
| } | |||
| return checkRequiredNotSet(mi) | |||
| } | |||
| @@ -0,0 +1,34 @@ | |||
| // Copyright 2019 The Go Authors. All rights reserved. | |||
| // Use of this source code is governed by a BSD-style | |||
| // license that can be found in the LICENSE file. | |||
| package proto | |||
| // Bool stores v in a new bool value and returns a pointer to it. | |||
| func Bool(v bool) *bool { return &v } | |||
| // Int stores v in a new int32 value and returns a pointer to it. | |||
| // | |||
| // Deprecated: Use Int32 instead. | |||
| func Int(v int) *int32 { return Int32(int32(v)) } | |||
| // Int32 stores v in a new int32 value and returns a pointer to it. | |||
| func Int32(v int32) *int32 { return &v } | |||
| // Int64 stores v in a new int64 value and returns a pointer to it. | |||
| func Int64(v int64) *int64 { return &v } | |||
| // Uint32 stores v in a new uint32 value and returns a pointer to it. | |||
| func Uint32(v uint32) *uint32 { return &v } | |||
| // Uint64 stores v in a new uint64 value and returns a pointer to it. | |||
| func Uint64(v uint64) *uint64 { return &v } | |||
| // Float32 stores v in a new float32 value and returns a pointer to it. | |||
| func Float32(v float32) *float32 { return &v } | |||
| // Float64 stores v in a new float64 value and returns a pointer to it. | |||
| func Float64(v float64) *float64 { return &v } | |||
| // String stores v in a new string value and returns a pointer to it. | |||
| func String(v string) *string { return &v } | |||
| @@ -0,0 +1,363 @@ | |||
| Mozilla Public License, version 2.0 | |||
| 1. Definitions | |||
| 1.1. "Contributor" | |||
| means each individual or legal entity that creates, contributes to the | |||
| creation of, or owns Covered Software. | |||
| 1.2. "Contributor Version" | |||
| means the combination of the Contributions of others (if any) used by a | |||
| Contributor and that particular Contributor's Contribution. | |||
| 1.3. "Contribution" | |||
| means Covered Software of a particular Contributor. | |||
| 1.4. "Covered Software" | |||
| means Source Code Form to which the initial Contributor has attached the | |||
| notice in Exhibit A, the Executable Form of such Source Code Form, and | |||
| Modifications of such Source Code Form, in each case including portions | |||
| thereof. | |||
| 1.5. "Incompatible With Secondary Licenses" | |||
| means | |||
| a. that the initial Contributor has attached the notice described in | |||
| Exhibit B to the Covered Software; or | |||
| b. that the Covered Software was made available under the terms of | |||
| version 1.1 or earlier of the License, but not also under the terms of | |||
| a Secondary License. | |||
| 1.6. "Executable Form" | |||
| means any form of the work other than Source Code Form. | |||
| 1.7. "Larger Work" | |||
| means a work that combines Covered Software with other material, in a | |||
| separate file or files, that is not Covered Software. | |||
| 1.8. "License" | |||
| means this document. | |||
| 1.9. "Licensable" | |||
| means having the right to grant, to the maximum extent possible, whether | |||
| at the time of the initial grant or subsequently, any and all of the | |||
| rights conveyed by this License. | |||
| 1.10. "Modifications" | |||
| means any of the following: | |||
| a. any file in Source Code Form that results from an addition to, | |||
| deletion from, or modification of the contents of Covered Software; or | |||
| b. any new file in Source Code Form that contains any Covered Software. | |||
| 1.11. "Patent Claims" of a Contributor | |||
| means any patent claim(s), including without limitation, method, | |||
| process, and apparatus claims, in any patent Licensable by such | |||
| Contributor that would be infringed, but for the grant of the License, | |||
| by the making, using, selling, offering for sale, having made, import, | |||
| or transfer of either its Contributions or its Contributor Version. | |||
| 1.12. "Secondary License" | |||
| means either the GNU General Public License, Version 2.0, the GNU Lesser | |||
| General Public License, Version 2.1, the GNU Affero General Public | |||
| License, Version 3.0, or any later versions of those licenses. | |||
| 1.13. "Source Code Form" | |||
| means the form of the work preferred for making modifications. | |||
| 1.14. "You" (or "Your") | |||
| means an individual or a legal entity exercising rights under this | |||
| License. For legal entities, "You" includes any entity that controls, is | |||
| controlled by, or is under common control with You. For purposes of this | |||
| definition, "control" means (a) the power, direct or indirect, to cause | |||
| the direction or management of such entity, whether by contract or | |||
| otherwise, or (b) ownership of more than fifty percent (50%) of the | |||
| outstanding shares or beneficial ownership of such entity. | |||
| 2. License Grants and Conditions | |||
| 2.1. Grants | |||
| Each Contributor hereby grants You a world-wide, royalty-free, | |||
| non-exclusive license: | |||
| a. under intellectual property rights (other than patent or trademark) | |||
| Licensable by such Contributor to use, reproduce, make available, | |||
| modify, display, perform, distribute, and otherwise exploit its | |||
| Contributions, either on an unmodified basis, with Modifications, or | |||
| as part of a Larger Work; and | |||
| b. under Patent Claims of such Contributor to make, use, sell, offer for | |||
| sale, have made, import, and otherwise transfer either its | |||
| Contributions or its Contributor Version. | |||
| 2.2. Effective Date | |||
| The licenses granted in Section 2.1 with respect to any Contribution | |||
| become effective for each Contribution on the date the Contributor first | |||
| distributes such Contribution. | |||
| 2.3. Limitations on Grant Scope | |||
| The licenses granted in this Section 2 are the only rights granted under | |||
| this License. No additional rights or licenses will be implied from the | |||
| distribution or licensing of Covered Software under this License. | |||
| Notwithstanding Section 2.1(b) above, no patent license is granted by a | |||
| Contributor: | |||
| a. for any code that a Contributor has removed from Covered Software; or | |||
| b. for infringements caused by: (i) Your and any other third party's | |||
| modifications of Covered Software, or (ii) the combination of its | |||
| Contributions with other software (except as part of its Contributor | |||
| Version); or | |||
| c. under Patent Claims infringed by Covered Software in the absence of | |||
| its Contributions. | |||
| This License does not grant any rights in the trademarks, service marks, | |||
| or logos of any Contributor (except as may be necessary to comply with | |||
| the notice requirements in Section 3.4). | |||
| 2.4. Subsequent Licenses | |||
| No Contributor makes additional grants as a result of Your choice to | |||
| distribute the Covered Software under a subsequent version of this | |||
| License (see Section 10.2) or under the terms of a Secondary License (if | |||
| permitted under the terms of Section 3.3). | |||
| 2.5. Representation | |||
| Each Contributor represents that the Contributor believes its | |||
| Contributions are its original creation(s) or it has sufficient rights to | |||
| grant the rights to its Contributions conveyed by this License. | |||
| 2.6. Fair Use | |||
| This License is not intended to limit any rights You have under | |||
| applicable copyright doctrines of fair use, fair dealing, or other | |||
| equivalents. | |||
| 2.7. Conditions | |||
| Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in | |||
| Section 2.1. | |||
| 3. Responsibilities | |||
| 3.1. Distribution of Source Form | |||
| All distribution of Covered Software in Source Code Form, including any | |||
| Modifications that You create or to which You contribute, must be under | |||
| the terms of this License. You must inform recipients that the Source | |||
| Code Form of the Covered Software is governed by the terms of this | |||
| License, and how they can obtain a copy of this License. You may not | |||
| attempt to alter or restrict the recipients' rights in the Source Code | |||
| Form. | |||
| 3.2. Distribution of Executable Form | |||
| If You distribute Covered Software in Executable Form then: | |||
| a. such Covered Software must also be made available in Source Code Form, | |||
| as described in Section 3.1, and You must inform recipients of the | |||
| Executable Form how they can obtain a copy of such Source Code Form by | |||
| reasonable means in a timely manner, at a charge no more than the cost | |||
| of distribution to the recipient; and | |||
| b. You may distribute such Executable Form under the terms of this | |||
| License, or sublicense it under different terms, provided that the | |||
| license for the Executable Form does not attempt to limit or alter the | |||
| recipients' rights in the Source Code Form under this License. | |||
| 3.3. Distribution of a Larger Work | |||
| You may create and distribute a Larger Work under terms of Your choice, | |||
| provided that You also comply with the requirements of this License for | |||
| the Covered Software. If the Larger Work is a combination of Covered | |||
| Software with a work governed by one or more Secondary Licenses, and the | |||
| Covered Software is not Incompatible With Secondary Licenses, this | |||
| License permits You to additionally distribute such Covered Software | |||
| under the terms of such Secondary License(s), so that the recipient of | |||
| the Larger Work may, at their option, further distribute the Covered | |||
| Software under the terms of either this License or such Secondary | |||
| License(s). | |||
| 3.4. Notices | |||
| You may not remove or alter the substance of any license notices | |||
| (including copyright notices, patent notices, disclaimers of warranty, or | |||
| limitations of liability) contained within the Source Code Form of the | |||
| Covered Software, except that You may alter any license notices to the | |||
| extent required to remedy known factual inaccuracies. | |||
| 3.5. Application of Additional Terms | |||
| You may choose to offer, and to charge a fee for, warranty, support, | |||
| indemnity or liability obligations to one or more recipients of Covered | |||
| Software. However, You may do so only on Your own behalf, and not on | |||
| behalf of any Contributor. You must make it absolutely clear that any | |||
| such warranty, support, indemnity, or liability obligation is offered by | |||
| You alone, and You hereby agree to indemnify every Contributor for any | |||
| liability incurred by such Contributor as a result of warranty, support, | |||
| indemnity or liability terms You offer. You may include additional | |||
| disclaimers of warranty and limitations of liability specific to any | |||
| jurisdiction. | |||
| 4. Inability to Comply Due to Statute or Regulation | |||
| If it is impossible for You to comply with any of the terms of this License | |||
| with respect to some or all of the Covered Software due to statute, | |||
| judicial order, or regulation then You must: (a) comply with the terms of | |||
| this License to the maximum extent possible; and (b) describe the | |||
| limitations and the code they affect. Such description must be placed in a | |||
| text file included with all distributions of the Covered Software under | |||
| this License. Except to the extent prohibited by statute or regulation, | |||
| such description must be sufficiently detailed for a recipient of ordinary | |||
| skill to be able to understand it. | |||
| 5. Termination | |||
| 5.1. The rights granted under this License will terminate automatically if You | |||
| fail to comply with any of its terms. However, if You become compliant, | |||
| then the rights granted under this License from a particular Contributor | |||
| are reinstated (a) provisionally, unless and until such Contributor | |||
| explicitly and finally terminates Your grants, and (b) on an ongoing | |||
| basis, if such Contributor fails to notify You of the non-compliance by | |||
| some reasonable means prior to 60 days after You have come back into | |||
| compliance. Moreover, Your grants from a particular Contributor are | |||
| reinstated on an ongoing basis if such Contributor notifies You of the | |||
| non-compliance by some reasonable means, this is the first time You have | |||
| received notice of non-compliance with this License from such | |||
| Contributor, and You become compliant prior to 30 days after Your receipt | |||
| of the notice. | |||
| 5.2. If You initiate litigation against any entity by asserting a patent | |||
| infringement claim (excluding declaratory judgment actions, | |||
| counter-claims, and cross-claims) alleging that a Contributor Version | |||
| directly or indirectly infringes any patent, then the rights granted to | |||
| You by any and all Contributors for the Covered Software under Section | |||
| 2.1 of this License shall terminate. | |||
| 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user | |||
| license agreements (excluding distributors and resellers) which have been | |||
| validly granted by You or Your distributors under this License prior to | |||
| termination shall survive termination. | |||
| 6. Disclaimer of Warranty | |||
| Covered Software is provided under this License on an "as is" basis, | |||
| without warranty of any kind, either expressed, implied, or statutory, | |||
| including, without limitation, warranties that the Covered Software is free | |||
| of defects, merchantable, fit for a particular purpose or non-infringing. | |||
| The entire risk as to the quality and performance of the Covered Software | |||
| is with You. Should any Covered Software prove defective in any respect, | |||
| You (not any Contributor) assume the cost of any necessary servicing, | |||
| repair, or correction. This disclaimer of warranty constitutes an essential | |||
| part of this License. No use of any Covered Software is authorized under | |||
| this License except under this disclaimer. | |||
| 7. Limitation of Liability | |||
| Under no circumstances and under no legal theory, whether tort (including | |||
| negligence), contract, or otherwise, shall any Contributor, or anyone who | |||
| distributes Covered Software as permitted above, be liable to You for any | |||
| direct, indirect, special, incidental, or consequential damages of any | |||
| character including, without limitation, damages for lost profits, loss of | |||
| goodwill, work stoppage, computer failure or malfunction, or any and all | |||
| other commercial damages or losses, even if such party shall have been | |||
| informed of the possibility of such damages. This limitation of liability | |||
| shall not apply to liability for death or personal injury resulting from | |||
| such party's negligence to the extent applicable law prohibits such | |||
| limitation. Some jurisdictions do not allow the exclusion or limitation of | |||
| incidental or consequential damages, so this exclusion and limitation may | |||
| not apply to You. | |||
| 8. Litigation | |||
| Any litigation relating to this License may be brought only in the courts | |||
| of a jurisdiction where the defendant maintains its principal place of | |||
| business and such litigation shall be governed by laws of that | |||
| jurisdiction, without reference to its conflict-of-law provisions. Nothing | |||
| in this Section shall prevent a party's ability to bring cross-claims or | |||
| counter-claims. | |||
| 9. Miscellaneous | |||
| This License represents the complete agreement concerning the subject | |||
| matter hereof. If any provision of this License is held to be | |||
| unenforceable, such provision shall be reformed only to the extent | |||
| necessary to make it enforceable. Any law or regulation which provides that | |||
| the language of a contract shall be construed against the drafter shall not | |||
| be used to construe this License against a Contributor. | |||
| 10. Versions of the License | |||
| 10.1. New Versions | |||
| Mozilla Foundation is the license steward. Except as provided in Section | |||
| 10.3, no one other than the license steward has the right to modify or | |||
| publish new versions of this License. Each version will be given a | |||
| distinguishing version number. | |||
| 10.2. Effect of New Versions | |||
| You may distribute the Covered Software under the terms of the version | |||
| of the License under which You originally received the Covered Software, | |||
| or under the terms of any subsequent version published by the license | |||
| steward. | |||
| 10.3. Modified Versions | |||
| If you create software not governed by this License, and you want to | |||
| create a new license for such software, you may create and use a | |||
| modified version of this License if you rename the license and remove | |||
| any references to the name of the license steward (except to note that | |||
| such modified license differs from this License). | |||
| 10.4. Distributing Source Code Form that is Incompatible With Secondary | |||
| Licenses If You choose to distribute Source Code Form that is | |||
| Incompatible With Secondary Licenses under the terms of this version of | |||
| the License, the notice described in Exhibit B of this License must be | |||
| attached. | |||
| Exhibit A - Source Code Form License Notice | |||
| This Source Code Form is subject to the | |||
| terms of the Mozilla Public License, v. | |||
| 2.0. If a copy of the MPL was not | |||
| distributed with this file, You can | |||
| obtain one at | |||
| http://mozilla.org/MPL/2.0/. | |||
| If it is not possible or desirable to put the notice in a particular file, | |||
| then You may include the notice in a location (such as a LICENSE file in a | |||
| relevant directory) where a recipient would be likely to look for such a | |||
| notice. | |||
| You may add additional accurate notices of copyright ownership. | |||
| Exhibit B - "Incompatible With Secondary Licenses" Notice | |||
| This Source Code Form is "Incompatible | |||
| With Secondary Licenses", as defined by | |||
| the Mozilla Public License, v. 2.0. | |||
| @@ -0,0 +1,30 @@ | |||
| # cleanhttp | |||
| Functions for accessing "clean" Go http.Client values | |||
| ------------- | |||
| The Go standard library contains a default `http.Client` called | |||
| `http.DefaultClient`. It is a common idiom in Go code to start with | |||
| `http.DefaultClient` and tweak it as necessary, and in fact, this is | |||
| encouraged; from the `http` package documentation: | |||
| > The Client's Transport typically has internal state (cached TCP connections), | |||
| so Clients should be reused instead of created as needed. Clients are safe for | |||
| concurrent use by multiple goroutines. | |||
| Unfortunately, this is a shared value, and it is not uncommon for libraries to | |||
| assume that they are free to modify it at will. With enough dependencies, it | |||
| can be very easy to encounter strange problems and race conditions due to | |||
| manipulation of this shared value across libraries and goroutines (clients are | |||
| safe for concurrent use, but writing values to the client struct itself is not | |||
| protected). | |||
| Making things worse is the fact that a bare `http.Client` will use a default | |||
| `http.Transport` called `http.DefaultTransport`, which is another global value | |||
| that behaves the same way. So it is not simply enough to replace | |||
| `http.DefaultClient` with `&http.Client{}`. | |||
| This repository provides some simple functions to get a "clean" `http.Client` | |||
| -- one that uses the same default values as the Go standard library, but | |||
| returns a client that does not share any state with other clients. | |||
| @@ -0,0 +1,57 @@ | |||
| package cleanhttp | |||
| import ( | |||
| "net" | |||
| "net/http" | |||
| "runtime" | |||
| "time" | |||
| ) | |||
| // DefaultTransport returns a new http.Transport with similar default values to | |||
| // http.DefaultTransport, but with idle connections and keepalives disabled. | |||
| func DefaultTransport() *http.Transport { | |||
| transport := DefaultPooledTransport() | |||
| transport.DisableKeepAlives = true | |||
| transport.MaxIdleConnsPerHost = -1 | |||
| return transport | |||
| } | |||
| // DefaultPooledTransport returns a new http.Transport with similar default | |||
| // values to http.DefaultTransport. Do not use this for transient transports as | |||
| // it can leak file descriptors over time. Only use this for transports that | |||
| // will be re-used for the same host(s). | |||
| func DefaultPooledTransport() *http.Transport { | |||
| transport := &http.Transport{ | |||
| Proxy: http.ProxyFromEnvironment, | |||
| DialContext: (&net.Dialer{ | |||
| Timeout: 30 * time.Second, | |||
| KeepAlive: 30 * time.Second, | |||
| DualStack: true, | |||
| }).DialContext, | |||
| MaxIdleConns: 100, | |||
| IdleConnTimeout: 90 * time.Second, | |||
| TLSHandshakeTimeout: 10 * time.Second, | |||
| ExpectContinueTimeout: 1 * time.Second, | |||
| MaxIdleConnsPerHost: runtime.GOMAXPROCS(0) + 1, | |||
| } | |||
| return transport | |||
| } | |||
| // DefaultClient returns a new http.Client with similar default values to | |||
| // http.Client, but with a non-shared Transport, idle connections disabled, and | |||
| // keepalives disabled. | |||
| func DefaultClient() *http.Client { | |||
| return &http.Client{ | |||
| Transport: DefaultTransport(), | |||
| } | |||
| } | |||
| // DefaultPooledClient returns a new http.Client with similar default values to | |||
| // http.Client, but with a shared Transport. Do not use this function for | |||
| // transient clients as it can leak file descriptors over time. Only use this | |||
| // for clients that will be re-used for the same host(s). | |||
| func DefaultPooledClient() *http.Client { | |||
| return &http.Client{ | |||
| Transport: DefaultPooledTransport(), | |||
| } | |||
| } | |||
| @@ -0,0 +1,20 @@ | |||
| // Package cleanhttp offers convenience utilities for acquiring "clean" | |||
| // http.Transport and http.Client structs. | |||
| // | |||
| // Values set on http.DefaultClient and http.DefaultTransport affect all | |||
| // callers. This can have detrimental effects, esepcially in TLS contexts, | |||
| // where client or root certificates set to talk to multiple endpoints can end | |||
| // up displacing each other, leading to hard-to-debug issues. This package | |||
| // provides non-shared http.Client and http.Transport structs to ensure that | |||
| // the configuration will not be overwritten by other parts of the application | |||
| // or dependencies. | |||
| // | |||
| // The DefaultClient and DefaultTransport functions disable idle connections | |||
| // and keepalives. Without ensuring that idle connections are closed before | |||
| // garbage collection, short-term clients/transports can leak file descriptors, | |||
| // eventually leading to "too many open files" errors. If you will be | |||
| // connecting to the same hosts repeatedly from the same client, you can use | |||
| // DefaultPooledClient to receive a client that has connection pooling | |||
| // semantics similar to http.DefaultClient. | |||
| // | |||
| package cleanhttp | |||
| @@ -0,0 +1 @@ | |||
| module github.com/hashicorp/go-cleanhttp | |||
| @@ -0,0 +1,48 @@ | |||
| package cleanhttp | |||
| import ( | |||
| "net/http" | |||
| "strings" | |||
| "unicode" | |||
| ) | |||
| // HandlerInput provides input options to cleanhttp's handlers | |||
| type HandlerInput struct { | |||
| ErrStatus int | |||
| } | |||
| // PrintablePathCheckHandler is a middleware that ensures the request path | |||
| // contains only printable runes. | |||
| func PrintablePathCheckHandler(next http.Handler, input *HandlerInput) http.Handler { | |||
| // Nil-check on input to make it optional | |||
| if input == nil { | |||
| input = &HandlerInput{ | |||
| ErrStatus: http.StatusBadRequest, | |||
| } | |||
| } | |||
| // Default to http.StatusBadRequest on error | |||
| if input.ErrStatus == 0 { | |||
| input.ErrStatus = http.StatusBadRequest | |||
| } | |||
| return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | |||
| if r != nil { | |||
| // Check URL path for non-printable characters | |||
| idx := strings.IndexFunc(r.URL.Path, func(c rune) bool { | |||
| return !unicode.IsPrint(c) | |||
| }) | |||
| if idx != -1 { | |||
| w.WriteHeader(input.ErrStatus) | |||
| return | |||
| } | |||
| if next != nil { | |||
| next.ServeHTTP(w, r) | |||
| } | |||
| } | |||
| return | |||
| }) | |||
| } | |||
| @@ -0,0 +1,4 @@ | |||
| .idea/ | |||
| *.iml | |||
| *.test | |||
| .vscode/ | |||
| @@ -0,0 +1,12 @@ | |||
| sudo: false | |||
| language: go | |||
| go: | |||
| - 1.12.4 | |||
| branches: | |||
| only: | |||
| - master | |||
| script: make updatedeps test | |||
| @@ -0,0 +1,363 @@ | |||
| Mozilla Public License, version 2.0 | |||
| 1. Definitions | |||
| 1.1. "Contributor" | |||
| means each individual or legal entity that creates, contributes to the | |||
| creation of, or owns Covered Software. | |||
| 1.2. "Contributor Version" | |||
| means the combination of the Contributions of others (if any) used by a | |||
| Contributor and that particular Contributor's Contribution. | |||
| 1.3. "Contribution" | |||
| means Covered Software of a particular Contributor. | |||
| 1.4. "Covered Software" | |||
| means Source Code Form to which the initial Contributor has attached the | |||
| notice in Exhibit A, the Executable Form of such Source Code Form, and | |||
| Modifications of such Source Code Form, in each case including portions | |||
| thereof. | |||
| 1.5. "Incompatible With Secondary Licenses" | |||
| means | |||
| a. that the initial Contributor has attached the notice described in | |||
| Exhibit B to the Covered Software; or | |||
| b. that the Covered Software was made available under the terms of | |||
| version 1.1 or earlier of the License, but not also under the terms of | |||
| a Secondary License. | |||
| 1.6. "Executable Form" | |||
| means any form of the work other than Source Code Form. | |||
| 1.7. "Larger Work" | |||
| means a work that combines Covered Software with other material, in a | |||
| separate file or files, that is not Covered Software. | |||
| 1.8. "License" | |||
| means this document. | |||
| 1.9. "Licensable" | |||
| means having the right to grant, to the maximum extent possible, whether | |||
| at the time of the initial grant or subsequently, any and all of the | |||
| rights conveyed by this License. | |||
| 1.10. "Modifications" | |||
| means any of the following: | |||
| a. any file in Source Code Form that results from an addition to, | |||
| deletion from, or modification of the contents of Covered Software; or | |||
| b. any new file in Source Code Form that contains any Covered Software. | |||
| 1.11. "Patent Claims" of a Contributor | |||
| means any patent claim(s), including without limitation, method, | |||
| process, and apparatus claims, in any patent Licensable by such | |||
| Contributor that would be infringed, but for the grant of the License, | |||
| by the making, using, selling, offering for sale, having made, import, | |||
| or transfer of either its Contributions or its Contributor Version. | |||
| 1.12. "Secondary License" | |||
| means either the GNU General Public License, Version 2.0, the GNU Lesser | |||
| General Public License, Version 2.1, the GNU Affero General Public | |||
| License, Version 3.0, or any later versions of those licenses. | |||
| 1.13. "Source Code Form" | |||
| means the form of the work preferred for making modifications. | |||
| 1.14. "You" (or "Your") | |||
| means an individual or a legal entity exercising rights under this | |||
| License. For legal entities, "You" includes any entity that controls, is | |||
| controlled by, or is under common control with You. For purposes of this | |||
| definition, "control" means (a) the power, direct or indirect, to cause | |||
| the direction or management of such entity, whether by contract or | |||
| otherwise, or (b) ownership of more than fifty percent (50%) of the | |||
| outstanding shares or beneficial ownership of such entity. | |||
| 2. License Grants and Conditions | |||
| 2.1. Grants | |||
| Each Contributor hereby grants You a world-wide, royalty-free, | |||
| non-exclusive license: | |||
| a. under intellectual property rights (other than patent or trademark) | |||
| Licensable by such Contributor to use, reproduce, make available, | |||
| modify, display, perform, distribute, and otherwise exploit its | |||
| Contributions, either on an unmodified basis, with Modifications, or | |||
| as part of a Larger Work; and | |||
| b. under Patent Claims of such Contributor to make, use, sell, offer for | |||
| sale, have made, import, and otherwise transfer either its | |||
| Contributions or its Contributor Version. | |||
| 2.2. Effective Date | |||
| The licenses granted in Section 2.1 with respect to any Contribution | |||
| become effective for each Contribution on the date the Contributor first | |||
| distributes such Contribution. | |||
| 2.3. Limitations on Grant Scope | |||
| The licenses granted in this Section 2 are the only rights granted under | |||
| this License. No additional rights or licenses will be implied from the | |||
| distribution or licensing of Covered Software under this License. | |||
| Notwithstanding Section 2.1(b) above, no patent license is granted by a | |||
| Contributor: | |||
| a. for any code that a Contributor has removed from Covered Software; or | |||
| b. for infringements caused by: (i) Your and any other third party's | |||
| modifications of Covered Software, or (ii) the combination of its | |||
| Contributions with other software (except as part of its Contributor | |||
| Version); or | |||
| c. under Patent Claims infringed by Covered Software in the absence of | |||
| its Contributions. | |||
| This License does not grant any rights in the trademarks, service marks, | |||
| or logos of any Contributor (except as may be necessary to comply with | |||
| the notice requirements in Section 3.4). | |||
| 2.4. Subsequent Licenses | |||
| No Contributor makes additional grants as a result of Your choice to | |||
| distribute the Covered Software under a subsequent version of this | |||
| License (see Section 10.2) or under the terms of a Secondary License (if | |||
| permitted under the terms of Section 3.3). | |||
| 2.5. Representation | |||
| Each Contributor represents that the Contributor believes its | |||
| Contributions are its original creation(s) or it has sufficient rights to | |||
| grant the rights to its Contributions conveyed by this License. | |||
| 2.6. Fair Use | |||
| This License is not intended to limit any rights You have under | |||
| applicable copyright doctrines of fair use, fair dealing, or other | |||
| equivalents. | |||
| 2.7. Conditions | |||
| Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in | |||
| Section 2.1. | |||
| 3. Responsibilities | |||
| 3.1. Distribution of Source Form | |||
| All distribution of Covered Software in Source Code Form, including any | |||
| Modifications that You create or to which You contribute, must be under | |||
| the terms of this License. You must inform recipients that the Source | |||
| Code Form of the Covered Software is governed by the terms of this | |||
| License, and how they can obtain a copy of this License. You may not | |||
| attempt to alter or restrict the recipients' rights in the Source Code | |||
| Form. | |||
| 3.2. Distribution of Executable Form | |||
| If You distribute Covered Software in Executable Form then: | |||
| a. such Covered Software must also be made available in Source Code Form, | |||
| as described in Section 3.1, and You must inform recipients of the | |||
| Executable Form how they can obtain a copy of such Source Code Form by | |||
| reasonable means in a timely manner, at a charge no more than the cost | |||
| of distribution to the recipient; and | |||
| b. You may distribute such Executable Form under the terms of this | |||
| License, or sublicense it under different terms, provided that the | |||
| license for the Executable Form does not attempt to limit or alter the | |||
| recipients' rights in the Source Code Form under this License. | |||
| 3.3. Distribution of a Larger Work | |||
| You may create and distribute a Larger Work under terms of Your choice, | |||
| provided that You also comply with the requirements of this License for | |||
| the Covered Software. If the Larger Work is a combination of Covered | |||
| Software with a work governed by one or more Secondary Licenses, and the | |||
| Covered Software is not Incompatible With Secondary Licenses, this | |||
| License permits You to additionally distribute such Covered Software | |||
| under the terms of such Secondary License(s), so that the recipient of | |||
| the Larger Work may, at their option, further distribute the Covered | |||
| Software under the terms of either this License or such Secondary | |||
| License(s). | |||
| 3.4. Notices | |||
| You may not remove or alter the substance of any license notices | |||
| (including copyright notices, patent notices, disclaimers of warranty, or | |||
| limitations of liability) contained within the Source Code Form of the | |||
| Covered Software, except that You may alter any license notices to the | |||
| extent required to remedy known factual inaccuracies. | |||
| 3.5. Application of Additional Terms | |||
| You may choose to offer, and to charge a fee for, warranty, support, | |||
| indemnity or liability obligations to one or more recipients of Covered | |||
| Software. However, You may do so only on Your own behalf, and not on | |||
| behalf of any Contributor. You must make it absolutely clear that any | |||
| such warranty, support, indemnity, or liability obligation is offered by | |||
| You alone, and You hereby agree to indemnify every Contributor for any | |||
| liability incurred by such Contributor as a result of warranty, support, | |||
| indemnity or liability terms You offer. You may include additional | |||
| disclaimers of warranty and limitations of liability specific to any | |||
| jurisdiction. | |||
| 4. Inability to Comply Due to Statute or Regulation | |||
| If it is impossible for You to comply with any of the terms of this License | |||
| with respect to some or all of the Covered Software due to statute, | |||
| judicial order, or regulation then You must: (a) comply with the terms of | |||
| this License to the maximum extent possible; and (b) describe the | |||
| limitations and the code they affect. Such description must be placed in a | |||
| text file included with all distributions of the Covered Software under | |||
| this License. Except to the extent prohibited by statute or regulation, | |||
| such description must be sufficiently detailed for a recipient of ordinary | |||
| skill to be able to understand it. | |||
| 5. Termination | |||
| 5.1. The rights granted under this License will terminate automatically if You | |||
| fail to comply with any of its terms. However, if You become compliant, | |||
| then the rights granted under this License from a particular Contributor | |||
| are reinstated (a) provisionally, unless and until such Contributor | |||
| explicitly and finally terminates Your grants, and (b) on an ongoing | |||
| basis, if such Contributor fails to notify You of the non-compliance by | |||
| some reasonable means prior to 60 days after You have come back into | |||
| compliance. Moreover, Your grants from a particular Contributor are | |||
| reinstated on an ongoing basis if such Contributor notifies You of the | |||
| non-compliance by some reasonable means, this is the first time You have | |||
| received notice of non-compliance with this License from such | |||
| Contributor, and You become compliant prior to 30 days after Your receipt | |||
| of the notice. | |||
| 5.2. If You initiate litigation against any entity by asserting a patent | |||
| infringement claim (excluding declaratory judgment actions, | |||
| counter-claims, and cross-claims) alleging that a Contributor Version | |||
| directly or indirectly infringes any patent, then the rights granted to | |||
| You by any and all Contributors for the Covered Software under Section | |||
| 2.1 of this License shall terminate. | |||
| 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user | |||
| license agreements (excluding distributors and resellers) which have been | |||
| validly granted by You or Your distributors under this License prior to | |||
| termination shall survive termination. | |||
| 6. Disclaimer of Warranty | |||
| Covered Software is provided under this License on an "as is" basis, | |||
| without warranty of any kind, either expressed, implied, or statutory, | |||
| including, without limitation, warranties that the Covered Software is free | |||
| of defects, merchantable, fit for a particular purpose or non-infringing. | |||
| The entire risk as to the quality and performance of the Covered Software | |||
| is with You. Should any Covered Software prove defective in any respect, | |||
| You (not any Contributor) assume the cost of any necessary servicing, | |||
| repair, or correction. This disclaimer of warranty constitutes an essential | |||
| part of this License. No use of any Covered Software is authorized under | |||
| this License except under this disclaimer. | |||
| 7. Limitation of Liability | |||
| Under no circumstances and under no legal theory, whether tort (including | |||
| negligence), contract, or otherwise, shall any Contributor, or anyone who | |||
| distributes Covered Software as permitted above, be liable to You for any | |||
| direct, indirect, special, incidental, or consequential damages of any | |||
| character including, without limitation, damages for lost profits, loss of | |||
| goodwill, work stoppage, computer failure or malfunction, or any and all | |||
| other commercial damages or losses, even if such party shall have been | |||
| informed of the possibility of such damages. This limitation of liability | |||
| shall not apply to liability for death or personal injury resulting from | |||
| such party's negligence to the extent applicable law prohibits such | |||
| limitation. Some jurisdictions do not allow the exclusion or limitation of | |||
| incidental or consequential damages, so this exclusion and limitation may | |||
| not apply to You. | |||
| 8. Litigation | |||
| Any litigation relating to this License may be brought only in the courts | |||
| of a jurisdiction where the defendant maintains its principal place of | |||
| business and such litigation shall be governed by laws of that | |||
| jurisdiction, without reference to its conflict-of-law provisions. Nothing | |||
| in this Section shall prevent a party's ability to bring cross-claims or | |||
| counter-claims. | |||
| 9. Miscellaneous | |||
| This License represents the complete agreement concerning the subject | |||
| matter hereof. If any provision of this License is held to be | |||
| unenforceable, such provision shall be reformed only to the extent | |||
| necessary to make it enforceable. Any law or regulation which provides that | |||
| the language of a contract shall be construed against the drafter shall not | |||
| be used to construe this License against a Contributor. | |||
| 10. Versions of the License | |||
| 10.1. New Versions | |||
| Mozilla Foundation is the license steward. Except as provided in Section | |||
| 10.3, no one other than the license steward has the right to modify or | |||
| publish new versions of this License. Each version will be given a | |||
| distinguishing version number. | |||
| 10.2. Effect of New Versions | |||
| You may distribute the Covered Software under the terms of the version | |||
| of the License under which You originally received the Covered Software, | |||
| or under the terms of any subsequent version published by the license | |||
| steward. | |||
| 10.3. Modified Versions | |||
| If you create software not governed by this License, and you want to | |||
| create a new license for such software, you may create and use a | |||
| modified version of this License if you rename the license and remove | |||
| any references to the name of the license steward (except to note that | |||
| such modified license differs from this License). | |||
| 10.4. Distributing Source Code Form that is Incompatible With Secondary | |||
| Licenses If You choose to distribute Source Code Form that is | |||
| Incompatible With Secondary Licenses under the terms of this version of | |||
| the License, the notice described in Exhibit B of this License must be | |||
| attached. | |||
| Exhibit A - Source Code Form License Notice | |||
| This Source Code Form is subject to the | |||
| terms of the Mozilla Public License, v. | |||
| 2.0. If a copy of the MPL was not | |||
| distributed with this file, You can | |||
| obtain one at | |||
| http://mozilla.org/MPL/2.0/. | |||
| If it is not possible or desirable to put the notice in a particular file, | |||
| then You may include the notice in a location (such as a LICENSE file in a | |||
| relevant directory) where a recipient would be likely to look for such a | |||
| notice. | |||
| You may add additional accurate notices of copyright ownership. | |||
| Exhibit B - "Incompatible With Secondary Licenses" Notice | |||
| This Source Code Form is "Incompatible | |||
| With Secondary Licenses", as defined by | |||
| the Mozilla Public License, v. 2.0. | |||
| @@ -0,0 +1,11 @@ | |||
| default: test | |||
| test: | |||
| go vet ./... | |||
| go test -race ./... | |||
| updatedeps: | |||
| go get -f -t -u ./... | |||
| go get -f -u ./... | |||
| .PHONY: default test updatedeps | |||
| @@ -0,0 +1,61 @@ | |||
| go-retryablehttp | |||
| ================ | |||
| [][travis] | |||
| [][godocs] | |||
| [travis]: http://travis-ci.org/hashicorp/go-retryablehttp | |||
| [godocs]: http://godoc.org/github.com/hashicorp/go-retryablehttp | |||
| The `retryablehttp` package provides a familiar HTTP client interface with | |||
| automatic retries and exponential backoff. It is a thin wrapper over the | |||
| standard `net/http` client library and exposes nearly the same public API. This | |||
| makes `retryablehttp` very easy to drop into existing programs. | |||
| `retryablehttp` performs automatic retries under certain conditions. Mainly, if | |||
| an error is returned by the client (connection errors, etc.), or if a 500-range | |||
| response code is received (except 501), then a retry is invoked after a wait | |||
| period. Otherwise, the response is returned and left to the caller to | |||
| interpret. | |||
| The main difference from `net/http` is that requests which take a request body | |||
| (POST/PUT et. al) can have the body provided in a number of ways (some more or | |||
| less efficient) that allow "rewinding" the request body if the initial request | |||
| fails so that the full request can be attempted again. See the | |||
| [godoc](http://godoc.org/github.com/hashicorp/go-retryablehttp) for more | |||
| details. | |||
| Version 0.6.0 and before are compatible with Go prior to 1.12. From 0.6.1 onward, Go 1.12+ is required. | |||
| Example Use | |||
| =========== | |||
| Using this library should look almost identical to what you would do with | |||
| `net/http`. The most simple example of a GET request is shown below: | |||
| ```go | |||
| resp, err := retryablehttp.Get("/foo") | |||
| if err != nil { | |||
| panic(err) | |||
| } | |||
| ``` | |||
| The returned response object is an `*http.Response`, the same thing you would | |||
| usually get from `net/http`. Had the request failed one or more times, the above | |||
| call would block and retry with exponential backoff. | |||
| ## Getting a stdlib `*http.Client` with retries | |||
| It's possible to convert a `*retryablehttp.Client` directly to a `*http.Client`. | |||
| This makes use of retryablehttp broadly applicable with minimal effort. Simply | |||
| configure a `*retryablehttp.Client` as you wish, and then call `StandardClient()`: | |||
| ```go | |||
| retryClient := retryablehttp.NewClient() | |||
| retryClient.RetryMax = 10 | |||
| standardClient := retryClient.StandardClient() // *http.Client | |||
| ``` | |||
| For more usage and examples see the | |||
| [godoc](http://godoc.org/github.com/hashicorp/go-retryablehttp). | |||
| @@ -0,0 +1,705 @@ | |||
| // Package retryablehttp provides a familiar HTTP client interface with | |||
| // automatic retries and exponential backoff. It is a thin wrapper over the | |||
| // standard net/http client library and exposes nearly the same public API. | |||
| // This makes retryablehttp very easy to drop into existing programs. | |||
| // | |||
| // retryablehttp performs automatic retries under certain conditions. Mainly, if | |||
| // an error is returned by the client (connection errors etc), or if a 500-range | |||
| // response is received, then a retry is invoked. Otherwise, the response is | |||
| // returned and left to the caller to interpret. | |||
| // | |||
| // Requests which take a request body should provide a non-nil function | |||
| // parameter. The best choice is to provide either a function satisfying | |||
| // ReaderFunc which provides multiple io.Readers in an efficient manner, a | |||
| // *bytes.Buffer (the underlying raw byte slice will be used) or a raw byte | |||
| // slice. As it is a reference type, and we will wrap it as needed by readers, | |||
| // we can efficiently re-use the request body without needing to copy it. If an | |||
| // io.Reader (such as a *bytes.Reader) is provided, the full body will be read | |||
| // prior to the first request, and will be efficiently re-used for any retries. | |||
| // ReadSeeker can be used, but some users have observed occasional data races | |||
| // between the net/http library and the Seek functionality of some | |||
| // implementations of ReadSeeker, so should be avoided if possible. | |||
| package retryablehttp | |||
| import ( | |||
| "bytes" | |||
| "context" | |||
| "crypto/x509" | |||
| "fmt" | |||
| "io" | |||
| "io/ioutil" | |||
| "log" | |||
| "math" | |||
| "math/rand" | |||
| "net/http" | |||
| "net/url" | |||
| "os" | |||
| "regexp" | |||
| "strings" | |||
| "sync" | |||
| "time" | |||
| "github.com/hashicorp/go-cleanhttp" | |||
| ) | |||
| var ( | |||
| // Default retry configuration | |||
| defaultRetryWaitMin = 1 * time.Second | |||
| defaultRetryWaitMax = 30 * time.Second | |||
| defaultRetryMax = 4 | |||
| // defaultLogger is the logger provided with defaultClient | |||
| defaultLogger = log.New(os.Stderr, "", log.LstdFlags) | |||
| // defaultClient is used for performing requests without explicitly making | |||
| // a new client. It is purposely private to avoid modifications. | |||
| defaultClient = NewClient() | |||
| // We need to consume response bodies to maintain http connections, but | |||
| // limit the size we consume to respReadLimit. | |||
| respReadLimit = int64(4096) | |||
| // A regular expression to match the error returned by net/http when the | |||
| // configured number of redirects is exhausted. This error isn't typed | |||
| // specifically so we resort to matching on the error string. | |||
| redirectsErrorRe = regexp.MustCompile(`stopped after \d+ redirects\z`) | |||
| // A regular expression to match the error returned by net/http when the | |||
| // scheme specified in the URL is invalid. This error isn't typed | |||
| // specifically so we resort to matching on the error string. | |||
| schemeErrorRe = regexp.MustCompile(`unsupported protocol scheme`) | |||
| ) | |||
| // ReaderFunc is the type of function that can be given natively to NewRequest | |||
| type ReaderFunc func() (io.Reader, error) | |||
| // LenReader is an interface implemented by many in-memory io.Reader's. Used | |||
| // for automatically sending the right Content-Length header when possible. | |||
| type LenReader interface { | |||
| Len() int | |||
| } | |||
| // Request wraps the metadata needed to create HTTP requests. | |||
| type Request struct { | |||
| // body is a seekable reader over the request body payload. This is | |||
| // used to rewind the request data in between retries. | |||
| body ReaderFunc | |||
| // Embed an HTTP request directly. This makes a *Request act exactly | |||
| // like an *http.Request so that all meta methods are supported. | |||
| *http.Request | |||
| } | |||
| // WithContext returns wrapped Request with a shallow copy of underlying *http.Request | |||
| // with its context changed to ctx. The provided ctx must be non-nil. | |||
| func (r *Request) WithContext(ctx context.Context) *Request { | |||
| r.Request = r.Request.WithContext(ctx) | |||
| return r | |||
| } | |||
| // BodyBytes allows accessing the request body. It is an analogue to | |||
| // http.Request's Body variable, but it returns a copy of the underlying data | |||
| // rather than consuming it. | |||
| // | |||
| // This function is not thread-safe; do not call it at the same time as another | |||
| // call, or at the same time this request is being used with Client.Do. | |||
| func (r *Request) BodyBytes() ([]byte, error) { | |||
| if r.body == nil { | |||
| return nil, nil | |||
| } | |||
| body, err := r.body() | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| buf := new(bytes.Buffer) | |||
| _, err = buf.ReadFrom(body) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| return buf.Bytes(), nil | |||
| } | |||
| // SetBody allows setting the request body. | |||
| // | |||
| // It is useful if a new body needs to be set without constructing a new Request. | |||
| func (r *Request) SetBody(rawBody interface{}) error { | |||
| bodyReader, contentLength, err := getBodyReaderAndContentLength(rawBody) | |||
| if err != nil { | |||
| return err | |||
| } | |||
| r.body = bodyReader | |||
| r.ContentLength = contentLength | |||
| return nil | |||
| } | |||
| // WriteTo allows copying the request body into a writer. | |||
| // | |||
| // It writes data to w until there's no more data to write or | |||
| // when an error occurs. The return int64 value is the number of bytes | |||
| // written. Any error encountered during the write is also returned. | |||
| // The signature matches io.WriterTo interface. | |||
| func (r *Request) WriteTo(w io.Writer) (int64, error) { | |||
| body, err := r.body() | |||
| if err != nil { | |||
| return 0, err | |||
| } | |||
| if c, ok := body.(io.Closer); ok { | |||
| defer c.Close() | |||
| } | |||
| return io.Copy(w, body) | |||
| } | |||
| func getBodyReaderAndContentLength(rawBody interface{}) (ReaderFunc, int64, error) { | |||
| var bodyReader ReaderFunc | |||
| var contentLength int64 | |||
| switch body := rawBody.(type) { | |||
| // If they gave us a function already, great! Use it. | |||
| case ReaderFunc: | |||
| bodyReader = body | |||
| tmp, err := body() | |||
| if err != nil { | |||
| return nil, 0, err | |||
| } | |||
| if lr, ok := tmp.(LenReader); ok { | |||
| contentLength = int64(lr.Len()) | |||
| } | |||
| if c, ok := tmp.(io.Closer); ok { | |||
| c.Close() | |||
| } | |||
| case func() (io.Reader, error): | |||
| bodyReader = body | |||
| tmp, err := body() | |||
| if err != nil { | |||
| return nil, 0, err | |||
| } | |||
| if lr, ok := tmp.(LenReader); ok { | |||
| contentLength = int64(lr.Len()) | |||
| } | |||
| if c, ok := tmp.(io.Closer); ok { | |||
| c.Close() | |||
| } | |||
| // If a regular byte slice, we can read it over and over via new | |||
| // readers | |||
| case []byte: | |||
| buf := body | |||
| bodyReader = func() (io.Reader, error) { | |||
| return bytes.NewReader(buf), nil | |||
| } | |||
| contentLength = int64(len(buf)) | |||
| // If a bytes.Buffer we can read the underlying byte slice over and | |||
| // over | |||
| case *bytes.Buffer: | |||
| buf := body | |||
| bodyReader = func() (io.Reader, error) { | |||
| return bytes.NewReader(buf.Bytes()), nil | |||
| } | |||
| contentLength = int64(buf.Len()) | |||
| // We prioritize *bytes.Reader here because we don't really want to | |||
| // deal with it seeking so want it to match here instead of the | |||
| // io.ReadSeeker case. | |||
| case *bytes.Reader: | |||
| buf, err := ioutil.ReadAll(body) | |||
| if err != nil { | |||
| return nil, 0, err | |||
| } | |||
| bodyReader = func() (io.Reader, error) { | |||
| return bytes.NewReader(buf), nil | |||
| } | |||
| contentLength = int64(len(buf)) | |||
| // Compat case | |||
| case io.ReadSeeker: | |||
| raw := body | |||
| bodyReader = func() (io.Reader, error) { | |||
| _, err := raw.Seek(0, 0) | |||
| return ioutil.NopCloser(raw), err | |||
| } | |||
| if lr, ok := raw.(LenReader); ok { | |||
| contentLength = int64(lr.Len()) | |||
| } | |||
| // Read all in so we can reset | |||
| case io.Reader: | |||
| buf, err := ioutil.ReadAll(body) | |||
| if err != nil { | |||
| return nil, 0, err | |||
| } | |||
| bodyReader = func() (io.Reader, error) { | |||
| return bytes.NewReader(buf), nil | |||
| } | |||
| contentLength = int64(len(buf)) | |||
| // No body provided, nothing to do | |||
| case nil: | |||
| // Unrecognized type | |||
| default: | |||
| return nil, 0, fmt.Errorf("cannot handle type %T", rawBody) | |||
| } | |||
| return bodyReader, contentLength, nil | |||
| } | |||
| // FromRequest wraps an http.Request in a retryablehttp.Request | |||
| func FromRequest(r *http.Request) (*Request, error) { | |||
| bodyReader, _, err := getBodyReaderAndContentLength(r.Body) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| // Could assert contentLength == r.ContentLength | |||
| return &Request{bodyReader, r}, nil | |||
| } | |||
| // NewRequest creates a new wrapped request. | |||
| func NewRequest(method, url string, rawBody interface{}) (*Request, error) { | |||
| bodyReader, contentLength, err := getBodyReaderAndContentLength(rawBody) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| httpReq, err := http.NewRequest(method, url, nil) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| httpReq.ContentLength = contentLength | |||
| return &Request{bodyReader, httpReq}, nil | |||
| } | |||
| // Logger interface allows to use other loggers than | |||
| // standard log.Logger. | |||
| type Logger interface { | |||
| Printf(string, ...interface{}) | |||
| } | |||
| // LeveledLogger interface implements the basic methods that a logger library needs | |||
| type LeveledLogger interface { | |||
| Error(string, ...interface{}) | |||
| Info(string, ...interface{}) | |||
| Debug(string, ...interface{}) | |||
| Warn(string, ...interface{}) | |||
| } | |||
| // hookLogger adapts an LeveledLogger to Logger for use by the existing hook functions | |||
| // without changing the API. | |||
| type hookLogger struct { | |||
| LeveledLogger | |||
| } | |||
| func (h hookLogger) Printf(s string, args ...interface{}) { | |||
| h.Info(fmt.Sprintf(s, args...)) | |||
| } | |||
| // RequestLogHook allows a function to run before each retry. The HTTP | |||
| // request which will be made, and the retry number (0 for the initial | |||
| // request) are available to users. The internal logger is exposed to | |||
| // consumers. | |||
| type RequestLogHook func(Logger, *http.Request, int) | |||
| // ResponseLogHook is like RequestLogHook, but allows running a function | |||
| // on each HTTP response. This function will be invoked at the end of | |||
| // every HTTP request executed, regardless of whether a subsequent retry | |||
| // needs to be performed or not. If the response body is read or closed | |||
| // from this method, this will affect the response returned from Do(). | |||
| type ResponseLogHook func(Logger, *http.Response) | |||
| // CheckRetry specifies a policy for handling retries. It is called | |||
| // following each request with the response and error values returned by | |||
| // the http.Client. If CheckRetry returns false, the Client stops retrying | |||
| // and returns the response to the caller. If CheckRetry returns an error, | |||
| // that error value is returned in lieu of the error from the request. The | |||
| // Client will close any response body when retrying, but if the retry is | |||
| // aborted it is up to the CheckRetry callback to properly close any | |||
| // response body before returning. | |||
| type CheckRetry func(ctx context.Context, resp *http.Response, err error) (bool, error) | |||
| // Backoff specifies a policy for how long to wait between retries. | |||
| // It is called after a failing request to determine the amount of time | |||
| // that should pass before trying again. | |||
| type Backoff func(min, max time.Duration, attemptNum int, resp *http.Response) time.Duration | |||
| // ErrorHandler is called if retries are expired, containing the last status | |||
| // from the http library. If not specified, default behavior for the library is | |||
| // to close the body and return an error indicating how many tries were | |||
| // attempted. If overriding this, be sure to close the body if needed. | |||
| type ErrorHandler func(resp *http.Response, err error, numTries int) (*http.Response, error) | |||
| // Client is used to make HTTP requests. It adds additional functionality | |||
| // like automatic retries to tolerate minor outages. | |||
| type Client struct { | |||
| HTTPClient *http.Client // Internal HTTP client. | |||
| Logger interface{} // Customer logger instance. Can be either Logger or LeveledLogger | |||
| RetryWaitMin time.Duration // Minimum time to wait | |||
| RetryWaitMax time.Duration // Maximum time to wait | |||
| RetryMax int // Maximum number of retries | |||
| // RequestLogHook allows a user-supplied function to be called | |||
| // before each retry. | |||
| RequestLogHook RequestLogHook | |||
| // ResponseLogHook allows a user-supplied function to be called | |||
| // with the response from each HTTP request executed. | |||
| ResponseLogHook ResponseLogHook | |||
| // CheckRetry specifies the policy for handling retries, and is called | |||
| // after each request. The default policy is DefaultRetryPolicy. | |||
| CheckRetry CheckRetry | |||
| // Backoff specifies the policy for how long to wait between retries | |||
| Backoff Backoff | |||
| // ErrorHandler specifies the custom error handler to use, if any | |||
| ErrorHandler ErrorHandler | |||
| loggerInit sync.Once | |||
| } | |||
| // NewClient creates a new Client with default settings. | |||
| func NewClient() *Client { | |||
| return &Client{ | |||
| HTTPClient: cleanhttp.DefaultPooledClient(), | |||
| Logger: defaultLogger, | |||
| RetryWaitMin: defaultRetryWaitMin, | |||
| RetryWaitMax: defaultRetryWaitMax, | |||
| RetryMax: defaultRetryMax, | |||
| CheckRetry: DefaultRetryPolicy, | |||
| Backoff: DefaultBackoff, | |||
| } | |||
| } | |||
| func (c *Client) logger() interface{} { | |||
| c.loggerInit.Do(func() { | |||
| if c.Logger == nil { | |||
| return | |||
| } | |||
| switch c.Logger.(type) { | |||
| case Logger, LeveledLogger: | |||
| // ok | |||
| default: | |||
| // This should happen in dev when they are setting Logger and work on code, not in prod. | |||
| panic(fmt.Sprintf("invalid logger type passed, must be Logger or LeveledLogger, was %T", c.Logger)) | |||
| } | |||
| }) | |||
| return c.Logger | |||
| } | |||
| // DefaultRetryPolicy provides a default callback for Client.CheckRetry, which | |||
| // will retry on connection errors and server errors. | |||
| func DefaultRetryPolicy(ctx context.Context, resp *http.Response, err error) (bool, error) { | |||
| // do not retry on context.Canceled or context.DeadlineExceeded | |||
| if ctx.Err() != nil { | |||
| return false, ctx.Err() | |||
| } | |||
| if err != nil { | |||
| if v, ok := err.(*url.Error); ok { | |||
| // Don't retry if the error was due to too many redirects. | |||
| if redirectsErrorRe.MatchString(v.Error()) { | |||
| return false, nil | |||
| } | |||
| // Don't retry if the error was due to an invalid protocol scheme. | |||
| if schemeErrorRe.MatchString(v.Error()) { | |||
| return false, nil | |||
| } | |||
| // Don't retry if the error was due to TLS cert verification failure. | |||
| if _, ok := v.Err.(x509.UnknownAuthorityError); ok { | |||
| return false, nil | |||
| } | |||
| } | |||
| // The error is likely recoverable so retry. | |||
| return true, nil | |||
| } | |||
| // Check the response code. We retry on 500-range responses to allow | |||
| // the server time to recover, as 500's are typically not permanent | |||
| // errors and may relate to outages on the server side. This will catch | |||
| // invalid response codes as well, like 0 and 999. | |||
| if resp.StatusCode == 0 || (resp.StatusCode >= 500 && resp.StatusCode != 501) { | |||
| return true, nil | |||
| } | |||
| return false, nil | |||
| } | |||
| // DefaultBackoff provides a default callback for Client.Backoff which | |||
| // will perform exponential backoff based on the attempt number and limited | |||
| // by the provided minimum and maximum durations. | |||
| func DefaultBackoff(min, max time.Duration, attemptNum int, resp *http.Response) time.Duration { | |||
| mult := math.Pow(2, float64(attemptNum)) * float64(min) | |||
| sleep := time.Duration(mult) | |||
| if float64(sleep) != mult || sleep > max { | |||
| sleep = max | |||
| } | |||
| return sleep | |||
| } | |||
| // LinearJitterBackoff provides a callback for Client.Backoff which will | |||
| // perform linear backoff based on the attempt number and with jitter to | |||
| // prevent a thundering herd. | |||
| // | |||
| // min and max here are *not* absolute values. The number to be multiplied by | |||
| // the attempt number will be chosen at random from between them, thus they are | |||
| // bounding the jitter. | |||
| // | |||
| // For instance: | |||
| // * To get strictly linear backoff of one second increasing each retry, set | |||
| // both to one second (1s, 2s, 3s, 4s, ...) | |||
| // * To get a small amount of jitter centered around one second increasing each | |||
| // retry, set to around one second, such as a min of 800ms and max of 1200ms | |||
| // (892ms, 2102ms, 2945ms, 4312ms, ...) | |||
| // * To get extreme jitter, set to a very wide spread, such as a min of 100ms | |||
| // and a max of 20s (15382ms, 292ms, 51321ms, 35234ms, ...) | |||
| func LinearJitterBackoff(min, max time.Duration, attemptNum int, resp *http.Response) time.Duration { | |||
| // attemptNum always starts at zero but we want to start at 1 for multiplication | |||
| attemptNum++ | |||
| if max <= min { | |||
| // Unclear what to do here, or they are the same, so return min * | |||
| // attemptNum | |||
| return min * time.Duration(attemptNum) | |||
| } | |||
| // Seed rand; doing this every time is fine | |||
| rand := rand.New(rand.NewSource(int64(time.Now().Nanosecond()))) | |||
| // Pick a random number that lies somewhere between the min and max and | |||
| // multiply by the attemptNum. attemptNum starts at zero so we always | |||
| // increment here. We first get a random percentage, then apply that to the | |||
| // difference between min and max, and add to min. | |||
| jitter := rand.Float64() * float64(max-min) | |||
| jitterMin := int64(jitter) + int64(min) | |||
| return time.Duration(jitterMin * int64(attemptNum)) | |||
| } | |||
| // PassthroughErrorHandler is an ErrorHandler that directly passes through the | |||
| // values from the net/http library for the final request. The body is not | |||
| // closed. | |||
| func PassthroughErrorHandler(resp *http.Response, err error, _ int) (*http.Response, error) { | |||
| return resp, err | |||
| } | |||
| // Do wraps calling an HTTP method with retries. | |||
| func (c *Client) Do(req *Request) (*http.Response, error) { | |||
| if c.HTTPClient == nil { | |||
| c.HTTPClient = cleanhttp.DefaultPooledClient() | |||
| } | |||
| logger := c.logger() | |||
| if logger != nil { | |||
| switch v := logger.(type) { | |||
| case Logger: | |||
| v.Printf("[DEBUG] %s %s", req.Method, req.URL) | |||
| case LeveledLogger: | |||
| v.Debug("performing request", "method", req.Method, "url", req.URL) | |||
| } | |||
| } | |||
| var resp *http.Response | |||
| var err error | |||
| for i := 0; ; i++ { | |||
| var code int // HTTP response code | |||
| // Always rewind the request body when non-nil. | |||
| if req.body != nil { | |||
| body, err := req.body() | |||
| if err != nil { | |||
| c.HTTPClient.CloseIdleConnections() | |||
| return resp, err | |||
| } | |||
| if c, ok := body.(io.ReadCloser); ok { | |||
| req.Body = c | |||
| } else { | |||
| req.Body = ioutil.NopCloser(body) | |||
| } | |||
| } | |||
| if c.RequestLogHook != nil { | |||
| switch v := logger.(type) { | |||
| case Logger: | |||
| c.RequestLogHook(v, req.Request, i) | |||
| case LeveledLogger: | |||
| c.RequestLogHook(hookLogger{v}, req.Request, i) | |||
| default: | |||
| c.RequestLogHook(nil, req.Request, i) | |||
| } | |||
| } | |||
| // Attempt the request | |||
| resp, err = c.HTTPClient.Do(req.Request) | |||
| if resp != nil { | |||
| code = resp.StatusCode | |||
| } | |||
| // Check if we should continue with retries. | |||
| checkOK, checkErr := c.CheckRetry(req.Context(), resp, err) | |||
| if err != nil { | |||
| switch v := logger.(type) { | |||
| case Logger: | |||
| v.Printf("[ERR] %s %s request failed: %v", req.Method, req.URL, err) | |||
| case LeveledLogger: | |||
| v.Error("request failed", "error", err, "method", req.Method, "url", req.URL) | |||
| } | |||
| } else { | |||
| // Call this here to maintain the behavior of logging all requests, | |||
| // even if CheckRetry signals to stop. | |||
| if c.ResponseLogHook != nil { | |||
| // Call the response logger function if provided. | |||
| switch v := logger.(type) { | |||
| case Logger: | |||
| c.ResponseLogHook(v, resp) | |||
| case LeveledLogger: | |||
| c.ResponseLogHook(hookLogger{v}, resp) | |||
| default: | |||
| c.ResponseLogHook(nil, resp) | |||
| } | |||
| } | |||
| } | |||
| // Now decide if we should continue. | |||
| if !checkOK { | |||
| if checkErr != nil { | |||
| err = checkErr | |||
| } | |||
| c.HTTPClient.CloseIdleConnections() | |||
| return resp, err | |||
| } | |||
| // We do this before drainBody because there's no need for the I/O if | |||
| // we're breaking out | |||
| remain := c.RetryMax - i | |||
| if remain <= 0 { | |||
| break | |||
| } | |||
| // We're going to retry, consume any response to reuse the connection. | |||
| if err == nil && resp != nil { | |||
| c.drainBody(resp.Body) | |||
| } | |||
| wait := c.Backoff(c.RetryWaitMin, c.RetryWaitMax, i, resp) | |||
| desc := fmt.Sprintf("%s %s", req.Method, req.URL) | |||
| if code > 0 { | |||
| desc = fmt.Sprintf("%s (status: %d)", desc, code) | |||
| } | |||
| if logger != nil { | |||
| switch v := logger.(type) { | |||
| case Logger: | |||
| v.Printf("[DEBUG] %s: retrying in %s (%d left)", desc, wait, remain) | |||
| case LeveledLogger: | |||
| v.Debug("retrying request", "request", desc, "timeout", wait, "remaining", remain) | |||
| } | |||
| } | |||
| select { | |||
| case <-req.Context().Done(): | |||
| c.HTTPClient.CloseIdleConnections() | |||
| return nil, req.Context().Err() | |||
| case <-time.After(wait): | |||
| } | |||
| } | |||
| if c.ErrorHandler != nil { | |||
| c.HTTPClient.CloseIdleConnections() | |||
| return c.ErrorHandler(resp, err, c.RetryMax+1) | |||
| } | |||
| // By default, we close the response body and return an error without | |||
| // returning the response | |||
| if resp != nil { | |||
| resp.Body.Close() | |||
| } | |||
| c.HTTPClient.CloseIdleConnections() | |||
| return nil, fmt.Errorf("%s %s giving up after %d attempts", | |||
| req.Method, req.URL, c.RetryMax+1) | |||
| } | |||
| // Try to read the response body so we can reuse this connection. | |||
| func (c *Client) drainBody(body io.ReadCloser) { | |||
| defer body.Close() | |||
| _, err := io.Copy(ioutil.Discard, io.LimitReader(body, respReadLimit)) | |||
| if err != nil { | |||
| if c.logger() != nil { | |||
| switch v := c.logger().(type) { | |||
| case Logger: | |||
| v.Printf("[ERR] error reading response body: %v", err) | |||
| case LeveledLogger: | |||
| v.Error("error reading response body", "error", err) | |||
| } | |||
| } | |||
| } | |||
| } | |||
| // Get is a shortcut for doing a GET request without making a new client. | |||
| func Get(url string) (*http.Response, error) { | |||
| return defaultClient.Get(url) | |||
| } | |||
| // Get is a convenience helper for doing simple GET requests. | |||
| func (c *Client) Get(url string) (*http.Response, error) { | |||
| req, err := NewRequest("GET", url, nil) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| return c.Do(req) | |||
| } | |||
| // Head is a shortcut for doing a HEAD request without making a new client. | |||
| func Head(url string) (*http.Response, error) { | |||
| return defaultClient.Head(url) | |||
| } | |||
| // Head is a convenience method for doing simple HEAD requests. | |||
| func (c *Client) Head(url string) (*http.Response, error) { | |||
| req, err := NewRequest("HEAD", url, nil) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| return c.Do(req) | |||
| } | |||
| // Post is a shortcut for doing a POST request without making a new client. | |||
| func Post(url, bodyType string, body interface{}) (*http.Response, error) { | |||
| return defaultClient.Post(url, bodyType, body) | |||
| } | |||
| // Post is a convenience method for doing simple POST requests. | |||
| func (c *Client) Post(url, bodyType string, body interface{}) (*http.Response, error) { | |||
| req, err := NewRequest("POST", url, body) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| req.Header.Set("Content-Type", bodyType) | |||
| return c.Do(req) | |||
| } | |||
| // PostForm is a shortcut to perform a POST with form data without creating | |||
| // a new client. | |||
| func PostForm(url string, data url.Values) (*http.Response, error) { | |||
| return defaultClient.PostForm(url, data) | |||
| } | |||
| // PostForm is a convenience method for doing simple POST operations using | |||
| // pre-filled url.Values form data. | |||
| func (c *Client) PostForm(url string, data url.Values) (*http.Response, error) { | |||
| return c.Post(url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode())) | |||
| } | |||
| // StandardClient returns a stdlib *http.Client with a custom Transport, which | |||
| // shims in a *retryablehttp.Client for added retries. | |||
| func (c *Client) StandardClient() *http.Client { | |||
| return &http.Client{ | |||
| Transport: &RoundTripper{Client: c}, | |||
| } | |||
| } | |||
| @@ -0,0 +1,8 @@ | |||
| module github.com/hashicorp/go-retryablehttp | |||
| require ( | |||
| github.com/hashicorp/go-cleanhttp v0.5.1 | |||
| github.com/hashicorp/go-hclog v0.9.2 | |||
| ) | |||
| go 1.13 | |||
| @@ -0,0 +1,10 @@ | |||
| github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | |||
| github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | |||
| github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= | |||
| github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= | |||
| github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI= | |||
| github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= | |||
| 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/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= | |||
| github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= | |||
| @@ -0,0 +1,43 @@ | |||
| package retryablehttp | |||
| import ( | |||
| "net/http" | |||
| "sync" | |||
| ) | |||
| // RoundTripper implements the http.RoundTripper interface, using a retrying | |||
| // HTTP client to execute requests. | |||
| // | |||
| // It is important to note that retryablehttp doesn't always act exactly as a | |||
| // RoundTripper should. This is highly dependent on the retryable client's | |||
| // configuration. | |||
| type RoundTripper struct { | |||
| // The client to use during requests. If nil, the default retryablehttp | |||
| // client and settings will be used. | |||
| Client *Client | |||
| // once ensures that the logic to initialize the default client runs at | |||
| // most once, in a single thread. | |||
| once sync.Once | |||
| } | |||
| // init initializes the underlying retryable client. | |||
| func (rt *RoundTripper) init() { | |||
| if rt.Client == nil { | |||
| rt.Client = NewClient() | |||
| } | |||
| } | |||
| // RoundTrip satisfies the http.RoundTripper interface. | |||
| func (rt *RoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { | |||
| rt.once.Do(rt.init) | |||
| // Convert the request to be retryable. | |||
| retryableReq, err := FromRequest(req) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| // Execute the request. | |||
| return rt.Client.Do(retryableReq) | |||
| } | |||
| @@ -1,10 +1,8 @@ | |||
| language: go | |||
| go: | |||
| - 1.10.x | |||
| - 1.11.x | |||
| - 1.12.x | |||
| - 1.13.x | |||
| - 1.14.x | |||
| - master | |||
| stages: | |||
| @@ -1,4 +1,4 @@ | |||
| Apache License | |||
| Apache License | |||
| Version 2.0, January 2004 | |||
| http://www.apache.org/licenses/ | |||
| @@ -178,7 +178,7 @@ Apache License | |||
| APPENDIX: How to apply the Apache License to your work. | |||
| To apply the Apache License to your work, attach the following | |||
| boilerplate notice, with the fields enclosed by brackets "{}" | |||
| boilerplate notice, with the fields enclosed by brackets "[]" | |||
| replaced with your own identifying information. (Don't include | |||
| the brackets!) The text should be enclosed in the appropriate | |||
| comment syntax for the file format. We also recommend that a | |||
| @@ -186,7 +186,7 @@ Apache License | |||
| same "printed page" as the copyright notice for easier | |||
| identification within third-party archives. | |||
| Copyright {yyyy} {name of copyright owner} | |||
| Copyright [yyyy] [name of copyright owner] | |||
| Licensed under the Apache License, Version 2.0 (the "License"); | |||
| you may not use this file except in compliance with the License. | |||
| @@ -199,4 +199,3 @@ Apache License | |||
| 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. | |||
| @@ -20,6 +20,7 @@ incompatible changes that were needed to fully support the V4 Gitlab API. | |||
| This API client package covers most of the existing Gitlab API calls and is updated regularly | |||
| to add new and/or missing endpoints. Currently the following services are supported: | |||
| - [x] Applications | |||
| - [x] Award Emojis | |||
| - [x] Branches | |||
| - [x] Broadcast Messages | |||
| @@ -96,8 +97,21 @@ access different parts of the GitLab API. For example, to list all | |||
| users: | |||
| ```go | |||
| git := gitlab.NewClient(nil, "yourtokengoeshere") | |||
| //git.SetBaseURL("https://git.mydomain.com/api/v4") | |||
| git, err := gitlab.NewClient("yourtokengoeshere") | |||
| if err != nil { | |||
| log.Fatalf("Failed to create client: %v", err) | |||
| } | |||
| users, _, err := git.Users.ListUsers(&gitlab.ListUsersOptions{}) | |||
| ``` | |||
| There are a few `With...` option functions that can be used to customize | |||
| the API client. For example, to set a custom base URL: | |||
| ```go | |||
| git, err := gitlab.NewClient("yourtokengoeshere", WithBaseURL("https://git.mydomain.com/api/v4")) | |||
| if err != nil { | |||
| log.Fatalf("Failed to create client: %v", err) | |||
| } | |||
| users, _, err := git.Users.ListUsers(&gitlab.ListUsersOptions{}) | |||
| ``` | |||
| @@ -105,7 +119,7 @@ Some API methods have optional parameters that can be passed. For example, | |||
| to list all projects for user "svanharmelen": | |||
| ```go | |||
| git := gitlab.NewClient(nil) | |||
| git := gitlab.NewClient("yourtokengoeshere") | |||
| opt := &ListProjectsOptions{Search: gitlab.String("svanharmelen")} | |||
| projects, _, err := git.Projects.ListProjects(opt) | |||
| ``` | |||
| @@ -125,7 +139,10 @@ import ( | |||
| ) | |||
| func main() { | |||
| git := gitlab.NewClient(nil, "yourtokengoeshere") | |||
| git, err := gitlab.NewClient("yourtokengoeshere") | |||
| if err != nil { | |||
| log.Fatalf("Failed to create client: %v", err) | |||
| } | |||
| // Create new project | |||
| p := &gitlab.CreateProjectOptions{ | |||
| @@ -166,7 +183,7 @@ For complete usage of go-gitlab, see the full [package docs](https://godoc.org/g | |||
| ## Author | |||
| Sander van Harmelen (<sander@xanzy.io>) | |||
| Sander van Harmelen (<sander@vanharmelen.nl>) | |||
| ## License | |||
| @@ -39,7 +39,7 @@ type ListAccessRequestsOptions ListOptions | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/access_requests.html#list-access-requests-for-a-group-or-project | |||
| func (s *AccessRequestsService) ListProjectAccessRequests(pid interface{}, opt *ListAccessRequestsOptions, options ...OptionFunc) ([]*AccessRequest, *Response, error) { | |||
| func (s *AccessRequestsService) ListProjectAccessRequests(pid interface{}, opt *ListAccessRequestsOptions, options ...RequestOptionFunc) ([]*AccessRequest, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -65,7 +65,7 @@ func (s *AccessRequestsService) ListProjectAccessRequests(pid interface{}, opt * | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/access_requests.html#list-access-requests-for-a-group-or-project | |||
| func (s *AccessRequestsService) ListGroupAccessRequests(gid interface{}, opt *ListAccessRequestsOptions, options ...OptionFunc) ([]*AccessRequest, *Response, error) { | |||
| func (s *AccessRequestsService) ListGroupAccessRequests(gid interface{}, opt *ListAccessRequestsOptions, options ...RequestOptionFunc) ([]*AccessRequest, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -91,7 +91,7 @@ func (s *AccessRequestsService) ListGroupAccessRequests(gid interface{}, opt *Li | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/access_requests.html#request-access-to-a-group-or-project | |||
| func (s *AccessRequestsService) RequestProjectAccess(pid interface{}, options ...OptionFunc) (*AccessRequest, *Response, error) { | |||
| func (s *AccessRequestsService) RequestProjectAccess(pid interface{}, options ...RequestOptionFunc) (*AccessRequest, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -117,7 +117,7 @@ func (s *AccessRequestsService) RequestProjectAccess(pid interface{}, options .. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/access_requests.html#request-access-to-a-group-or-project | |||
| func (s *AccessRequestsService) RequestGroupAccess(gid interface{}, options ...OptionFunc) (*AccessRequest, *Response, error) { | |||
| func (s *AccessRequestsService) RequestGroupAccess(gid interface{}, options ...RequestOptionFunc) (*AccessRequest, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -151,7 +151,7 @@ type ApproveAccessRequestOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/access_requests.html#approve-an-access-request | |||
| func (s *AccessRequestsService) ApproveProjectAccessRequest(pid interface{}, user int, opt *ApproveAccessRequestOptions, options ...OptionFunc) (*AccessRequest, *Response, error) { | |||
| func (s *AccessRequestsService) ApproveProjectAccessRequest(pid interface{}, user int, opt *ApproveAccessRequestOptions, options ...RequestOptionFunc) (*AccessRequest, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -176,7 +176,7 @@ func (s *AccessRequestsService) ApproveProjectAccessRequest(pid interface{}, use | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/access_requests.html#approve-an-access-request | |||
| func (s *AccessRequestsService) ApproveGroupAccessRequest(gid interface{}, user int, opt *ApproveAccessRequestOptions, options ...OptionFunc) (*AccessRequest, *Response, error) { | |||
| func (s *AccessRequestsService) ApproveGroupAccessRequest(gid interface{}, user int, opt *ApproveAccessRequestOptions, options ...RequestOptionFunc) (*AccessRequest, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -201,7 +201,7 @@ func (s *AccessRequestsService) ApproveGroupAccessRequest(gid interface{}, user | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/access_requests.html#deny-an-access-request | |||
| func (s *AccessRequestsService) DenyProjectAccessRequest(pid interface{}, user int, options ...OptionFunc) (*Response, error) { | |||
| func (s *AccessRequestsService) DenyProjectAccessRequest(pid interface{}, user int, options ...RequestOptionFunc) (*Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, err | |||
| @@ -220,7 +220,7 @@ func (s *AccessRequestsService) DenyProjectAccessRequest(pid interface{}, user i | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/access_requests.html#deny-an-access-request | |||
| func (s *AccessRequestsService) DenyGroupAccessRequest(gid interface{}, user int, options ...OptionFunc) (*Response, error) { | |||
| func (s *AccessRequestsService) DenyGroupAccessRequest(gid interface{}, user int, options ...RequestOptionFunc) (*Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, err | |||
| @@ -0,0 +1,100 @@ | |||
| // | |||
| // Copyright 2017, Sander van Harmelen | |||
| // | |||
| // 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 gitlab | |||
| import "fmt" | |||
| // ApplicationsService handles communication with administrables applications | |||
| // of the Gitlab API. | |||
| // | |||
| // Gitlab API docs : https://docs.gitlab.com/ee/api/applications.html | |||
| type ApplicationsService struct { | |||
| client *Client | |||
| } | |||
| type Application struct { | |||
| ID int `json:"id"` | |||
| ApplicationID string `json:"application_id"` | |||
| ApplicationName string `json:"application_name"` | |||
| Secret string `json:"secret"` | |||
| CallbackURL string `json:"callback_url"` | |||
| Confidential bool `json:"confidential"` | |||
| } | |||
| // CreateApplicationOptions represents the available CreateApplication() options. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/applications.html#create-an-application | |||
| type CreateApplicationOptions struct { | |||
| Name *string `url:"name,omitempty" json:"name,omitempty"` | |||
| RedirectURI *string `url:"redirect_uri,omitempty" json:"redirect_uri,omitempty"` | |||
| Scopes *string `url:"scopes,omitempty" json:"scopes,omitempty"` | |||
| Confidential *bool `url:"confidential,omitempty" json:"confidential,omitempty"` | |||
| } | |||
| // CreateApplication creates a new application owned by the authenticated user. | |||
| // | |||
| // Gitlab API docs : https://docs.gitlab.com/ce/api/applications.html#create-an-application | |||
| func (s *ApplicationsService) CreateApplication(opt *CreateApplicationOptions, options ...RequestOptionFunc) (*Application, *Response, error) { | |||
| req, err := s.client.NewRequest("POST", "applications", opt, options) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| } | |||
| a := new(Application) | |||
| resp, err := s.client.Do(req, a) | |||
| if err != nil { | |||
| return nil, resp, err | |||
| } | |||
| return a, resp, err | |||
| } | |||
| type ListApplicationsOptions ListOptions | |||
| // ListApplications get a list of administrables applications by the authenticated user | |||
| // | |||
| // Gitlab API docs : https://docs.gitlab.com/ce/api/applications.html#list-all-applications | |||
| func (s *ApplicationsService) ListApplications(opt *ListApplicationsOptions, options ...RequestOptionFunc) ([]*Application, *Response, error) { | |||
| req, err := s.client.NewRequest("GET", "applications", opt, options) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| } | |||
| var as []*Application | |||
| resp, err := s.client.Do(req, &as) | |||
| if err != nil { | |||
| return nil, resp, err | |||
| } | |||
| return as, resp, err | |||
| } | |||
| // DeleteApplication removes a specific application. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/applications.html#delete-an-application | |||
| func (s *ApplicationsService) DeleteApplication(application int, options ...RequestOptionFunc) (*Response, error) { | |||
| u := fmt.Sprintf("applications/%d", application) | |||
| req, err := s.client.NewRequest("DELETE", u, nil, options) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| return s.client.Do(req, nil) | |||
| } | |||
| @@ -66,7 +66,7 @@ type ListAwardEmojiOptions ListOptions | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/award_emoji.html#list-an-awardable-39-s-award-emoji | |||
| func (s *AwardEmojiService) ListMergeRequestAwardEmoji(pid interface{}, mergeRequestIID int, opt *ListAwardEmojiOptions, options ...OptionFunc) ([]*AwardEmoji, *Response, error) { | |||
| func (s *AwardEmojiService) ListMergeRequestAwardEmoji(pid interface{}, mergeRequestIID int, opt *ListAwardEmojiOptions, options ...RequestOptionFunc) ([]*AwardEmoji, *Response, error) { | |||
| return s.listAwardEmoji(pid, awardMergeRequest, mergeRequestIID, opt, options...) | |||
| } | |||
| @@ -74,7 +74,7 @@ func (s *AwardEmojiService) ListMergeRequestAwardEmoji(pid interface{}, mergeReq | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/award_emoji.html#list-an-awardable-39-s-award-emoji | |||
| func (s *AwardEmojiService) ListIssueAwardEmoji(pid interface{}, issueIID int, opt *ListAwardEmojiOptions, options ...OptionFunc) ([]*AwardEmoji, *Response, error) { | |||
| func (s *AwardEmojiService) ListIssueAwardEmoji(pid interface{}, issueIID int, opt *ListAwardEmojiOptions, options ...RequestOptionFunc) ([]*AwardEmoji, *Response, error) { | |||
| return s.listAwardEmoji(pid, awardIssue, issueIID, opt, options...) | |||
| } | |||
| @@ -82,11 +82,11 @@ func (s *AwardEmojiService) ListIssueAwardEmoji(pid interface{}, issueIID int, o | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/award_emoji.html#list-an-awardable-39-s-award-emoji | |||
| func (s *AwardEmojiService) ListSnippetAwardEmoji(pid interface{}, snippetID int, opt *ListAwardEmojiOptions, options ...OptionFunc) ([]*AwardEmoji, *Response, error) { | |||
| func (s *AwardEmojiService) ListSnippetAwardEmoji(pid interface{}, snippetID int, opt *ListAwardEmojiOptions, options ...RequestOptionFunc) ([]*AwardEmoji, *Response, error) { | |||
| return s.listAwardEmoji(pid, awardSnippets, snippetID, opt, options...) | |||
| } | |||
| func (s *AwardEmojiService) listAwardEmoji(pid interface{}, resource string, resourceID int, opt *ListAwardEmojiOptions, options ...OptionFunc) ([]*AwardEmoji, *Response, error) { | |||
| func (s *AwardEmojiService) listAwardEmoji(pid interface{}, resource string, resourceID int, opt *ListAwardEmojiOptions, options ...RequestOptionFunc) ([]*AwardEmoji, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -115,7 +115,7 @@ func (s *AwardEmojiService) listAwardEmoji(pid interface{}, resource string, res | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/award_emoji.html#list-an-awardable-39-s-award-emoji | |||
| func (s *AwardEmojiService) GetMergeRequestAwardEmoji(pid interface{}, mergeRequestIID, awardID int, options ...OptionFunc) (*AwardEmoji, *Response, error) { | |||
| func (s *AwardEmojiService) GetMergeRequestAwardEmoji(pid interface{}, mergeRequestIID, awardID int, options ...RequestOptionFunc) (*AwardEmoji, *Response, error) { | |||
| return s.getAwardEmoji(pid, awardMergeRequest, mergeRequestIID, awardID, options...) | |||
| } | |||
| @@ -123,7 +123,7 @@ func (s *AwardEmojiService) GetMergeRequestAwardEmoji(pid interface{}, mergeRequ | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/award_emoji.html#list-an-awardable-39-s-award-emoji | |||
| func (s *AwardEmojiService) GetIssueAwardEmoji(pid interface{}, issueIID, awardID int, options ...OptionFunc) (*AwardEmoji, *Response, error) { | |||
| func (s *AwardEmojiService) GetIssueAwardEmoji(pid interface{}, issueIID, awardID int, options ...RequestOptionFunc) (*AwardEmoji, *Response, error) { | |||
| return s.getAwardEmoji(pid, awardIssue, issueIID, awardID, options...) | |||
| } | |||
| @@ -131,11 +131,11 @@ func (s *AwardEmojiService) GetIssueAwardEmoji(pid interface{}, issueIID, awardI | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/award_emoji.html#list-an-awardable-39-s-award-emoji | |||
| func (s *AwardEmojiService) GetSnippetAwardEmoji(pid interface{}, snippetID, awardID int, options ...OptionFunc) (*AwardEmoji, *Response, error) { | |||
| func (s *AwardEmojiService) GetSnippetAwardEmoji(pid interface{}, snippetID, awardID int, options ...RequestOptionFunc) (*AwardEmoji, *Response, error) { | |||
| return s.getAwardEmoji(pid, awardSnippets, snippetID, awardID, options...) | |||
| } | |||
| func (s *AwardEmojiService) getAwardEmoji(pid interface{}, resource string, resourceID, awardID int, options ...OptionFunc) (*AwardEmoji, *Response, error) { | |||
| func (s *AwardEmojiService) getAwardEmoji(pid interface{}, resource string, resourceID, awardID int, options ...RequestOptionFunc) (*AwardEmoji, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -174,7 +174,7 @@ type CreateAwardEmojiOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/award_emoji.html#award-a-new-emoji | |||
| func (s *AwardEmojiService) CreateMergeRequestAwardEmoji(pid interface{}, mergeRequestIID int, opt *CreateAwardEmojiOptions, options ...OptionFunc) (*AwardEmoji, *Response, error) { | |||
| func (s *AwardEmojiService) CreateMergeRequestAwardEmoji(pid interface{}, mergeRequestIID int, opt *CreateAwardEmojiOptions, options ...RequestOptionFunc) (*AwardEmoji, *Response, error) { | |||
| return s.createAwardEmoji(pid, awardMergeRequest, mergeRequestIID, opt, options...) | |||
| } | |||
| @@ -182,7 +182,7 @@ func (s *AwardEmojiService) CreateMergeRequestAwardEmoji(pid interface{}, mergeR | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/award_emoji.html#award-a-new-emoji | |||
| func (s *AwardEmojiService) CreateIssueAwardEmoji(pid interface{}, issueIID int, opt *CreateAwardEmojiOptions, options ...OptionFunc) (*AwardEmoji, *Response, error) { | |||
| func (s *AwardEmojiService) CreateIssueAwardEmoji(pid interface{}, issueIID int, opt *CreateAwardEmojiOptions, options ...RequestOptionFunc) (*AwardEmoji, *Response, error) { | |||
| return s.createAwardEmoji(pid, awardIssue, issueIID, opt, options...) | |||
| } | |||
| @@ -190,11 +190,11 @@ func (s *AwardEmojiService) CreateIssueAwardEmoji(pid interface{}, issueIID int, | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/award_emoji.html#award-a-new-emoji | |||
| func (s *AwardEmojiService) CreateSnippetAwardEmoji(pid interface{}, snippetID int, opt *CreateAwardEmojiOptions, options ...OptionFunc) (*AwardEmoji, *Response, error) { | |||
| func (s *AwardEmojiService) CreateSnippetAwardEmoji(pid interface{}, snippetID int, opt *CreateAwardEmojiOptions, options ...RequestOptionFunc) (*AwardEmoji, *Response, error) { | |||
| return s.createAwardEmoji(pid, awardSnippets, snippetID, opt, options...) | |||
| } | |||
| func (s *AwardEmojiService) createAwardEmoji(pid interface{}, resource string, resourceID int, opt *CreateAwardEmojiOptions, options ...OptionFunc) (*AwardEmoji, *Response, error) { | |||
| func (s *AwardEmojiService) createAwardEmoji(pid interface{}, resource string, resourceID int, opt *CreateAwardEmojiOptions, options ...RequestOptionFunc) (*AwardEmoji, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -223,7 +223,7 @@ func (s *AwardEmojiService) createAwardEmoji(pid interface{}, resource string, r | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/award_emoji.html#award-a-new-emoji-on-a-note | |||
| func (s *AwardEmojiService) DeleteIssueAwardEmoji(pid interface{}, issueIID, awardID int, options ...OptionFunc) (*Response, error) { | |||
| func (s *AwardEmojiService) DeleteIssueAwardEmoji(pid interface{}, issueIID, awardID int, options ...RequestOptionFunc) (*Response, error) { | |||
| return s.deleteAwardEmoji(pid, awardMergeRequest, issueIID, awardID, options...) | |||
| } | |||
| @@ -231,7 +231,7 @@ func (s *AwardEmojiService) DeleteIssueAwardEmoji(pid interface{}, issueIID, awa | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/award_emoji.html#award-a-new-emoji-on-a-note | |||
| func (s *AwardEmojiService) DeleteMergeRequestAwardEmoji(pid interface{}, mergeRequestIID, awardID int, options ...OptionFunc) (*Response, error) { | |||
| func (s *AwardEmojiService) DeleteMergeRequestAwardEmoji(pid interface{}, mergeRequestIID, awardID int, options ...RequestOptionFunc) (*Response, error) { | |||
| return s.deleteAwardEmoji(pid, awardMergeRequest, mergeRequestIID, awardID, options...) | |||
| } | |||
| @@ -239,7 +239,7 @@ func (s *AwardEmojiService) DeleteMergeRequestAwardEmoji(pid interface{}, mergeR | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/award_emoji.html#award-a-new-emoji-on-a-note | |||
| func (s *AwardEmojiService) DeleteSnippetAwardEmoji(pid interface{}, snippetID, awardID int, options ...OptionFunc) (*Response, error) { | |||
| func (s *AwardEmojiService) DeleteSnippetAwardEmoji(pid interface{}, snippetID, awardID int, options ...RequestOptionFunc) (*Response, error) { | |||
| return s.deleteAwardEmoji(pid, awardMergeRequest, snippetID, awardID, options...) | |||
| } | |||
| @@ -247,7 +247,7 @@ func (s *AwardEmojiService) DeleteSnippetAwardEmoji(pid interface{}, snippetID, | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/award_emoji.html#delete-an-award-emoji | |||
| func (s *AwardEmojiService) deleteAwardEmoji(pid interface{}, resource string, resourceID, awardID int, options ...OptionFunc) (*Response, error) { | |||
| func (s *AwardEmojiService) deleteAwardEmoji(pid interface{}, resource string, resourceID, awardID int, options ...RequestOptionFunc) (*Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, err | |||
| @@ -267,7 +267,7 @@ func (s *AwardEmojiService) deleteAwardEmoji(pid interface{}, resource string, r | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes | |||
| func (s *AwardEmojiService) ListIssuesAwardEmojiOnNote(pid interface{}, issueID, noteID int, opt *ListAwardEmojiOptions, options ...OptionFunc) ([]*AwardEmoji, *Response, error) { | |||
| func (s *AwardEmojiService) ListIssuesAwardEmojiOnNote(pid interface{}, issueID, noteID int, opt *ListAwardEmojiOptions, options ...RequestOptionFunc) ([]*AwardEmoji, *Response, error) { | |||
| return s.listAwardEmojiOnNote(pid, awardIssue, issueID, noteID, opt, options...) | |||
| } | |||
| @@ -276,7 +276,7 @@ func (s *AwardEmojiService) ListIssuesAwardEmojiOnNote(pid interface{}, issueID, | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes | |||
| func (s *AwardEmojiService) ListMergeRequestAwardEmojiOnNote(pid interface{}, mergeRequestIID, noteID int, opt *ListAwardEmojiOptions, options ...OptionFunc) ([]*AwardEmoji, *Response, error) { | |||
| func (s *AwardEmojiService) ListMergeRequestAwardEmojiOnNote(pid interface{}, mergeRequestIID, noteID int, opt *ListAwardEmojiOptions, options ...RequestOptionFunc) ([]*AwardEmoji, *Response, error) { | |||
| return s.listAwardEmojiOnNote(pid, awardMergeRequest, mergeRequestIID, noteID, opt, options...) | |||
| } | |||
| @@ -285,11 +285,11 @@ func (s *AwardEmojiService) ListMergeRequestAwardEmojiOnNote(pid interface{}, me | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes | |||
| func (s *AwardEmojiService) ListSnippetAwardEmojiOnNote(pid interface{}, snippetIID, noteID int, opt *ListAwardEmojiOptions, options ...OptionFunc) ([]*AwardEmoji, *Response, error) { | |||
| func (s *AwardEmojiService) ListSnippetAwardEmojiOnNote(pid interface{}, snippetIID, noteID int, opt *ListAwardEmojiOptions, options ...RequestOptionFunc) ([]*AwardEmoji, *Response, error) { | |||
| return s.listAwardEmojiOnNote(pid, awardSnippets, snippetIID, noteID, opt, options...) | |||
| } | |||
| func (s *AwardEmojiService) listAwardEmojiOnNote(pid interface{}, resources string, ressourceID, noteID int, opt *ListAwardEmojiOptions, options ...OptionFunc) ([]*AwardEmoji, *Response, error) { | |||
| func (s *AwardEmojiService) listAwardEmojiOnNote(pid interface{}, resources string, ressourceID, noteID int, opt *ListAwardEmojiOptions, options ...RequestOptionFunc) ([]*AwardEmoji, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -315,7 +315,7 @@ func (s *AwardEmojiService) listAwardEmojiOnNote(pid interface{}, resources stri | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes | |||
| func (s *AwardEmojiService) GetIssuesAwardEmojiOnNote(pid interface{}, issueID, noteID, awardID int, options ...OptionFunc) (*AwardEmoji, *Response, error) { | |||
| func (s *AwardEmojiService) GetIssuesAwardEmojiOnNote(pid interface{}, issueID, noteID, awardID int, options ...RequestOptionFunc) (*AwardEmoji, *Response, error) { | |||
| return s.getSingleNoteAwardEmoji(pid, awardIssue, issueID, noteID, awardID, options...) | |||
| } | |||
| @@ -324,7 +324,7 @@ func (s *AwardEmojiService) GetIssuesAwardEmojiOnNote(pid interface{}, issueID, | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes | |||
| func (s *AwardEmojiService) GetMergeRequestAwardEmojiOnNote(pid interface{}, mergeRequestIID, noteID, awardID int, options ...OptionFunc) (*AwardEmoji, *Response, error) { | |||
| func (s *AwardEmojiService) GetMergeRequestAwardEmojiOnNote(pid interface{}, mergeRequestIID, noteID, awardID int, options ...RequestOptionFunc) (*AwardEmoji, *Response, error) { | |||
| return s.getSingleNoteAwardEmoji(pid, awardMergeRequest, mergeRequestIID, noteID, awardID, | |||
| options...) | |||
| } | |||
| @@ -333,11 +333,11 @@ func (s *AwardEmojiService) GetMergeRequestAwardEmojiOnNote(pid interface{}, mer | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes | |||
| func (s *AwardEmojiService) GetSnippetAwardEmojiOnNote(pid interface{}, snippetIID, noteID, awardID int, options ...OptionFunc) (*AwardEmoji, *Response, error) { | |||
| func (s *AwardEmojiService) GetSnippetAwardEmojiOnNote(pid interface{}, snippetIID, noteID, awardID int, options ...RequestOptionFunc) (*AwardEmoji, *Response, error) { | |||
| return s.getSingleNoteAwardEmoji(pid, awardSnippets, snippetIID, noteID, awardID, options...) | |||
| } | |||
| func (s *AwardEmojiService) getSingleNoteAwardEmoji(pid interface{}, ressource string, resourceID, noteID, awardID int, options ...OptionFunc) (*AwardEmoji, *Response, error) { | |||
| func (s *AwardEmojiService) getSingleNoteAwardEmoji(pid interface{}, ressource string, resourceID, noteID, awardID int, options ...RequestOptionFunc) (*AwardEmoji, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -368,7 +368,7 @@ func (s *AwardEmojiService) getSingleNoteAwardEmoji(pid interface{}, ressource s | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes | |||
| func (s *AwardEmojiService) CreateIssuesAwardEmojiOnNote(pid interface{}, issueID, noteID int, opt *CreateAwardEmojiOptions, options ...OptionFunc) (*AwardEmoji, *Response, error) { | |||
| func (s *AwardEmojiService) CreateIssuesAwardEmojiOnNote(pid interface{}, issueID, noteID int, opt *CreateAwardEmojiOptions, options ...RequestOptionFunc) (*AwardEmoji, *Response, error) { | |||
| return s.createAwardEmojiOnNote(pid, awardIssue, issueID, noteID, opt, options...) | |||
| } | |||
| @@ -377,7 +377,7 @@ func (s *AwardEmojiService) CreateIssuesAwardEmojiOnNote(pid interface{}, issueI | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes | |||
| func (s *AwardEmojiService) CreateMergeRequestAwardEmojiOnNote(pid interface{}, mergeRequestIID, noteID int, opt *CreateAwardEmojiOptions, options ...OptionFunc) (*AwardEmoji, *Response, error) { | |||
| func (s *AwardEmojiService) CreateMergeRequestAwardEmojiOnNote(pid interface{}, mergeRequestIID, noteID int, opt *CreateAwardEmojiOptions, options ...RequestOptionFunc) (*AwardEmoji, *Response, error) { | |||
| return s.createAwardEmojiOnNote(pid, awardMergeRequest, mergeRequestIID, noteID, opt, options...) | |||
| } | |||
| @@ -385,7 +385,7 @@ func (s *AwardEmojiService) CreateMergeRequestAwardEmojiOnNote(pid interface{}, | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes | |||
| func (s *AwardEmojiService) CreateSnippetAwardEmojiOnNote(pid interface{}, snippetIID, noteID int, opt *CreateAwardEmojiOptions, options ...OptionFunc) (*AwardEmoji, *Response, error) { | |||
| func (s *AwardEmojiService) CreateSnippetAwardEmojiOnNote(pid interface{}, snippetIID, noteID int, opt *CreateAwardEmojiOptions, options ...RequestOptionFunc) (*AwardEmoji, *Response, error) { | |||
| return s.createAwardEmojiOnNote(pid, awardSnippets, snippetIID, noteID, opt, options...) | |||
| } | |||
| @@ -393,7 +393,7 @@ func (s *AwardEmojiService) CreateSnippetAwardEmojiOnNote(pid interface{}, snipp | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/award_emoji.html#award-a-new-emoji-on-a-note | |||
| func (s *AwardEmojiService) createAwardEmojiOnNote(pid interface{}, resource string, resourceID, noteID int, opt *CreateAwardEmojiOptions, options ...OptionFunc) (*AwardEmoji, *Response, error) { | |||
| func (s *AwardEmojiService) createAwardEmojiOnNote(pid interface{}, resource string, resourceID, noteID int, opt *CreateAwardEmojiOptions, options ...RequestOptionFunc) (*AwardEmoji, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -423,7 +423,7 @@ func (s *AwardEmojiService) createAwardEmojiOnNote(pid interface{}, resource str | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes | |||
| func (s *AwardEmojiService) DeleteIssuesAwardEmojiOnNote(pid interface{}, issueID, noteID, awardID int, options ...OptionFunc) (*Response, error) { | |||
| func (s *AwardEmojiService) DeleteIssuesAwardEmojiOnNote(pid interface{}, issueID, noteID, awardID int, options ...RequestOptionFunc) (*Response, error) { | |||
| return s.deleteAwardEmojiOnNote(pid, awardIssue, issueID, noteID, awardID, options...) | |||
| } | |||
| @@ -432,7 +432,7 @@ func (s *AwardEmojiService) DeleteIssuesAwardEmojiOnNote(pid interface{}, issueI | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes | |||
| func (s *AwardEmojiService) DeleteMergeRequestAwardEmojiOnNote(pid interface{}, mergeRequestIID, noteID, awardID int, options ...OptionFunc) (*Response, error) { | |||
| func (s *AwardEmojiService) DeleteMergeRequestAwardEmojiOnNote(pid interface{}, mergeRequestIID, noteID, awardID int, options ...RequestOptionFunc) (*Response, error) { | |||
| return s.deleteAwardEmojiOnNote(pid, awardMergeRequest, mergeRequestIID, noteID, awardID, | |||
| options...) | |||
| } | |||
| @@ -441,11 +441,11 @@ func (s *AwardEmojiService) DeleteMergeRequestAwardEmojiOnNote(pid interface{}, | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/award_emoji.html#award-emoji-on-notes | |||
| func (s *AwardEmojiService) DeleteSnippetAwardEmojiOnNote(pid interface{}, snippetIID, noteID, awardID int, options ...OptionFunc) (*Response, error) { | |||
| func (s *AwardEmojiService) DeleteSnippetAwardEmojiOnNote(pid interface{}, snippetIID, noteID, awardID int, options ...RequestOptionFunc) (*Response, error) { | |||
| return s.deleteAwardEmojiOnNote(pid, awardSnippets, snippetIID, noteID, awardID, options...) | |||
| } | |||
| func (s *AwardEmojiService) deleteAwardEmojiOnNote(pid interface{}, resource string, resourceID, noteID, awardID int, options ...OptionFunc) (*Response, error) { | |||
| func (s *AwardEmojiService) deleteAwardEmojiOnNote(pid interface{}, resource string, resourceID, noteID, awardID int, options ...RequestOptionFunc) (*Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, err | |||
| @@ -66,7 +66,7 @@ type CreateIssueBoardOptions struct { | |||
| // CreateIssueBoard creates a new issue board. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ee/api/boards.html#create-a-board-starter | |||
| func (s *IssueBoardsService) CreateIssueBoard(pid interface{}, opt *CreateIssueBoardOptions, options ...OptionFunc) (*IssueBoard, *Response, error) { | |||
| func (s *IssueBoardsService) CreateIssueBoard(pid interface{}, opt *CreateIssueBoardOptions, options ...RequestOptionFunc) (*IssueBoard, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -101,7 +101,7 @@ type UpdateIssueBoardOptions struct { | |||
| // UpdateIssueBoard update an issue board. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ee/api/boards.html#create-a-board-starter | |||
| func (s *IssueBoardsService) UpdateIssueBoard(pid interface{}, board int, opt *UpdateIssueBoardOptions, options ...OptionFunc) (*IssueBoard, *Response, error) { | |||
| func (s *IssueBoardsService) UpdateIssueBoard(pid interface{}, board int, opt *UpdateIssueBoardOptions, options ...RequestOptionFunc) (*IssueBoard, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -125,7 +125,7 @@ func (s *IssueBoardsService) UpdateIssueBoard(pid interface{}, board int, opt *U | |||
| // DeleteIssueBoard deletes an issue board. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ee/api/boards.html#delete-a-board-starter | |||
| func (s *IssueBoardsService) DeleteIssueBoard(pid interface{}, board int, options ...OptionFunc) (*Response, error) { | |||
| func (s *IssueBoardsService) DeleteIssueBoard(pid interface{}, board int, options ...RequestOptionFunc) (*Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, err | |||
| @@ -148,7 +148,7 @@ type ListIssueBoardsOptions ListOptions | |||
| // ListIssueBoards gets a list of all issue boards in a project. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/boards.html#project-board | |||
| func (s *IssueBoardsService) ListIssueBoards(pid interface{}, opt *ListIssueBoardsOptions, options ...OptionFunc) ([]*IssueBoard, *Response, error) { | |||
| func (s *IssueBoardsService) ListIssueBoards(pid interface{}, opt *ListIssueBoardsOptions, options ...RequestOptionFunc) ([]*IssueBoard, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -172,7 +172,7 @@ func (s *IssueBoardsService) ListIssueBoards(pid interface{}, opt *ListIssueBoar | |||
| // GetIssueBoard gets a single issue board of a project. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/boards.html#single-board | |||
| func (s *IssueBoardsService) GetIssueBoard(pid interface{}, board int, options ...OptionFunc) (*IssueBoard, *Response, error) { | |||
| func (s *IssueBoardsService) GetIssueBoard(pid interface{}, board int, options ...RequestOptionFunc) (*IssueBoard, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -202,7 +202,7 @@ type GetIssueBoardListsOptions ListOptions | |||
| // backlog and closed lists. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/boards.html#list-board-lists | |||
| func (s *IssueBoardsService) GetIssueBoardLists(pid interface{}, board int, opt *GetIssueBoardListsOptions, options ...OptionFunc) ([]*BoardList, *Response, error) { | |||
| func (s *IssueBoardsService) GetIssueBoardLists(pid interface{}, board int, opt *GetIssueBoardListsOptions, options ...RequestOptionFunc) ([]*BoardList, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -226,7 +226,7 @@ func (s *IssueBoardsService) GetIssueBoardLists(pid interface{}, board int, opt | |||
| // GetIssueBoardList gets a single issue board list. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/boards.html#single-board-list | |||
| func (s *IssueBoardsService) GetIssueBoardList(pid interface{}, board, list int, options ...OptionFunc) (*BoardList, *Response, error) { | |||
| func (s *IssueBoardsService) GetIssueBoardList(pid interface{}, board, list int, options ...RequestOptionFunc) (*BoardList, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -262,7 +262,7 @@ type CreateIssueBoardListOptions struct { | |||
| // CreateIssueBoardList creates a new issue board list. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/boards.html#new-board-list | |||
| func (s *IssueBoardsService) CreateIssueBoardList(pid interface{}, board int, opt *CreateIssueBoardListOptions, options ...OptionFunc) (*BoardList, *Response, error) { | |||
| func (s *IssueBoardsService) CreateIssueBoardList(pid interface{}, board int, opt *CreateIssueBoardListOptions, options ...RequestOptionFunc) (*BoardList, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -294,7 +294,7 @@ type UpdateIssueBoardListOptions struct { | |||
| // UpdateIssueBoardList updates the position of an existing issue board list. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/boards.html#edit-board-list | |||
| func (s *IssueBoardsService) UpdateIssueBoardList(pid interface{}, board, list int, opt *UpdateIssueBoardListOptions, options ...OptionFunc) (*BoardList, *Response, error) { | |||
| func (s *IssueBoardsService) UpdateIssueBoardList(pid interface{}, board, list int, opt *UpdateIssueBoardListOptions, options ...RequestOptionFunc) (*BoardList, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -324,7 +324,7 @@ func (s *IssueBoardsService) UpdateIssueBoardList(pid interface{}, board, list i | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/boards.html#delete-a-board-list | |||
| func (s *IssueBoardsService) DeleteIssueBoardList(pid interface{}, board, list int, options ...OptionFunc) (*Response, error) { | |||
| func (s *IssueBoardsService) DeleteIssueBoardList(pid interface{}, board, list int, options ...RequestOptionFunc) (*Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, err | |||
| @@ -60,7 +60,7 @@ type ListBranchesOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/branches.html#list-repository-branches | |||
| func (s *BranchesService) ListBranches(pid interface{}, opts *ListBranchesOptions, options ...OptionFunc) ([]*Branch, *Response, error) { | |||
| func (s *BranchesService) ListBranches(pid interface{}, opts *ListBranchesOptions, options ...RequestOptionFunc) ([]*Branch, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -85,7 +85,7 @@ func (s *BranchesService) ListBranches(pid interface{}, opts *ListBranchesOption | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/branches.html#get-single-repository-branch | |||
| func (s *BranchesService) GetBranch(pid interface{}, branch string, options ...OptionFunc) (*Branch, *Response, error) { | |||
| func (s *BranchesService) GetBranch(pid interface{}, branch string, options ...RequestOptionFunc) (*Branch, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -121,7 +121,7 @@ type ProtectBranchOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/branches.html#protect-repository-branch | |||
| func (s *BranchesService) ProtectBranch(pid interface{}, branch string, opts *ProtectBranchOptions, options ...OptionFunc) (*Branch, *Response, error) { | |||
| func (s *BranchesService) ProtectBranch(pid interface{}, branch string, opts *ProtectBranchOptions, options ...RequestOptionFunc) (*Branch, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -148,7 +148,7 @@ func (s *BranchesService) ProtectBranch(pid interface{}, branch string, opts *Pr | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/branches.html#unprotect-repository-branch | |||
| func (s *BranchesService) UnprotectBranch(pid interface{}, branch string, options ...OptionFunc) (*Branch, *Response, error) { | |||
| func (s *BranchesService) UnprotectBranch(pid interface{}, branch string, options ...RequestOptionFunc) (*Branch, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -182,7 +182,7 @@ type CreateBranchOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/branches.html#create-repository-branch | |||
| func (s *BranchesService) CreateBranch(pid interface{}, opt *CreateBranchOptions, options ...OptionFunc) (*Branch, *Response, error) { | |||
| func (s *BranchesService) CreateBranch(pid interface{}, opt *CreateBranchOptions, options ...RequestOptionFunc) (*Branch, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -207,7 +207,7 @@ func (s *BranchesService) CreateBranch(pid interface{}, opt *CreateBranchOptions | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/branches.html#delete-repository-branch | |||
| func (s *BranchesService) DeleteBranch(pid interface{}, branch string, options ...OptionFunc) (*Response, error) { | |||
| func (s *BranchesService) DeleteBranch(pid interface{}, branch string, options ...RequestOptionFunc) (*Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, err | |||
| @@ -226,7 +226,7 @@ func (s *BranchesService) DeleteBranch(pid interface{}, branch string, options . | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/branches.html#delete-merged-branches | |||
| func (s *BranchesService) DeleteMergedBranches(pid interface{}, options ...OptionFunc) (*Response, error) { | |||
| func (s *BranchesService) DeleteMergedBranches(pid interface{}, options ...RequestOptionFunc) (*Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, err | |||
| @@ -54,7 +54,7 @@ type ListBroadcastMessagesOptions ListOptions | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/broadcast_messages.html#get-all-broadcast-messages | |||
| func (s *BroadcastMessagesService) ListBroadcastMessages(opt *ListBroadcastMessagesOptions, options ...OptionFunc) ([]*BroadcastMessage, *Response, error) { | |||
| func (s *BroadcastMessagesService) ListBroadcastMessages(opt *ListBroadcastMessagesOptions, options ...RequestOptionFunc) ([]*BroadcastMessage, *Response, error) { | |||
| req, err := s.client.NewRequest("GET", "broadcast_messages", opt, options) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -73,7 +73,7 @@ func (s *BroadcastMessagesService) ListBroadcastMessages(opt *ListBroadcastMessa | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/broadcast_messages.html#get-a-specific-broadcast-message | |||
| func (s *BroadcastMessagesService) GetBroadcastMessage(broadcast int, options ...OptionFunc) (*BroadcastMessage, *Response, error) { | |||
| func (s *BroadcastMessagesService) GetBroadcastMessage(broadcast int, options ...RequestOptionFunc) (*BroadcastMessage, *Response, error) { | |||
| u := fmt.Sprintf("broadcast_messages/%d", broadcast) | |||
| req, err := s.client.NewRequest("GET", u, nil, options) | |||
| @@ -107,7 +107,7 @@ type CreateBroadcastMessageOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/broadcast_messages.html#create-a-broadcast-message | |||
| func (s *BroadcastMessagesService) CreateBroadcastMessage(opt *CreateBroadcastMessageOptions, options ...OptionFunc) (*BroadcastMessage, *Response, error) { | |||
| func (s *BroadcastMessagesService) CreateBroadcastMessage(opt *CreateBroadcastMessageOptions, options ...RequestOptionFunc) (*BroadcastMessage, *Response, error) { | |||
| req, err := s.client.NewRequest("POST", "broadcast_messages", opt, options) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -139,7 +139,7 @@ type UpdateBroadcastMessageOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/broadcast_messages.html#update-a-broadcast-message | |||
| func (s *BroadcastMessagesService) UpdateBroadcastMessage(broadcast int, opt *UpdateBroadcastMessageOptions, options ...OptionFunc) (*BroadcastMessage, *Response, error) { | |||
| func (s *BroadcastMessagesService) UpdateBroadcastMessage(broadcast int, opt *UpdateBroadcastMessageOptions, options ...RequestOptionFunc) (*BroadcastMessage, *Response, error) { | |||
| u := fmt.Sprintf("broadcast_messages/%d", broadcast) | |||
| req, err := s.client.NewRequest("PUT", u, opt, options) | |||
| @@ -160,7 +160,7 @@ func (s *BroadcastMessagesService) UpdateBroadcastMessage(broadcast int, opt *Up | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/broadcast_messages.html#delete-a-broadcast-message | |||
| func (s *BroadcastMessagesService) DeleteBroadcastMessage(broadcast int, options ...OptionFunc) (*Response, error) { | |||
| func (s *BroadcastMessagesService) DeleteBroadcastMessage(broadcast int, options ...RequestOptionFunc) (*Response, error) { | |||
| u := fmt.Sprintf("broadcast_messages/%d", broadcast) | |||
| req, err := s.client.NewRequest("DELETE", u, nil, options) | |||
| @@ -32,7 +32,7 @@ type ListCIYMLTemplatesOptions ListOptions | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/templates/gitlab_ci_ymls.html#list-gitlab-ci-yml-templates | |||
| func (s *CIYMLTemplatesService) ListAllTemplates(opt *ListCIYMLTemplatesOptions, options ...OptionFunc) ([]*CIYMLTemplate, *Response, error) { | |||
| func (s *CIYMLTemplatesService) ListAllTemplates(opt *ListCIYMLTemplatesOptions, options ...RequestOptionFunc) ([]*CIYMLTemplate, *Response, error) { | |||
| req, err := s.client.NewRequest("GET", "templates/gitlab_ci_ymls", opt, options) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -51,7 +51,7 @@ func (s *CIYMLTemplatesService) ListAllTemplates(opt *ListCIYMLTemplatesOptions, | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/templates/gitlab_ci_ymls.html#single-gitlab-ci-yml-template | |||
| func (s *CIYMLTemplatesService) GetTemplate(key string, options ...OptionFunc) (*CIYMLTemplate, *Response, error) { | |||
| func (s *CIYMLTemplatesService) GetTemplate(key string, options ...RequestOptionFunc) (*CIYMLTemplate, *Response, error) { | |||
| u := fmt.Sprintf("templates/gitlab_ci_ymls/%s", pathEscape(key)) | |||
| req, err := s.client.NewRequest("GET", u, nil, options) | |||
| @@ -0,0 +1,49 @@ | |||
| package gitlab | |||
| import ( | |||
| "net/http" | |||
| retryablehttp "github.com/hashicorp/go-retryablehttp" | |||
| ) | |||
| // ClientOptionFunc can be used customize a new GitLab API client. | |||
| type ClientOptionFunc func(*Client) error | |||
| // WithBaseURL sets the base URL for API requests to a custom endpoint. | |||
| func WithBaseURL(urlStr string) ClientOptionFunc { | |||
| return func(c *Client) error { | |||
| return c.setBaseURL(urlStr) | |||
| } | |||
| } | |||
| // WithCustomBackoff can be used to configure a custom backoff policy. | |||
| func WithCustomBackoff(backoff retryablehttp.Backoff) ClientOptionFunc { | |||
| return func(c *Client) error { | |||
| c.client.Backoff = backoff | |||
| return nil | |||
| } | |||
| } | |||
| // WithCustomRetry can be used to configure a custom retry policy. | |||
| func WithCustomRetry(checkRetry retryablehttp.CheckRetry) ClientOptionFunc { | |||
| return func(c *Client) error { | |||
| c.client.CheckRetry = checkRetry | |||
| return nil | |||
| } | |||
| } | |||
| // WithHTTPClient can be used to configure a custom HTTP client. | |||
| func WithHTTPClient(httpClient *http.Client) ClientOptionFunc { | |||
| return func(c *Client) error { | |||
| c.client.HTTPClient = httpClient | |||
| return nil | |||
| } | |||
| } | |||
| // WithoutRetries disables the default retry logic. | |||
| func WithoutRetries() ClientOptionFunc { | |||
| return func(c *Client) error { | |||
| c.disableRetries = true | |||
| return nil | |||
| } | |||
| } | |||
| @@ -70,18 +70,19 @@ func (c Commit) String() string { | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#list-repository-commits | |||
| type ListCommitsOptions struct { | |||
| ListOptions | |||
| RefName *string `url:"ref_name,omitempty" json:"ref_name,omitempty"` | |||
| Since *time.Time `url:"since,omitempty" json:"since,omitempty"` | |||
| Until *time.Time `url:"until,omitempty" json:"until,omitempty"` | |||
| Path *string `url:"path,omitempty" json:"path,omitempty"` | |||
| All *bool `url:"all,omitempty" json:"all,omitempty"` | |||
| WithStats *bool `url:"with_stats,omitempty" json:"with_stats,omitempty"` | |||
| RefName *string `url:"ref_name,omitempty" json:"ref_name,omitempty"` | |||
| Since *time.Time `url:"since,omitempty" json:"since,omitempty"` | |||
| Until *time.Time `url:"until,omitempty" json:"until,omitempty"` | |||
| Path *string `url:"path,omitempty" json:"path,omitempty"` | |||
| All *bool `url:"all,omitempty" json:"all,omitempty"` | |||
| WithStats *bool `url:"with_stats,omitempty" json:"with_stats,omitempty"` | |||
| FirstParent *bool `url:"first_parent,omitempty" json:"first_parent,omitempty"` | |||
| } | |||
| // ListCommits gets a list of repository commits in a project. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#list-commits | |||
| func (s *CommitsService) ListCommits(pid interface{}, opt *ListCommitsOptions, options ...OptionFunc) ([]*Commit, *Response, error) { | |||
| func (s *CommitsService) ListCommits(pid interface{}, opt *ListCommitsOptions, options ...RequestOptionFunc) ([]*Commit, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -146,7 +147,7 @@ type GetCommitRefsOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/commits.html#get-references-a-commit-is-pushed-to | |||
| func (s *CommitsService) GetCommitRefs(pid interface{}, sha string, opt *GetCommitRefsOptions, options ...OptionFunc) ([]*CommitRef, *Response, error) { | |||
| func (s *CommitsService) GetCommitRefs(pid interface{}, sha string, opt *GetCommitRefsOptions, options ...RequestOptionFunc) ([]*CommitRef, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -171,7 +172,7 @@ func (s *CommitsService) GetCommitRefs(pid interface{}, sha string, opt *GetComm | |||
| // branch or tag. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#get-a-single-commit | |||
| func (s *CommitsService) GetCommit(pid interface{}, sha string, options ...OptionFunc) (*Commit, *Response, error) { | |||
| func (s *CommitsService) GetCommit(pid interface{}, sha string, options ...RequestOptionFunc) (*Commit, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -214,7 +215,7 @@ type CreateCommitOptions struct { | |||
| // CreateCommit creates a commit with multiple files and actions. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#create-a-commit-with-multiple-files-and-actions | |||
| func (s *CommitsService) CreateCommit(pid interface{}, opt *CreateCommitOptions, options ...OptionFunc) (*Commit, *Response, error) { | |||
| func (s *CommitsService) CreateCommit(pid interface{}, opt *CreateCommitOptions, options ...RequestOptionFunc) (*Commit, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -263,7 +264,7 @@ type GetCommitDiffOptions ListOptions | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/commits.html#get-the-diff-of-a-commit | |||
| func (s *CommitsService) GetCommitDiff(pid interface{}, sha string, opt *GetCommitDiffOptions, options ...OptionFunc) ([]*Diff, *Response, error) { | |||
| func (s *CommitsService) GetCommitDiff(pid interface{}, sha string, opt *GetCommitDiffOptions, options ...RequestOptionFunc) ([]*Diff, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -320,7 +321,7 @@ type GetCommitCommentsOptions ListOptions | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/commits.html#get-the-comments-of-a-commit | |||
| func (s *CommitsService) GetCommitComments(pid interface{}, sha string, opt *GetCommitCommentsOptions, options ...OptionFunc) ([]*CommitComment, *Response, error) { | |||
| func (s *CommitsService) GetCommitComments(pid interface{}, sha string, opt *GetCommitCommentsOptions, options ...RequestOptionFunc) ([]*CommitComment, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -359,7 +360,7 @@ type PostCommitCommentOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/commits.html#post-comment-to-commit | |||
| func (s *CommitsService) PostCommitComment(pid interface{}, sha string, opt *PostCommitCommentOptions, options ...OptionFunc) (*CommitComment, *Response, error) { | |||
| func (s *CommitsService) PostCommitComment(pid interface{}, sha string, opt *PostCommitCommentOptions, options ...RequestOptionFunc) (*CommitComment, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -411,7 +412,7 @@ type CommitStatus struct { | |||
| // GetCommitStatuses gets the statuses of a commit in a project. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#get-the-status-of-a-commit | |||
| func (s *CommitsService) GetCommitStatuses(pid interface{}, sha string, opt *GetCommitStatusesOptions, options ...OptionFunc) ([]*CommitStatus, *Response, error) { | |||
| func (s *CommitsService) GetCommitStatuses(pid interface{}, sha string, opt *GetCommitStatusesOptions, options ...RequestOptionFunc) ([]*CommitStatus, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -447,7 +448,7 @@ type SetCommitStatusOptions struct { | |||
| // SetCommitStatus sets the status of a commit in a project. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#post-the-status-to-commit | |||
| func (s *CommitsService) SetCommitStatus(pid interface{}, sha string, opt *SetCommitStatusOptions, options ...OptionFunc) (*CommitStatus, *Response, error) { | |||
| func (s *CommitsService) SetCommitStatus(pid interface{}, sha string, opt *SetCommitStatusOptions, options ...RequestOptionFunc) (*CommitStatus, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -472,7 +473,7 @@ func (s *CommitsService) SetCommitStatus(pid interface{}, sha string, opt *SetCo | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/commits.html#list-merge-requests-associated-with-a-commit | |||
| func (s *CommitsService) GetMergeRequestsByCommit(pid interface{}, sha string, options ...OptionFunc) ([]*MergeRequest, *Response, error) { | |||
| func (s *CommitsService) GetMergeRequestsByCommit(pid interface{}, sha string, options ...RequestOptionFunc) ([]*MergeRequest, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -503,7 +504,7 @@ type CherryPickCommitOptions struct { | |||
| // CherryPickCommit cherry picks a commit to a given branch. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/commits.html#cherry-pick-a-commit | |||
| func (s *CommitsService) CherryPickCommit(pid interface{}, sha string, opt *CherryPickCommitOptions, options ...OptionFunc) (*Commit, *Response, error) { | |||
| func (s *CommitsService) CherryPickCommit(pid interface{}, sha string, opt *CherryPickCommitOptions, options ...RequestOptionFunc) (*Commit, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -534,7 +535,7 @@ type RevertCommitOptions struct { | |||
| // RevertCommit reverts a commit in a given branch. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ee/api/commits.html#revert-a-commit | |||
| func (s *CommitsService) RevertCommit(pid interface{}, sha string, opt *RevertCommitOptions, options ...OptionFunc) (*Commit, *Response, error) { | |||
| func (s *CommitsService) RevertCommit(pid interface{}, sha string, opt *RevertCommitOptions, options ...RequestOptionFunc) (*Commit, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -571,7 +572,7 @@ type GPGSignature struct { | |||
| // GetGPGSiganature gets a GPG signature of a commit. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ee/api/commits.html#get-gpg-signature-of-a-commit | |||
| func (s *CommitsService) GetGPGSiganature(pid interface{}, sha string, options ...OptionFunc) (*GPGSignature, *Response, error) { | |||
| func (s *CommitsService) GetGPGSiganature(pid interface{}, sha string, options ...RequestOptionFunc) (*GPGSignature, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -24,7 +24,7 @@ type CustomAttribute struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/custom_attributes.html#list-custom-attributes | |||
| func (s *CustomAttributesService) ListCustomUserAttributes(user int, options ...OptionFunc) ([]*CustomAttribute, *Response, error) { | |||
| func (s *CustomAttributesService) ListCustomUserAttributes(user int, options ...RequestOptionFunc) ([]*CustomAttribute, *Response, error) { | |||
| return s.listCustomAttributes("users", user, options...) | |||
| } | |||
| @@ -32,7 +32,7 @@ func (s *CustomAttributesService) ListCustomUserAttributes(user int, options ... | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/custom_attributes.html#list-custom-attributes | |||
| func (s *CustomAttributesService) ListCustomGroupAttributes(group int, options ...OptionFunc) ([]*CustomAttribute, *Response, error) { | |||
| func (s *CustomAttributesService) ListCustomGroupAttributes(group int, options ...RequestOptionFunc) ([]*CustomAttribute, *Response, error) { | |||
| return s.listCustomAttributes("groups", group, options...) | |||
| } | |||
| @@ -40,11 +40,11 @@ func (s *CustomAttributesService) ListCustomGroupAttributes(group int, options . | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/custom_attributes.html#list-custom-attributes | |||
| func (s *CustomAttributesService) ListCustomProjectAttributes(project int, options ...OptionFunc) ([]*CustomAttribute, *Response, error) { | |||
| func (s *CustomAttributesService) ListCustomProjectAttributes(project int, options ...RequestOptionFunc) ([]*CustomAttribute, *Response, error) { | |||
| return s.listCustomAttributes("projects", project, options...) | |||
| } | |||
| func (s *CustomAttributesService) listCustomAttributes(resource string, id int, options ...OptionFunc) ([]*CustomAttribute, *Response, error) { | |||
| func (s *CustomAttributesService) listCustomAttributes(resource string, id int, options ...RequestOptionFunc) ([]*CustomAttribute, *Response, error) { | |||
| u := fmt.Sprintf("%s/%d/custom_attributes", resource, id) | |||
| req, err := s.client.NewRequest("GET", u, nil, options) | |||
| if err != nil { | |||
| @@ -63,7 +63,7 @@ func (s *CustomAttributesService) listCustomAttributes(resource string, id int, | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/custom_attributes.html#single-custom-attribute | |||
| func (s *CustomAttributesService) GetCustomUserAttribute(user int, key string, options ...OptionFunc) (*CustomAttribute, *Response, error) { | |||
| func (s *CustomAttributesService) GetCustomUserAttribute(user int, key string, options ...RequestOptionFunc) (*CustomAttribute, *Response, error) { | |||
| return s.getCustomAttribute("users", user, key, options...) | |||
| } | |||
| @@ -71,7 +71,7 @@ func (s *CustomAttributesService) GetCustomUserAttribute(user int, key string, o | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/custom_attributes.html#single-custom-attribute | |||
| func (s *CustomAttributesService) GetCustomGroupAttribute(group int, key string, options ...OptionFunc) (*CustomAttribute, *Response, error) { | |||
| func (s *CustomAttributesService) GetCustomGroupAttribute(group int, key string, options ...RequestOptionFunc) (*CustomAttribute, *Response, error) { | |||
| return s.getCustomAttribute("groups", group, key, options...) | |||
| } | |||
| @@ -79,11 +79,11 @@ func (s *CustomAttributesService) GetCustomGroupAttribute(group int, key string, | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/custom_attributes.html#single-custom-attribute | |||
| func (s *CustomAttributesService) GetCustomProjectAttribute(project int, key string, options ...OptionFunc) (*CustomAttribute, *Response, error) { | |||
| func (s *CustomAttributesService) GetCustomProjectAttribute(project int, key string, options ...RequestOptionFunc) (*CustomAttribute, *Response, error) { | |||
| return s.getCustomAttribute("projects", project, key, options...) | |||
| } | |||
| func (s *CustomAttributesService) getCustomAttribute(resource string, id int, key string, options ...OptionFunc) (*CustomAttribute, *Response, error) { | |||
| func (s *CustomAttributesService) getCustomAttribute(resource string, id int, key string, options ...RequestOptionFunc) (*CustomAttribute, *Response, error) { | |||
| u := fmt.Sprintf("%s/%d/custom_attributes/%s", resource, id, key) | |||
| req, err := s.client.NewRequest("GET", u, nil, options) | |||
| if err != nil { | |||
| @@ -102,7 +102,7 @@ func (s *CustomAttributesService) getCustomAttribute(resource string, id int, ke | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/custom_attributes.html#set-custom-attribute | |||
| func (s *CustomAttributesService) SetCustomUserAttribute(user int, c CustomAttribute, options ...OptionFunc) (*CustomAttribute, *Response, error) { | |||
| func (s *CustomAttributesService) SetCustomUserAttribute(user int, c CustomAttribute, options ...RequestOptionFunc) (*CustomAttribute, *Response, error) { | |||
| return s.setCustomAttribute("users", user, c, options...) | |||
| } | |||
| @@ -110,7 +110,7 @@ func (s *CustomAttributesService) SetCustomUserAttribute(user int, c CustomAttri | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/custom_attributes.html#set-custom-attribute | |||
| func (s *CustomAttributesService) SetCustomGroupAttribute(group int, c CustomAttribute, options ...OptionFunc) (*CustomAttribute, *Response, error) { | |||
| func (s *CustomAttributesService) SetCustomGroupAttribute(group int, c CustomAttribute, options ...RequestOptionFunc) (*CustomAttribute, *Response, error) { | |||
| return s.setCustomAttribute("groups", group, c, options...) | |||
| } | |||
| @@ -118,11 +118,11 @@ func (s *CustomAttributesService) SetCustomGroupAttribute(group int, c CustomAtt | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/custom_attributes.html#set-custom-attribute | |||
| func (s *CustomAttributesService) SetCustomProjectAttribute(project int, c CustomAttribute, options ...OptionFunc) (*CustomAttribute, *Response, error) { | |||
| func (s *CustomAttributesService) SetCustomProjectAttribute(project int, c CustomAttribute, options ...RequestOptionFunc) (*CustomAttribute, *Response, error) { | |||
| return s.setCustomAttribute("projects", project, c, options...) | |||
| } | |||
| func (s *CustomAttributesService) setCustomAttribute(resource string, id int, c CustomAttribute, options ...OptionFunc) (*CustomAttribute, *Response, error) { | |||
| func (s *CustomAttributesService) setCustomAttribute(resource string, id int, c CustomAttribute, options ...RequestOptionFunc) (*CustomAttribute, *Response, error) { | |||
| u := fmt.Sprintf("%s/%d/custom_attributes/%s", resource, id, c.Key) | |||
| req, err := s.client.NewRequest("PUT", u, c, options) | |||
| if err != nil { | |||
| @@ -141,7 +141,7 @@ func (s *CustomAttributesService) setCustomAttribute(resource string, id int, c | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/custom_attributes.html#delete-custom-attribute | |||
| func (s *CustomAttributesService) DeleteCustomUserAttribute(user int, key string, options ...OptionFunc) (*Response, error) { | |||
| func (s *CustomAttributesService) DeleteCustomUserAttribute(user int, key string, options ...RequestOptionFunc) (*Response, error) { | |||
| return s.deleteCustomAttribute("users", user, key, options...) | |||
| } | |||
| @@ -149,7 +149,7 @@ func (s *CustomAttributesService) DeleteCustomUserAttribute(user int, key string | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/custom_attributes.html#delete-custom-attribute | |||
| func (s *CustomAttributesService) DeleteCustomGroupAttribute(group int, key string, options ...OptionFunc) (*Response, error) { | |||
| func (s *CustomAttributesService) DeleteCustomGroupAttribute(group int, key string, options ...RequestOptionFunc) (*Response, error) { | |||
| return s.deleteCustomAttribute("groups", group, key, options...) | |||
| } | |||
| @@ -157,11 +157,11 @@ func (s *CustomAttributesService) DeleteCustomGroupAttribute(group int, key stri | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/custom_attributes.html#delete-custom-attribute | |||
| func (s *CustomAttributesService) DeleteCustomProjectAttribute(project int, key string, options ...OptionFunc) (*Response, error) { | |||
| func (s *CustomAttributesService) DeleteCustomProjectAttribute(project int, key string, options ...RequestOptionFunc) (*Response, error) { | |||
| return s.deleteCustomAttribute("projects", project, key, options...) | |||
| } | |||
| func (s *CustomAttributesService) deleteCustomAttribute(resource string, id int, key string, options ...OptionFunc) (*Response, error) { | |||
| func (s *CustomAttributesService) deleteCustomAttribute(resource string, id int, key string, options ...RequestOptionFunc) (*Response, error) { | |||
| u := fmt.Sprintf("%s/%d/custom_attributes/%s", resource, id, key) | |||
| req, err := s.client.NewRequest("DELETE", u, nil, options) | |||
| if err != nil { | |||
| @@ -46,7 +46,7 @@ func (k DeployKey) String() string { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/deploy_keys.html#list-all-deploy-keys | |||
| func (s *DeployKeysService) ListAllDeployKeys(options ...OptionFunc) ([]*DeployKey, *Response, error) { | |||
| func (s *DeployKeysService) ListAllDeployKeys(options ...RequestOptionFunc) ([]*DeployKey, *Response, error) { | |||
| req, err := s.client.NewRequest("GET", "deploy_keys", nil, options) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -72,7 +72,7 @@ type ListProjectDeployKeysOptions ListOptions | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/deploy_keys.html#list-project-deploy-keys | |||
| func (s *DeployKeysService) ListProjectDeployKeys(pid interface{}, opt *ListProjectDeployKeysOptions, options ...OptionFunc) ([]*DeployKey, *Response, error) { | |||
| func (s *DeployKeysService) ListProjectDeployKeys(pid interface{}, opt *ListProjectDeployKeysOptions, options ...RequestOptionFunc) ([]*DeployKey, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -97,7 +97,7 @@ func (s *DeployKeysService) ListProjectDeployKeys(pid interface{}, opt *ListProj | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/deploy_keys.html#single-deploy-key | |||
| func (s *DeployKeysService) GetDeployKey(pid interface{}, deployKey int, options ...OptionFunc) (*DeployKey, *Response, error) { | |||
| func (s *DeployKeysService) GetDeployKey(pid interface{}, deployKey int, options ...RequestOptionFunc) (*DeployKey, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -134,7 +134,7 @@ type AddDeployKeyOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/deploy_keys.html#add-deploy-key | |||
| func (s *DeployKeysService) AddDeployKey(pid interface{}, opt *AddDeployKeyOptions, options ...OptionFunc) (*DeployKey, *Response, error) { | |||
| func (s *DeployKeysService) AddDeployKey(pid interface{}, opt *AddDeployKeyOptions, options ...RequestOptionFunc) (*DeployKey, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -159,7 +159,7 @@ func (s *DeployKeysService) AddDeployKey(pid interface{}, opt *AddDeployKeyOptio | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/deploy_keys.html#delete-deploy-key | |||
| func (s *DeployKeysService) DeleteDeployKey(pid interface{}, deployKey int, options ...OptionFunc) (*Response, error) { | |||
| func (s *DeployKeysService) DeleteDeployKey(pid interface{}, deployKey int, options ...RequestOptionFunc) (*Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, err | |||
| @@ -178,7 +178,7 @@ func (s *DeployKeysService) DeleteDeployKey(pid interface{}, deployKey int, opti | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/deploy_keys.html#enable-deploy-key | |||
| func (s *DeployKeysService) EnableDeployKey(pid interface{}, deployKey int, options ...OptionFunc) (*DeployKey, *Response, error) { | |||
| func (s *DeployKeysService) EnableDeployKey(pid interface{}, deployKey int, options ...RequestOptionFunc) (*DeployKey, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -0,0 +1,221 @@ | |||
| package gitlab | |||
| import ( | |||
| "fmt" | |||
| "time" | |||
| ) | |||
| // DeployTokensService handles communication with the deploy tokens related methods | |||
| // of the GitLab API. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/deploy_tokens.html | |||
| type DeployTokensService struct { | |||
| client *Client | |||
| } | |||
| // DeployToken represents a GitLab deploy token. | |||
| type DeployToken struct { | |||
| ID int `json:"id"` | |||
| Name string `json:"name"` | |||
| Username string `json:"username"` | |||
| ExpiresAt *time.Time `json:"expires_at"` | |||
| Token string `json:"token,omitempty"` | |||
| Scopes []string `json:"scopes"` | |||
| } | |||
| func (k DeployToken) String() string { | |||
| return Stringify(k) | |||
| } | |||
| // ListAllDeployTokens gets a list of all deploy tokens. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/deploy_tokens.html#list-all-deploy-tokens | |||
| func (s *DeployTokensService) ListAllDeployTokens(options ...RequestOptionFunc) ([]*DeployToken, *Response, error) { | |||
| req, err := s.client.NewRequest("GET", "deploy_tokens", nil, options) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| } | |||
| var ts []*DeployToken | |||
| resp, err := s.client.Do(req, &ts) | |||
| if err != nil { | |||
| return nil, resp, err | |||
| } | |||
| return ts, resp, err | |||
| } | |||
| // ListProjectDeployTokensOptions represents the available ListProjectDeployTokens() | |||
| // options. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/deploy_tokens.html#list-project-deploy-tokens | |||
| type ListProjectDeployTokensOptions ListOptions | |||
| // ListProjectDeployTokens gets a list of a project's deploy tokens. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/deploy_tokens.html#list-project-deploy-tokens | |||
| func (s *DeployTokensService) ListProjectDeployTokens(pid interface{}, opt *ListProjectDeployTokensOptions, options ...RequestOptionFunc) ([]*DeployToken, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| } | |||
| u := fmt.Sprintf("projects/%s/deploy_tokens", pathEscape(project)) | |||
| req, err := s.client.NewRequest("GET", u, opt, options) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| } | |||
| var ts []*DeployToken | |||
| resp, err := s.client.Do(req, &ts) | |||
| if err != nil { | |||
| return nil, resp, err | |||
| } | |||
| return ts, resp, err | |||
| } | |||
| // CreateProjectDeployTokenOptions represents the available CreateProjectDeployToken() options. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/deploy_tokens.html#create-a-project-deploy-token | |||
| type CreateProjectDeployTokenOptions struct { | |||
| Name *string `url:"name,omitempty" json:"name,omitempty"` | |||
| ExpiresAt *time.Time `url:"expires_at,omitempty" json:"expires_at,omitempty"` | |||
| Username *string `url:"username,omitempty" json:"username,omitempty"` | |||
| Scopes []string `url:"scopes,omitempty" json:"scopes,omitempty"` | |||
| } | |||
| // CreateProjectDeployToken creates a new deploy token for a project. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/deploy_tokens.html#create-a-project-deploy-token | |||
| func (s *DeployTokensService) CreateProjectDeployToken(pid interface{}, opt *CreateProjectDeployTokenOptions, options ...RequestOptionFunc) (*DeployToken, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| } | |||
| u := fmt.Sprintf("projects/%s/deploy_tokens", pathEscape(project)) | |||
| req, err := s.client.NewRequest("POST", u, opt, options) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| } | |||
| t := new(DeployToken) | |||
| resp, err := s.client.Do(req, t) | |||
| if err != nil { | |||
| return nil, resp, err | |||
| } | |||
| return t, resp, err | |||
| } | |||
| // DeleteProjectDeployToken removes a deploy token from the project. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/deploy_tokens.html#delete-a-project-deploy-token | |||
| func (s *DeployTokensService) DeleteProjectDeployToken(pid interface{}, deployToken int, options ...RequestOptionFunc) (*Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| u := fmt.Sprintf("projects/%s/deploy_tokens/%d", pathEscape(project), deployToken) | |||
| req, err := s.client.NewRequest("DELETE", u, nil, options) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| return s.client.Do(req, nil) | |||
| } | |||
| // ListGroupDeployTokensOptions represents the available ListGroupDeployTokens() | |||
| // options. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/deploy_tokens.html#list-group-deploy-deploy-tokens | |||
| type ListGroupDeployTokensOptions ListOptions | |||
| // ListGroupDeployTokens gets a list of a group’s deploy tokens. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/deploy_tokens.html#list-project-deploy-tokens | |||
| func (s *DeployTokensService) ListGroupDeployTokens(gid interface{}, opt *ListGroupDeployTokensOptions, options ...RequestOptionFunc) ([]*DeployToken, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| } | |||
| u := fmt.Sprintf("groups/%s/deploy_tokens", pathEscape(group)) | |||
| req, err := s.client.NewRequest("GET", u, opt, options) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| } | |||
| var ts []*DeployToken | |||
| resp, err := s.client.Do(req, &ts) | |||
| if err != nil { | |||
| return nil, resp, err | |||
| } | |||
| return ts, resp, err | |||
| } | |||
| // CreateGroupDeployTokenOptions represents the available CreateGroupDeployToken() options. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/deploy_tokens.html#create-a-group-deploy-token | |||
| type CreateGroupDeployTokenOptions struct { | |||
| Name *string `url:"name,omitempty" json:"name,omitempty"` | |||
| ExpiresAt *time.Time `url:"expires_at,omitempty" json:"expires_at,omitempty"` | |||
| Username *string `url:"username,omitempty" json:"username,omitempty"` | |||
| Scopes []string `url:"scopes,omitempty" json:"scopes,omitempty"` | |||
| } | |||
| // CreateGroupDeployToken creates a new deploy token for a group. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/deploy_tokens.html#create-a-group-deploy-token | |||
| func (s *DeployTokensService) CreateGroupDeployToken(gid interface{}, opt *CreateGroupDeployTokenOptions, options ...RequestOptionFunc) (*DeployToken, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| } | |||
| u := fmt.Sprintf("groups/%s/deploy_tokens", pathEscape(group)) | |||
| req, err := s.client.NewRequest("POST", u, opt, options) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| } | |||
| t := new(DeployToken) | |||
| resp, err := s.client.Do(req, t) | |||
| if err != nil { | |||
| return nil, resp, err | |||
| } | |||
| return t, resp, err | |||
| } | |||
| // DeleteGroupDeployToken removes a deploy token from the group. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/deploy_tokens.html#delete-a-group-deploy-token | |||
| func (s *DeployTokensService) DeleteGroupDeployToken(gid interface{}, deployToken int, options ...RequestOptionFunc) (*Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| u := fmt.Sprintf("groups/%s/deploy_tokens/%d", pathEscape(group), deployToken) | |||
| req, err := s.client.NewRequest("DELETE", u, nil, options) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| return s.client.Do(req, nil) | |||
| } | |||
| @@ -67,14 +67,18 @@ type Deployment struct { | |||
| // https://docs.gitlab.com/ce/api/deployments.html#list-project-deployments | |||
| type ListProjectDeploymentsOptions struct { | |||
| ListOptions | |||
| OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"` | |||
| Sort *string `url:"sort,omitempty" json:"sort,omitempty"` | |||
| OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"` | |||
| Sort *string `url:"sort,omitempty" json:"sort,omitempty"` | |||
| UpdatedAfter *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"` | |||
| UpdatedBefore *time.Time `url:"update_before,omitempty" json:"updated_before,omitempty"` | |||
| Environment *string `url:"environment,omitempty" json:"environment,omitempty"` | |||
| Status *string `url:"status,omitempty" json:"status,omitempty"` | |||
| } | |||
| // ListProjectDeployments gets a list of deployments in a project. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/deployments.html#list-project-deployments | |||
| func (s *DeploymentsService) ListProjectDeployments(pid interface{}, opts *ListProjectDeploymentsOptions, options ...OptionFunc) ([]*Deployment, *Response, error) { | |||
| func (s *DeploymentsService) ListProjectDeployments(pid interface{}, opts *ListProjectDeploymentsOptions, options ...RequestOptionFunc) ([]*Deployment, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -98,7 +102,7 @@ func (s *DeploymentsService) ListProjectDeployments(pid interface{}, opts *ListP | |||
| // GetProjectDeployment get a deployment for a project. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/deployments.html#get-a-specific-deployment | |||
| func (s *DeploymentsService) GetProjectDeployment(pid interface{}, deployment int, options ...OptionFunc) (*Deployment, *Response, error) { | |||
| func (s *DeploymentsService) GetProjectDeployment(pid interface{}, deployment int, options ...RequestOptionFunc) (*Deployment, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -118,3 +122,71 @@ func (s *DeploymentsService) GetProjectDeployment(pid interface{}, deployment in | |||
| return d, resp, err | |||
| } | |||
| // CreateProjectDeploymentOptions represents the available | |||
| // CreateProjectDeployment() options. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ee/api/deployments.html#create-a-deployment | |||
| type CreateProjectDeploymentOptions struct { | |||
| Environment *string `url:"environment,omitempty" json:"environment,omitempty"` | |||
| Ref *string `url:"ref,omitempty" json:"ref,omitempty"` | |||
| SHA *string `url:"sha,omitempty" json:"sha,omitempty"` | |||
| Tag *bool `url:"tag,omitempty" json:"tag,omitempty"` | |||
| Status *DeploymentStatusValue `url:"status,omitempty" json:"status,omitempty"` | |||
| } | |||
| // CreateProjectDeployment creates a project deployment. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ee/api/deployments.html#create-a-deployment | |||
| func (s *DeploymentsService) CreateProjectDeployment(pid interface{}, opt *CreateProjectDeploymentOptions, options ...RequestOptionFunc) (*Deployment, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| } | |||
| u := fmt.Sprintf("projects/%s/deployments", pathEscape(project)) | |||
| req, err := s.client.NewRequest("POST", u, opt, options) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| } | |||
| d := new(Deployment) | |||
| resp, err := s.client.Do(req, &d) | |||
| if err != nil { | |||
| return nil, resp, err | |||
| } | |||
| return d, resp, err | |||
| } | |||
| // UpdateProjectDeploymentOptions represents the available | |||
| // UpdateProjectDeployment() options. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ee/api/deployments.html#updating-a-deployment | |||
| type UpdateProjectDeploymentOptions struct { | |||
| Status *DeploymentStatusValue `url:"status,omitempty" json:"status,omitempty"` | |||
| } | |||
| // UpdateProjectDeployment updates a project deployment. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ee/api/deployments.html#updating-a-deployment | |||
| func (s *DeploymentsService) UpdateProjectDeployment(pid interface{}, deployment int, opt *UpdateProjectDeploymentOptions, options ...RequestOptionFunc) (*Deployment, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| } | |||
| u := fmt.Sprintf("projects/%s/deployments/%d", pathEscape(project), deployment) | |||
| req, err := s.client.NewRequest("PUT", u, opt, options) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| } | |||
| d := new(Deployment) | |||
| resp, err := s.client.Do(req, &d) | |||
| if err != nil { | |||
| return nil, resp, err | |||
| } | |||
| return d, resp, err | |||
| } | |||
| @@ -54,7 +54,7 @@ type ListIssueDiscussionsOptions ListOptions | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/discussions.html#list-project-issue-discussion-items | |||
| func (s *DiscussionsService) ListIssueDiscussions(pid interface{}, issue int, opt *ListIssueDiscussionsOptions, options ...OptionFunc) ([]*Discussion, *Response, error) { | |||
| func (s *DiscussionsService) ListIssueDiscussions(pid interface{}, issue int, opt *ListIssueDiscussionsOptions, options ...RequestOptionFunc) ([]*Discussion, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -79,7 +79,7 @@ func (s *DiscussionsService) ListIssueDiscussions(pid interface{}, issue int, op | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/discussions.html#get-single-issue-discussion-item | |||
| func (s *DiscussionsService) GetIssueDiscussion(pid interface{}, issue int, discussion string, options ...OptionFunc) (*Discussion, *Response, error) { | |||
| func (s *DiscussionsService) GetIssueDiscussion(pid interface{}, issue int, discussion string, options ...RequestOptionFunc) (*Discussion, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -118,7 +118,7 @@ type CreateIssueDiscussionOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/discussions.html#create-new-issue-thread | |||
| func (s *DiscussionsService) CreateIssueDiscussion(pid interface{}, issue int, opt *CreateIssueDiscussionOptions, options ...OptionFunc) (*Discussion, *Response, error) { | |||
| func (s *DiscussionsService) CreateIssueDiscussion(pid interface{}, issue int, opt *CreateIssueDiscussionOptions, options ...RequestOptionFunc) (*Discussion, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -153,7 +153,7 @@ type AddIssueDiscussionNoteOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/discussions.html#add-note-to-existing-issue-thread | |||
| func (s *DiscussionsService) AddIssueDiscussionNote(pid interface{}, issue int, discussion string, opt *AddIssueDiscussionNoteOptions, options ...OptionFunc) (*Note, *Response, error) { | |||
| func (s *DiscussionsService) AddIssueDiscussionNote(pid interface{}, issue int, discussion string, opt *AddIssueDiscussionNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -192,7 +192,7 @@ type UpdateIssueDiscussionNoteOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/discussions.html#modify-existing-issue-thread-note | |||
| func (s *DiscussionsService) UpdateIssueDiscussionNote(pid interface{}, issue int, discussion string, note int, opt *UpdateIssueDiscussionNoteOptions, options ...OptionFunc) (*Note, *Response, error) { | |||
| func (s *DiscussionsService) UpdateIssueDiscussionNote(pid interface{}, issue int, discussion string, note int, opt *UpdateIssueDiscussionNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -222,7 +222,7 @@ func (s *DiscussionsService) UpdateIssueDiscussionNote(pid interface{}, issue in | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/discussions.html#delete-an-issue-thread-note | |||
| func (s *DiscussionsService) DeleteIssueDiscussionNote(pid interface{}, issue int, discussion string, note int, options ...OptionFunc) (*Response, error) { | |||
| func (s *DiscussionsService) DeleteIssueDiscussionNote(pid interface{}, issue int, discussion string, note int, options ...RequestOptionFunc) (*Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, err | |||
| @@ -254,7 +254,7 @@ type ListSnippetDiscussionsOptions ListOptions | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/discussions.html#list-project-snippet-discussion-items | |||
| func (s *DiscussionsService) ListSnippetDiscussions(pid interface{}, snippet int, opt *ListSnippetDiscussionsOptions, options ...OptionFunc) ([]*Discussion, *Response, error) { | |||
| func (s *DiscussionsService) ListSnippetDiscussions(pid interface{}, snippet int, opt *ListSnippetDiscussionsOptions, options ...RequestOptionFunc) ([]*Discussion, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -279,7 +279,7 @@ func (s *DiscussionsService) ListSnippetDiscussions(pid interface{}, snippet int | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/discussions.html#get-single-snippet-discussion-item | |||
| func (s *DiscussionsService) GetSnippetDiscussion(pid interface{}, snippet int, discussion string, options ...OptionFunc) (*Discussion, *Response, error) { | |||
| func (s *DiscussionsService) GetSnippetDiscussion(pid interface{}, snippet int, discussion string, options ...RequestOptionFunc) (*Discussion, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -319,7 +319,7 @@ type CreateSnippetDiscussionOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/discussions.html#create-new-snippet-thread | |||
| func (s *DiscussionsService) CreateSnippetDiscussion(pid interface{}, snippet int, opt *CreateSnippetDiscussionOptions, options ...OptionFunc) (*Discussion, *Response, error) { | |||
| func (s *DiscussionsService) CreateSnippetDiscussion(pid interface{}, snippet int, opt *CreateSnippetDiscussionOptions, options ...RequestOptionFunc) (*Discussion, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -355,7 +355,7 @@ type AddSnippetDiscussionNoteOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/discussions.html#add-note-to-existing-snippet-thread | |||
| func (s *DiscussionsService) AddSnippetDiscussionNote(pid interface{}, snippet int, discussion string, opt *AddSnippetDiscussionNoteOptions, options ...OptionFunc) (*Note, *Response, error) { | |||
| func (s *DiscussionsService) AddSnippetDiscussionNote(pid interface{}, snippet int, discussion string, opt *AddSnippetDiscussionNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -394,7 +394,7 @@ type UpdateSnippetDiscussionNoteOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/discussions.html#modify-existing-snippet-thread-note | |||
| func (s *DiscussionsService) UpdateSnippetDiscussionNote(pid interface{}, snippet int, discussion string, note int, opt *UpdateSnippetDiscussionNoteOptions, options ...OptionFunc) (*Note, *Response, error) { | |||
| func (s *DiscussionsService) UpdateSnippetDiscussionNote(pid interface{}, snippet int, discussion string, note int, opt *UpdateSnippetDiscussionNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -424,7 +424,7 @@ func (s *DiscussionsService) UpdateSnippetDiscussionNote(pid interface{}, snippe | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/discussions.html#delete-a-snippet-thread-note | |||
| func (s *DiscussionsService) DeleteSnippetDiscussionNote(pid interface{}, snippet int, discussion string, note int, options ...OptionFunc) (*Response, error) { | |||
| func (s *DiscussionsService) DeleteSnippetDiscussionNote(pid interface{}, snippet int, discussion string, note int, options ...RequestOptionFunc) (*Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, err | |||
| @@ -456,7 +456,7 @@ type ListGroupEpicDiscussionsOptions ListOptions | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/discussions.html#list-group-epic-discussion-items | |||
| func (s *DiscussionsService) ListGroupEpicDiscussions(gid interface{}, epic int, opt *ListGroupEpicDiscussionsOptions, options ...OptionFunc) ([]*Discussion, *Response, error) { | |||
| func (s *DiscussionsService) ListGroupEpicDiscussions(gid interface{}, epic int, opt *ListGroupEpicDiscussionsOptions, options ...RequestOptionFunc) ([]*Discussion, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -484,7 +484,7 @@ func (s *DiscussionsService) ListGroupEpicDiscussions(gid interface{}, epic int, | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/discussions.html#get-single-epic-discussion-item | |||
| func (s *DiscussionsService) GetEpicDiscussion(gid interface{}, epic int, discussion string, options ...OptionFunc) (*Discussion, *Response, error) { | |||
| func (s *DiscussionsService) GetEpicDiscussion(gid interface{}, epic int, discussion string, options ...RequestOptionFunc) (*Discussion, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -524,7 +524,7 @@ type CreateEpicDiscussionOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/discussions.html#add-note-to-existing-epic-thread | |||
| func (s *DiscussionsService) CreateEpicDiscussion(gid interface{}, epic int, opt *CreateEpicDiscussionOptions, options ...OptionFunc) (*Discussion, *Response, error) { | |||
| func (s *DiscussionsService) CreateEpicDiscussion(gid interface{}, epic int, opt *CreateEpicDiscussionOptions, options ...RequestOptionFunc) (*Discussion, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -562,7 +562,7 @@ type AddEpicDiscussionNoteOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/discussions.html#add-note-to-existing-epic-thread | |||
| func (s *DiscussionsService) AddEpicDiscussionNote(gid interface{}, epic int, discussion string, opt *AddEpicDiscussionNoteOptions, options ...OptionFunc) (*Note, *Response, error) { | |||
| func (s *DiscussionsService) AddEpicDiscussionNote(gid interface{}, epic int, discussion string, opt *AddEpicDiscussionNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -601,7 +601,7 @@ type UpdateEpicDiscussionNoteOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/discussions.html#modify-existing-epic-thread-note | |||
| func (s *DiscussionsService) UpdateEpicDiscussionNote(gid interface{}, epic int, discussion string, note int, opt *UpdateEpicDiscussionNoteOptions, options ...OptionFunc) (*Note, *Response, error) { | |||
| func (s *DiscussionsService) UpdateEpicDiscussionNote(gid interface{}, epic int, discussion string, note int, opt *UpdateEpicDiscussionNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -631,7 +631,7 @@ func (s *DiscussionsService) UpdateEpicDiscussionNote(gid interface{}, epic int, | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/discussions.html#delete-an-epic-thread-note | |||
| func (s *DiscussionsService) DeleteEpicDiscussionNote(gid interface{}, epic int, discussion string, note int, options ...OptionFunc) (*Response, error) { | |||
| func (s *DiscussionsService) DeleteEpicDiscussionNote(gid interface{}, epic int, discussion string, note int, options ...RequestOptionFunc) (*Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, err | |||
| @@ -663,7 +663,7 @@ type ListMergeRequestDiscussionsOptions ListOptions | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/discussions.html#list-project-merge-request-discussion-items | |||
| func (s *DiscussionsService) ListMergeRequestDiscussions(pid interface{}, mergeRequest int, opt *ListMergeRequestDiscussionsOptions, options ...OptionFunc) ([]*Discussion, *Response, error) { | |||
| func (s *DiscussionsService) ListMergeRequestDiscussions(pid interface{}, mergeRequest int, opt *ListMergeRequestDiscussionsOptions, options ...RequestOptionFunc) ([]*Discussion, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -692,7 +692,7 @@ func (s *DiscussionsService) ListMergeRequestDiscussions(pid interface{}, mergeR | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/discussions.html#get-single-merge-request-discussion-item | |||
| func (s *DiscussionsService) GetMergeRequestDiscussion(pid interface{}, mergeRequest int, discussion string, options ...OptionFunc) (*Discussion, *Response, error) { | |||
| func (s *DiscussionsService) GetMergeRequestDiscussion(pid interface{}, mergeRequest int, discussion string, options ...RequestOptionFunc) (*Discussion, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -733,7 +733,7 @@ type CreateMergeRequestDiscussionOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/discussions.html#create-new-merge-request-thread | |||
| func (s *DiscussionsService) CreateMergeRequestDiscussion(pid interface{}, mergeRequest int, opt *CreateMergeRequestDiscussionOptions, options ...OptionFunc) (*Discussion, *Response, error) { | |||
| func (s *DiscussionsService) CreateMergeRequestDiscussion(pid interface{}, mergeRequest int, opt *CreateMergeRequestDiscussionOptions, options ...RequestOptionFunc) (*Discussion, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -771,7 +771,7 @@ type ResolveMergeRequestDiscussionOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/discussions.html#resolve-a-merge-request-thread | |||
| func (s *DiscussionsService) ResolveMergeRequestDiscussion(pid interface{}, mergeRequest int, discussion string, opt *ResolveMergeRequestDiscussionOptions, options ...OptionFunc) (*Discussion, *Response, error) { | |||
| func (s *DiscussionsService) ResolveMergeRequestDiscussion(pid interface{}, mergeRequest int, discussion string, opt *ResolveMergeRequestDiscussionOptions, options ...RequestOptionFunc) (*Discussion, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -811,7 +811,7 @@ type AddMergeRequestDiscussionNoteOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/discussions.html#add-note-to-existing-merge-request-discussion | |||
| func (s *DiscussionsService) AddMergeRequestDiscussionNote(pid interface{}, mergeRequest int, discussion string, opt *AddMergeRequestDiscussionNoteOptions, options ...OptionFunc) (*Note, *Response, error) { | |||
| func (s *DiscussionsService) AddMergeRequestDiscussionNote(pid interface{}, mergeRequest int, discussion string, opt *AddMergeRequestDiscussionNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -852,7 +852,7 @@ type UpdateMergeRequestDiscussionNoteOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/discussions.html#modify-existing-merge-request-discussion-note | |||
| func (s *DiscussionsService) UpdateMergeRequestDiscussionNote(pid interface{}, mergeRequest int, discussion string, note int, opt *UpdateMergeRequestDiscussionNoteOptions, options ...OptionFunc) (*Note, *Response, error) { | |||
| func (s *DiscussionsService) UpdateMergeRequestDiscussionNote(pid interface{}, mergeRequest int, discussion string, note int, opt *UpdateMergeRequestDiscussionNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -883,7 +883,7 @@ func (s *DiscussionsService) UpdateMergeRequestDiscussionNote(pid interface{}, m | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/discussions.html#delete-a-merge-request-discussion-note | |||
| func (s *DiscussionsService) DeleteMergeRequestDiscussionNote(pid interface{}, mergeRequest int, discussion string, note int, options ...OptionFunc) (*Response, error) { | |||
| func (s *DiscussionsService) DeleteMergeRequestDiscussionNote(pid interface{}, mergeRequest int, discussion string, note int, options ...RequestOptionFunc) (*Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, err | |||
| @@ -915,7 +915,7 @@ type ListCommitDiscussionsOptions ListOptions | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/discussions.html#list-project-commit-discussion-items | |||
| func (s *DiscussionsService) ListCommitDiscussions(pid interface{}, commit string, opt *ListCommitDiscussionsOptions, options ...OptionFunc) ([]*Discussion, *Response, error) { | |||
| func (s *DiscussionsService) ListCommitDiscussions(pid interface{}, commit string, opt *ListCommitDiscussionsOptions, options ...RequestOptionFunc) ([]*Discussion, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -944,7 +944,7 @@ func (s *DiscussionsService) ListCommitDiscussions(pid interface{}, commit strin | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/discussions.html#get-single-commit-discussion-item | |||
| func (s *DiscussionsService) GetCommitDiscussion(pid interface{}, commit string, discussion string, options ...OptionFunc) (*Discussion, *Response, error) { | |||
| func (s *DiscussionsService) GetCommitDiscussion(pid interface{}, commit string, discussion string, options ...RequestOptionFunc) (*Discussion, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -984,7 +984,7 @@ type CreateCommitDiscussionOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/discussions.html#create-new-commit-thread | |||
| func (s *DiscussionsService) CreateCommitDiscussion(pid interface{}, commit string, opt *CreateCommitDiscussionOptions, options ...OptionFunc) (*Discussion, *Response, error) { | |||
| func (s *DiscussionsService) CreateCommitDiscussion(pid interface{}, commit string, opt *CreateCommitDiscussionOptions, options ...RequestOptionFunc) (*Discussion, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -1022,7 +1022,7 @@ type AddCommitDiscussionNoteOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/discussions.html#add-note-to-existing-commit-thread | |||
| func (s *DiscussionsService) AddCommitDiscussionNote(pid interface{}, commit string, discussion string, opt *AddCommitDiscussionNoteOptions, options ...OptionFunc) (*Note, *Response, error) { | |||
| func (s *DiscussionsService) AddCommitDiscussionNote(pid interface{}, commit string, discussion string, opt *AddCommitDiscussionNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -1061,7 +1061,7 @@ type UpdateCommitDiscussionNoteOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/discussions.html#modify-an-existing-commit-thread-note | |||
| func (s *DiscussionsService) UpdateCommitDiscussionNote(pid interface{}, commit string, discussion string, note int, opt *UpdateCommitDiscussionNoteOptions, options ...OptionFunc) (*Note, *Response, error) { | |||
| func (s *DiscussionsService) UpdateCommitDiscussionNote(pid interface{}, commit string, discussion string, note int, opt *UpdateCommitDiscussionNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -1091,7 +1091,7 @@ func (s *DiscussionsService) UpdateCommitDiscussionNote(pid interface{}, commit | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/discussions.html#delete-a-commit-thread-note | |||
| func (s *DiscussionsService) DeleteCommitDiscussionNote(pid interface{}, commit string, discussion string, note int, options ...OptionFunc) (*Response, error) { | |||
| func (s *DiscussionsService) DeleteCommitDiscussionNote(pid interface{}, commit string, discussion string, note int, options ...RequestOptionFunc) (*Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, err | |||
| @@ -56,7 +56,7 @@ type ListEnvironmentsOptions ListOptions | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/environments.html#list-environments | |||
| func (s *EnvironmentsService) ListEnvironments(pid interface{}, opts *ListEnvironmentsOptions, options ...OptionFunc) ([]*Environment, *Response, error) { | |||
| func (s *EnvironmentsService) ListEnvironments(pid interface{}, opts *ListEnvironmentsOptions, options ...RequestOptionFunc) ([]*Environment, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -81,7 +81,7 @@ func (s *EnvironmentsService) ListEnvironments(pid interface{}, opts *ListEnviro | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/environments.html#get-a-specific-environment | |||
| func (s *EnvironmentsService) GetEnvironment(pid interface{}, environment int, options ...OptionFunc) (*Environment, *Response, error) { | |||
| func (s *EnvironmentsService) GetEnvironment(pid interface{}, environment int, options ...RequestOptionFunc) (*Environment, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -118,7 +118,7 @@ type CreateEnvironmentOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/environments.html#create-a-new-environment | |||
| func (s *EnvironmentsService) CreateEnvironment(pid interface{}, opt *CreateEnvironmentOptions, options ...OptionFunc) (*Environment, *Response, error) { | |||
| func (s *EnvironmentsService) CreateEnvironment(pid interface{}, opt *CreateEnvironmentOptions, options ...RequestOptionFunc) (*Environment, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -152,7 +152,7 @@ type EditEnvironmentOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/environments.html#edit-an-existing-environment | |||
| func (s *EnvironmentsService) EditEnvironment(pid interface{}, environment int, opt *EditEnvironmentOptions, options ...OptionFunc) (*Environment, *Response, error) { | |||
| func (s *EnvironmentsService) EditEnvironment(pid interface{}, environment int, opt *EditEnvironmentOptions, options ...RequestOptionFunc) (*Environment, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -177,7 +177,7 @@ func (s *EnvironmentsService) EditEnvironment(pid interface{}, environment int, | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/environments.html#remove-a-environment-from-a-group-or-project | |||
| func (s *EnvironmentsService) DeleteEnvironment(pid interface{}, environment int, options ...OptionFunc) (*Response, error) { | |||
| func (s *EnvironmentsService) DeleteEnvironment(pid interface{}, environment int, options ...RequestOptionFunc) (*Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, err | |||
| @@ -196,7 +196,7 @@ func (s *EnvironmentsService) DeleteEnvironment(pid interface{}, environment int | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/environments.html#stop-an-environment | |||
| func (s *EnvironmentsService) StopEnvironment(pid interface{}, environmentID int, options ...OptionFunc) (*Response, error) { | |||
| func (s *EnvironmentsService) StopEnvironment(pid interface{}, environmentID int, options ...RequestOptionFunc) (*Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, err | |||
| @@ -71,7 +71,7 @@ type ListGroupEpicsOptions struct { | |||
| // parameters page and per_page to return the list of group epics. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#list-epics-for-a-group | |||
| func (s *EpicsService) ListGroupEpics(gid interface{}, opt *ListGroupEpicsOptions, options ...OptionFunc) ([]*Epic, *Response, error) { | |||
| func (s *EpicsService) ListGroupEpics(gid interface{}, opt *ListGroupEpicsOptions, options ...RequestOptionFunc) ([]*Epic, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -95,7 +95,7 @@ func (s *EpicsService) ListGroupEpics(gid interface{}, opt *ListGroupEpicsOption | |||
| // GetEpic gets a single group epic. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#single-epic | |||
| func (s *EpicsService) GetEpic(gid interface{}, epic int, options ...OptionFunc) (*Epic, *Response, error) { | |||
| func (s *EpicsService) GetEpic(gid interface{}, epic int, options ...RequestOptionFunc) (*Epic, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -132,7 +132,7 @@ type CreateEpicOptions struct { | |||
| // CreateEpic creates a new group epic. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#new-epic | |||
| func (s *EpicsService) CreateEpic(gid interface{}, opt *CreateEpicOptions, options ...OptionFunc) (*Epic, *Response, error) { | |||
| func (s *EpicsService) CreateEpic(gid interface{}, opt *CreateEpicOptions, options ...RequestOptionFunc) (*Epic, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -171,7 +171,7 @@ type UpdateEpicOptions struct { | |||
| // to mark an epic as closed. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#update-epic | |||
| func (s *EpicsService) UpdateEpic(gid interface{}, epic int, opt *UpdateEpicOptions, options ...OptionFunc) (*Epic, *Response, error) { | |||
| func (s *EpicsService) UpdateEpic(gid interface{}, epic int, opt *UpdateEpicOptions, options ...RequestOptionFunc) (*Epic, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -195,7 +195,7 @@ func (s *EpicsService) UpdateEpic(gid interface{}, epic int, opt *UpdateEpicOpti | |||
| // DeleteEpic deletes a single group epic. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#delete-epic | |||
| func (s *EpicsService) DeleteEpic(gid interface{}, epic int, options ...OptionFunc) (*Response, error) { | |||
| func (s *EpicsService) DeleteEpic(gid interface{}, epic int, options ...RequestOptionFunc) (*Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, err | |||
| @@ -11,15 +11,18 @@ type EventType string | |||
| // List of available event types. | |||
| const ( | |||
| EventTypeBuild EventType = "Build Hook" | |||
| EventTypeIssue EventType = "Issue Hook" | |||
| EventTypeJob EventType = "Job Hook" | |||
| EventTypeMergeRequest EventType = "Merge Request Hook" | |||
| EventTypeNote EventType = "Note Hook" | |||
| EventTypePipeline EventType = "Pipeline Hook" | |||
| EventTypePush EventType = "Push Hook" | |||
| EventTypeTagPush EventType = "Tag Push Hook" | |||
| EventTypeWikiPage EventType = "Wiki Page Hook" | |||
| EventTypeBuild EventType = "Build Hook" | |||
| EventTypeIssue EventType = "Issue Hook" | |||
| EventConfidentialIssue EventType = "Confidential Issue Hook" | |||
| EventTypeJob EventType = "Job Hook" | |||
| EventTypeMergeRequest EventType = "Merge Request Hook" | |||
| EventTypeNote EventType = "Note Hook" | |||
| EventConfidentialNote EventType = "Confidential Note Hook" | |||
| EventTypePipeline EventType = "Pipeline Hook" | |||
| EventTypePush EventType = "Push Hook" | |||
| EventTypeSystemHook EventType = "System Hook" | |||
| EventTypeTagPush EventType = "Tag Push Hook" | |||
| EventTypeWikiPage EventType = "Wiki Page Hook" | |||
| ) | |||
| const ( | |||
| @@ -38,6 +41,119 @@ type noteEvent struct { | |||
| const eventTypeHeader = "X-Gitlab-Event" | |||
| // HookEventType returns the event type for the given request. | |||
| func HookEventType(r *http.Request) EventType { | |||
| return EventType(r.Header.Get(eventTypeHeader)) | |||
| } | |||
| // ParseHook tries to parse both web- and system hooks. | |||
| // | |||
| // Example usage: | |||
| // | |||
| // func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { | |||
| // payload, err := ioutil.ReadAll(r.Body) | |||
| // if err != nil { ... } | |||
| // event, err := gitlab.ParseHook(gitlab.HookEventType(r), payload) | |||
| // if err != nil { ... } | |||
| // switch event := event.(type) { | |||
| // case *gitlab.PushEvent: | |||
| // processPushEvent(event) | |||
| // case *gitlab.MergeEvent: | |||
| // processMergeEvent(event) | |||
| // ... | |||
| // } | |||
| // } | |||
| // | |||
| func ParseHook(eventType EventType, payload []byte) (event interface{}, err error) { | |||
| switch eventType { | |||
| case EventTypeSystemHook: | |||
| return ParseSystemhook(payload) | |||
| default: | |||
| return ParseWebhook(eventType, payload) | |||
| } | |||
| } | |||
| // ParseSystemhook parses the event payload. For recognized event types, a | |||
| // value of the corresponding struct type will be returned. An error will be | |||
| // returned for unrecognized event types. | |||
| // | |||
| // Example usage: | |||
| // | |||
| // func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { | |||
| // payload, err := ioutil.ReadAll(r.Body) | |||
| // if err != nil { ... } | |||
| // event, err := gitlab.ParseSystemhook(payload) | |||
| // if err != nil { ... } | |||
| // switch event := event.(type) { | |||
| // case *gitlab.PushSystemEvent: | |||
| // processPushSystemEvent(event) | |||
| // case *gitlab.MergeSystemEvent: | |||
| // processMergeSystemEvent(event) | |||
| // ... | |||
| // } | |||
| // } | |||
| // | |||
| func ParseSystemhook(payload []byte) (event interface{}, err error) { | |||
| e := &systemHookEvent{} | |||
| err = json.Unmarshal(payload, e) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| switch e.EventName { | |||
| case "push": | |||
| event = &PushSystemEvent{} | |||
| case "tag_push": | |||
| event = &TagPushSystemEvent{} | |||
| case "repository_update": | |||
| event = &RepositoryUpdateSystemEvent{} | |||
| case | |||
| "project_create", | |||
| "project_update", | |||
| "project_destroy", | |||
| "project_transfer", | |||
| "project_rename": | |||
| event = &ProjectSystemEvent{} | |||
| case | |||
| "group_create", | |||
| "group_destroy", | |||
| "group_rename": | |||
| event = &GroupSystemEvent{} | |||
| case | |||
| "key_create", | |||
| "key_destroy": | |||
| event = &KeySystemEvent{} | |||
| case | |||
| "user_create", | |||
| "user_destroy", | |||
| "user_rename": | |||
| event = &UserSystemEvent{} | |||
| case | |||
| "user_add_to_group", | |||
| "user_remove_from_group", | |||
| "user_update_for_group": | |||
| event = &UserGroupSystemEvent{} | |||
| case | |||
| "user_add_to_team", | |||
| "user_remove_from_team", | |||
| "user_update_for_team": | |||
| event = &UserTeamSystemEvent{} | |||
| default: | |||
| switch e.ObjectKind { | |||
| case "merge_request": | |||
| event = &MergeEvent{} | |||
| default: | |||
| return nil, fmt.Errorf("unexpected system hook type %s", e.EventName) | |||
| } | |||
| } | |||
| if err := json.Unmarshal(payload, event); err != nil { | |||
| return nil, err | |||
| } | |||
| return event, nil | |||
| } | |||
| // WebhookEventType returns the event type for the given request. | |||
| func WebhookEventType(r *http.Request) EventType { | |||
| return EventType(r.Header.Get(eventTypeHeader)) | |||
| @@ -52,7 +168,7 @@ func WebhookEventType(r *http.Request) EventType { | |||
| // func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { | |||
| // payload, err := ioutil.ReadAll(r.Body) | |||
| // if err != nil { ... } | |||
| // event, err := gitlab.ParseWebhook(gitlab.WebhookEventType(r), payload) | |||
| // event, err := gitlab.ParseWebhook(gitlab.HookEventType(r), payload) | |||
| // if err != nil { ... } | |||
| // switch event := event.(type) { | |||
| // case *gitlab.PushEvent: | |||
| @@ -67,7 +183,7 @@ func ParseWebhook(eventType EventType, payload []byte) (event interface{}, err e | |||
| switch eventType { | |||
| case EventTypeBuild: | |||
| event = &BuildEvent{} | |||
| case EventTypeIssue: | |||
| case EventTypeIssue, EventConfidentialIssue: | |||
| event = &IssueEvent{} | |||
| case EventTypeJob: | |||
| event = &JobEvent{} | |||
| @@ -81,7 +197,7 @@ func ParseWebhook(eventType EventType, payload []byte) (event interface{}, err e | |||
| event = &TagEvent{} | |||
| case EventTypeWikiPage: | |||
| event = &WikiPageEvent{} | |||
| case EventTypeNote: | |||
| case EventTypeNote, EventConfidentialNote: | |||
| note := ¬eEvent{} | |||
| err := json.Unmarshal(payload, note) | |||
| if err != nil { | |||
| @@ -0,0 +1,133 @@ | |||
| package gitlab | |||
| // systemHookEvent is used to pre-process events to determine the | |||
| // system hook event type. | |||
| type systemHookEvent struct { | |||
| BaseSystemEvent | |||
| ObjectKind string `json:"object_kind"` | |||
| } | |||
| // BaseSystemEvent contains system hook's common properties. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/system_hooks/system_hooks.html | |||
| type BaseSystemEvent struct { | |||
| EventName string `json:"event_name"` | |||
| CreatedAt string `json:"created_at"` | |||
| UpdatedAt string `json:"updated_at"` | |||
| } | |||
| // ProjectSystemEvent represents a project system event. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/system_hooks/system_hooks.html | |||
| type ProjectSystemEvent struct { | |||
| BaseSystemEvent | |||
| Name string `json:"name"` | |||
| Path string `json:"path"` | |||
| PathWithNamespace string `json:"path_with_namespace"` | |||
| ProjectID int `json:"project_id"` | |||
| OwnerName string `json:"owner_name"` | |||
| OwnerEmail string `json:"owner_email"` | |||
| ProjectVisibility string `json:"project_visibility"` | |||
| OldPathWithNamespace string `json:"old_path_with_namespace,omitempty"` | |||
| } | |||
| // GroupSystemEvent represents a group system event. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/system_hooks/system_hooks.html | |||
| type GroupSystemEvent struct { | |||
| BaseSystemEvent | |||
| Name string `json:"name"` | |||
| Path string `json:"path"` | |||
| PathWithNamespace string `json:"full_path"` | |||
| GroupID int `json:"group_id"` | |||
| OwnerName string `json:"owner_name"` | |||
| OwnerEmail string `json:"owner_email"` | |||
| ProjectVisibility string `json:"project_visibility"` | |||
| OldPath string `json:"old_path,omitempty"` | |||
| OldPathWithNamespace string `json:"old_full_path,omitempty"` | |||
| } | |||
| // KeySystemEvent represents a key system event. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/system_hooks/system_hooks.html | |||
| type KeySystemEvent struct { | |||
| BaseSystemEvent | |||
| ID int `json:"id"` | |||
| Username string `json:"username"` | |||
| Key string `json:"key"` | |||
| } | |||
| // UserSystemEvent represents a user system event. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/system_hooks/system_hooks.html | |||
| type UserSystemEvent struct { | |||
| BaseSystemEvent | |||
| ID int `json:"user_id"` | |||
| Name string `json:"name"` | |||
| Username string `json:"username"` | |||
| OldUsername string `json:"old_username,omitempty"` | |||
| Email string `json:"email"` | |||
| } | |||
| // UserGroupSystemEvent represents a user group system event. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/system_hooks/system_hooks.html | |||
| type UserGroupSystemEvent struct { | |||
| BaseSystemEvent | |||
| ID int `json:"user_id"` | |||
| Name string `json:"user_name"` | |||
| Username string `json:"user_username"` | |||
| Email string `json:"user_email"` | |||
| GroupID int `json:"group_id"` | |||
| GroupName string `json:"group_name"` | |||
| GroupPath string `json:"group_path"` | |||
| GroupAccess string `json:"group_access"` | |||
| } | |||
| // UserTeamSystemEvent represents a user team system event. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/system_hooks/system_hooks.html | |||
| type UserTeamSystemEvent struct { | |||
| BaseSystemEvent | |||
| ID int `json:"user_id"` | |||
| Name string `json:"user_name"` | |||
| Username string `json:"user_username"` | |||
| Email string `json:"user_email"` | |||
| ProjectID int `json:"project_id"` | |||
| ProjectName string `json:"project_name"` | |||
| ProjectPath string `json:"project_path"` | |||
| ProjectPathWithNamespace string `json:"project_path_with_namespace"` | |||
| ProjectVisibility string `json:"project_visibility"` | |||
| AccessLevel string `json:"access_level"` | |||
| } | |||
| // PushSystemEvent represents a push system event. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/system_hooks/system_hooks.html | |||
| type PushSystemEvent struct { | |||
| BaseSystemEvent | |||
| } | |||
| // TagPushSystemEvent represents a tag push system event. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/system_hooks/system_hooks.html | |||
| type TagPushSystemEvent struct { | |||
| BaseSystemEvent | |||
| } | |||
| // RepositoryUpdateSystemEvent represents a repository updated system event. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/system_hooks/system_hooks.html | |||
| type RepositoryUpdateSystemEvent struct { | |||
| BaseSystemEvent | |||
| } | |||
| @@ -128,6 +128,7 @@ type IssueEvent struct { | |||
| ObjectKind string `json:"object_kind"` | |||
| User *User `json:"user"` | |||
| Project struct { | |||
| ID int `json:"id"` | |||
| Name string `json:"name"` | |||
| Description string `json:"description"` | |||
| AvatarURL string `json:"avatar_url"` | |||
| @@ -203,6 +204,7 @@ type JobEvent struct { | |||
| BuildFinishedAt string `json:"build_finished_at"` | |||
| BuildDuration float64 `json:"build_duration"` | |||
| BuildAllowFailure bool `json:"build_allow_failure"` | |||
| PipelineID int `json:"pipeline_id"` | |||
| ProjectID int `json:"project_id"` | |||
| ProjectName string `json:"project_name"` | |||
| User struct { | |||
| @@ -329,42 +331,41 @@ type MergeCommentEvent struct { | |||
| } `json:"object_attributes"` | |||
| Repository *Repository `json:"repository"` | |||
| MergeRequest struct { | |||
| ID int `json:"id"` | |||
| TargetBranch string `json:"target_branch"` | |||
| SourceBranch string `json:"source_branch"` | |||
| SourceProjectID int `json:"source_project_id"` | |||
| AuthorID int `json:"author_id"` | |||
| AssigneeID int `json:"assignee_id"` | |||
| Title string `json:"title"` | |||
| CreatedAt string `json:"created_at"` | |||
| UpdatedAt string `json:"updated_at"` | |||
| MilestoneID int `json:"milestone_id"` | |||
| State string `json:"state"` | |||
| MergeStatus string `json:"merge_status"` | |||
| TargetProjectID int `json:"target_project_id"` | |||
| IID int `json:"iid"` | |||
| Description string `json:"description"` | |||
| Position int `json:"position"` | |||
| LockedAt string `json:"locked_at"` | |||
| UpdatedByID int `json:"updated_by_id"` | |||
| MergeError string `json:"merge_error"` | |||
| MergeParams struct { | |||
| ForceRemoveSourceBranch string `json:"force_remove_source_branch"` | |||
| } `json:"merge_params"` | |||
| MergeWhenPipelineSucceeds bool `json:"merge_when_pipeline_succeeds"` | |||
| MergeUserID int `json:"merge_user_id"` | |||
| MergeCommitSHA string `json:"merge_commit_sha"` | |||
| DeletedAt string `json:"deleted_at"` | |||
| InProgressMergeCommitSHA string `json:"in_progress_merge_commit_sha"` | |||
| LockVersion int `json:"lock_version"` | |||
| ApprovalsBeforeMerge string `json:"approvals_before_merge"` | |||
| RebaseCommitSHA string `json:"rebase_commit_sha"` | |||
| TimeEstimate int `json:"time_estimate"` | |||
| Squash bool `json:"squash"` | |||
| LastEditedAt string `json:"last_edited_at"` | |||
| LastEditedByID int `json:"last_edited_by_id"` | |||
| Source *Repository `json:"source"` | |||
| Target *Repository `json:"target"` | |||
| ID int `json:"id"` | |||
| TargetBranch string `json:"target_branch"` | |||
| SourceBranch string `json:"source_branch"` | |||
| SourceProjectID int `json:"source_project_id"` | |||
| AuthorID int `json:"author_id"` | |||
| AssigneeID int `json:"assignee_id"` | |||
| AssigneeIDs []int `json:"assignee_ids"` | |||
| Title string `json:"title"` | |||
| CreatedAt string `json:"created_at"` | |||
| UpdatedAt string `json:"updated_at"` | |||
| MilestoneID int `json:"milestone_id"` | |||
| State string `json:"state"` | |||
| MergeStatus string `json:"merge_status"` | |||
| TargetProjectID int `json:"target_project_id"` | |||
| IID int `json:"iid"` | |||
| Description string `json:"description"` | |||
| Position int `json:"position"` | |||
| LockedAt string `json:"locked_at"` | |||
| UpdatedByID int `json:"updated_by_id"` | |||
| MergeError string `json:"merge_error"` | |||
| MergeParams *MergeParams `json:"merge_params"` | |||
| MergeWhenPipelineSucceeds bool `json:"merge_when_pipeline_succeeds"` | |||
| MergeUserID int `json:"merge_user_id"` | |||
| MergeCommitSHA string `json:"merge_commit_sha"` | |||
| DeletedAt string `json:"deleted_at"` | |||
| InProgressMergeCommitSHA string `json:"in_progress_merge_commit_sha"` | |||
| LockVersion int `json:"lock_version"` | |||
| ApprovalsBeforeMerge string `json:"approvals_before_merge"` | |||
| RebaseCommitSHA string `json:"rebase_commit_sha"` | |||
| TimeEstimate int `json:"time_estimate"` | |||
| Squash bool `json:"squash"` | |||
| LastEditedAt string `json:"last_edited_at"` | |||
| LastEditedByID int `json:"last_edited_by_id"` | |||
| Source *Repository `json:"source"` | |||
| Target *Repository `json:"target"` | |||
| LastCommit struct { | |||
| ID string `json:"id"` | |||
| Message string `json:"message"` | |||
| @@ -524,6 +525,7 @@ type MergeEvent struct { | |||
| SourceProjectID int `json:"source_project_id"` | |||
| AuthorID int `json:"author_id"` | |||
| AssigneeID int `json:"assignee_id"` | |||
| AssigneeIDs []int `json:"assignee_ids"` | |||
| Title string `json:"title"` | |||
| CreatedAt string `json:"created_at"` // Should be *time.Time (see Gitlab issue #21468) | |||
| UpdatedAt string `json:"updated_at"` // Should be *time.Time (see Gitlab issue #21468) | |||
| @@ -715,6 +717,18 @@ type PipelineEvent struct { | |||
| FinishedAt string `json:"finished_at"` | |||
| Duration int `json:"duration"` | |||
| } `json:"object_attributes"` | |||
| MergeRequest struct { | |||
| ID int `json:"id"` | |||
| IID int `json:"iid"` | |||
| Title string `json:"title"` | |||
| SourceBranch string `json:"source_branch"` | |||
| SourceProjectID int `json:"source_project_id"` | |||
| TargetBranch string `json:"target_branch"` | |||
| TargetProjectID int `json:"target_project_id"` | |||
| State string `json:"state"` | |||
| MergeRequestStatus string `json:"merge_status"` | |||
| URL string `json:"url"` | |||
| } `json:"merge_request"` | |||
| User struct { | |||
| Name string `json:"name"` | |||
| Username string `json:"username"` | |||
| @@ -82,7 +82,7 @@ type ListContributionEventsOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/events.html#get-user-contribution-events | |||
| func (s *UsersService) ListUserContributionEvents(uid interface{}, opt *ListContributionEventsOptions, options ...OptionFunc) ([]*ContributionEvent, *Response, error) { | |||
| func (s *UsersService) ListUserContributionEvents(uid interface{}, opt *ListContributionEventsOptions, options ...RequestOptionFunc) ([]*ContributionEvent, *Response, error) { | |||
| user, err := parseID(uid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -106,7 +106,7 @@ func (s *UsersService) ListUserContributionEvents(uid interface{}, opt *ListCont | |||
| // ListCurrentUserContributionEvents gets a list currently authenticated user's events | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/events.html#list-currently-authenticated-user-39-s-events | |||
| func (s *EventsService) ListCurrentUserContributionEvents(opt *ListContributionEventsOptions, options ...OptionFunc) ([]*ContributionEvent, *Response, error) { | |||
| func (s *EventsService) ListCurrentUserContributionEvents(opt *ListContributionEventsOptions, options ...RequestOptionFunc) ([]*ContributionEvent, *Response, error) { | |||
| req, err := s.client.NewRequest("GET", "events", opt, options) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -124,7 +124,7 @@ func (s *EventsService) ListCurrentUserContributionEvents(opt *ListContributionE | |||
| // ListProjectVisibleEvents gets a list of visible events for a particular project | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ee/api/events.html#list-a-project-s-visible-events | |||
| func (s *EventsService) ListProjectVisibleEvents(pid interface{}, opt *ListContributionEventsOptions, options ...OptionFunc) ([]*ContributionEvent, *Response, error) { | |||
| func (s *EventsService) ListProjectVisibleEvents(pid interface{}, opt *ListContributionEventsOptions, options ...RequestOptionFunc) ([]*ContributionEvent, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -38,7 +38,7 @@ func (f Feature) String() string { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/features.html#list-all-features | |||
| func (s *FeaturesService) ListFeatures(options ...OptionFunc) ([]*Feature, *Response, error) { | |||
| func (s *FeaturesService) ListFeatures(options ...RequestOptionFunc) ([]*Feature, *Response, error) { | |||
| req, err := s.client.NewRequest("GET", "features", nil, options) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -56,7 +56,7 @@ func (s *FeaturesService) ListFeatures(options ...OptionFunc) ([]*Feature, *Resp | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/features.html#set-or-create-a-feature | |||
| func (s *FeaturesService) SetFeatureFlag(name string, value interface{}, options ...OptionFunc) (*Feature, *Response, error) { | |||
| func (s *FeaturesService) SetFeatureFlag(name string, value interface{}, options ...RequestOptionFunc) (*Feature, *Response, error) { | |||
| u := fmt.Sprintf("features/%s", url.PathEscape(name)) | |||
| opt := struct { | |||
| @@ -47,7 +47,7 @@ type ListTemplatesOptions ListOptions | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/templates/gitignores.html#list-gitignore-templates | |||
| func (s *GitIgnoreTemplatesService) ListTemplates(opt *ListTemplatesOptions, options ...OptionFunc) ([]*GitIgnoreTemplate, *Response, error) { | |||
| func (s *GitIgnoreTemplatesService) ListTemplates(opt *ListTemplatesOptions, options ...RequestOptionFunc) ([]*GitIgnoreTemplate, *Response, error) { | |||
| req, err := s.client.NewRequest("GET", "templates/gitignores", opt, options) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -66,7 +66,7 @@ func (s *GitIgnoreTemplatesService) ListTemplates(opt *ListTemplatesOptions, opt | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/templates/gitignores.html#single-gitignore-template | |||
| func (s *GitIgnoreTemplatesService) GetTemplate(key string, options ...OptionFunc) (*GitIgnoreTemplate, *Response, error) { | |||
| func (s *GitIgnoreTemplatesService) GetTemplate(key string, options ...RequestOptionFunc) (*GitIgnoreTemplate, *Response, error) { | |||
| u := fmt.Sprintf("templates/gitignores/%s", url.PathEscape(key)) | |||
| req, err := s.client.NewRequest("GET", u, nil, options) | |||
| @@ -18,28 +18,35 @@ | |||
| package gitlab | |||
| import ( | |||
| "bytes" | |||
| "context" | |||
| "encoding/json" | |||
| "errors" | |||
| "fmt" | |||
| "io" | |||
| "io/ioutil" | |||
| "math/rand" | |||
| "net/http" | |||
| "net/url" | |||
| "sort" | |||
| "strconv" | |||
| "strings" | |||
| "sync" | |||
| "time" | |||
| "github.com/google/go-querystring/query" | |||
| "github.com/hashicorp/go-cleanhttp" | |||
| retryablehttp "github.com/hashicorp/go-retryablehttp" | |||
| "golang.org/x/oauth2" | |||
| "golang.org/x/time/rate" | |||
| ) | |||
| const ( | |||
| defaultBaseURL = "https://gitlab.com/" | |||
| apiVersionPath = "api/v4/" | |||
| userAgent = "go-gitlab" | |||
| headerRateLimit = "RateLimit-Limit" | |||
| headerRateReset = "RateLimit-Reset" | |||
| ) | |||
| // authType represents an authentication type within GitLab. | |||
| @@ -83,6 +90,7 @@ type BuildStateValue string | |||
| // These constants represent all valid build states. | |||
| const ( | |||
| Pending BuildStateValue = "pending" | |||
| Created BuildStateValue = "created" | |||
| Running BuildStateValue = "running" | |||
| Success BuildStateValue = "success" | |||
| Failed BuildStateValue = "failed" | |||
| @@ -91,6 +99,18 @@ const ( | |||
| Manual BuildStateValue = "manual" | |||
| ) | |||
| // DeploymentStatusValue represents a Gitlab deployment status. | |||
| type DeploymentStatusValue string | |||
| // These constants represent all valid deployment statuses. | |||
| const ( | |||
| DeploymentStatusCreated DeploymentStatusValue = "created" | |||
| DeploymentStatusRunning DeploymentStatusValue = "running" | |||
| DeploymentStatusSuccess DeploymentStatusValue = "success" | |||
| DeploymentStatusFailed DeploymentStatusValue = "failed" | |||
| DeploymentStatusCanceled DeploymentStatusValue = "canceled" | |||
| ) | |||
| // ISOTime represents an ISO 8601 formatted date | |||
| type ISOTime time.Time | |||
| @@ -215,6 +235,33 @@ const ( | |||
| PublicVisibility VisibilityValue = "public" | |||
| ) | |||
| // ProjectCreationLevelValue represents a project creation level within GitLab. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/ | |||
| type ProjectCreationLevelValue string | |||
| // List of available project creation levels. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/ | |||
| const ( | |||
| NoOneProjectCreation ProjectCreationLevelValue = "noone" | |||
| MaintainerProjectCreation ProjectCreationLevelValue = "maintainer" | |||
| DeveloperProjectCreation ProjectCreationLevelValue = "developer" | |||
| ) | |||
| // SubGroupCreationLevelValue represents a sub group creation level within GitLab. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/ | |||
| type SubGroupCreationLevelValue string | |||
| // List of available sub group creation levels. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/ | |||
| const ( | |||
| OwnerSubGroupCreationLevelValue SubGroupCreationLevelValue = "owner" | |||
| MaintainerSubGroupCreationLevelValue SubGroupCreationLevelValue = "maintainer" | |||
| ) | |||
| // VariableTypeValue represents a variable type within GitLab. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/ | |||
| @@ -281,13 +328,23 @@ const ( | |||
| // A Client manages communication with the GitLab API. | |||
| type Client struct { | |||
| // HTTP client used to communicate with the API. | |||
| client *http.Client | |||
| client *retryablehttp.Client | |||
| // Base URL for API requests. Defaults to the public GitLab API, but can be | |||
| // set to a domain endpoint to use with a self hosted GitLab server. baseURL | |||
| // should always be specified with a trailing slash. | |||
| baseURL *url.URL | |||
| // disableRetries is used to disable the default retry logic. | |||
| disableRetries bool | |||
| // configLimiter is used to make sure the limiter is configured exactly | |||
| // once and block all other calls until the initial (one) call is done. | |||
| configureLimiterOnce sync.Once | |||
| // Limiter is used to limit API calls and prevent 429 responses. | |||
| limiter *rate.Limiter | |||
| // Token type used to make authenticated API calls. | |||
| authType authType | |||
| @@ -302,6 +359,7 @@ type Client struct { | |||
| // Services used for talking to different parts of the GitLab API. | |||
| AccessRequests *AccessRequestsService | |||
| Applications *ApplicationsService | |||
| AwardEmoji *AwardEmojiService | |||
| Boards *IssueBoardsService | |||
| Branches *BranchesService | |||
| @@ -311,6 +369,7 @@ type Client struct { | |||
| ContainerRegistry *ContainerRegistryService | |||
| CustomAttribute *CustomAttributesService | |||
| DeployKeys *DeployKeysService | |||
| DeployTokens *DeployTokensService | |||
| Deployments *DeploymentsService | |||
| Discussions *DiscussionsService | |||
| Environments *EnvironmentsService | |||
| @@ -382,27 +441,31 @@ type ListOptions struct { | |||
| PerPage int `url:"per_page,omitempty" json:"per_page,omitempty"` | |||
| } | |||
| // NewClient returns a new GitLab API client. If a nil httpClient is | |||
| // provided, http.DefaultClient will be used. To use API methods which require | |||
| // NewClient returns a new GitLab API client. To use API methods which require | |||
| // authentication, provide a valid private or personal token. | |||
| func NewClient(httpClient *http.Client, token string) *Client { | |||
| client := newClient(httpClient) | |||
| func NewClient(token string, options ...ClientOptionFunc) (*Client, error) { | |||
| client, err := newClient(options...) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| client.authType = privateToken | |||
| client.token = token | |||
| return client | |||
| return client, nil | |||
| } | |||
| // NewBasicAuthClient returns a new GitLab API client. If a nil httpClient is | |||
| // provided, http.DefaultClient will be used. To use API methods which require | |||
| // authentication, provide a valid username and password. | |||
| func NewBasicAuthClient(httpClient *http.Client, endpoint, username, password string) (*Client, error) { | |||
| client := newClient(httpClient) | |||
| // NewBasicAuthClient returns a new GitLab API client. To use API methods which | |||
| // require authentication, provide a valid username and password. | |||
| func NewBasicAuthClient(username, password string, options ...ClientOptionFunc) (*Client, error) { | |||
| client, err := newClient(options...) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| client.authType = basicAuth | |||
| client.username = username | |||
| client.password = password | |||
| client.SetBaseURL(endpoint) | |||
| err := client.requestOAuthToken(context.TODO()) | |||
| err = client.requestOAuthToken(context.Background()) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| @@ -410,6 +473,18 @@ func NewBasicAuthClient(httpClient *http.Client, endpoint, username, password st | |||
| return client, nil | |||
| } | |||
| // NewOAuthClient returns a new GitLab API client. To use API methods which | |||
| // require authentication, provide a valid oauth token. | |||
| func NewOAuthClient(token string, options ...ClientOptionFunc) (*Client, error) { | |||
| client, err := newClient(options...) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| client.authType = oAuthToken | |||
| client.token = token | |||
| return client, nil | |||
| } | |||
| func (c *Client) requestOAuthToken(ctx context.Context) error { | |||
| config := &oauth2.Config{ | |||
| Endpoint: oauth2.Endpoint{ | |||
| @@ -426,25 +501,31 @@ func (c *Client) requestOAuthToken(ctx context.Context) error { | |||
| return nil | |||
| } | |||
| // NewOAuthClient returns a new GitLab API client. If a nil httpClient is | |||
| // provided, http.DefaultClient will be used. To use API methods which require | |||
| // authentication, provide a valid oauth token. | |||
| func NewOAuthClient(httpClient *http.Client, token string) *Client { | |||
| client := newClient(httpClient) | |||
| client.authType = oAuthToken | |||
| client.token = token | |||
| return client | |||
| } | |||
| func newClient(httpClient *http.Client) *Client { | |||
| if httpClient == nil { | |||
| httpClient = http.DefaultClient | |||
| func newClient(options ...ClientOptionFunc) (*Client, error) { | |||
| c := &Client{UserAgent: userAgent} | |||
| // Configure the HTTP client. | |||
| c.client = &retryablehttp.Client{ | |||
| Backoff: c.retryHTTPBackoff, | |||
| CheckRetry: c.retryHTTPCheck, | |||
| ErrorHandler: retryablehttp.PassthroughErrorHandler, | |||
| HTTPClient: cleanhttp.DefaultPooledClient(), | |||
| RetryWaitMin: 100 * time.Millisecond, | |||
| RetryWaitMax: 400 * time.Millisecond, | |||
| RetryMax: 5, | |||
| } | |||
| c := &Client{client: httpClient, UserAgent: userAgent} | |||
| if err := c.SetBaseURL(defaultBaseURL); err != nil { | |||
| // Should never happen since defaultBaseURL is our constant. | |||
| panic(err) | |||
| // Set the default base URL. | |||
| c.setBaseURL(defaultBaseURL) | |||
| // Apply any given client options. | |||
| for _, fn := range options { | |||
| if fn == nil { | |||
| continue | |||
| } | |||
| if err := fn(c); err != nil { | |||
| return nil, err | |||
| } | |||
| } | |||
| // Create the internal timeStats service. | |||
| @@ -452,6 +533,7 @@ func newClient(httpClient *http.Client) *Client { | |||
| // Create all the public services. | |||
| c.AccessRequests = &AccessRequestsService{client: c} | |||
| c.Applications = &ApplicationsService{client: c} | |||
| c.AwardEmoji = &AwardEmojiService{client: c} | |||
| c.Boards = &IssueBoardsService{client: c} | |||
| c.Branches = &BranchesService{client: c} | |||
| @@ -461,6 +543,7 @@ func newClient(httpClient *http.Client) *Client { | |||
| c.ContainerRegistry = &ContainerRegistryService{client: c} | |||
| c.CustomAttribute = &CustomAttributesService{client: c} | |||
| c.DeployKeys = &DeployKeysService{client: c} | |||
| c.DeployTokens = &DeployTokensService{client: c} | |||
| c.Deployments = &DeploymentsService{client: c} | |||
| c.Discussions = &DiscussionsService{client: c} | |||
| c.Environments = &EnvironmentsService{client: c} | |||
| @@ -521,7 +604,107 @@ func newClient(httpClient *http.Client) *Client { | |||
| c.Version = &VersionService{client: c} | |||
| c.Wikis = &WikisService{client: c} | |||
| return c | |||
| return c, nil | |||
| } | |||
| // retryHTTPCheck provides a callback for Client.CheckRetry which | |||
| // will retry both rate limit (429) and server (>= 500) errors. | |||
| func (c *Client) retryHTTPCheck(ctx context.Context, resp *http.Response, err error) (bool, error) { | |||
| if ctx.Err() != nil { | |||
| return false, ctx.Err() | |||
| } | |||
| if err != nil { | |||
| return false, err | |||
| } | |||
| if !c.disableRetries && (resp.StatusCode == 429 || resp.StatusCode >= 500) { | |||
| return true, nil | |||
| } | |||
| return false, nil | |||
| } | |||
| // retryHTTPBackoff provides a generic callback for Client.Backoff which | |||
| // will pass through all calls based on the status code of the response. | |||
| func (c *Client) retryHTTPBackoff(min, max time.Duration, attemptNum int, resp *http.Response) time.Duration { | |||
| // Use the rate limit backoff function when we are rate limited. | |||
| if resp != nil && resp.StatusCode == 429 { | |||
| return rateLimitBackoff(min, max, attemptNum, resp) | |||
| } | |||
| // Set custom duration's when we experience a service interruption. | |||
| min = 700 * time.Millisecond | |||
| max = 900 * time.Millisecond | |||
| return retryablehttp.LinearJitterBackoff(min, max, attemptNum, resp) | |||
| } | |||
| // rateLimitBackoff provides a callback for Client.Backoff which will use the | |||
| // RateLimit-Reset header to determine the time to wait. We add some jitter | |||
| // to prevent a thundering herd. | |||
| // | |||
| // min and max are mainly used for bounding the jitter that will be added to | |||
| // the reset time retrieved from the headers. But if the final wait time is | |||
| // less then min, min will be used instead. | |||
| func rateLimitBackoff(min, max time.Duration, attemptNum int, resp *http.Response) time.Duration { | |||
| // rnd is used to generate pseudo-random numbers. | |||
| rnd := rand.New(rand.NewSource(time.Now().UnixNano())) | |||
| // First create some jitter bounded by the min and max durations. | |||
| jitter := time.Duration(rnd.Float64() * float64(max-min)) | |||
| if resp != nil { | |||
| if v := resp.Header.Get(headerRateReset); v != "" { | |||
| if reset, _ := strconv.ParseInt(v, 10, 64); reset > 0 { | |||
| // Only update min if the given time to wait is longer. | |||
| if wait := time.Until(time.Unix(reset, 0)); wait > min { | |||
| min = wait | |||
| } | |||
| } | |||
| } | |||
| } | |||
| return min + jitter | |||
| } | |||
| // configureLimiter configures the rate limiter. | |||
| func (c *Client) configureLimiter() error { | |||
| // Set default values for when rate limiting is disabled. | |||
| limit := rate.Inf | |||
| burst := 0 | |||
| defer func() { | |||
| // Create a new limiter using the calculated values. | |||
| c.limiter = rate.NewLimiter(limit, burst) | |||
| }() | |||
| // Create a new request. | |||
| req, err := http.NewRequest("GET", c.baseURL.String(), nil) | |||
| if err != nil { | |||
| return err | |||
| } | |||
| // Make a single request to retrieve the rate limit headers. | |||
| resp, err := c.client.HTTPClient.Do(req) | |||
| if err != nil { | |||
| return err | |||
| } | |||
| resp.Body.Close() | |||
| if v := resp.Header.Get(headerRateLimit); v != "" { | |||
| if rateLimit, _ := strconv.ParseFloat(v, 64); rateLimit > 0 { | |||
| // The rate limit is based on requests per minute, so for our limiter to | |||
| // work correctly we devide the limit by 60 to get the limit per second. | |||
| rateLimit /= 60 | |||
| // Configure the limit and burst using a split of 2/3 for the limit and | |||
| // 1/3 for the burst. This enables clients to burst 1/3 of the allowed | |||
| // calls before the limiter kicks in. The remaining calls will then be | |||
| // spread out evenly using intervals of time.Second / limit which should | |||
| // prevent hitting the rate limit. | |||
| limit = rate.Limit(rateLimit * 0.66) | |||
| burst = int(rateLimit * 0.33) | |||
| } | |||
| } | |||
| return nil | |||
| } | |||
| // BaseURL return a copy of the baseURL. | |||
| @@ -530,9 +713,8 @@ func (c *Client) BaseURL() *url.URL { | |||
| return &u | |||
| } | |||
| // SetBaseURL sets the base URL for API requests to a custom endpoint. urlStr | |||
| // should always be specified with a trailing slash. | |||
| func (c *Client) SetBaseURL(urlStr string) error { | |||
| // setBaseURL sets the base URL for API requests to a custom endpoint. | |||
| func (c *Client) setBaseURL(urlStr string) error { | |||
| // Make sure the given URL end with a slash | |||
| if !strings.HasSuffix(urlStr, "/") { | |||
| urlStr += "/" | |||
| @@ -554,11 +736,11 @@ func (c *Client) SetBaseURL(urlStr string) error { | |||
| } | |||
| // NewRequest creates an API request. A relative URL path can be provided in | |||
| // urlStr, in which case it is resolved relative to the base URL of the Client. | |||
| // path, in which case it is resolved relative to the base URL of the Client. | |||
| // Relative URL paths should always be specified without a preceding slash. If | |||
| // specified, the value pointed to by body is JSON encoded and included as the | |||
| // request body. | |||
| func (c *Client) NewRequest(method, path string, opt interface{}, options []OptionFunc) (*http.Request, error) { | |||
| func (c *Client) NewRequest(method, path string, opt interface{}, options []RequestOptionFunc) (*retryablehttp.Request, error) { | |||
| u := *c.baseURL | |||
| unescaped, err := url.PathUnescape(path) | |||
| if err != nil { | |||
| @@ -569,7 +751,33 @@ func (c *Client) NewRequest(method, path string, opt interface{}, options []Opti | |||
| u.RawPath = c.baseURL.Path + path | |||
| u.Path = c.baseURL.Path + unescaped | |||
| if opt != nil { | |||
| // Create a request specific headers map. | |||
| reqHeaders := make(http.Header) | |||
| reqHeaders.Set("Accept", "application/json") | |||
| switch c.authType { | |||
| case basicAuth, oAuthToken: | |||
| reqHeaders.Set("Authorization", "Bearer "+c.token) | |||
| case privateToken: | |||
| reqHeaders.Set("PRIVATE-TOKEN", c.token) | |||
| } | |||
| if c.UserAgent != "" { | |||
| reqHeaders.Set("User-Agent", c.UserAgent) | |||
| } | |||
| var body interface{} | |||
| switch { | |||
| case method == "POST" || method == "PUT": | |||
| reqHeaders.Set("Content-Type", "application/json") | |||
| if opt != nil { | |||
| body, err = json.Marshal(opt) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| } | |||
| case opt != nil: | |||
| q, err := query.Values(opt) | |||
| if err != nil { | |||
| return nil, err | |||
| @@ -577,53 +785,23 @@ func (c *Client) NewRequest(method, path string, opt interface{}, options []Opti | |||
| u.RawQuery = q.Encode() | |||
| } | |||
| req := &http.Request{ | |||
| Method: method, | |||
| URL: &u, | |||
| Proto: "HTTP/1.1", | |||
| ProtoMajor: 1, | |||
| ProtoMinor: 1, | |||
| Header: make(http.Header), | |||
| Host: u.Host, | |||
| req, err := retryablehttp.NewRequest(method, u.String(), body) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| for _, fn := range options { | |||
| if fn == nil { | |||
| continue | |||
| } | |||
| if err := fn(req); err != nil { | |||
| return nil, err | |||
| } | |||
| } | |||
| if method == "POST" || method == "PUT" { | |||
| bodyBytes, err := json.Marshal(opt) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| bodyReader := bytes.NewReader(bodyBytes) | |||
| u.RawQuery = "" | |||
| req.Body = ioutil.NopCloser(bodyReader) | |||
| req.GetBody = func() (io.ReadCloser, error) { | |||
| return ioutil.NopCloser(bodyReader), nil | |||
| } | |||
| req.ContentLength = int64(bodyReader.Len()) | |||
| req.Header.Set("Content-Type", "application/json") | |||
| } | |||
| req.Header.Set("Accept", "application/json") | |||
| switch c.authType { | |||
| case basicAuth, oAuthToken: | |||
| req.Header.Set("Authorization", "Bearer "+c.token) | |||
| case privateToken: | |||
| req.Header.Set("PRIVATE-TOKEN", c.token) | |||
| } | |||
| if c.UserAgent != "" { | |||
| req.Header.Set("User-Agent", c.UserAgent) | |||
| // Set the request specific headers. | |||
| for k, v := range reqHeaders { | |||
| req.Header[k] = v | |||
| } | |||
| return req, nil | |||
| @@ -691,7 +869,16 @@ func (r *Response) populatePageValues() { | |||
| // error if an API error has occurred. If v implements the io.Writer | |||
| // interface, the raw response body will be written to v, without attempting to | |||
| // first decode it. | |||
| func (c *Client) Do(req *http.Request, v interface{}) (*Response, error) { | |||
| func (c *Client) Do(req *retryablehttp.Request, v interface{}) (*Response, error) { | |||
| // If not yet configured, try to configure the rate limiter. Fail | |||
| // silently as the limiter will be disabled in case of an error. | |||
| c.configureLimiterOnce.Do(func() { c.configureLimiter() }) | |||
| // Wait will block until the limiter can obtain a new token. | |||
| if err := c.limiter.Wait(req.Context()); err != nil { | |||
| return nil, err | |||
| } | |||
| resp, err := c.client.Do(req) | |||
| if err != nil { | |||
| return nil, err | |||
| @@ -710,8 +897,8 @@ func (c *Client) Do(req *http.Request, v interface{}) (*Response, error) { | |||
| err = CheckResponse(resp) | |||
| if err != nil { | |||
| // even though there was an error, we still return the response | |||
| // in case the caller wants to inspect it further | |||
| // Even though there was an error, we still return the response | |||
| // in case the caller wants to inspect it further. | |||
| return response, err | |||
| } | |||
| @@ -826,32 +1013,6 @@ func parseError(raw interface{}) string { | |||
| } | |||
| } | |||
| // OptionFunc can be passed to all API requests to make the API call as if you were | |||
| // another user, provided your private token is from an administrator account. | |||
| // | |||
| // GitLab docs: https://docs.gitlab.com/ce/api/README.html#sudo | |||
| type OptionFunc func(*http.Request) error | |||
| // WithSudo takes either a username or user ID and sets the SUDO request header | |||
| func WithSudo(uid interface{}) OptionFunc { | |||
| return func(req *http.Request) error { | |||
| user, err := parseID(uid) | |||
| if err != nil { | |||
| return err | |||
| } | |||
| req.Header.Set("SUDO", user) | |||
| return nil | |||
| } | |||
| } | |||
| // WithContext runs the request with the provided context | |||
| func WithContext(ctx context.Context) OptionFunc { | |||
| return func(req *http.Request) error { | |||
| *req = *req.WithContext(ctx) | |||
| return nil | |||
| } | |||
| } | |||
| // Bool is a helper routine that allocates a new bool value | |||
| // to store v and returns a pointer to it. | |||
| func Bool(v bool) *bool { | |||
| @@ -901,6 +1062,14 @@ func BuildState(v BuildStateValue) *BuildStateValue { | |||
| return p | |||
| } | |||
| // DeploymentStatus is a helper routine that allocates a new | |||
| // DeploymentStatusValue to store v and returns a pointer to it. | |||
| func DeploymentStatus(v DeploymentStatusValue) *DeploymentStatusValue { | |||
| p := new(DeploymentStatusValue) | |||
| *p = v | |||
| return p | |||
| } | |||
| // NotificationLevel is a helper routine that allocates a new NotificationLevelValue | |||
| // to store v and returns a pointer to it. | |||
| func NotificationLevel(v NotificationLevelValue) *NotificationLevelValue { | |||
| @@ -925,6 +1094,22 @@ func Visibility(v VisibilityValue) *VisibilityValue { | |||
| return p | |||
| } | |||
| // ProjectCreationLevel is a helper routine that allocates a new ProjectCreationLevelValue | |||
| // to store v and returns a pointer to it. | |||
| func ProjectCreationLevel(v ProjectCreationLevelValue) *ProjectCreationLevelValue { | |||
| p := new(ProjectCreationLevelValue) | |||
| *p = v | |||
| return p | |||
| } | |||
| // SubGroupCreationLevel is a helper routine that allocates a new SubGroupCreationLevelValue | |||
| // to store v and returns a pointer to it. | |||
| func SubGroupCreationLevel(v SubGroupCreationLevelValue) *SubGroupCreationLevelValue { | |||
| p := new(SubGroupCreationLevelValue) | |||
| *p = v | |||
| return p | |||
| } | |||
| // MergeMethod is a helper routine that allocates a new MergeMethod | |||
| // to sotre v and returns a pointer to it. | |||
| func MergeMethod(v MergeMethodValue) *MergeMethodValue { | |||
| @@ -2,10 +2,13 @@ module github.com/xanzy/go-gitlab | |||
| require ( | |||
| github.com/google/go-querystring v1.0.0 | |||
| github.com/hashicorp/go-cleanhttp v0.5.1 | |||
| github.com/hashicorp/go-retryablehttp v0.6.4 | |||
| github.com/stretchr/testify v1.4.0 | |||
| golang.org/x/net v0.0.0-20181108082009-03003ca0c849 // indirect | |||
| golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288 | |||
| golang.org/x/sync v0.0.0-20181108010431-42b317875d0f // indirect | |||
| golang.org/x/time v0.0.0-20191024005414-555d28b269f0 | |||
| google.golang.org/appengine v1.3.0 // indirect | |||
| ) | |||
| @@ -1,12 +1,21 @@ | |||
| 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/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | |||
| github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | |||
| github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= | |||
| github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | |||
| github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= | |||
| github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= | |||
| github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= | |||
| github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= | |||
| github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI= | |||
| github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= | |||
| github.com/hashicorp/go-retryablehttp v0.6.4 h1:BbgctKO892xEyOXnGiaAwIoSq1QZ/SS4AhjoAh9DnfY= | |||
| github.com/hashicorp/go-retryablehttp v0.6.4/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= | |||
| 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.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= | |||
| github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= | |||
| github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= | |||
| golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | |||
| @@ -17,6 +26,8 @@ golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAG | |||
| golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTmV7VDcZyvRZ+QQXkXTZQ= | |||
| golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | |||
| golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | |||
| golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= | |||
| golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= | |||
| google.golang.org/appengine v1.3.0 h1:FBSsiFRMz3LBeXIomRnVzrQwSDj4ibvcRexLG0LZGQk= | |||
| google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= | |||
| gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= | |||
| @@ -44,7 +44,7 @@ type ListGroupBadgesOptions ListOptions | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/group_badges.html#list-all-badges-of-a-group | |||
| func (s *GroupBadgesService) ListGroupBadges(gid interface{}, opt *ListGroupBadgesOptions, options ...OptionFunc) ([]*GroupBadge, *Response, error) { | |||
| func (s *GroupBadgesService) ListGroupBadges(gid interface{}, opt *ListGroupBadgesOptions, options ...RequestOptionFunc) ([]*GroupBadge, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -69,7 +69,7 @@ func (s *GroupBadgesService) ListGroupBadges(gid interface{}, opt *ListGroupBadg | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/group_badges.html#get-a-badge-of-a-group | |||
| func (s *GroupBadgesService) GetGroupBadge(gid interface{}, badge int, options ...OptionFunc) (*GroupBadge, *Response, error) { | |||
| func (s *GroupBadgesService) GetGroupBadge(gid interface{}, badge int, options ...RequestOptionFunc) (*GroupBadge, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -103,7 +103,7 @@ type AddGroupBadgeOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/group_badges.html#add-a-badge-to-a-group | |||
| func (s *GroupBadgesService) AddGroupBadge(gid interface{}, opt *AddGroupBadgeOptions, options ...OptionFunc) (*GroupBadge, *Response, error) { | |||
| func (s *GroupBadgesService) AddGroupBadge(gid interface{}, opt *AddGroupBadgeOptions, options ...RequestOptionFunc) (*GroupBadge, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -137,7 +137,7 @@ type EditGroupBadgeOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/group_badges.html#edit-a-badge-of-a-group | |||
| func (s *GroupBadgesService) EditGroupBadge(gid interface{}, badge int, opt *EditGroupBadgeOptions, options ...OptionFunc) (*GroupBadge, *Response, error) { | |||
| func (s *GroupBadgesService) EditGroupBadge(gid interface{}, badge int, opt *EditGroupBadgeOptions, options ...RequestOptionFunc) (*GroupBadge, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -162,7 +162,7 @@ func (s *GroupBadgesService) EditGroupBadge(gid interface{}, badge int, opt *Edi | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/group_badges.html#remove-a-badge-from-a-group | |||
| func (s *GroupBadgesService) DeleteGroupBadge(gid interface{}, badge int, options ...OptionFunc) (*Response, error) { | |||
| func (s *GroupBadgesService) DeleteGroupBadge(gid interface{}, badge int, options ...RequestOptionFunc) (*Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, err | |||
| @@ -191,7 +191,7 @@ type GroupBadgePreviewOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/group_badges.html#preview-a-badge-from-a-group | |||
| func (s *GroupBadgesService) PreviewGroupBadge(gid interface{}, opt *GroupBadgePreviewOptions, options ...OptionFunc) (*GroupBadge, *Response, error) { | |||
| func (s *GroupBadgesService) PreviewGroupBadge(gid interface{}, opt *GroupBadgePreviewOptions, options ...RequestOptionFunc) (*GroupBadge, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -56,7 +56,7 @@ type ListGroupIssueBoardsOptions ListOptions | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/group_boards.html#group-board | |||
| func (s *GroupIssueBoardsService) ListGroupIssueBoards(gid interface{}, opt *ListGroupIssueBoardsOptions, options ...OptionFunc) ([]*GroupIssueBoard, *Response, error) { | |||
| func (s *GroupIssueBoardsService) ListGroupIssueBoards(gid interface{}, opt *ListGroupIssueBoardsOptions, options ...RequestOptionFunc) ([]*GroupIssueBoard, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -77,11 +77,45 @@ func (s *GroupIssueBoardsService) ListGroupIssueBoards(gid interface{}, opt *Lis | |||
| return gs, resp, err | |||
| } | |||
| // CreateGroupIssueBoardOptions represents the available | |||
| // CreateGroupIssueBoard() options. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/group_boards.html#create-a-group-issue-board-premium | |||
| type CreateGroupIssueBoardOptions struct { | |||
| Name *string `url:"name" json:"name"` | |||
| } | |||
| // CreateGroupIssueBoard creates a new issue board. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/group_boards.html#create-a-group-issue-board-premium | |||
| func (s *GroupIssueBoardsService) CreateGroupIssueBoard(gid interface{}, opt *CreateGroupIssueBoardOptions, options ...RequestOptionFunc) (*GroupIssueBoard, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| } | |||
| u := fmt.Sprintf("groups/%s/boards", pathEscape(group)) | |||
| req, err := s.client.NewRequest("POST", u, opt, options) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| } | |||
| gib := new(GroupIssueBoard) | |||
| resp, err := s.client.Do(req, gib) | |||
| if err != nil { | |||
| return nil, resp, err | |||
| } | |||
| return gib, resp, err | |||
| } | |||
| // GetGroupIssueBoard gets a single issue board of a group. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/group_boards.html#single-board | |||
| func (s *GroupIssueBoardsService) GetGroupIssueBoard(gid interface{}, board int, options ...OptionFunc) (*GroupIssueBoard, *Response, error) { | |||
| func (s *GroupIssueBoardsService) GetGroupIssueBoard(gid interface{}, board int, options ...RequestOptionFunc) (*GroupIssueBoard, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -102,6 +136,62 @@ func (s *GroupIssueBoardsService) GetGroupIssueBoard(gid interface{}, board int, | |||
| return gib, resp, err | |||
| } | |||
| // UpdateGroupIssueBoardOptions represents a group issue board. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/group_boards.html#update-a-group-issue-board-premium | |||
| type UpdateGroupIssueBoardOptions struct { | |||
| Name *string `url:"name,omitempty" json:"name,omitempty"` | |||
| AssigneeID *int `url:"assignee_id,omitempty" json:"assignee_id,omitempty"` | |||
| MilestoneID *int `url:"milestone_id,omitempty" json:"milestone_id,omitempty"` | |||
| Labels *Labels `url:"labels,omitempty" json:"labels,omitempty"` | |||
| Weight *int `url:"weight,omitempty" json:"weight,omitempty"` | |||
| } | |||
| // UpdateIssueBoard updates a single issue board of a group. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/group_boards.html#update-a-group-issue-board-premium | |||
| func (s *GroupIssueBoardsService) UpdateIssueBoard(gid interface{}, board int, opt *UpdateGroupIssueBoardOptions, options ...RequestOptionFunc) (*GroupIssueBoard, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| } | |||
| u := fmt.Sprintf("groups/%s/boards/%d", pathEscape(group), board) | |||
| req, err := s.client.NewRequest("PUT", u, opt, options) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| } | |||
| gib := new(GroupIssueBoard) | |||
| resp, err := s.client.Do(req, gib) | |||
| if err != nil { | |||
| return nil, resp, err | |||
| } | |||
| return gib, resp, err | |||
| } | |||
| // DeleteIssueBoard delete a single issue board of a group. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/group_boards.html#delete-a-group-issue-board-premium | |||
| func (s *GroupIssueBoardsService) DeleteIssueBoard(gid interface{}, board int, options ...RequestOptionFunc) (*Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| u := fmt.Sprintf("groups/%s/boards/%d", pathEscape(group), board) | |||
| req, err := s.client.NewRequest("DELETE", u, nil, options) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| return s.client.Do(req, nil) | |||
| } | |||
| // ListGroupIssueBoardListsOptions represents the available | |||
| // ListGroupIssueBoardLists() options. | |||
| // | |||
| @@ -113,7 +203,7 @@ type ListGroupIssueBoardListsOptions ListOptions | |||
| // backlog and closed lists. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/group_boards.html#list-board-lists | |||
| func (s *GroupIssueBoardsService) ListGroupIssueBoardLists(gid interface{}, board int, opt *ListGroupIssueBoardListsOptions, options ...OptionFunc) ([]*BoardList, *Response, error) { | |||
| func (s *GroupIssueBoardsService) ListGroupIssueBoardLists(gid interface{}, board int, opt *ListGroupIssueBoardListsOptions, options ...RequestOptionFunc) ([]*BoardList, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -138,7 +228,7 @@ func (s *GroupIssueBoardsService) ListGroupIssueBoardLists(gid interface{}, boar | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/group_boards.html#single-board-list | |||
| func (s *GroupIssueBoardsService) GetGroupIssueBoardList(gid interface{}, board, list int, options ...OptionFunc) (*BoardList, *Response, error) { | |||
| func (s *GroupIssueBoardsService) GetGroupIssueBoardList(gid interface{}, board, list int, options ...RequestOptionFunc) (*BoardList, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -176,7 +266,7 @@ type CreateGroupIssueBoardListOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/group_boards.html#new-board-list | |||
| func (s *GroupIssueBoardsService) CreateGroupIssueBoardList(gid interface{}, board int, opt *CreateGroupIssueBoardListOptions, options ...OptionFunc) (*BoardList, *Response, error) { | |||
| func (s *GroupIssueBoardsService) CreateGroupIssueBoardList(gid interface{}, board int, opt *CreateGroupIssueBoardListOptions, options ...RequestOptionFunc) (*BoardList, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -211,7 +301,7 @@ type UpdateGroupIssueBoardListOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/group_boards.html#edit-board-list | |||
| func (s *GroupIssueBoardsService) UpdateIssueBoardList(gid interface{}, board, list int, opt *UpdateGroupIssueBoardListOptions, options ...OptionFunc) ([]*BoardList, *Response, error) { | |||
| func (s *GroupIssueBoardsService) UpdateIssueBoardList(gid interface{}, board, list int, opt *UpdateGroupIssueBoardListOptions, options ...RequestOptionFunc) ([]*BoardList, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -241,7 +331,7 @@ func (s *GroupIssueBoardsService) UpdateIssueBoardList(gid interface{}, board, l | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/group_boards.html#delete-a-board-list | |||
| func (s *GroupIssueBoardsService) DeleteGroupIssueBoardList(gid interface{}, board, list int, options ...OptionFunc) (*Response, error) { | |||
| func (s *GroupIssueBoardsService) DeleteGroupIssueBoardList(gid interface{}, board, list int, options ...RequestOptionFunc) (*Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, err | |||
| @@ -44,6 +44,7 @@ type GroupCluster struct { | |||
| ClusterType string `json:"cluster_type"` | |||
| User *User `json:"user"` | |||
| PlatformKubernetes *PlatformKubernetes `json:"platform_kubernetes"` | |||
| ManagementProject *ManagementProject `json:"management_project"` | |||
| Group *Group `json:"group"` | |||
| } | |||
| @@ -55,7 +56,7 @@ func (v GroupCluster) String() string { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/group_clusters.html#list-group-clusters | |||
| func (s *GroupClustersService) ListClusters(pid interface{}, options ...OptionFunc) ([]*GroupCluster, *Response, error) { | |||
| func (s *GroupClustersService) ListClusters(pid interface{}, options ...RequestOptionFunc) ([]*GroupCluster, *Response, error) { | |||
| group, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -80,7 +81,7 @@ func (s *GroupClustersService) ListClusters(pid interface{}, options ...OptionFu | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/group_clusters.html#get-a-single-group-cluster | |||
| func (s *GroupClustersService) GetCluster(pid interface{}, cluster int, options ...OptionFunc) (*GroupCluster, *Response, error) { | |||
| func (s *GroupClustersService) GetCluster(pid interface{}, cluster int, options ...RequestOptionFunc) (*GroupCluster, *Response, error) { | |||
| group, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -106,12 +107,13 @@ func (s *GroupClustersService) GetCluster(pid interface{}, cluster int, options | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/group_clusters.html#add-existing-cluster-to-group | |||
| type AddGroupClusterOptions struct { | |||
| Name *string `url:"name,omitempty" json:"name,omitempty"` | |||
| Domain *string `url:"domain,omitempty" json:"domain,omitempty"` | |||
| Enabled *bool `url:"enabled,omitempty" json:"enabled,omitempty"` | |||
| Managed *bool `url:"managed,omitempty" json:"managed,omitempty"` | |||
| EnvironmentScope *string `url:"environment_scope,omitempty" json:"environment_scope,omitempty"` | |||
| PlatformKubernetes *AddGroupPlatformKubernetesOptions `url:"platform_kubernetes_attributes,omitempty" json:"platform_kubernetes_attributes,omitempty"` | |||
| Name *string `url:"name,omitempty" json:"name,omitempty"` | |||
| Domain *string `url:"domain,omitempty" json:"domain,omitempty"` | |||
| ManagementProjectID *string `url:"management_project_id,omitempty" json:"management_project_id,omitempty"` | |||
| Enabled *bool `url:"enabled,omitempty" json:"enabled,omitempty"` | |||
| Managed *bool `url:"managed,omitempty" json:"managed,omitempty"` | |||
| EnvironmentScope *string `url:"environment_scope,omitempty" json:"environment_scope,omitempty"` | |||
| PlatformKubernetes *AddGroupPlatformKubernetesOptions `url:"platform_kubernetes_attributes,omitempty" json:"platform_kubernetes_attributes,omitempty"` | |||
| } | |||
| // AddGroupPlatformKubernetesOptions represents the available PlatformKubernetes options for adding. | |||
| @@ -127,7 +129,7 @@ type AddGroupPlatformKubernetesOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/group_clusters.html#add-existing-cluster-to-group | |||
| func (s *GroupClustersService) AddCluster(pid interface{}, opt *AddGroupClusterOptions, options ...OptionFunc) (*GroupCluster, *Response, error) { | |||
| func (s *GroupClustersService) AddCluster(pid interface{}, opt *AddGroupClusterOptions, options ...RequestOptionFunc) (*GroupCluster, *Response, error) { | |||
| group, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -153,10 +155,11 @@ func (s *GroupClustersService) AddCluster(pid interface{}, opt *AddGroupClusterO | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/group_clusters.html#edit-group-cluster | |||
| type EditGroupClusterOptions struct { | |||
| Name *string `url:"name,omitempty" json:"name,omitempty"` | |||
| Domain *string `url:"domain,omitempty" json:"domain,omitempty"` | |||
| EnvironmentScope *string `url:"environment_scope,omitempty" json:"environment_scope,omitempty"` | |||
| PlatformKubernetes *EditGroupPlatformKubernetesOptions `url:"platform_kubernetes_attributes,omitempty" json:"platform_kubernetes_attributes,omitempty"` | |||
| Name *string `url:"name,omitempty" json:"name,omitempty"` | |||
| Domain *string `url:"domain,omitempty" json:"domain,omitempty"` | |||
| EnvironmentScope *string `url:"environment_scope,omitempty" json:"environment_scope,omitempty"` | |||
| PlatformKubernetes *EditGroupPlatformKubernetesOptions `url:"platform_kubernetes_attributes,omitempty" json:"platform_kubernetes_attributes,omitempty"` | |||
| ManagementProjectID *string `url:"management_project_id,omitempty" json:"management_project_id,omitempty"` | |||
| } | |||
| // EditGroupPlatformKubernetesOptions represents the available PlatformKubernetes options for editing. | |||
| @@ -170,7 +173,7 @@ type EditGroupPlatformKubernetesOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/group_clusters.html#edit-group-cluster | |||
| func (s *GroupClustersService) EditCluster(pid interface{}, cluster int, opt *EditGroupClusterOptions, options ...OptionFunc) (*GroupCluster, *Response, error) { | |||
| func (s *GroupClustersService) EditCluster(pid interface{}, cluster int, opt *EditGroupClusterOptions, options ...RequestOptionFunc) (*GroupCluster, *Response, error) { | |||
| group, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -195,7 +198,7 @@ func (s *GroupClustersService) EditCluster(pid interface{}, cluster int, opt *Ed | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/group_clusters.html#delete-group-cluster | |||
| func (s *GroupClustersService) DeleteCluster(pid interface{}, cluster int, options ...OptionFunc) (*Response, error) { | |||
| func (s *GroupClustersService) DeleteCluster(pid interface{}, cluster int, options ...RequestOptionFunc) (*Response, error) { | |||
| group, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, err | |||
| @@ -0,0 +1,199 @@ | |||
| // | |||
| // Copyright 2020, Eric Stevens | |||
| // | |||
| // 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 gitlab | |||
| import ( | |||
| "fmt" | |||
| "time" | |||
| ) | |||
| // GroupHook represents a GitLab group hook. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#list-group-hooks | |||
| type GroupHook struct { | |||
| ID int `json:"id"` | |||
| URL string `json:"url"` | |||
| GroupID int `json:"group_id"` | |||
| PushEvents bool `json:"push_events"` | |||
| IssuesEvents bool `json:"issues_events"` | |||
| ConfidentialIssuesEvents bool `json:"confidential_issues_events"` | |||
| ConfidentialNoteEvents bool `json:"confidential_note_events"` | |||
| MergeRequestsEvents bool `json:"merge_requests_events"` | |||
| TagPushEvents bool `json:"tag_push_events"` | |||
| NoteEvents bool `json:"note_events"` | |||
| JobEvents bool `json:"job_events"` | |||
| PipelineEvents bool `json:"pipeline_events"` | |||
| WikiPageEvents bool `json:"wiki_page_events"` | |||
| EnableSSLVerification bool `json:"enable_ssl_verification"` | |||
| CreatedAt *time.Time `json:"created_at"` | |||
| } | |||
| // ListGroupHooks gets a list of group hooks. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#list-group-hooks | |||
| func (s *GroupsService) ListGroupHooks(gid interface{}) ([]*GroupHook, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| } | |||
| u := fmt.Sprintf("groups/%s/hooks", pathEscape(group)) | |||
| req, err := s.client.NewRequest("GET", u, nil, nil) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| } | |||
| var gh []*GroupHook | |||
| resp, err := s.client.Do(req, &gh) | |||
| if err != nil { | |||
| return nil, resp, err | |||
| } | |||
| return gh, resp, err | |||
| } | |||
| // GetGroupHook gets a specific hook for a group. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/groups.html#get-group-hook | |||
| func (s *GroupsService) GetGroupHook(pid interface{}, hook int, options ...RequestOptionFunc) (*GroupHook, *Response, error) { | |||
| group, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| } | |||
| u := fmt.Sprintf("groups/%s/hooks/%d", pathEscape(group), hook) | |||
| req, err := s.client.NewRequest("GET", u, nil, options) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| } | |||
| gh := new(GroupHook) | |||
| resp, err := s.client.Do(req, gh) | |||
| if err != nil { | |||
| return nil, resp, err | |||
| } | |||
| return gh, resp, err | |||
| } | |||
| // AddGroupHookOptions represents the available AddGroupHook() options. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ee/api/groups.html#add-group-hook | |||
| type AddGroupHookOptions struct { | |||
| URL *string `url:"url,omitempty" json:"url,omitempty"` | |||
| PushEvents *bool `url:"push_events,omitempty" json:"push_events,omitempty"` | |||
| IssuesEvents *bool `url:"issues_events,omitempty" json:"issues_events,omitempty"` | |||
| ConfidentialIssuesEvents *bool `url:"confidential_issues_events,omitempty" json:"confidential_issues_events,omitempty"` | |||
| ConfidentialNoteEvents *bool `url:"confidential_note_events,omitempty" json:"confidential_note_events,omitempty"` | |||
| MergeRequestsEvents *bool `url:"merge_requests_events,omitempty" json:"merge_requests_events,omitempty"` | |||
| TagPushEvents *bool `url:"tag_push_events,omitempty" json:"tag_push_events,omitempty"` | |||
| NoteEvents *bool `url:"note_events,omitempty" json:"note_events,omitempty"` | |||
| JobEvents *bool `url:"job_events,omitempty" json:"job_events,omitempty"` | |||
| PipelineEvents *bool `url:"pipeline_events,omitempty" json:"pipeline_events,omitempty"` | |||
| WikiPageEvents *bool `url:"wiki_page_events,omitempty" json:"wiki_page_events,omitempty"` | |||
| EnableSSLVerification *bool `url:"enable_ssl_verification,omitempty" json:"enable_ssl_verification,omitempty"` | |||
| Token *string `url:"token,omitempty" json:"token,omitempty"` | |||
| } | |||
| // AddGroupHook create a new group scoped webhook. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ee/api/groups.html#add-group-hook | |||
| func (s *GroupsService) AddGroupHook(gid interface{}, opt *AddGroupHookOptions, options ...RequestOptionFunc) (*GroupHook, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| } | |||
| u := fmt.Sprintf("groups/%s/hooks", pathEscape(group)) | |||
| req, err := s.client.NewRequest("POST", u, opt, options) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| } | |||
| gh := new(GroupHook) | |||
| resp, err := s.client.Do(req, gh) | |||
| if err != nil { | |||
| return nil, resp, err | |||
| } | |||
| return gh, resp, err | |||
| } | |||
| // EditGroupHookOptions represents the available EditGroupHook() options. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/groups.html#edit-group-hook | |||
| type EditGroupHookOptions struct { | |||
| URL *string `url:"url,omitempty" json:"url,omitempty"` | |||
| PushEvents *bool `url:"push_events,omitempty" json:"push_events,omitempty"` | |||
| IssuesEvents *bool `url:"issues_events,omitempty" json:"issues_events,omitempty"` | |||
| ConfidentialIssuesEvents *bool `url:"confidential_issues_events,omitempty" json:"confidential_issues_events,omitempty"` | |||
| ConfidentialNoteEvents *bool `url:"confidential_note_events,omitempty" json:"confidential_note_events,omitempty"` | |||
| MergeRequestsEvents *bool `url:"merge_requests_events,omitempty" json:"merge_requests_events,omitempty"` | |||
| TagPushEvents *bool `url:"tag_push_events,omitempty" json:"tag_push_events,omitempty"` | |||
| NoteEvents *bool `url:"note_events,omitempty" json:"note_events,omitempty"` | |||
| JobEvents *bool `url:"job_events,omitempty" json:"job_events,omitempty"` | |||
| PipelineEvents *bool `url:"pipeline_events,omitempty" json:"pipeline_events,omitempty"` | |||
| WikiPageEvents *bool `url:"wiki_page_events,omitempty" json:"wiki_page_events,omitempty"` | |||
| EnableSSLVerification *bool `url:"enable_ssl_verification,omitempty" json:"enable_ssl_verification,omitempty"` | |||
| Token *string `url:"token,omitempty" json:"token,omitempty"` | |||
| } | |||
| // EditGroupHook edits a hook for a specified group. | |||
| // | |||
| // Gitlab API docs: | |||
| // https://docs.gitlab.com/ce/api/groups.html#edit-group-hook | |||
| func (s *GroupsService) EditGroupHook(pid interface{}, hook int, opt *EditGroupHookOptions, options ...RequestOptionFunc) (*GroupHook, *Response, error) { | |||
| group, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| } | |||
| u := fmt.Sprintf("groups/%s/hooks/%d", pathEscape(group), hook) | |||
| req, err := s.client.NewRequest("PUT", u, opt, options) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| } | |||
| gh := new(GroupHook) | |||
| resp, err := s.client.Do(req, gh) | |||
| if err != nil { | |||
| return nil, resp, err | |||
| } | |||
| return gh, resp, err | |||
| } | |||
| // DeleteGroupHook removes a hook from a group. This is an idempotent | |||
| // method and can be called multiple times. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/groups.html#delete-group-hook | |||
| func (s *GroupsService) DeleteGroupHook(pid interface{}, hook int, options ...RequestOptionFunc) (*Response, error) { | |||
| group, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| u := fmt.Sprintf("groups/%s/hooks/%d", pathEscape(group), hook) | |||
| req, err := s.client.NewRequest("DELETE", u, nil, options) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| return s.client.Do(req, nil) | |||
| } | |||
| @@ -30,7 +30,7 @@ type ListGroupLabelsOptions ListOptions | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/group_labels.html#list-group-labels | |||
| func (s *GroupLabelsService) ListGroupLabels(gid interface{}, opt *ListGroupLabelsOptions, options ...OptionFunc) ([]*GroupLabel, *Response, error) { | |||
| func (s *GroupLabelsService) ListGroupLabels(gid interface{}, opt *ListGroupLabelsOptions, options ...RequestOptionFunc) ([]*GroupLabel, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -62,7 +62,7 @@ type CreateGroupLabelOptions CreateLabelOptions | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/group_labels.html#create-a-new-group-label | |||
| func (s *GroupLabelsService) CreateGroupLabel(gid interface{}, opt *CreateGroupLabelOptions, options ...OptionFunc) (*GroupLabel, *Response, error) { | |||
| func (s *GroupLabelsService) CreateGroupLabel(gid interface{}, opt *CreateGroupLabelOptions, options ...RequestOptionFunc) (*GroupLabel, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -92,7 +92,7 @@ type DeleteGroupLabelOptions DeleteLabelOptions | |||
| // DeleteGroupLabel deletes a group label given by its name. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/labels.html#delete-a-label | |||
| func (s *GroupLabelsService) DeleteGroupLabel(gid interface{}, opt *DeleteGroupLabelOptions, options ...OptionFunc) (*Response, error) { | |||
| func (s *GroupLabelsService) DeleteGroupLabel(gid interface{}, opt *DeleteGroupLabelOptions, options ...RequestOptionFunc) (*Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, err | |||
| @@ -118,7 +118,7 @@ type UpdateGroupLabelOptions UpdateLabelOptions | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/group_labels.html#update-a-group-label | |||
| func (s *GroupLabelsService) UpdateGroupLabel(gid interface{}, opt *UpdateGroupLabelOptions, options ...OptionFunc) (*GroupLabel, *Response, error) { | |||
| func (s *GroupLabelsService) UpdateGroupLabel(gid interface{}, opt *UpdateGroupLabelOptions, options ...RequestOptionFunc) (*GroupLabel, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -145,7 +145,7 @@ func (s *GroupLabelsService) UpdateGroupLabel(gid interface{}, opt *UpdateGroupL | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/group_labels.html#subscribe-to-a-group-label | |||
| func (s *GroupLabelsService) SubscribeToGroupLabel(gid interface{}, labelID interface{}, options ...OptionFunc) (*GroupLabel, *Response, error) { | |||
| func (s *GroupLabelsService) SubscribeToGroupLabel(gid interface{}, labelID interface{}, options ...RequestOptionFunc) (*GroupLabel, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -176,7 +176,7 @@ func (s *GroupLabelsService) SubscribeToGroupLabel(gid interface{}, labelID inte | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/group_labels.html#unsubscribe-from-a-group-label | |||
| func (s *GroupLabelsService) UnsubscribeFromGroupLabel(gid interface{}, labelID interface{}, options ...OptionFunc) (*Response, error) { | |||
| func (s *GroupLabelsService) UnsubscribeFromGroupLabel(gid interface{}, labelID interface{}, options ...RequestOptionFunc) (*Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, err | |||
| @@ -28,18 +28,30 @@ type GroupMembersService struct { | |||
| client *Client | |||
| } | |||
| // GroupMemberSAMLIdentity represents the SAML Identity link for the group member. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/members.html#list-all-members-of-a-group-or-project | |||
| // Gitlab MR for API change: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/20357 | |||
| // Gitlab MR for API Doc change: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25652 | |||
| type GroupMemberSAMLIdentity struct { | |||
| ExternUID string `json:"extern_uid"` | |||
| Provider string `json:"provider"` | |||
| SAMLProviderID int `json:"saml_provider_id"` | |||
| } | |||
| // GroupMember represents a GitLab group member. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/members.html | |||
| type GroupMember struct { | |||
| ID int `json:"id"` | |||
| Username string `json:"username"` | |||
| Name string `json:"name"` | |||
| State string `json:"state"` | |||
| AvatarURL string `json:"avatar_url"` | |||
| WebURL string `json:"web_url"` | |||
| ExpiresAt *ISOTime `json:"expires_at"` | |||
| AccessLevel AccessLevelValue `json:"access_level"` | |||
| ID int `json:"id"` | |||
| Username string `json:"username"` | |||
| Name string `json:"name"` | |||
| State string `json:"state"` | |||
| AvatarURL string `json:"avatar_url"` | |||
| WebURL string `json:"web_url"` | |||
| ExpiresAt *ISOTime `json:"expires_at"` | |||
| AccessLevel AccessLevelValue `json:"access_level"` | |||
| GroupSAMLIdentity *GroupMemberSAMLIdentity `json:"group_saml_identity"` | |||
| } | |||
| // ListGroupMembersOptions represents the available ListGroupMembers() and | |||
| @@ -57,7 +69,7 @@ type ListGroupMembersOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/members.html#list-all-members-of-a-group-or-project | |||
| func (s *GroupsService) ListGroupMembers(gid interface{}, opt *ListGroupMembersOptions, options ...OptionFunc) ([]*GroupMember, *Response, error) { | |||
| func (s *GroupsService) ListGroupMembers(gid interface{}, opt *ListGroupMembersOptions, options ...RequestOptionFunc) ([]*GroupMember, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -83,7 +95,7 @@ func (s *GroupsService) ListGroupMembers(gid interface{}, opt *ListGroupMembersO | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/members.html#list-all-members-of-a-group-or-project-including-inherited-members | |||
| func (s *GroupsService) ListAllGroupMembers(gid interface{}, opt *ListGroupMembersOptions, options ...OptionFunc) ([]*GroupMember, *Response, error) { | |||
| func (s *GroupsService) ListAllGroupMembers(gid interface{}, opt *ListGroupMembersOptions, options ...RequestOptionFunc) ([]*GroupMember, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -118,7 +130,7 @@ type AddGroupMemberOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/members.html#get-a-member-of-a-group-or-project | |||
| func (s *GroupMembersService) GetGroupMember(gid interface{}, user int, options ...OptionFunc) (*GroupMember, *Response, error) { | |||
| func (s *GroupMembersService) GetGroupMember(gid interface{}, user int, options ...RequestOptionFunc) (*GroupMember, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -143,7 +155,7 @@ func (s *GroupMembersService) GetGroupMember(gid interface{}, user int, options | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/members.html#add-a-member-to-a-group-or-project | |||
| func (s *GroupMembersService) AddGroupMember(gid interface{}, opt *AddGroupMemberOptions, options ...OptionFunc) (*GroupMember, *Response, error) { | |||
| func (s *GroupMembersService) AddGroupMember(gid interface{}, opt *AddGroupMemberOptions, options ...RequestOptionFunc) (*GroupMember, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -178,7 +190,7 @@ type EditGroupMemberOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/members.html#edit-a-member-of-a-group-or-project | |||
| func (s *GroupMembersService) EditGroupMember(gid interface{}, user int, opt *EditGroupMemberOptions, options ...OptionFunc) (*GroupMember, *Response, error) { | |||
| func (s *GroupMembersService) EditGroupMember(gid interface{}, user int, opt *EditGroupMemberOptions, options ...RequestOptionFunc) (*GroupMember, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -203,7 +215,7 @@ func (s *GroupMembersService) EditGroupMember(gid interface{}, user int, opt *Ed | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/members.html#remove-a-member-from-a-group-or-project | |||
| func (s *GroupMembersService) RemoveGroupMember(gid interface{}, user int, options ...OptionFunc) (*Response, error) { | |||
| func (s *GroupMembersService) RemoveGroupMember(gid interface{}, user int, options ...RequestOptionFunc) (*Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, err | |||
| @@ -65,7 +65,7 @@ type ListGroupMilestonesOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/group_milestones.html#list-group-milestones | |||
| func (s *GroupMilestonesService) ListGroupMilestones(gid interface{}, opt *ListGroupMilestonesOptions, options ...OptionFunc) ([]*GroupMilestone, *Response, error) { | |||
| func (s *GroupMilestonesService) ListGroupMilestones(gid interface{}, opt *ListGroupMilestonesOptions, options ...RequestOptionFunc) ([]*GroupMilestone, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -90,7 +90,7 @@ func (s *GroupMilestonesService) ListGroupMilestones(gid interface{}, opt *ListG | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/group_milestones.html#get-single-milestone | |||
| func (s *GroupMilestonesService) GetGroupMilestone(gid interface{}, milestone int, options ...OptionFunc) (*GroupMilestone, *Response, error) { | |||
| func (s *GroupMilestonesService) GetGroupMilestone(gid interface{}, milestone int, options ...RequestOptionFunc) (*GroupMilestone, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -126,7 +126,7 @@ type CreateGroupMilestoneOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/group_milestones.html#create-new-milestone | |||
| func (s *GroupMilestonesService) CreateGroupMilestone(gid interface{}, opt *CreateGroupMilestoneOptions, options ...OptionFunc) (*GroupMilestone, *Response, error) { | |||
| func (s *GroupMilestonesService) CreateGroupMilestone(gid interface{}, opt *CreateGroupMilestoneOptions, options ...RequestOptionFunc) (*GroupMilestone, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -163,7 +163,7 @@ type UpdateGroupMilestoneOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/group_milestones.html#edit-milestone | |||
| func (s *GroupMilestonesService) UpdateGroupMilestone(gid interface{}, milestone int, opt *UpdateGroupMilestoneOptions, options ...OptionFunc) (*GroupMilestone, *Response, error) { | |||
| func (s *GroupMilestonesService) UpdateGroupMilestone(gid interface{}, milestone int, opt *UpdateGroupMilestoneOptions, options ...RequestOptionFunc) (*GroupMilestone, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -194,7 +194,7 @@ type GetGroupMilestoneIssuesOptions ListOptions | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/group_milestones.html#get-all-issues-assigned-to-a-single-milestone | |||
| func (s *GroupMilestonesService) GetGroupMilestoneIssues(gid interface{}, milestone int, opt *GetGroupMilestoneIssuesOptions, options ...OptionFunc) ([]*Issue, *Response, error) { | |||
| func (s *GroupMilestonesService) GetGroupMilestoneIssues(gid interface{}, milestone int, opt *GetGroupMilestoneIssuesOptions, options ...RequestOptionFunc) ([]*Issue, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -227,7 +227,7 @@ type GetGroupMilestoneMergeRequestsOptions ListOptions | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/group_milestones.html#get-all-merge-requests-assigned-to-a-single-milestone | |||
| func (s *GroupMilestonesService) GetGroupMilestoneMergeRequests(gid interface{}, milestone int, opt *GetGroupMilestoneMergeRequestsOptions, options ...OptionFunc) ([]*MergeRequest, *Response, error) { | |||
| func (s *GroupMilestonesService) GetGroupMilestoneMergeRequests(gid interface{}, milestone int, opt *GetGroupMilestoneMergeRequestsOptions, options ...RequestOptionFunc) ([]*MergeRequest, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -39,6 +39,7 @@ type GroupVariable struct { | |||
| Value string `json:"value"` | |||
| VariableType VariableTypeValue `json:"variable_type"` | |||
| Protected bool `json:"protected"` | |||
| Masked bool `json:"masked"` | |||
| } | |||
| func (v GroupVariable) String() string { | |||
| @@ -56,7 +57,7 @@ type ListGroupVariablesOptions ListOptions | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/group_level_variables.html#list-group-variables | |||
| func (s *GroupVariablesService) ListVariables(gid interface{}, opt *ListGroupVariablesOptions, options ...OptionFunc) ([]*GroupVariable, *Response, error) { | |||
| func (s *GroupVariablesService) ListVariables(gid interface{}, opt *ListGroupVariablesOptions, options ...RequestOptionFunc) ([]*GroupVariable, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -81,7 +82,7 @@ func (s *GroupVariablesService) ListVariables(gid interface{}, opt *ListGroupVar | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/group_level_variables.html#show-variable-details | |||
| func (s *GroupVariablesService) GetVariable(gid interface{}, key string, options ...OptionFunc) (*GroupVariable, *Response, error) { | |||
| func (s *GroupVariablesService) GetVariable(gid interface{}, key string, options ...RequestOptionFunc) (*GroupVariable, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -112,13 +113,14 @@ type CreateGroupVariableOptions struct { | |||
| Value *string `url:"value,omitempty" json:"value,omitempty"` | |||
| VariableType *VariableTypeValue `url:"variable_type,omitempty" json:"variable_type,omitempty"` | |||
| Protected *bool `url:"protected,omitempty" json:"protected,omitempty"` | |||
| Masked *bool `url:"masked,omitempty" json:"masked,omitempty"` | |||
| } | |||
| // CreateVariable creates a new group variable. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/group_level_variables.html#create-variable | |||
| func (s *GroupVariablesService) CreateVariable(gid interface{}, opt *CreateGroupVariableOptions, options ...OptionFunc) (*GroupVariable, *Response, error) { | |||
| func (s *GroupVariablesService) CreateVariable(gid interface{}, opt *CreateGroupVariableOptions, options ...RequestOptionFunc) (*GroupVariable, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -148,6 +150,7 @@ type UpdateGroupVariableOptions struct { | |||
| Value *string `url:"value,omitempty" json:"value,omitempty"` | |||
| VariableType *VariableTypeValue `url:"variable_type,omitempty" json:"variable_type,omitempty"` | |||
| Protected *bool `url:"protected,omitempty" json:"protected,omitempty"` | |||
| Masked *bool `url:"masked,omitempty" json:"masked,omitempty"` | |||
| } | |||
| // UpdateVariable updates the position of an existing | |||
| @@ -155,7 +158,7 @@ type UpdateGroupVariableOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/group_level_variables.html#update-variable | |||
| func (s *GroupVariablesService) UpdateVariable(gid interface{}, key string, opt *UpdateGroupVariableOptions, options ...OptionFunc) (*GroupVariable, *Response, error) { | |||
| func (s *GroupVariablesService) UpdateVariable(gid interface{}, key string, opt *UpdateGroupVariableOptions, options ...RequestOptionFunc) (*GroupVariable, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -180,7 +183,7 @@ func (s *GroupVariablesService) UpdateVariable(gid interface{}, key string, opt | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/group_level_variables.html#remove-variable | |||
| func (s *GroupVariablesService) RemoveVariable(gid interface{}, key string, options ...OptionFunc) (*Response, error) { | |||
| func (s *GroupVariablesService) RemoveVariable(gid interface{}, key string, options ...RequestOptionFunc) (*Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, err | |||
| @@ -32,34 +32,44 @@ type GroupsService struct { | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/groups.html | |||
| type Group struct { | |||
| ID int `json:"id"` | |||
| Name string `json:"name"` | |||
| Path string `json:"path"` | |||
| Description string `json:"description"` | |||
| Visibility *VisibilityValue `json:"visibility"` | |||
| LFSEnabled bool `json:"lfs_enabled"` | |||
| AvatarURL string `json:"avatar_url"` | |||
| WebURL string `json:"web_url"` | |||
| RequestAccessEnabled bool `json:"request_access_enabled"` | |||
| FullName string `json:"full_name"` | |||
| FullPath string `json:"full_path"` | |||
| ParentID int `json:"parent_id"` | |||
| Projects []*Project `json:"projects"` | |||
| Statistics *StorageStatistics `json:"statistics"` | |||
| CustomAttributes []*CustomAttribute `json:"custom_attributes"` | |||
| ShareWithGroupLock bool `json:"share_with_group_lock"` | |||
| RequireTwoFactorAuth bool `json:"require_two_factor_authentication"` | |||
| TwoFactorGracePeriod int `json:"two_factor_grace_period"` | |||
| ProjectCreationLevel string `json:"project_creation_level"` | |||
| AutoDevopsEnabled bool `json:"auto_devops_enabled"` | |||
| SubGroupCreationLevel string `json:"subgroup_creation_level"` | |||
| EmailsDisabled bool `json:"emails_disabled"` | |||
| RunnersToken string `json:"runners_token"` | |||
| SharedProjects []*Project `json:"shared_projects"` | |||
| LDAPCN string `json:"ldap_cn"` | |||
| LDAPAccess bool `json:"ldap_access"` | |||
| SharedRunnersMinutesLimit int `json:"shared_runners_minutes_limit"` | |||
| ExtraSharedRunnersMinutesLimit int `json:"extra_shared_runners_minutes_limit"` | |||
| ID int `json:"id"` | |||
| Name string `json:"name"` | |||
| Path string `json:"path"` | |||
| Description string `json:"description"` | |||
| MembershipLock bool `json:"membership_lock"` | |||
| Visibility VisibilityValue `json:"visibility"` | |||
| LFSEnabled bool `json:"lfs_enabled"` | |||
| AvatarURL string `json:"avatar_url"` | |||
| WebURL string `json:"web_url"` | |||
| RequestAccessEnabled bool `json:"request_access_enabled"` | |||
| FullName string `json:"full_name"` | |||
| FullPath string `json:"full_path"` | |||
| ParentID int `json:"parent_id"` | |||
| Projects []*Project `json:"projects"` | |||
| Statistics *StorageStatistics `json:"statistics"` | |||
| CustomAttributes []*CustomAttribute `json:"custom_attributes"` | |||
| ShareWithGroupLock bool `json:"share_with_group_lock"` | |||
| RequireTwoFactorAuth bool `json:"require_two_factor_authentication"` | |||
| TwoFactorGracePeriod int `json:"two_factor_grace_period"` | |||
| ProjectCreationLevel ProjectCreationLevelValue `json:"project_creation_level"` | |||
| AutoDevopsEnabled bool `json:"auto_devops_enabled"` | |||
| SubGroupCreationLevel SubGroupCreationLevelValue `json:"subgroup_creation_level"` | |||
| EmailsDisabled bool `json:"emails_disabled"` | |||
| MentionsDisabled bool `json:"mentions_disabled"` | |||
| RunnersToken string `json:"runners_token"` | |||
| SharedProjects []*Project `json:"shared_projects"` | |||
| LDAPCN string `json:"ldap_cn"` | |||
| LDAPAccess AccessLevelValue `json:"ldap_access"` | |||
| LDAPGroupLinks []*LDAPGroupLink `json:"ldap_group_links"` | |||
| SharedRunnersMinutesLimit int `json:"shared_runners_minutes_limit"` | |||
| ExtraSharedRunnersMinutesLimit int `json:"extra_shared_runners_minutes_limit"` | |||
| MarkedForDeletionOn *ISOTime `json:"marked_for_deletion_on"` | |||
| } | |||
| type LDAPGroupLink struct { | |||
| CN string `json:"cn"` | |||
| GroupAccess AccessLevelValue `json:"group_access"` | |||
| Provider string `json:"provider"` | |||
| } | |||
| // ListGroupsOptions represents the available ListGroups() options. | |||
| @@ -82,7 +92,7 @@ type ListGroupsOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/groups.html#list-project-groups | |||
| func (s *GroupsService) ListGroups(opt *ListGroupsOptions, options ...OptionFunc) ([]*Group, *Response, error) { | |||
| func (s *GroupsService) ListGroups(opt *ListGroupsOptions, options ...RequestOptionFunc) ([]*Group, *Response, error) { | |||
| req, err := s.client.NewRequest("GET", "groups", opt, options) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -100,7 +110,7 @@ func (s *GroupsService) ListGroups(opt *ListGroupsOptions, options ...OptionFunc | |||
| // GetGroup gets all details of a group. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#details-of-a-group | |||
| func (s *GroupsService) GetGroup(gid interface{}, options ...OptionFunc) (*Group, *Response, error) { | |||
| func (s *GroupsService) GetGroup(gid interface{}, options ...RequestOptionFunc) (*Group, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -125,20 +135,31 @@ func (s *GroupsService) GetGroup(gid interface{}, options ...OptionFunc) (*Group | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#new-group | |||
| type CreateGroupOptions struct { | |||
| Name *string `url:"name,omitempty" json:"name,omitempty"` | |||
| Path *string `url:"path,omitempty" json:"path,omitempty"` | |||
| Description *string `url:"description,omitempty" json:"description,omitempty"` | |||
| Visibility *VisibilityValue `url:"visibility,omitempty" json:"visibility,omitempty"` | |||
| LFSEnabled *bool `url:"lfs_enabled,omitempty" json:"lfs_enabled,omitempty"` | |||
| RequestAccessEnabled *bool `url:"request_access_enabled,omitempty" json:"request_access_enabled,omitempty"` | |||
| ParentID *int `url:"parent_id,omitempty" json:"parent_id,omitempty"` | |||
| Name *string `url:"name,omitempty" json:"name,omitempty"` | |||
| Path *string `url:"path,omitempty" json:"path,omitempty"` | |||
| Description *string `url:"description,omitempty" json:"description,omitempty"` | |||
| MembershipLock *bool `url:"membership_lock,omitempty" json:"membership_lock,omitempty"` | |||
| Visibility *VisibilityValue `url:"visibility,omitempty" json:"visibility,omitempty"` | |||
| ShareWithGroupLock *bool `url:"share_with_group_lock,omitempty" json:"share_with_group_lock,omitempty"` | |||
| RequireTwoFactorAuth *bool `url:"require_two_factor_authentication,omitempty" json:"require_two_factor_authentication,omitempty"` | |||
| TwoFactorGracePeriod *int `url:"two_factor_grace_period,omitempty" json:"two_factor_grace_period,omitempty"` | |||
| ProjectCreationLevel *ProjectCreationLevelValue `url:"project_creation_level,omitempty" json:"project_creation_level,omitempty"` | |||
| AutoDevopsEnabled *bool `url:"auto_devops_enabled,omitempty" json:"auto_devops_enabled,omitempty"` | |||
| SubGroupCreationLevel *SubGroupCreationLevelValue `url:"subgroup_creation_level,omitempty" json:"subgroup_creation_level,omitempty"` | |||
| EmailsDisabled *bool `url:"emails_disabled,omitempty" json:"emails_disabled,omitempty"` | |||
| MentionsDisabled *bool `url:"mentions_disabled,omitempty" json:"mentions_disabled,omitempty"` | |||
| LFSEnabled *bool `url:"lfs_enabled,omitempty" json:"lfs_enabled,omitempty"` | |||
| RequestAccessEnabled *bool `url:"request_access_enabled,omitempty" json:"request_access_enabled,omitempty"` | |||
| ParentID *int `url:"parent_id,omitempty" json:"parent_id,omitempty"` | |||
| SharedRunnersMinutesLimit *int `url:"shared_runners_minutes_limit,omitempty" json:"shared_runners_minutes_limit,omitempty"` | |||
| ExtraSharedRunnersMinutesLimit *int `url:"extra_shared_runners_minutes_limit,omitempty" json:"extra_shared_runners_minutes_limit,omitempty"` | |||
| } | |||
| // CreateGroup creates a new project group. Available only for users who can | |||
| // create groups. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#new-group | |||
| func (s *GroupsService) CreateGroup(opt *CreateGroupOptions, options ...OptionFunc) (*Group, *Response, error) { | |||
| func (s *GroupsService) CreateGroup(opt *CreateGroupOptions, options ...RequestOptionFunc) (*Group, *Response, error) { | |||
| req, err := s.client.NewRequest("POST", "groups", opt, options) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -158,7 +179,7 @@ func (s *GroupsService) CreateGroup(opt *CreateGroupOptions, options ...OptionFu | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/groups.html#transfer-project-to-group | |||
| func (s *GroupsService) TransferGroup(gid interface{}, pid interface{}, options ...OptionFunc) (*Group, *Response, error) { | |||
| func (s *GroupsService) TransferGroup(gid interface{}, pid interface{}, options ...RequestOptionFunc) (*Group, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -193,7 +214,7 @@ type UpdateGroupOptions CreateGroupOptions | |||
| // administrators. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#update-group | |||
| func (s *GroupsService) UpdateGroup(gid interface{}, opt *UpdateGroupOptions, options ...OptionFunc) (*Group, *Response, error) { | |||
| func (s *GroupsService) UpdateGroup(gid interface{}, opt *UpdateGroupOptions, options ...RequestOptionFunc) (*Group, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -217,7 +238,7 @@ func (s *GroupsService) UpdateGroup(gid interface{}, opt *UpdateGroupOptions, op | |||
| // DeleteGroup removes group with all projects inside. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#remove-group | |||
| func (s *GroupsService) DeleteGroup(gid interface{}, options ...OptionFunc) (*Response, error) { | |||
| func (s *GroupsService) DeleteGroup(gid interface{}, options ...RequestOptionFunc) (*Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, err | |||
| @@ -235,7 +256,7 @@ func (s *GroupsService) DeleteGroup(gid interface{}, options ...OptionFunc) (*Re | |||
| // SearchGroup get all groups that match your string in their name or path. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#search-for-group | |||
| func (s *GroupsService) SearchGroup(query string, options ...OptionFunc) ([]*Group, *Response, error) { | |||
| func (s *GroupsService) SearchGroup(query string, options ...RequestOptionFunc) ([]*Group, *Response, error) { | |||
| var q struct { | |||
| Search string `url:"search,omitempty" json:"search,omitempty"` | |||
| } | |||
| @@ -281,7 +302,7 @@ type ListGroupProjectsOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/groups.html#list-a-group-39-s-projects | |||
| func (s *GroupsService) ListGroupProjects(gid interface{}, opt *ListGroupProjectsOptions, options ...OptionFunc) ([]*Project, *Response, error) { | |||
| func (s *GroupsService) ListGroupProjects(gid interface{}, opt *ListGroupProjectsOptions, options ...RequestOptionFunc) ([]*Project, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -313,7 +334,7 @@ type ListSubgroupsOptions ListGroupsOptions | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/groups.html#list-a-groups-s-subgroups | |||
| func (s *GroupsService) ListSubgroups(gid interface{}, opt *ListSubgroupsOptions, options ...OptionFunc) ([]*Group, *Response, error) { | |||
| func (s *GroupsService) ListSubgroups(gid interface{}, opt *ListSubgroupsOptions, options ...RequestOptionFunc) ([]*Group, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -333,3 +354,110 @@ func (s *GroupsService) ListSubgroups(gid interface{}, opt *ListSubgroupsOptions | |||
| return g, resp, err | |||
| } | |||
| // ListGroupLDAPLinks lists the group's LDAP links. Available only for users who | |||
| // can edit groups. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/groups.html#list-ldap-group-links-starter | |||
| func (s *GroupsService) ListGroupLDAPLinks(gid interface{}, options ...RequestOptionFunc) ([]*LDAPGroupLink, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| } | |||
| u := fmt.Sprintf("groups/%s/ldap_group_links", pathEscape(group)) | |||
| req, err := s.client.NewRequest("GET", u, nil, options) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| } | |||
| var gl []*LDAPGroupLink | |||
| resp, err := s.client.Do(req, &gl) | |||
| if err != nil { | |||
| return nil, resp, err | |||
| } | |||
| return gl, resp, nil | |||
| } | |||
| // AddGroupLDAPLinkOptions represents the available AddGroupLDAPLink() options. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/groups.html#add-ldap-group-link-starter | |||
| type AddGroupLDAPLinkOptions struct { | |||
| CN *string `url:"cn,omitempty" json:"cn,omitempty"` | |||
| GroupAccess *int `url:"group_access,omitempty" json:"group_access,omitempty"` | |||
| Provider *string `url:"provider,omitempty" json:"provider,ommitempty"` | |||
| } | |||
| // AddGroupLDAPLink creates a new group LDAP link. Available only for users who | |||
| // can edit groups. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/groups.html#add-ldap-group-link-starter | |||
| func (s *GroupsService) AddGroupLDAPLink(gid interface{}, opt *AddGroupLDAPLinkOptions, options ...RequestOptionFunc) (*LDAPGroupLink, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| } | |||
| u := fmt.Sprintf("groups/%s/ldap_group_links", pathEscape(group)) | |||
| req, err := s.client.NewRequest("POST", u, opt, options) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| } | |||
| gl := new(LDAPGroupLink) | |||
| resp, err := s.client.Do(req, gl) | |||
| if err != nil { | |||
| return nil, resp, err | |||
| } | |||
| return gl, resp, err | |||
| } | |||
| // DeleteGroupLDAPLink deletes a group LDAP link. Available only for users who | |||
| // can edit groups. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/groups.html#delete-ldap-group-link-starter | |||
| func (s *GroupsService) DeleteGroupLDAPLink(gid interface{}, cn string, options ...RequestOptionFunc) (*Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| u := fmt.Sprintf("groups/%s/ldap_group_links/%s", pathEscape(group), pathEscape(cn)) | |||
| req, err := s.client.NewRequest("DELETE", u, nil, options) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| return s.client.Do(req, nil) | |||
| } | |||
| // DeleteGroupLDAPLinkForProvider deletes a group LDAP link from a specific | |||
| // provider. Available only for users who can edit groups. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/groups.html#delete-ldap-group-link-starter | |||
| func (s *GroupsService) DeleteGroupLDAPLinkForProvider(gid interface{}, provider, cn string, options ...RequestOptionFunc) (*Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| u := fmt.Sprintf( | |||
| "groups/%s/ldap_group_links/%s/%s", | |||
| pathEscape(group), | |||
| pathEscape(provider), | |||
| pathEscape(cn), | |||
| ) | |||
| req, err := s.client.NewRequest("DELETE", u, nil, options) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| return s.client.Do(req, nil) | |||
| } | |||
| @@ -43,7 +43,7 @@ type IssueLink struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/issue_links.html#list-issue-relations | |||
| func (s *IssueLinksService) ListIssueRelations(pid interface{}, issueIID int, options ...OptionFunc) ([]*Issue, *Response, error) { | |||
| func (s *IssueLinksService) ListIssueRelations(pid interface{}, issueIID int, options ...RequestOptionFunc) ([]*Issue, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -77,7 +77,7 @@ type CreateIssueLinkOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/issue_links.html#create-an-issue-link | |||
| func (s *IssueLinksService) CreateIssueLink(pid interface{}, issueIID int, opt *CreateIssueLinkOptions, options ...OptionFunc) (*IssueLink, *Response, error) { | |||
| func (s *IssueLinksService) CreateIssueLink(pid interface{}, issueIID int, opt *CreateIssueLinkOptions, options ...RequestOptionFunc) (*IssueLink, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -102,7 +102,7 @@ func (s *IssueLinksService) CreateIssueLink(pid interface{}, issueIID int, opt * | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/issue_links.html#delete-an-issue-link | |||
| func (s *IssueLinksService) DeleteIssueLink(pid interface{}, issueIID, issueLinkID int, options ...OptionFunc) (*IssueLink, *Response, error) { | |||
| func (s *IssueLinksService) DeleteIssueLink(pid interface{}, issueIID, issueLinkID int, options ...RequestOptionFunc) (*IssueLink, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -19,6 +19,7 @@ package gitlab | |||
| import ( | |||
| "encoding/json" | |||
| "fmt" | |||
| "net/url" | |||
| "strings" | |||
| "time" | |||
| ) | |||
| @@ -52,6 +53,13 @@ type IssueAssignee struct { | |||
| Username string `json:"username"` | |||
| } | |||
| // IssueReferences represents references of the issue. | |||
| type IssueReferences struct { | |||
| Short string `json:"short"` | |||
| Relative string `json:"relative"` | |||
| Full string `json:"full"` | |||
| } | |||
| // IssueLinks represents links of the issue. | |||
| type IssueLinks struct { | |||
| Self string `json:"self"` | |||
| @@ -64,33 +72,38 @@ type IssueLinks struct { | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/issues.html | |||
| type Issue struct { | |||
| ID int `json:"id"` | |||
| IID int `json:"iid"` | |||
| ProjectID int `json:"project_id"` | |||
| Milestone *Milestone `json:"milestone"` | |||
| Author *IssueAuthor `json:"author"` | |||
| Description string `json:"description"` | |||
| State string `json:"state"` | |||
| Assignees []*IssueAssignee `json:"assignees"` | |||
| Assignee *IssueAssignee `json:"assignee"` | |||
| Upvotes int `json:"upvotes"` | |||
| Downvotes int `json:"downvotes"` | |||
| Labels Labels `json:"labels"` | |||
| Title string `json:"title"` | |||
| UpdatedAt *time.Time `json:"updated_at"` | |||
| CreatedAt *time.Time `json:"created_at"` | |||
| ClosedAt *time.Time `json:"closed_at"` | |||
| Subscribed bool `json:"subscribed"` | |||
| UserNotesCount int `json:"user_notes_count"` | |||
| DueDate *ISOTime `json:"due_date"` | |||
| WebURL string `json:"web_url"` | |||
| TimeStats *TimeStats `json:"time_stats"` | |||
| Confidential bool `json:"confidential"` | |||
| Weight int `json:"weight"` | |||
| DiscussionLocked bool `json:"discussion_locked"` | |||
| Links *IssueLinks `json:"_links"` | |||
| IssueLinkID int `json:"issue_link_id"` | |||
| MergeRequestCount int `json:"merge_requests_count"` | |||
| ID int `json:"id"` | |||
| IID int `json:"iid"` | |||
| State string `json:"state"` | |||
| Description string `json:"description"` | |||
| Author *IssueAuthor `json:"author"` | |||
| Milestone *Milestone `json:"milestone"` | |||
| ProjectID int `json:"project_id"` | |||
| Assignees []*IssueAssignee `json:"assignees"` | |||
| Assignee *IssueAssignee `json:"assignee"` | |||
| UpdatedAt *time.Time `json:"updated_at"` | |||
| ClosedAt *time.Time `json:"closed_at"` | |||
| Title string `json:"title"` | |||
| CreatedAt *time.Time `json:"created_at"` | |||
| Labels Labels `json:"labels"` | |||
| Upvotes int `json:"upvotes"` | |||
| Downvotes int `json:"downvotes"` | |||
| DueDate *ISOTime `json:"due_date"` | |||
| WebURL string `json:"web_url"` | |||
| References *IssueReferences `json:"references"` | |||
| TimeStats *TimeStats `json:"time_stats"` | |||
| Confidential bool `json:"confidential"` | |||
| Weight int `json:"weight"` | |||
| DiscussionLocked bool `json:"discussion_locked"` | |||
| Subscribed bool `json:"subscribed"` | |||
| UserNotesCount int `json:"user_notes_count"` | |||
| Links *IssueLinks `json:"_links"` | |||
| IssueLinkID int `json:"issue_link_id"` | |||
| MergeRequestCount int `json:"merge_requests_count"` | |||
| TaskCompletionStatus struct { | |||
| Count int `json:"count"` | |||
| CompletedCount int `json:"completed_count"` | |||
| } `json:"task_completion_status"` | |||
| } | |||
| func (i Issue) String() string { | |||
| @@ -105,6 +118,12 @@ func (l *Labels) MarshalJSON() ([]byte, error) { | |||
| return json.Marshal(strings.Join(*l, ",")) | |||
| } | |||
| // EncodeValues implements the query.EncodeValues interface | |||
| func (l *Labels) EncodeValues(key string, v *url.Values) error { | |||
| v.Set(key, strings.Join(*l, ",")) | |||
| return nil | |||
| } | |||
| // ListIssuesOptions represents the available ListIssues() options. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#list-issues | |||
| @@ -133,7 +152,7 @@ type ListIssuesOptions struct { | |||
| // takes pagination parameters page and per_page to restrict the list of issues. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#list-issues | |||
| func (s *IssuesService) ListIssues(opt *ListIssuesOptions, options ...OptionFunc) ([]*Issue, *Response, error) { | |||
| func (s *IssuesService) ListIssues(opt *ListIssuesOptions, options ...RequestOptionFunc) ([]*Issue, *Response, error) { | |||
| req, err := s.client.NewRequest("GET", "issues", opt, options) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -175,7 +194,7 @@ type ListGroupIssuesOptions struct { | |||
| // pagination parameters page and per_page to return the list of group issues. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#list-group-issues | |||
| func (s *IssuesService) ListGroupIssues(pid interface{}, opt *ListGroupIssuesOptions, options ...OptionFunc) ([]*Issue, *Response, error) { | |||
| func (s *IssuesService) ListGroupIssues(pid interface{}, opt *ListGroupIssuesOptions, options ...RequestOptionFunc) ([]*Issue, *Response, error) { | |||
| group, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -225,7 +244,7 @@ type ListProjectIssuesOptions struct { | |||
| // pagination parameters page and per_page to return the list of project issues. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#list-project-issues | |||
| func (s *IssuesService) ListProjectIssues(pid interface{}, opt *ListProjectIssuesOptions, options ...OptionFunc) ([]*Issue, *Response, error) { | |||
| func (s *IssuesService) ListProjectIssues(pid interface{}, opt *ListProjectIssuesOptions, options ...RequestOptionFunc) ([]*Issue, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -249,7 +268,7 @@ func (s *IssuesService) ListProjectIssues(pid interface{}, opt *ListProjectIssue | |||
| // GetIssue gets a single project issue. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#single-issues | |||
| func (s *IssuesService) GetIssue(pid interface{}, issue int, options ...OptionFunc) (*Issue, *Response, error) { | |||
| func (s *IssuesService) GetIssue(pid interface{}, issue int, options ...RequestOptionFunc) (*Issue, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -291,7 +310,7 @@ type CreateIssueOptions struct { | |||
| // CreateIssue creates a new project issue. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#new-issues | |||
| func (s *IssuesService) CreateIssue(pid interface{}, opt *CreateIssueOptions, options ...OptionFunc) (*Issue, *Response, error) { | |||
| func (s *IssuesService) CreateIssue(pid interface{}, opt *CreateIssueOptions, options ...RequestOptionFunc) (*Issue, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -333,7 +352,7 @@ type UpdateIssueOptions struct { | |||
| // to mark an issue as closed. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#edit-issues | |||
| func (s *IssuesService) UpdateIssue(pid interface{}, issue int, opt *UpdateIssueOptions, options ...OptionFunc) (*Issue, *Response, error) { | |||
| func (s *IssuesService) UpdateIssue(pid interface{}, issue int, opt *UpdateIssueOptions, options ...RequestOptionFunc) (*Issue, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -357,7 +376,7 @@ func (s *IssuesService) UpdateIssue(pid interface{}, issue int, opt *UpdateIssue | |||
| // DeleteIssue deletes a single project issue. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#delete-an-issue | |||
| func (s *IssuesService) DeleteIssue(pid interface{}, issue int, options ...OptionFunc) (*Response, error) { | |||
| func (s *IssuesService) DeleteIssue(pid interface{}, issue int, options ...RequestOptionFunc) (*Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, err | |||
| @@ -383,7 +402,7 @@ type MoveIssueOptions struct { | |||
| // to mark an issue as closed. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ee/api/issues.html#move-an-issue | |||
| func (s *IssuesService) MoveIssue(pid interface{}, issue int, opt *MoveIssueOptions, options ...OptionFunc) (*Issue, *Response, error) { | |||
| func (s *IssuesService) MoveIssue(pid interface{}, issue int, opt *MoveIssueOptions, options ...RequestOptionFunc) (*Issue, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -410,7 +429,7 @@ func (s *IssuesService) MoveIssue(pid interface{}, issue int, opt *MoveIssueOpti | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/merge_requests.html#subscribe-to-a-merge-request | |||
| func (s *IssuesService) SubscribeToIssue(pid interface{}, issue int, options ...OptionFunc) (*Issue, *Response, error) { | |||
| func (s *IssuesService) SubscribeToIssue(pid interface{}, issue int, options ...RequestOptionFunc) (*Issue, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -437,7 +456,7 @@ func (s *IssuesService) SubscribeToIssue(pid interface{}, issue int, options ... | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/merge_requests.html#unsubscribe-from-a-merge-request | |||
| func (s *IssuesService) UnsubscribeFromIssue(pid interface{}, issue int, options ...OptionFunc) (*Issue, *Response, error) { | |||
| func (s *IssuesService) UnsubscribeFromIssue(pid interface{}, issue int, options ...RequestOptionFunc) (*Issue, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -470,7 +489,7 @@ type ListMergeRequestsClosingIssueOptions ListOptions | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/issues.html#list-merge-requests-that-will-close-issue-on-merge | |||
| func (s *IssuesService) ListMergeRequestsClosingIssue(pid interface{}, issue int, opt *ListMergeRequestsClosingIssueOptions, options ...OptionFunc) ([]*MergeRequest, *Response, error) { | |||
| func (s *IssuesService) ListMergeRequestsClosingIssue(pid interface{}, issue int, opt *ListMergeRequestsClosingIssueOptions, options ...RequestOptionFunc) ([]*MergeRequest, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -503,7 +522,7 @@ type ListMergeRequestsRelatedToIssueOptions ListOptions | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/issues.html#list-merge-requests-related-to-issue | |||
| func (s *IssuesService) ListMergeRequestsRelatedToIssue(pid interface{}, issue int, opt *ListMergeRequestsRelatedToIssueOptions, options ...OptionFunc) ([]*MergeRequest, *Response, error) { | |||
| func (s *IssuesService) ListMergeRequestsRelatedToIssue(pid interface{}, issue int, opt *ListMergeRequestsRelatedToIssueOptions, options ...RequestOptionFunc) ([]*MergeRequest, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -531,7 +550,7 @@ func (s *IssuesService) ListMergeRequestsRelatedToIssue(pid interface{}, issue i | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/issues.html#set-a-time-estimate-for-an-issue | |||
| func (s *IssuesService) SetTimeEstimate(pid interface{}, issue int, opt *SetTimeEstimateOptions, options ...OptionFunc) (*TimeStats, *Response, error) { | |||
| func (s *IssuesService) SetTimeEstimate(pid interface{}, issue int, opt *SetTimeEstimateOptions, options ...RequestOptionFunc) (*TimeStats, *Response, error) { | |||
| return s.timeStats.setTimeEstimate(pid, "issues", issue, opt, options...) | |||
| } | |||
| @@ -539,7 +558,7 @@ func (s *IssuesService) SetTimeEstimate(pid interface{}, issue int, opt *SetTime | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/issues.html#reset-the-time-estimate-for-an-issue | |||
| func (s *IssuesService) ResetTimeEstimate(pid interface{}, issue int, options ...OptionFunc) (*TimeStats, *Response, error) { | |||
| func (s *IssuesService) ResetTimeEstimate(pid interface{}, issue int, options ...RequestOptionFunc) (*TimeStats, *Response, error) { | |||
| return s.timeStats.resetTimeEstimate(pid, "issues", issue, options...) | |||
| } | |||
| @@ -547,7 +566,7 @@ func (s *IssuesService) ResetTimeEstimate(pid interface{}, issue int, options .. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/issues.html#add-spent-time-for-an-issue | |||
| func (s *IssuesService) AddSpentTime(pid interface{}, issue int, opt *AddSpentTimeOptions, options ...OptionFunc) (*TimeStats, *Response, error) { | |||
| func (s *IssuesService) AddSpentTime(pid interface{}, issue int, opt *AddSpentTimeOptions, options ...RequestOptionFunc) (*TimeStats, *Response, error) { | |||
| return s.timeStats.addSpentTime(pid, "issues", issue, opt, options...) | |||
| } | |||
| @@ -555,7 +574,7 @@ func (s *IssuesService) AddSpentTime(pid interface{}, issue int, opt *AddSpentTi | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/issues.html#reset-spent-time-for-an-issue | |||
| func (s *IssuesService) ResetSpentTime(pid interface{}, issue int, options ...OptionFunc) (*TimeStats, *Response, error) { | |||
| func (s *IssuesService) ResetSpentTime(pid interface{}, issue int, options ...RequestOptionFunc) (*TimeStats, *Response, error) { | |||
| return s.timeStats.resetSpentTime(pid, "issues", issue, options...) | |||
| } | |||
| @@ -563,6 +582,6 @@ func (s *IssuesService) ResetSpentTime(pid interface{}, issue int, options ...Op | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/issues.html#get-time-tracking-stats | |||
| func (s *IssuesService) GetTimeSpent(pid interface{}, issue int, options ...OptionFunc) (*TimeStats, *Response, error) { | |||
| func (s *IssuesService) GetTimeSpent(pid interface{}, issue int, options ...RequestOptionFunc) (*TimeStats, *Response, error) { | |||
| return s.timeStats.getTimeSpent(pid, "issues", issue, options...) | |||
| } | |||
| @@ -89,7 +89,7 @@ type ListJobsOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/jobs.html#list-project-jobs | |||
| func (s *JobsService) ListProjectJobs(pid interface{}, opts *ListJobsOptions, options ...OptionFunc) ([]Job, *Response, error) { | |||
| func (s *JobsService) ListProjectJobs(pid interface{}, opts *ListJobsOptions, options ...RequestOptionFunc) ([]Job, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -115,7 +115,7 @@ func (s *JobsService) ListProjectJobs(pid interface{}, opts *ListJobsOptions, op | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/jobs.html#list-pipeline-jobs | |||
| func (s *JobsService) ListPipelineJobs(pid interface{}, pipelineID int, opts *ListJobsOptions, options ...OptionFunc) ([]*Job, *Response, error) { | |||
| func (s *JobsService) ListPipelineJobs(pid interface{}, pipelineID int, opts *ListJobsOptions, options ...RequestOptionFunc) ([]*Job, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -140,7 +140,7 @@ func (s *JobsService) ListPipelineJobs(pid interface{}, pipelineID int, opts *Li | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/jobs.html#get-a-single-job | |||
| func (s *JobsService) GetJob(pid interface{}, jobID int, options ...OptionFunc) (*Job, *Response, error) { | |||
| func (s *JobsService) GetJob(pid interface{}, jobID int, options ...RequestOptionFunc) (*Job, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -165,7 +165,7 @@ func (s *JobsService) GetJob(pid interface{}, jobID int, options ...OptionFunc) | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/jobs.html#get-job-artifacts | |||
| func (s *JobsService) GetJobArtifacts(pid interface{}, jobID int, options ...OptionFunc) (io.Reader, *Response, error) { | |||
| func (s *JobsService) GetJobArtifacts(pid interface{}, jobID int, options ...RequestOptionFunc) (io.Reader, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -200,7 +200,7 @@ type DownloadArtifactsFileOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/jobs.html#download-the-artifacts-file | |||
| func (s *JobsService) DownloadArtifactsFile(pid interface{}, refName string, opt *DownloadArtifactsFileOptions, options ...OptionFunc) (io.Reader, *Response, error) { | |||
| func (s *JobsService) DownloadArtifactsFile(pid interface{}, refName string, opt *DownloadArtifactsFileOptions, options ...RequestOptionFunc) (io.Reader, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -228,7 +228,7 @@ func (s *JobsService) DownloadArtifactsFile(pid interface{}, refName string, opt | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/jobs.html#download-a-single-artifact-file | |||
| func (s *JobsService) DownloadSingleArtifactsFile(pid interface{}, jobID int, artifactPath string, options ...OptionFunc) (io.Reader, *Response, error) { | |||
| func (s *JobsService) DownloadSingleArtifactsFile(pid interface{}, jobID int, artifactPath string, options ...RequestOptionFunc) (io.Reader, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -259,7 +259,7 @@ func (s *JobsService) DownloadSingleArtifactsFile(pid interface{}, jobID int, ar | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/jobs.html#get-a-trace-file | |||
| func (s *JobsService) GetTraceFile(pid interface{}, jobID int, options ...OptionFunc) (io.Reader, *Response, error) { | |||
| func (s *JobsService) GetTraceFile(pid interface{}, jobID int, options ...RequestOptionFunc) (io.Reader, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -284,7 +284,7 @@ func (s *JobsService) GetTraceFile(pid interface{}, jobID int, options ...Option | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/jobs.html#cancel-a-job | |||
| func (s *JobsService) CancelJob(pid interface{}, jobID int, options ...OptionFunc) (*Job, *Response, error) { | |||
| func (s *JobsService) CancelJob(pid interface{}, jobID int, options ...RequestOptionFunc) (*Job, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -309,7 +309,7 @@ func (s *JobsService) CancelJob(pid interface{}, jobID int, options ...OptionFun | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/jobs.html#retry-a-job | |||
| func (s *JobsService) RetryJob(pid interface{}, jobID int, options ...OptionFunc) (*Job, *Response, error) { | |||
| func (s *JobsService) RetryJob(pid interface{}, jobID int, options ...RequestOptionFunc) (*Job, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -335,7 +335,7 @@ func (s *JobsService) RetryJob(pid interface{}, jobID int, options ...OptionFunc | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/jobs.html#erase-a-job | |||
| func (s *JobsService) EraseJob(pid interface{}, jobID int, options ...OptionFunc) (*Job, *Response, error) { | |||
| func (s *JobsService) EraseJob(pid interface{}, jobID int, options ...RequestOptionFunc) (*Job, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -361,7 +361,7 @@ func (s *JobsService) EraseJob(pid interface{}, jobID int, options ...OptionFunc | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/jobs.html#keep-artifacts | |||
| func (s *JobsService) KeepArtifacts(pid interface{}, jobID int, options ...OptionFunc) (*Job, *Response, error) { | |||
| func (s *JobsService) KeepArtifacts(pid interface{}, jobID int, options ...RequestOptionFunc) (*Job, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -386,7 +386,7 @@ func (s *JobsService) KeepArtifacts(pid interface{}, jobID int, options ...Optio | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/jobs.html#play-a-job | |||
| func (s *JobsService) PlayJob(pid interface{}, jobID int, options ...OptionFunc) (*Job, *Response, error) { | |||
| func (s *JobsService) PlayJob(pid interface{}, jobID int, options ...RequestOptionFunc) (*Job, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -47,7 +47,7 @@ type Key struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/keys.html#get-ssh-key-with-user-by-id-of-an-ssh-key | |||
| func (s *KeysService) GetKeyWithUser(key int, options ...OptionFunc) (*Key, *Response, error) { | |||
| func (s *KeysService) GetKeyWithUser(key int, options ...RequestOptionFunc) (*Key, *Response, error) { | |||
| u := fmt.Sprintf("keys/%d", key) | |||
| req, err := s.client.NewRequest("GET", u, nil, options) | |||
| @@ -78,7 +78,7 @@ type ListLabelsOptions ListOptions | |||
| // ListLabels gets all labels for given project. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/labels.html#list-labels | |||
| func (s *LabelsService) ListLabels(pid interface{}, opt *ListLabelsOptions, options ...OptionFunc) ([]*Label, *Response, error) { | |||
| func (s *LabelsService) ListLabels(pid interface{}, opt *ListLabelsOptions, options ...RequestOptionFunc) ([]*Label, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -112,7 +112,7 @@ type CreateLabelOptions struct { | |||
| // color. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/labels.html#create-a-new-label | |||
| func (s *LabelsService) CreateLabel(pid interface{}, opt *CreateLabelOptions, options ...OptionFunc) (*Label, *Response, error) { | |||
| func (s *LabelsService) CreateLabel(pid interface{}, opt *CreateLabelOptions, options ...RequestOptionFunc) (*Label, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -143,7 +143,7 @@ type DeleteLabelOptions struct { | |||
| // DeleteLabel deletes a label given by its name. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/labels.html#delete-a-label | |||
| func (s *LabelsService) DeleteLabel(pid interface{}, opt *DeleteLabelOptions, options ...OptionFunc) (*Response, error) { | |||
| func (s *LabelsService) DeleteLabel(pid interface{}, opt *DeleteLabelOptions, options ...RequestOptionFunc) (*Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, err | |||
| @@ -172,7 +172,7 @@ type UpdateLabelOptions struct { | |||
| // one parameter is required, to update the label. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/labels.html#edit-an-existing-label | |||
| func (s *LabelsService) UpdateLabel(pid interface{}, opt *UpdateLabelOptions, options ...OptionFunc) (*Label, *Response, error) { | |||
| func (s *LabelsService) UpdateLabel(pid interface{}, opt *UpdateLabelOptions, options ...RequestOptionFunc) (*Label, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -199,7 +199,7 @@ func (s *LabelsService) UpdateLabel(pid interface{}, opt *UpdateLabelOptions, op | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/labels.html#subscribe-to-a-label | |||
| func (s *LabelsService) SubscribeToLabel(pid interface{}, labelID interface{}, options ...OptionFunc) (*Label, *Response, error) { | |||
| func (s *LabelsService) SubscribeToLabel(pid interface{}, labelID interface{}, options ...RequestOptionFunc) (*Label, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -230,7 +230,7 @@ func (s *LabelsService) SubscribeToLabel(pid interface{}, labelID interface{}, o | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/labels.html#unsubscribe-from-a-label | |||
| func (s *LabelsService) UnsubscribeFromLabel(pid interface{}, labelID interface{}, options ...OptionFunc) (*Response, error) { | |||
| func (s *LabelsService) UnsubscribeFromLabel(pid interface{}, labelID interface{}, options ...RequestOptionFunc) (*Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, err | |||
| @@ -78,7 +78,7 @@ type AddLicenseOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/license.html#add-a-new-license | |||
| func (s *LicenseService) AddLicense(opt *AddLicenseOptions, options ...OptionFunc) (*License, *Response, error) { | |||
| func (s *LicenseService) AddLicense(opt *AddLicenseOptions, options ...RequestOptionFunc) (*License, *Response, error) { | |||
| req, err := s.client.NewRequest("POST", "license", opt, options) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -44,7 +44,7 @@ type ListLicenseTemplatesOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/templates/licenses.html#list-license-templates | |||
| func (s *LicenseTemplatesService) ListLicenseTemplates(opt *ListLicenseTemplatesOptions, options ...OptionFunc) ([]*LicenseTemplate, *Response, error) { | |||
| func (s *LicenseTemplatesService) ListLicenseTemplates(opt *ListLicenseTemplatesOptions, options ...RequestOptionFunc) ([]*LicenseTemplate, *Response, error) { | |||
| req, err := s.client.NewRequest("GET", "templates/licenses", opt, options) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -74,7 +74,7 @@ type GetLicenseTemplateOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/templates/licenses.html#single-license-template | |||
| func (s *LicenseTemplatesService) GetLicenseTemplate(template string, opt *GetLicenseTemplateOptions, options ...OptionFunc) (*LicenseTemplate, *Response, error) { | |||
| func (s *LicenseTemplatesService) GetLicenseTemplate(template string, opt *GetLicenseTemplateOptions, options ...RequestOptionFunc) (*LicenseTemplate, *Response, error) { | |||
| u := fmt.Sprintf("templates/licenses/%s", template) | |||
| req, err := s.client.NewRequest("GET", u, opt, options) | |||
| @@ -60,6 +60,38 @@ type MergeRequestApproverGroup struct { | |||
| } | |||
| } | |||
| // MergeRequestApprovalRule represents a GitLab merge request approval rule. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/merge_request_approvals.html#get-merge-request-level-rules | |||
| type MergeRequestApprovalRule struct { | |||
| ID int `json:"id"` | |||
| Name string `json:"name"` | |||
| RuleType string `json:"rule_type"` | |||
| EligibleApprovers []*BasicUser `json:"eligible_approvers"` | |||
| ApprovalsRequired int `json:"approvals_required"` | |||
| SourceRule *ProjectApprovalRule `json:"source_rule"` | |||
| Users []*BasicUser `json:"users"` | |||
| Groups []*Group `json:"groups"` | |||
| ContainsHiddenGroups bool `json:"contains_hidden_groups"` | |||
| ApprovedBy []*BasicUser `json:"approved_by"` | |||
| Approved bool `json:"approved"` | |||
| } | |||
| // MergeRequestApprovalState represents a GitLab merge request approval state. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/merge_request_approvals.html#get-the-approval-state-of-merge-requests | |||
| type MergeRequestApprovalState struct { | |||
| ApprovalRulesOverwritten bool `json:"approval_rules_overwritten"` | |||
| Rules []*MergeRequestApprovalRule `json:"rules"` | |||
| } | |||
| // String is a stringify for MergeRequestApprovalRule | |||
| func (s MergeRequestApprovalRule) String() string { | |||
| return Stringify(s) | |||
| } | |||
| // MergeRequestApproverUser represents GitLab project level merge request approver user. | |||
| // | |||
| // GitLab API docs: | |||
| @@ -81,7 +113,7 @@ type ApproveMergeRequestOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/merge_request_approvals.html#approve-merge-request | |||
| func (s *MergeRequestApprovalsService) ApproveMergeRequest(pid interface{}, mr int, opt *ApproveMergeRequestOptions, options ...OptionFunc) (*MergeRequestApprovals, *Response, error) { | |||
| func (s *MergeRequestApprovalsService) ApproveMergeRequest(pid interface{}, mr int, opt *ApproveMergeRequestOptions, options ...RequestOptionFunc) (*MergeRequestApprovals, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -106,7 +138,7 @@ func (s *MergeRequestApprovalsService) ApproveMergeRequest(pid interface{}, mr i | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/merge_request_approvals.html#unapprove-merge-request | |||
| func (s *MergeRequestApprovalsService) UnapproveMergeRequest(pid interface{}, mr int, options ...OptionFunc) (*Response, error) { | |||
| func (s *MergeRequestApprovalsService) UnapproveMergeRequest(pid interface{}, mr int, options ...RequestOptionFunc) (*Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, err | |||
| @@ -130,16 +162,41 @@ type ChangeMergeRequestApprovalConfigurationOptions struct { | |||
| ApprovalsRequired *int `url:"approvals_required,omitempty" json:"approvals_required,omitempty"` | |||
| } | |||
| // GetConfiguration shows information about single merge request approvals | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/merge_request_approvals.html#get-configuration-1 | |||
| func (s *MergeRequestApprovalsService) GetConfiguration(pid interface{}, mr int, options ...RequestOptionFunc) (*MergeRequestApprovals, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| } | |||
| u := fmt.Sprintf("projects/%s/merge_requests/%d/approvals", pathEscape(project), mr) | |||
| req, err := s.client.NewRequest("GET", u, nil, options) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| } | |||
| m := new(MergeRequestApprovals) | |||
| resp, err := s.client.Do(req, m) | |||
| if err != nil { | |||
| return nil, resp, err | |||
| } | |||
| return m, resp, err | |||
| } | |||
| // ChangeApprovalConfiguration updates the approval configuration of a merge request. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/merge_request_approvals.html#change-approval-configuration | |||
| func (s *MergeRequestApprovalsService) ChangeApprovalConfiguration(pid interface{}, mergeRequestIID int, opt *ChangeMergeRequestApprovalConfigurationOptions, options ...OptionFunc) (*MergeRequest, *Response, error) { | |||
| func (s *MergeRequestApprovalsService) ChangeApprovalConfiguration(pid interface{}, mergeRequest int, opt *ChangeMergeRequestApprovalConfigurationOptions, options ...RequestOptionFunc) (*MergeRequest, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| } | |||
| u := fmt.Sprintf("projects/%s/merge_requests/%d/approvals", pathEscape(project), mergeRequestIID) | |||
| u := fmt.Sprintf("projects/%s/merge_requests/%d/approvals", pathEscape(project), mergeRequest) | |||
| req, err := s.client.NewRequest("POST", u, opt, options) | |||
| if err != nil { | |||
| @@ -169,12 +226,12 @@ type ChangeMergeRequestAllowedApproversOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/merge_request_approvals.html#change-allowed-approvers-for-merge-request | |||
| func (s *MergeRequestApprovalsService) ChangeAllowedApprovers(pid interface{}, mergeRequestIID int, opt *ChangeMergeRequestAllowedApproversOptions, options ...OptionFunc) (*MergeRequest, *Response, error) { | |||
| func (s *MergeRequestApprovalsService) ChangeAllowedApprovers(pid interface{}, mergeRequest int, opt *ChangeMergeRequestAllowedApproversOptions, options ...RequestOptionFunc) (*MergeRequest, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| } | |||
| u := fmt.Sprintf("projects/%s/merge_requests/%d/approvers", pathEscape(project), mergeRequestIID) | |||
| u := fmt.Sprintf("projects/%s/merge_requests/%d/approvers", pathEscape(project), mergeRequest) | |||
| req, err := s.client.NewRequest("PUT", u, opt, options) | |||
| if err != nil { | |||
| @@ -189,3 +246,147 @@ func (s *MergeRequestApprovalsService) ChangeAllowedApprovers(pid interface{}, m | |||
| return m, resp, err | |||
| } | |||
| // GetApprovalRules requests information about a merge request’s approval rules | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/merge_request_approvals.html#get-merge-request-level-rules | |||
| func (s *MergeRequestApprovalsService) GetApprovalRules(pid interface{}, mergeRequest int, options ...RequestOptionFunc) ([]*MergeRequestApprovalRule, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| } | |||
| u := fmt.Sprintf("projects/%s/merge_requests/%d/approval_rules", pathEscape(project), mergeRequest) | |||
| req, err := s.client.NewRequest("GET", u, nil, options) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| } | |||
| var par []*MergeRequestApprovalRule | |||
| resp, err := s.client.Do(req, &par) | |||
| if err != nil { | |||
| return nil, resp, err | |||
| } | |||
| return par, resp, err | |||
| } | |||
| // GetApprovalState requests information about a merge request’s approval state | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/merge_request_approvals.html#get-the-approval-state-of-merge-requests | |||
| func (s *MergeRequestApprovalsService) GetApprovalState(pid interface{}, mergeRequest int, options ...RequestOptionFunc) (*MergeRequestApprovalState, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| } | |||
| u := fmt.Sprintf("projects/%s/merge_requests/%d/approval_state", pathEscape(project), mergeRequest) | |||
| req, err := s.client.NewRequest("GET", u, nil, options) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| } | |||
| var pas *MergeRequestApprovalState | |||
| resp, err := s.client.Do(req, &pas) | |||
| if err != nil { | |||
| return nil, resp, err | |||
| } | |||
| return pas, resp, err | |||
| } | |||
| // CreateMergeRequestApprovalRuleOptions represents the available CreateApprovalRule() | |||
| // options. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/merge_request_approvals.html#create-merge-request-level-rule | |||
| type CreateMergeRequestApprovalRuleOptions struct { | |||
| Name *string `url:"name,omitempty" json:"name,omitempty"` | |||
| ApprovalsRequired *int `url:"approvals_required,omitempty" json:"approvals_required,omitempty"` | |||
| ApprovalProjectRuleID *int `url:"approval_project_rule_id,omitempty" json:"approval_project_rule_id,omitempty"` | |||
| UserIDs []int `url:"user_ids,omitempty" json:"user_ids,omitempty"` | |||
| GroupIDs []int `url:"group_ids,omitempty" json:"group_ids,omitempty"` | |||
| } | |||
| // CreateApprovalRule creates a new MR level approval rule. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/merge_request_approvals.html#create-merge-request-level-rule | |||
| func (s *MergeRequestApprovalsService) CreateApprovalRule(pid interface{}, mergeRequest int, opt *CreateMergeRequestApprovalRuleOptions, options ...RequestOptionFunc) (*MergeRequestApprovalRule, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| } | |||
| u := fmt.Sprintf("projects/%s/merge_requests/%d/approval_rules", pathEscape(project), mergeRequest) | |||
| req, err := s.client.NewRequest("POST", u, opt, options) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| } | |||
| par := new(MergeRequestApprovalRule) | |||
| resp, err := s.client.Do(req, &par) | |||
| if err != nil { | |||
| return nil, resp, err | |||
| } | |||
| return par, resp, err | |||
| } | |||
| // UpdateMergeRequestApprovalRuleOptions represents the available UpdateApprovalRule() | |||
| // options. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/merge_request_approvals.html#update-merge-request-level-rule | |||
| type UpdateMergeRequestApprovalRuleOptions struct { | |||
| Name *string `url:"name,omitempty" json:"name,omitempty"` | |||
| ApprovalsRequired *int `url:"approvals_required,omitempty" json:"approvals_required,omitempty"` | |||
| UserIDs []int `url:"user_ids,omitempty" json:"user_ids,omitempty"` | |||
| GroupIDs []int `url:"group_ids,omitempty" json:"group_ids,omitempty"` | |||
| } | |||
| // UpdateApprovalRule updates an existing approval rule with new options. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/merge_request_approvals.html#update-merge-request-level-rule | |||
| func (s *MergeRequestApprovalsService) UpdateApprovalRule(pid interface{}, mergeRequest int, approvalRule int, opt *UpdateMergeRequestApprovalRuleOptions, options ...RequestOptionFunc) (*MergeRequestApprovalRule, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| } | |||
| u := fmt.Sprintf("projects/%s/merge_requests/%d/approval_rules/%d", pathEscape(project), mergeRequest, approvalRule) | |||
| req, err := s.client.NewRequest("PUT", u, opt, options) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| } | |||
| par := new(MergeRequestApprovalRule) | |||
| resp, err := s.client.Do(req, &par) | |||
| if err != nil { | |||
| return nil, resp, err | |||
| } | |||
| return par, resp, err | |||
| } | |||
| // DeleteApprovalRule deletes a mr level approval rule. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/merge_request_approvals.html#delete-merge-request-level-rule | |||
| func (s *MergeRequestApprovalsService) DeleteApprovalRule(pid interface{}, mergeRequest int, approvalRule int, options ...RequestOptionFunc) (*Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| u := fmt.Sprintf("projects/%s/merge_requests/%d/approval_rules/%d", pathEscape(project), mergeRequest, approvalRule) | |||
| req, err := s.client.NewRequest("DELETE", u, nil, options) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| return s.client.Do(req, nil) | |||
| } | |||
| @@ -64,6 +64,7 @@ type MergeRequest struct { | |||
| Subscribed bool `json:"subscribed"` | |||
| SHA string `json:"sha"` | |||
| MergeCommitSHA string `json:"merge_commit_sha"` | |||
| SquashCommitSHA string `json:"squash_commit_sha"` | |||
| UserNotesCount int `json:"user_notes_count"` | |||
| ChangesCount string `json:"changes_count"` | |||
| ShouldRemoveSourceBranch bool `json:"should_remove_source_branch"` | |||
| @@ -97,6 +98,7 @@ type MergeRequest struct { | |||
| Count int `json:"count"` | |||
| CompletedCount int `json:"completed_count"` | |||
| } `json:"task_completion_status"` | |||
| HasConflicts bool `json:"has_conflicts"` | |||
| } | |||
| func (m MergeRequest) String() string { | |||
| @@ -159,7 +161,7 @@ type ListMergeRequestsOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/merge_requests.html#list-merge-requests | |||
| func (s *MergeRequestsService) ListMergeRequests(opt *ListMergeRequestsOptions, options ...OptionFunc) ([]*MergeRequest, *Response, error) { | |||
| func (s *MergeRequestsService) ListMergeRequests(opt *ListMergeRequestsOptions, options ...RequestOptionFunc) ([]*MergeRequest, *Response, error) { | |||
| req, err := s.client.NewRequest("GET", "merge_requests", opt, options) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -204,7 +206,7 @@ type ListGroupMergeRequestsOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/merge_requests.html#list-group-merge-requests | |||
| func (s *MergeRequestsService) ListGroupMergeRequests(gid interface{}, opt *ListGroupMergeRequestsOptions, options ...OptionFunc) ([]*MergeRequest, *Response, error) { | |||
| func (s *MergeRequestsService) ListGroupMergeRequests(gid interface{}, opt *ListGroupMergeRequestsOptions, options ...RequestOptionFunc) ([]*MergeRequest, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -257,7 +259,7 @@ type ListProjectMergeRequestsOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/merge_requests.html#list-project-merge-requests | |||
| func (s *MergeRequestsService) ListProjectMergeRequests(pid interface{}, opt *ListProjectMergeRequestsOptions, options ...OptionFunc) ([]*MergeRequest, *Response, error) { | |||
| func (s *MergeRequestsService) ListProjectMergeRequests(pid interface{}, opt *ListProjectMergeRequestsOptions, options ...RequestOptionFunc) ([]*MergeRequest, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -293,7 +295,7 @@ type GetMergeRequestsOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/merge_requests.html#get-single-mr | |||
| func (s *MergeRequestsService) GetMergeRequest(pid interface{}, mergeRequest int, opt *GetMergeRequestsOptions, options ...OptionFunc) (*MergeRequest, *Response, error) { | |||
| func (s *MergeRequestsService) GetMergeRequest(pid interface{}, mergeRequest int, opt *GetMergeRequestsOptions, options ...RequestOptionFunc) (*MergeRequest, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -318,7 +320,7 @@ func (s *MergeRequestsService) GetMergeRequest(pid interface{}, mergeRequest int | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/merge_request_approvals.html#merge-request-level-mr-approvals | |||
| func (s *MergeRequestsService) GetMergeRequestApprovals(pid interface{}, mergeRequest int, options ...OptionFunc) (*MergeRequestApprovals, *Response, error) { | |||
| func (s *MergeRequestsService) GetMergeRequestApprovals(pid interface{}, mergeRequest int, options ...RequestOptionFunc) (*MergeRequestApprovals, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -350,7 +352,7 @@ type GetMergeRequestCommitsOptions ListOptions | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/merge_requests.html#get-single-mr-commits | |||
| func (s *MergeRequestsService) GetMergeRequestCommits(pid interface{}, mergeRequest int, opt *GetMergeRequestCommitsOptions, options ...OptionFunc) ([]*Commit, *Response, error) { | |||
| func (s *MergeRequestsService) GetMergeRequestCommits(pid interface{}, mergeRequest int, opt *GetMergeRequestCommitsOptions, options ...RequestOptionFunc) ([]*Commit, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -376,7 +378,7 @@ func (s *MergeRequestsService) GetMergeRequestCommits(pid interface{}, mergeRequ | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/merge_requests.html#get-single-mr-changes | |||
| func (s *MergeRequestsService) GetMergeRequestChanges(pid interface{}, mergeRequest int, options ...OptionFunc) (*MergeRequest, *Response, error) { | |||
| func (s *MergeRequestsService) GetMergeRequestChanges(pid interface{}, mergeRequest int, options ...RequestOptionFunc) (*MergeRequest, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -401,7 +403,7 @@ func (s *MergeRequestsService) GetMergeRequestChanges(pid interface{}, mergeRequ | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/merge_requests.html#list-mr-pipelines | |||
| func (s *MergeRequestsService) ListMergeRequestPipelines(pid interface{}, mergeRequest int, options ...OptionFunc) ([]*PipelineInfo, *Response, error) { | |||
| func (s *MergeRequestsService) ListMergeRequestPipelines(pid interface{}, mergeRequest int, options ...RequestOptionFunc) ([]*PipelineInfo, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -434,7 +436,7 @@ type GetIssuesClosedOnMergeOptions ListOptions | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/merge_requests.html#list-issues-that-will-close-on-merge | |||
| func (s *MergeRequestsService) GetIssuesClosedOnMerge(pid interface{}, mergeRequest int, opt *GetIssuesClosedOnMergeOptions, options ...OptionFunc) ([]*Issue, *Response, error) { | |||
| func (s *MergeRequestsService) GetIssuesClosedOnMerge(pid interface{}, mergeRequest int, opt *GetIssuesClosedOnMergeOptions, options ...RequestOptionFunc) ([]*Issue, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -479,7 +481,7 @@ type CreateMergeRequestOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/merge_requests.html#create-mr | |||
| func (s *MergeRequestsService) CreateMergeRequest(pid interface{}, opt *CreateMergeRequestOptions, options ...OptionFunc) (*MergeRequest, *Response, error) { | |||
| func (s *MergeRequestsService) CreateMergeRequest(pid interface{}, opt *CreateMergeRequestOptions, options ...RequestOptionFunc) (*MergeRequest, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -524,7 +526,7 @@ type UpdateMergeRequestOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/merge_requests.html#update-mr | |||
| func (s *MergeRequestsService) UpdateMergeRequest(pid interface{}, mergeRequest int, opt *UpdateMergeRequestOptions, options ...OptionFunc) (*MergeRequest, *Response, error) { | |||
| func (s *MergeRequestsService) UpdateMergeRequest(pid interface{}, mergeRequest int, opt *UpdateMergeRequestOptions, options ...RequestOptionFunc) (*MergeRequest, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -549,7 +551,7 @@ func (s *MergeRequestsService) UpdateMergeRequest(pid interface{}, mergeRequest | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/merge_requests.html#delete-a-merge-request | |||
| func (s *MergeRequestsService) DeleteMergeRequest(pid interface{}, mergeRequest int, options ...OptionFunc) (*Response, error) { | |||
| func (s *MergeRequestsService) DeleteMergeRequest(pid interface{}, mergeRequest int, options ...RequestOptionFunc) (*Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, err | |||
| @@ -585,7 +587,7 @@ type AcceptMergeRequestOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/merge_requests.html#accept-mr | |||
| func (s *MergeRequestsService) AcceptMergeRequest(pid interface{}, mergeRequest int, opt *AcceptMergeRequestOptions, options ...OptionFunc) (*MergeRequest, *Response, error) { | |||
| func (s *MergeRequestsService) AcceptMergeRequest(pid interface{}, mergeRequest int, opt *AcceptMergeRequestOptions, options ...RequestOptionFunc) (*MergeRequest, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -614,7 +616,7 @@ func (s *MergeRequestsService) AcceptMergeRequest(pid interface{}, mergeRequest | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/merge_requests.html#cancel-merge-when-pipeline-succeeds | |||
| func (s *MergeRequestsService) CancelMergeWhenPipelineSucceeds(pid interface{}, mergeRequest int, options ...OptionFunc) (*MergeRequest, *Response, error) { | |||
| func (s *MergeRequestsService) CancelMergeWhenPipelineSucceeds(pid interface{}, mergeRequest int, options ...RequestOptionFunc) (*MergeRequest, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -641,7 +643,7 @@ func (s *MergeRequestsService) CancelMergeWhenPipelineSucceeds(pid interface{}, | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/merge_requests.html#rebase-a-merge-request | |||
| func (s *MergeRequestsService) RebaseMergeRequest(pid interface{}, mergeRequest int, options ...OptionFunc) (*Response, error) { | |||
| func (s *MergeRequestsService) RebaseMergeRequest(pid interface{}, mergeRequest int, options ...RequestOptionFunc) (*Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, err | |||
| @@ -667,7 +669,7 @@ type GetMergeRequestDiffVersionsOptions ListOptions | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/merge_requests.html#get-mr-diff-versions | |||
| func (s *MergeRequestsService) GetMergeRequestDiffVersions(pid interface{}, mergeRequest int, opt *GetMergeRequestDiffVersionsOptions, options ...OptionFunc) ([]*MergeRequestDiffVersion, *Response, error) { | |||
| func (s *MergeRequestsService) GetMergeRequestDiffVersions(pid interface{}, mergeRequest int, opt *GetMergeRequestDiffVersionsOptions, options ...RequestOptionFunc) ([]*MergeRequestDiffVersion, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -692,7 +694,7 @@ func (s *MergeRequestsService) GetMergeRequestDiffVersions(pid interface{}, merg | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/merge_requests.html#get-a-single-mr-diff-version | |||
| func (s *MergeRequestsService) GetSingleMergeRequestDiffVersion(pid interface{}, mergeRequest, version int, options ...OptionFunc) (*MergeRequestDiffVersion, *Response, error) { | |||
| func (s *MergeRequestsService) GetSingleMergeRequestDiffVersion(pid interface{}, mergeRequest, version int, options ...RequestOptionFunc) (*MergeRequestDiffVersion, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -719,7 +721,7 @@ func (s *MergeRequestsService) GetSingleMergeRequestDiffVersion(pid interface{}, | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/merge_requests.html#subscribe-to-a-merge-request | |||
| func (s *MergeRequestsService) SubscribeToMergeRequest(pid interface{}, mergeRequest int, options ...OptionFunc) (*MergeRequest, *Response, error) { | |||
| func (s *MergeRequestsService) SubscribeToMergeRequest(pid interface{}, mergeRequest int, options ...RequestOptionFunc) (*MergeRequest, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -747,7 +749,7 @@ func (s *MergeRequestsService) SubscribeToMergeRequest(pid interface{}, mergeReq | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/merge_requests.html#unsubscribe-from-a-merge-request | |||
| func (s *MergeRequestsService) UnsubscribeFromMergeRequest(pid interface{}, mergeRequest int, options ...OptionFunc) (*MergeRequest, *Response, error) { | |||
| func (s *MergeRequestsService) UnsubscribeFromMergeRequest(pid interface{}, mergeRequest int, options ...RequestOptionFunc) (*MergeRequest, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -774,7 +776,7 @@ func (s *MergeRequestsService) UnsubscribeFromMergeRequest(pid interface{}, merg | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/merge_requests.html#create-a-todo | |||
| func (s *MergeRequestsService) CreateTodo(pid interface{}, mergeRequest int, options ...OptionFunc) (*Todo, *Response, error) { | |||
| func (s *MergeRequestsService) CreateTodo(pid interface{}, mergeRequest int, options ...RequestOptionFunc) (*Todo, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -799,7 +801,7 @@ func (s *MergeRequestsService) CreateTodo(pid interface{}, mergeRequest int, opt | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/merge_requests.html#set-a-time-estimate-for-a-merge-request | |||
| func (s *MergeRequestsService) SetTimeEstimate(pid interface{}, mergeRequest int, opt *SetTimeEstimateOptions, options ...OptionFunc) (*TimeStats, *Response, error) { | |||
| func (s *MergeRequestsService) SetTimeEstimate(pid interface{}, mergeRequest int, opt *SetTimeEstimateOptions, options ...RequestOptionFunc) (*TimeStats, *Response, error) { | |||
| return s.timeStats.setTimeEstimate(pid, "merge_requests", mergeRequest, opt, options...) | |||
| } | |||
| @@ -807,7 +809,7 @@ func (s *MergeRequestsService) SetTimeEstimate(pid interface{}, mergeRequest int | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/merge_requests.html#reset-the-time-estimate-for-a-merge-request | |||
| func (s *MergeRequestsService) ResetTimeEstimate(pid interface{}, mergeRequest int, options ...OptionFunc) (*TimeStats, *Response, error) { | |||
| func (s *MergeRequestsService) ResetTimeEstimate(pid interface{}, mergeRequest int, options ...RequestOptionFunc) (*TimeStats, *Response, error) { | |||
| return s.timeStats.resetTimeEstimate(pid, "merge_requests", mergeRequest, options...) | |||
| } | |||
| @@ -815,7 +817,7 @@ func (s *MergeRequestsService) ResetTimeEstimate(pid interface{}, mergeRequest i | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/merge_requests.html#add-spent-time-for-a-merge-request | |||
| func (s *MergeRequestsService) AddSpentTime(pid interface{}, mergeRequest int, opt *AddSpentTimeOptions, options ...OptionFunc) (*TimeStats, *Response, error) { | |||
| func (s *MergeRequestsService) AddSpentTime(pid interface{}, mergeRequest int, opt *AddSpentTimeOptions, options ...RequestOptionFunc) (*TimeStats, *Response, error) { | |||
| return s.timeStats.addSpentTime(pid, "merge_requests", mergeRequest, opt, options...) | |||
| } | |||
| @@ -823,7 +825,7 @@ func (s *MergeRequestsService) AddSpentTime(pid interface{}, mergeRequest int, o | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/merge_requests.html#reset-spent-time-for-a-merge-request | |||
| func (s *MergeRequestsService) ResetSpentTime(pid interface{}, mergeRequest int, options ...OptionFunc) (*TimeStats, *Response, error) { | |||
| func (s *MergeRequestsService) ResetSpentTime(pid interface{}, mergeRequest int, options ...RequestOptionFunc) (*TimeStats, *Response, error) { | |||
| return s.timeStats.resetSpentTime(pid, "merge_requests", mergeRequest, options...) | |||
| } | |||
| @@ -831,6 +833,6 @@ func (s *MergeRequestsService) ResetSpentTime(pid interface{}, mergeRequest int, | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/merge_requests.html#get-time-tracking-stats | |||
| func (s *MergeRequestsService) GetTimeSpent(pid interface{}, mergeRequest int, options ...OptionFunc) (*TimeStats, *Response, error) { | |||
| func (s *MergeRequestsService) GetTimeSpent(pid interface{}, mergeRequest int, options ...RequestOptionFunc) (*TimeStats, *Response, error) { | |||
| return s.timeStats.getTimeSpent(pid, "merge_requests", mergeRequest, options...) | |||
| } | |||
| @@ -65,7 +65,7 @@ type ListMilestonesOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/milestones.html#list-project-milestones | |||
| func (s *MilestonesService) ListMilestones(pid interface{}, opt *ListMilestonesOptions, options ...OptionFunc) ([]*Milestone, *Response, error) { | |||
| func (s *MilestonesService) ListMilestones(pid interface{}, opt *ListMilestonesOptions, options ...RequestOptionFunc) ([]*Milestone, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -90,7 +90,7 @@ func (s *MilestonesService) ListMilestones(pid interface{}, opt *ListMilestonesO | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/milestones.html#get-single-milestone | |||
| func (s *MilestonesService) GetMilestone(pid interface{}, milestone int, options ...OptionFunc) (*Milestone, *Response, error) { | |||
| func (s *MilestonesService) GetMilestone(pid interface{}, milestone int, options ...RequestOptionFunc) (*Milestone, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -126,7 +126,7 @@ type CreateMilestoneOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/milestones.html#create-new-milestone | |||
| func (s *MilestonesService) CreateMilestone(pid interface{}, opt *CreateMilestoneOptions, options ...OptionFunc) (*Milestone, *Response, error) { | |||
| func (s *MilestonesService) CreateMilestone(pid interface{}, opt *CreateMilestoneOptions, options ...RequestOptionFunc) (*Milestone, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -163,7 +163,7 @@ type UpdateMilestoneOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/milestones.html#edit-milestone | |||
| func (s *MilestonesService) UpdateMilestone(pid interface{}, milestone int, opt *UpdateMilestoneOptions, options ...OptionFunc) (*Milestone, *Response, error) { | |||
| func (s *MilestonesService) UpdateMilestone(pid interface{}, milestone int, opt *UpdateMilestoneOptions, options ...RequestOptionFunc) (*Milestone, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -188,7 +188,7 @@ func (s *MilestonesService) UpdateMilestone(pid interface{}, milestone int, opt | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/milestones.html#delete-project-milestone | |||
| func (s *MilestonesService) DeleteMilestone(pid interface{}, milestone int, options ...OptionFunc) (*Response, error) { | |||
| func (s *MilestonesService) DeleteMilestone(pid interface{}, milestone int, options ...RequestOptionFunc) (*Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, err | |||
| @@ -212,7 +212,7 @@ type GetMilestoneIssuesOptions ListOptions | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/milestones.html#get-all-issues-assigned-to-a-single-milestone | |||
| func (s *MilestonesService) GetMilestoneIssues(pid interface{}, milestone int, opt *GetMilestoneIssuesOptions, options ...OptionFunc) ([]*Issue, *Response, error) { | |||
| func (s *MilestonesService) GetMilestoneIssues(pid interface{}, milestone int, opt *GetMilestoneIssuesOptions, options ...RequestOptionFunc) ([]*Issue, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -245,7 +245,7 @@ type GetMilestoneMergeRequestsOptions ListOptions | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/milestones.html#get-all-merge-requests-assigned-to-a-single-milestone | |||
| func (s *MilestonesService) GetMilestoneMergeRequests(pid interface{}, milestone int, opt *GetMilestoneMergeRequestsOptions, options ...OptionFunc) ([]*MergeRequest, *Response, error) { | |||
| func (s *MilestonesService) GetMilestoneMergeRequests(pid interface{}, milestone int, opt *GetMilestoneMergeRequestsOptions, options ...RequestOptionFunc) ([]*MergeRequest, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -56,7 +56,7 @@ type ListNamespacesOptions struct { | |||
| // ListNamespaces gets a list of projects accessible by the authenticated user. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/namespaces.html#list-namespaces | |||
| func (s *NamespacesService) ListNamespaces(opt *ListNamespacesOptions, options ...OptionFunc) ([]*Namespace, *Response, error) { | |||
| func (s *NamespacesService) ListNamespaces(opt *ListNamespacesOptions, options ...RequestOptionFunc) ([]*Namespace, *Response, error) { | |||
| req, err := s.client.NewRequest("GET", "namespaces", opt, options) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -76,7 +76,7 @@ func (s *NamespacesService) ListNamespaces(opt *ListNamespacesOptions, options . | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/namespaces.html#search-for-namespace | |||
| func (s *NamespacesService) SearchNamespace(query string, options ...OptionFunc) ([]*Namespace, *Response, error) { | |||
| func (s *NamespacesService) SearchNamespace(query string, options ...RequestOptionFunc) ([]*Namespace, *Response, error) { | |||
| var q struct { | |||
| Search string `url:"search,omitempty" json:"search,omitempty"` | |||
| } | |||
| @@ -100,7 +100,7 @@ func (s *NamespacesService) SearchNamespace(query string, options ...OptionFunc) | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/namespaces.html#get-namespace-by-id | |||
| func (s *NamespacesService) GetNamespace(id interface{}, options ...OptionFunc) (*Namespace, *Response, error) { | |||
| func (s *NamespacesService) GetNamespace(id interface{}, options ...RequestOptionFunc) (*Namespace, *Response, error) { | |||
| namespace, err := parseID(id) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -102,7 +102,7 @@ type ListIssueNotesOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/notes.html#list-project-issue-notes | |||
| func (s *NotesService) ListIssueNotes(pid interface{}, issue int, opt *ListIssueNotesOptions, options ...OptionFunc) ([]*Note, *Response, error) { | |||
| func (s *NotesService) ListIssueNotes(pid interface{}, issue int, opt *ListIssueNotesOptions, options ...RequestOptionFunc) ([]*Note, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -127,7 +127,7 @@ func (s *NotesService) ListIssueNotes(pid interface{}, issue int, opt *ListIssue | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/notes.html#get-single-issue-note | |||
| func (s *NotesService) GetIssueNote(pid interface{}, issue, note int, options ...OptionFunc) (*Note, *Response, error) { | |||
| func (s *NotesService) GetIssueNote(pid interface{}, issue, note int, options ...RequestOptionFunc) (*Note, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -162,7 +162,7 @@ type CreateIssueNoteOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/notes.html#create-new-issue-note | |||
| func (s *NotesService) CreateIssueNote(pid interface{}, issue int, opt *CreateIssueNoteOptions, options ...OptionFunc) (*Note, *Response, error) { | |||
| func (s *NotesService) CreateIssueNote(pid interface{}, issue int, opt *CreateIssueNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -196,7 +196,7 @@ type UpdateIssueNoteOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/notes.html#modify-existing-issue-note | |||
| func (s *NotesService) UpdateIssueNote(pid interface{}, issue, note int, opt *UpdateIssueNoteOptions, options ...OptionFunc) (*Note, *Response, error) { | |||
| func (s *NotesService) UpdateIssueNote(pid interface{}, issue, note int, opt *UpdateIssueNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -221,7 +221,7 @@ func (s *NotesService) UpdateIssueNote(pid interface{}, issue, note int, opt *Up | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/notes.html#delete-an-issue-note | |||
| func (s *NotesService) DeleteIssueNote(pid interface{}, issue, note int, options ...OptionFunc) (*Response, error) { | |||
| func (s *NotesService) DeleteIssueNote(pid interface{}, issue, note int, options ...RequestOptionFunc) (*Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, err | |||
| @@ -251,7 +251,7 @@ type ListSnippetNotesOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/notes.html#list-all-snippet-notes | |||
| func (s *NotesService) ListSnippetNotes(pid interface{}, snippet int, opt *ListSnippetNotesOptions, options ...OptionFunc) ([]*Note, *Response, error) { | |||
| func (s *NotesService) ListSnippetNotes(pid interface{}, snippet int, opt *ListSnippetNotesOptions, options ...RequestOptionFunc) ([]*Note, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -276,7 +276,7 @@ func (s *NotesService) ListSnippetNotes(pid interface{}, snippet int, opt *ListS | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/notes.html#get-single-snippet-note | |||
| func (s *NotesService) GetSnippetNote(pid interface{}, snippet, note int, options ...OptionFunc) (*Note, *Response, error) { | |||
| func (s *NotesService) GetSnippetNote(pid interface{}, snippet, note int, options ...RequestOptionFunc) (*Note, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -311,7 +311,7 @@ type CreateSnippetNoteOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/notes.html#create-new-snippet-note | |||
| func (s *NotesService) CreateSnippetNote(pid interface{}, snippet int, opt *CreateSnippetNoteOptions, options ...OptionFunc) (*Note, *Response, error) { | |||
| func (s *NotesService) CreateSnippetNote(pid interface{}, snippet int, opt *CreateSnippetNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -345,7 +345,7 @@ type UpdateSnippetNoteOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/notes.html#modify-existing-snippet-note | |||
| func (s *NotesService) UpdateSnippetNote(pid interface{}, snippet, note int, opt *UpdateSnippetNoteOptions, options ...OptionFunc) (*Note, *Response, error) { | |||
| func (s *NotesService) UpdateSnippetNote(pid interface{}, snippet, note int, opt *UpdateSnippetNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -370,7 +370,7 @@ func (s *NotesService) UpdateSnippetNote(pid interface{}, snippet, note int, opt | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/notes.html#delete-a-snippet-note | |||
| func (s *NotesService) DeleteSnippetNote(pid interface{}, snippet, note int, options ...OptionFunc) (*Response, error) { | |||
| func (s *NotesService) DeleteSnippetNote(pid interface{}, snippet, note int, options ...RequestOptionFunc) (*Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, err | |||
| @@ -400,7 +400,7 @@ type ListMergeRequestNotesOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/notes.html#list-all-merge-request-notes | |||
| func (s *NotesService) ListMergeRequestNotes(pid interface{}, mergeRequest int, opt *ListMergeRequestNotesOptions, options ...OptionFunc) ([]*Note, *Response, error) { | |||
| func (s *NotesService) ListMergeRequestNotes(pid interface{}, mergeRequest int, opt *ListMergeRequestNotesOptions, options ...RequestOptionFunc) ([]*Note, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -425,7 +425,7 @@ func (s *NotesService) ListMergeRequestNotes(pid interface{}, mergeRequest int, | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/notes.html#get-single-merge-request-note | |||
| func (s *NotesService) GetMergeRequestNote(pid interface{}, mergeRequest, note int, options ...OptionFunc) (*Note, *Response, error) { | |||
| func (s *NotesService) GetMergeRequestNote(pid interface{}, mergeRequest, note int, options ...RequestOptionFunc) (*Note, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -459,7 +459,7 @@ type CreateMergeRequestNoteOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/notes.html#create-new-merge-request-note | |||
| func (s *NotesService) CreateMergeRequestNote(pid interface{}, mergeRequest int, opt *CreateMergeRequestNoteOptions, options ...OptionFunc) (*Note, *Response, error) { | |||
| func (s *NotesService) CreateMergeRequestNote(pid interface{}, mergeRequest int, opt *CreateMergeRequestNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -493,7 +493,7 @@ type UpdateMergeRequestNoteOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/notes.html#modify-existing-merge-request-note | |||
| func (s *NotesService) UpdateMergeRequestNote(pid interface{}, mergeRequest, note int, opt *UpdateMergeRequestNoteOptions, options ...OptionFunc) (*Note, *Response, error) { | |||
| func (s *NotesService) UpdateMergeRequestNote(pid interface{}, mergeRequest, note int, opt *UpdateMergeRequestNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -518,7 +518,7 @@ func (s *NotesService) UpdateMergeRequestNote(pid interface{}, mergeRequest, not | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/notes.html#delete-a-merge-request-note | |||
| func (s *NotesService) DeleteMergeRequestNote(pid interface{}, mergeRequest, note int, options ...OptionFunc) (*Response, error) { | |||
| func (s *NotesService) DeleteMergeRequestNote(pid interface{}, mergeRequest, note int, options ...RequestOptionFunc) (*Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, err | |||
| @@ -548,7 +548,7 @@ type ListEpicNotesOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/notes.html#list-all-epic-notes | |||
| func (s *NotesService) ListEpicNotes(gid interface{}, epic int, opt *ListEpicNotesOptions, options ...OptionFunc) ([]*Note, *Response, error) { | |||
| func (s *NotesService) ListEpicNotes(gid interface{}, epic int, opt *ListEpicNotesOptions, options ...RequestOptionFunc) ([]*Note, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -573,7 +573,7 @@ func (s *NotesService) ListEpicNotes(gid interface{}, epic int, opt *ListEpicNot | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/notes.html#get-single-epic-note | |||
| func (s *NotesService) GetEpicNote(gid interface{}, epic, note int, options ...OptionFunc) (*Note, *Response, error) { | |||
| func (s *NotesService) GetEpicNote(gid interface{}, epic, note int, options ...RequestOptionFunc) (*Note, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -606,7 +606,7 @@ type CreateEpicNoteOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/notes.html#create-new-epic-note | |||
| func (s *NotesService) CreateEpicNote(gid interface{}, epic int, opt *CreateEpicNoteOptions, options ...OptionFunc) (*Note, *Response, error) { | |||
| func (s *NotesService) CreateEpicNote(gid interface{}, epic int, opt *CreateEpicNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -638,7 +638,7 @@ type UpdateEpicNoteOptions struct { | |||
| // UpdateEpicNote modifies existing note of an epic. | |||
| // | |||
| // https://docs.gitlab.com/ee/api/notes.html#modify-existing-epic-note | |||
| func (s *NotesService) UpdateEpicNote(gid interface{}, epic, note int, opt *UpdateEpicNoteOptions, options ...OptionFunc) (*Note, *Response, error) { | |||
| func (s *NotesService) UpdateEpicNote(gid interface{}, epic, note int, opt *UpdateEpicNoteOptions, options ...RequestOptionFunc) (*Note, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -662,7 +662,7 @@ func (s *NotesService) UpdateEpicNote(gid interface{}, epic, note int, opt *Upda | |||
| // DeleteEpicNote deletes an existing note of a merge request. | |||
| // | |||
| // https://docs.gitlab.com/ee/api/notes.html#delete-an-epic-note | |||
| func (s *NotesService) DeleteEpicNote(gid interface{}, epic, note int, options ...OptionFunc) (*Response, error) { | |||
| func (s *NotesService) DeleteEpicNote(gid interface{}, epic, note int, options ...RequestOptionFunc) (*Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, err | |||
| @@ -50,7 +50,7 @@ func (ns NotificationSettings) String() string { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/notification_settings.html#global-notification-settings | |||
| func (s *NotificationSettingsService) GetGlobalSettings(options ...OptionFunc) (*NotificationSettings, *Response, error) { | |||
| func (s *NotificationSettingsService) GetGlobalSettings(options ...RequestOptionFunc) (*NotificationSettings, *Response, error) { | |||
| u := "notification_settings" | |||
| req, err := s.client.NewRequest("GET", u, nil, options) | |||
| @@ -90,7 +90,7 @@ type NotificationSettingsOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/notification_settings.html#update-global-notification-settings | |||
| func (s *NotificationSettingsService) UpdateGlobalSettings(opt *NotificationSettingsOptions, options ...OptionFunc) (*NotificationSettings, *Response, error) { | |||
| func (s *NotificationSettingsService) UpdateGlobalSettings(opt *NotificationSettingsOptions, options ...RequestOptionFunc) (*NotificationSettings, *Response, error) { | |||
| if opt.Level != nil && *opt.Level == GlobalNotificationLevel { | |||
| return nil, nil, errors.New( | |||
| "notification level 'global' is not valid for global notification settings") | |||
| @@ -116,7 +116,7 @@ func (s *NotificationSettingsService) UpdateGlobalSettings(opt *NotificationSett | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/notification_settings.html#group-project-level-notification-settings | |||
| func (s *NotificationSettingsService) GetSettingsForGroup(gid interface{}, options ...OptionFunc) (*NotificationSettings, *Response, error) { | |||
| func (s *NotificationSettingsService) GetSettingsForGroup(gid interface{}, options ...RequestOptionFunc) (*NotificationSettings, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -141,7 +141,7 @@ func (s *NotificationSettingsService) GetSettingsForGroup(gid interface{}, optio | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/notification_settings.html#group-project-level-notification-settings | |||
| func (s *NotificationSettingsService) GetSettingsForProject(pid interface{}, options ...OptionFunc) (*NotificationSettings, *Response, error) { | |||
| func (s *NotificationSettingsService) GetSettingsForProject(pid interface{}, options ...RequestOptionFunc) (*NotificationSettings, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -166,7 +166,7 @@ func (s *NotificationSettingsService) GetSettingsForProject(pid interface{}, opt | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/notification_settings.html#update-group-project-level-notification-settings | |||
| func (s *NotificationSettingsService) UpdateSettingsForGroup(gid interface{}, opt *NotificationSettingsOptions, options ...OptionFunc) (*NotificationSettings, *Response, error) { | |||
| func (s *NotificationSettingsService) UpdateSettingsForGroup(gid interface{}, opt *NotificationSettingsOptions, options ...RequestOptionFunc) (*NotificationSettings, *Response, error) { | |||
| group, err := parseID(gid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -191,7 +191,7 @@ func (s *NotificationSettingsService) UpdateSettingsForGroup(gid interface{}, op | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/notification_settings.html#update-group-project-level-notification-settings | |||
| func (s *NotificationSettingsService) UpdateSettingsForProject(pid interface{}, opt *NotificationSettingsOptions, options ...OptionFunc) (*NotificationSettings, *Response, error) { | |||
| func (s *NotificationSettingsService) UpdateSettingsForProject(pid interface{}, opt *NotificationSettingsOptions, options ...RequestOptionFunc) (*NotificationSettings, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -18,6 +18,7 @@ type PagesDomainsService struct { | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/pages_domains.html | |||
| type PagesDomain struct { | |||
| Domain string `json:"domain"` | |||
| AutoSslEnabled bool `json:"auto_ssl_enabled"` | |||
| URL string `json:"url"` | |||
| ProjectID int `json:"project_id"` | |||
| Verified bool `json:"verified"` | |||
| @@ -39,7 +40,7 @@ type ListPagesDomainsOptions ListOptions | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/pages_domains.html#list-pages-domains | |||
| func (s *PagesDomainsService) ListPagesDomains(pid interface{}, opt *ListPagesDomainsOptions, options ...OptionFunc) ([]*PagesDomain, *Response, error) { | |||
| func (s *PagesDomainsService) ListPagesDomains(pid interface{}, opt *ListPagesDomainsOptions, options ...RequestOptionFunc) ([]*PagesDomain, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -64,7 +65,7 @@ func (s *PagesDomainsService) ListPagesDomains(pid interface{}, opt *ListPagesDo | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/pages_domains.html#list-all-pages-domains | |||
| func (s *PagesDomainsService) ListAllPagesDomains(options ...OptionFunc) ([]*PagesDomain, *Response, error) { | |||
| func (s *PagesDomainsService) ListAllPagesDomains(options ...RequestOptionFunc) ([]*PagesDomain, *Response, error) { | |||
| req, err := s.client.NewRequest("GET", "pages/domains", nil, options) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -83,7 +84,7 @@ func (s *PagesDomainsService) ListAllPagesDomains(options ...OptionFunc) ([]*Pag | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/pages_domains.html#single-pages-domain | |||
| func (s *PagesDomainsService) GetPagesDomain(pid interface{}, domain string, options ...OptionFunc) (*PagesDomain, *Response, error) { | |||
| func (s *PagesDomainsService) GetPagesDomain(pid interface{}, domain string, options ...RequestOptionFunc) (*PagesDomain, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -107,18 +108,19 @@ func (s *PagesDomainsService) GetPagesDomain(pid interface{}, domain string, opt | |||
| // CreatePagesDomainOptions represents the available CreatePagesDomain() options. | |||
| // | |||
| // GitLab API docs: | |||
| // // https://docs.gitlab.com/ce/api/pages_domains.html#create-new-pages-domain | |||
| // https://docs.gitlab.com/ce/api/pages_domains.html#create-new-pages-domain | |||
| type CreatePagesDomainOptions struct { | |||
| Domain *string `url:"domain,omitempty" json:"domain,omitempty"` | |||
| Certificate *string `url:"certifiate,omitempty" json:"certifiate,omitempty"` | |||
| Key *string `url:"key,omitempty" json:"key,omitempty"` | |||
| Domain *string `url:"domain,omitempty" json:"domain,omitempty"` | |||
| AutoSslEnabled *bool `url:"auto_ssl_enabled,omitempty" json:"auto_ssl_enabled,omitempty"` | |||
| Certificate *string `url:"certifiate,omitempty" json:"certifiate,omitempty"` | |||
| Key *string `url:"key,omitempty" json:"key,omitempty"` | |||
| } | |||
| // CreatePagesDomain creates a new project pages domain. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/pages_domains.html#create-new-pages-domain | |||
| func (s *PagesDomainsService) CreatePagesDomain(pid interface{}, opt *CreatePagesDomainOptions, options ...OptionFunc) (*PagesDomain, *Response, error) { | |||
| func (s *PagesDomainsService) CreatePagesDomain(pid interface{}, opt *CreatePagesDomainOptions, options ...RequestOptionFunc) (*PagesDomain, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -144,15 +146,16 @@ func (s *PagesDomainsService) CreatePagesDomain(pid interface{}, opt *CreatePage | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/pages_domains.html#update-pages-domain | |||
| type UpdatePagesDomainOptions struct { | |||
| Cerificate *string `url:"certifiate" json:"certifiate"` | |||
| Key *string `url:"key" json:"key"` | |||
| AutoSslEnabled *bool `url:"auto_ssl_enabled,omitempty" json:"auto_ssl_enabled,omitempty"` | |||
| Certificate *string `url:"certifiate,omitempty" json:"certifiate,omitempty"` | |||
| Key *string `url:"key,omitempty" json:"key,omitempty"` | |||
| } | |||
| // UpdatePagesDomain updates an existing project pages domain. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/pages_domains.html#update-pages-domain | |||
| func (s *PagesDomainsService) UpdatePagesDomain(pid interface{}, domain string, opt *UpdatePagesDomainOptions, options ...OptionFunc) (*PagesDomain, *Response, error) { | |||
| func (s *PagesDomainsService) UpdatePagesDomain(pid interface{}, domain string, opt *UpdatePagesDomainOptions, options ...RequestOptionFunc) (*PagesDomain, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -177,7 +180,7 @@ func (s *PagesDomainsService) UpdatePagesDomain(pid interface{}, domain string, | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/pages_domains.html#delete-pages-domain | |||
| func (s *PagesDomainsService) DeletePagesDomain(pid interface{}, domain string, options ...OptionFunc) (*Response, error) { | |||
| func (s *PagesDomainsService) DeletePagesDomain(pid interface{}, domain string, options ...RequestOptionFunc) (*Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, err | |||
| @@ -63,7 +63,7 @@ type ListPipelineSchedulesOptions ListOptions | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/pipeline_schedules.html | |||
| func (s *PipelineSchedulesService) ListPipelineSchedules(pid interface{}, opt *ListPipelineSchedulesOptions, options ...OptionFunc) ([]*PipelineSchedule, *Response, error) { | |||
| func (s *PipelineSchedulesService) ListPipelineSchedules(pid interface{}, opt *ListPipelineSchedulesOptions, options ...RequestOptionFunc) ([]*PipelineSchedule, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -88,7 +88,7 @@ func (s *PipelineSchedulesService) ListPipelineSchedules(pid interface{}, opt *L | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/pipeline_schedules.html | |||
| func (s *PipelineSchedulesService) GetPipelineSchedule(pid interface{}, schedule int, options ...OptionFunc) (*PipelineSchedule, *Response, error) { | |||
| func (s *PipelineSchedulesService) GetPipelineSchedule(pid interface{}, schedule int, options ...RequestOptionFunc) (*PipelineSchedule, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -126,7 +126,7 @@ type CreatePipelineScheduleOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/pipeline_schedules.html#create-a-new-pipeline-schedule | |||
| func (s *PipelineSchedulesService) CreatePipelineSchedule(pid interface{}, opt *CreatePipelineScheduleOptions, options ...OptionFunc) (*PipelineSchedule, *Response, error) { | |||
| func (s *PipelineSchedulesService) CreatePipelineSchedule(pid interface{}, opt *CreatePipelineScheduleOptions, options ...RequestOptionFunc) (*PipelineSchedule, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -164,7 +164,7 @@ type EditPipelineScheduleOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/pipeline_schedules.html#edit-a-pipeline-schedule | |||
| func (s *PipelineSchedulesService) EditPipelineSchedule(pid interface{}, schedule int, opt *EditPipelineScheduleOptions, options ...OptionFunc) (*PipelineSchedule, *Response, error) { | |||
| func (s *PipelineSchedulesService) EditPipelineSchedule(pid interface{}, schedule int, opt *EditPipelineScheduleOptions, options ...RequestOptionFunc) (*PipelineSchedule, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -190,7 +190,7 @@ func (s *PipelineSchedulesService) EditPipelineSchedule(pid interface{}, schedul | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/pipeline_schedules.html#take-ownership-of-a-pipeline-schedule | |||
| func (s *PipelineSchedulesService) TakeOwnershipOfPipelineSchedule(pid interface{}, schedule int, options ...OptionFunc) (*PipelineSchedule, *Response, error) { | |||
| func (s *PipelineSchedulesService) TakeOwnershipOfPipelineSchedule(pid interface{}, schedule int, options ...RequestOptionFunc) (*PipelineSchedule, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -215,7 +215,7 @@ func (s *PipelineSchedulesService) TakeOwnershipOfPipelineSchedule(pid interface | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/pipeline_schedules.html#delete-a-pipeline-schedule | |||
| func (s *PipelineSchedulesService) DeletePipelineSchedule(pid interface{}, schedule int, options ...OptionFunc) (*Response, error) { | |||
| func (s *PipelineSchedulesService) DeletePipelineSchedule(pid interface{}, schedule int, options ...RequestOptionFunc) (*Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, err | |||
| @@ -245,7 +245,7 @@ type CreatePipelineScheduleVariableOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/pipeline_schedules.html#create-a-new-pipeline-schedule | |||
| func (s *PipelineSchedulesService) CreatePipelineScheduleVariable(pid interface{}, schedule int, opt *CreatePipelineScheduleVariableOptions, options ...OptionFunc) (*PipelineVariable, *Response, error) { | |||
| func (s *PipelineSchedulesService) CreatePipelineScheduleVariable(pid interface{}, schedule int, opt *CreatePipelineScheduleVariableOptions, options ...RequestOptionFunc) (*PipelineVariable, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -280,7 +280,7 @@ type EditPipelineScheduleVariableOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/pipeline_schedules.html#edit-a-pipeline-schedule-variable | |||
| func (s *PipelineSchedulesService) EditPipelineScheduleVariable(pid interface{}, schedule int, key string, opt *EditPipelineScheduleVariableOptions, options ...OptionFunc) (*PipelineVariable, *Response, error) { | |||
| func (s *PipelineSchedulesService) EditPipelineScheduleVariable(pid interface{}, schedule int, key string, opt *EditPipelineScheduleVariableOptions, options ...RequestOptionFunc) (*PipelineVariable, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -305,7 +305,7 @@ func (s *PipelineSchedulesService) EditPipelineScheduleVariable(pid interface{}, | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/pipeline_schedules.html#delete-a-pipeline-schedule-variable | |||
| func (s *PipelineSchedulesService) DeletePipelineScheduleVariable(pid interface{}, schedule int, key string, options ...OptionFunc) (*PipelineVariable, *Response, error) { | |||
| func (s *PipelineSchedulesService) DeletePipelineScheduleVariable(pid interface{}, schedule int, key string, options ...RequestOptionFunc) (*PipelineVariable, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -38,7 +38,7 @@ type ListPipelineTriggersOptions ListOptions | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/pipeline_triggers.html#list-project-triggers | |||
| func (s *PipelineTriggersService) ListPipelineTriggers(pid interface{}, opt *ListPipelineTriggersOptions, options ...OptionFunc) ([]*PipelineTrigger, *Response, error) { | |||
| func (s *PipelineTriggersService) ListPipelineTriggers(pid interface{}, opt *ListPipelineTriggersOptions, options ...RequestOptionFunc) ([]*PipelineTrigger, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -63,7 +63,7 @@ func (s *PipelineTriggersService) ListPipelineTriggers(pid interface{}, opt *Lis | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/pipeline_triggers.html#get-trigger-details | |||
| func (s *PipelineTriggersService) GetPipelineTrigger(pid interface{}, trigger int, options ...OptionFunc) (*PipelineTrigger, *Response, error) { | |||
| func (s *PipelineTriggersService) GetPipelineTrigger(pid interface{}, trigger int, options ...RequestOptionFunc) (*PipelineTrigger, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -96,7 +96,7 @@ type AddPipelineTriggerOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/pipeline_triggers.html#create-a-project-trigger | |||
| func (s *PipelineTriggersService) AddPipelineTrigger(pid interface{}, opt *AddPipelineTriggerOptions, options ...OptionFunc) (*PipelineTrigger, *Response, error) { | |||
| func (s *PipelineTriggersService) AddPipelineTrigger(pid interface{}, opt *AddPipelineTriggerOptions, options ...RequestOptionFunc) (*PipelineTrigger, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -129,7 +129,7 @@ type EditPipelineTriggerOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/pipeline_triggers.html#update-a-project-trigger | |||
| func (s *PipelineTriggersService) EditPipelineTrigger(pid interface{}, trigger int, opt *EditPipelineTriggerOptions, options ...OptionFunc) (*PipelineTrigger, *Response, error) { | |||
| func (s *PipelineTriggersService) EditPipelineTrigger(pid interface{}, trigger int, opt *EditPipelineTriggerOptions, options ...RequestOptionFunc) (*PipelineTrigger, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -155,7 +155,7 @@ func (s *PipelineTriggersService) EditPipelineTrigger(pid interface{}, trigger i | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/pipeline_triggers.html#take-ownership-of-a-project-trigger | |||
| func (s *PipelineTriggersService) TakeOwnershipOfPipelineTrigger(pid interface{}, trigger int, options ...OptionFunc) (*PipelineTrigger, *Response, error) { | |||
| func (s *PipelineTriggersService) TakeOwnershipOfPipelineTrigger(pid interface{}, trigger int, options ...RequestOptionFunc) (*PipelineTrigger, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -180,7 +180,7 @@ func (s *PipelineTriggersService) TakeOwnershipOfPipelineTrigger(pid interface{} | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/pipeline_triggers.html#remove-a-project-trigger | |||
| func (s *PipelineTriggersService) DeletePipelineTrigger(pid interface{}, trigger int, options ...OptionFunc) (*Response, error) { | |||
| func (s *PipelineTriggersService) DeletePipelineTrigger(pid interface{}, trigger int, options ...RequestOptionFunc) (*Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, err | |||
| @@ -209,7 +209,7 @@ type RunPipelineTriggerOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/ci/triggers/README.html#triggering-a-pipeline | |||
| func (s *PipelineTriggersService) RunPipelineTrigger(pid interface{}, opt *RunPipelineTriggerOptions, options ...OptionFunc) (*Pipeline, *Response, error) { | |||
| func (s *PipelineTriggersService) RunPipelineTrigger(pid interface{}, opt *RunPipelineTriggerOptions, options ...RequestOptionFunc) (*Pipeline, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -101,21 +101,23 @@ func (p PipelineInfo) String() string { | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/pipelines.html#list-project-pipelines | |||
| type ListProjectPipelinesOptions struct { | |||
| ListOptions | |||
| Scope *string `url:"scope,omitempty" json:"scope,omitempty"` | |||
| Status *BuildStateValue `url:"status,omitempty" json:"status,omitempty"` | |||
| Ref *string `url:"ref,omitempty" json:"ref,omitempty"` | |||
| SHA *string `url:"sha,omitempty" json:"sha,omitempty"` | |||
| YamlErrors *bool `url:"yaml_errors,omitempty" json:"yaml_errors,omitempty"` | |||
| Name *string `url:"name,omitempty" json:"name,omitempty"` | |||
| Username *string `url:"username,omitempty" json:"username,omitempty"` | |||
| OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"` | |||
| Sort *string `url:"sort,omitempty" json:"sort,omitempty"` | |||
| Scope *string `url:"scope,omitempty" json:"scope,omitempty"` | |||
| Status *BuildStateValue `url:"status,omitempty" json:"status,omitempty"` | |||
| Ref *string `url:"ref,omitempty" json:"ref,omitempty"` | |||
| SHA *string `url:"sha,omitempty" json:"sha,omitempty"` | |||
| YamlErrors *bool `url:"yaml_errors,omitempty" json:"yaml_errors,omitempty"` | |||
| Name *string `url:"name,omitempty" json:"name,omitempty"` | |||
| Username *string `url:"username,omitempty" json:"username,omitempty"` | |||
| UpdatedAfter *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"` | |||
| UpdatedBefore *time.Time `url:"update_before,omitempty" json:"updated_before,omitempty"` | |||
| OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"` | |||
| Sort *string `url:"sort,omitempty" json:"sort,omitempty"` | |||
| } | |||
| // ListProjectPipelines gets a list of project piplines. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/pipelines.html#list-project-pipelines | |||
| func (s *PipelinesService) ListProjectPipelines(pid interface{}, opt *ListProjectPipelinesOptions, options ...OptionFunc) ([]*PipelineInfo, *Response, error) { | |||
| func (s *PipelinesService) ListProjectPipelines(pid interface{}, opt *ListProjectPipelinesOptions, options ...RequestOptionFunc) ([]*PipelineInfo, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -139,7 +141,7 @@ func (s *PipelinesService) ListProjectPipelines(pid interface{}, opt *ListProjec | |||
| // GetPipeline gets a single project pipeline. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/pipelines.html#get-a-single-pipeline | |||
| func (s *PipelinesService) GetPipeline(pid interface{}, pipeline int, options ...OptionFunc) (*Pipeline, *Response, error) { | |||
| func (s *PipelinesService) GetPipeline(pid interface{}, pipeline int, options ...RequestOptionFunc) (*Pipeline, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -163,7 +165,7 @@ func (s *PipelinesService) GetPipeline(pid interface{}, pipeline int, options .. | |||
| // GetPipelineVariables gets the variables of a single project pipeline. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/pipelines.html#get-variables-of-a-pipeline | |||
| func (s *PipelinesService) GetPipelineVariables(pid interface{}, pipeline int, options ...OptionFunc) ([]*PipelineVariable, *Response, error) { | |||
| func (s *PipelinesService) GetPipelineVariables(pid interface{}, pipeline int, options ...RequestOptionFunc) ([]*PipelineVariable, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -195,7 +197,7 @@ type CreatePipelineOptions struct { | |||
| // CreatePipeline creates a new project pipeline. | |||
| // | |||
| // GitLab API docs: https://docs.gitlab.com/ce/api/pipelines.html#create-a-new-pipeline | |||
| func (s *PipelinesService) CreatePipeline(pid interface{}, opt *CreatePipelineOptions, options ...OptionFunc) (*Pipeline, *Response, error) { | |||
| func (s *PipelinesService) CreatePipeline(pid interface{}, opt *CreatePipelineOptions, options ...RequestOptionFunc) (*Pipeline, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -220,7 +222,7 @@ func (s *PipelinesService) CreatePipeline(pid interface{}, opt *CreatePipelineOp | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/pipelines.html#retry-failed-builds-in-a-pipeline | |||
| func (s *PipelinesService) RetryPipelineBuild(pid interface{}, pipeline int, options ...OptionFunc) (*Pipeline, *Response, error) { | |||
| func (s *PipelinesService) RetryPipelineBuild(pid interface{}, pipeline int, options ...RequestOptionFunc) (*Pipeline, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -245,7 +247,7 @@ func (s *PipelinesService) RetryPipelineBuild(pid interface{}, pipeline int, opt | |||
| // | |||
| // GitLab API docs: | |||
| //https://docs.gitlab.com/ce/api/pipelines.html#cancel-a-pipelines-builds | |||
| func (s *PipelinesService) CancelPipelineBuild(pid interface{}, pipeline int, options ...OptionFunc) (*Pipeline, *Response, error) { | |||
| func (s *PipelinesService) CancelPipelineBuild(pid interface{}, pipeline int, options ...RequestOptionFunc) (*Pipeline, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -270,7 +272,7 @@ func (s *PipelinesService) CancelPipelineBuild(pid interface{}, pipeline int, op | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/pipelines.html#delete-a-pipeline | |||
| func (s *PipelinesService) DeletePipeline(pid interface{}, pipeline int, options ...OptionFunc) (*Response, error) { | |||
| func (s *PipelinesService) DeletePipeline(pid interface{}, pipeline int, options ...RequestOptionFunc) (*Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, err | |||
| @@ -37,7 +37,7 @@ type ListProjectBadgesOptions ListOptions | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/project_badges.html#list-all-badges-of-a-project | |||
| func (s *ProjectBadgesService) ListProjectBadges(pid interface{}, opt *ListProjectBadgesOptions, options ...OptionFunc) ([]*ProjectBadge, *Response, error) { | |||
| func (s *ProjectBadgesService) ListProjectBadges(pid interface{}, opt *ListProjectBadgesOptions, options ...RequestOptionFunc) ([]*ProjectBadge, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -62,7 +62,7 @@ func (s *ProjectBadgesService) ListProjectBadges(pid interface{}, opt *ListProje | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/project_badges.html#get-a-badge-of-a-project | |||
| func (s *ProjectBadgesService) GetProjectBadge(pid interface{}, badge int, options ...OptionFunc) (*ProjectBadge, *Response, error) { | |||
| func (s *ProjectBadgesService) GetProjectBadge(pid interface{}, badge int, options ...RequestOptionFunc) (*ProjectBadge, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -96,7 +96,7 @@ type AddProjectBadgeOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/project_badges.html#add-a-badge-to-a-project | |||
| func (s *ProjectBadgesService) AddProjectBadge(pid interface{}, opt *AddProjectBadgeOptions, options ...OptionFunc) (*ProjectBadge, *Response, error) { | |||
| func (s *ProjectBadgesService) AddProjectBadge(pid interface{}, opt *AddProjectBadgeOptions, options ...RequestOptionFunc) (*ProjectBadge, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -130,7 +130,7 @@ type EditProjectBadgeOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/project_badges.html#edit-a-badge-of-a-project | |||
| func (s *ProjectBadgesService) EditProjectBadge(pid interface{}, badge int, opt *EditProjectBadgeOptions, options ...OptionFunc) (*ProjectBadge, *Response, error) { | |||
| func (s *ProjectBadgesService) EditProjectBadge(pid interface{}, badge int, opt *EditProjectBadgeOptions, options ...RequestOptionFunc) (*ProjectBadge, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -156,7 +156,7 @@ func (s *ProjectBadgesService) EditProjectBadge(pid interface{}, badge int, opt | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/project_badges.html#remove-a-badge-from-a-project | |||
| func (s *ProjectBadgesService) DeleteProjectBadge(pid interface{}, badge int, options ...OptionFunc) (*Response, error) { | |||
| func (s *ProjectBadgesService) DeleteProjectBadge(pid interface{}, badge int, options ...RequestOptionFunc) (*Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, err | |||
| @@ -185,7 +185,7 @@ type ProjectBadgePreviewOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/project_badges.html#preview-a-badge-from-a-project | |||
| func (s *ProjectBadgesService) PreviewProjectBadge(pid interface{}, opt *ProjectBadgePreviewOptions, options ...OptionFunc) (*ProjectBadge, *Response, error) { | |||
| func (s *ProjectBadgesService) PreviewProjectBadge(pid interface{}, opt *ProjectBadgePreviewOptions, options ...RequestOptionFunc) (*ProjectBadge, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -44,6 +44,7 @@ type ProjectCluster struct { | |||
| ClusterType string `json:"cluster_type"` | |||
| User *User `json:"user"` | |||
| PlatformKubernetes *PlatformKubernetes `json:"platform_kubernetes"` | |||
| ManagementProject *ManagementProject `json:"management_project"` | |||
| Project *Project `json:"project"` | |||
| } | |||
| @@ -60,11 +61,22 @@ type PlatformKubernetes struct { | |||
| AuthorizationType string `json:"authorization_type"` | |||
| } | |||
| // ManagementProject represents a GitLab Project Cluster management_project. | |||
| type ManagementProject struct { | |||
| ID int `json:"id"` | |||
| Description string `json:"description"` | |||
| Name string `json:"name"` | |||
| NameWithNamespace string `json:"name_with_namespace"` | |||
| Path string `json:"path"` | |||
| PathWithNamespace string `json:"path_with_namespace"` | |||
| CreatedAt *time.Time `json:"created_at"` | |||
| } | |||
| // ListClusters gets a list of all clusters in a project. | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/project_clusters.html#list-project-clusters | |||
| func (s *ProjectClustersService) ListClusters(pid interface{}, options ...OptionFunc) ([]*ProjectCluster, *Response, error) { | |||
| func (s *ProjectClustersService) ListClusters(pid interface{}, options ...RequestOptionFunc) ([]*ProjectCluster, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -89,7 +101,7 @@ func (s *ProjectClustersService) ListClusters(pid interface{}, options ...Option | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/project_clusters.html#get-a-single-project-cluster | |||
| func (s *ProjectClustersService) GetCluster(pid interface{}, cluster int, options ...OptionFunc) (*ProjectCluster, *Response, error) { | |||
| func (s *ProjectClustersService) GetCluster(pid interface{}, cluster int, options ...RequestOptionFunc) (*ProjectCluster, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -115,12 +127,13 @@ func (s *ProjectClustersService) GetCluster(pid interface{}, cluster int, option | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/project_clusters.html#add-existing-cluster-to-project | |||
| type AddClusterOptions struct { | |||
| Name *string `url:"name,omitempty" json:"name,omitempty"` | |||
| Domain *string `url:"domain,omitempty" json:"domain,omitempty"` | |||
| Enabled *bool `url:"enabled,omitempty" json:"enabled,omitempty"` | |||
| Managed *bool `url:"managed,omitempty" json:"managed,omitempty"` | |||
| EnvironmentScope *string `url:"environment_scope,omitempty" json:"environment_scope,omitempty"` | |||
| PlatformKubernetes *AddPlatformKubernetesOptions `url:"platform_kubernetes_attributes,omitempty" json:"platform_kubernetes_attributes,omitempty"` | |||
| Name *string `url:"name,omitempty" json:"name,omitempty"` | |||
| Domain *string `url:"domain,omitempty" json:"domain,omitempty"` | |||
| Enabled *bool `url:"enabled,omitempty" json:"enabled,omitempty"` | |||
| Managed *bool `url:"managed,omitempty" json:"managed,omitempty"` | |||
| EnvironmentScope *string `url:"environment_scope,omitempty" json:"environment_scope,omitempty"` | |||
| PlatformKubernetes *AddPlatformKubernetesOptions `url:"platform_kubernetes_attributes,omitempty" json:"platform_kubernetes_attributes,omitempty"` | |||
| ManagementProjectID *string `url:"management_project_id,omitempty" json:"management_project_id,omitempty"` | |||
| } | |||
| // AddPlatformKubernetesOptions represents the available PlatformKubernetes options for adding. | |||
| @@ -136,7 +149,7 @@ type AddPlatformKubernetesOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/project_clusters.html#add-existing-cluster-to-project | |||
| func (s *ProjectClustersService) AddCluster(pid interface{}, opt *AddClusterOptions, options ...OptionFunc) (*ProjectCluster, *Response, error) { | |||
| func (s *ProjectClustersService) AddCluster(pid interface{}, opt *AddClusterOptions, options ...RequestOptionFunc) (*ProjectCluster, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -162,10 +175,11 @@ func (s *ProjectClustersService) AddCluster(pid interface{}, opt *AddClusterOpti | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/project_clusters.html#edit-project-cluster | |||
| type EditClusterOptions struct { | |||
| Name *string `url:"name,omitempty" json:"name,omitempty"` | |||
| Domain *string `url:"domain,omitempty" json:"domain,omitempty"` | |||
| EnvironmentScope *string `url:"environment_scope,omitempty" json:"environment_scope,omitempty"` | |||
| PlatformKubernetes *EditPlatformKubernetesOptions `url:"platform_kubernetes_attributes,omitempty" json:"platform_kubernetes_attributes,omitempty"` | |||
| Name *string `url:"name,omitempty" json:"name,omitempty"` | |||
| Domain *string `url:"domain,omitempty" json:"domain,omitempty"` | |||
| EnvironmentScope *string `url:"environment_scope,omitempty" json:"environment_scope,omitempty"` | |||
| ManagementProjectID *string `url:"management_project_id,omitempty" json:"management_project_id,omitempty"` | |||
| PlatformKubernetes *EditPlatformKubernetesOptions `url:"platform_kubernetes_attributes,omitempty" json:"platform_kubernetes_attributes,omitempty"` | |||
| } | |||
| // EditPlatformKubernetesOptions represents the available PlatformKubernetes options for editing. | |||
| @@ -180,7 +194,7 @@ type EditPlatformKubernetesOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/project_clusters.html#edit-project-cluster | |||
| func (s *ProjectClustersService) EditCluster(pid interface{}, cluster int, opt *EditClusterOptions, options ...OptionFunc) (*ProjectCluster, *Response, error) { | |||
| func (s *ProjectClustersService) EditCluster(pid interface{}, cluster int, opt *EditClusterOptions, options ...RequestOptionFunc) (*ProjectCluster, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -205,7 +219,7 @@ func (s *ProjectClustersService) EditCluster(pid interface{}, cluster int, opt * | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ee/api/project_clusters.html#delete-project-cluster | |||
| func (s *ProjectClustersService) DeleteCluster(pid interface{}, cluster int, options ...OptionFunc) (*Response, error) { | |||
| func (s *ProjectClustersService) DeleteCluster(pid interface{}, cluster int, options ...RequestOptionFunc) (*Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, err | |||
| @@ -74,7 +74,7 @@ type ScheduleExportOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/project_import_export.html#schedule-an-export | |||
| func (s *ProjectImportExportService) ScheduleExport(pid interface{}, opt *ScheduleExportOptions, options ...OptionFunc) (*Response, error) { | |||
| func (s *ProjectImportExportService) ScheduleExport(pid interface{}, opt *ScheduleExportOptions, options ...RequestOptionFunc) (*Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, err | |||
| @@ -93,7 +93,7 @@ func (s *ProjectImportExportService) ScheduleExport(pid interface{}, opt *Schedu | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/project_import_export.html#export-status | |||
| func (s *ProjectImportExportService) ExportStatus(pid interface{}, options ...OptionFunc) (*ExportStatus, *Response, error) { | |||
| func (s *ProjectImportExportService) ExportStatus(pid interface{}, options ...RequestOptionFunc) (*ExportStatus, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -118,7 +118,7 @@ func (s *ProjectImportExportService) ExportStatus(pid interface{}, options ...Op | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/project_import_export.html#export-download | |||
| func (s *ProjectImportExportService) ExportDownload(pid interface{}, options ...OptionFunc) ([]byte, *Response, error) { | |||
| func (s *ProjectImportExportService) ExportDownload(pid interface{}, options ...RequestOptionFunc) ([]byte, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -155,7 +155,7 @@ type ImportFileOptions struct { | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/project_import_export.html#import-a-file | |||
| func (s *ProjectImportExportService) ImportFile(opt *ImportFileOptions, options ...OptionFunc) (*ImportStatus, *Response, error) { | |||
| func (s *ProjectImportExportService) ImportFile(opt *ImportFileOptions, options ...RequestOptionFunc) (*ImportStatus, *Response, error) { | |||
| req, err := s.client.NewRequest("POST", "projects/import", opt, options) | |||
| if err != nil { | |||
| return nil, nil, err | |||
| @@ -174,7 +174,7 @@ func (s *ProjectImportExportService) ImportFile(opt *ImportFileOptions, options | |||
| // | |||
| // GitLab API docs: | |||
| // https://docs.gitlab.com/ce/api/project_import_export.html#import-status | |||
| func (s *ProjectImportExportService) ImportStatus(pid interface{}, options ...OptionFunc) (*ImportStatus, *Response, error) { | |||
| func (s *ProjectImportExportService) ImportStatus(pid interface{}, options ...RequestOptionFunc) (*ImportStatus, *Response, error) { | |||
| project, err := parseID(pid) | |||
| if err != nil { | |||
| return nil, nil, err | |||