Compare commits
16 Commits
9f9e25a796
...
a31a0b4829
| Author | SHA1 | Date | |
|---|---|---|---|
| a31a0b4829 | |||
|
|
959d1e3c1f | ||
|
|
bda5aa9238 | ||
|
|
4e5da3f322 | ||
|
|
826576516f | ||
|
|
933c7cc307 | ||
|
|
9471f0b5ac | ||
|
|
4d774937df | ||
|
|
3702654ec3 | ||
|
|
7d63422555 | ||
|
|
fa6fc5fb34 | ||
|
|
86d865ce1b | ||
|
|
516dd6b611 | ||
|
|
df421fcccb | ||
|
|
a050189e12 | ||
|
|
7bcdc3276f |
2
.github/workflows/docker-build.yml
vendored
2
.github/workflows/docker-build.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
||||
- name: "Setup"
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.20'
|
||||
go-version: '1.22'
|
||||
|
||||
- name: Lint
|
||||
uses: golangci/golangci-lint-action@v3
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -22,3 +22,5 @@ _testmain.go
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
|
||||
vendor/
|
||||
@@ -1,22 +0,0 @@
|
||||
run:
|
||||
deadline: 10m
|
||||
|
||||
linters:
|
||||
enable-all: false
|
||||
enable:
|
||||
- errcheck
|
||||
- govet
|
||||
- ineffassign
|
||||
- typecheck
|
||||
- dupl
|
||||
- goconst
|
||||
- gofmt
|
||||
- unconvert
|
||||
- revive
|
||||
- staticcheck
|
||||
- gosimple
|
||||
- unused
|
||||
|
||||
issues:
|
||||
max-per-linter: 0
|
||||
max-same-issues: 0
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.20-alpine as builder
|
||||
FROM golang:1.22-alpine as builder
|
||||
|
||||
RUN mkdir /build
|
||||
COPY . /build/
|
||||
|
||||
6
Makefile
6
Makefile
@@ -47,7 +47,7 @@ docker:
|
||||
lint:
|
||||
docker run --rm -it \
|
||||
-w /src -v $(shell pwd):/src \
|
||||
golangci/golangci-lint:v1.51 golangci-lint run \
|
||||
golangci/golangci-lint:v1.56 golangci-lint run \
|
||||
-v -c .golangci.yml
|
||||
|
||||
clean:
|
||||
@@ -56,3 +56,7 @@ clean:
|
||||
vendor:
|
||||
go mod tidy
|
||||
go mod vendor
|
||||
|
||||
.PHONY: upgrade-vendor
|
||||
upgrade-vendor:
|
||||
go get -u ./...
|
||||
|
||||
110
README.md
110
README.md
@@ -21,7 +21,7 @@ docker run --name invertergui --device /dev/ttyUSB0:/dev/ttyUSB0 -p 8080:8080 gh
|
||||
|
||||
## Requirements
|
||||
|
||||
This project makes use of [Go Modules](https://github.com/golang/go/wiki/Modules). The minimum version for Go is 1.16
|
||||
This project makes use of [Go Modules](https://github.com/golang/go/wiki/Modules). The minimum supported version for Go is 1.22
|
||||
|
||||
## Getting started
|
||||
|
||||
@@ -37,10 +37,17 @@ Application Options:
|
||||
--cli.enabled Enable CLI output. [$CLI_ENABLED]
|
||||
--mqtt.enabled Enable MQTT publishing. [$MQTT_ENABLED]
|
||||
--mqtt.broker= Set the host port and scheme of the MQTT broker. (default: tcp://localhost:1883) [$MQTT_BROKER]
|
||||
--mqtt.client_id= Set the client ID for the MQTT connection. (default: interter-gui) [$MQTT_CLIENT_ID]
|
||||
--mqtt.client_id= Set the client ID for the MQTT connection. (default: inverter-gui) [$MQTT_CLIENT_ID]
|
||||
--mqtt.topic= Set the MQTT topic updates published to. (default: invertergui/updates) [$MQTT_TOPIC]
|
||||
--mqtt.command_topic= Set the MQTT topic that receives write commands for Victron settings/RAM variables. (default: invertergui/settings/set) [$MQTT_COMMAND_TOPIC]
|
||||
--mqtt.status_topic= Set the MQTT topic where write command status updates are published. (default: invertergui/settings/status) [$MQTT_STATUS_TOPIC]
|
||||
--mqtt.ha.enabled Enable Home Assistant MQTT discovery integration. [$MQTT_HA_ENABLED]
|
||||
--mqtt.ha.discovery_prefix= Set Home Assistant MQTT discovery prefix. (default: homeassistant) [$MQTT_HA_DISCOVERY_PREFIX]
|
||||
--mqtt.ha.node_id= Set Home Assistant node ID used for discovery topics and unique IDs. (default: invertergui) [$MQTT_HA_NODE_ID]
|
||||
--mqtt.ha.device_name= Set Home Assistant device display name. (default: Victron Inverter) [$MQTT_HA_DEVICE_NAME]
|
||||
--mqtt.username= Set the MQTT username [$MQTT_USERNAME]
|
||||
--mqtt.password= Set the MQTT password [$MQTT_PASSWORD]
|
||||
--mqtt.password-file= Path to a file containing the MQTT password [$MQTT_PASSWORD_FILE]
|
||||
--loglevel= The log level to generate logs at. ("panic", "fatal", "error", "warn", "info", "debug", "trace") (default: info) [$LOGLEVEL]
|
||||
|
||||
Help Options:
|
||||
@@ -82,6 +89,20 @@ Battery Power: -0.659 W
|
||||
Battery Charge: 100.000 %
|
||||
```
|
||||
|
||||
The web UI also includes a **Remote Panel Control** section for:
|
||||
|
||||
- Remote Panel Mode (`on`, `off`, `charger_only`, `inverter_only`)
|
||||
- Remote Panel Current Limit (AC input current limit in amps)
|
||||
- Remote Panel Standby (prevent sleep while turned off)
|
||||
|
||||
The combined mode + current limit action maps to the same behavior as
|
||||
`set_remote_panel_state` in `victron-mk3`.
|
||||
|
||||
The backing HTTP API endpoints are:
|
||||
|
||||
- `GET/POST /api/remote-panel/state`
|
||||
- `GET/POST /api/remote-panel/standby`
|
||||
|
||||
### Munin
|
||||
|
||||
The Munin plugin location is at /munin (http://localhost:8080/munin).
|
||||
@@ -281,16 +302,97 @@ The MQTT client will publish updates to the given broker at the set topic.
|
||||
```bash
|
||||
--mqtt.enabled Enable MQTT publishing. [$MQTT_ENABLED]
|
||||
--mqtt.broker= Set the host port and scheme of the MQTT broker. (default: tcp://localhost:1883) [$MQTT_BROKER]
|
||||
--mqtt.client_id= Set the client ID for the MQTT connection. (default: interter-gui) [$MQTT_CLIENT_ID]
|
||||
--mqtt.client_id= Set the client ID for the MQTT connection. (default: inverter-gui) [$MQTT_CLIENT_ID]
|
||||
--mqtt.topic= Set the MQTT topic updates published to. (default: invertergui/updates) [$MQTT_TOPIC]
|
||||
--mqtt.command_topic= Set the MQTT topic that receives write commands for Victron settings/RAM variables. (default: invertergui/settings/set) [$MQTT_COMMAND_TOPIC]
|
||||
--mqtt.status_topic= Set the MQTT topic where write command status updates are published. (default: invertergui/settings/status) [$MQTT_STATUS_TOPIC]
|
||||
--mqtt.ha.enabled Enable Home Assistant MQTT discovery integration. [$MQTT_HA_ENABLED]
|
||||
--mqtt.ha.discovery_prefix= Set Home Assistant MQTT discovery prefix. (default: homeassistant) [$MQTT_HA_DISCOVERY_PREFIX]
|
||||
--mqtt.ha.node_id= Set Home Assistant node ID used for discovery topics and unique IDs. (default: invertergui) [$MQTT_HA_NODE_ID]
|
||||
--mqtt.ha.device_name= Set Home Assistant device display name. (default: Victron Inverter) [$MQTT_HA_DEVICE_NAME]
|
||||
--mqtt.username= Set the MQTT username [$MQTT_USERNAME]
|
||||
--mqtt.password= Set the MQTT password [$MQTT_PASSWORD]
|
||||
--mqtt.password-file= Path to a file containing the MQTT password [$MQTT_PASSWORD_FILE]
|
||||
```
|
||||
|
||||
The MQTT client can be enabled by setting the environment variable `MQTT_ENABLED=true` or flag `--mqtt.enabled`.
|
||||
All MQTT configuration can be done via flags or as environment variables.
|
||||
The URI for the broker can be configured format should be `scheme://host:port`, where "scheme" is one of "tcp", "ssl", or "ws".
|
||||
|
||||
When `--mqtt.command_topic` is configured, the application subscribes to that topic and accepts JSON write commands.
|
||||
The recommended command for inverter control follows the same model used by `victron-mk3`:
|
||||
|
||||
```json
|
||||
{
|
||||
"request_id": "optional-correlation-id",
|
||||
"kind": "panel_state",
|
||||
"switch": "on",
|
||||
"current_limit": 16.5
|
||||
}
|
||||
```
|
||||
|
||||
`switch` supports `charger_only`, `inverter_only`, `on`, and `off` (or numeric values `1..4`).
|
||||
`current_limit` is in amps and optional. If omitted, only the switch state is changed.
|
||||
To update only the current limit (while preserving the last known mode), send:
|
||||
|
||||
```json
|
||||
{
|
||||
"kind": "panel_state",
|
||||
"current_limit": 12.0
|
||||
}
|
||||
```
|
||||
|
||||
If no prior mode is known (for example on a fresh broker state), this command is rejected until a mode command is sent once.
|
||||
|
||||
Standby can be controlled with:
|
||||
|
||||
```json
|
||||
{
|
||||
"kind": "standby",
|
||||
"standby": true
|
||||
}
|
||||
```
|
||||
|
||||
Low-level writes are still supported:
|
||||
|
||||
```json
|
||||
{
|
||||
"kind": "setting",
|
||||
"id": 15,
|
||||
"value": 1
|
||||
}
|
||||
```
|
||||
|
||||
`kind` supports `panel_state`, `setting`, and `ram_var` (with aliases for each).
|
||||
The result is published to `--mqtt.status_topic` with status `ok` or `error`.
|
||||
|
||||
### Home Assistant
|
||||
|
||||
Enable Home Assistant auto-discovery with:
|
||||
|
||||
```bash
|
||||
--mqtt.ha.enabled
|
||||
```
|
||||
|
||||
When enabled, `invertergui` publishes retained discovery payloads and availability under:
|
||||
|
||||
- `{topic-root}/homeassistant/availability` (`online`/`offline`)
|
||||
- `{discovery_prefix}/sensor/{node_id}/.../config`
|
||||
- `{discovery_prefix}/binary_sensor/{node_id}/.../config`
|
||||
- `{discovery_prefix}/select/{node_id}/remote_panel_mode/config` (if command topic is configured)
|
||||
- `{discovery_prefix}/number/{node_id}/remote_panel_current_limit/config` (if command topic is configured)
|
||||
- `{discovery_prefix}/switch/{node_id}/remote_panel_standby/config` (if command topic is configured)
|
||||
|
||||
The discovered entities include battery/input/output sensors, a data-valid diagnostic binary sensor,
|
||||
plus remote panel controls for:
|
||||
|
||||
- `Remote Panel Mode` (`on`, `off`, `charger_only`, `inverter_only`)
|
||||
- `Remote Panel Current Limit` (AC input current limit in amps)
|
||||
- `Remote Panel Standby` (prevent device sleep while off)
|
||||
|
||||
The combined mode + current limit behavior is provided through the `panel_state` MQTT command kind,
|
||||
which mirrors `victron_mk3.set_remote_panel_state`.
|
||||
|
||||
## TTY Device
|
||||
|
||||
The intertergui application makes use of a serial tty device to monitor the Multiplus.
|
||||
@@ -360,4 +462,4 @@ This repos includes a [Grafana](https://grafana.com/) dashboard in the [grafana
|
||||
[release-badge]: https://img.shields.io/github/v/release/diebietse/invertergui
|
||||
[release-link]: https://github.com/diebietse/invertergui/releases
|
||||
[codecov-badge]: https://codecov.io/gh/diebietse/invertergui/branch/master/graph/badge.svg?token=xTLfEzoqYF
|
||||
[codecov-link]: https://codecov.io/gh/diebietse/invertergui
|
||||
[codecov-link]: https://codecov.io/gh/diebietse/invertergui
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/jessevdk/go-flags"
|
||||
)
|
||||
|
||||
@@ -15,12 +19,21 @@ type config struct {
|
||||
Enabled bool `long:"cli.enabled" env:"CLI_ENABLED" description:"Enable CLI output."`
|
||||
}
|
||||
MQTT struct {
|
||||
Enabled bool `long:"mqtt.enabled" env:"MQTT_ENABLED" description:"Enable MQTT publishing."`
|
||||
Broker string `long:"mqtt.broker" env:"MQTT_BROKER" default:"tcp://localhost:1883" description:"Set the host port and scheme of the MQTT broker."`
|
||||
ClientID string `long:"mqtt.client_id" env:"MQTT_CLIENT_ID" default:"interter-gui" description:"Set the client ID for the MQTT connection."`
|
||||
Topic string `long:"mqtt.topic" env:"MQTT_TOPIC" default:"invertergui/updates" description:"Set the MQTT topic updates published to."`
|
||||
Username string `long:"mqtt.username" env:"MQTT_USERNAME" default:"" description:"Set the MQTT username"`
|
||||
Password string `long:"mqtt.password" env:"MQTT_PASSWORD" default:"" description:"Set the MQTT password"`
|
||||
Enabled bool `long:"mqtt.enabled" env:"MQTT_ENABLED" description:"Enable MQTT publishing."`
|
||||
Broker string `long:"mqtt.broker" env:"MQTT_BROKER" default:"tcp://localhost:1883" description:"Set the host port and scheme of the MQTT broker."`
|
||||
ClientID string `long:"mqtt.client_id" env:"MQTT_CLIENT_ID" default:"inverter-gui" description:"Set the client ID for the MQTT connection."`
|
||||
Topic string `long:"mqtt.topic" env:"MQTT_TOPIC" default:"invertergui/updates" description:"Set the MQTT topic updates published to."`
|
||||
CommandTopic string `long:"mqtt.command_topic" env:"MQTT_COMMAND_TOPIC" default:"invertergui/settings/set" description:"Set the MQTT topic that receives write commands for Victron settings/RAM variables."`
|
||||
StatusTopic string `long:"mqtt.status_topic" env:"MQTT_STATUS_TOPIC" default:"invertergui/settings/status" description:"Set the MQTT topic where write command status updates are published."`
|
||||
HA struct {
|
||||
Enabled bool `long:"mqtt.ha.enabled" env:"MQTT_HA_ENABLED" description:"Enable Home Assistant MQTT discovery integration."`
|
||||
DiscoveryPrefix string `long:"mqtt.ha.discovery_prefix" env:"MQTT_HA_DISCOVERY_PREFIX" default:"homeassistant" description:"Set Home Assistant MQTT discovery prefix."`
|
||||
NodeID string `long:"mqtt.ha.node_id" env:"MQTT_HA_NODE_ID" default:"invertergui" description:"Set Home Assistant node ID used for discovery topics and unique IDs."`
|
||||
DeviceName string `long:"mqtt.ha.device_name" env:"MQTT_HA_DEVICE_NAME" default:"Victron Inverter" description:"Set Home Assistant device display name."`
|
||||
}
|
||||
Username string `long:"mqtt.username" env:"MQTT_USERNAME" default:"" description:"Set the MQTT username"`
|
||||
Password string `long:"mqtt.password" env:"MQTT_PASSWORD" default:"" description:"Set the MQTT password"`
|
||||
PasswordFile string `long:"mqtt.password-file" env:"MQTT_PASSWORD_FILE" default:"" description:"Path to a file containing the MQTT password"`
|
||||
}
|
||||
Loglevel string `long:"loglevel" env:"LOGLEVEL" default:"info" description:"The log level to generate logs at. (\"panic\", \"fatal\", \"error\", \"warn\", \"info\", \"debug\", \"trace\")"`
|
||||
}
|
||||
@@ -31,5 +44,30 @@ func parseConfig() (*config, error) {
|
||||
if _, err := parser.Parse(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := resolvePasswordFile(conf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return conf, nil
|
||||
}
|
||||
|
||||
func resolvePasswordFile(conf *config) error {
|
||||
if conf.MQTT.PasswordFile != "" && conf.MQTT.Password != "" {
|
||||
return fmt.Errorf("mqtt.password and mqtt.password-file are mutually exclusive")
|
||||
}
|
||||
if conf.MQTT.PasswordFile != "" {
|
||||
password, err := readPasswordFile(conf.MQTT.PasswordFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
conf.MQTT.Password = password
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func readPasswordFile(path string) (string, error) {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not read MQTT password file: %w", err)
|
||||
}
|
||||
return strings.TrimRight(string(data), "\n\r"), nil
|
||||
}
|
||||
|
||||
107
cmd/invertergui/config_test.go
Normal file
107
cmd/invertergui/config_test.go
Normal file
@@ -0,0 +1,107 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const testInlineSecret = "inline-secret"
|
||||
|
||||
func TestReadPasswordFile(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
content string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "plain password",
|
||||
content: "secret",
|
||||
expected: "secret",
|
||||
},
|
||||
{
|
||||
name: "password with trailing newline",
|
||||
content: "secret\n",
|
||||
expected: "secret",
|
||||
},
|
||||
{
|
||||
name: "password with trailing carriage return and newline",
|
||||
content: "secret\r\n",
|
||||
expected: "secret",
|
||||
},
|
||||
{
|
||||
name: "empty file",
|
||||
content: "",
|
||||
expected: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
path := filepath.Join(t.TempDir(), "password")
|
||||
if err := os.WriteFile(path, []byte(tt.content), 0o600); err != nil {
|
||||
t.Fatalf("failed to write test file: %v", err)
|
||||
}
|
||||
|
||||
got, err := readPasswordFile(path)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if got != tt.expected {
|
||||
t.Errorf("got %q, want %q", got, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadPasswordFile_NotFound(t *testing.T) {
|
||||
_, err := readPasswordFile("/nonexistent/path/password")
|
||||
if err == nil {
|
||||
t.Fatal("expected error for missing file, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolvePassword_MutuallyExclusive(t *testing.T) {
|
||||
path := filepath.Join(t.TempDir(), "password")
|
||||
if err := os.WriteFile(path, []byte("secret"), 0o600); err != nil {
|
||||
t.Fatalf("failed to write test file: %v", err)
|
||||
}
|
||||
|
||||
conf := &config{}
|
||||
conf.MQTT.Password = testInlineSecret
|
||||
conf.MQTT.PasswordFile = path
|
||||
|
||||
err := resolvePasswordFile(conf)
|
||||
if err == nil {
|
||||
t.Fatal("expected error when both mqtt.password and mqtt.password-file are set, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolvePassword_FromFile(t *testing.T) {
|
||||
path := filepath.Join(t.TempDir(), "password")
|
||||
if err := os.WriteFile(path, []byte("file-secret\n"), 0o600); err != nil {
|
||||
t.Fatalf("failed to write test file: %v", err)
|
||||
}
|
||||
|
||||
conf := &config{}
|
||||
conf.MQTT.PasswordFile = path
|
||||
|
||||
if err := resolvePasswordFile(conf); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if conf.MQTT.Password != "file-secret" {
|
||||
t.Errorf("got %q, want %q", conf.MQTT.Password, "file-secret")
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolvePassword_NoFile(t *testing.T) {
|
||||
conf := &config{}
|
||||
conf.MQTT.Password = testInlineSecret
|
||||
|
||||
if err := resolvePasswordFile(conf); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if conf.MQTT.Password != testInlineSecret {
|
||||
t.Errorf("got %q, want %q", conf.MQTT.Password, testInlineSecret)
|
||||
}
|
||||
}
|
||||
@@ -37,14 +37,14 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/diebietse/invertergui/mk2core"
|
||||
"github.com/diebietse/invertergui/mk2driver"
|
||||
"github.com/diebietse/invertergui/plugins/cli"
|
||||
"github.com/diebietse/invertergui/plugins/mqttclient"
|
||||
"github.com/diebietse/invertergui/plugins/munin"
|
||||
"github.com/diebietse/invertergui/plugins/prometheus"
|
||||
"github.com/diebietse/invertergui/plugins/webui"
|
||||
"github.com/diebietse/invertergui/plugins/webui/static"
|
||||
"invertergui/mk2core"
|
||||
"invertergui/mk2driver"
|
||||
"invertergui/plugins/cli"
|
||||
"invertergui/plugins/mqttclient"
|
||||
"invertergui/plugins/munin"
|
||||
"invertergui/plugins/prometheus"
|
||||
"invertergui/plugins/webui"
|
||||
"invertergui/plugins/webui/static"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/tarm/serial"
|
||||
@@ -77,9 +77,15 @@ func main() {
|
||||
}
|
||||
|
||||
// Webgui
|
||||
gui := webui.NewWebGui(core.NewSubscription())
|
||||
var writer mk2driver.SettingsWriter
|
||||
if w, ok := mk2.(mk2driver.SettingsWriter); ok {
|
||||
writer = w
|
||||
}
|
||||
gui := webui.NewWebGui(core.NewSubscription(), writer)
|
||||
http.Handle("/", static.New())
|
||||
http.Handle("/ws", http.HandlerFunc(gui.ServeHub))
|
||||
http.Handle("/api/remote-panel/state", http.HandlerFunc(gui.ServeRemotePanelState))
|
||||
http.Handle("/api/remote-panel/standby", http.HandlerFunc(gui.ServeRemotePanelStandby))
|
||||
|
||||
// Munin
|
||||
mu := munin.NewMunin(core.NewSubscription())
|
||||
@@ -93,13 +99,24 @@ func main() {
|
||||
// MQTT
|
||||
if conf.MQTT.Enabled {
|
||||
mqttConf := mqttclient.Config{
|
||||
Broker: conf.MQTT.Broker,
|
||||
Topic: conf.MQTT.Topic,
|
||||
ClientID: conf.MQTT.ClientID,
|
||||
Broker: conf.MQTT.Broker,
|
||||
Topic: conf.MQTT.Topic,
|
||||
CommandTopic: conf.MQTT.CommandTopic,
|
||||
StatusTopic: conf.MQTT.StatusTopic,
|
||||
ClientID: conf.MQTT.ClientID,
|
||||
HomeAssistant: mqttclient.HomeAssistantConfig{
|
||||
Enabled: conf.MQTT.HA.Enabled,
|
||||
DiscoveryPrefix: conf.MQTT.HA.DiscoveryPrefix,
|
||||
NodeID: conf.MQTT.HA.NodeID,
|
||||
DeviceName: conf.MQTT.HA.DeviceName,
|
||||
},
|
||||
Username: conf.MQTT.Username,
|
||||
Password: conf.MQTT.Password,
|
||||
}
|
||||
if err := mqttclient.New(core.NewSubscription(), mqttConf); err != nil {
|
||||
if writer == nil {
|
||||
log.Warn("MK2 data source does not support write commands; MQTT command topic will be ignored")
|
||||
}
|
||||
if err := mqttclient.New(core.NewSubscription(), writer, mqttConf); err != nil {
|
||||
log.Fatalf("Could not setup MQTT client: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
coverage:
|
||||
precision: 2
|
||||
round: down
|
||||
range: "65...100"
|
||||
37
go.mod
37
go.mod
@@ -1,30 +1,31 @@
|
||||
module github.com/diebietse/invertergui
|
||||
module invertergui
|
||||
|
||||
go 1.20
|
||||
go 1.26
|
||||
|
||||
require (
|
||||
github.com/eclipse/paho.mqtt.golang v1.4.2
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
github.com/jessevdk/go-flags v1.5.0
|
||||
github.com/prometheus/client_golang v1.14.0
|
||||
github.com/sirupsen/logrus v1.9.0
|
||||
github.com/stretchr/testify v1.8.2
|
||||
github.com/eclipse/paho.mqtt.golang v1.5.1
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/jessevdk/go-flags v1.6.1
|
||||
github.com/prometheus/client_golang v1.23.2
|
||||
github.com/sirupsen/logrus v1.9.4
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_model v0.3.0 // indirect
|
||||
github.com/prometheus/common v0.37.0 // indirect
|
||||
github.com/prometheus/procfs v0.8.0 // indirect
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect
|
||||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f // indirect
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
github.com/prometheus/common v0.67.5 // indirect
|
||||
github.com/prometheus/procfs v0.19.2 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
||||
golang.org/x/net v0.50.0 // indirect
|
||||
golang.org/x/sync v0.19.0 // indirect
|
||||
golang.org/x/sys v0.41.0 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
552
go.sum
552
go.sum
@@ -1,509 +1,93 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
||||
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
||||
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
|
||||
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
|
||||
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
||||
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
||||
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
||||
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
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/eclipse/paho.mqtt.golang v1.4.2 h1:66wOzfUHSSI1zamx7jR6yMEI5EuHnT1G6rNA5PM12m4=
|
||||
github.com/eclipse/paho.mqtt.golang v1.4.2/go.mod h1:JGt0RsEwEX+Xa/agj90YJ9d9DH2b7upDZMK9HRbFvCA=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||
github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||
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/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
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/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/eclipse/paho.mqtt.golang v1.4.3 h1:2kwcUGn8seMUfWndX0hGbvH8r7crgcJguQNCyp70xik=
|
||||
github.com/eclipse/paho.mqtt.golang v1.4.3/go.mod h1:CSYvoAlsMkhYOXh/oKyxa8EcBci6dVkLCbo5tTC1RIE=
|
||||
github.com/eclipse/paho.mqtt.golang v1.5.1 h1:/VSOv3oDLlpqR2Epjn1Q7b2bSTplJIeV2ISgCl2W7nE=
|
||||
github.com/eclipse/paho.mqtt.golang v1.5.1/go.mod h1:1/yJCneuyOoCOzKSsOTUc0AJfpsItBGWvYpBLimhArU=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
|
||||
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc=
|
||||
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/jessevdk/go-flags v1.6.1 h1:Cvu5U8UGrLay1rZfv/zP7iLpSHGUZ/Ou68T0iX1bBK4=
|
||||
github.com/jessevdk/go-flags v1.6.1/go.mod h1:Mk8T1hIAWpOiJiHa9rJASDK2UGWji0EuPGBnNLMooyc=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
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/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
|
||||
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
|
||||
github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw=
|
||||
github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
|
||||
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
|
||||
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
|
||||
github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE=
|
||||
github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
|
||||
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU=
|
||||
github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
|
||||
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
|
||||
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
|
||||
github.com/prometheus/client_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos=
|
||||
github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8=
|
||||
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||
github.com/prometheus/common v0.50.0 h1:YSZE6aa9+luNa2da6/Tik0q0A5AbR+U003TItK57CPQ=
|
||||
github.com/prometheus/common v0.50.0/go.mod h1:wHFBCEVWVmHMUpg7pYcOm2QUR/ocQdYSJVQJKnHc3xQ=
|
||||
github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
|
||||
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
|
||||
github.com/prometheus/procfs v0.13.0 h1:GqzLlQyfsPbaEHaQkO7tbDlriv/4o5Hudv6OXHGKX7o=
|
||||
github.com/prometheus/procfs v0.13.0/go.mod h1:cd4PFCR54QLnGKPaKGA6l+cfuNXtht43ZKY6tow0Y1g=
|
||||
github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
|
||||
github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
|
||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
|
||||
github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07 h1:UyzmZLoiDWMRywV4DUYb9Fbt8uiOSooupjTq10vpvnU=
|
||||
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
|
||||
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=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8=
|
||||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
|
||||
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
|
||||
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
|
||||
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
|
||||
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
|
||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
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/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
||||
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6/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-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
|
||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
||||
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
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/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
|
||||
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package mk2core
|
||||
|
||||
import (
|
||||
"github.com/diebietse/invertergui/mk2driver"
|
||||
"invertergui/mk2driver"
|
||||
)
|
||||
|
||||
type Core struct {
|
||||
|
||||
294
mk2driver/mk2.go
294
mk2driver/mk2.go
@@ -4,6 +4,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -41,20 +42,33 @@ const (
|
||||
)
|
||||
|
||||
const (
|
||||
infoFrameHeader = 0x20
|
||||
frameHeader = 0xff
|
||||
infoFrameHeader = 0x20
|
||||
frameHeader = 0xff
|
||||
bootupFrameHeader = 0x0
|
||||
)
|
||||
|
||||
const (
|
||||
acL1InfoFrame = 0x08
|
||||
dcInfoFrame = 0x0C
|
||||
interfaceFrame = 0x48 // H
|
||||
setTargetFrame = 0x41
|
||||
infoReqFrame = 0x46 //F
|
||||
ledFrame = 0x4C
|
||||
stateFrame = 0x53 // S
|
||||
vFrame = 0x56
|
||||
winmonFrame = 0x57
|
||||
)
|
||||
|
||||
const (
|
||||
panelStateVariant2Flags = 0x80
|
||||
|
||||
interfacePanelDetectFlag = 0x01
|
||||
interfaceStandbyFlag = 0x02
|
||||
|
||||
panelCurrentLimitUnknown = 0x8000
|
||||
panelCurrentLimitMax = 0x7FFF
|
||||
)
|
||||
|
||||
// info frame types
|
||||
const (
|
||||
infoReqAddrDC = 0x00
|
||||
@@ -64,12 +78,21 @@ const (
|
||||
// winmon frame commands
|
||||
const (
|
||||
commandReadRAMVar = 0x30
|
||||
commandWriteRAMVar = 0x32
|
||||
commandWriteSetting = 0x33
|
||||
commandWriteData = 0x34
|
||||
commandGetRAMVarInfo = 0x36
|
||||
|
||||
commandReadRAMResponse = 0x85
|
||||
commandGetRAMVarInfoResponse = 0x8E
|
||||
commandUnsupportedResponse = 0x80
|
||||
commandReadRAMResponse = 0x85
|
||||
commandWriteRAMResponse = 0x87
|
||||
commandWriteSettingResponse = 0x88
|
||||
commandWriteNotAllowedResponse = 0x9B
|
||||
commandGetRAMVarInfoResponse = 0x8E
|
||||
)
|
||||
|
||||
const writeResponseTimeout = 3 * time.Second
|
||||
|
||||
type mk2Ser struct {
|
||||
info *Mk2Info
|
||||
p io.ReadWriter
|
||||
@@ -78,6 +101,10 @@ type mk2Ser struct {
|
||||
run chan struct{}
|
||||
frameLock bool
|
||||
infochan chan *Mk2Info
|
||||
commandMu sync.Mutex
|
||||
writeAck chan byte
|
||||
stateAck chan struct{}
|
||||
ifaceAck chan byte
|
||||
wg sync.WaitGroup
|
||||
}
|
||||
|
||||
@@ -88,6 +115,9 @@ func NewMk2Connection(dev io.ReadWriter) (Mk2, error) {
|
||||
mk2.scaleCount = 0
|
||||
mk2.frameLock = false
|
||||
mk2.scales = make([]scaling, 0, ramVarMaxOffset)
|
||||
mk2.writeAck = make(chan byte, 4)
|
||||
mk2.stateAck = make(chan struct{}, 1)
|
||||
mk2.ifaceAck = make(chan byte, 1)
|
||||
mk2.setTarget()
|
||||
mk2.run = make(chan struct{})
|
||||
mk2.infochan = make(chan *Mk2Info)
|
||||
@@ -152,6 +182,233 @@ func (m *mk2Ser) C() chan *Mk2Info {
|
||||
return m.infochan
|
||||
}
|
||||
|
||||
func (m *mk2Ser) WriteRAMVar(id uint16, value int16) error {
|
||||
return m.writeByID(commandWriteRAMVar, commandWriteRAMResponse, id, value)
|
||||
}
|
||||
|
||||
func (m *mk2Ser) WriteSetting(id uint16, value int16) error {
|
||||
return m.writeByID(commandWriteSetting, commandWriteSettingResponse, id, value)
|
||||
}
|
||||
|
||||
func (m *mk2Ser) SetPanelState(switchState PanelSwitchState, currentLimitA *float64) error {
|
||||
if !validPanelSwitchState(switchState) {
|
||||
return fmt.Errorf("invalid panel switch state: %d", switchState)
|
||||
}
|
||||
|
||||
currentLimitRaw, err := encodePanelCurrentLimit(currentLimitA)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.commandMu.Lock()
|
||||
defer m.commandMu.Unlock()
|
||||
|
||||
m.clearStateResponses()
|
||||
m.sendCommandLocked([]byte{
|
||||
stateFrame,
|
||||
byte(switchState),
|
||||
byte(currentLimitRaw),
|
||||
byte(currentLimitRaw >> 8),
|
||||
0x01,
|
||||
panelStateVariant2Flags,
|
||||
})
|
||||
|
||||
return m.waitForStateResponse()
|
||||
}
|
||||
|
||||
func (m *mk2Ser) SetStandby(enabled bool) error {
|
||||
lineState := byte(interfacePanelDetectFlag)
|
||||
if enabled {
|
||||
lineState |= interfaceStandbyFlag
|
||||
}
|
||||
|
||||
m.commandMu.Lock()
|
||||
defer m.commandMu.Unlock()
|
||||
|
||||
m.clearInterfaceResponses()
|
||||
m.sendCommandLocked([]byte{
|
||||
interfaceFrame,
|
||||
lineState,
|
||||
})
|
||||
|
||||
return m.waitForInterfaceResponse(enabled)
|
||||
}
|
||||
|
||||
func validPanelSwitchState(switchState PanelSwitchState) bool {
|
||||
switch switchState {
|
||||
case PanelSwitchChargerOnly, PanelSwitchInverterOnly, PanelSwitchOn, PanelSwitchOff:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func encodePanelCurrentLimit(currentLimitA *float64) (uint16, error) {
|
||||
if currentLimitA == nil {
|
||||
return panelCurrentLimitUnknown, nil
|
||||
}
|
||||
if *currentLimitA < 0 {
|
||||
return 0, fmt.Errorf("current_limit must be >= 0, got %.3f", *currentLimitA)
|
||||
}
|
||||
|
||||
raw := math.Round(*currentLimitA * 10)
|
||||
if raw > panelCurrentLimitMax {
|
||||
return 0, fmt.Errorf("current_limit %.3f A is above MK2 maximum %.1f A", *currentLimitA, panelCurrentLimitMax/10.0)
|
||||
}
|
||||
return uint16(raw), nil
|
||||
}
|
||||
|
||||
func (m *mk2Ser) writeByID(selectCommand, expectedResponse byte, id uint16, value int16) error {
|
||||
m.commandMu.Lock()
|
||||
defer m.commandMu.Unlock()
|
||||
|
||||
m.clearWriteResponses()
|
||||
|
||||
m.sendCommandLocked([]byte{
|
||||
winmonFrame,
|
||||
selectCommand,
|
||||
byte(id),
|
||||
byte(id >> 8),
|
||||
})
|
||||
|
||||
rawValue := uint16(value)
|
||||
m.sendCommandLocked([]byte{
|
||||
winmonFrame,
|
||||
commandWriteData,
|
||||
byte(rawValue),
|
||||
byte(rawValue >> 8),
|
||||
})
|
||||
|
||||
return m.waitForWriteResponse(expectedResponse)
|
||||
}
|
||||
|
||||
func (m *mk2Ser) clearWriteResponses() {
|
||||
if m.writeAck == nil {
|
||||
m.writeAck = make(chan byte, 4)
|
||||
return
|
||||
}
|
||||
for {
|
||||
select {
|
||||
case <-m.writeAck:
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *mk2Ser) waitForWriteResponse(expectedResponse byte) error {
|
||||
if m.writeAck == nil {
|
||||
return errors.New("write response channel is not initialized")
|
||||
}
|
||||
|
||||
select {
|
||||
case response := <-m.writeAck:
|
||||
switch response {
|
||||
case expectedResponse:
|
||||
return nil
|
||||
case commandUnsupportedResponse:
|
||||
return errors.New("write command is not supported by this device firmware")
|
||||
case commandWriteNotAllowedResponse:
|
||||
return errors.New("write command rejected by device access level")
|
||||
default:
|
||||
return fmt.Errorf("unexpected write response 0x%02x", response)
|
||||
}
|
||||
case <-time.After(writeResponseTimeout):
|
||||
return fmt.Errorf("timed out waiting for write response after %s", writeResponseTimeout)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *mk2Ser) pushWriteResponse(response byte) {
|
||||
if m.writeAck == nil {
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case m.writeAck <- response:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func (m *mk2Ser) clearStateResponses() {
|
||||
if m.stateAck == nil {
|
||||
m.stateAck = make(chan struct{}, 1)
|
||||
return
|
||||
}
|
||||
for {
|
||||
select {
|
||||
case <-m.stateAck:
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *mk2Ser) waitForStateResponse() error {
|
||||
if m.stateAck == nil {
|
||||
return errors.New("panel state response channel is not initialized")
|
||||
}
|
||||
|
||||
select {
|
||||
case <-m.stateAck:
|
||||
return nil
|
||||
case <-time.After(writeResponseTimeout):
|
||||
return fmt.Errorf("timed out waiting for panel state response after %s", writeResponseTimeout)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *mk2Ser) pushStateResponse() {
|
||||
if m.stateAck == nil {
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case m.stateAck <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func (m *mk2Ser) clearInterfaceResponses() {
|
||||
if m.ifaceAck == nil {
|
||||
m.ifaceAck = make(chan byte, 1)
|
||||
return
|
||||
}
|
||||
for {
|
||||
select {
|
||||
case <-m.ifaceAck:
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *mk2Ser) waitForInterfaceResponse(expectedStandby bool) error {
|
||||
if m.ifaceAck == nil {
|
||||
return errors.New("interface response channel is not initialized")
|
||||
}
|
||||
|
||||
select {
|
||||
case lineState := <-m.ifaceAck:
|
||||
standbyEnabled := lineState&interfaceStandbyFlag != 0
|
||||
if standbyEnabled != expectedStandby {
|
||||
return fmt.Errorf("unexpected standby line state 0x%02x", lineState)
|
||||
}
|
||||
return nil
|
||||
case <-time.After(writeResponseTimeout):
|
||||
return fmt.Errorf("timed out waiting for standby response after %s", writeResponseTimeout)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *mk2Ser) pushInterfaceResponse(lineState byte) {
|
||||
if m.ifaceAck == nil {
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case m.ifaceAck <- lineState:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func (m *mk2Ser) readByte() byte {
|
||||
buffer := make([]byte, 1)
|
||||
_, err := io.ReadFull(m.p, buffer)
|
||||
@@ -164,6 +421,7 @@ func (m *mk2Ser) readByte() byte {
|
||||
|
||||
// Adds error to error slice.
|
||||
func (m *mk2Ser) addError(err error) {
|
||||
logrus.Errorf("Mk2 serial slice error: %q", err.Error())
|
||||
if m.info.Errors == nil {
|
||||
m.info.Errors = make([]error, 0)
|
||||
}
|
||||
@@ -183,11 +441,19 @@ func (m *mk2Ser) updateReport() {
|
||||
|
||||
// Checks for valid frame and chooses decoding.
|
||||
func (m *mk2Ser) handleFrame(l byte, frame []byte) {
|
||||
logrus.Debugf("frame %#v", frame)
|
||||
logrus.Debugf("[handleFrame] frame %#v", frame)
|
||||
if checkChecksum(l, frame[0], frame[1:]) {
|
||||
switch frame[0] {
|
||||
case bootupFrameHeader:
|
||||
m.setTarget()
|
||||
case frameHeader:
|
||||
switch frame[1] {
|
||||
case interfaceFrame:
|
||||
if len(frame) > 2 {
|
||||
m.pushInterfaceResponse(frame[2])
|
||||
}
|
||||
case stateFrame:
|
||||
m.pushStateResponse()
|
||||
case vFrame:
|
||||
m.versionDecode(frame[2:])
|
||||
case winmonFrame:
|
||||
@@ -196,10 +462,16 @@ func (m *mk2Ser) handleFrame(l byte, frame []byte) {
|
||||
m.scaleDecode(frame[2:])
|
||||
case commandReadRAMResponse:
|
||||
m.stateDecode(frame[2:])
|
||||
case commandWriteRAMResponse, commandWriteSettingResponse, commandUnsupportedResponse, commandWriteNotAllowedResponse:
|
||||
m.pushWriteResponse(frame[2])
|
||||
default:
|
||||
logrus.Warnf("[handleFrame] invalid winmonFrame %v", frame[2:])
|
||||
}
|
||||
|
||||
case ledFrame:
|
||||
m.ledDecode(frame[2:])
|
||||
default:
|
||||
logrus.Warnf("[handleFrame] invalid frameHeader %v", frame[1])
|
||||
}
|
||||
|
||||
case infoFrameHeader:
|
||||
@@ -208,10 +480,14 @@ func (m *mk2Ser) handleFrame(l byte, frame []byte) {
|
||||
m.dcDecode(frame[1:])
|
||||
case acL1InfoFrame:
|
||||
m.acDecode(frame[1:])
|
||||
default:
|
||||
logrus.Warnf("[handleFrame] invalid infoFrameHeader %v", frame[5])
|
||||
}
|
||||
default:
|
||||
logrus.Warnf("[handleFrame] Invalid frame %v", frame[0])
|
||||
}
|
||||
} else {
|
||||
logrus.Errorf("Invalid incoming frame checksum: %x", frame)
|
||||
logrus.Errorf("[handleFrame] Invalid incoming frame checksum: %x", frame)
|
||||
m.frameLock = false
|
||||
}
|
||||
}
|
||||
@@ -418,6 +694,12 @@ func getLEDs(ledsOn, ledsBlink byte) map[Led]LEDstate {
|
||||
|
||||
// Adds header and trailing crc for frame to send.
|
||||
func (m *mk2Ser) sendCommand(data []byte) {
|
||||
m.commandMu.Lock()
|
||||
defer m.commandMu.Unlock()
|
||||
m.sendCommandLocked(data)
|
||||
}
|
||||
|
||||
func (m *mk2Ser) sendCommandLocked(data []byte) {
|
||||
l := len(data)
|
||||
dataOut := make([]byte, l+3)
|
||||
dataOut[0] = byte(l + 1)
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"io"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
@@ -42,6 +43,7 @@ type testIo struct {
|
||||
}
|
||||
|
||||
func NewIOStub(readBuffer []byte) io.ReadWriter {
|
||||
writeBuffer = bytes.NewBuffer(nil)
|
||||
return &testIo{
|
||||
Reader: bytes.NewBuffer(readBuffer),
|
||||
Writer: writeBuffer,
|
||||
@@ -309,3 +311,127 @@ func Test_mk2Ser_calcFreq(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_mk2Ser_WriteSetting(t *testing.T) {
|
||||
testIO := NewIOStub(nil)
|
||||
m := &mk2Ser{
|
||||
p: testIO,
|
||||
writeAck: make(chan byte, 1),
|
||||
}
|
||||
|
||||
go func() {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
m.pushWriteResponse(commandWriteSettingResponse)
|
||||
}()
|
||||
|
||||
err := m.WriteSetting(0x1234, 1234)
|
||||
assert.NoError(t, err)
|
||||
|
||||
expected := []byte{
|
||||
0x05, 0xff, 0x57, 0x33, 0x34, 0x12, 0x2c,
|
||||
0x05, 0xff, 0x57, 0x34, 0xd2, 0x04, 0x9b,
|
||||
}
|
||||
assert.Equal(t, expected, writeBuffer.Bytes())
|
||||
}
|
||||
|
||||
func Test_mk2Ser_WriteRAMVarRejected(t *testing.T) {
|
||||
testIO := NewIOStub(nil)
|
||||
m := &mk2Ser{
|
||||
p: testIO,
|
||||
writeAck: make(chan byte, 1),
|
||||
}
|
||||
|
||||
go func() {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
m.pushWriteResponse(commandWriteNotAllowedResponse)
|
||||
}()
|
||||
|
||||
err := m.WriteRAMVar(0x000d, 1)
|
||||
assert.Error(t, err)
|
||||
assert.ErrorContains(t, err, "rejected")
|
||||
}
|
||||
|
||||
func Test_mk2Ser_SetPanelState(t *testing.T) {
|
||||
testIO := NewIOStub(nil)
|
||||
m := &mk2Ser{
|
||||
p: testIO,
|
||||
stateAck: make(chan struct{}, 1),
|
||||
}
|
||||
|
||||
go func() {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
m.pushStateResponse()
|
||||
}()
|
||||
|
||||
currentLimit := 16.5
|
||||
err := m.SetPanelState(PanelSwitchOn, ¤tLimit)
|
||||
assert.NoError(t, err)
|
||||
|
||||
expected := []byte{
|
||||
0x07, 0xff, 0x53, 0x03, 0xa5, 0x00, 0x01, 0x80, 0x7e,
|
||||
}
|
||||
assert.Equal(t, expected, writeBuffer.Bytes())
|
||||
}
|
||||
|
||||
func Test_mk2Ser_SetPanelState_SwitchOnly(t *testing.T) {
|
||||
testIO := NewIOStub(nil)
|
||||
m := &mk2Ser{
|
||||
p: testIO,
|
||||
stateAck: make(chan struct{}, 1),
|
||||
}
|
||||
|
||||
go func() {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
m.pushStateResponse()
|
||||
}()
|
||||
|
||||
err := m.SetPanelState(PanelSwitchOff, nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
expected := []byte{
|
||||
0x07, 0xff, 0x53, 0x04, 0x00, 0x80, 0x01, 0x80, 0xa2,
|
||||
}
|
||||
assert.Equal(t, expected, writeBuffer.Bytes())
|
||||
}
|
||||
|
||||
func Test_mk2Ser_SetStandby(t *testing.T) {
|
||||
testIO := NewIOStub(nil)
|
||||
m := &mk2Ser{
|
||||
p: testIO,
|
||||
ifaceAck: make(chan byte, 1),
|
||||
}
|
||||
|
||||
go func() {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
m.pushInterfaceResponse(interfacePanelDetectFlag | interfaceStandbyFlag)
|
||||
}()
|
||||
|
||||
err := m.SetStandby(true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
expected := []byte{
|
||||
0x03, 0xff, 0x48, 0x03, 0xb3,
|
||||
}
|
||||
assert.Equal(t, expected, writeBuffer.Bytes())
|
||||
}
|
||||
|
||||
func Test_mk2Ser_SetStandby_Disabled(t *testing.T) {
|
||||
testIO := NewIOStub(nil)
|
||||
m := &mk2Ser{
|
||||
p: testIO,
|
||||
ifaceAck: make(chan byte, 1),
|
||||
}
|
||||
|
||||
go func() {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
m.pushInterfaceResponse(interfacePanelDetectFlag)
|
||||
}()
|
||||
|
||||
err := m.SetStandby(false)
|
||||
assert.NoError(t, err)
|
||||
|
||||
expected := []byte{
|
||||
0x03, 0xff, 0x48, 0x01, 0xb5,
|
||||
}
|
||||
assert.Equal(t, expected, writeBuffer.Bytes())
|
||||
}
|
||||
|
||||
@@ -76,3 +76,29 @@ type Mk2 interface {
|
||||
C() chan *Mk2Info
|
||||
Close()
|
||||
}
|
||||
|
||||
type PanelSwitchState byte
|
||||
|
||||
const (
|
||||
// PanelSwitchChargerOnly enables charging only.
|
||||
PanelSwitchChargerOnly PanelSwitchState = 0x01
|
||||
// PanelSwitchInverterOnly enables inverter output and disables charging.
|
||||
PanelSwitchInverterOnly PanelSwitchState = 0x02
|
||||
// PanelSwitchOn enables both inverter and charger.
|
||||
PanelSwitchOn PanelSwitchState = 0x03
|
||||
// PanelSwitchOff disables inverter and charger.
|
||||
PanelSwitchOff PanelSwitchState = 0x04
|
||||
)
|
||||
|
||||
type SettingsWriter interface {
|
||||
// WriteRAMVar writes a signed 16-bit value to a RAM variable id.
|
||||
WriteRAMVar(id uint16, value int16) error
|
||||
// WriteSetting writes a signed 16-bit value to a setting id.
|
||||
WriteSetting(id uint16, value int16) error
|
||||
// SetPanelState sends the MK2 "S" command using a virtual panel switch state.
|
||||
// If currentLimitA is nil, the command does not update the AC current limit.
|
||||
SetPanelState(switchState PanelSwitchState, currentLimitA *float64) error
|
||||
// SetStandby configures the remote panel standby line.
|
||||
// When enabled, the inverter is prevented from sleeping while switched off.
|
||||
SetStandby(enabled bool) error
|
||||
}
|
||||
|
||||
@@ -37,6 +37,22 @@ func (m *mock) Close() {
|
||||
|
||||
}
|
||||
|
||||
func (m *mock) WriteRAMVar(_ uint16, _ int16) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mock) WriteSetting(_ uint16, _ int16) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mock) SetPanelState(_ PanelSwitchState, _ *float64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mock) SetStandby(_ bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mock) genMockValues() {
|
||||
mult := 1.0
|
||||
ledState := LedOff
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"github.com/diebietse/invertergui/mk2driver"
|
||||
"invertergui/mk2driver"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
|
||||
@@ -2,9 +2,14 @@ package mqttclient
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/diebietse/invertergui/mk2driver"
|
||||
"invertergui/mk2driver"
|
||||
mqtt "github.com/eclipse/paho.mqtt.golang"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
@@ -13,42 +18,758 @@ var log = logrus.WithField("ctx", "inverter-gui-mqtt")
|
||||
|
||||
const keepAlive = 5 * time.Second
|
||||
|
||||
// Config sets MQTT client configuration
|
||||
type Config struct {
|
||||
Broker string
|
||||
ClientID string
|
||||
Topic string
|
||||
Username string
|
||||
Password string
|
||||
const (
|
||||
commandKindSetting = "setting"
|
||||
commandKindRAMVar = "ram_var"
|
||||
commandKindPanel = "panel_state"
|
||||
commandKindStandby = "standby"
|
||||
|
||||
writeStatusOK = "ok"
|
||||
writeStatusError = "error"
|
||||
)
|
||||
|
||||
type HomeAssistantConfig struct {
|
||||
Enabled bool
|
||||
DiscoveryPrefix string
|
||||
NodeID string
|
||||
DeviceName string
|
||||
}
|
||||
|
||||
// New creates an MQTT client that starts publishing MK2 data as it is received.
|
||||
func New(mk2 mk2driver.Mk2, config Config) error {
|
||||
// Config sets MQTT client configuration
|
||||
type Config struct {
|
||||
Broker string
|
||||
ClientID string
|
||||
Topic string
|
||||
CommandTopic string
|
||||
StatusTopic string
|
||||
HomeAssistant HomeAssistantConfig
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
|
||||
type writeCommand struct {
|
||||
RequestID string
|
||||
Kind string
|
||||
ID uint16
|
||||
Value int16
|
||||
HasSwitch bool
|
||||
SwitchState mk2driver.PanelSwitchState
|
||||
SwitchName string
|
||||
CurrentLimitA *float64
|
||||
Standby *bool
|
||||
}
|
||||
|
||||
type writeCommandPayload struct {
|
||||
RequestID string `json:"request_id"`
|
||||
Kind string `json:"kind"`
|
||||
Type string `json:"type"`
|
||||
ID *uint16 `json:"id"`
|
||||
Value json.RawMessage `json:"value"`
|
||||
Switch string `json:"switch"`
|
||||
SwitchState string `json:"switch_state"`
|
||||
CurrentLimitA *float64 `json:"current_limit"`
|
||||
Standby *bool `json:"standby"`
|
||||
}
|
||||
|
||||
type writeStatus struct {
|
||||
RequestID string `json:"request_id,omitempty"`
|
||||
Status string `json:"status"`
|
||||
Kind string `json:"kind,omitempty"`
|
||||
ID uint16 `json:"id"`
|
||||
Value int16 `json:"value"`
|
||||
Switch string `json:"switch,omitempty"`
|
||||
CurrentLimitA *float64 `json:"current_limit,omitempty"`
|
||||
Standby *bool `json:"standby,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
}
|
||||
|
||||
type haDiscoveryDefinition struct {
|
||||
Component string
|
||||
ObjectID string
|
||||
Config map[string]any
|
||||
}
|
||||
|
||||
type panelStateCache struct {
|
||||
mu sync.Mutex
|
||||
hasSwitch bool
|
||||
switchName string
|
||||
switchState mk2driver.PanelSwitchState
|
||||
}
|
||||
|
||||
// New creates an MQTT client that publishes MK2 updates and optionally handles setting write commands.
|
||||
func New(mk2 mk2driver.Mk2, writer mk2driver.SettingsWriter, config Config) error {
|
||||
c := mqtt.NewClient(getOpts(config))
|
||||
if token := c.Connect(); token.Wait() && token.Error() != nil {
|
||||
return token.Error()
|
||||
}
|
||||
cache := &panelStateCache{}
|
||||
|
||||
if config.HomeAssistant.Enabled {
|
||||
if err := publishHAAvailability(c, config, "online"); err != nil {
|
||||
return fmt.Errorf("could not publish Home Assistant availability: %w", err)
|
||||
}
|
||||
if err := publishHADiscovery(c, config); err != nil {
|
||||
return fmt.Errorf("could not publish Home Assistant discovery payloads: %w", err)
|
||||
}
|
||||
if writer != nil {
|
||||
if err := subscribeHAPanelModeState(c, config, cache); err != nil {
|
||||
log.Warnf("Could not subscribe to Home Assistant panel mode state topic: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if config.CommandTopic != "" {
|
||||
if writer == nil {
|
||||
log.Warnf("MQTT command topic %q configured, but no settings writer is available", config.CommandTopic)
|
||||
} else {
|
||||
t := c.Subscribe(config.CommandTopic, 1, commandHandler(c, writer, config, cache))
|
||||
t.Wait()
|
||||
if t.Error() != nil {
|
||||
return fmt.Errorf("could not subscribe to MQTT command topic %q: %w", config.CommandTopic, t.Error())
|
||||
}
|
||||
log.Infof("Subscribed to MQTT command topic: %s", config.CommandTopic)
|
||||
}
|
||||
}
|
||||
|
||||
go func() {
|
||||
for e := range mk2.C() {
|
||||
if e.Valid {
|
||||
data, err := json.Marshal(e)
|
||||
if err != nil {
|
||||
log.Errorf("Could not parse data source: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
t := c.Publish(config.Topic, 0, false, data)
|
||||
t.Wait()
|
||||
if t.Error() != nil {
|
||||
log.Errorf("Could not publish data: %v", t.Error())
|
||||
}
|
||||
if e == nil || !e.Valid {
|
||||
continue
|
||||
}
|
||||
if err := publishJSON(c, config.Topic, e, 0, false); err != nil {
|
||||
log.Errorf("Could not publish update to MQTT topic %q: %v", config.Topic, err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func subscribeHAPanelModeState(client mqtt.Client, config Config, cache *panelStateCache) error {
|
||||
if cache == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
stateTopic := haPanelSwitchStateTopic(config)
|
||||
t := client.Subscribe(stateTopic, 1, func(_ mqtt.Client, msg mqtt.Message) {
|
||||
switchState, switchName, err := normalizePanelSwitch(string(msg.Payload()))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
cache.remember(writeCommand{
|
||||
Kind: commandKindPanel,
|
||||
HasSwitch: true,
|
||||
SwitchState: switchState,
|
||||
SwitchName: switchName,
|
||||
})
|
||||
})
|
||||
t.Wait()
|
||||
return t.Error()
|
||||
}
|
||||
|
||||
func commandHandler(client mqtt.Client, writer mk2driver.SettingsWriter, config Config, cache *panelStateCache) mqtt.MessageHandler {
|
||||
if cache == nil {
|
||||
cache = &panelStateCache{}
|
||||
}
|
||||
|
||||
return func(_ mqtt.Client, msg mqtt.Message) {
|
||||
cmd, err := decodeWriteCommand(msg.Payload())
|
||||
if err != nil {
|
||||
log.Errorf("Invalid MQTT write command payload from topic %q: %v", msg.Topic(), err)
|
||||
publishWriteStatus(client, config.StatusTopic, writeStatus{
|
||||
Status: writeStatusError,
|
||||
Error: err.Error(),
|
||||
Timestamp: time.Now().UTC(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
execCmd := cmd
|
||||
status := writeStatus{
|
||||
RequestID: cmd.RequestID,
|
||||
Status: writeStatusOK,
|
||||
Kind: cmd.Kind,
|
||||
Timestamp: time.Now().UTC(),
|
||||
}
|
||||
switch cmd.Kind {
|
||||
case commandKindPanel:
|
||||
execCmd, err = cache.resolvePanelCommand(cmd)
|
||||
if err != nil {
|
||||
status.Status = writeStatusError
|
||||
status.Error = err.Error()
|
||||
log.Errorf("Invalid MQTT write command %s: %v", formatWriteCommandLog(cmd), err)
|
||||
publishWriteStatus(client, config.StatusTopic, status)
|
||||
return
|
||||
}
|
||||
status.Switch = execCmd.SwitchName
|
||||
status.CurrentLimitA = execCmd.CurrentLimitA
|
||||
case commandKindStandby:
|
||||
status.Standby = copyBoolPtr(execCmd.Standby)
|
||||
default:
|
||||
status.ID = cmd.ID
|
||||
status.Value = cmd.Value
|
||||
}
|
||||
|
||||
if err := executeWriteCommand(writer, execCmd); err != nil {
|
||||
status.Status = writeStatusError
|
||||
status.Error = err.Error()
|
||||
log.Errorf("Failed MQTT write command %s: %v", formatWriteCommandLog(execCmd), err)
|
||||
} else {
|
||||
log.Infof("Applied MQTT write command %s", formatWriteCommandLog(execCmd))
|
||||
cache.remember(execCmd)
|
||||
if config.HomeAssistant.Enabled {
|
||||
if err := publishHAControlState(client, config, execCmd); err != nil {
|
||||
log.Errorf("Could not publish Home Assistant control state update: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
publishWriteStatus(client, config.StatusTopic, status)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *panelStateCache) resolvePanelCommand(cmd writeCommand) (writeCommand, error) {
|
||||
if cmd.Kind != commandKindPanel {
|
||||
return cmd, nil
|
||||
}
|
||||
|
||||
if cmd.HasSwitch {
|
||||
return cmd, nil
|
||||
}
|
||||
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
if !c.hasSwitch {
|
||||
return writeCommand{}, errors.New("panel_state command missing switch and no prior mode is known; set mode once first")
|
||||
}
|
||||
|
||||
cmd.HasSwitch = true
|
||||
cmd.SwitchName = c.switchName
|
||||
cmd.SwitchState = c.switchState
|
||||
return cmd, nil
|
||||
}
|
||||
|
||||
func (c *panelStateCache) remember(cmd writeCommand) {
|
||||
if cmd.Kind != commandKindPanel || !cmd.HasSwitch {
|
||||
return
|
||||
}
|
||||
|
||||
c.mu.Lock()
|
||||
c.hasSwitch = true
|
||||
c.switchName = cmd.SwitchName
|
||||
c.switchState = cmd.SwitchState
|
||||
c.mu.Unlock()
|
||||
}
|
||||
|
||||
func decodeWriteCommand(payload []byte) (writeCommand, error) {
|
||||
msg := writeCommandPayload{}
|
||||
if err := json.Unmarshal(payload, &msg); err != nil {
|
||||
return writeCommand{}, fmt.Errorf("invalid JSON payload: %w", err)
|
||||
}
|
||||
|
||||
kind := msg.Kind
|
||||
if kind == "" {
|
||||
kind = msg.Type
|
||||
}
|
||||
normalizedKind, err := normalizeWriteKind(kind)
|
||||
if err != nil {
|
||||
return writeCommand{}, err
|
||||
}
|
||||
|
||||
if normalizedKind == commandKindPanel {
|
||||
switchName := msg.Switch
|
||||
if switchName == "" {
|
||||
switchName = msg.SwitchState
|
||||
}
|
||||
|
||||
hasSwitch := false
|
||||
switchState := mk2driver.PanelSwitchState(0)
|
||||
normalizedSwitchName := ""
|
||||
if switchName != "" {
|
||||
var err error
|
||||
switchState, normalizedSwitchName, err = normalizePanelSwitch(switchName)
|
||||
if err != nil {
|
||||
return writeCommand{}, err
|
||||
}
|
||||
hasSwitch = true
|
||||
}
|
||||
if msg.CurrentLimitA != nil && *msg.CurrentLimitA < 0 {
|
||||
return writeCommand{}, fmt.Errorf("current_limit must be >= 0, got %.3f", *msg.CurrentLimitA)
|
||||
}
|
||||
if !hasSwitch && msg.CurrentLimitA == nil {
|
||||
return writeCommand{}, errors.New(`missing required field "switch" (or "switch_state"), or "current_limit"`)
|
||||
}
|
||||
|
||||
return writeCommand{
|
||||
RequestID: msg.RequestID,
|
||||
Kind: normalizedKind,
|
||||
HasSwitch: hasSwitch,
|
||||
SwitchState: switchState,
|
||||
SwitchName: normalizedSwitchName,
|
||||
CurrentLimitA: msg.CurrentLimitA,
|
||||
}, nil
|
||||
}
|
||||
|
||||
if normalizedKind == commandKindStandby {
|
||||
standby, err := decodeStandbyValue(msg)
|
||||
if err != nil {
|
||||
return writeCommand{}, err
|
||||
}
|
||||
|
||||
return writeCommand{
|
||||
RequestID: msg.RequestID,
|
||||
Kind: normalizedKind,
|
||||
Standby: &standby,
|
||||
}, nil
|
||||
}
|
||||
|
||||
if msg.ID == nil {
|
||||
return writeCommand{}, errors.New(`missing required field "id"`)
|
||||
}
|
||||
value, err := decodeInt16Value(msg.Value)
|
||||
if err != nil {
|
||||
return writeCommand{}, err
|
||||
}
|
||||
|
||||
return writeCommand{
|
||||
RequestID: msg.RequestID,
|
||||
Kind: normalizedKind,
|
||||
ID: *msg.ID,
|
||||
Value: value,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func normalizeWriteKind(raw string) (string, error) {
|
||||
switch strings.ToLower(strings.TrimSpace(raw)) {
|
||||
case "setting", "settings":
|
||||
return commandKindSetting, nil
|
||||
case "ram", "ramvar", "ram_var", "ram-variable", "ramvariable":
|
||||
return commandKindRAMVar, nil
|
||||
case "panel", "panel_state", "switch", "remote_panel":
|
||||
return commandKindPanel, nil
|
||||
case "standby", "panel_standby", "remote_panel_standby":
|
||||
return commandKindStandby, nil
|
||||
default:
|
||||
return "", fmt.Errorf("unsupported write command kind %q", raw)
|
||||
}
|
||||
}
|
||||
|
||||
func normalizePanelSwitch(raw string) (mk2driver.PanelSwitchState, string, error) {
|
||||
switch strings.ToLower(strings.TrimSpace(raw)) {
|
||||
case "1", "charger_only", "charger-only", "charger":
|
||||
return mk2driver.PanelSwitchChargerOnly, "charger_only", nil
|
||||
case "2", "inverter_only", "inverter-only", "inverter":
|
||||
return mk2driver.PanelSwitchInverterOnly, "inverter_only", nil
|
||||
case "3", "on":
|
||||
return mk2driver.PanelSwitchOn, "on", nil
|
||||
case "4", "off":
|
||||
return mk2driver.PanelSwitchOff, "off", nil
|
||||
default:
|
||||
return 0, "", fmt.Errorf("unsupported panel switch %q", raw)
|
||||
}
|
||||
}
|
||||
|
||||
func executeWriteCommand(writer mk2driver.SettingsWriter, cmd writeCommand) error {
|
||||
if writer == nil {
|
||||
return errors.New("settings writer is not available")
|
||||
}
|
||||
switch cmd.Kind {
|
||||
case commandKindPanel:
|
||||
if !cmd.HasSwitch {
|
||||
return errors.New("panel_state command requires a switch state")
|
||||
}
|
||||
return writer.SetPanelState(cmd.SwitchState, cmd.CurrentLimitA)
|
||||
case commandKindStandby:
|
||||
if cmd.Standby == nil {
|
||||
return errors.New("standby command missing standby value")
|
||||
}
|
||||
return writer.SetStandby(*cmd.Standby)
|
||||
case commandKindRAMVar:
|
||||
return writer.WriteRAMVar(cmd.ID, cmd.Value)
|
||||
case commandKindSetting:
|
||||
return writer.WriteSetting(cmd.ID, cmd.Value)
|
||||
default:
|
||||
return fmt.Errorf("unsupported write command kind %q", cmd.Kind)
|
||||
}
|
||||
}
|
||||
|
||||
func formatWriteCommandLog(cmd writeCommand) string {
|
||||
switch cmd.Kind {
|
||||
case commandKindPanel:
|
||||
switchName := cmd.SwitchName
|
||||
if switchName == "" {
|
||||
switchName = "<cached>"
|
||||
}
|
||||
if cmd.CurrentLimitA == nil {
|
||||
return fmt.Sprintf("kind=%s switch=%s", cmd.Kind, switchName)
|
||||
}
|
||||
return fmt.Sprintf("kind=%s switch=%s current_limit=%.3f", cmd.Kind, switchName, *cmd.CurrentLimitA)
|
||||
case commandKindStandby:
|
||||
if cmd.Standby == nil {
|
||||
return fmt.Sprintf("kind=%s standby=<unset>", cmd.Kind)
|
||||
}
|
||||
return fmt.Sprintf("kind=%s standby=%t", cmd.Kind, *cmd.Standby)
|
||||
default:
|
||||
return fmt.Sprintf("kind=%s id=%d value=%d", cmd.Kind, cmd.ID, cmd.Value)
|
||||
}
|
||||
}
|
||||
|
||||
func decodeInt16Value(raw json.RawMessage) (int16, error) {
|
||||
if len(raw) == 0 {
|
||||
return 0, errors.New(`missing required field "value"`)
|
||||
}
|
||||
|
||||
var value int16
|
||||
if err := json.Unmarshal(raw, &value); err != nil {
|
||||
return 0, fmt.Errorf(`field "value" must be a signed integer: %w`, err)
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func decodeStandbyValue(msg writeCommandPayload) (bool, error) {
|
||||
if msg.Standby != nil {
|
||||
return *msg.Standby, nil
|
||||
}
|
||||
if len(msg.Value) == 0 {
|
||||
return false, errors.New(`missing required field "standby" (or boolean "value")`)
|
||||
}
|
||||
|
||||
var boolValue bool
|
||||
if err := json.Unmarshal(msg.Value, &boolValue); err == nil {
|
||||
return boolValue, nil
|
||||
}
|
||||
|
||||
var stringValue string
|
||||
if err := json.Unmarshal(msg.Value, &stringValue); err == nil {
|
||||
switch strings.ToLower(strings.TrimSpace(stringValue)) {
|
||||
case "1", "true", "on", "enable", "enabled":
|
||||
return true, nil
|
||||
case "0", "false", "off", "disable", "disabled":
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
var intValue int
|
||||
if err := json.Unmarshal(msg.Value, &intValue); err == nil {
|
||||
switch intValue {
|
||||
case 1:
|
||||
return true, nil
|
||||
case 0:
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
return false, errors.New(`field "standby" must be true/false`)
|
||||
}
|
||||
|
||||
func publishWriteStatus(client mqtt.Client, topic string, status writeStatus) {
|
||||
if topic == "" {
|
||||
return
|
||||
}
|
||||
if err := publishJSON(client, topic, status, 1, false); err != nil {
|
||||
log.Errorf("Could not publish command status to MQTT topic %q: %v", topic, err)
|
||||
}
|
||||
}
|
||||
|
||||
func publishHADiscovery(client mqtt.Client, config Config) error {
|
||||
definitions := buildHADiscoveryDefinitions(config)
|
||||
prefix := haDiscoveryPrefix(config)
|
||||
nodeID := haNodeID(config)
|
||||
|
||||
for _, def := range definitions {
|
||||
topic := fmt.Sprintf("%s/%s/%s/%s/config", prefix, def.Component, nodeID, def.ObjectID)
|
||||
if err := publishJSON(client, topic, def.Config, 1, true); err != nil {
|
||||
return fmt.Errorf("could not publish discovery for %s/%s: %w", def.Component, def.ObjectID, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildHADiscoveryDefinitions(config Config) []haDiscoveryDefinition {
|
||||
if !config.HomeAssistant.Enabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
nodeID := haNodeID(config)
|
||||
device := map[string]any{
|
||||
"identifiers": []string{fmt.Sprintf("invertergui_%s", nodeID)},
|
||||
"name": haDeviceName(config),
|
||||
"manufacturer": "Victron Energy",
|
||||
"model": "MultiPlus",
|
||||
"sw_version": "invertergui",
|
||||
}
|
||||
availabilityTopic := haAvailabilityTopic(config)
|
||||
stateTopic := config.Topic
|
||||
|
||||
sensors := []haDiscoveryDefinition{
|
||||
buildHASensor(device, availabilityTopic, stateTopic, nodeID, "battery_voltage", "Battery Voltage", "{{ value_json.BatVoltage }}", "V", "voltage", "measurement"),
|
||||
buildHASensor(device, availabilityTopic, stateTopic, nodeID, "battery_current", "Battery Current", "{{ value_json.BatCurrent }}", "A", "current", "measurement"),
|
||||
buildHASensor(device, availabilityTopic, stateTopic, nodeID, "battery_charge", "Battery Charge", "{{ ((value_json.ChargeState | float(0)) * 100) | round(1) }}", "%", "battery", "measurement"),
|
||||
buildHASensor(device, availabilityTopic, stateTopic, nodeID, "input_voltage", "Input Voltage", "{{ value_json.InVoltage }}", "V", "voltage", "measurement"),
|
||||
buildHASensor(device, availabilityTopic, stateTopic, nodeID, "input_current", "Input Current", "{{ value_json.InCurrent }}", "A", "current", "measurement"),
|
||||
buildHASensor(device, availabilityTopic, stateTopic, nodeID, "input_frequency", "Input Frequency", "{{ value_json.InFrequency }}", "Hz", "frequency", "measurement"),
|
||||
buildHASensor(device, availabilityTopic, stateTopic, nodeID, "output_voltage", "Output Voltage", "{{ value_json.OutVoltage }}", "V", "voltage", "measurement"),
|
||||
buildHASensor(device, availabilityTopic, stateTopic, nodeID, "output_current", "Output Current", "{{ value_json.OutCurrent }}", "A", "current", "measurement"),
|
||||
buildHASensor(device, availabilityTopic, stateTopic, nodeID, "output_frequency", "Output Frequency", "{{ value_json.OutFrequency }}", "Hz", "frequency", "measurement"),
|
||||
buildHASensor(device, availabilityTopic, stateTopic, nodeID, "input_power", "Input Power", "{{ ((value_json.InVoltage | float(0)) * (value_json.InCurrent | float(0))) | round(1) }}", "VA", "", "measurement"),
|
||||
buildHASensor(device, availabilityTopic, stateTopic, nodeID, "output_power", "Output Power", "{{ ((value_json.OutVoltage | float(0)) * (value_json.OutCurrent | float(0))) | round(1) }}", "VA", "", "measurement"),
|
||||
{
|
||||
Component: "binary_sensor",
|
||||
ObjectID: "data_valid",
|
||||
Config: map[string]any{
|
||||
"name": "Data Valid",
|
||||
"unique_id": fmt.Sprintf("%s_data_valid", nodeID),
|
||||
"state_topic": stateTopic,
|
||||
"value_template": "{{ value_json.Valid }}",
|
||||
"payload_on": "true",
|
||||
"payload_off": "false",
|
||||
"availability_topic": availabilityTopic,
|
||||
"device": device,
|
||||
"entity_category": "diagnostic",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if config.CommandTopic != "" {
|
||||
sensors = append(sensors,
|
||||
haDiscoveryDefinition{
|
||||
Component: "select",
|
||||
ObjectID: "remote_panel_mode",
|
||||
Config: map[string]any{
|
||||
"name": "Remote Panel Mode",
|
||||
"unique_id": fmt.Sprintf("%s_remote_panel_mode", nodeID),
|
||||
"state_topic": haPanelSwitchStateTopic(config),
|
||||
"command_topic": config.CommandTopic,
|
||||
"command_template": "{\"kind\":\"panel_state\",\"switch\":\"{{ value }}\"}",
|
||||
"options": []string{"charger_only", "inverter_only", "on", "off"},
|
||||
"availability_topic": availabilityTopic,
|
||||
"device": device,
|
||||
"icon": "mdi:transmission-tower-export",
|
||||
},
|
||||
},
|
||||
haDiscoveryDefinition{
|
||||
Component: "number",
|
||||
ObjectID: "remote_panel_current_limit",
|
||||
Config: map[string]any{
|
||||
"name": "Remote Panel Current Limit",
|
||||
"unique_id": fmt.Sprintf("%s_remote_panel_current_limit", nodeID),
|
||||
"state_topic": haCurrentLimitStateTopic(config),
|
||||
"command_topic": config.CommandTopic,
|
||||
"command_template": "{\"kind\":\"panel_state\",\"current_limit\":{{ value | float(0) }}}",
|
||||
"unit_of_measurement": "A",
|
||||
"device_class": "current",
|
||||
"mode": "box",
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"step": 0.1,
|
||||
"availability_topic": availabilityTopic,
|
||||
"device": device,
|
||||
"icon": "mdi:current-ac",
|
||||
},
|
||||
},
|
||||
haDiscoveryDefinition{
|
||||
Component: "switch",
|
||||
ObjectID: "remote_panel_standby",
|
||||
Config: map[string]any{
|
||||
"name": "Remote Panel Standby",
|
||||
"unique_id": fmt.Sprintf("%s_remote_panel_standby", nodeID),
|
||||
"state_topic": haStandbyStateTopic(config),
|
||||
"command_topic": config.CommandTopic,
|
||||
"payload_on": "{\"kind\":\"standby\",\"standby\":true}",
|
||||
"payload_off": "{\"kind\":\"standby\",\"standby\":false}",
|
||||
"state_on": "ON",
|
||||
"state_off": "OFF",
|
||||
"availability_topic": availabilityTopic,
|
||||
"device": device,
|
||||
"icon": "mdi:power-sleep",
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
return sensors
|
||||
}
|
||||
|
||||
func buildHASensor(device map[string]any, availabilityTopic, stateTopic, nodeID, objectID, name, valueTemplate, unit, deviceClass, stateClass string) haDiscoveryDefinition {
|
||||
config := map[string]any{
|
||||
"name": name,
|
||||
"unique_id": fmt.Sprintf("%s_%s", nodeID, objectID),
|
||||
"state_topic": stateTopic,
|
||||
"value_template": valueTemplate,
|
||||
"availability_topic": availabilityTopic,
|
||||
"device": device,
|
||||
}
|
||||
if unit != "" {
|
||||
config["unit_of_measurement"] = unit
|
||||
}
|
||||
if deviceClass != "" {
|
||||
config["device_class"] = deviceClass
|
||||
}
|
||||
if stateClass != "" {
|
||||
config["state_class"] = stateClass
|
||||
}
|
||||
|
||||
return haDiscoveryDefinition{
|
||||
Component: "sensor",
|
||||
ObjectID: objectID,
|
||||
Config: config,
|
||||
}
|
||||
}
|
||||
|
||||
func publishHAAvailability(client mqtt.Client, config Config, status string) error {
|
||||
return publishString(client, haAvailabilityTopic(config), status, 1, true)
|
||||
}
|
||||
|
||||
func publishHAControlState(client mqtt.Client, config Config, cmd writeCommand) error {
|
||||
switch cmd.Kind {
|
||||
case commandKindPanel:
|
||||
if err := publishString(client, haPanelSwitchStateTopic(config), cmd.SwitchName, 1, true); err != nil {
|
||||
return err
|
||||
}
|
||||
if cmd.CurrentLimitA != nil {
|
||||
limit := strconv.FormatFloat(*cmd.CurrentLimitA, 'f', 1, 64)
|
||||
if err := publishString(client, haCurrentLimitStateTopic(config), limit, 1, true); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case commandKindStandby:
|
||||
if cmd.Standby == nil {
|
||||
return nil
|
||||
}
|
||||
state := "OFF"
|
||||
if *cmd.Standby {
|
||||
state = "ON"
|
||||
}
|
||||
if err := publishString(client, haStandbyStateTopic(config), state, 1, true); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func publishJSON(client mqtt.Client, topic string, payload any, qos byte, retained bool) error {
|
||||
if topic == "" {
|
||||
return errors.New("topic is empty")
|
||||
}
|
||||
|
||||
data, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not marshal payload: %w", err)
|
||||
}
|
||||
|
||||
t := client.Publish(topic, qos, retained, data)
|
||||
t.Wait()
|
||||
if t.Error() != nil {
|
||||
return t.Error()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func publishString(client mqtt.Client, topic, payload string, qos byte, retained bool) error {
|
||||
if topic == "" {
|
||||
return errors.New("topic is empty")
|
||||
}
|
||||
|
||||
t := client.Publish(topic, qos, retained, payload)
|
||||
t.Wait()
|
||||
if t.Error() != nil {
|
||||
return t.Error()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func mqttTopicRoot(topic string) string {
|
||||
t := strings.Trim(strings.TrimSpace(topic), "/")
|
||||
if t == "" {
|
||||
return "invertergui"
|
||||
}
|
||||
if strings.HasSuffix(t, "/updates") {
|
||||
root := strings.TrimSuffix(t, "/updates")
|
||||
if root != "" {
|
||||
return root
|
||||
}
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func haAvailabilityTopic(config Config) string {
|
||||
return fmt.Sprintf("%s/homeassistant/availability", mqttTopicRoot(config.Topic))
|
||||
}
|
||||
|
||||
func haPanelSwitchStateTopic(config Config) string {
|
||||
return fmt.Sprintf("%s/homeassistant/remote_panel_mode/state", mqttTopicRoot(config.Topic))
|
||||
}
|
||||
|
||||
func haCurrentLimitStateTopic(config Config) string {
|
||||
return fmt.Sprintf("%s/homeassistant/remote_panel_current_limit/state", mqttTopicRoot(config.Topic))
|
||||
}
|
||||
|
||||
func haStandbyStateTopic(config Config) string {
|
||||
return fmt.Sprintf("%s/homeassistant/remote_panel_standby/state", mqttTopicRoot(config.Topic))
|
||||
}
|
||||
|
||||
func haDiscoveryPrefix(config Config) string {
|
||||
prefix := strings.Trim(strings.TrimSpace(config.HomeAssistant.DiscoveryPrefix), "/")
|
||||
if prefix == "" {
|
||||
return "homeassistant"
|
||||
}
|
||||
return prefix
|
||||
}
|
||||
|
||||
func haNodeID(config Config) string {
|
||||
nodeID := normalizeID(config.HomeAssistant.NodeID)
|
||||
if nodeID == "" {
|
||||
nodeID = normalizeID(config.ClientID)
|
||||
}
|
||||
if nodeID == "" {
|
||||
return "invertergui"
|
||||
}
|
||||
return nodeID
|
||||
}
|
||||
|
||||
func haDeviceName(config Config) string {
|
||||
name := strings.TrimSpace(config.HomeAssistant.DeviceName)
|
||||
if name == "" {
|
||||
return "Victron Inverter"
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
func normalizeID(in string) string {
|
||||
trimmed := strings.TrimSpace(strings.ToLower(in))
|
||||
if trimmed == "" {
|
||||
return ""
|
||||
}
|
||||
var b strings.Builder
|
||||
lastUnderscore := false
|
||||
for _, r := range trimmed {
|
||||
valid := (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') || r == '-' || r == '_'
|
||||
if valid {
|
||||
b.WriteRune(r)
|
||||
lastUnderscore = false
|
||||
continue
|
||||
}
|
||||
if !lastUnderscore {
|
||||
b.WriteRune('_')
|
||||
lastUnderscore = true
|
||||
}
|
||||
}
|
||||
|
||||
return strings.Trim(b.String(), "_")
|
||||
}
|
||||
|
||||
func copyBoolPtr(in *bool) *bool {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
value := *in
|
||||
return &value
|
||||
}
|
||||
|
||||
func getOpts(config Config) *mqtt.ClientOptions {
|
||||
opts := mqtt.NewClientOptions()
|
||||
opts.AddBroker(config.Broker)
|
||||
@@ -60,14 +781,16 @@ func getOpts(config Config) *mqtt.ClientOptions {
|
||||
if config.Password != "" {
|
||||
opts.SetPassword(config.Password)
|
||||
}
|
||||
if config.HomeAssistant.Enabled {
|
||||
opts.SetWill(haAvailabilityTopic(config), "offline", 1, true)
|
||||
}
|
||||
opts.SetKeepAlive(keepAlive)
|
||||
|
||||
opts.SetOnConnectHandler(func(mqtt.Client) {
|
||||
log.Info("Client connected to broker")
|
||||
})
|
||||
opts.SetConnectionLostHandler(func(cli mqtt.Client, err error) {
|
||||
opts.SetConnectionLostHandler(func(_ mqtt.Client, err error) {
|
||||
log.Errorf("Client connection to broker lost: %v", err)
|
||||
|
||||
})
|
||||
return opts
|
||||
}
|
||||
|
||||
316
plugins/mqttclient/mqtt_test.go
Normal file
316
plugins/mqttclient/mqtt_test.go
Normal file
@@ -0,0 +1,316 @@
|
||||
package mqttclient
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"invertergui/mk2driver"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type fakeWriter struct {
|
||||
lastKind string
|
||||
lastID uint16
|
||||
lastValue int16
|
||||
lastSwitchState mk2driver.PanelSwitchState
|
||||
lastCurrentLimit *float64
|
||||
lastStandby *bool
|
||||
err error
|
||||
}
|
||||
|
||||
func (f *fakeWriter) WriteRAMVar(id uint16, value int16) error {
|
||||
f.lastKind = commandKindRAMVar
|
||||
f.lastID = id
|
||||
f.lastValue = value
|
||||
return f.err
|
||||
}
|
||||
|
||||
func (f *fakeWriter) WriteSetting(id uint16, value int16) error {
|
||||
f.lastKind = commandKindSetting
|
||||
f.lastID = id
|
||||
f.lastValue = value
|
||||
return f.err
|
||||
}
|
||||
|
||||
func (f *fakeWriter) SetPanelState(switchState mk2driver.PanelSwitchState, currentLimitA *float64) error {
|
||||
f.lastKind = commandKindPanel
|
||||
f.lastSwitchState = switchState
|
||||
f.lastCurrentLimit = currentLimitA
|
||||
return f.err
|
||||
}
|
||||
|
||||
func (f *fakeWriter) SetStandby(standby bool) error {
|
||||
f.lastKind = commandKindStandby
|
||||
f.lastStandby = &standby
|
||||
return f.err
|
||||
}
|
||||
|
||||
func Test_decodeWriteCommand(t *testing.T) {
|
||||
currentLimit := 16.5
|
||||
tests := []struct {
|
||||
name string
|
||||
payload string
|
||||
check func(*testing.T, writeCommand)
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
name: "setting",
|
||||
payload: `{"request_id":"abc","kind":"setting","id":15,"value":-5}`,
|
||||
check: func(t *testing.T, got writeCommand) {
|
||||
assert.Equal(t, writeCommand{
|
||||
RequestID: "abc",
|
||||
Kind: commandKindSetting,
|
||||
ID: 15,
|
||||
Value: -5,
|
||||
}, got)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ram_var alias from type",
|
||||
payload: `{"type":"ramvar","id":2,"value":7}`,
|
||||
check: func(t *testing.T, got writeCommand) {
|
||||
assert.Equal(t, writeCommand{
|
||||
Kind: commandKindRAMVar,
|
||||
ID: 2,
|
||||
Value: 7,
|
||||
}, got)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "panel state",
|
||||
payload: `{"kind":"panel_state","switch":"on","current_limit":16.5}`,
|
||||
check: func(t *testing.T, got writeCommand) {
|
||||
assert.Equal(t, commandKindPanel, got.Kind)
|
||||
assert.True(t, got.HasSwitch)
|
||||
assert.Equal(t, mk2driver.PanelSwitchOn, got.SwitchState)
|
||||
assert.Equal(t, "on", got.SwitchName)
|
||||
if assert.NotNil(t, got.CurrentLimitA) {
|
||||
assert.Equal(t, currentLimit, *got.CurrentLimitA)
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "panel current limit only",
|
||||
payload: `{"kind":"panel_state","current_limit":12.0}`,
|
||||
check: func(t *testing.T, got writeCommand) {
|
||||
assert.Equal(t, commandKindPanel, got.Kind)
|
||||
assert.False(t, got.HasSwitch)
|
||||
assert.Nil(t, got.Standby)
|
||||
if assert.NotNil(t, got.CurrentLimitA) {
|
||||
assert.Equal(t, 12.0, *got.CurrentLimitA)
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "standby bool",
|
||||
payload: `{"kind":"standby","standby":true}`,
|
||||
check: func(t *testing.T, got writeCommand) {
|
||||
assert.Equal(t, commandKindStandby, got.Kind)
|
||||
if assert.NotNil(t, got.Standby) {
|
||||
assert.True(t, *got.Standby)
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "standby using value string",
|
||||
payload: `{"kind":"standby","value":"OFF"}`,
|
||||
check: func(t *testing.T, got writeCommand) {
|
||||
assert.Equal(t, commandKindStandby, got.Kind)
|
||||
if assert.NotNil(t, got.Standby) {
|
||||
assert.False(t, *got.Standby)
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "missing id",
|
||||
payload: `{"kind":"setting","value":1}`,
|
||||
wantErr: `missing required field "id"`,
|
||||
},
|
||||
{
|
||||
name: "missing panel switch and current limit",
|
||||
payload: `{"kind":"panel_state"}`,
|
||||
wantErr: `missing required field "switch"`,
|
||||
},
|
||||
{
|
||||
name: "invalid standby",
|
||||
payload: `{"kind":"standby","value":"banana"}`,
|
||||
wantErr: `field "standby" must be true/false`,
|
||||
},
|
||||
{
|
||||
name: "invalid kind",
|
||||
payload: `{"kind":"unknown","id":1,"value":1}`,
|
||||
wantErr: `unsupported write command kind "unknown"`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := decodeWriteCommand([]byte(tt.payload))
|
||||
if tt.wantErr != "" {
|
||||
assert.Error(t, err)
|
||||
assert.ErrorContains(t, err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
|
||||
assert.NoError(t, err)
|
||||
tt.check(t, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_executeWriteCommand(t *testing.T) {
|
||||
limit := 8.0
|
||||
standby := true
|
||||
tests := []struct {
|
||||
name string
|
||||
cmd writeCommand
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "setting",
|
||||
cmd: writeCommand{
|
||||
Kind: commandKindSetting,
|
||||
ID: 9,
|
||||
Value: 2,
|
||||
},
|
||||
want: commandKindSetting,
|
||||
},
|
||||
{
|
||||
name: "ram_var",
|
||||
cmd: writeCommand{
|
||||
Kind: commandKindRAMVar,
|
||||
ID: 3,
|
||||
Value: -1,
|
||||
},
|
||||
want: commandKindRAMVar,
|
||||
},
|
||||
{
|
||||
name: "panel_state",
|
||||
cmd: writeCommand{
|
||||
Kind: commandKindPanel,
|
||||
HasSwitch: true,
|
||||
SwitchState: mk2driver.PanelSwitchInverterOnly,
|
||||
CurrentLimitA: &limit,
|
||||
},
|
||||
want: commandKindPanel,
|
||||
},
|
||||
{
|
||||
name: "standby",
|
||||
cmd: writeCommand{
|
||||
Kind: commandKindStandby,
|
||||
Standby: &standby,
|
||||
},
|
||||
want: commandKindStandby,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
writer := &fakeWriter{}
|
||||
err := executeWriteCommand(writer, tt.cmd)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tt.want, writer.lastKind)
|
||||
switch tt.want {
|
||||
case commandKindPanel:
|
||||
assert.Equal(t, tt.cmd.SwitchState, writer.lastSwitchState)
|
||||
if assert.NotNil(t, writer.lastCurrentLimit) {
|
||||
assert.Equal(t, *tt.cmd.CurrentLimitA, *writer.lastCurrentLimit)
|
||||
}
|
||||
case commandKindStandby:
|
||||
if assert.NotNil(t, writer.lastStandby) {
|
||||
assert.Equal(t, *tt.cmd.Standby, *writer.lastStandby)
|
||||
}
|
||||
default:
|
||||
assert.Equal(t, tt.cmd.ID, writer.lastID)
|
||||
assert.Equal(t, tt.cmd.Value, writer.lastValue)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_buildHADiscoveryDefinitions(t *testing.T) {
|
||||
cfg := Config{
|
||||
Topic: "invertergui/updates",
|
||||
CommandTopic: "invertergui/settings/set",
|
||||
HomeAssistant: HomeAssistantConfig{
|
||||
Enabled: true,
|
||||
DiscoveryPrefix: "homeassistant",
|
||||
NodeID: "victron_main",
|
||||
DeviceName: "Shed Victron",
|
||||
},
|
||||
}
|
||||
|
||||
definitions := buildHADiscoveryDefinitions(cfg)
|
||||
assert.NotEmpty(t, definitions)
|
||||
|
||||
var panelMode *haDiscoveryDefinition
|
||||
var panelCurrentLimit *haDiscoveryDefinition
|
||||
var panelStandby *haDiscoveryDefinition
|
||||
var batteryVoltage *haDiscoveryDefinition
|
||||
for i := range definitions {
|
||||
def := &definitions[i]
|
||||
if def.Component == "select" && def.ObjectID == "remote_panel_mode" {
|
||||
panelMode = def
|
||||
}
|
||||
if def.Component == "number" && def.ObjectID == "remote_panel_current_limit" {
|
||||
panelCurrentLimit = def
|
||||
}
|
||||
if def.Component == "switch" && def.ObjectID == "remote_panel_standby" {
|
||||
panelStandby = def
|
||||
}
|
||||
if def.Component == "sensor" && def.ObjectID == "battery_voltage" {
|
||||
batteryVoltage = def
|
||||
}
|
||||
}
|
||||
|
||||
if assert.NotNil(t, panelMode) {
|
||||
assert.Equal(t, cfg.CommandTopic, panelMode.Config["command_topic"])
|
||||
assert.Equal(t, haPanelSwitchStateTopic(cfg), panelMode.Config["state_topic"])
|
||||
}
|
||||
if assert.NotNil(t, panelCurrentLimit) {
|
||||
assert.Equal(t, cfg.CommandTopic, panelCurrentLimit.Config["command_topic"])
|
||||
assert.Equal(t, haCurrentLimitStateTopic(cfg), panelCurrentLimit.Config["state_topic"])
|
||||
}
|
||||
if assert.NotNil(t, panelStandby) {
|
||||
assert.Equal(t, cfg.CommandTopic, panelStandby.Config["command_topic"])
|
||||
assert.Equal(t, haStandbyStateTopic(cfg), panelStandby.Config["state_topic"])
|
||||
}
|
||||
if assert.NotNil(t, batteryVoltage) {
|
||||
assert.Equal(t, cfg.Topic, batteryVoltage.Config["state_topic"])
|
||||
}
|
||||
}
|
||||
|
||||
func Test_panelStateCacheResolvePanelCommand(t *testing.T) {
|
||||
cache := &panelStateCache{}
|
||||
|
||||
_, err := cache.resolvePanelCommand(writeCommand{
|
||||
Kind: commandKindPanel,
|
||||
CurrentLimitA: float64Ptr(12),
|
||||
})
|
||||
assert.Error(t, err)
|
||||
|
||||
cache.remember(writeCommand{
|
||||
Kind: commandKindPanel,
|
||||
HasSwitch: true,
|
||||
SwitchState: mk2driver.PanelSwitchOn,
|
||||
SwitchName: "on",
|
||||
})
|
||||
|
||||
resolved, err := cache.resolvePanelCommand(writeCommand{
|
||||
Kind: commandKindPanel,
|
||||
CurrentLimitA: float64Ptr(10),
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, resolved.HasSwitch)
|
||||
assert.Equal(t, mk2driver.PanelSwitchOn, resolved.SwitchState)
|
||||
assert.Equal(t, "on", resolved.SwitchName)
|
||||
}
|
||||
|
||||
func float64Ptr(in float64) *float64 {
|
||||
return &in
|
||||
}
|
||||
|
||||
func Test_normalizeID(t *testing.T) {
|
||||
assert.Equal(t, "victron_main_01", normalizeID("Victron Main #01"))
|
||||
assert.Equal(t, "inverter-gui", normalizeID(" inverter-gui "))
|
||||
assert.Equal(t, "", normalizeID(" "))
|
||||
}
|
||||
@@ -36,7 +36,7 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/diebietse/invertergui/mk2driver"
|
||||
"invertergui/mk2driver"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@@ -63,7 +63,7 @@ func NewMunin(mk2 mk2driver.Mk2) *Munin {
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *Munin) ServeMuninHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
func (m *Munin) ServeMuninHTTP(rw http.ResponseWriter, _ *http.Request) {
|
||||
muninDat := <-m.muninResponse
|
||||
if muninDat.timesUpdated == 0 {
|
||||
log.Error("No data returned")
|
||||
@@ -103,7 +103,7 @@ func (m *Munin) ServeMuninHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Munin) ServeMuninConfigHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
func (m *Munin) ServeMuninConfigHTTP(rw http.ResponseWriter, _ *http.Request) {
|
||||
output := muninConfig
|
||||
_, err := rw.Write([]byte(output))
|
||||
if err != nil {
|
||||
|
||||
@@ -6,10 +6,10 @@ import (
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/diebietse/invertergui/mk2driver"
|
||||
"invertergui/mk2driver"
|
||||
)
|
||||
|
||||
func TestServer(t *testing.T) {
|
||||
func TestServer(_ *testing.T) {
|
||||
|
||||
mockMk2 := mk2driver.NewMk2Mock()
|
||||
muninServer := NewMunin(mockMk2)
|
||||
|
||||
@@ -31,7 +31,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
package prometheus
|
||||
|
||||
import (
|
||||
"github.com/diebietse/invertergui/mk2driver"
|
||||
"invertergui/mk2driver"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
|
||||
@@ -98,6 +98,117 @@
|
||||
<div class="alert alert-danger" role="alert" v-if="error.has_error">
|
||||
{{ error.error_message }}
|
||||
</div>
|
||||
<div
|
||||
class="alert"
|
||||
v-if="control.message !== ''"
|
||||
v-bind:class="[control.has_error ? 'alert-danger' : 'alert-success']"
|
||||
>
|
||||
{{ control.message }}
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="card mb-3">
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">Remote Panel Control</h4>
|
||||
<p class="text-muted mb-2">
|
||||
Mode and current limit are applied together, equivalent to
|
||||
<code>set_remote_panel_state</code>.
|
||||
</p>
|
||||
<p class="mb-1">
|
||||
<strong>Current Mode:</strong>
|
||||
{{ remoteModeLabel(state.remote_panel) }}
|
||||
</p>
|
||||
<p class="mb-1">
|
||||
<strong>Current Limit:</strong>
|
||||
{{ state.remote_panel.current_limit === null || state.remote_panel.current_limit === undefined ? 'Unknown' : state.remote_panel.current_limit + ' A' }}
|
||||
</p>
|
||||
<p class="mb-3">
|
||||
<strong>Standby:</strong>
|
||||
{{ remoteStandbyLabel(state.remote_panel) }}
|
||||
</p>
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<form v-on:submit.prevent="applyRemotePanelState">
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-6">
|
||||
<label for="modeSelect">Remote Panel Mode</label>
|
||||
<select
|
||||
class="form-control"
|
||||
id="modeSelect"
|
||||
v-model="remote_form.mode"
|
||||
v-bind:disabled="!state.remote_panel.writable || control.busy"
|
||||
>
|
||||
<option value="on">on</option>
|
||||
<option value="off">off</option>
|
||||
<option value="charger_only">charger_only</option>
|
||||
<option value="inverter_only">inverter_only</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group col-md-6">
|
||||
<label for="currentLimitInput">AC Input Current Limit (A)</label>
|
||||
<input
|
||||
id="currentLimitInput"
|
||||
type="number"
|
||||
min="0"
|
||||
step="0.1"
|
||||
class="form-control"
|
||||
v-model="remote_form.current_limit"
|
||||
placeholder="leave blank to keep current"
|
||||
v-bind:disabled="!state.remote_panel.writable || control.busy"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-primary"
|
||||
v-bind:disabled="!state.remote_panel.writable || control.busy"
|
||||
>
|
||||
Apply Mode + Current Limit
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<form v-on:submit.prevent="applyStandby">
|
||||
<div class="form-group">
|
||||
<div class="form-check mt-4">
|
||||
<input
|
||||
id="standbySwitch"
|
||||
type="checkbox"
|
||||
class="form-check-input"
|
||||
v-model="remote_form.standby"
|
||||
v-bind:disabled="!state.remote_panel.writable || control.busy"
|
||||
/>
|
||||
<label class="form-check-label" for="standbySwitch">
|
||||
Prevent sleep while off
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-secondary"
|
||||
v-bind:disabled="!state.remote_panel.writable || control.busy"
|
||||
>
|
||||
Apply Standby
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 text-muted" v-if="state.remote_panel.last_updated">
|
||||
Last update {{ state.remote_panel.last_updated }}
|
||||
<span v-if="state.remote_panel.last_command">
|
||||
({{ state.remote_panel.last_command }})
|
||||
</span>
|
||||
</div>
|
||||
<div class="mt-2 text-danger" v-if="state.remote_panel.last_error">
|
||||
{{ state.remote_panel.last_error }}
|
||||
</div>
|
||||
<div class="mt-2 text-warning" v-if="!state.remote_panel.writable">
|
||||
Remote control is unavailable for this data source.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<hr />
|
||||
|
||||
@@ -3,6 +3,46 @@ const timeoutMax = 30000;
|
||||
const timeoutMin = 1000;
|
||||
var timeout = timeoutMin;
|
||||
|
||||
function defaultRemotePanelState() {
|
||||
return {
|
||||
writable: false,
|
||||
mode: "unknown",
|
||||
current_limit: null,
|
||||
standby: null,
|
||||
last_command: "",
|
||||
last_error: "",
|
||||
last_updated: ""
|
||||
};
|
||||
}
|
||||
|
||||
function defaultState() {
|
||||
return {
|
||||
output_current: null,
|
||||
output_voltage: 0,
|
||||
output_frequency: 0,
|
||||
output_power: 0,
|
||||
input_current: 0,
|
||||
input_voltage: 0,
|
||||
input_frequency: 0,
|
||||
input_power: 0,
|
||||
battery_current: 0,
|
||||
battery_voltage: 0,
|
||||
battery_charge: 0,
|
||||
battery_power: 0,
|
||||
led_map: {
|
||||
led_mains: "dot-off",
|
||||
led_absorb: "dot-off",
|
||||
led_bulk: "dot-off",
|
||||
led_float: "dot-off",
|
||||
led_inverter: "dot-off",
|
||||
led_overload: "dot-off",
|
||||
led_bat_low: "dot-off",
|
||||
led_over_temp: "dot-off"
|
||||
},
|
||||
remote_panel: defaultRemotePanelState()
|
||||
};
|
||||
}
|
||||
|
||||
function loadContent() {
|
||||
app = new Vue({
|
||||
el: "#app",
|
||||
@@ -11,33 +51,172 @@ function loadContent() {
|
||||
has_error: false,
|
||||
error_message: ""
|
||||
},
|
||||
state: {
|
||||
output_current: null,
|
||||
output_voltage: 0,
|
||||
output_frequency: 0,
|
||||
output_power: 0,
|
||||
input_current: 0,
|
||||
input_voltage: 0,
|
||||
input_frequency: 0,
|
||||
input_power: 0,
|
||||
battery_current: 0,
|
||||
battery_voltage: 0,
|
||||
battery_charge: 0,
|
||||
battery_power: 0,
|
||||
led_map: [
|
||||
{ led_mains: "dot-off" },
|
||||
{ led_absorb: "dot-off" },
|
||||
{ led_bulk: "dot-off" },
|
||||
{ led_float: "dot-off" },
|
||||
{ led_inverter: "dot-off" },
|
||||
{ led_overload: "dot-off" },
|
||||
{ led_bat_low: "dot-off" },
|
||||
{ led_over_temp: "dot-off" }
|
||||
]
|
||||
control: {
|
||||
busy: false,
|
||||
has_error: false,
|
||||
message: ""
|
||||
},
|
||||
remote_form: {
|
||||
mode: "on",
|
||||
current_limit: "",
|
||||
standby: false
|
||||
},
|
||||
state: defaultState()
|
||||
},
|
||||
methods: {
|
||||
syncRemoteFormFromState: function(remoteState) {
|
||||
if (!remoteState) {
|
||||
return;
|
||||
}
|
||||
if (remoteState.mode && remoteState.mode !== "unknown") {
|
||||
this.remote_form.mode = remoteState.mode;
|
||||
}
|
||||
if (remoteState.current_limit === null || remoteState.current_limit === undefined) {
|
||||
this.remote_form.current_limit = "";
|
||||
} else {
|
||||
this.remote_form.current_limit = String(remoteState.current_limit);
|
||||
}
|
||||
if (remoteState.standby === null || remoteState.standby === undefined) {
|
||||
this.remote_form.standby = false;
|
||||
} else {
|
||||
this.remote_form.standby = !!remoteState.standby;
|
||||
}
|
||||
},
|
||||
remoteModeLabel: function(remoteState) {
|
||||
var mode = (remoteState && remoteState.mode) || "unknown";
|
||||
if (mode === "charger_only") {
|
||||
return "Charger Only";
|
||||
}
|
||||
if (mode === "inverter_only") {
|
||||
return "Inverter Only";
|
||||
}
|
||||
if (mode === "on") {
|
||||
return "On";
|
||||
}
|
||||
if (mode === "off") {
|
||||
return "Off";
|
||||
}
|
||||
return "Unknown";
|
||||
},
|
||||
remoteStandbyLabel: function(remoteState) {
|
||||
if (!remoteState || remoteState.standby === null || remoteState.standby === undefined) {
|
||||
return "Unknown";
|
||||
}
|
||||
return remoteState.standby ? "Enabled" : "Disabled";
|
||||
},
|
||||
refreshRemoteState: function() {
|
||||
var self = this;
|
||||
fetch(getAPIURI("api/remote-panel/state"))
|
||||
.then(function(resp) {
|
||||
if (!resp.ok) {
|
||||
throw new Error("Could not load remote panel state.");
|
||||
}
|
||||
return resp.json();
|
||||
})
|
||||
.then(function(payload) {
|
||||
self.state.remote_panel = payload;
|
||||
self.syncRemoteFormFromState(payload);
|
||||
})
|
||||
.catch(function(err) {
|
||||
self.control.has_error = true;
|
||||
self.control.message = err.message;
|
||||
});
|
||||
},
|
||||
applyRemotePanelState: function() {
|
||||
var self = this;
|
||||
if (!self.state.remote_panel.writable) {
|
||||
return;
|
||||
}
|
||||
|
||||
var body = {
|
||||
mode: self.remote_form.mode
|
||||
};
|
||||
if (self.remote_form.current_limit !== "") {
|
||||
var parsed = parseFloat(self.remote_form.current_limit);
|
||||
if (isNaN(parsed)) {
|
||||
self.control.has_error = true;
|
||||
self.control.message = "Current limit must be numeric.";
|
||||
return;
|
||||
}
|
||||
body.current_limit = parsed;
|
||||
}
|
||||
|
||||
self.control.busy = true;
|
||||
self.control.has_error = false;
|
||||
self.control.message = "";
|
||||
fetch(getAPIURI("api/remote-panel/state"), {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify(body)
|
||||
})
|
||||
.then(function(resp) {
|
||||
if (!resp.ok) {
|
||||
return resp.text().then(function(text) {
|
||||
throw new Error(text || "Failed to set remote panel mode/current limit.");
|
||||
});
|
||||
}
|
||||
return resp.json();
|
||||
})
|
||||
.then(function(payload) {
|
||||
self.state.remote_panel = payload;
|
||||
self.syncRemoteFormFromState(payload);
|
||||
self.control.has_error = false;
|
||||
self.control.message = "Remote panel state updated.";
|
||||
})
|
||||
.catch(function(err) {
|
||||
self.control.has_error = true;
|
||||
self.control.message = err.message;
|
||||
})
|
||||
.finally(function() {
|
||||
self.control.busy = false;
|
||||
});
|
||||
},
|
||||
applyStandby: function() {
|
||||
var self = this;
|
||||
if (!self.state.remote_panel.writable) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.control.busy = true;
|
||||
self.control.has_error = false;
|
||||
self.control.message = "";
|
||||
fetch(getAPIURI("api/remote-panel/standby"), {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify({
|
||||
standby: !!self.remote_form.standby
|
||||
})
|
||||
})
|
||||
.then(function(resp) {
|
||||
if (!resp.ok) {
|
||||
return resp.text().then(function(text) {
|
||||
throw new Error(text || "Failed to set standby mode.");
|
||||
});
|
||||
}
|
||||
return resp.json();
|
||||
})
|
||||
.then(function(payload) {
|
||||
self.state.remote_panel = payload;
|
||||
self.syncRemoteFormFromState(payload);
|
||||
self.control.has_error = false;
|
||||
self.control.message = "Standby mode updated.";
|
||||
})
|
||||
.catch(function(err) {
|
||||
self.control.has_error = true;
|
||||
self.control.message = err.message;
|
||||
})
|
||||
.finally(function() {
|
||||
self.control.busy = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
app.refreshRemoteState();
|
||||
connect();
|
||||
}
|
||||
|
||||
@@ -61,7 +240,7 @@ function connect() {
|
||||
}
|
||||
};
|
||||
|
||||
conn.onopen = function(evt) {
|
||||
conn.onopen = function() {
|
||||
timeout = timeoutMin;
|
||||
app.error.has_error = false;
|
||||
};
|
||||
@@ -69,6 +248,9 @@ function connect() {
|
||||
conn.onmessage = function(evt) {
|
||||
var update = JSON.parse(evt.data);
|
||||
app.state = update;
|
||||
if (!app.control.busy) {
|
||||
app.syncRemoteFormFromState(update.remote_panel);
|
||||
}
|
||||
};
|
||||
} else {
|
||||
app.error.has_error = true;
|
||||
@@ -88,3 +270,11 @@ function getURI() {
|
||||
new_uri += loc.pathname + "ws";
|
||||
return new_uri;
|
||||
}
|
||||
|
||||
function getAPIURI(path) {
|
||||
var base = window.location.pathname;
|
||||
if (base.slice(-1) !== "/") {
|
||||
base += "/";
|
||||
}
|
||||
return base + path.replace(/^\/+/, "");
|
||||
}
|
||||
|
||||
@@ -31,13 +31,15 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
package webui
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/diebietse/invertergui/mk2driver"
|
||||
"github.com/diebietse/invertergui/websocket"
|
||||
"invertergui/mk2driver"
|
||||
"invertergui/websocket"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@@ -51,25 +53,53 @@ const (
|
||||
BlinkGreen = "blink-green"
|
||||
)
|
||||
|
||||
const (
|
||||
modeChargerOnly = "charger_only"
|
||||
modeInverterOnly = "inverter_only"
|
||||
modeOn = "on"
|
||||
modeOff = "off"
|
||||
modeUnknown = "unknown"
|
||||
)
|
||||
|
||||
type WebGui struct {
|
||||
mk2driver.Mk2
|
||||
writer mk2driver.SettingsWriter
|
||||
stopChan chan struct{}
|
||||
|
||||
wg sync.WaitGroup
|
||||
hub *websocket.Hub
|
||||
|
||||
stateMu sync.RWMutex
|
||||
latest *templateInput
|
||||
remote remotePanelState
|
||||
}
|
||||
|
||||
func NewWebGui(source mk2driver.Mk2) *WebGui {
|
||||
func NewWebGui(source mk2driver.Mk2, writer mk2driver.SettingsWriter) *WebGui {
|
||||
w := &WebGui{
|
||||
stopChan: make(chan struct{}),
|
||||
Mk2: source,
|
||||
writer: writer,
|
||||
hub: websocket.NewHub(),
|
||||
remote: remotePanelState{
|
||||
Writable: writer != nil,
|
||||
Mode: modeUnknown,
|
||||
},
|
||||
}
|
||||
w.wg.Add(1)
|
||||
go w.dataPoll()
|
||||
return w
|
||||
}
|
||||
|
||||
type remotePanelState struct {
|
||||
Writable bool `json:"writable"`
|
||||
Mode string `json:"mode"`
|
||||
CurrentLimit *float64 `json:"current_limit,omitempty"`
|
||||
Standby *bool `json:"standby,omitempty"`
|
||||
LastCommand string `json:"last_command,omitempty"`
|
||||
LastError string `json:"last_error,omitempty"`
|
||||
LastUpdated string `json:"last_updated,omitempty"`
|
||||
}
|
||||
|
||||
type templateInput struct {
|
||||
Error []error `json:"errors"`
|
||||
|
||||
@@ -94,12 +124,125 @@ type templateInput struct {
|
||||
OutFreq string `json:"output_frequency"`
|
||||
|
||||
LedMap map[string]string `json:"led_map"`
|
||||
|
||||
RemotePanel remotePanelState `json:"remote_panel"`
|
||||
}
|
||||
|
||||
type setRemotePanelStateRequest struct {
|
||||
Mode string `json:"mode"`
|
||||
CurrentLimit *float64 `json:"current_limit"`
|
||||
}
|
||||
|
||||
type setRemotePanelStandbyRequest struct {
|
||||
Standby bool `json:"standby"`
|
||||
}
|
||||
|
||||
func (w *WebGui) ServeHub(rw http.ResponseWriter, r *http.Request) {
|
||||
w.hub.ServeHTTP(rw, r)
|
||||
}
|
||||
|
||||
func (w *WebGui) ServeRemotePanelState(rw http.ResponseWriter, r *http.Request) {
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
writeJSON(rw, http.StatusOK, w.getRemotePanelState())
|
||||
case http.MethodPost:
|
||||
w.handleSetRemotePanelState(rw, r)
|
||||
default:
|
||||
http.Error(rw, "method not allowed", http.StatusMethodNotAllowed)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *WebGui) ServeRemotePanelStandby(rw http.ResponseWriter, r *http.Request) {
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
writeJSON(rw, http.StatusOK, w.getRemotePanelState())
|
||||
case http.MethodPost:
|
||||
w.handleSetRemotePanelStandby(rw, r)
|
||||
default:
|
||||
http.Error(rw, "method not allowed", http.StatusMethodNotAllowed)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *WebGui) handleSetRemotePanelState(rw http.ResponseWriter, r *http.Request) {
|
||||
if w.writer == nil {
|
||||
http.Error(rw, "remote control is not supported by this data source", http.StatusNotImplemented)
|
||||
return
|
||||
}
|
||||
|
||||
req := setRemotePanelStateRequest{}
|
||||
if err := decodeJSONBody(r, &req); err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
switchState, normalizedMode, err := parsePanelMode(req.Mode)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if err := w.writer.SetPanelState(switchState, req.CurrentLimit); err != nil {
|
||||
w.updateRemotePanelState(func(state *remotePanelState) {
|
||||
state.LastCommand = "set_remote_panel_state"
|
||||
state.LastError = err.Error()
|
||||
})
|
||||
http.Error(rw, err.Error(), http.StatusBadGateway)
|
||||
return
|
||||
}
|
||||
|
||||
w.updateRemotePanelState(func(state *remotePanelState) {
|
||||
state.Mode = normalizedMode
|
||||
state.CurrentLimit = copyFloat64Ptr(req.CurrentLimit)
|
||||
state.LastCommand = "set_remote_panel_state"
|
||||
state.LastError = ""
|
||||
})
|
||||
writeJSON(rw, http.StatusOK, w.getRemotePanelState())
|
||||
}
|
||||
|
||||
func (w *WebGui) handleSetRemotePanelStandby(rw http.ResponseWriter, r *http.Request) {
|
||||
if w.writer == nil {
|
||||
http.Error(rw, "remote control is not supported by this data source", http.StatusNotImplemented)
|
||||
return
|
||||
}
|
||||
|
||||
req := setRemotePanelStandbyRequest{}
|
||||
if err := decodeJSONBody(r, &req); err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if err := w.writer.SetStandby(req.Standby); err != nil {
|
||||
w.updateRemotePanelState(func(state *remotePanelState) {
|
||||
state.LastCommand = "set_remote_panel_standby"
|
||||
state.LastError = err.Error()
|
||||
})
|
||||
http.Error(rw, err.Error(), http.StatusBadGateway)
|
||||
return
|
||||
}
|
||||
|
||||
w.updateRemotePanelState(func(state *remotePanelState) {
|
||||
state.Standby = copyBoolPtr(&req.Standby)
|
||||
state.LastCommand = "set_remote_panel_standby"
|
||||
state.LastError = ""
|
||||
})
|
||||
writeJSON(rw, http.StatusOK, w.getRemotePanelState())
|
||||
}
|
||||
|
||||
func parsePanelMode(raw string) (mk2driver.PanelSwitchState, string, error) {
|
||||
switch strings.TrimSpace(strings.ToLower(raw)) {
|
||||
case modeChargerOnly:
|
||||
return mk2driver.PanelSwitchChargerOnly, modeChargerOnly, nil
|
||||
case modeInverterOnly:
|
||||
return mk2driver.PanelSwitchInverterOnly, modeInverterOnly, nil
|
||||
case modeOn:
|
||||
return mk2driver.PanelSwitchOn, modeOn, nil
|
||||
case modeOff:
|
||||
return mk2driver.PanelSwitchOff, modeOff, nil
|
||||
default:
|
||||
return 0, "", fmt.Errorf("unsupported panel mode %q", raw)
|
||||
}
|
||||
}
|
||||
|
||||
func ledName(led mk2driver.Led) string {
|
||||
name, ok := mk2driver.LedNames[led]
|
||||
if !ok {
|
||||
@@ -162,15 +305,17 @@ func (w *WebGui) Stop() {
|
||||
w.wg.Wait()
|
||||
}
|
||||
|
||||
// dataPoll waits for data from the w.poller channel. It will send its currently stored status
|
||||
// to respChan if anything reads from it.
|
||||
func (w *WebGui) dataPoll() {
|
||||
for {
|
||||
select {
|
||||
case s := <-w.C():
|
||||
if s.Valid {
|
||||
err := w.hub.Broadcast(buildTemplateInput(s))
|
||||
if err != nil {
|
||||
payload := buildTemplateInput(s)
|
||||
w.stateMu.Lock()
|
||||
payload.RemotePanel = w.remote
|
||||
w.latest = payload
|
||||
w.stateMu.Unlock()
|
||||
if err := w.hub.Broadcast(payload); err != nil {
|
||||
log.Errorf("Could not send update to clients: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -180,3 +325,93 @@ func (w *WebGui) dataPoll() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (w *WebGui) getRemotePanelState() remotePanelState {
|
||||
w.stateMu.RLock()
|
||||
defer w.stateMu.RUnlock()
|
||||
return copyRemotePanelState(w.remote)
|
||||
}
|
||||
|
||||
func (w *WebGui) updateRemotePanelState(update func(state *remotePanelState)) {
|
||||
w.stateMu.Lock()
|
||||
update(&w.remote)
|
||||
w.remote.LastUpdated = time.Now().UTC().Format(time.RFC3339)
|
||||
snapshot := w.snapshotLocked()
|
||||
w.stateMu.Unlock()
|
||||
|
||||
if snapshot != nil {
|
||||
if err := w.hub.Broadcast(snapshot); err != nil {
|
||||
log.Errorf("Could not send control update to clients: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (w *WebGui) snapshotLocked() *templateInput {
|
||||
if w.latest == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
snapshot := cloneTemplateInput(w.latest)
|
||||
snapshot.RemotePanel = copyRemotePanelState(w.remote)
|
||||
return snapshot
|
||||
}
|
||||
|
||||
func cloneTemplateInput(in *templateInput) *templateInput {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := *in
|
||||
|
||||
if in.Error != nil {
|
||||
out.Error = append([]error(nil), in.Error...)
|
||||
}
|
||||
if in.LedMap != nil {
|
||||
out.LedMap = make(map[string]string, len(in.LedMap))
|
||||
for k, v := range in.LedMap {
|
||||
out.LedMap[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
out.RemotePanel = copyRemotePanelState(in.RemotePanel)
|
||||
return &out
|
||||
}
|
||||
|
||||
func copyRemotePanelState(in remotePanelState) remotePanelState {
|
||||
in.CurrentLimit = copyFloat64Ptr(in.CurrentLimit)
|
||||
in.Standby = copyBoolPtr(in.Standby)
|
||||
return in
|
||||
}
|
||||
|
||||
func copyFloat64Ptr(in *float64) *float64 {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
value := *in
|
||||
return &value
|
||||
}
|
||||
|
||||
func copyBoolPtr(in *bool) *bool {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
value := *in
|
||||
return &value
|
||||
}
|
||||
|
||||
func decodeJSONBody(r *http.Request, destination any) error {
|
||||
defer r.Body.Close()
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
decoder.DisallowUnknownFields()
|
||||
if err := decoder.Decode(destination); err != nil {
|
||||
return fmt.Errorf("invalid request body: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeJSON(rw http.ResponseWriter, statusCode int, payload any) {
|
||||
rw.Header().Set("Content-Type", "application/json")
|
||||
rw.WriteHeader(statusCode)
|
||||
if err := json.NewEncoder(rw).Encode(payload); err != nil {
|
||||
log.Errorf("Could not encode webui API response: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/diebietse/invertergui/mk2driver"
|
||||
"invertergui/mk2driver"
|
||||
)
|
||||
|
||||
type templateTest struct {
|
||||
@@ -91,3 +91,53 @@ func TestTemplateInput(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsePanelMode(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
want mk2driver.PanelSwitchState
|
||||
wantRaw string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "on",
|
||||
input: "on",
|
||||
want: mk2driver.PanelSwitchOn,
|
||||
wantRaw: "on",
|
||||
},
|
||||
{
|
||||
name: "charger_only",
|
||||
input: "charger_only",
|
||||
want: mk2driver.PanelSwitchChargerOnly,
|
||||
wantRaw: "charger_only",
|
||||
},
|
||||
{
|
||||
name: "invalid",
|
||||
input: "banana",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, gotRaw, err := parsePanelMode(tt.input)
|
||||
if tt.wantErr {
|
||||
if err == nil {
|
||||
t.Fatal("expected error, got nil")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if got != tt.want {
|
||||
t.Fatalf("got switch %d, want %d", got, tt.want)
|
||||
}
|
||||
if gotRaw != tt.wantRaw {
|
||||
t.Fatalf("got mode %q, want %q", gotRaw, tt.wantRaw)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
33
vendor/github.com/cespare/xxhash/v2/README.md
generated
vendored
33
vendor/github.com/cespare/xxhash/v2/README.md
generated
vendored
@@ -3,8 +3,7 @@
|
||||
[](https://pkg.go.dev/github.com/cespare/xxhash/v2)
|
||||
[](https://github.com/cespare/xxhash/actions/workflows/test.yml)
|
||||
|
||||
xxhash is a Go implementation of the 64-bit
|
||||
[xxHash](http://cyan4973.github.io/xxHash/) algorithm, XXH64. This is a
|
||||
xxhash is a Go implementation of the 64-bit [xxHash] algorithm, XXH64. This is a
|
||||
high-quality hashing algorithm that is much faster than anything in the Go
|
||||
standard library.
|
||||
|
||||
@@ -25,8 +24,11 @@ func (*Digest) WriteString(string) (int, error)
|
||||
func (*Digest) Sum64() uint64
|
||||
```
|
||||
|
||||
This implementation provides a fast pure-Go implementation and an even faster
|
||||
assembly implementation for amd64.
|
||||
The package is written with optimized pure Go and also contains even faster
|
||||
assembly implementations for amd64 and arm64. If desired, the `purego` build tag
|
||||
opts into using the Go code even on those architectures.
|
||||
|
||||
[xxHash]: http://cyan4973.github.io/xxHash/
|
||||
|
||||
## Compatibility
|
||||
|
||||
@@ -45,19 +47,20 @@ I recommend using the latest release of Go.
|
||||
Here are some quick benchmarks comparing the pure-Go and assembly
|
||||
implementations of Sum64.
|
||||
|
||||
| input size | purego | asm |
|
||||
| --- | --- | --- |
|
||||
| 5 B | 979.66 MB/s | 1291.17 MB/s |
|
||||
| 100 B | 7475.26 MB/s | 7973.40 MB/s |
|
||||
| 4 KB | 17573.46 MB/s | 17602.65 MB/s |
|
||||
| 10 MB | 17131.46 MB/s | 17142.16 MB/s |
|
||||
| input size | purego | asm |
|
||||
| ---------- | --------- | --------- |
|
||||
| 4 B | 1.3 GB/s | 1.2 GB/s |
|
||||
| 16 B | 2.9 GB/s | 3.5 GB/s |
|
||||
| 100 B | 6.9 GB/s | 8.1 GB/s |
|
||||
| 4 KB | 11.7 GB/s | 16.7 GB/s |
|
||||
| 10 MB | 12.0 GB/s | 17.3 GB/s |
|
||||
|
||||
These numbers were generated on Ubuntu 18.04 with an Intel i7-8700K CPU using
|
||||
the following commands under Go 1.11.2:
|
||||
These numbers were generated on Ubuntu 20.04 with an Intel Xeon Platinum 8252C
|
||||
CPU using the following commands under Go 1.19.2:
|
||||
|
||||
```
|
||||
$ go test -tags purego -benchtime 10s -bench '/xxhash,direct,bytes'
|
||||
$ go test -benchtime 10s -bench '/xxhash,direct,bytes'
|
||||
benchstat <(go test -tags purego -benchtime 500ms -count 15 -bench 'Sum64$')
|
||||
benchstat <(go test -benchtime 500ms -count 15 -bench 'Sum64$')
|
||||
```
|
||||
|
||||
## Projects using this package
|
||||
@@ -67,3 +70,5 @@ $ go test -benchtime 10s -bench '/xxhash,direct,bytes'
|
||||
- [VictoriaMetrics](https://github.com/VictoriaMetrics/VictoriaMetrics)
|
||||
- [FreeCache](https://github.com/coocood/freecache)
|
||||
- [FastCache](https://github.com/VictoriaMetrics/fastcache)
|
||||
- [Ristretto](https://github.com/dgraph-io/ristretto)
|
||||
- [Badger](https://github.com/dgraph-io/badger)
|
||||
|
||||
10
vendor/github.com/cespare/xxhash/v2/testall.sh
generated
vendored
Normal file
10
vendor/github.com/cespare/xxhash/v2/testall.sh
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
#!/bin/bash
|
||||
set -eu -o pipefail
|
||||
|
||||
# Small convenience script for running the tests with various combinations of
|
||||
# arch/tags. This assumes we're running on amd64 and have qemu available.
|
||||
|
||||
go test ./...
|
||||
go test -tags purego ./...
|
||||
GOARCH=arm64 go test
|
||||
GOARCH=arm64 go test -tags purego
|
||||
70
vendor/github.com/cespare/xxhash/v2/xxhash.go
generated
vendored
70
vendor/github.com/cespare/xxhash/v2/xxhash.go
generated
vendored
@@ -16,21 +16,16 @@ const (
|
||||
prime5 uint64 = 2870177450012600261
|
||||
)
|
||||
|
||||
// NOTE(caleb): I'm using both consts and vars of the primes. Using consts where
|
||||
// possible in the Go code is worth a small (but measurable) performance boost
|
||||
// by avoiding some MOVQs. Vars are needed for the asm and also are useful for
|
||||
// convenience in the Go code in a few places where we need to intentionally
|
||||
// avoid constant arithmetic (e.g., v1 := prime1 + prime2 fails because the
|
||||
// result overflows a uint64).
|
||||
var (
|
||||
prime1v = prime1
|
||||
prime2v = prime2
|
||||
prime3v = prime3
|
||||
prime4v = prime4
|
||||
prime5v = prime5
|
||||
)
|
||||
// Store the primes in an array as well.
|
||||
//
|
||||
// The consts are used when possible in Go code to avoid MOVs but we need a
|
||||
// contiguous array for the assembly code.
|
||||
var primes = [...]uint64{prime1, prime2, prime3, prime4, prime5}
|
||||
|
||||
// Digest implements hash.Hash64.
|
||||
//
|
||||
// Note that a zero-valued Digest is not ready to receive writes.
|
||||
// Call Reset or create a Digest using New before calling other methods.
|
||||
type Digest struct {
|
||||
v1 uint64
|
||||
v2 uint64
|
||||
@@ -41,19 +36,31 @@ type Digest struct {
|
||||
n int // how much of mem is used
|
||||
}
|
||||
|
||||
// New creates a new Digest that computes the 64-bit xxHash algorithm.
|
||||
// New creates a new Digest with a zero seed.
|
||||
func New() *Digest {
|
||||
return NewWithSeed(0)
|
||||
}
|
||||
|
||||
// NewWithSeed creates a new Digest with the given seed.
|
||||
func NewWithSeed(seed uint64) *Digest {
|
||||
var d Digest
|
||||
d.Reset()
|
||||
d.ResetWithSeed(seed)
|
||||
return &d
|
||||
}
|
||||
|
||||
// Reset clears the Digest's state so that it can be reused.
|
||||
// It uses a seed value of zero.
|
||||
func (d *Digest) Reset() {
|
||||
d.v1 = prime1v + prime2
|
||||
d.v2 = prime2
|
||||
d.v3 = 0
|
||||
d.v4 = -prime1v
|
||||
d.ResetWithSeed(0)
|
||||
}
|
||||
|
||||
// ResetWithSeed clears the Digest's state so that it can be reused.
|
||||
// It uses the given seed to initialize the state.
|
||||
func (d *Digest) ResetWithSeed(seed uint64) {
|
||||
d.v1 = seed + prime1 + prime2
|
||||
d.v2 = seed + prime2
|
||||
d.v3 = seed
|
||||
d.v4 = seed - prime1
|
||||
d.total = 0
|
||||
d.n = 0
|
||||
}
|
||||
@@ -69,21 +76,23 @@ func (d *Digest) Write(b []byte) (n int, err error) {
|
||||
n = len(b)
|
||||
d.total += uint64(n)
|
||||
|
||||
memleft := d.mem[d.n&(len(d.mem)-1):]
|
||||
|
||||
if d.n+n < 32 {
|
||||
// This new data doesn't even fill the current block.
|
||||
copy(d.mem[d.n:], b)
|
||||
copy(memleft, b)
|
||||
d.n += n
|
||||
return
|
||||
}
|
||||
|
||||
if d.n > 0 {
|
||||
// Finish off the partial block.
|
||||
copy(d.mem[d.n:], b)
|
||||
c := copy(memleft, b)
|
||||
d.v1 = round(d.v1, u64(d.mem[0:8]))
|
||||
d.v2 = round(d.v2, u64(d.mem[8:16]))
|
||||
d.v3 = round(d.v3, u64(d.mem[16:24]))
|
||||
d.v4 = round(d.v4, u64(d.mem[24:32]))
|
||||
b = b[32-d.n:]
|
||||
b = b[c:]
|
||||
d.n = 0
|
||||
}
|
||||
|
||||
@@ -133,21 +142,20 @@ func (d *Digest) Sum64() uint64 {
|
||||
|
||||
h += d.total
|
||||
|
||||
i, end := 0, d.n
|
||||
for ; i+8 <= end; i += 8 {
|
||||
k1 := round(0, u64(d.mem[i:i+8]))
|
||||
b := d.mem[:d.n&(len(d.mem)-1)]
|
||||
for ; len(b) >= 8; b = b[8:] {
|
||||
k1 := round(0, u64(b[:8]))
|
||||
h ^= k1
|
||||
h = rol27(h)*prime1 + prime4
|
||||
}
|
||||
if i+4 <= end {
|
||||
h ^= uint64(u32(d.mem[i:i+4])) * prime1
|
||||
if len(b) >= 4 {
|
||||
h ^= uint64(u32(b[:4])) * prime1
|
||||
h = rol23(h)*prime2 + prime3
|
||||
i += 4
|
||||
b = b[4:]
|
||||
}
|
||||
for i < end {
|
||||
h ^= uint64(d.mem[i]) * prime5
|
||||
for ; len(b) > 0; b = b[1:] {
|
||||
h ^= uint64(b[0]) * prime5
|
||||
h = rol11(h) * prime1
|
||||
i++
|
||||
}
|
||||
|
||||
h ^= h >> 33
|
||||
|
||||
308
vendor/github.com/cespare/xxhash/v2/xxhash_amd64.s
generated
vendored
308
vendor/github.com/cespare/xxhash/v2/xxhash_amd64.s
generated
vendored
@@ -1,215 +1,209 @@
|
||||
//go:build !appengine && gc && !purego
|
||||
// +build !appengine
|
||||
// +build gc
|
||||
// +build !purego
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// Register allocation:
|
||||
// AX h
|
||||
// SI pointer to advance through b
|
||||
// DX n
|
||||
// BX loop end
|
||||
// R8 v1, k1
|
||||
// R9 v2
|
||||
// R10 v3
|
||||
// R11 v4
|
||||
// R12 tmp
|
||||
// R13 prime1v
|
||||
// R14 prime2v
|
||||
// DI prime4v
|
||||
// Registers:
|
||||
#define h AX
|
||||
#define d AX
|
||||
#define p SI // pointer to advance through b
|
||||
#define n DX
|
||||
#define end BX // loop end
|
||||
#define v1 R8
|
||||
#define v2 R9
|
||||
#define v3 R10
|
||||
#define v4 R11
|
||||
#define x R12
|
||||
#define prime1 R13
|
||||
#define prime2 R14
|
||||
#define prime4 DI
|
||||
|
||||
// round reads from and advances the buffer pointer in SI.
|
||||
// It assumes that R13 has prime1v and R14 has prime2v.
|
||||
#define round(r) \
|
||||
MOVQ (SI), R12 \
|
||||
ADDQ $8, SI \
|
||||
IMULQ R14, R12 \
|
||||
ADDQ R12, r \
|
||||
ROLQ $31, r \
|
||||
IMULQ R13, r
|
||||
#define round(acc, x) \
|
||||
IMULQ prime2, x \
|
||||
ADDQ x, acc \
|
||||
ROLQ $31, acc \
|
||||
IMULQ prime1, acc
|
||||
|
||||
// mergeRound applies a merge round on the two registers acc and val.
|
||||
// It assumes that R13 has prime1v, R14 has prime2v, and DI has prime4v.
|
||||
#define mergeRound(acc, val) \
|
||||
IMULQ R14, val \
|
||||
ROLQ $31, val \
|
||||
IMULQ R13, val \
|
||||
XORQ val, acc \
|
||||
IMULQ R13, acc \
|
||||
ADDQ DI, acc
|
||||
// round0 performs the operation x = round(0, x).
|
||||
#define round0(x) \
|
||||
IMULQ prime2, x \
|
||||
ROLQ $31, x \
|
||||
IMULQ prime1, x
|
||||
|
||||
// mergeRound applies a merge round on the two registers acc and x.
|
||||
// It assumes that prime1, prime2, and prime4 have been loaded.
|
||||
#define mergeRound(acc, x) \
|
||||
round0(x) \
|
||||
XORQ x, acc \
|
||||
IMULQ prime1, acc \
|
||||
ADDQ prime4, acc
|
||||
|
||||
// blockLoop processes as many 32-byte blocks as possible,
|
||||
// updating v1, v2, v3, and v4. It assumes that there is at least one block
|
||||
// to process.
|
||||
#define blockLoop() \
|
||||
loop: \
|
||||
MOVQ +0(p), x \
|
||||
round(v1, x) \
|
||||
MOVQ +8(p), x \
|
||||
round(v2, x) \
|
||||
MOVQ +16(p), x \
|
||||
round(v3, x) \
|
||||
MOVQ +24(p), x \
|
||||
round(v4, x) \
|
||||
ADDQ $32, p \
|
||||
CMPQ p, end \
|
||||
JLE loop
|
||||
|
||||
// func Sum64(b []byte) uint64
|
||||
TEXT ·Sum64(SB), NOSPLIT, $0-32
|
||||
TEXT ·Sum64(SB), NOSPLIT|NOFRAME, $0-32
|
||||
// Load fixed primes.
|
||||
MOVQ ·prime1v(SB), R13
|
||||
MOVQ ·prime2v(SB), R14
|
||||
MOVQ ·prime4v(SB), DI
|
||||
MOVQ ·primes+0(SB), prime1
|
||||
MOVQ ·primes+8(SB), prime2
|
||||
MOVQ ·primes+24(SB), prime4
|
||||
|
||||
// Load slice.
|
||||
MOVQ b_base+0(FP), SI
|
||||
MOVQ b_len+8(FP), DX
|
||||
LEAQ (SI)(DX*1), BX
|
||||
MOVQ b_base+0(FP), p
|
||||
MOVQ b_len+8(FP), n
|
||||
LEAQ (p)(n*1), end
|
||||
|
||||
// The first loop limit will be len(b)-32.
|
||||
SUBQ $32, BX
|
||||
SUBQ $32, end
|
||||
|
||||
// Check whether we have at least one block.
|
||||
CMPQ DX, $32
|
||||
CMPQ n, $32
|
||||
JLT noBlocks
|
||||
|
||||
// Set up initial state (v1, v2, v3, v4).
|
||||
MOVQ R13, R8
|
||||
ADDQ R14, R8
|
||||
MOVQ R14, R9
|
||||
XORQ R10, R10
|
||||
XORQ R11, R11
|
||||
SUBQ R13, R11
|
||||
MOVQ prime1, v1
|
||||
ADDQ prime2, v1
|
||||
MOVQ prime2, v2
|
||||
XORQ v3, v3
|
||||
XORQ v4, v4
|
||||
SUBQ prime1, v4
|
||||
|
||||
// Loop until SI > BX.
|
||||
blockLoop:
|
||||
round(R8)
|
||||
round(R9)
|
||||
round(R10)
|
||||
round(R11)
|
||||
blockLoop()
|
||||
|
||||
CMPQ SI, BX
|
||||
JLE blockLoop
|
||||
MOVQ v1, h
|
||||
ROLQ $1, h
|
||||
MOVQ v2, x
|
||||
ROLQ $7, x
|
||||
ADDQ x, h
|
||||
MOVQ v3, x
|
||||
ROLQ $12, x
|
||||
ADDQ x, h
|
||||
MOVQ v4, x
|
||||
ROLQ $18, x
|
||||
ADDQ x, h
|
||||
|
||||
MOVQ R8, AX
|
||||
ROLQ $1, AX
|
||||
MOVQ R9, R12
|
||||
ROLQ $7, R12
|
||||
ADDQ R12, AX
|
||||
MOVQ R10, R12
|
||||
ROLQ $12, R12
|
||||
ADDQ R12, AX
|
||||
MOVQ R11, R12
|
||||
ROLQ $18, R12
|
||||
ADDQ R12, AX
|
||||
|
||||
mergeRound(AX, R8)
|
||||
mergeRound(AX, R9)
|
||||
mergeRound(AX, R10)
|
||||
mergeRound(AX, R11)
|
||||
mergeRound(h, v1)
|
||||
mergeRound(h, v2)
|
||||
mergeRound(h, v3)
|
||||
mergeRound(h, v4)
|
||||
|
||||
JMP afterBlocks
|
||||
|
||||
noBlocks:
|
||||
MOVQ ·prime5v(SB), AX
|
||||
MOVQ ·primes+32(SB), h
|
||||
|
||||
afterBlocks:
|
||||
ADDQ DX, AX
|
||||
ADDQ n, h
|
||||
|
||||
// Right now BX has len(b)-32, and we want to loop until SI > len(b)-8.
|
||||
ADDQ $24, BX
|
||||
ADDQ $24, end
|
||||
CMPQ p, end
|
||||
JG try4
|
||||
|
||||
CMPQ SI, BX
|
||||
JG fourByte
|
||||
loop8:
|
||||
MOVQ (p), x
|
||||
ADDQ $8, p
|
||||
round0(x)
|
||||
XORQ x, h
|
||||
ROLQ $27, h
|
||||
IMULQ prime1, h
|
||||
ADDQ prime4, h
|
||||
|
||||
wordLoop:
|
||||
// Calculate k1.
|
||||
MOVQ (SI), R8
|
||||
ADDQ $8, SI
|
||||
IMULQ R14, R8
|
||||
ROLQ $31, R8
|
||||
IMULQ R13, R8
|
||||
CMPQ p, end
|
||||
JLE loop8
|
||||
|
||||
XORQ R8, AX
|
||||
ROLQ $27, AX
|
||||
IMULQ R13, AX
|
||||
ADDQ DI, AX
|
||||
try4:
|
||||
ADDQ $4, end
|
||||
CMPQ p, end
|
||||
JG try1
|
||||
|
||||
CMPQ SI, BX
|
||||
JLE wordLoop
|
||||
MOVL (p), x
|
||||
ADDQ $4, p
|
||||
IMULQ prime1, x
|
||||
XORQ x, h
|
||||
|
||||
fourByte:
|
||||
ADDQ $4, BX
|
||||
CMPQ SI, BX
|
||||
JG singles
|
||||
ROLQ $23, h
|
||||
IMULQ prime2, h
|
||||
ADDQ ·primes+16(SB), h
|
||||
|
||||
MOVL (SI), R8
|
||||
ADDQ $4, SI
|
||||
IMULQ R13, R8
|
||||
XORQ R8, AX
|
||||
|
||||
ROLQ $23, AX
|
||||
IMULQ R14, AX
|
||||
ADDQ ·prime3v(SB), AX
|
||||
|
||||
singles:
|
||||
ADDQ $4, BX
|
||||
CMPQ SI, BX
|
||||
try1:
|
||||
ADDQ $4, end
|
||||
CMPQ p, end
|
||||
JGE finalize
|
||||
|
||||
singlesLoop:
|
||||
MOVBQZX (SI), R12
|
||||
ADDQ $1, SI
|
||||
IMULQ ·prime5v(SB), R12
|
||||
XORQ R12, AX
|
||||
loop1:
|
||||
MOVBQZX (p), x
|
||||
ADDQ $1, p
|
||||
IMULQ ·primes+32(SB), x
|
||||
XORQ x, h
|
||||
ROLQ $11, h
|
||||
IMULQ prime1, h
|
||||
|
||||
ROLQ $11, AX
|
||||
IMULQ R13, AX
|
||||
|
||||
CMPQ SI, BX
|
||||
JL singlesLoop
|
||||
CMPQ p, end
|
||||
JL loop1
|
||||
|
||||
finalize:
|
||||
MOVQ AX, R12
|
||||
SHRQ $33, R12
|
||||
XORQ R12, AX
|
||||
IMULQ R14, AX
|
||||
MOVQ AX, R12
|
||||
SHRQ $29, R12
|
||||
XORQ R12, AX
|
||||
IMULQ ·prime3v(SB), AX
|
||||
MOVQ AX, R12
|
||||
SHRQ $32, R12
|
||||
XORQ R12, AX
|
||||
MOVQ h, x
|
||||
SHRQ $33, x
|
||||
XORQ x, h
|
||||
IMULQ prime2, h
|
||||
MOVQ h, x
|
||||
SHRQ $29, x
|
||||
XORQ x, h
|
||||
IMULQ ·primes+16(SB), h
|
||||
MOVQ h, x
|
||||
SHRQ $32, x
|
||||
XORQ x, h
|
||||
|
||||
MOVQ AX, ret+24(FP)
|
||||
MOVQ h, ret+24(FP)
|
||||
RET
|
||||
|
||||
// writeBlocks uses the same registers as above except that it uses AX to store
|
||||
// the d pointer.
|
||||
|
||||
// func writeBlocks(d *Digest, b []byte) int
|
||||
TEXT ·writeBlocks(SB), NOSPLIT, $0-40
|
||||
TEXT ·writeBlocks(SB), NOSPLIT|NOFRAME, $0-40
|
||||
// Load fixed primes needed for round.
|
||||
MOVQ ·prime1v(SB), R13
|
||||
MOVQ ·prime2v(SB), R14
|
||||
MOVQ ·primes+0(SB), prime1
|
||||
MOVQ ·primes+8(SB), prime2
|
||||
|
||||
// Load slice.
|
||||
MOVQ b_base+8(FP), SI
|
||||
MOVQ b_len+16(FP), DX
|
||||
LEAQ (SI)(DX*1), BX
|
||||
SUBQ $32, BX
|
||||
MOVQ b_base+8(FP), p
|
||||
MOVQ b_len+16(FP), n
|
||||
LEAQ (p)(n*1), end
|
||||
SUBQ $32, end
|
||||
|
||||
// Load vN from d.
|
||||
MOVQ d+0(FP), AX
|
||||
MOVQ 0(AX), R8 // v1
|
||||
MOVQ 8(AX), R9 // v2
|
||||
MOVQ 16(AX), R10 // v3
|
||||
MOVQ 24(AX), R11 // v4
|
||||
MOVQ s+0(FP), d
|
||||
MOVQ 0(d), v1
|
||||
MOVQ 8(d), v2
|
||||
MOVQ 16(d), v3
|
||||
MOVQ 24(d), v4
|
||||
|
||||
// We don't need to check the loop condition here; this function is
|
||||
// always called with at least one block of data to process.
|
||||
blockLoop:
|
||||
round(R8)
|
||||
round(R9)
|
||||
round(R10)
|
||||
round(R11)
|
||||
|
||||
CMPQ SI, BX
|
||||
JLE blockLoop
|
||||
blockLoop()
|
||||
|
||||
// Copy vN back to d.
|
||||
MOVQ R8, 0(AX)
|
||||
MOVQ R9, 8(AX)
|
||||
MOVQ R10, 16(AX)
|
||||
MOVQ R11, 24(AX)
|
||||
MOVQ v1, 0(d)
|
||||
MOVQ v2, 8(d)
|
||||
MOVQ v3, 16(d)
|
||||
MOVQ v4, 24(d)
|
||||
|
||||
// The number of bytes written is SI minus the old base pointer.
|
||||
SUBQ b_base+8(FP), SI
|
||||
MOVQ SI, ret+32(FP)
|
||||
// The number of bytes written is p minus the old base pointer.
|
||||
SUBQ b_base+8(FP), p
|
||||
MOVQ p, ret+32(FP)
|
||||
|
||||
RET
|
||||
|
||||
183
vendor/github.com/cespare/xxhash/v2/xxhash_arm64.s
generated
vendored
Normal file
183
vendor/github.com/cespare/xxhash/v2/xxhash_arm64.s
generated
vendored
Normal file
@@ -0,0 +1,183 @@
|
||||
//go:build !appengine && gc && !purego
|
||||
// +build !appengine
|
||||
// +build gc
|
||||
// +build !purego
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// Registers:
|
||||
#define digest R1
|
||||
#define h R2 // return value
|
||||
#define p R3 // input pointer
|
||||
#define n R4 // input length
|
||||
#define nblocks R5 // n / 32
|
||||
#define prime1 R7
|
||||
#define prime2 R8
|
||||
#define prime3 R9
|
||||
#define prime4 R10
|
||||
#define prime5 R11
|
||||
#define v1 R12
|
||||
#define v2 R13
|
||||
#define v3 R14
|
||||
#define v4 R15
|
||||
#define x1 R20
|
||||
#define x2 R21
|
||||
#define x3 R22
|
||||
#define x4 R23
|
||||
|
||||
#define round(acc, x) \
|
||||
MADD prime2, acc, x, acc \
|
||||
ROR $64-31, acc \
|
||||
MUL prime1, acc
|
||||
|
||||
// round0 performs the operation x = round(0, x).
|
||||
#define round0(x) \
|
||||
MUL prime2, x \
|
||||
ROR $64-31, x \
|
||||
MUL prime1, x
|
||||
|
||||
#define mergeRound(acc, x) \
|
||||
round0(x) \
|
||||
EOR x, acc \
|
||||
MADD acc, prime4, prime1, acc
|
||||
|
||||
// blockLoop processes as many 32-byte blocks as possible,
|
||||
// updating v1, v2, v3, and v4. It assumes that n >= 32.
|
||||
#define blockLoop() \
|
||||
LSR $5, n, nblocks \
|
||||
PCALIGN $16 \
|
||||
loop: \
|
||||
LDP.P 16(p), (x1, x2) \
|
||||
LDP.P 16(p), (x3, x4) \
|
||||
round(v1, x1) \
|
||||
round(v2, x2) \
|
||||
round(v3, x3) \
|
||||
round(v4, x4) \
|
||||
SUB $1, nblocks \
|
||||
CBNZ nblocks, loop
|
||||
|
||||
// func Sum64(b []byte) uint64
|
||||
TEXT ·Sum64(SB), NOSPLIT|NOFRAME, $0-32
|
||||
LDP b_base+0(FP), (p, n)
|
||||
|
||||
LDP ·primes+0(SB), (prime1, prime2)
|
||||
LDP ·primes+16(SB), (prime3, prime4)
|
||||
MOVD ·primes+32(SB), prime5
|
||||
|
||||
CMP $32, n
|
||||
CSEL LT, prime5, ZR, h // if n < 32 { h = prime5 } else { h = 0 }
|
||||
BLT afterLoop
|
||||
|
||||
ADD prime1, prime2, v1
|
||||
MOVD prime2, v2
|
||||
MOVD $0, v3
|
||||
NEG prime1, v4
|
||||
|
||||
blockLoop()
|
||||
|
||||
ROR $64-1, v1, x1
|
||||
ROR $64-7, v2, x2
|
||||
ADD x1, x2
|
||||
ROR $64-12, v3, x3
|
||||
ROR $64-18, v4, x4
|
||||
ADD x3, x4
|
||||
ADD x2, x4, h
|
||||
|
||||
mergeRound(h, v1)
|
||||
mergeRound(h, v2)
|
||||
mergeRound(h, v3)
|
||||
mergeRound(h, v4)
|
||||
|
||||
afterLoop:
|
||||
ADD n, h
|
||||
|
||||
TBZ $4, n, try8
|
||||
LDP.P 16(p), (x1, x2)
|
||||
|
||||
round0(x1)
|
||||
|
||||
// NOTE: here and below, sequencing the EOR after the ROR (using a
|
||||
// rotated register) is worth a small but measurable speedup for small
|
||||
// inputs.
|
||||
ROR $64-27, h
|
||||
EOR x1 @> 64-27, h, h
|
||||
MADD h, prime4, prime1, h
|
||||
|
||||
round0(x2)
|
||||
ROR $64-27, h
|
||||
EOR x2 @> 64-27, h, h
|
||||
MADD h, prime4, prime1, h
|
||||
|
||||
try8:
|
||||
TBZ $3, n, try4
|
||||
MOVD.P 8(p), x1
|
||||
|
||||
round0(x1)
|
||||
ROR $64-27, h
|
||||
EOR x1 @> 64-27, h, h
|
||||
MADD h, prime4, prime1, h
|
||||
|
||||
try4:
|
||||
TBZ $2, n, try2
|
||||
MOVWU.P 4(p), x2
|
||||
|
||||
MUL prime1, x2
|
||||
ROR $64-23, h
|
||||
EOR x2 @> 64-23, h, h
|
||||
MADD h, prime3, prime2, h
|
||||
|
||||
try2:
|
||||
TBZ $1, n, try1
|
||||
MOVHU.P 2(p), x3
|
||||
AND $255, x3, x1
|
||||
LSR $8, x3, x2
|
||||
|
||||
MUL prime5, x1
|
||||
ROR $64-11, h
|
||||
EOR x1 @> 64-11, h, h
|
||||
MUL prime1, h
|
||||
|
||||
MUL prime5, x2
|
||||
ROR $64-11, h
|
||||
EOR x2 @> 64-11, h, h
|
||||
MUL prime1, h
|
||||
|
||||
try1:
|
||||
TBZ $0, n, finalize
|
||||
MOVBU (p), x4
|
||||
|
||||
MUL prime5, x4
|
||||
ROR $64-11, h
|
||||
EOR x4 @> 64-11, h, h
|
||||
MUL prime1, h
|
||||
|
||||
finalize:
|
||||
EOR h >> 33, h
|
||||
MUL prime2, h
|
||||
EOR h >> 29, h
|
||||
MUL prime3, h
|
||||
EOR h >> 32, h
|
||||
|
||||
MOVD h, ret+24(FP)
|
||||
RET
|
||||
|
||||
// func writeBlocks(d *Digest, b []byte) int
|
||||
TEXT ·writeBlocks(SB), NOSPLIT|NOFRAME, $0-40
|
||||
LDP ·primes+0(SB), (prime1, prime2)
|
||||
|
||||
// Load state. Assume v[1-4] are stored contiguously.
|
||||
MOVD d+0(FP), digest
|
||||
LDP 0(digest), (v1, v2)
|
||||
LDP 16(digest), (v3, v4)
|
||||
|
||||
LDP b_base+8(FP), (p, n)
|
||||
|
||||
blockLoop()
|
||||
|
||||
// Store updated state.
|
||||
STP (v1, v2), 0(digest)
|
||||
STP (v3, v4), 16(digest)
|
||||
|
||||
BIC $31, n
|
||||
MOVD n, ret+32(FP)
|
||||
RET
|
||||
@@ -1,10 +1,12 @@
|
||||
//go:build (amd64 || arm64) && !appengine && gc && !purego
|
||||
// +build amd64 arm64
|
||||
// +build !appengine
|
||||
// +build gc
|
||||
// +build !purego
|
||||
|
||||
package xxhash
|
||||
|
||||
// Sum64 computes the 64-bit xxHash digest of b.
|
||||
// Sum64 computes the 64-bit xxHash digest of b with a zero seed.
|
||||
//
|
||||
//go:noescape
|
||||
func Sum64(b []byte) uint64
|
||||
24
vendor/github.com/cespare/xxhash/v2/xxhash_other.go
generated
vendored
24
vendor/github.com/cespare/xxhash/v2/xxhash_other.go
generated
vendored
@@ -1,8 +1,9 @@
|
||||
// +build !amd64 appengine !gc purego
|
||||
//go:build (!amd64 && !arm64) || appengine || !gc || purego
|
||||
// +build !amd64,!arm64 appengine !gc purego
|
||||
|
||||
package xxhash
|
||||
|
||||
// Sum64 computes the 64-bit xxHash digest of b.
|
||||
// Sum64 computes the 64-bit xxHash digest of b with a zero seed.
|
||||
func Sum64(b []byte) uint64 {
|
||||
// A simpler version would be
|
||||
// d := New()
|
||||
@@ -14,10 +15,10 @@ func Sum64(b []byte) uint64 {
|
||||
var h uint64
|
||||
|
||||
if n >= 32 {
|
||||
v1 := prime1v + prime2
|
||||
v1 := primes[0] + prime2
|
||||
v2 := prime2
|
||||
v3 := uint64(0)
|
||||
v4 := -prime1v
|
||||
v4 := -primes[0]
|
||||
for len(b) >= 32 {
|
||||
v1 = round(v1, u64(b[0:8:len(b)]))
|
||||
v2 = round(v2, u64(b[8:16:len(b)]))
|
||||
@@ -36,19 +37,18 @@ func Sum64(b []byte) uint64 {
|
||||
|
||||
h += uint64(n)
|
||||
|
||||
i, end := 0, len(b)
|
||||
for ; i+8 <= end; i += 8 {
|
||||
k1 := round(0, u64(b[i:i+8:len(b)]))
|
||||
for ; len(b) >= 8; b = b[8:] {
|
||||
k1 := round(0, u64(b[:8]))
|
||||
h ^= k1
|
||||
h = rol27(h)*prime1 + prime4
|
||||
}
|
||||
if i+4 <= end {
|
||||
h ^= uint64(u32(b[i:i+4:len(b)])) * prime1
|
||||
if len(b) >= 4 {
|
||||
h ^= uint64(u32(b[:4])) * prime1
|
||||
h = rol23(h)*prime2 + prime3
|
||||
i += 4
|
||||
b = b[4:]
|
||||
}
|
||||
for ; i < end; i++ {
|
||||
h ^= uint64(b[i]) * prime5
|
||||
for ; len(b) > 0; b = b[1:] {
|
||||
h ^= uint64(b[0]) * prime5
|
||||
h = rol11(h) * prime1
|
||||
}
|
||||
|
||||
|
||||
3
vendor/github.com/cespare/xxhash/v2/xxhash_safe.go
generated
vendored
3
vendor/github.com/cespare/xxhash/v2/xxhash_safe.go
generated
vendored
@@ -1,10 +1,11 @@
|
||||
//go:build appengine
|
||||
// +build appengine
|
||||
|
||||
// This file contains the safe implementations of otherwise unsafe-using code.
|
||||
|
||||
package xxhash
|
||||
|
||||
// Sum64String computes the 64-bit xxHash digest of s.
|
||||
// Sum64String computes the 64-bit xxHash digest of s with a zero seed.
|
||||
func Sum64String(s string) uint64 {
|
||||
return Sum64([]byte(s))
|
||||
}
|
||||
|
||||
5
vendor/github.com/cespare/xxhash/v2/xxhash_unsafe.go
generated
vendored
5
vendor/github.com/cespare/xxhash/v2/xxhash_unsafe.go
generated
vendored
@@ -1,3 +1,4 @@
|
||||
//go:build !appengine
|
||||
// +build !appengine
|
||||
|
||||
// This file encapsulates usage of unsafe.
|
||||
@@ -11,7 +12,7 @@ import (
|
||||
|
||||
// In the future it's possible that compiler optimizations will make these
|
||||
// XxxString functions unnecessary by realizing that calls such as
|
||||
// Sum64([]byte(s)) don't need to copy s. See https://golang.org/issue/2205.
|
||||
// Sum64([]byte(s)) don't need to copy s. See https://go.dev/issue/2205.
|
||||
// If that happens, even if we keep these functions they can be replaced with
|
||||
// the trivial safe code.
|
||||
|
||||
@@ -32,7 +33,7 @@ import (
|
||||
//
|
||||
// See https://github.com/golang/go/issues/42739 for discussion.
|
||||
|
||||
// Sum64String computes the 64-bit xxHash digest of s.
|
||||
// Sum64String computes the 64-bit xxHash digest of s with a zero seed.
|
||||
// It may be faster than Sum64([]byte(s)) by avoiding a copy.
|
||||
func Sum64String(s string) uint64 {
|
||||
b := *(*[]byte)(unsafe.Pointer(&sliceHeader{s, len(s)}))
|
||||
|
||||
6
vendor/github.com/eclipse/paho.mqtt.golang/README.md
generated
vendored
6
vendor/github.com/eclipse/paho.mqtt.golang/README.md
generated
vendored
@@ -113,7 +113,9 @@ identifier; this is as per the [spec](https://docs.oasis-open.org/mqtt/mqtt/v3.1
|
||||
not received, disconnecting` errors).
|
||||
* When QOS1+ subscriptions have been created previously and you connect with `CleanSession` set to false it is possible
|
||||
that the broker will deliver retained messages before `Subscribe` can be called. To process these messages either
|
||||
configure a handler with `AddRoute` or set a `DefaultPublishHandler`.
|
||||
configure a handler with `AddRoute` or set a `DefaultPublishHandler`. If there is no handler (or `DefaultPublishHandler`)
|
||||
then inbound messages will not be acknowledged. Adding a handler (even if it's `opts.SetDefaultPublishHandler(func(mqtt.Client, mqtt.Message) {})`)
|
||||
is highly recommended to avoid inadvertently hitting inflight message limits.
|
||||
* Loss of network connectivity may not be detected immediately. If this is an issue then consider setting
|
||||
`ClientOptions.KeepAlive` (sends regular messages to check the link is active).
|
||||
* Reusing a `Client` is not completely safe. After calling `Disconnect` please create a new Client (`NewClient()`) rather
|
||||
@@ -193,4 +195,4 @@ Discussion of the Paho clients takes place on the [Eclipse paho-dev mailing list
|
||||
|
||||
General questions about the MQTT protocol are discussed in the [MQTT Google Group](https://groups.google.com/forum/?hl=en-US&fromgroups#!forum/mqtt).
|
||||
|
||||
There is much more information available via the [MQTT community site](http://mqtt.org).
|
||||
There is much more information available via the [MQTT community site](http://mqtt.org).
|
||||
|
||||
104
vendor/github.com/eclipse/paho.mqtt.golang/backoff.go
generated
vendored
Normal file
104
vendor/github.com/eclipse/paho.mqtt.golang/backoff.go
generated
vendored
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (c) 2021 IBM Corp and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Matt Brittan
|
||||
* Daichi Tomaru
|
||||
*/
|
||||
|
||||
package mqtt
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Controller for sleep with backoff when the client attempts reconnection
|
||||
// It has statuses for each situations cause reconnection.
|
||||
type backoffController struct {
|
||||
sync.RWMutex
|
||||
statusMap map[string]*backoffStatus
|
||||
}
|
||||
|
||||
type backoffStatus struct {
|
||||
lastSleepPeriod time.Duration
|
||||
lastErrorTime time.Time
|
||||
}
|
||||
|
||||
func newBackoffController() *backoffController {
|
||||
return &backoffController{
|
||||
statusMap: map[string]*backoffStatus{},
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate next sleep period from the specified parameters.
|
||||
// Returned values are next sleep period and whether the error situation is continual.
|
||||
// If connection errors continuouslly occurs, its sleep period is exponentially increased.
|
||||
// Also if there is a lot of time between last and this error, sleep period is initialized.
|
||||
func (b *backoffController) getBackoffSleepTime(
|
||||
situation string, initSleepPeriod time.Duration, maxSleepPeriod time.Duration, processTime time.Duration, skipFirst bool,
|
||||
) (time.Duration, bool) {
|
||||
// Decide first sleep time if the situation is not continual.
|
||||
var firstProcess = func(status *backoffStatus, init time.Duration, skip bool) (time.Duration, bool) {
|
||||
if skip {
|
||||
status.lastSleepPeriod = 0
|
||||
return 0, false
|
||||
}
|
||||
status.lastSleepPeriod = init
|
||||
return init, false
|
||||
}
|
||||
|
||||
// Prioritize maxSleep.
|
||||
if initSleepPeriod > maxSleepPeriod {
|
||||
initSleepPeriod = maxSleepPeriod
|
||||
}
|
||||
b.Lock()
|
||||
defer b.Unlock()
|
||||
|
||||
status, exist := b.statusMap[situation]
|
||||
if !exist {
|
||||
b.statusMap[situation] = &backoffStatus{initSleepPeriod, time.Now()}
|
||||
return firstProcess(b.statusMap[situation], initSleepPeriod, skipFirst)
|
||||
}
|
||||
|
||||
oldTime := status.lastErrorTime
|
||||
status.lastErrorTime = time.Now()
|
||||
|
||||
// When there is a lot of time between last and this error, sleep period is initialized.
|
||||
if status.lastErrorTime.Sub(oldTime) > (processTime * 2 + status.lastSleepPeriod) {
|
||||
return firstProcess(status, initSleepPeriod, skipFirst)
|
||||
}
|
||||
|
||||
if status.lastSleepPeriod == 0 {
|
||||
status.lastSleepPeriod = initSleepPeriod
|
||||
return initSleepPeriod, true
|
||||
}
|
||||
|
||||
if nextSleepPeriod := status.lastSleepPeriod * 2; nextSleepPeriod <= maxSleepPeriod {
|
||||
status.lastSleepPeriod = nextSleepPeriod
|
||||
} else {
|
||||
status.lastSleepPeriod = maxSleepPeriod
|
||||
}
|
||||
|
||||
return status.lastSleepPeriod, true
|
||||
}
|
||||
|
||||
// Execute sleep the time returned from getBackoffSleepTime.
|
||||
func (b *backoffController) sleepWithBackoff(
|
||||
situation string, initSleepPeriod time.Duration, maxSleepPeriod time.Duration, processTime time.Duration, skipFirst bool,
|
||||
) (time.Duration, bool) {
|
||||
sleep, isFirst := b.getBackoffSleepTime(situation, initSleepPeriod, maxSleepPeriod, processTime, skipFirst)
|
||||
if sleep != 0 {
|
||||
time.Sleep(sleep)
|
||||
}
|
||||
return sleep, isFirst
|
||||
}
|
||||
77
vendor/github.com/eclipse/paho.mqtt.golang/client.go
generated
vendored
77
vendor/github.com/eclipse/paho.mqtt.golang/client.go
generated
vendored
@@ -141,6 +141,8 @@ type client struct {
|
||||
stop chan struct{} // Closed to request that workers stop
|
||||
workers sync.WaitGroup // used to wait for workers to complete (ping, keepalive, errwatch, resume)
|
||||
commsStopped chan struct{} // closed when the comms routines have stopped (kept running until after workers have closed to avoid deadlocks)
|
||||
|
||||
backoff *backoffController
|
||||
}
|
||||
|
||||
// NewClient will create an MQTT v3.1.1 client with all of the options specified
|
||||
@@ -169,6 +171,7 @@ func NewClient(o *ClientOptions) Client {
|
||||
c.msgRouter.setDefaultHandler(c.options.DefaultPublishHandler)
|
||||
c.obound = make(chan *PacketAndToken)
|
||||
c.oboundP = make(chan *PacketAndToken)
|
||||
c.backoff = newBackoffController()
|
||||
return c
|
||||
}
|
||||
|
||||
@@ -255,12 +258,15 @@ func (c *client) Connect() Token {
|
||||
return
|
||||
}
|
||||
|
||||
var attemptCount int
|
||||
|
||||
RETRYCONN:
|
||||
var conn net.Conn
|
||||
var rc byte
|
||||
var err error
|
||||
conn, rc, t.sessionPresent, err = c.attemptConnection()
|
||||
conn, rc, t.sessionPresent, err = c.attemptConnection(false, attemptCount)
|
||||
if err != nil {
|
||||
attemptCount++
|
||||
if c.options.ConnectRetry {
|
||||
DEBUG.Println(CLI, "Connect failed, sleeping for", int(c.options.ConnectRetryInterval.Seconds()), "seconds and will then retry, error:", err.Error())
|
||||
time.Sleep(c.options.ConnectRetryInterval)
|
||||
@@ -302,28 +308,29 @@ func (c *client) Connect() Token {
|
||||
func (c *client) reconnect(connectionUp connCompletedFn) {
|
||||
DEBUG.Println(CLI, "enter reconnect")
|
||||
var (
|
||||
sleep = 1 * time.Second
|
||||
conn net.Conn
|
||||
initSleep = 1 * time.Second
|
||||
conn net.Conn
|
||||
)
|
||||
|
||||
// If the reason of connection lost is same as the before one, sleep timer is set before attempting connection is started.
|
||||
// Sleep time is exponentially increased as the same situation continues
|
||||
if slp, isContinual := c.backoff.sleepWithBackoff("connectionLost", initSleep, c.options.MaxReconnectInterval, 3*time.Second, true); isContinual {
|
||||
DEBUG.Println(CLI, "Detect continual connection lost after reconnect, slept for", int(slp.Seconds()), "seconds")
|
||||
}
|
||||
|
||||
var attemptCount int
|
||||
for {
|
||||
if nil != c.options.OnReconnecting {
|
||||
c.options.OnReconnecting(c, &c.options)
|
||||
}
|
||||
var err error
|
||||
conn, _, _, err = c.attemptConnection()
|
||||
conn, _, _, err = c.attemptConnection(true, attemptCount)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
DEBUG.Println(CLI, "Reconnect failed, sleeping for", int(sleep.Seconds()), "seconds:", err)
|
||||
time.Sleep(sleep)
|
||||
if sleep < c.options.MaxReconnectInterval {
|
||||
sleep *= 2
|
||||
}
|
||||
|
||||
if sleep > c.options.MaxReconnectInterval {
|
||||
sleep = c.options.MaxReconnectInterval
|
||||
}
|
||||
attemptCount++
|
||||
sleep, _ := c.backoff.sleepWithBackoff("attemptReconnection", initSleep, c.options.MaxReconnectInterval, c.options.ConnectTimeout, false)
|
||||
DEBUG.Println(CLI, "Reconnect failed, slept for", int(sleep.Seconds()), "seconds:", err)
|
||||
|
||||
if c.status.ConnectionStatus() != reconnecting { // Disconnect may have been called
|
||||
if err := connectionUp(false); err != nil { // Should always return an error
|
||||
@@ -349,7 +356,7 @@ func (c *client) reconnect(connectionUp connCompletedFn) {
|
||||
// byte - Return code (packets.Accepted indicates a successful connection).
|
||||
// bool - SessionPresent flag from the connect ack (only valid if packets.Accepted)
|
||||
// err - Error (err != nil guarantees that conn has been set to active connection).
|
||||
func (c *client) attemptConnection() (net.Conn, byte, bool, error) {
|
||||
func (c *client) attemptConnection(isReconnect bool, attempt int) (net.Conn, byte, bool, error) {
|
||||
protocolVersion := c.options.ProtocolVersion
|
||||
var (
|
||||
sessionPresent bool
|
||||
@@ -358,6 +365,10 @@ func (c *client) attemptConnection() (net.Conn, byte, bool, error) {
|
||||
rc byte
|
||||
)
|
||||
|
||||
if c.options.OnConnectionNotification != nil {
|
||||
c.options.OnConnectionNotification(c, ConnectionNotificationConnecting{isReconnect, attempt})
|
||||
}
|
||||
|
||||
c.optionsMu.Lock() // Protect c.options.Servers so that servers can be added in test cases
|
||||
brokers := c.options.Servers
|
||||
c.optionsMu.Unlock()
|
||||
@@ -370,6 +381,9 @@ func (c *client) attemptConnection() (net.Conn, byte, bool, error) {
|
||||
DEBUG.Println(CLI, "using custom onConnectAttempt handler...")
|
||||
tlsCfg = c.options.OnConnectAttempt(broker, c.options.TLSConfig)
|
||||
}
|
||||
if c.options.OnConnectionNotification != nil {
|
||||
c.options.OnConnectionNotification(c, ConnectionNotificationBroker{broker})
|
||||
}
|
||||
connDeadline := time.Now().Add(c.options.ConnectTimeout) // Time by which connection must be established
|
||||
dialer := c.options.Dialer
|
||||
if dialer == nil { //
|
||||
@@ -386,6 +400,9 @@ func (c *client) attemptConnection() (net.Conn, byte, bool, error) {
|
||||
ERROR.Println(CLI, err.Error())
|
||||
WARN.Println(CLI, "failed to connect to broker, trying next")
|
||||
rc = packets.ErrNetworkError
|
||||
if c.options.OnConnectionNotification != nil {
|
||||
c.options.OnConnectionNotification(c, ConnectionNotificationBrokerFailed{broker, err})
|
||||
}
|
||||
continue
|
||||
}
|
||||
DEBUG.Println(CLI, "socket connected to broker")
|
||||
@@ -425,9 +442,12 @@ func (c *client) attemptConnection() (net.Conn, byte, bool, error) {
|
||||
if rc != packets.ErrNetworkError { // mqtt error
|
||||
err = packets.ConnErrors[rc]
|
||||
} else { // network error (if this occurred in ConnectMQTT then err will be nil)
|
||||
err = fmt.Errorf("%s : %s", packets.ConnErrors[rc], err)
|
||||
err = fmt.Errorf("%w : %w", packets.ConnErrors[rc], err)
|
||||
}
|
||||
}
|
||||
if err != nil && c.options.OnConnectionNotification != nil {
|
||||
c.options.OnConnectionNotification(c, ConnectionNotificationFailed{err})
|
||||
}
|
||||
return conn, rc, sessionPresent, err
|
||||
}
|
||||
|
||||
@@ -562,6 +582,9 @@ func (c *client) internalConnLost(whyConnLost error) {
|
||||
if c.options.OnConnectionLost != nil {
|
||||
go c.options.OnConnectionLost(c, whyConnLost)
|
||||
}
|
||||
if c.options.OnConnectionNotification != nil {
|
||||
go c.options.OnConnectionNotification(c, ConnectionNotificationLost{whyConnLost})
|
||||
}
|
||||
DEBUG.Println(CLI, "internalConnLost complete")
|
||||
}()
|
||||
}
|
||||
@@ -599,21 +622,21 @@ func (c *client) startCommsWorkers(conn net.Conn, connectionUp connCompletedFn,
|
||||
c.workers.Add(1) // Done will be called when ackOut is closed
|
||||
ackOut := c.msgRouter.matchAndDispatch(incomingPubChan, c.options.Order, c)
|
||||
|
||||
// The connection is now ready for use (we spin up a few go routines below). It is possible that
|
||||
// Disconnect has been called in the interim...
|
||||
// The connection is now ready for use (we spin up a few go routines below).
|
||||
// It is possible that Disconnect has been called in the interim...
|
||||
// issue 675:we will allow the connection to complete before the Disconnect is allowed to proceed
|
||||
// as if a Disconnect event occurred immediately after connectionUp(true) completed.
|
||||
if err := connectionUp(true); err != nil {
|
||||
DEBUG.Println(CLI, err)
|
||||
close(c.stop) // Tidy up anything we have already started
|
||||
close(incomingPubChan)
|
||||
c.workers.Wait()
|
||||
c.conn.Close()
|
||||
c.conn = nil
|
||||
return false
|
||||
ERROR.Println(CLI, err)
|
||||
}
|
||||
|
||||
DEBUG.Println(CLI, "client is connected/reconnected")
|
||||
if c.options.OnConnect != nil {
|
||||
go c.options.OnConnect(c)
|
||||
}
|
||||
if c.options.OnConnectionNotification != nil {
|
||||
go c.options.OnConnectionNotification(c, ConnectionNotificationConnected{})
|
||||
}
|
||||
|
||||
// c.oboundP and c.obound need to stay active for the life of the client because, depending upon the options,
|
||||
// messages may be published while the client is disconnected (they will block unless in a goroutine). However
|
||||
@@ -797,9 +820,13 @@ func (c *client) Publish(topic string, qos byte, retained bool, payload interfac
|
||||
if publishWaitTimeout == 0 {
|
||||
publishWaitTimeout = time.Second * 30
|
||||
}
|
||||
|
||||
t := time.NewTimer(publishWaitTimeout)
|
||||
defer t.Stop()
|
||||
|
||||
select {
|
||||
case c.obound <- &PacketAndToken{p: pub, t: token}:
|
||||
case <-time.After(publishWaitTimeout):
|
||||
case <-t.C:
|
||||
token.setError(errors.New("publish was broken by timeout"))
|
||||
}
|
||||
}
|
||||
|
||||
13
vendor/github.com/eclipse/paho.mqtt.golang/filestore.go
generated
vendored
13
vendor/github.com/eclipse/paho.mqtt.golang/filestore.go
generated
vendored
@@ -19,7 +19,7 @@
|
||||
package mqtt
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path"
|
||||
"sort"
|
||||
@@ -159,15 +159,20 @@ func (store *FileStore) Reset() {
|
||||
func (store *FileStore) all() []string {
|
||||
var err error
|
||||
var keys []string
|
||||
var files fileInfos
|
||||
|
||||
if !store.opened {
|
||||
ERROR.Println(STR, "trying to use file store, but not open")
|
||||
return nil
|
||||
}
|
||||
|
||||
files, err = ioutil.ReadDir(store.directory)
|
||||
entries, err := os.ReadDir(store.directory)
|
||||
chkerr(err)
|
||||
files := make(fileInfos, 0, len(entries))
|
||||
for _, entry := range entries {
|
||||
info, err := entry.Info()
|
||||
chkerr(err)
|
||||
files = append(files, info)
|
||||
}
|
||||
sort.Sort(files)
|
||||
for _, f := range files {
|
||||
DEBUG.Println(STR, "file in All():", f.Name())
|
||||
@@ -246,7 +251,7 @@ func exists(file string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
type fileInfos []os.FileInfo
|
||||
type fileInfos []fs.FileInfo
|
||||
|
||||
func (f fileInfos) Len() int {
|
||||
return len(f)
|
||||
|
||||
13
vendor/github.com/eclipse/paho.mqtt.golang/net.go
generated
vendored
13
vendor/github.com/eclipse/paho.mqtt.golang/net.go
generated
vendored
@@ -444,24 +444,23 @@ func startComms(conn net.Conn, // Network connection (must be active)
|
||||
}
|
||||
|
||||
// ackFunc acknowledges a packet
|
||||
// WARNING the function returned must not be called if the comms routine is shutting down or not running
|
||||
// (it needs outgoing comms in order to send the acknowledgement). Currently this is only called from
|
||||
// matchAndDispatch which will be shutdown before the comms are
|
||||
func ackFunc(oboundP chan *PacketAndToken, persist Store, packet *packets.PublishPacket) func() {
|
||||
// WARNING sendAck may be called at any time (even after the connection is dead). At the time of writing ACK sent after
|
||||
// connection loss will be dropped (this is not ideal)
|
||||
func ackFunc(sendAck func(*PacketAndToken), persist Store, packet *packets.PublishPacket) func() {
|
||||
return func() {
|
||||
switch packet.Qos {
|
||||
case 2:
|
||||
pr := packets.NewControlPacket(packets.Pubrec).(*packets.PubrecPacket)
|
||||
pr.MessageID = packet.MessageID
|
||||
DEBUG.Println(NET, "putting pubrec msg on obound")
|
||||
oboundP <- &PacketAndToken{p: pr, t: nil}
|
||||
sendAck(&PacketAndToken{p: pr, t: nil})
|
||||
DEBUG.Println(NET, "done putting pubrec msg on obound")
|
||||
case 1:
|
||||
pa := packets.NewControlPacket(packets.Puback).(*packets.PubackPacket)
|
||||
pa.MessageID = packet.MessageID
|
||||
DEBUG.Println(NET, "putting puback msg on obound")
|
||||
persistOutbound(persist, pa)
|
||||
oboundP <- &PacketAndToken{p: pa, t: nil}
|
||||
persistOutbound(persist, pa) // May fail if store has been closed
|
||||
sendAck(&PacketAndToken{p: pa, t: nil})
|
||||
DEBUG.Println(NET, "done putting puback msg on obound")
|
||||
case 0:
|
||||
// do nothing, since there is no need to send an ack packet back
|
||||
|
||||
19
vendor/github.com/eclipse/paho.mqtt.golang/netconn.go
generated
vendored
19
vendor/github.com/eclipse/paho.mqtt.golang/netconn.go
generated
vendored
@@ -40,22 +40,17 @@ import (
|
||||
func openConnection(uri *url.URL, tlsc *tls.Config, timeout time.Duration, headers http.Header, websocketOptions *WebsocketOptions, dialer *net.Dialer) (net.Conn, error) {
|
||||
switch uri.Scheme {
|
||||
case "ws":
|
||||
conn, err := NewWebsocket(uri.String(), nil, timeout, headers, websocketOptions)
|
||||
dialURI := *uri // #623 - Gorilla Websockets does not accept URL's where uri.User != nil
|
||||
dialURI.User = nil
|
||||
conn, err := NewWebsocket(dialURI.String(), nil, timeout, headers, websocketOptions)
|
||||
return conn, err
|
||||
case "wss":
|
||||
conn, err := NewWebsocket(uri.String(), tlsc, timeout, headers, websocketOptions)
|
||||
dialURI := *uri // #623 - Gorilla Websockets does not accept URL's where uri.User != nil
|
||||
dialURI.User = nil
|
||||
conn, err := NewWebsocket(dialURI.String(), tlsc, timeout, headers, websocketOptions)
|
||||
return conn, err
|
||||
case "mqtt", "tcp":
|
||||
allProxy := os.Getenv("all_proxy")
|
||||
if len(allProxy) == 0 {
|
||||
conn, err := dialer.Dial("tcp", uri.Host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
proxyDialer := proxy.FromEnvironment()
|
||||
|
||||
proxyDialer := proxy.FromEnvironmentUsing(dialer)
|
||||
conn, err := proxyDialer.Dial("tcp", uri.Host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
164
vendor/github.com/eclipse/paho.mqtt.golang/options.go
generated
vendored
164
vendor/github.com/eclipse/paho.mqtt.golang/options.go
generated
vendored
@@ -62,93 +62,99 @@ type ConnectionAttemptHandler func(broker *url.URL, tlsCfg *tls.Config) *tls.Con
|
||||
// Does not carry out any MQTT specific handshakes.
|
||||
type OpenConnectionFunc func(uri *url.URL, options ClientOptions) (net.Conn, error)
|
||||
|
||||
// ConnectionNotificationHandler is invoked for any type of connection event.
|
||||
type ConnectionNotificationHandler func(Client, ConnectionNotification)
|
||||
|
||||
// ClientOptions contains configurable options for an Client. Note that these should be set using the
|
||||
// relevant methods (e.g. AddBroker) rather than directly. See those functions for information on usage.
|
||||
// WARNING: Create the below using NewClientOptions unless you have a compelling reason not to. It is easy
|
||||
// to create a configuration with difficult to trace issues (e.g. Mosquitto 2.0.12+ will reject connections
|
||||
// with KeepAlive=0 by default).
|
||||
type ClientOptions struct {
|
||||
Servers []*url.URL
|
||||
ClientID string
|
||||
Username string
|
||||
Password string
|
||||
CredentialsProvider CredentialsProvider
|
||||
CleanSession bool
|
||||
Order bool
|
||||
WillEnabled bool
|
||||
WillTopic string
|
||||
WillPayload []byte
|
||||
WillQos byte
|
||||
WillRetained bool
|
||||
ProtocolVersion uint
|
||||
protocolVersionExplicit bool
|
||||
TLSConfig *tls.Config
|
||||
KeepAlive int64 // Warning: Some brokers may reject connections with Keepalive = 0.
|
||||
PingTimeout time.Duration
|
||||
ConnectTimeout time.Duration
|
||||
MaxReconnectInterval time.Duration
|
||||
AutoReconnect bool
|
||||
ConnectRetryInterval time.Duration
|
||||
ConnectRetry bool
|
||||
Store Store
|
||||
DefaultPublishHandler MessageHandler
|
||||
OnConnect OnConnectHandler
|
||||
OnConnectionLost ConnectionLostHandler
|
||||
OnReconnecting ReconnectHandler
|
||||
OnConnectAttempt ConnectionAttemptHandler
|
||||
WriteTimeout time.Duration
|
||||
MessageChannelDepth uint
|
||||
ResumeSubs bool
|
||||
HTTPHeaders http.Header
|
||||
WebsocketOptions *WebsocketOptions
|
||||
MaxResumePubInFlight int // // 0 = no limit; otherwise this is the maximum simultaneous messages sent while resuming
|
||||
Dialer *net.Dialer
|
||||
CustomOpenConnectionFn OpenConnectionFunc
|
||||
AutoAckDisabled bool
|
||||
Servers []*url.URL
|
||||
ClientID string
|
||||
Username string
|
||||
Password string
|
||||
CredentialsProvider CredentialsProvider
|
||||
CleanSession bool
|
||||
Order bool
|
||||
WillEnabled bool
|
||||
WillTopic string
|
||||
WillPayload []byte
|
||||
WillQos byte
|
||||
WillRetained bool
|
||||
ProtocolVersion uint
|
||||
protocolVersionExplicit bool
|
||||
TLSConfig *tls.Config
|
||||
KeepAlive int64 // Warning: Some brokers may reject connections with Keepalive = 0.
|
||||
PingTimeout time.Duration
|
||||
ConnectTimeout time.Duration
|
||||
MaxReconnectInterval time.Duration
|
||||
AutoReconnect bool
|
||||
ConnectRetryInterval time.Duration
|
||||
ConnectRetry bool
|
||||
Store Store
|
||||
DefaultPublishHandler MessageHandler
|
||||
OnConnect OnConnectHandler
|
||||
OnConnectionLost ConnectionLostHandler
|
||||
OnReconnecting ReconnectHandler
|
||||
OnConnectAttempt ConnectionAttemptHandler
|
||||
OnConnectionNotification ConnectionNotificationHandler
|
||||
WriteTimeout time.Duration
|
||||
MessageChannelDepth uint
|
||||
ResumeSubs bool
|
||||
HTTPHeaders http.Header
|
||||
WebsocketOptions *WebsocketOptions
|
||||
MaxResumePubInFlight int // // 0 = no limit; otherwise this is the maximum simultaneous messages sent while resuming
|
||||
Dialer *net.Dialer
|
||||
CustomOpenConnectionFn OpenConnectionFunc
|
||||
AutoAckDisabled bool
|
||||
}
|
||||
|
||||
// NewClientOptions will create a new ClientClientOptions type with some
|
||||
// default values.
|
||||
// Port: 1883
|
||||
// CleanSession: True
|
||||
// Order: True (note: it is recommended that this be set to FALSE unless order is important)
|
||||
// KeepAlive: 30 (seconds)
|
||||
// ConnectTimeout: 30 (seconds)
|
||||
// MaxReconnectInterval 10 (minutes)
|
||||
// AutoReconnect: True
|
||||
//
|
||||
// Port: 1883
|
||||
// CleanSession: True
|
||||
// Order: True (note: it is recommended that this be set to FALSE unless order is important)
|
||||
// KeepAlive: 30 (seconds)
|
||||
// ConnectTimeout: 30 (seconds)
|
||||
// MaxReconnectInterval 10 (minutes)
|
||||
// AutoReconnect: True
|
||||
func NewClientOptions() *ClientOptions {
|
||||
o := &ClientOptions{
|
||||
Servers: nil,
|
||||
ClientID: "",
|
||||
Username: "",
|
||||
Password: "",
|
||||
CleanSession: true,
|
||||
Order: true,
|
||||
WillEnabled: false,
|
||||
WillTopic: "",
|
||||
WillPayload: nil,
|
||||
WillQos: 0,
|
||||
WillRetained: false,
|
||||
ProtocolVersion: 0,
|
||||
protocolVersionExplicit: false,
|
||||
KeepAlive: 30,
|
||||
PingTimeout: 10 * time.Second,
|
||||
ConnectTimeout: 30 * time.Second,
|
||||
MaxReconnectInterval: 10 * time.Minute,
|
||||
AutoReconnect: true,
|
||||
ConnectRetryInterval: 30 * time.Second,
|
||||
ConnectRetry: false,
|
||||
Store: nil,
|
||||
OnConnect: nil,
|
||||
OnConnectionLost: DefaultConnectionLostHandler,
|
||||
OnConnectAttempt: nil,
|
||||
WriteTimeout: 0, // 0 represents timeout disabled
|
||||
ResumeSubs: false,
|
||||
HTTPHeaders: make(map[string][]string),
|
||||
WebsocketOptions: &WebsocketOptions{},
|
||||
Dialer: &net.Dialer{Timeout: 30 * time.Second},
|
||||
CustomOpenConnectionFn: nil,
|
||||
AutoAckDisabled: false,
|
||||
Servers: nil,
|
||||
ClientID: "",
|
||||
Username: "",
|
||||
Password: "",
|
||||
CleanSession: true,
|
||||
Order: true,
|
||||
WillEnabled: false,
|
||||
WillTopic: "",
|
||||
WillPayload: nil,
|
||||
WillQos: 0,
|
||||
WillRetained: false,
|
||||
ProtocolVersion: 0,
|
||||
protocolVersionExplicit: false,
|
||||
KeepAlive: 30,
|
||||
PingTimeout: 10 * time.Second,
|
||||
ConnectTimeout: 30 * time.Second,
|
||||
MaxReconnectInterval: 10 * time.Minute,
|
||||
AutoReconnect: true,
|
||||
ConnectRetryInterval: 30 * time.Second,
|
||||
ConnectRetry: false,
|
||||
Store: nil,
|
||||
OnConnect: nil,
|
||||
OnConnectionLost: DefaultConnectionLostHandler,
|
||||
OnConnectAttempt: nil,
|
||||
OnConnectionNotification: nil,
|
||||
WriteTimeout: 0, // 0 represents timeout disabled
|
||||
ResumeSubs: false,
|
||||
HTTPHeaders: make(map[string][]string),
|
||||
WebsocketOptions: &WebsocketOptions{},
|
||||
Dialer: &net.Dialer{Timeout: 30 * time.Second},
|
||||
CustomOpenConnectionFn: nil,
|
||||
AutoAckDisabled: false,
|
||||
}
|
||||
return o
|
||||
}
|
||||
@@ -355,6 +361,13 @@ func (o *ClientOptions) SetConnectionAttemptHandler(onConnectAttempt ConnectionA
|
||||
return o
|
||||
}
|
||||
|
||||
// SetConnectionNotificationHandler sets the ConnectionNotificationHandler callback to receive all types of connection
|
||||
// events.
|
||||
func (o *ClientOptions) SetConnectionNotificationHandler(onConnectionNotification ConnectionNotificationHandler) *ClientOptions {
|
||||
o.OnConnectionNotification = onConnectionNotification
|
||||
return o
|
||||
}
|
||||
|
||||
// SetWriteTimeout puts a limit on how long a mqtt publish should block until it unblocks with a
|
||||
// timeout error. A duration of 0 never times out. Default never times out
|
||||
func (o *ClientOptions) SetWriteTimeout(t time.Duration) *ClientOptions {
|
||||
@@ -450,6 +463,7 @@ func (o *ClientOptions) SetCustomOpenConnectionFn(customOpenConnectionFn OpenCon
|
||||
}
|
||||
|
||||
// SetAutoAckDisabled enables or disables the Automated Acking of Messages received by the handler.
|
||||
//
|
||||
// By default it is set to false. Setting it to true will disable the auto-ack globally.
|
||||
func (o *ClientOptions) SetAutoAckDisabled(autoAckDisabled bool) *ClientOptions {
|
||||
o.AutoAckDisabled = autoAckDisabled
|
||||
|
||||
15
vendor/github.com/eclipse/paho.mqtt.golang/options_reader.go
generated
vendored
15
vendor/github.com/eclipse/paho.mqtt.golang/options_reader.go
generated
vendored
@@ -30,6 +30,21 @@ type ClientOptionsReader struct {
|
||||
options *ClientOptions
|
||||
}
|
||||
|
||||
// NewOptionsReader creates a ClientOptionsReader, this should only be used for mocking purposes.
|
||||
//
|
||||
// An example implementation:
|
||||
//
|
||||
// func (c *mqttClientMock) OptionsReader() mqtt.ClientOptionsReader {
|
||||
// opts := mqtt.NewClientOptions()
|
||||
// opts.UserName = "TestUserName"
|
||||
// return mqtt.NewOptionsReader(opts)
|
||||
// }
|
||||
func NewOptionsReader(o *ClientOptions) ClientOptionsReader {
|
||||
return ClientOptionsReader{
|
||||
options: o,
|
||||
}
|
||||
}
|
||||
|
||||
// Servers returns a slice of the servers defined in the clientoptions
|
||||
func (r *ClientOptionsReader) Servers() []*url.URL {
|
||||
s := make([]*url.URL, len(r.options.Servers))
|
||||
|
||||
5
vendor/github.com/eclipse/paho.mqtt.golang/packets/packets.go
generated
vendored
5
vendor/github.com/eclipse/paho.mqtt.golang/packets/packets.go
generated
vendored
@@ -330,6 +330,11 @@ func decodeBytes(b io.Reader) ([]byte, error) {
|
||||
}
|
||||
|
||||
func encodeBytes(field []byte) []byte {
|
||||
// Attempting to encode more than 65,535 bytes would lead to an unexpected 16-bit length and extra data written
|
||||
// (which would be parsed as later parts of the message). The safest option is to truncate.
|
||||
if len(field) > 65535 {
|
||||
field = field[0:65535]
|
||||
}
|
||||
fieldLength := make([]byte, 2)
|
||||
binary.BigEndian.PutUint16(fieldLength, uint16(len(field)))
|
||||
return append(fieldLength, field...)
|
||||
|
||||
8
vendor/github.com/eclipse/paho.mqtt.golang/ping.go
generated
vendored
8
vendor/github.com/eclipse/paho.mqtt.golang/ping.go
generated
vendored
@@ -32,16 +32,16 @@ import (
|
||||
func keepalive(c *client, conn io.Writer) {
|
||||
defer c.workers.Done()
|
||||
DEBUG.Println(PNG, "keepalive starting")
|
||||
var checkInterval int64
|
||||
var checkInterval time.Duration
|
||||
var pingSent time.Time
|
||||
|
||||
if c.options.KeepAlive > 10 {
|
||||
checkInterval = 5
|
||||
checkInterval = 5 * time.Second
|
||||
} else {
|
||||
checkInterval = c.options.KeepAlive / 2
|
||||
checkInterval = time.Duration(c.options.KeepAlive) * time.Second / 4
|
||||
}
|
||||
|
||||
intervalTicker := time.NewTicker(time.Duration(checkInterval * int64(time.Second)))
|
||||
intervalTicker := time.NewTicker(checkInterval)
|
||||
defer intervalTicker.Stop()
|
||||
|
||||
for {
|
||||
|
||||
81
vendor/github.com/eclipse/paho.mqtt.golang/router.go
generated
vendored
81
vendor/github.com/eclipse/paho.mqtt.golang/router.go
generated
vendored
@@ -136,60 +136,41 @@ func (r *router) setDefaultHandler(handler MessageHandler) {
|
||||
// associated callback (or the defaultHandler, if one exists and no other route matched). If
|
||||
// anything is sent down the stop channel the function will end.
|
||||
func (r *router) matchAndDispatch(messages <-chan *packets.PublishPacket, order bool, client *client) <-chan *PacketAndToken {
|
||||
var wg sync.WaitGroup
|
||||
ackOutChan := make(chan *PacketAndToken) // Channel returned to caller; closed when messages channel closed
|
||||
var ackInChan chan *PacketAndToken // ACKs generated by ackFunc get put onto this channel
|
||||
ackChan := make(chan *PacketAndToken) // Channel returned to caller; closed when goroutine terminates
|
||||
|
||||
stopAckCopy := make(chan struct{}) // Closure requests stop of go routine copying ackInChan to ackOutChan
|
||||
ackCopyStopped := make(chan struct{}) // Closure indicates that it is safe to close ackOutChan
|
||||
goRoutinesDone := make(chan struct{}) // closed on wg.Done()
|
||||
if order {
|
||||
ackInChan = ackOutChan // When order = true no go routines are used so safe to use one channel and close when done
|
||||
} else {
|
||||
// When order = false ACK messages are sent in go routines so ackInChan cannot be closed until all goroutines done
|
||||
ackInChan = make(chan *PacketAndToken)
|
||||
go func() { // go routine to copy from ackInChan to ackOutChan until stopped
|
||||
for {
|
||||
select {
|
||||
case a := <-ackInChan:
|
||||
ackOutChan <- a
|
||||
case <-stopAckCopy:
|
||||
close(ackCopyStopped) // Signal main go routine that it is safe to close ackOutChan
|
||||
for {
|
||||
select {
|
||||
case <-ackInChan: // drain ackInChan to ensure all goRoutines can complete cleanly (ACK dropped)
|
||||
DEBUG.Println(ROU, "matchAndDispatch received acknowledgment after processing stopped (ACK dropped).")
|
||||
case <-goRoutinesDone:
|
||||
close(ackInChan) // Nothing further should be sent (a panic is probably better than silent failure)
|
||||
DEBUG.Println(ROU, "matchAndDispatch order=false copy goroutine exiting.")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
// In some cases message acknowledgments may come through after shutdown (connection is down etc). Where this is the
|
||||
// case we need to accept any such requests and then ignore them. Note that this is not a perfect solution, if we
|
||||
// have reconnected, and the session is still live, then the Ack really should be sent (see Issus #726)
|
||||
var ackMutex sync.RWMutex
|
||||
sendAckChan := ackChan // This will be set to nil before ackChan is closed
|
||||
sendAck := func(ack *PacketAndToken) {
|
||||
ackMutex.RLock()
|
||||
defer ackMutex.RUnlock()
|
||||
if sendAckChan != nil {
|
||||
sendAckChan <- ack
|
||||
} else {
|
||||
DEBUG.Println(ROU, "matchAndDispatch received acknowledgment after processing stopped (ACK dropped).")
|
||||
}
|
||||
}
|
||||
|
||||
go func() { // Main go routine handling inbound messages
|
||||
var handlers []MessageHandler
|
||||
for message := range messages {
|
||||
// DEBUG.Println(ROU, "matchAndDispatch received message")
|
||||
sent := false
|
||||
r.RLock()
|
||||
m := messageFromPublish(message, ackFunc(ackInChan, client.persist, message))
|
||||
var handlers []MessageHandler
|
||||
m := messageFromPublish(message, ackFunc(sendAck, client.persist, message))
|
||||
for e := r.routes.Front(); e != nil; e = e.Next() {
|
||||
if e.Value.(*route).match(message.TopicName) {
|
||||
if order {
|
||||
handlers = append(handlers, e.Value.(*route).callback)
|
||||
} else {
|
||||
hd := e.Value.(*route).callback
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
hd(client, m)
|
||||
if !client.options.AutoAckDisabled {
|
||||
m.Ack()
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
sent = true
|
||||
@@ -200,13 +181,11 @@ func (r *router) matchAndDispatch(messages <-chan *packets.PublishPacket, order
|
||||
if order {
|
||||
handlers = append(handlers, r.defaultHandler)
|
||||
} else {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
r.defaultHandler(client, m)
|
||||
if !client.options.AutoAckDisabled {
|
||||
m.Ack()
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
} else {
|
||||
@@ -214,26 +193,22 @@ func (r *router) matchAndDispatch(messages <-chan *packets.PublishPacket, order
|
||||
}
|
||||
}
|
||||
r.RUnlock()
|
||||
for _, handler := range handlers {
|
||||
handler(client, m)
|
||||
if !client.options.AutoAckDisabled {
|
||||
m.Ack()
|
||||
if order {
|
||||
for _, handler := range handlers {
|
||||
handler(client, m)
|
||||
if !client.options.AutoAckDisabled {
|
||||
m.Ack()
|
||||
}
|
||||
}
|
||||
handlers = handlers[:0]
|
||||
}
|
||||
// DEBUG.Println(ROU, "matchAndDispatch handled message")
|
||||
}
|
||||
if order {
|
||||
close(ackOutChan)
|
||||
} else { // Ensure that nothing further will be written to ackOutChan before closing it
|
||||
close(stopAckCopy)
|
||||
<-ackCopyStopped
|
||||
close(ackOutChan)
|
||||
go func() {
|
||||
wg.Wait() // Note: If this remains running then the user has handlers that are not returning
|
||||
close(goRoutinesDone)
|
||||
}()
|
||||
}
|
||||
ackMutex.Lock()
|
||||
sendAckChan = nil
|
||||
ackMutex.Unlock()
|
||||
close(ackChan) // as sendAckChan is now nil nothing further will be sent on this
|
||||
DEBUG.Println(ROU, "matchAndDispatch exiting")
|
||||
}()
|
||||
return ackOutChan
|
||||
return ackChan
|
||||
}
|
||||
|
||||
18
vendor/github.com/eclipse/paho.mqtt.golang/token.go
generated
vendored
18
vendor/github.com/eclipse/paho.mqtt.golang/token.go
generated
vendored
@@ -17,6 +17,7 @@
|
||||
package mqtt
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -202,3 +203,20 @@ type UnsubscribeToken struct {
|
||||
type DisconnectToken struct {
|
||||
baseToken
|
||||
}
|
||||
|
||||
// TimedOut is the error returned by WaitTimeout when the timeout expires
|
||||
var TimedOut = errors.New("context canceled")
|
||||
|
||||
// WaitTokenTimeout is a utility function used to simplify the use of token.WaitTimeout
|
||||
// token.WaitTimeout may return `false` due to time out but t.Error() still results
|
||||
// in nil.
|
||||
// `if t := client.X(); t.WaitTimeout(time.Second) && t.Error() != nil {` may evaluate
|
||||
// to false even if the operation fails.
|
||||
// It is important to note that if TimedOut is returned, then the operation may still be running
|
||||
// and could eventually complete successfully.
|
||||
func WaitTokenTimeout(t Token, d time.Duration) error {
|
||||
if !t.WaitTimeout(d) {
|
||||
return TimedOut
|
||||
}
|
||||
return t.Error()
|
||||
}
|
||||
|
||||
3
vendor/github.com/golang/protobuf/AUTHORS
generated
vendored
3
vendor/github.com/golang/protobuf/AUTHORS
generated
vendored
@@ -1,3 +0,0 @@
|
||||
# This source code refers to The Go Authors for copyright purposes.
|
||||
# The master list of authors is in the main Go distribution,
|
||||
# visible at http://tip.golang.org/AUTHORS.
|
||||
3
vendor/github.com/golang/protobuf/CONTRIBUTORS
generated
vendored
3
vendor/github.com/golang/protobuf/CONTRIBUTORS
generated
vendored
@@ -1,3 +0,0 @@
|
||||
# This source code was written by the Go contributors.
|
||||
# The master list of contributors is in the main Go distribution,
|
||||
# visible at http://tip.golang.org/CONTRIBUTORS.
|
||||
28
vendor/github.com/golang/protobuf/LICENSE
generated
vendored
28
vendor/github.com/golang/protobuf/LICENSE
generated
vendored
@@ -1,28 +0,0 @@
|
||||
Copyright 2010 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* 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.
|
||||
|
||||
324
vendor/github.com/golang/protobuf/proto/buffer.go
generated
vendored
324
vendor/github.com/golang/protobuf/proto/buffer.go
generated
vendored
@@ -1,324 +0,0 @@
|
||||
// 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.
|
||||
// It does not reset m before unmarshaling.
|
||||
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 before unmarshaling.
|
||||
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 before unmarshaling.
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
63
vendor/github.com/golang/protobuf/proto/defaults.go
generated
vendored
63
vendor/github.com/golang/protobuf/proto/defaults.go
generated
vendored
@@ -1,63 +0,0 @@
|
||||
// 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
|
||||
})
|
||||
}
|
||||
113
vendor/github.com/golang/protobuf/proto/deprecated.go
generated
vendored
113
vendor/github.com/golang/protobuf/proto/deprecated.go
generated
vendored
@@ -1,113 +0,0 @@
|
||||
// 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 (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
protoV2 "google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
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.
|
||||
func GetStats() Stats { return Stats{} }
|
||||
|
||||
// Deprecated: Do not use.
|
||||
func MarshalMessageSet(interface{}) ([]byte, error) {
|
||||
return nil, errors.New("proto: not implemented")
|
||||
}
|
||||
|
||||
// Deprecated: Do not use.
|
||||
func UnmarshalMessageSet([]byte, interface{}) error {
|
||||
return errors.New("proto: not implemented")
|
||||
}
|
||||
|
||||
// Deprecated: Do not use.
|
||||
func MarshalMessageSetJSON(interface{}) ([]byte, error) {
|
||||
return nil, errors.New("proto: not implemented")
|
||||
}
|
||||
|
||||
// Deprecated: Do not use.
|
||||
func UnmarshalMessageSetJSON([]byte, interface{}) error {
|
||||
return errors.New("proto: not implemented")
|
||||
}
|
||||
|
||||
// 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; this type existed for intenal-use only.
|
||||
type InternalMessageInfo struct{}
|
||||
|
||||
// Deprecated: Do not use; this method existed for intenal-use only.
|
||||
func (*InternalMessageInfo) DiscardUnknown(m Message) {
|
||||
DiscardUnknown(m)
|
||||
}
|
||||
|
||||
// Deprecated: Do not use; this method existed for intenal-use only.
|
||||
func (*InternalMessageInfo) Marshal(b []byte, m Message, deterministic bool) ([]byte, error) {
|
||||
return protoV2.MarshalOptions{Deterministic: deterministic}.MarshalAppend(b, MessageV2(m))
|
||||
}
|
||||
|
||||
// Deprecated: Do not use; this method existed for intenal-use only.
|
||||
func (*InternalMessageInfo) Merge(dst, src Message) {
|
||||
protoV2.Merge(MessageV2(dst), MessageV2(src))
|
||||
}
|
||||
|
||||
// Deprecated: Do not use; this method existed for intenal-use only.
|
||||
func (*InternalMessageInfo) Size(m Message) int {
|
||||
return protoV2.Size(MessageV2(m))
|
||||
}
|
||||
|
||||
// Deprecated: Do not use; this method existed for intenal-use only.
|
||||
func (*InternalMessageInfo) Unmarshal(m Message, b []byte) error {
|
||||
return protoV2.UnmarshalOptions{Merge: true}.Unmarshal(b, MessageV2(m))
|
||||
}
|
||||
58
vendor/github.com/golang/protobuf/proto/discard.go
generated
vendored
58
vendor/github.com/golang/protobuf/proto/discard.go
generated
vendored
@@ -1,58 +0,0 @@
|
||||
// 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"
|
||||
)
|
||||
|
||||
// DiscardUnknown recursively discards all unknown fields from this message
|
||||
// and all embedded messages.
|
||||
//
|
||||
// When unmarshaling a message with unrecognized fields, the tags and values
|
||||
// of such fields are preserved in the Message. This allows a later call to
|
||||
// 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.
|
||||
func DiscardUnknown(m Message) {
|
||||
if m != nil {
|
||||
discardUnknown(MessageReflect(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())
|
||||
}
|
||||
// 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())
|
||||
}
|
||||
}
|
||||
// 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
|
||||
})
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
// Discard unknown fields.
|
||||
if len(m.GetUnknown()) > 0 {
|
||||
m.SetUnknown(nil)
|
||||
}
|
||||
}
|
||||
356
vendor/github.com/golang/protobuf/proto/extensions.go
generated
vendored
356
vendor/github.com/golang/protobuf/proto/extensions.go
generated
vendored
@@ -1,356 +0,0 @@
|
||||
// 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 (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
// ExtensionRange represents a range of message extensions.
|
||||
// Used in code generated by protoc-gen-go.
|
||||
ExtensionRange = protoiface.ExtensionRangeV1
|
||||
|
||||
// Deprecated: Do not use; this is an internal type.
|
||||
Extension = protoimpl.ExtensionFieldV1
|
||||
|
||||
// Deprecated: Do not use; this is an internal type.
|
||||
XXX_InternalExtensions = protoimpl.ExtensionFields
|
||||
)
|
||||
|
||||
// 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")
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
})
|
||||
}
|
||||
|
||||
// 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 has
|
||||
}
|
||||
|
||||
// ClearExtension removes the extension 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
|
||||
}
|
||||
|
||||
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
|
||||
})
|
||||
}
|
||||
clearUnknown(mr, fieldNum(xt.Field))
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
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 m.
|
||||
//
|
||||
// If the descriptor is type complete (i.e., ExtensionDesc.ExtensionType is non-nil),
|
||||
// then GetExtension parses the encoded field and returns a Go value of the specified type.
|
||||
// If the field is not present, then the default value is returned (if one is specified),
|
||||
// otherwise ErrMissingExtension is reported.
|
||||
//
|
||||
// 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:]
|
||||
}
|
||||
|
||||
// For type incomplete descriptors, only retrieve the unknown fields.
|
||||
if xt.ExtensionType == nil {
|
||||
return []byte(bo), 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)
|
||||
}
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// 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 }
|
||||
|
||||
func (r extensionResolver) FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error) {
|
||||
if xtd := r.xt.TypeDescriptor(); xtd.FullName() == field {
|
||||
return r.xt, nil
|
||||
}
|
||||
return protoregistry.GlobalTypes.FindExtensionByName(field)
|
||||
}
|
||||
|
||||
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 protoregistry.GlobalTypes.FindExtensionByNumber(message, field)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
vs := make([]interface{}, len(xts))
|
||||
for i, xt := range xts {
|
||||
v, err := GetExtension(m, xt)
|
||||
if err != nil {
|
||||
if err == ErrMissingExtension {
|
||||
continue
|
||||
}
|
||||
return vs, err
|
||||
}
|
||||
vs[i] = v
|
||||
}
|
||||
return vs, nil
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
xtd := xt.TypeDescriptor()
|
||||
if !isValidExtension(mr.Descriptor(), xtd) {
|
||||
return fmt.Errorf("proto: bad extended type; %T does not extend %T", xt.ExtendedType, m)
|
||||
}
|
||||
mr.Set(xtd, xt.ValueOf(v))
|
||||
clearUnknown(mr, fieldNum(xt.Field))
|
||||
return 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
|
||||
}
|
||||
|
||||
// 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:]
|
||||
}
|
||||
|
||||
ClearExtension(m, &ExtensionDesc{Field: fnum})
|
||||
mr.SetUnknown(append(mr.GetUnknown(), b...))
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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:]
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
return xts, nil
|
||||
}
|
||||
|
||||
// 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())
|
||||
}
|
||||
|
||||
// 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:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// 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:]
|
||||
}
|
||||
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
|
||||
}
|
||||
306
vendor/github.com/golang/protobuf/proto/properties.go
generated
vendored
306
vendor/github.com/golang/protobuf/proto/properties.go
generated
vendored
@@ -1,306 +0,0 @@
|
||||
// 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 (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
"google.golang.org/protobuf/runtime/protoimpl"
|
||||
)
|
||||
|
||||
// StructProperties represents protocol buffer type information for a
|
||||
// generated protobuf message in the open-struct API.
|
||||
//
|
||||
// Deprecated: Do not use.
|
||||
type StructProperties struct {
|
||||
// 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 protobuf field name.
|
||||
OneofTypes map[string]*OneofProperties
|
||||
}
|
||||
|
||||
// Properties represents the type information for a protobuf message field.
|
||||
//
|
||||
// Deprecated: Do not use.
|
||||
type Properties struct {
|
||||
// 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 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 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
|
||||
}
|
||||
|
||||
// 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 += "," + strconv.Itoa(p.Tag)
|
||||
if p.Required {
|
||||
s += ",req"
|
||||
}
|
||||
if p.Optional {
|
||||
s += ",opt"
|
||||
}
|
||||
if p.Repeated {
|
||||
s += ",rep"
|
||||
}
|
||||
if p.Packed {
|
||||
s += ",packed"
|
||||
}
|
||||
s += ",name=" + p.OrigName
|
||||
if p.JSONName != "" {
|
||||
s += ",json=" + p.JSONName
|
||||
}
|
||||
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 {
|
||||
s += ",oneof"
|
||||
}
|
||||
if p.HasDefault {
|
||||
s += ",def=" + p.Default
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Parse populates p by parsing a string in the protobuf struct field tag style.
|
||||
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 s == "req":
|
||||
p.Required = true
|
||||
case s == "rep":
|
||||
p.Repeated = true
|
||||
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 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, i = tag[len("def="):], len(tag)
|
||||
}
|
||||
tag = strings.TrimPrefix(tag[i:], ",")
|
||||
}
|
||||
}
|
||||
|
||||
// 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.Name = name
|
||||
p.OrigName = name
|
||||
if tag == "" {
|
||||
return
|
||||
}
|
||||
p.Parse(tag)
|
||||
|
||||
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 propertiesCache sync.Map // map[reflect.Type]*StructProperties
|
||||
|
||||
// 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 p, ok := propertiesCache.Load(t); ok {
|
||||
return p.(*StructProperties)
|
||||
}
|
||||
p, _ := propertiesCache.LoadOrStore(t, newProperties(t))
|
||||
return p.(*StructProperties)
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
// Construct a list of properties for each field in the struct.
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
p := new(Properties)
|
||||
f := t.Field(i)
|
||||
tagField := f.Tag.Get("protobuf")
|
||||
p.Init(f.Type, f.Name, tagField, &f)
|
||||
|
||||
tagOneof := f.Tag.Get("protobuf_oneof")
|
||||
if tagOneof != "" {
|
||||
hasOneof = true
|
||||
p.OrigName = tagOneof
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
prop.OneofTypes = make(map[string]*OneofProperties)
|
||||
for _, wrapper := range oneofWrappers {
|
||||
p := &OneofProperties{
|
||||
Type: reflect.ValueOf(wrapper).Type(), // *T
|
||||
Prop: new(Properties),
|
||||
}
|
||||
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 !foundOneof {
|
||||
panic(fmt.Sprintf("%v is not a generated message in the open-struct API", t))
|
||||
}
|
||||
prop.OneofTypes[p.Prop.OrigName] = p
|
||||
}
|
||||
}
|
||||
|
||||
return prop
|
||||
}
|
||||
|
||||
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 }
|
||||
167
vendor/github.com/golang/protobuf/proto/proto.go
generated
vendored
167
vendor/github.com/golang/protobuf/proto/proto.go
generated
vendored
@@ -1,167 +0,0 @@
|
||||
// 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()
|
||||
}
|
||||
317
vendor/github.com/golang/protobuf/proto/registry.go
generated
vendored
317
vendor/github.com/golang/protobuf/proto/registry.go
generated
vendored
@@ -1,317 +0,0 @@
|
||||
// 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/protodesc"
|
||||
"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.RegisterFile 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.FindFileByPath 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 {
|
||||
b, _ = Marshal(protodesc.ToFileDescriptorProto(fd))
|
||||
}
|
||||
|
||||
// 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.RegisterEnum 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.RegisterMessage 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.RegisterExtension 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
|
||||
}
|
||||
801
vendor/github.com/golang/protobuf/proto/text_decode.go
generated
vendored
801
vendor/github.com/golang/protobuf/proto/text_decode.go
generated
vendored
@@ -1,801 +0,0 @@
|
||||
// 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(rune(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
|
||||
}
|
||||
560
vendor/github.com/golang/protobuf/proto/text_encode.go
generated
vendored
560
vendor/github.com/golang/protobuf/proto/text_encode.go
generated
vendored
@@ -1,560 +0,0 @@
|
||||
// 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, m Message) error { return defaultTextMarshaler.Marshal(w, m) }
|
||||
|
||||
// MarshalTextString returns a proto text formatted string of m.
|
||||
func MarshalTextString(m Message) string { return defaultTextMarshaler.Text(m) }
|
||||
|
||||
// CompactText writes the compact proto text format of m to w.
|
||||
func CompactText(w io.Writer, m Message) error { return compactTextMarshaler.Marshal(w, m) }
|
||||
|
||||
// CompactTextString returns a compact proto text formatted string of m.
|
||||
func CompactTextString(m Message) string { return compactTextMarshaler.Text(m) }
|
||||
|
||||
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
|
||||
}
|
||||
78
vendor/github.com/golang/protobuf/proto/wire.go
generated
vendored
78
vendor/github.com/golang/protobuf/proto/wire.go
generated
vendored
@@ -1,78 +0,0 @@
|
||||
// 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)
|
||||
}
|
||||
34
vendor/github.com/golang/protobuf/proto/wrappers.go
generated
vendored
34
vendor/github.com/golang/protobuf/proto/wrappers.go
generated
vendored
@@ -1,34 +0,0 @@
|
||||
// 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 }
|
||||
64
vendor/github.com/golang/protobuf/ptypes/timestamp/timestamp.pb.go
generated
vendored
64
vendor/github.com/golang/protobuf/ptypes/timestamp/timestamp.pb.go
generated
vendored
@@ -1,64 +0,0 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: github.com/golang/protobuf/ptypes/timestamp/timestamp.proto
|
||||
|
||||
package timestamp
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
|
||||
reflect "reflect"
|
||||
)
|
||||
|
||||
// Symbols defined in public import of google/protobuf/timestamp.proto.
|
||||
|
||||
type Timestamp = timestamppb.Timestamp
|
||||
|
||||
var File_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_rawDesc = []byte{
|
||||
0x0a, 0x3b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6c,
|
||||
0x61, 0x6e, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x70, 0x74, 0x79,
|
||||
0x70, 0x65, 0x73, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2f, 0x74, 0x69,
|
||||
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67,
|
||||
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74,
|
||||
0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x42, 0x37,
|
||||
0x5a, 0x35, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6c,
|
||||
0x61, 0x6e, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x70, 0x74, 0x79,
|
||||
0x70, 0x65, 0x73, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x3b, 0x74, 0x69,
|
||||
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x50, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x33,
|
||||
}
|
||||
|
||||
var file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_goTypes = []interface{}{}
|
||||
var file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_depIdxs = []int32{
|
||||
0, // [0:0] is the sub-list for method output_type
|
||||
0, // [0:0] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_init() }
|
||||
func file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_init() {
|
||||
if File_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto != nil {
|
||||
return
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 0,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_goTypes,
|
||||
DependencyIndexes: file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_depIdxs,
|
||||
}.Build()
|
||||
File_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto = out.File
|
||||
file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_rawDesc = nil
|
||||
file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_goTypes = nil
|
||||
file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_depIdxs = nil
|
||||
}
|
||||
9
vendor/github.com/gorilla/websocket/AUTHORS
generated
vendored
9
vendor/github.com/gorilla/websocket/AUTHORS
generated
vendored
@@ -1,9 +0,0 @@
|
||||
# This is the official list of Gorilla WebSocket authors for copyright
|
||||
# purposes.
|
||||
#
|
||||
# Please keep the list sorted.
|
||||
|
||||
Gary Burd <gary@beagledreams.com>
|
||||
Google LLC (https://opensource.google.com/)
|
||||
Joachim Bauch <mail@joachim-bauch.de>
|
||||
|
||||
6
vendor/github.com/gorilla/websocket/README.md
generated
vendored
6
vendor/github.com/gorilla/websocket/README.md
generated
vendored
@@ -7,12 +7,6 @@ Gorilla WebSocket is a [Go](http://golang.org/) implementation of the
|
||||
[WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol.
|
||||
|
||||
|
||||
---
|
||||
|
||||
⚠️ **[The Gorilla WebSocket Package is looking for a new maintainer](https://github.com/gorilla/websocket/issues/370)**
|
||||
|
||||
---
|
||||
|
||||
### Documentation
|
||||
|
||||
* [API Reference](https://pkg.go.dev/github.com/gorilla/websocket?tab=doc)
|
||||
|
||||
18
vendor/github.com/gorilla/websocket/client.go
generated
vendored
18
vendor/github.com/gorilla/websocket/client.go
generated
vendored
@@ -9,6 +9,7 @@ import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
@@ -318,14 +319,14 @@ func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader h
|
||||
}
|
||||
|
||||
netConn, err := netDial("tcp", hostPort)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if trace != nil && trace.GotConn != nil {
|
||||
trace.GotConn(httptrace.GotConnInfo{
|
||||
Conn: netConn,
|
||||
})
|
||||
}
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if netConn != nil {
|
||||
@@ -370,6 +371,17 @@ func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader h
|
||||
|
||||
resp, err := http.ReadResponse(conn.br, req)
|
||||
if err != nil {
|
||||
if d.TLSClientConfig != nil {
|
||||
for _, proto := range d.TLSClientConfig.NextProtos {
|
||||
if proto != "http/1.1" {
|
||||
return nil, nil, fmt.Errorf(
|
||||
"websocket: protocol %q was given but is not supported;"+
|
||||
"sharing tls.Config with net/http Transport can cause this error: %w",
|
||||
proto, err,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
|
||||
8
vendor/github.com/gorilla/websocket/conn.go
generated
vendored
8
vendor/github.com/gorilla/websocket/conn.go
generated
vendored
@@ -1189,8 +1189,16 @@ func (c *Conn) SetPongHandler(h func(appData string) error) {
|
||||
c.handlePong = h
|
||||
}
|
||||
|
||||
// NetConn returns the underlying connection that is wrapped by c.
|
||||
// Note that writing to or reading from this connection directly will corrupt the
|
||||
// WebSocket connection.
|
||||
func (c *Conn) NetConn() net.Conn {
|
||||
return c.conn
|
||||
}
|
||||
|
||||
// UnderlyingConn returns the internal net.Conn. This can be used to further
|
||||
// modifications to connection specific flags.
|
||||
// Deprecated: Use the NetConn method.
|
||||
func (c *Conn) UnderlyingConn() net.Conn {
|
||||
return c.conn
|
||||
}
|
||||
|
||||
4
vendor/github.com/gorilla/websocket/server.go
generated
vendored
4
vendor/github.com/gorilla/websocket/server.go
generated
vendored
@@ -154,8 +154,8 @@ func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeade
|
||||
}
|
||||
|
||||
challengeKey := r.Header.Get("Sec-Websocket-Key")
|
||||
if challengeKey == "" {
|
||||
return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: 'Sec-WebSocket-Key' header is missing or blank")
|
||||
if !isValidChallengeKey(challengeKey) {
|
||||
return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: 'Sec-WebSocket-Key' header must be Base64 encoded value of 16-byte in length")
|
||||
}
|
||||
|
||||
subprotocol := u.selectSubprotocol(r, responseHeader)
|
||||
|
||||
21
vendor/github.com/gorilla/websocket/tls_handshake_116.go
generated
vendored
21
vendor/github.com/gorilla/websocket/tls_handshake_116.go
generated
vendored
@@ -1,21 +0,0 @@
|
||||
//go:build !go1.17
|
||||
// +build !go1.17
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
)
|
||||
|
||||
func doHandshake(ctx context.Context, tlsConn *tls.Conn, cfg *tls.Config) error {
|
||||
if err := tlsConn.Handshake(); err != nil {
|
||||
return err
|
||||
}
|
||||
if !cfg.InsecureSkipVerify {
|
||||
if err := tlsConn.VerifyHostname(cfg.ServerName); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
15
vendor/github.com/gorilla/websocket/util.go
generated
vendored
15
vendor/github.com/gorilla/websocket/util.go
generated
vendored
@@ -281,3 +281,18 @@ headers:
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// isValidChallengeKey checks if the argument meets RFC6455 specification.
|
||||
func isValidChallengeKey(s string) bool {
|
||||
// From RFC6455:
|
||||
//
|
||||
// A |Sec-WebSocket-Key| header field with a base64-encoded (see
|
||||
// Section 4 of [RFC4648]) value that, when decoded, is 16 bytes in
|
||||
// length.
|
||||
|
||||
if s == "" {
|
||||
return false
|
||||
}
|
||||
decoded, err := base64.StdEncoding.DecodeString(s)
|
||||
return err == nil && len(decoded) == 16
|
||||
}
|
||||
|
||||
473
vendor/github.com/gorilla/websocket/x_net_proxy.go
generated
vendored
473
vendor/github.com/gorilla/websocket/x_net_proxy.go
generated
vendored
@@ -1,473 +0,0 @@
|
||||
// Code generated by golang.org/x/tools/cmd/bundle. DO NOT EDIT.
|
||||
//go:generate bundle -o x_net_proxy.go golang.org/x/net/proxy
|
||||
|
||||
// Package proxy provides support for a variety of protocols to proxy network
|
||||
// data.
|
||||
//
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type proxy_direct struct{}
|
||||
|
||||
// Direct is a direct proxy: one that makes network connections directly.
|
||||
var proxy_Direct = proxy_direct{}
|
||||
|
||||
func (proxy_direct) Dial(network, addr string) (net.Conn, error) {
|
||||
return net.Dial(network, addr)
|
||||
}
|
||||
|
||||
// A PerHost directs connections to a default Dialer unless the host name
|
||||
// requested matches one of a number of exceptions.
|
||||
type proxy_PerHost struct {
|
||||
def, bypass proxy_Dialer
|
||||
|
||||
bypassNetworks []*net.IPNet
|
||||
bypassIPs []net.IP
|
||||
bypassZones []string
|
||||
bypassHosts []string
|
||||
}
|
||||
|
||||
// NewPerHost returns a PerHost Dialer that directs connections to either
|
||||
// defaultDialer or bypass, depending on whether the connection matches one of
|
||||
// the configured rules.
|
||||
func proxy_NewPerHost(defaultDialer, bypass proxy_Dialer) *proxy_PerHost {
|
||||
return &proxy_PerHost{
|
||||
def: defaultDialer,
|
||||
bypass: bypass,
|
||||
}
|
||||
}
|
||||
|
||||
// Dial connects to the address addr on the given network through either
|
||||
// defaultDialer or bypass.
|
||||
func (p *proxy_PerHost) Dial(network, addr string) (c net.Conn, err error) {
|
||||
host, _, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return p.dialerForRequest(host).Dial(network, addr)
|
||||
}
|
||||
|
||||
func (p *proxy_PerHost) dialerForRequest(host string) proxy_Dialer {
|
||||
if ip := net.ParseIP(host); ip != nil {
|
||||
for _, net := range p.bypassNetworks {
|
||||
if net.Contains(ip) {
|
||||
return p.bypass
|
||||
}
|
||||
}
|
||||
for _, bypassIP := range p.bypassIPs {
|
||||
if bypassIP.Equal(ip) {
|
||||
return p.bypass
|
||||
}
|
||||
}
|
||||
return p.def
|
||||
}
|
||||
|
||||
for _, zone := range p.bypassZones {
|
||||
if strings.HasSuffix(host, zone) {
|
||||
return p.bypass
|
||||
}
|
||||
if host == zone[1:] {
|
||||
// For a zone ".example.com", we match "example.com"
|
||||
// too.
|
||||
return p.bypass
|
||||
}
|
||||
}
|
||||
for _, bypassHost := range p.bypassHosts {
|
||||
if bypassHost == host {
|
||||
return p.bypass
|
||||
}
|
||||
}
|
||||
return p.def
|
||||
}
|
||||
|
||||
// AddFromString parses a string that contains comma-separated values
|
||||
// specifying hosts that should use the bypass proxy. Each value is either an
|
||||
// IP address, a CIDR range, a zone (*.example.com) or a host name
|
||||
// (localhost). A best effort is made to parse the string and errors are
|
||||
// ignored.
|
||||
func (p *proxy_PerHost) AddFromString(s string) {
|
||||
hosts := strings.Split(s, ",")
|
||||
for _, host := range hosts {
|
||||
host = strings.TrimSpace(host)
|
||||
if len(host) == 0 {
|
||||
continue
|
||||
}
|
||||
if strings.Contains(host, "/") {
|
||||
// We assume that it's a CIDR address like 127.0.0.0/8
|
||||
if _, net, err := net.ParseCIDR(host); err == nil {
|
||||
p.AddNetwork(net)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if ip := net.ParseIP(host); ip != nil {
|
||||
p.AddIP(ip)
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(host, "*.") {
|
||||
p.AddZone(host[1:])
|
||||
continue
|
||||
}
|
||||
p.AddHost(host)
|
||||
}
|
||||
}
|
||||
|
||||
// AddIP specifies an IP address that will use the bypass proxy. Note that
|
||||
// this will only take effect if a literal IP address is dialed. A connection
|
||||
// to a named host will never match an IP.
|
||||
func (p *proxy_PerHost) AddIP(ip net.IP) {
|
||||
p.bypassIPs = append(p.bypassIPs, ip)
|
||||
}
|
||||
|
||||
// AddNetwork specifies an IP range that will use the bypass proxy. Note that
|
||||
// this will only take effect if a literal IP address is dialed. A connection
|
||||
// to a named host will never match.
|
||||
func (p *proxy_PerHost) AddNetwork(net *net.IPNet) {
|
||||
p.bypassNetworks = append(p.bypassNetworks, net)
|
||||
}
|
||||
|
||||
// AddZone specifies a DNS suffix that will use the bypass proxy. A zone of
|
||||
// "example.com" matches "example.com" and all of its subdomains.
|
||||
func (p *proxy_PerHost) AddZone(zone string) {
|
||||
if strings.HasSuffix(zone, ".") {
|
||||
zone = zone[:len(zone)-1]
|
||||
}
|
||||
if !strings.HasPrefix(zone, ".") {
|
||||
zone = "." + zone
|
||||
}
|
||||
p.bypassZones = append(p.bypassZones, zone)
|
||||
}
|
||||
|
||||
// AddHost specifies a host name that will use the bypass proxy.
|
||||
func (p *proxy_PerHost) AddHost(host string) {
|
||||
if strings.HasSuffix(host, ".") {
|
||||
host = host[:len(host)-1]
|
||||
}
|
||||
p.bypassHosts = append(p.bypassHosts, host)
|
||||
}
|
||||
|
||||
// A Dialer is a means to establish a connection.
|
||||
type proxy_Dialer interface {
|
||||
// Dial connects to the given address via the proxy.
|
||||
Dial(network, addr string) (c net.Conn, err error)
|
||||
}
|
||||
|
||||
// Auth contains authentication parameters that specific Dialers may require.
|
||||
type proxy_Auth struct {
|
||||
User, Password string
|
||||
}
|
||||
|
||||
// FromEnvironment returns the dialer specified by the proxy related variables in
|
||||
// the environment.
|
||||
func proxy_FromEnvironment() proxy_Dialer {
|
||||
allProxy := proxy_allProxyEnv.Get()
|
||||
if len(allProxy) == 0 {
|
||||
return proxy_Direct
|
||||
}
|
||||
|
||||
proxyURL, err := url.Parse(allProxy)
|
||||
if err != nil {
|
||||
return proxy_Direct
|
||||
}
|
||||
proxy, err := proxy_FromURL(proxyURL, proxy_Direct)
|
||||
if err != nil {
|
||||
return proxy_Direct
|
||||
}
|
||||
|
||||
noProxy := proxy_noProxyEnv.Get()
|
||||
if len(noProxy) == 0 {
|
||||
return proxy
|
||||
}
|
||||
|
||||
perHost := proxy_NewPerHost(proxy, proxy_Direct)
|
||||
perHost.AddFromString(noProxy)
|
||||
return perHost
|
||||
}
|
||||
|
||||
// proxySchemes is a map from URL schemes to a function that creates a Dialer
|
||||
// from a URL with such a scheme.
|
||||
var proxy_proxySchemes map[string]func(*url.URL, proxy_Dialer) (proxy_Dialer, error)
|
||||
|
||||
// RegisterDialerType takes a URL scheme and a function to generate Dialers from
|
||||
// a URL with that scheme and a forwarding Dialer. Registered schemes are used
|
||||
// by FromURL.
|
||||
func proxy_RegisterDialerType(scheme string, f func(*url.URL, proxy_Dialer) (proxy_Dialer, error)) {
|
||||
if proxy_proxySchemes == nil {
|
||||
proxy_proxySchemes = make(map[string]func(*url.URL, proxy_Dialer) (proxy_Dialer, error))
|
||||
}
|
||||
proxy_proxySchemes[scheme] = f
|
||||
}
|
||||
|
||||
// FromURL returns a Dialer given a URL specification and an underlying
|
||||
// Dialer for it to make network requests.
|
||||
func proxy_FromURL(u *url.URL, forward proxy_Dialer) (proxy_Dialer, error) {
|
||||
var auth *proxy_Auth
|
||||
if u.User != nil {
|
||||
auth = new(proxy_Auth)
|
||||
auth.User = u.User.Username()
|
||||
if p, ok := u.User.Password(); ok {
|
||||
auth.Password = p
|
||||
}
|
||||
}
|
||||
|
||||
switch u.Scheme {
|
||||
case "socks5":
|
||||
return proxy_SOCKS5("tcp", u.Host, auth, forward)
|
||||
}
|
||||
|
||||
// If the scheme doesn't match any of the built-in schemes, see if it
|
||||
// was registered by another package.
|
||||
if proxy_proxySchemes != nil {
|
||||
if f, ok := proxy_proxySchemes[u.Scheme]; ok {
|
||||
return f(u, forward)
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errors.New("proxy: unknown scheme: " + u.Scheme)
|
||||
}
|
||||
|
||||
var (
|
||||
proxy_allProxyEnv = &proxy_envOnce{
|
||||
names: []string{"ALL_PROXY", "all_proxy"},
|
||||
}
|
||||
proxy_noProxyEnv = &proxy_envOnce{
|
||||
names: []string{"NO_PROXY", "no_proxy"},
|
||||
}
|
||||
)
|
||||
|
||||
// envOnce looks up an environment variable (optionally by multiple
|
||||
// names) once. It mitigates expensive lookups on some platforms
|
||||
// (e.g. Windows).
|
||||
// (Borrowed from net/http/transport.go)
|
||||
type proxy_envOnce struct {
|
||||
names []string
|
||||
once sync.Once
|
||||
val string
|
||||
}
|
||||
|
||||
func (e *proxy_envOnce) Get() string {
|
||||
e.once.Do(e.init)
|
||||
return e.val
|
||||
}
|
||||
|
||||
func (e *proxy_envOnce) init() {
|
||||
for _, n := range e.names {
|
||||
e.val = os.Getenv(n)
|
||||
if e.val != "" {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SOCKS5 returns a Dialer that makes SOCKSv5 connections to the given address
|
||||
// with an optional username and password. See RFC 1928 and RFC 1929.
|
||||
func proxy_SOCKS5(network, addr string, auth *proxy_Auth, forward proxy_Dialer) (proxy_Dialer, error) {
|
||||
s := &proxy_socks5{
|
||||
network: network,
|
||||
addr: addr,
|
||||
forward: forward,
|
||||
}
|
||||
if auth != nil {
|
||||
s.user = auth.User
|
||||
s.password = auth.Password
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
type proxy_socks5 struct {
|
||||
user, password string
|
||||
network, addr string
|
||||
forward proxy_Dialer
|
||||
}
|
||||
|
||||
const proxy_socks5Version = 5
|
||||
|
||||
const (
|
||||
proxy_socks5AuthNone = 0
|
||||
proxy_socks5AuthPassword = 2
|
||||
)
|
||||
|
||||
const proxy_socks5Connect = 1
|
||||
|
||||
const (
|
||||
proxy_socks5IP4 = 1
|
||||
proxy_socks5Domain = 3
|
||||
proxy_socks5IP6 = 4
|
||||
)
|
||||
|
||||
var proxy_socks5Errors = []string{
|
||||
"",
|
||||
"general failure",
|
||||
"connection forbidden",
|
||||
"network unreachable",
|
||||
"host unreachable",
|
||||
"connection refused",
|
||||
"TTL expired",
|
||||
"command not supported",
|
||||
"address type not supported",
|
||||
}
|
||||
|
||||
// Dial connects to the address addr on the given network via the SOCKS5 proxy.
|
||||
func (s *proxy_socks5) Dial(network, addr string) (net.Conn, error) {
|
||||
switch network {
|
||||
case "tcp", "tcp6", "tcp4":
|
||||
default:
|
||||
return nil, errors.New("proxy: no support for SOCKS5 proxy connections of type " + network)
|
||||
}
|
||||
|
||||
conn, err := s.forward.Dial(s.network, s.addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := s.connect(conn, addr); err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// connect takes an existing connection to a socks5 proxy server,
|
||||
// and commands the server to extend that connection to target,
|
||||
// which must be a canonical address with a host and port.
|
||||
func (s *proxy_socks5) connect(conn net.Conn, target string) error {
|
||||
host, portStr, err := net.SplitHostPort(target)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
port, err := strconv.Atoi(portStr)
|
||||
if err != nil {
|
||||
return errors.New("proxy: failed to parse port number: " + portStr)
|
||||
}
|
||||
if port < 1 || port > 0xffff {
|
||||
return errors.New("proxy: port number out of range: " + portStr)
|
||||
}
|
||||
|
||||
// the size here is just an estimate
|
||||
buf := make([]byte, 0, 6+len(host))
|
||||
|
||||
buf = append(buf, proxy_socks5Version)
|
||||
if len(s.user) > 0 && len(s.user) < 256 && len(s.password) < 256 {
|
||||
buf = append(buf, 2 /* num auth methods */, proxy_socks5AuthNone, proxy_socks5AuthPassword)
|
||||
} else {
|
||||
buf = append(buf, 1 /* num auth methods */, proxy_socks5AuthNone)
|
||||
}
|
||||
|
||||
if _, err := conn.Write(buf); err != nil {
|
||||
return errors.New("proxy: failed to write greeting to SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
if _, err := io.ReadFull(conn, buf[:2]); err != nil {
|
||||
return errors.New("proxy: failed to read greeting from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
if buf[0] != 5 {
|
||||
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " has unexpected version " + strconv.Itoa(int(buf[0])))
|
||||
}
|
||||
if buf[1] == 0xff {
|
||||
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " requires authentication")
|
||||
}
|
||||
|
||||
// See RFC 1929
|
||||
if buf[1] == proxy_socks5AuthPassword {
|
||||
buf = buf[:0]
|
||||
buf = append(buf, 1 /* password protocol version */)
|
||||
buf = append(buf, uint8(len(s.user)))
|
||||
buf = append(buf, s.user...)
|
||||
buf = append(buf, uint8(len(s.password)))
|
||||
buf = append(buf, s.password...)
|
||||
|
||||
if _, err := conn.Write(buf); err != nil {
|
||||
return errors.New("proxy: failed to write authentication request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
if _, err := io.ReadFull(conn, buf[:2]); err != nil {
|
||||
return errors.New("proxy: failed to read authentication reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
if buf[1] != 0 {
|
||||
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " rejected username/password")
|
||||
}
|
||||
}
|
||||
|
||||
buf = buf[:0]
|
||||
buf = append(buf, proxy_socks5Version, proxy_socks5Connect, 0 /* reserved */)
|
||||
|
||||
if ip := net.ParseIP(host); ip != nil {
|
||||
if ip4 := ip.To4(); ip4 != nil {
|
||||
buf = append(buf, proxy_socks5IP4)
|
||||
ip = ip4
|
||||
} else {
|
||||
buf = append(buf, proxy_socks5IP6)
|
||||
}
|
||||
buf = append(buf, ip...)
|
||||
} else {
|
||||
if len(host) > 255 {
|
||||
return errors.New("proxy: destination host name too long: " + host)
|
||||
}
|
||||
buf = append(buf, proxy_socks5Domain)
|
||||
buf = append(buf, byte(len(host)))
|
||||
buf = append(buf, host...)
|
||||
}
|
||||
buf = append(buf, byte(port>>8), byte(port))
|
||||
|
||||
if _, err := conn.Write(buf); err != nil {
|
||||
return errors.New("proxy: failed to write connect request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
if _, err := io.ReadFull(conn, buf[:4]); err != nil {
|
||||
return errors.New("proxy: failed to read connect reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
failure := "unknown error"
|
||||
if int(buf[1]) < len(proxy_socks5Errors) {
|
||||
failure = proxy_socks5Errors[buf[1]]
|
||||
}
|
||||
|
||||
if len(failure) > 0 {
|
||||
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " failed to connect: " + failure)
|
||||
}
|
||||
|
||||
bytesToDiscard := 0
|
||||
switch buf[3] {
|
||||
case proxy_socks5IP4:
|
||||
bytesToDiscard = net.IPv4len
|
||||
case proxy_socks5IP6:
|
||||
bytesToDiscard = net.IPv6len
|
||||
case proxy_socks5Domain:
|
||||
_, err := io.ReadFull(conn, buf[:1])
|
||||
if err != nil {
|
||||
return errors.New("proxy: failed to read domain length from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
bytesToDiscard = int(buf[0])
|
||||
default:
|
||||
return errors.New("proxy: got unknown address type " + strconv.Itoa(int(buf[3])) + " from SOCKS5 proxy at " + s.addr)
|
||||
}
|
||||
|
||||
if cap(buf) < bytesToDiscard {
|
||||
buf = make([]byte, bytesToDiscard)
|
||||
} else {
|
||||
buf = buf[:bytesToDiscard]
|
||||
}
|
||||
if _, err := io.ReadFull(conn, buf); err != nil {
|
||||
return errors.New("proxy: failed to read address from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
// Also need to discard the port number
|
||||
if _, err := io.ReadFull(conn, buf[:2]); err != nil {
|
||||
return errors.New("proxy: failed to read port from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
39
vendor/github.com/jessevdk/go-flags/.travis.yml
generated
vendored
39
vendor/github.com/jessevdk/go-flags/.travis.yml
generated
vendored
@@ -1,39 +0,0 @@
|
||||
language: go
|
||||
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
|
||||
go:
|
||||
- 1.16.x
|
||||
|
||||
install:
|
||||
# go-flags
|
||||
- go build -v ./...
|
||||
|
||||
# linting
|
||||
- go get -v golang.org/x/lint/golint
|
||||
|
||||
# code coverage
|
||||
- go get golang.org/x/tools/cmd/cover
|
||||
- go get github.com/onsi/ginkgo/ginkgo
|
||||
- go get github.com/modocache/gover
|
||||
- if [ "$TRAVIS_SECURE_ENV_VARS" = "true" ]; then go get github.com/mattn/goveralls; fi
|
||||
|
||||
script:
|
||||
# go-flags
|
||||
- $(exit $(gofmt -l . | wc -l))
|
||||
- go test -v ./...
|
||||
|
||||
# linting
|
||||
- go tool vet -all=true -v=true . || true
|
||||
- $(go env GOPATH | awk 'BEGIN{FS=":"} {print $1}')/bin/golint ./...
|
||||
|
||||
# code coverage
|
||||
- $(go env GOPATH | awk 'BEGIN{FS=":"} {print $1}')/bin/ginkgo -r -cover
|
||||
- $(go env GOPATH | awk 'BEGIN{FS=":"} {print $1}')/bin/gover
|
||||
- if [ "$TRAVIS_SECURE_ENV_VARS" = "true" ]; then $(go env GOPATH | awk 'BEGIN{FS=":"} {print $1}')/bin/goveralls -coverprofile=gover.coverprofile -service=travis-ci -repotoken $COVERALLS_TOKEN; fi
|
||||
|
||||
env:
|
||||
# coveralls.io
|
||||
secure: "RCYbiB4P0RjQRIoUx/vG/AjP3mmYCbzOmr86DCww1Z88yNcy3hYr3Cq8rpPtYU5v0g7wTpu4adaKIcqRE9xknYGbqj3YWZiCoBP1/n4Z+9sHW3Dsd9D/GRGeHUus0laJUGARjWoCTvoEtOgTdGQDoX7mH+pUUY0FBltNYUdOiiU="
|
||||
5
vendor/github.com/jessevdk/go-flags/README.md
generated
vendored
5
vendor/github.com/jessevdk/go-flags/README.md
generated
vendored
@@ -1,7 +1,7 @@
|
||||
go-flags: a go library for parsing command line arguments
|
||||
=========================================================
|
||||
|
||||
[](https://godoc.org/github.com/jessevdk/go-flags) [](https://travis-ci.org/jessevdk/go-flags) [](https://coveralls.io/r/jessevdk/go-flags?branch=master)
|
||||
[](https://godoc.org/github.com/jessevdk/go-flags)
|
||||
|
||||
This library provides similar functionality to the builtin flag library of
|
||||
go, but provides much more functionality and nicer formatting. From the
|
||||
@@ -78,6 +78,9 @@ var opts struct {
|
||||
|
||||
// Example of a map
|
||||
IntMap map[string]int `long:"intmap" description:"A map from string to int"`
|
||||
|
||||
// Example of env variable
|
||||
Thresholds []int `long:"thresholds" default:"1" default:"2" env:"THRESHOLD_VALUES" env-delim:","`
|
||||
}
|
||||
|
||||
// Callback which will invoke callto:<argument> to call a number.
|
||||
|
||||
11
vendor/github.com/jessevdk/go-flags/command.go
generated
vendored
11
vendor/github.com/jessevdk/go-flags/command.go
generated
vendored
@@ -30,6 +30,12 @@ type Command struct {
|
||||
// Whether positional arguments are required
|
||||
ArgsRequired bool
|
||||
|
||||
// Whether to pass all arguments after the first non option as remaining
|
||||
// command line arguments. This is equivalent to strict POSIX processing.
|
||||
// This is command-local version of PassAfterNonOption Parser flag. It
|
||||
// cannot be turned off when PassAfterNonOption Parser flag is set.
|
||||
PassAfterNonOption bool
|
||||
|
||||
commands []*Command
|
||||
hasBuiltinHelpGroup bool
|
||||
args []*Arg
|
||||
@@ -244,6 +250,7 @@ func (c *Command) scanSubcommandHandler(parentg *Group) scanHandler {
|
||||
longDescription := mtag.Get("long-description")
|
||||
subcommandsOptional := mtag.Get("subcommands-optional")
|
||||
aliases := mtag.GetMany("alias")
|
||||
passAfterNonOption := mtag.Get("pass-after-non-option")
|
||||
|
||||
subc, err := c.AddCommand(subcommand, shortDescription, longDescription, ptrval.Interface())
|
||||
|
||||
@@ -261,6 +268,10 @@ func (c *Command) scanSubcommandHandler(parentg *Group) scanHandler {
|
||||
subc.Aliases = aliases
|
||||
}
|
||||
|
||||
if len(passAfterNonOption) > 0 {
|
||||
subc.PassAfterNonOption = true
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
|
||||
17
vendor/github.com/jessevdk/go-flags/convert.go
generated
vendored
17
vendor/github.com/jessevdk/go-flags/convert.go
generated
vendored
@@ -53,7 +53,7 @@ func getBase(options multiTag, base int) (int, error) {
|
||||
|
||||
func convertMarshal(val reflect.Value) (bool, string, error) {
|
||||
// Check first for the Marshaler interface
|
||||
if val.Type().NumMethod() > 0 && val.CanInterface() {
|
||||
if val.IsValid() && val.Type().NumMethod() > 0 && val.CanInterface() {
|
||||
if marshaler, ok := val.Interface().(Marshaler); ok {
|
||||
ret, err := marshaler.MarshalFlag()
|
||||
return true, ret, err
|
||||
@@ -68,6 +68,10 @@ func convertToString(val reflect.Value, options multiTag) (string, error) {
|
||||
return ret, err
|
||||
}
|
||||
|
||||
if !val.IsValid() {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
tp := val.Type()
|
||||
|
||||
// Support for time.Duration
|
||||
@@ -220,7 +224,7 @@ func convert(val string, retval reflect.Value, options multiTag) error {
|
||||
retval.SetBool(b)
|
||||
}
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
base, err := getBase(options, 10)
|
||||
base, err := getBase(options, 0)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -234,7 +238,7 @@ func convert(val string, retval reflect.Value, options multiTag) error {
|
||||
|
||||
retval.SetInt(parsed)
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
base, err := getBase(options, 10)
|
||||
base, err := getBase(options, 0)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -267,7 +271,12 @@ func convert(val string, retval reflect.Value, options multiTag) error {
|
||||
|
||||
retval.Set(reflect.Append(retval, elemval))
|
||||
case reflect.Map:
|
||||
parts := strings.SplitN(val, ":", 2)
|
||||
keyValueDelimiter := options.Get("key-value-delimiter")
|
||||
if keyValueDelimiter == "" {
|
||||
keyValueDelimiter = ":"
|
||||
}
|
||||
|
||||
parts := strings.SplitN(val, keyValueDelimiter, 2)
|
||||
|
||||
key := parts[0]
|
||||
var value string
|
||||
|
||||
266
vendor/github.com/jessevdk/go-flags/flags.go
generated
vendored
266
vendor/github.com/jessevdk/go-flags/flags.go
generated
vendored
@@ -8,46 +8,45 @@ The flags package is similar in functionality to the go built-in flag package
|
||||
but provides more options and uses reflection to provide a convenient and
|
||||
succinct way of specifying command line options.
|
||||
|
||||
|
||||
Supported features
|
||||
# Supported features
|
||||
|
||||
The following features are supported in go-flags:
|
||||
|
||||
Options with short names (-v)
|
||||
Options with long names (--verbose)
|
||||
Options with and without arguments (bool v.s. other type)
|
||||
Options with optional arguments and default values
|
||||
Option default values from ENVIRONMENT_VARIABLES, including slice and map values
|
||||
Multiple option groups each containing a set of options
|
||||
Generate and print well-formatted help message
|
||||
Passing remaining command line arguments after -- (optional)
|
||||
Ignoring unknown command line options (optional)
|
||||
Supports -I/usr/include -I=/usr/include -I /usr/include option argument specification
|
||||
Supports multiple short options -aux
|
||||
Supports all primitive go types (string, int{8..64}, uint{8..64}, float)
|
||||
Supports same option multiple times (can store in slice or last option counts)
|
||||
Supports maps
|
||||
Supports function callbacks
|
||||
Supports namespaces for (nested) option groups
|
||||
Options with short names (-v)
|
||||
Options with long names (--verbose)
|
||||
Options with and without arguments (bool v.s. other type)
|
||||
Options with optional arguments and default values
|
||||
Option default values from ENVIRONMENT_VARIABLES, including slice and map values
|
||||
Multiple option groups each containing a set of options
|
||||
Generate and print well-formatted help message
|
||||
Passing remaining command line arguments after -- (optional)
|
||||
Ignoring unknown command line options (optional)
|
||||
Supports -I/usr/include -I=/usr/include -I /usr/include option argument specification
|
||||
Supports multiple short options -aux
|
||||
Supports all primitive go types (string, int{8..64}, uint{8..64}, float)
|
||||
Supports same option multiple times (can store in slice or last option counts)
|
||||
Supports maps
|
||||
Supports function callbacks
|
||||
Supports namespaces for (nested) option groups
|
||||
|
||||
Additional features specific to Windows:
|
||||
Options with short names (/v)
|
||||
Options with long names (/verbose)
|
||||
Windows-style options with arguments use a colon as the delimiter
|
||||
Modify generated help message with Windows-style / options
|
||||
Windows style options can be disabled at build time using the "forceposix"
|
||||
build tag
|
||||
|
||||
Options with short names (/v)
|
||||
Options with long names (/verbose)
|
||||
Windows-style options with arguments use a colon as the delimiter
|
||||
Modify generated help message with Windows-style / options
|
||||
Windows style options can be disabled at build time using the "forceposix"
|
||||
build tag
|
||||
|
||||
Basic usage
|
||||
# Basic usage
|
||||
|
||||
The flags package uses structs, reflection and struct field tags
|
||||
to allow users to specify command line options. This results in very simple
|
||||
and concise specification of your application options. For example:
|
||||
|
||||
type Options struct {
|
||||
Verbose []bool `short:"v" long:"verbose" description:"Show verbose debug information"`
|
||||
}
|
||||
type Options struct {
|
||||
Verbose []bool `short:"v" long:"verbose" description:"Show verbose debug information"`
|
||||
}
|
||||
|
||||
This specifies one option with a short name -v and a long name --verbose.
|
||||
When either -v or --verbose is found on the command line, a 'true' value
|
||||
@@ -60,9 +59,9 @@ whenever the option is encountered, a value is appended to the slice.
|
||||
Map options from string to primitive type are also supported. On the command
|
||||
line, you specify the value for such an option as key:value. For example
|
||||
|
||||
type Options struct {
|
||||
AuthorInfo string[string] `short:"a"`
|
||||
}
|
||||
type Options struct {
|
||||
AuthorInfo string[string] `short:"a"`
|
||||
}
|
||||
|
||||
Then, the AuthorInfo map can be filled with something like
|
||||
-a name:Jesse -a "surname:van den Kieboom".
|
||||
@@ -71,96 +70,94 @@ Finally, for full control over the conversion between command line argument
|
||||
values and options, user defined types can choose to implement the Marshaler
|
||||
and Unmarshaler interfaces.
|
||||
|
||||
|
||||
Available field tags
|
||||
# Available field tags
|
||||
|
||||
The following is a list of tags for struct fields supported by go-flags:
|
||||
|
||||
short: the short name of the option (single character)
|
||||
long: the long name of the option
|
||||
required: if non empty, makes the option required to appear on the command
|
||||
line. If a required option is not present, the parser will
|
||||
return ErrRequired (optional)
|
||||
description: the description of the option (optional)
|
||||
long-description: the long description of the option. Currently only
|
||||
displayed in generated man pages (optional)
|
||||
no-flag: if non-empty, this field is ignored as an option (optional)
|
||||
short: the short name of the option (single character)
|
||||
long: the long name of the option
|
||||
required: if non empty, makes the option required to appear on the command
|
||||
line. If a required option is not present, the parser will
|
||||
return ErrRequired (optional)
|
||||
description: the description of the option (optional)
|
||||
long-description: the long description of the option. Currently only
|
||||
displayed in generated man pages (optional)
|
||||
no-flag: if non-empty, this field is ignored as an option (optional)
|
||||
|
||||
optional: if non-empty, makes the argument of the option optional. When an
|
||||
argument is optional it can only be specified using
|
||||
--option=argument (optional)
|
||||
optional-value: the value of an optional option when the option occurs
|
||||
without an argument. This tag can be specified multiple
|
||||
times in the case of maps or slices (optional)
|
||||
default: the default value of an option. This tag can be specified
|
||||
multiple times in the case of slices or maps (optional)
|
||||
default-mask: when specified, this value will be displayed in the help
|
||||
instead of the actual default value. This is useful
|
||||
mostly for hiding otherwise sensitive information from
|
||||
showing up in the help. If default-mask takes the special
|
||||
value "-", then no default value will be shown at all
|
||||
(optional)
|
||||
env: the default value of the option is overridden from the
|
||||
specified environment variable, if one has been defined.
|
||||
(optional)
|
||||
env-delim: the 'env' default value from environment is split into
|
||||
multiple values with the given delimiter string, use with
|
||||
slices and maps (optional)
|
||||
value-name: the name of the argument value (to be shown in the help)
|
||||
(optional)
|
||||
choice: limits the values for an option to a set of values.
|
||||
Repeat this tag once for each allowable value.
|
||||
e.g. `long:"animal" choice:"cat" choice:"dog"`
|
||||
hidden: if non-empty, the option is not visible in the help or man page.
|
||||
optional: if non-empty, makes the argument of the option optional. When an
|
||||
argument is optional it can only be specified using
|
||||
--option=argument (optional)
|
||||
optional-value: the value of an optional option when the option occurs
|
||||
without an argument. This tag can be specified multiple
|
||||
times in the case of maps or slices (optional)
|
||||
default: the default value of an option. This tag can be specified
|
||||
multiple times in the case of slices or maps (optional)
|
||||
default-mask: when specified, this value will be displayed in the help
|
||||
instead of the actual default value. This is useful
|
||||
mostly for hiding otherwise sensitive information from
|
||||
showing up in the help. If default-mask takes the special
|
||||
value "-", then no default value will be shown at all
|
||||
(optional)
|
||||
env: the default value of the option is overridden from the
|
||||
specified environment variable, if one has been defined.
|
||||
(optional)
|
||||
env-delim: the 'env' default value from environment is split into
|
||||
multiple values with the given delimiter string, use with
|
||||
slices and maps (optional)
|
||||
value-name: the name of the argument value (to be shown in the help)
|
||||
(optional)
|
||||
choice: limits the values for an option to a set of values.
|
||||
Repeat this tag once for each allowable value.
|
||||
e.g. `long:"animal" choice:"cat" choice:"dog"`
|
||||
hidden: if non-empty, the option is not visible in the help or man page.
|
||||
|
||||
base: a base (radix) used to convert strings to integer values, the
|
||||
default base is 10 (i.e. decimal) (optional)
|
||||
base: a base (radix) used to convert strings to integer values, the
|
||||
default base is 10 (i.e. decimal) (optional)
|
||||
|
||||
ini-name: the explicit ini option name (optional)
|
||||
no-ini: if non-empty this field is ignored as an ini option
|
||||
(optional)
|
||||
ini-name: the explicit ini option name (optional)
|
||||
no-ini: if non-empty this field is ignored as an ini option
|
||||
(optional)
|
||||
|
||||
group: when specified on a struct field, makes the struct
|
||||
field a separate group with the given name (optional)
|
||||
namespace: when specified on a group struct field, the namespace
|
||||
gets prepended to every option's long name and
|
||||
subgroup's namespace of this group, separated by
|
||||
the parser's namespace delimiter (optional)
|
||||
env-namespace: when specified on a group struct field, the env-namespace
|
||||
gets prepended to every option's env key and
|
||||
subgroup's env-namespace of this group, separated by
|
||||
the parser's env-namespace delimiter (optional)
|
||||
command: when specified on a struct field, makes the struct
|
||||
field a (sub)command with the given name (optional)
|
||||
subcommands-optional: when specified on a command struct field, makes
|
||||
any subcommands of that command optional (optional)
|
||||
alias: when specified on a command struct field, adds the
|
||||
specified name as an alias for the command. Can be
|
||||
be specified multiple times to add more than one
|
||||
alias (optional)
|
||||
positional-args: when specified on a field with a struct type,
|
||||
uses the fields of that struct to parse remaining
|
||||
positional command line arguments into (in order
|
||||
of the fields). If a field has a slice type,
|
||||
then all remaining arguments will be added to it.
|
||||
Positional arguments are optional by default,
|
||||
unless the "required" tag is specified together
|
||||
with the "positional-args" tag. The "required" tag
|
||||
can also be set on the individual rest argument
|
||||
fields, to require only the first N positional
|
||||
arguments. If the "required" tag is set on the
|
||||
rest arguments slice, then its value determines
|
||||
the minimum amount of rest arguments that needs to
|
||||
be provided (e.g. `required:"2"`) (optional)
|
||||
positional-arg-name: used on a field in a positional argument struct; name
|
||||
of the positional argument placeholder to be shown in
|
||||
the help (optional)
|
||||
group: when specified on a struct field, makes the struct
|
||||
field a separate group with the given name (optional)
|
||||
namespace: when specified on a group struct field, the namespace
|
||||
gets prepended to every option's long name and
|
||||
subgroup's namespace of this group, separated by
|
||||
the parser's namespace delimiter (optional)
|
||||
env-namespace: when specified on a group struct field, the env-namespace
|
||||
gets prepended to every option's env key and
|
||||
subgroup's env-namespace of this group, separated by
|
||||
the parser's env-namespace delimiter (optional)
|
||||
command: when specified on a struct field, makes the struct
|
||||
field a (sub)command with the given name (optional)
|
||||
subcommands-optional: when specified on a command struct field, makes
|
||||
any subcommands of that command optional (optional)
|
||||
alias: when specified on a command struct field, adds the
|
||||
specified name as an alias for the command. Can be
|
||||
be specified multiple times to add more than one
|
||||
alias (optional)
|
||||
positional-args: when specified on a field with a struct type,
|
||||
uses the fields of that struct to parse remaining
|
||||
positional command line arguments into (in order
|
||||
of the fields). If a field has a slice type,
|
||||
then all remaining arguments will be added to it.
|
||||
Positional arguments are optional by default,
|
||||
unless the "required" tag is specified together
|
||||
with the "positional-args" tag. The "required" tag
|
||||
can also be set on the individual rest argument
|
||||
fields, to require only the first N positional
|
||||
arguments. If the "required" tag is set on the
|
||||
rest arguments slice, then its value determines
|
||||
the minimum amount of rest arguments that needs to
|
||||
be provided (e.g. `required:"2"`) (optional)
|
||||
positional-arg-name: used on a field in a positional argument struct; name
|
||||
of the positional argument placeholder to be shown in
|
||||
the help (optional)
|
||||
|
||||
Either the `short:` tag or the `long:` must be specified to make the field eligible as an
|
||||
option.
|
||||
|
||||
|
||||
Option groups
|
||||
# Option groups
|
||||
|
||||
Option groups are a simple way to semantically separate your options. All
|
||||
options in a particular group are shown together in the help under the name
|
||||
@@ -169,14 +166,12 @@ precisely and emphasize the options affiliation to their group.
|
||||
|
||||
There are currently three ways to specify option groups.
|
||||
|
||||
1. Use NewNamedParser specifying the various option groups.
|
||||
2. Use AddGroup to add a group to an existing parser.
|
||||
3. Add a struct field to the top-level options annotated with the
|
||||
group:"group-name" tag.
|
||||
1. Use NewNamedParser specifying the various option groups.
|
||||
2. Use AddGroup to add a group to an existing parser.
|
||||
3. Add a struct field to the top-level options annotated with the
|
||||
group:"group-name" tag.
|
||||
|
||||
|
||||
|
||||
Commands
|
||||
# Commands
|
||||
|
||||
The flags package also has basic support for commands. Commands are often
|
||||
used in monolithic applications that support various commands or actions.
|
||||
@@ -186,9 +181,9 @@ application.
|
||||
|
||||
There are currently two ways to specify a command.
|
||||
|
||||
1. Use AddCommand on an existing parser.
|
||||
2. Add a struct field to your options struct annotated with the
|
||||
command:"command-name" tag.
|
||||
1. Use AddCommand on an existing parser.
|
||||
2. Add a struct field to your options struct annotated with the
|
||||
command:"command-name" tag.
|
||||
|
||||
The most common, idiomatic way to implement commands is to define a global
|
||||
parser instance and implement each command in a separate file. These
|
||||
@@ -204,15 +199,14 @@ command has been specified on the command line, in addition to the options
|
||||
of all the parent commands. I.e. considering a -v flag on the parser and an
|
||||
add command, the following are equivalent:
|
||||
|
||||
./app -v add
|
||||
./app add -v
|
||||
./app -v add
|
||||
./app add -v
|
||||
|
||||
However, if the -v flag is defined on the add command, then the first of
|
||||
the two examples above would fail since the -v flag is not defined before
|
||||
the add command.
|
||||
|
||||
|
||||
Completion
|
||||
# Completion
|
||||
|
||||
go-flags has builtin support to provide bash completion of flags, commands
|
||||
and argument values. To use completion, the binary which uses go-flags
|
||||
@@ -226,7 +220,7 @@ by replacing the argument parsing routine with the completion routine which
|
||||
outputs completions for the passed arguments. The basic invocation to
|
||||
complete a set of arguments is therefore:
|
||||
|
||||
GO_FLAGS_COMPLETION=1 ./completion-example arg1 arg2 arg3
|
||||
GO_FLAGS_COMPLETION=1 ./completion-example arg1 arg2 arg3
|
||||
|
||||
where `completion-example` is the binary, `arg1` and `arg2` are
|
||||
the current arguments, and `arg3` (the last argument) is the argument
|
||||
@@ -237,20 +231,20 @@ are more than 1 completion items.
|
||||
To use this with bash completion, a simple file can be written which
|
||||
calls the binary which supports go-flags completion:
|
||||
|
||||
_completion_example() {
|
||||
# All arguments except the first one
|
||||
args=("${COMP_WORDS[@]:1:$COMP_CWORD}")
|
||||
_completion_example() {
|
||||
# All arguments except the first one
|
||||
args=("${COMP_WORDS[@]:1:$COMP_CWORD}")
|
||||
|
||||
# Only split on newlines
|
||||
local IFS=$'\n'
|
||||
# Only split on newlines
|
||||
local IFS=$'\n'
|
||||
|
||||
# Call completion (note that the first element of COMP_WORDS is
|
||||
# the executable itself)
|
||||
COMPREPLY=($(GO_FLAGS_COMPLETION=1 ${COMP_WORDS[0]} "${args[@]}"))
|
||||
return 0
|
||||
}
|
||||
# Call completion (note that the first element of COMP_WORDS is
|
||||
# the executable itself)
|
||||
COMPREPLY=($(GO_FLAGS_COMPLETION=1 ${COMP_WORDS[0]} "${args[@]}"))
|
||||
return 0
|
||||
}
|
||||
|
||||
complete -F _completion_example completion-example
|
||||
complete -F _completion_example completion-example
|
||||
|
||||
Completion requires the parser option PassDoubleDash and is therefore enforced if the environment variable GO_FLAGS_COMPLETION is set.
|
||||
|
||||
|
||||
14
vendor/github.com/jessevdk/go-flags/help.go
generated
vendored
14
vendor/github.com/jessevdk/go-flags/help.go
generated
vendored
@@ -72,15 +72,15 @@ func (p *Parser) getAlignmentInfo() alignmentInfo {
|
||||
var prevcmd *Command
|
||||
|
||||
p.eachActiveGroup(func(c *Command, grp *Group) {
|
||||
if !grp.showInHelp() {
|
||||
return
|
||||
}
|
||||
if c != prevcmd {
|
||||
for _, arg := range c.args {
|
||||
ret.updateLen(arg.Name, c != p.Command)
|
||||
}
|
||||
prevcmd = c
|
||||
}
|
||||
if !grp.showInHelp() {
|
||||
return
|
||||
}
|
||||
|
||||
for _, info := range grp.options {
|
||||
if !info.showInHelp() {
|
||||
continue
|
||||
@@ -334,7 +334,11 @@ func (p *Parser) WriteHelp(writer io.Writer) {
|
||||
}
|
||||
|
||||
if !allcmd.ArgsRequired {
|
||||
fmt.Fprintf(wr, "[%s]", name)
|
||||
if arg.Required > 0 {
|
||||
fmt.Fprintf(wr, "%s", name)
|
||||
} else {
|
||||
fmt.Fprintf(wr, "[%s]", name)
|
||||
}
|
||||
} else {
|
||||
fmt.Fprintf(wr, "%s", name)
|
||||
}
|
||||
|
||||
14
vendor/github.com/jessevdk/go-flags/ini.go
generated
vendored
14
vendor/github.com/jessevdk/go-flags/ini.go
generated
vendored
@@ -113,18 +113,18 @@ func (i *IniParser) ParseFile(filename string) error {
|
||||
//
|
||||
// The format of the ini file is as follows:
|
||||
//
|
||||
// [Option group name]
|
||||
// option = value
|
||||
// [Option group name]
|
||||
// option = value
|
||||
//
|
||||
// Each section in the ini file represents an option group or command in the
|
||||
// flags parser. The default flags parser option group (i.e. when using
|
||||
// flags.Parse) is named 'Application Options'. The ini option name is matched
|
||||
// in the following order:
|
||||
//
|
||||
// 1. Compared to the ini-name tag on the option struct field (if present)
|
||||
// 2. Compared to the struct field name
|
||||
// 3. Compared to the option long name (if present)
|
||||
// 4. Compared to the option short name (if present)
|
||||
// 1. Compared to the ini-name tag on the option struct field (if present)
|
||||
// 2. Compared to the struct field name
|
||||
// 3. Compared to the option long name (if present)
|
||||
// 4. Compared to the option short name (if present)
|
||||
//
|
||||
// Sections for nested groups and commands can be addressed using a dot `.'
|
||||
// namespacing notation (i.e [subcommand.Options]). Group section names are
|
||||
@@ -584,7 +584,7 @@ func (i *IniParser) parse(ini *ini) error {
|
||||
if i.ParseAsDefaults {
|
||||
err = opt.setDefault(pval)
|
||||
} else {
|
||||
err = opt.set(pval)
|
||||
err = opt.Set(pval)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
||||
4
vendor/github.com/jessevdk/go-flags/option.go
generated
vendored
4
vendor/github.com/jessevdk/go-flags/option.go
generated
vendored
@@ -239,7 +239,7 @@ func (option *Option) IsSetDefault() bool {
|
||||
// Set the value of an option to the specified value. An error will be returned
|
||||
// if the specified value could not be converted to the corresponding option
|
||||
// value type.
|
||||
func (option *Option) set(value *string) error {
|
||||
func (option *Option) Set(value *string) error {
|
||||
kind := option.value.Type().Kind()
|
||||
|
||||
if (kind == reflect.Map || kind == reflect.Slice) && option.clearReferenceBeforeSet {
|
||||
@@ -287,7 +287,7 @@ func (option *Option) setDefault(value *string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := option.set(value); err != nil {
|
||||
if err := option.Set(value); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
1
vendor/github.com/jessevdk/go-flags/optstyle_other.go
generated
vendored
1
vendor/github.com/jessevdk/go-flags/optstyle_other.go
generated
vendored
@@ -1,3 +1,4 @@
|
||||
//go:build !windows || forceposix
|
||||
// +build !windows forceposix
|
||||
|
||||
package flags
|
||||
|
||||
1
vendor/github.com/jessevdk/go-flags/optstyle_windows.go
generated
vendored
1
vendor/github.com/jessevdk/go-flags/optstyle_windows.go
generated
vendored
@@ -1,3 +1,4 @@
|
||||
//go:build !forceposix
|
||||
// +build !forceposix
|
||||
|
||||
package flags
|
||||
|
||||
15
vendor/github.com/jessevdk/go-flags/parser.go
generated
vendored
15
vendor/github.com/jessevdk/go-flags/parser.go
generated
vendored
@@ -113,6 +113,10 @@ const (
|
||||
// POSIX processing.
|
||||
PassAfterNonOption
|
||||
|
||||
// AllowBoolValues allows a user to assign true/false to a boolean value
|
||||
// rather than raising an error stating it cannot have an argument.
|
||||
AllowBoolValues
|
||||
|
||||
// Default is a convenient default set of options which should cover
|
||||
// most of the uses of the flags package.
|
||||
Default = HelpFlag | PrintErrors | PassDoubleDash
|
||||
@@ -252,7 +256,7 @@ func (p *Parser) ParseArgs(args []string) ([]string, error) {
|
||||
}
|
||||
|
||||
if !argumentIsOption(arg) {
|
||||
if (p.Options&PassAfterNonOption) != None && s.lookup.commands[arg] == nil {
|
||||
if ((p.Options&PassAfterNonOption) != None || s.command.PassAfterNonOption) && s.lookup.commands[arg] == nil {
|
||||
// If PassAfterNonOption is set then all remaining arguments
|
||||
// are considered positional
|
||||
if err = s.addArgs(s.arg); err != nil {
|
||||
@@ -521,11 +525,10 @@ func (p *parseState) estimateCommand() error {
|
||||
|
||||
func (p *Parser) parseOption(s *parseState, name string, option *Option, canarg bool, argument *string) (err error) {
|
||||
if !option.canArgument() {
|
||||
if argument != nil {
|
||||
if argument != nil && (p.Options&AllowBoolValues) == None {
|
||||
return newErrorf(ErrNoArgumentForBool, "bool flag `%s' cannot have an argument", option)
|
||||
}
|
||||
|
||||
err = option.set(nil)
|
||||
err = option.Set(argument)
|
||||
} else if argument != nil || (canarg && !s.eof()) {
|
||||
var arg string
|
||||
|
||||
@@ -546,13 +549,13 @@ func (p *Parser) parseOption(s *parseState, name string, option *Option, canarg
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
err = option.set(&arg)
|
||||
err = option.Set(&arg)
|
||||
}
|
||||
} else if option.OptionalArgument {
|
||||
option.empty()
|
||||
|
||||
for _, v := range option.OptionalValue {
|
||||
err = option.set(&v)
|
||||
err = option.Set(&v)
|
||||
|
||||
if err != nil {
|
||||
break
|
||||
|
||||
3
vendor/github.com/jessevdk/go-flags/termsize.go
generated
vendored
3
vendor/github.com/jessevdk/go-flags/termsize.go
generated
vendored
@@ -1,4 +1,5 @@
|
||||
// +build !windows,!plan9,!appengine,!wasm
|
||||
//go:build !windows && !plan9 && !appengine && !wasm && !aix
|
||||
// +build !windows,!plan9,!appengine,!wasm,!aix
|
||||
|
||||
package flags
|
||||
|
||||
|
||||
3
vendor/github.com/jessevdk/go-flags/termsize_nosysioctl.go
generated
vendored
3
vendor/github.com/jessevdk/go-flags/termsize_nosysioctl.go
generated
vendored
@@ -1,4 +1,5 @@
|
||||
// +build plan9 appengine wasm
|
||||
//go:build plan9 || appengine || wasm || aix
|
||||
// +build plan9 appengine wasm aix
|
||||
|
||||
package flags
|
||||
|
||||
|
||||
1
vendor/github.com/jessevdk/go-flags/termsize_windows.go
generated
vendored
1
vendor/github.com/jessevdk/go-flags/termsize_windows.go
generated
vendored
@@ -1,3 +1,4 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package flags
|
||||
|
||||
201
vendor/github.com/matttproud/golang_protobuf_extensions/LICENSE
generated
vendored
201
vendor/github.com/matttproud/golang_protobuf_extensions/LICENSE
generated
vendored
@@ -1,201 +0,0 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
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 "{}"
|
||||
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
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
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.
|
||||
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.
|
||||
1
vendor/github.com/matttproud/golang_protobuf_extensions/NOTICE
generated
vendored
1
vendor/github.com/matttproud/golang_protobuf_extensions/NOTICE
generated
vendored
@@ -1 +0,0 @@
|
||||
Copyright 2012 Matt T. Proud (matt.proud@gmail.com)
|
||||
1
vendor/github.com/matttproud/golang_protobuf_extensions/pbutil/.gitignore
generated
vendored
1
vendor/github.com/matttproud/golang_protobuf_extensions/pbutil/.gitignore
generated
vendored
@@ -1 +0,0 @@
|
||||
cover.dat
|
||||
7
vendor/github.com/matttproud/golang_protobuf_extensions/pbutil/Makefile
generated
vendored
7
vendor/github.com/matttproud/golang_protobuf_extensions/pbutil/Makefile
generated
vendored
@@ -1,7 +0,0 @@
|
||||
all:
|
||||
|
||||
cover:
|
||||
go test -cover -v -coverprofile=cover.dat ./...
|
||||
go tool cover -func cover.dat
|
||||
|
||||
.PHONY: cover
|
||||
75
vendor/github.com/matttproud/golang_protobuf_extensions/pbutil/decode.go
generated
vendored
75
vendor/github.com/matttproud/golang_protobuf_extensions/pbutil/decode.go
generated
vendored
@@ -1,75 +0,0 @@
|
||||
// Copyright 2013 Matt T. Proud
|
||||
//
|
||||
// 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 pbutil
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
)
|
||||
|
||||
var errInvalidVarint = errors.New("invalid varint32 encountered")
|
||||
|
||||
// ReadDelimited decodes a message from the provided length-delimited stream,
|
||||
// where the length is encoded as 32-bit varint prefix to the message body.
|
||||
// It returns the total number of bytes read and any applicable error. This is
|
||||
// roughly equivalent to the companion Java API's
|
||||
// MessageLite#parseDelimitedFrom. As per the reader contract, this function
|
||||
// calls r.Read repeatedly as required until exactly one message including its
|
||||
// prefix is read and decoded (or an error has occurred). The function never
|
||||
// reads more bytes from the stream than required. The function never returns
|
||||
// an error if a message has been read and decoded correctly, even if the end
|
||||
// of the stream has been reached in doing so. In that case, any subsequent
|
||||
// calls return (0, io.EOF).
|
||||
func ReadDelimited(r io.Reader, m proto.Message) (n int, err error) {
|
||||
// Per AbstractParser#parsePartialDelimitedFrom with
|
||||
// CodedInputStream#readRawVarint32.
|
||||
var headerBuf [binary.MaxVarintLen32]byte
|
||||
var bytesRead, varIntBytes int
|
||||
var messageLength uint64
|
||||
for varIntBytes == 0 { // i.e. no varint has been decoded yet.
|
||||
if bytesRead >= len(headerBuf) {
|
||||
return bytesRead, errInvalidVarint
|
||||
}
|
||||
// We have to read byte by byte here to avoid reading more bytes
|
||||
// than required. Each read byte is appended to what we have
|
||||
// read before.
|
||||
newBytesRead, err := r.Read(headerBuf[bytesRead : bytesRead+1])
|
||||
if newBytesRead == 0 {
|
||||
if err != nil {
|
||||
return bytesRead, err
|
||||
}
|
||||
// A Reader should not return (0, nil), but if it does,
|
||||
// it should be treated as no-op (according to the
|
||||
// Reader contract). So let's go on...
|
||||
continue
|
||||
}
|
||||
bytesRead += newBytesRead
|
||||
// Now present everything read so far to the varint decoder and
|
||||
// see if a varint can be decoded already.
|
||||
messageLength, varIntBytes = proto.DecodeVarint(headerBuf[:bytesRead])
|
||||
}
|
||||
|
||||
messageBuf := make([]byte, messageLength)
|
||||
newBytesRead, err := io.ReadFull(r, messageBuf)
|
||||
bytesRead += newBytesRead
|
||||
if err != nil {
|
||||
return bytesRead, err
|
||||
}
|
||||
|
||||
return bytesRead, proto.Unmarshal(messageBuf, m)
|
||||
}
|
||||
46
vendor/github.com/matttproud/golang_protobuf_extensions/pbutil/encode.go
generated
vendored
46
vendor/github.com/matttproud/golang_protobuf_extensions/pbutil/encode.go
generated
vendored
@@ -1,46 +0,0 @@
|
||||
// Copyright 2013 Matt T. Proud
|
||||
//
|
||||
// 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 pbutil
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
)
|
||||
|
||||
// WriteDelimited encodes and dumps a message to the provided writer prefixed
|
||||
// with a 32-bit varint indicating the length of the encoded message, producing
|
||||
// a length-delimited record stream, which can be used to chain together
|
||||
// encoded messages of the same type together in a file. It returns the total
|
||||
// number of bytes written and any applicable error. This is roughly
|
||||
// equivalent to the companion Java API's MessageLite#writeDelimitedTo.
|
||||
func WriteDelimited(w io.Writer, m proto.Message) (n int, err error) {
|
||||
buffer, err := proto.Marshal(m)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var buf [binary.MaxVarintLen32]byte
|
||||
encodedLength := binary.PutUvarint(buf[:], uint64(len(buffer)))
|
||||
|
||||
sync, err := w.Write(buf[:encodedLength])
|
||||
if err != nil {
|
||||
return sync, err
|
||||
}
|
||||
|
||||
n, err = w.Write(buffer)
|
||||
return n + sync, err
|
||||
}
|
||||
5
vendor/github.com/prometheus/client_golang/NOTICE
generated
vendored
5
vendor/github.com/prometheus/client_golang/NOTICE
generated
vendored
@@ -16,8 +16,3 @@ Go support for Protocol Buffers - Google's data interchange format
|
||||
http://github.com/golang/protobuf/
|
||||
Copyright 2010 The Go Authors
|
||||
See source code for license details.
|
||||
|
||||
Support for streaming Protocol Buffer messages for the Go language (golang).
|
||||
https://github.com/matttproud/golang_protobuf_extensions
|
||||
Copyright 2013 Matt T. Proud
|
||||
Licensed under the Apache License, Version 2.0
|
||||
|
||||
48
vendor/github.com/prometheus/client_golang/prometheus/counter.go
generated
vendored
48
vendor/github.com/prometheus/client_golang/prometheus/counter.go
generated
vendored
@@ -20,6 +20,7 @@ import (
|
||||
"time"
|
||||
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
// Counter is a Metric that represents a single numerical value that only ever
|
||||
@@ -59,6 +60,18 @@ type ExemplarAdder interface {
|
||||
// CounterOpts is an alias for Opts. See there for doc comments.
|
||||
type CounterOpts Opts
|
||||
|
||||
// CounterVecOpts bundles the options to create a CounterVec metric.
|
||||
// It is mandatory to set CounterOpts, see there for mandatory fields. VariableLabels
|
||||
// is optional and can safely be left to its default value.
|
||||
type CounterVecOpts struct {
|
||||
CounterOpts
|
||||
|
||||
// VariableLabels are used to partition the metric vector by the given set
|
||||
// of labels. Each label value will be constrained with the optional Constraint
|
||||
// function, if provided.
|
||||
VariableLabels ConstrainableLabels
|
||||
}
|
||||
|
||||
// NewCounter creates a new Counter based on the provided CounterOpts.
|
||||
//
|
||||
// The returned implementation also implements ExemplarAdder. It is safe to
|
||||
@@ -78,8 +91,12 @@ func NewCounter(opts CounterOpts) Counter {
|
||||
nil,
|
||||
opts.ConstLabels,
|
||||
)
|
||||
result := &counter{desc: desc, labelPairs: desc.constLabelPairs, now: time.Now}
|
||||
if opts.now == nil {
|
||||
opts.now = time.Now
|
||||
}
|
||||
result := &counter{desc: desc, labelPairs: desc.constLabelPairs, now: opts.now}
|
||||
result.init(result) // Init self-collection.
|
||||
result.createdTs = timestamppb.New(opts.now())
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -94,10 +111,12 @@ type counter struct {
|
||||
selfCollector
|
||||
desc *Desc
|
||||
|
||||
createdTs *timestamppb.Timestamp
|
||||
labelPairs []*dto.LabelPair
|
||||
exemplar atomic.Value // Containing nil or a *dto.Exemplar.
|
||||
|
||||
now func() time.Time // To mock out time.Now() for testing.
|
||||
// now is for testing purposes, by default it's time.Now.
|
||||
now func() time.Time
|
||||
}
|
||||
|
||||
func (c *counter) Desc() *Desc {
|
||||
@@ -147,8 +166,7 @@ func (c *counter) Write(out *dto.Metric) error {
|
||||
exemplar = e.(*dto.Exemplar)
|
||||
}
|
||||
val := c.get()
|
||||
|
||||
return populateMetric(CounterValue, val, c.labelPairs, exemplar, out)
|
||||
return populateMetric(CounterValue, val, c.labelPairs, exemplar, out, c.createdTs)
|
||||
}
|
||||
|
||||
func (c *counter) updateExemplar(v float64, l Labels) {
|
||||
@@ -174,19 +192,31 @@ type CounterVec struct {
|
||||
// NewCounterVec creates a new CounterVec based on the provided CounterOpts and
|
||||
// partitioned by the given label names.
|
||||
func NewCounterVec(opts CounterOpts, labelNames []string) *CounterVec {
|
||||
desc := NewDesc(
|
||||
return V2.NewCounterVec(CounterVecOpts{
|
||||
CounterOpts: opts,
|
||||
VariableLabels: UnconstrainedLabels(labelNames),
|
||||
})
|
||||
}
|
||||
|
||||
// NewCounterVec creates a new CounterVec based on the provided CounterVecOpts.
|
||||
func (v2) NewCounterVec(opts CounterVecOpts) *CounterVec {
|
||||
desc := V2.NewDesc(
|
||||
BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
|
||||
opts.Help,
|
||||
labelNames,
|
||||
opts.VariableLabels,
|
||||
opts.ConstLabels,
|
||||
)
|
||||
if opts.now == nil {
|
||||
opts.now = time.Now
|
||||
}
|
||||
return &CounterVec{
|
||||
MetricVec: NewMetricVec(desc, func(lvs ...string) Metric {
|
||||
if len(lvs) != len(desc.variableLabels) {
|
||||
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels, lvs))
|
||||
if len(lvs) != len(desc.variableLabels.names) {
|
||||
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels.names, lvs))
|
||||
}
|
||||
result := &counter{desc: desc, labelPairs: MakeLabelPairs(desc, lvs), now: time.Now}
|
||||
result := &counter{desc: desc, labelPairs: MakeLabelPairs(desc, lvs), now: opts.now}
|
||||
result.init(result) // Init self-collection.
|
||||
result.createdTs = timestamppb.New(opts.now())
|
||||
return result
|
||||
}),
|
||||
}
|
||||
|
||||
64
vendor/github.com/prometheus/client_golang/prometheus/desc.go
generated
vendored
64
vendor/github.com/prometheus/client_golang/prometheus/desc.go
generated
vendored
@@ -14,20 +14,16 @@
|
||||
package prometheus
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/cespare/xxhash/v2"
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
"github.com/prometheus/common/model"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus/internal"
|
||||
|
||||
//nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility.
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/prometheus/common/model"
|
||||
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
)
|
||||
|
||||
// Desc is the descriptor used by every Prometheus Metric. It is essentially
|
||||
@@ -54,9 +50,9 @@ type Desc struct {
|
||||
// constLabelPairs contains precalculated DTO label pairs based on
|
||||
// the constant labels.
|
||||
constLabelPairs []*dto.LabelPair
|
||||
// variableLabels contains names of labels for which the metric
|
||||
// maintains variable values.
|
||||
variableLabels []string
|
||||
// variableLabels contains names of labels and normalization function for
|
||||
// which the metric maintains variable values.
|
||||
variableLabels *compiledLabels
|
||||
// id is a hash of the values of the ConstLabels and fqName. This
|
||||
// must be unique among all registered descriptors and can therefore be
|
||||
// used as an identifier of the descriptor.
|
||||
@@ -80,12 +76,27 @@ type Desc struct {
|
||||
// For constLabels, the label values are constant. Therefore, they are fully
|
||||
// specified in the Desc. See the Collector example for a usage pattern.
|
||||
func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) *Desc {
|
||||
return V2.NewDesc(fqName, help, UnconstrainedLabels(variableLabels), constLabels)
|
||||
}
|
||||
|
||||
// NewDesc allocates and initializes a new Desc. Errors are recorded in the Desc
|
||||
// and will be reported on registration time. variableLabels and constLabels can
|
||||
// be nil if no such labels should be set. fqName must not be empty.
|
||||
//
|
||||
// variableLabels only contain the label names and normalization functions. Their
|
||||
// label values are variable and therefore not part of the Desc. (They are managed
|
||||
// within the Metric.)
|
||||
//
|
||||
// For constLabels, the label values are constant. Therefore, they are fully
|
||||
// specified in the Desc. See the Collector example for a usage pattern.
|
||||
func (v2) NewDesc(fqName, help string, variableLabels ConstrainableLabels, constLabels Labels) *Desc {
|
||||
d := &Desc{
|
||||
fqName: fqName,
|
||||
help: help,
|
||||
variableLabels: variableLabels,
|
||||
variableLabels: variableLabels.compile(),
|
||||
}
|
||||
if !model.IsValidMetricName(model.LabelValue(fqName)) {
|
||||
//nolint:staticcheck // TODO: Don't use deprecated model.NameValidationScheme.
|
||||
if !model.NameValidationScheme.IsValidMetricName(fqName) {
|
||||
d.err = fmt.Errorf("%q is not a valid metric name", fqName)
|
||||
return d
|
||||
}
|
||||
@@ -93,7 +104,7 @@ func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) *
|
||||
// their sorted label names) plus the fqName (at position 0).
|
||||
labelValues := make([]string, 1, len(constLabels)+1)
|
||||
labelValues[0] = fqName
|
||||
labelNames := make([]string, 0, len(constLabels)+len(variableLabels))
|
||||
labelNames := make([]string, 0, len(constLabels)+len(d.variableLabels.names))
|
||||
labelNameSet := map[string]struct{}{}
|
||||
// First add only the const label names and sort them...
|
||||
for labelName := range constLabels {
|
||||
@@ -118,16 +129,16 @@ func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) *
|
||||
// Now add the variable label names, but prefix them with something that
|
||||
// cannot be in a regular label name. That prevents matching the label
|
||||
// dimension with a different mix between preset and variable labels.
|
||||
for _, labelName := range variableLabels {
|
||||
if !checkLabelName(labelName) {
|
||||
d.err = fmt.Errorf("%q is not a valid label name for metric %q", labelName, fqName)
|
||||
for _, label := range d.variableLabels.names {
|
||||
if !checkLabelName(label) {
|
||||
d.err = fmt.Errorf("%q is not a valid label name for metric %q", label, fqName)
|
||||
return d
|
||||
}
|
||||
labelNames = append(labelNames, "$"+labelName)
|
||||
labelNameSet[labelName] = struct{}{}
|
||||
labelNames = append(labelNames, "$"+label)
|
||||
labelNameSet[label] = struct{}{}
|
||||
}
|
||||
if len(labelNames) != len(labelNameSet) {
|
||||
d.err = errors.New("duplicate label names")
|
||||
d.err = fmt.Errorf("duplicate label names in constant and variable labels for metric %q", fqName)
|
||||
return d
|
||||
}
|
||||
|
||||
@@ -179,11 +190,22 @@ func (d *Desc) String() string {
|
||||
fmt.Sprintf("%s=%q", lp.GetName(), lp.GetValue()),
|
||||
)
|
||||
}
|
||||
vlStrings := []string{}
|
||||
if d.variableLabels != nil {
|
||||
vlStrings = make([]string, 0, len(d.variableLabels.names))
|
||||
for _, vl := range d.variableLabels.names {
|
||||
if fn, ok := d.variableLabels.labelConstraints[vl]; ok && fn != nil {
|
||||
vlStrings = append(vlStrings, fmt.Sprintf("c(%s)", vl))
|
||||
} else {
|
||||
vlStrings = append(vlStrings, vl)
|
||||
}
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf(
|
||||
"Desc{fqName: %q, help: %q, constLabels: {%s}, variableLabels: %v}",
|
||||
"Desc{fqName: %q, help: %q, constLabels: {%s}, variableLabels: {%s}}",
|
||||
d.fqName,
|
||||
d.help,
|
||||
strings.Join(lpStrings, ","),
|
||||
d.variableLabels,
|
||||
strings.Join(vlStrings, ","),
|
||||
)
|
||||
}
|
||||
|
||||
44
vendor/github.com/prometheus/client_golang/prometheus/doc.go
generated
vendored
44
vendor/github.com/prometheus/client_golang/prometheus/doc.go
generated
vendored
@@ -37,35 +37,35 @@
|
||||
//
|
||||
// type metrics struct {
|
||||
// cpuTemp prometheus.Gauge
|
||||
// hdFailures *prometheus.CounterVec
|
||||
// hdFailures *prometheus.CounterVec
|
||||
// }
|
||||
//
|
||||
// func NewMetrics(reg prometheus.Registerer) *metrics {
|
||||
// m := &metrics{
|
||||
// cpuTemp: prometheus.NewGauge(prometheus.GaugeOpts{
|
||||
// Name: "cpu_temperature_celsius",
|
||||
// Help: "Current temperature of the CPU.",
|
||||
// }),
|
||||
// hdFailures: prometheus.NewCounterVec(
|
||||
// prometheus.CounterOpts{
|
||||
// Name: "hd_errors_total",
|
||||
// Help: "Number of hard-disk errors.",
|
||||
// },
|
||||
// []string{"device"},
|
||||
// ),
|
||||
// }
|
||||
// reg.MustRegister(m.cpuTemp)
|
||||
// reg.MustRegister(m.hdFailures)
|
||||
// return m
|
||||
// m := &metrics{
|
||||
// cpuTemp: prometheus.NewGauge(prometheus.GaugeOpts{
|
||||
// Name: "cpu_temperature_celsius",
|
||||
// Help: "Current temperature of the CPU.",
|
||||
// }),
|
||||
// hdFailures: prometheus.NewCounterVec(
|
||||
// prometheus.CounterOpts{
|
||||
// Name: "hd_errors_total",
|
||||
// Help: "Number of hard-disk errors.",
|
||||
// },
|
||||
// []string{"device"},
|
||||
// ),
|
||||
// }
|
||||
// reg.MustRegister(m.cpuTemp)
|
||||
// reg.MustRegister(m.hdFailures)
|
||||
// return m
|
||||
// }
|
||||
//
|
||||
// func main() {
|
||||
// // Create a non-global registry.
|
||||
// reg := prometheus.NewRegistry()
|
||||
// // Create a non-global registry.
|
||||
// reg := prometheus.NewRegistry()
|
||||
//
|
||||
// // Create new metrics and register them using the custom registry.
|
||||
// m := NewMetrics(reg)
|
||||
// // Set values for the new created metrics.
|
||||
// // Create new metrics and register them using the custom registry.
|
||||
// m := NewMetrics(reg)
|
||||
// // Set values for the new created metrics.
|
||||
// m.cpuTemp.Set(65.3)
|
||||
// m.hdFailures.With(prometheus.Labels{"device":"/dev/sda"}).Inc()
|
||||
//
|
||||
|
||||
2
vendor/github.com/prometheus/client_golang/prometheus/expvar_collector.go
generated
vendored
2
vendor/github.com/prometheus/client_golang/prometheus/expvar_collector.go
generated
vendored
@@ -48,7 +48,7 @@ func (e *expvarCollector) Collect(ch chan<- Metric) {
|
||||
continue
|
||||
}
|
||||
var v interface{}
|
||||
labels := make([]string, len(desc.variableLabels))
|
||||
labels := make([]string, len(desc.variableLabels.names))
|
||||
if err := json.Unmarshal([]byte(expVar.String()), &v); err != nil {
|
||||
ch <- NewInvalidMetric(desc, err)
|
||||
continue
|
||||
|
||||
30
vendor/github.com/prometheus/client_golang/prometheus/gauge.go
generated
vendored
30
vendor/github.com/prometheus/client_golang/prometheus/gauge.go
generated
vendored
@@ -55,6 +55,18 @@ type Gauge interface {
|
||||
// GaugeOpts is an alias for Opts. See there for doc comments.
|
||||
type GaugeOpts Opts
|
||||
|
||||
// GaugeVecOpts bundles the options to create a GaugeVec metric.
|
||||
// It is mandatory to set GaugeOpts, see there for mandatory fields. VariableLabels
|
||||
// is optional and can safely be left to its default value.
|
||||
type GaugeVecOpts struct {
|
||||
GaugeOpts
|
||||
|
||||
// VariableLabels are used to partition the metric vector by the given set
|
||||
// of labels. Each label value will be constrained with the optional Constraint
|
||||
// function, if provided.
|
||||
VariableLabels ConstrainableLabels
|
||||
}
|
||||
|
||||
// NewGauge creates a new Gauge based on the provided GaugeOpts.
|
||||
//
|
||||
// The returned implementation is optimized for a fast Set method. If you have a
|
||||
@@ -123,7 +135,7 @@ func (g *gauge) Sub(val float64) {
|
||||
|
||||
func (g *gauge) Write(out *dto.Metric) error {
|
||||
val := math.Float64frombits(atomic.LoadUint64(&g.valBits))
|
||||
return populateMetric(GaugeValue, val, g.labelPairs, nil, out)
|
||||
return populateMetric(GaugeValue, val, g.labelPairs, nil, out, nil)
|
||||
}
|
||||
|
||||
// GaugeVec is a Collector that bundles a set of Gauges that all share the same
|
||||
@@ -138,16 +150,24 @@ type GaugeVec struct {
|
||||
// NewGaugeVec creates a new GaugeVec based on the provided GaugeOpts and
|
||||
// partitioned by the given label names.
|
||||
func NewGaugeVec(opts GaugeOpts, labelNames []string) *GaugeVec {
|
||||
desc := NewDesc(
|
||||
return V2.NewGaugeVec(GaugeVecOpts{
|
||||
GaugeOpts: opts,
|
||||
VariableLabels: UnconstrainedLabels(labelNames),
|
||||
})
|
||||
}
|
||||
|
||||
// NewGaugeVec creates a new GaugeVec based on the provided GaugeVecOpts.
|
||||
func (v2) NewGaugeVec(opts GaugeVecOpts) *GaugeVec {
|
||||
desc := V2.NewDesc(
|
||||
BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
|
||||
opts.Help,
|
||||
labelNames,
|
||||
opts.VariableLabels,
|
||||
opts.ConstLabels,
|
||||
)
|
||||
return &GaugeVec{
|
||||
MetricVec: NewMetricVec(desc, func(lvs ...string) Metric {
|
||||
if len(lvs) != len(desc.variableLabels) {
|
||||
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels, lvs))
|
||||
if len(lvs) != len(desc.variableLabels.names) {
|
||||
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels.names, lvs))
|
||||
}
|
||||
result := &gauge{desc: desc, labelPairs: MakeLabelPairs(desc, lvs)}
|
||||
result.init(result) // Init self-collection.
|
||||
|
||||
55
vendor/github.com/prometheus/client_golang/prometheus/go_collector.go
generated
vendored
55
vendor/github.com/prometheus/client_golang/prometheus/go_collector.go
generated
vendored
@@ -22,13 +22,13 @@ import (
|
||||
// goRuntimeMemStats provides the metrics initially provided by runtime.ReadMemStats.
|
||||
// From Go 1.17 those similar (and better) statistics are provided by runtime/metrics, so
|
||||
// while eval closure works on runtime.MemStats, the struct from Go 1.17+ is
|
||||
// populated using runtime/metrics.
|
||||
// populated using runtime/metrics. Those are the defaults we can't alter.
|
||||
func goRuntimeMemStats() memStatsMetrics {
|
||||
return memStatsMetrics{
|
||||
{
|
||||
desc: NewDesc(
|
||||
memstatNamespace("alloc_bytes"),
|
||||
"Number of bytes allocated and still in use.",
|
||||
"Number of bytes allocated in heap and currently in use. Equals to /memory/classes/heap/objects:bytes.",
|
||||
nil, nil,
|
||||
),
|
||||
eval: func(ms *runtime.MemStats) float64 { return float64(ms.Alloc) },
|
||||
@@ -36,7 +36,7 @@ func goRuntimeMemStats() memStatsMetrics {
|
||||
}, {
|
||||
desc: NewDesc(
|
||||
memstatNamespace("alloc_bytes_total"),
|
||||
"Total number of bytes allocated, even if freed.",
|
||||
"Total number of bytes allocated in heap until now, even if released already. Equals to /gc/heap/allocs:bytes.",
|
||||
nil, nil,
|
||||
),
|
||||
eval: func(ms *runtime.MemStats) float64 { return float64(ms.TotalAlloc) },
|
||||
@@ -44,23 +44,16 @@ func goRuntimeMemStats() memStatsMetrics {
|
||||
}, {
|
||||
desc: NewDesc(
|
||||
memstatNamespace("sys_bytes"),
|
||||
"Number of bytes obtained from system.",
|
||||
"Number of bytes obtained from system. Equals to /memory/classes/total:byte.",
|
||||
nil, nil,
|
||||
),
|
||||
eval: func(ms *runtime.MemStats) float64 { return float64(ms.Sys) },
|
||||
valType: GaugeValue,
|
||||
}, {
|
||||
desc: NewDesc(
|
||||
memstatNamespace("lookups_total"),
|
||||
"Total number of pointer lookups.",
|
||||
nil, nil,
|
||||
),
|
||||
eval: func(ms *runtime.MemStats) float64 { return float64(ms.Lookups) },
|
||||
valType: CounterValue,
|
||||
}, {
|
||||
desc: NewDesc(
|
||||
memstatNamespace("mallocs_total"),
|
||||
"Total number of mallocs.",
|
||||
// TODO(bwplotka): We could add go_memstats_heap_objects, probably useful for discovery. Let's gather more feedback, kind of a waste of bytes for everybody for compatibility reasons to keep both, and we can't really rename/remove useful metric.
|
||||
"Total number of heap objects allocated, both live and gc-ed. Semantically a counter version for go_memstats_heap_objects gauge. Equals to /gc/heap/allocs:objects + /gc/heap/tiny/allocs:objects.",
|
||||
nil, nil,
|
||||
),
|
||||
eval: func(ms *runtime.MemStats) float64 { return float64(ms.Mallocs) },
|
||||
@@ -68,7 +61,7 @@ func goRuntimeMemStats() memStatsMetrics {
|
||||
}, {
|
||||
desc: NewDesc(
|
||||
memstatNamespace("frees_total"),
|
||||
"Total number of frees.",
|
||||
"Total number of heap objects frees. Equals to /gc/heap/frees:objects + /gc/heap/tiny/allocs:objects.",
|
||||
nil, nil,
|
||||
),
|
||||
eval: func(ms *runtime.MemStats) float64 { return float64(ms.Frees) },
|
||||
@@ -76,7 +69,7 @@ func goRuntimeMemStats() memStatsMetrics {
|
||||
}, {
|
||||
desc: NewDesc(
|
||||
memstatNamespace("heap_alloc_bytes"),
|
||||
"Number of heap bytes allocated and still in use.",
|
||||
"Number of heap bytes allocated and currently in use, same as go_memstats_alloc_bytes. Equals to /memory/classes/heap/objects:bytes.",
|
||||
nil, nil,
|
||||
),
|
||||
eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapAlloc) },
|
||||
@@ -84,7 +77,7 @@ func goRuntimeMemStats() memStatsMetrics {
|
||||
}, {
|
||||
desc: NewDesc(
|
||||
memstatNamespace("heap_sys_bytes"),
|
||||
"Number of heap bytes obtained from system.",
|
||||
"Number of heap bytes obtained from system. Equals to /memory/classes/heap/objects:bytes + /memory/classes/heap/unused:bytes + /memory/classes/heap/released:bytes + /memory/classes/heap/free:bytes.",
|
||||
nil, nil,
|
||||
),
|
||||
eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapSys) },
|
||||
@@ -92,7 +85,7 @@ func goRuntimeMemStats() memStatsMetrics {
|
||||
}, {
|
||||
desc: NewDesc(
|
||||
memstatNamespace("heap_idle_bytes"),
|
||||
"Number of heap bytes waiting to be used.",
|
||||
"Number of heap bytes waiting to be used. Equals to /memory/classes/heap/released:bytes + /memory/classes/heap/free:bytes.",
|
||||
nil, nil,
|
||||
),
|
||||
eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapIdle) },
|
||||
@@ -100,7 +93,7 @@ func goRuntimeMemStats() memStatsMetrics {
|
||||
}, {
|
||||
desc: NewDesc(
|
||||
memstatNamespace("heap_inuse_bytes"),
|
||||
"Number of heap bytes that are in use.",
|
||||
"Number of heap bytes that are in use. Equals to /memory/classes/heap/objects:bytes + /memory/classes/heap/unused:bytes",
|
||||
nil, nil,
|
||||
),
|
||||
eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapInuse) },
|
||||
@@ -108,7 +101,7 @@ func goRuntimeMemStats() memStatsMetrics {
|
||||
}, {
|
||||
desc: NewDesc(
|
||||
memstatNamespace("heap_released_bytes"),
|
||||
"Number of heap bytes released to OS.",
|
||||
"Number of heap bytes released to OS. Equals to /memory/classes/heap/released:bytes.",
|
||||
nil, nil,
|
||||
),
|
||||
eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapReleased) },
|
||||
@@ -116,7 +109,7 @@ func goRuntimeMemStats() memStatsMetrics {
|
||||
}, {
|
||||
desc: NewDesc(
|
||||
memstatNamespace("heap_objects"),
|
||||
"Number of allocated objects.",
|
||||
"Number of currently allocated objects. Equals to /gc/heap/objects:objects.",
|
||||
nil, nil,
|
||||
),
|
||||
eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapObjects) },
|
||||
@@ -124,7 +117,7 @@ func goRuntimeMemStats() memStatsMetrics {
|
||||
}, {
|
||||
desc: NewDesc(
|
||||
memstatNamespace("stack_inuse_bytes"),
|
||||
"Number of bytes in use by the stack allocator.",
|
||||
"Number of bytes obtained from system for stack allocator in non-CGO environments. Equals to /memory/classes/heap/stacks:bytes.",
|
||||
nil, nil,
|
||||
),
|
||||
eval: func(ms *runtime.MemStats) float64 { return float64(ms.StackInuse) },
|
||||
@@ -132,7 +125,7 @@ func goRuntimeMemStats() memStatsMetrics {
|
||||
}, {
|
||||
desc: NewDesc(
|
||||
memstatNamespace("stack_sys_bytes"),
|
||||
"Number of bytes obtained from system for stack allocator.",
|
||||
"Number of bytes obtained from system for stack allocator. Equals to /memory/classes/heap/stacks:bytes + /memory/classes/os-stacks:bytes.",
|
||||
nil, nil,
|
||||
),
|
||||
eval: func(ms *runtime.MemStats) float64 { return float64(ms.StackSys) },
|
||||
@@ -140,7 +133,7 @@ func goRuntimeMemStats() memStatsMetrics {
|
||||
}, {
|
||||
desc: NewDesc(
|
||||
memstatNamespace("mspan_inuse_bytes"),
|
||||
"Number of bytes in use by mspan structures.",
|
||||
"Number of bytes in use by mspan structures. Equals to /memory/classes/metadata/mspan/inuse:bytes.",
|
||||
nil, nil,
|
||||
),
|
||||
eval: func(ms *runtime.MemStats) float64 { return float64(ms.MSpanInuse) },
|
||||
@@ -148,7 +141,7 @@ func goRuntimeMemStats() memStatsMetrics {
|
||||
}, {
|
||||
desc: NewDesc(
|
||||
memstatNamespace("mspan_sys_bytes"),
|
||||
"Number of bytes used for mspan structures obtained from system.",
|
||||
"Number of bytes used for mspan structures obtained from system. Equals to /memory/classes/metadata/mspan/inuse:bytes + /memory/classes/metadata/mspan/free:bytes.",
|
||||
nil, nil,
|
||||
),
|
||||
eval: func(ms *runtime.MemStats) float64 { return float64(ms.MSpanSys) },
|
||||
@@ -156,7 +149,7 @@ func goRuntimeMemStats() memStatsMetrics {
|
||||
}, {
|
||||
desc: NewDesc(
|
||||
memstatNamespace("mcache_inuse_bytes"),
|
||||
"Number of bytes in use by mcache structures.",
|
||||
"Number of bytes in use by mcache structures. Equals to /memory/classes/metadata/mcache/inuse:bytes.",
|
||||
nil, nil,
|
||||
),
|
||||
eval: func(ms *runtime.MemStats) float64 { return float64(ms.MCacheInuse) },
|
||||
@@ -164,7 +157,7 @@ func goRuntimeMemStats() memStatsMetrics {
|
||||
}, {
|
||||
desc: NewDesc(
|
||||
memstatNamespace("mcache_sys_bytes"),
|
||||
"Number of bytes used for mcache structures obtained from system.",
|
||||
"Number of bytes used for mcache structures obtained from system. Equals to /memory/classes/metadata/mcache/inuse:bytes + /memory/classes/metadata/mcache/free:bytes.",
|
||||
nil, nil,
|
||||
),
|
||||
eval: func(ms *runtime.MemStats) float64 { return float64(ms.MCacheSys) },
|
||||
@@ -172,7 +165,7 @@ func goRuntimeMemStats() memStatsMetrics {
|
||||
}, {
|
||||
desc: NewDesc(
|
||||
memstatNamespace("buck_hash_sys_bytes"),
|
||||
"Number of bytes used by the profiling bucket hash table.",
|
||||
"Number of bytes used by the profiling bucket hash table. Equals to /memory/classes/profiling/buckets:bytes.",
|
||||
nil, nil,
|
||||
),
|
||||
eval: func(ms *runtime.MemStats) float64 { return float64(ms.BuckHashSys) },
|
||||
@@ -180,7 +173,7 @@ func goRuntimeMemStats() memStatsMetrics {
|
||||
}, {
|
||||
desc: NewDesc(
|
||||
memstatNamespace("gc_sys_bytes"),
|
||||
"Number of bytes used for garbage collection system metadata.",
|
||||
"Number of bytes used for garbage collection system metadata. Equals to /memory/classes/metadata/other:bytes.",
|
||||
nil, nil,
|
||||
),
|
||||
eval: func(ms *runtime.MemStats) float64 { return float64(ms.GCSys) },
|
||||
@@ -188,7 +181,7 @@ func goRuntimeMemStats() memStatsMetrics {
|
||||
}, {
|
||||
desc: NewDesc(
|
||||
memstatNamespace("other_sys_bytes"),
|
||||
"Number of bytes used for other system allocations.",
|
||||
"Number of bytes used for other system allocations. Equals to /memory/classes/other:bytes.",
|
||||
nil, nil,
|
||||
),
|
||||
eval: func(ms *runtime.MemStats) float64 { return float64(ms.OtherSys) },
|
||||
@@ -196,7 +189,7 @@ func goRuntimeMemStats() memStatsMetrics {
|
||||
}, {
|
||||
desc: NewDesc(
|
||||
memstatNamespace("next_gc_bytes"),
|
||||
"Number of heap bytes when next garbage collection will take place.",
|
||||
"Number of heap bytes when next garbage collection will take place. Equals to /gc/heap/goal:bytes.",
|
||||
nil, nil,
|
||||
),
|
||||
eval: func(ms *runtime.MemStats) float64 { return float64(ms.NextGC) },
|
||||
@@ -225,7 +218,7 @@ func newBaseGoCollector() baseGoCollector {
|
||||
nil, nil),
|
||||
gcDesc: NewDesc(
|
||||
"go_gc_duration_seconds",
|
||||
"A summary of the pause duration of garbage collection cycles.",
|
||||
"A summary of the wall-time pause (stop-the-world) duration in garbage collection cycles.",
|
||||
nil, nil),
|
||||
gcLastTimeDesc: NewDesc(
|
||||
"go_memstats_last_gc_time_seconds",
|
||||
|
||||
26
vendor/github.com/prometheus/client_golang/prometheus/go_collector_latest.go
generated
vendored
26
vendor/github.com/prometheus/client_golang/prometheus/go_collector_latest.go
generated
vendored
@@ -17,17 +17,17 @@
|
||||
package prometheus
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"runtime"
|
||||
"runtime/metrics"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
//nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility.
|
||||
"github.com/golang/protobuf/proto"
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus/internal"
|
||||
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -154,7 +154,8 @@ func defaultGoCollectorOptions() internal.GoCollectorOptions {
|
||||
"/gc/heap/frees-by-size:bytes": goGCHeapFreesBytes,
|
||||
},
|
||||
RuntimeMetricRules: []internal.GoCollectorRule{
|
||||
//{Matcher: regexp.MustCompile("")},
|
||||
// Recommended metrics we want by default from runtime/metrics.
|
||||
{Matcher: internal.GoCollectorDefaultRuntimeMetrics},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -204,6 +205,7 @@ func NewGoCollector(opts ...func(o *internal.GoCollectorOptions)) Collector {
|
||||
// to fail here. This condition is tested in TestExpectedRuntimeMetrics.
|
||||
continue
|
||||
}
|
||||
help := attachOriginalName(d.Description.Description, d.Name)
|
||||
|
||||
sampleBuf = append(sampleBuf, metrics.Sample{Name: d.Name})
|
||||
sampleMap[d.Name] = &sampleBuf[len(sampleBuf)-1]
|
||||
@@ -215,7 +217,7 @@ func NewGoCollector(opts ...func(o *internal.GoCollectorOptions)) Collector {
|
||||
m = newBatchHistogram(
|
||||
NewDesc(
|
||||
BuildFQName(namespace, subsystem, name),
|
||||
d.Description.Description,
|
||||
help,
|
||||
nil,
|
||||
nil,
|
||||
),
|
||||
@@ -227,7 +229,7 @@ func NewGoCollector(opts ...func(o *internal.GoCollectorOptions)) Collector {
|
||||
Namespace: namespace,
|
||||
Subsystem: subsystem,
|
||||
Name: name,
|
||||
Help: d.Description.Description,
|
||||
Help: help,
|
||||
},
|
||||
)
|
||||
} else {
|
||||
@@ -235,7 +237,7 @@ func NewGoCollector(opts ...func(o *internal.GoCollectorOptions)) Collector {
|
||||
Namespace: namespace,
|
||||
Subsystem: subsystem,
|
||||
Name: name,
|
||||
Help: d.Description.Description,
|
||||
Help: help,
|
||||
})
|
||||
}
|
||||
metricSet = append(metricSet, m)
|
||||
@@ -285,6 +287,10 @@ func NewGoCollector(opts ...func(o *internal.GoCollectorOptions)) Collector {
|
||||
}
|
||||
}
|
||||
|
||||
func attachOriginalName(desc, origName string) string {
|
||||
return fmt.Sprintf("%s Sourced from %s.", desc, origName)
|
||||
}
|
||||
|
||||
// Describe returns all descriptions of the collector.
|
||||
func (c *goCollector) Describe(ch chan<- *Desc) {
|
||||
c.base.Describe(ch)
|
||||
@@ -377,13 +383,13 @@ func unwrapScalarRMValue(v metrics.Value) float64 {
|
||||
//
|
||||
// This should never happen because we always populate our metric
|
||||
// set from the runtime/metrics package.
|
||||
panic("unexpected unsupported metric")
|
||||
panic("unexpected bad kind metric")
|
||||
default:
|
||||
// Unsupported metric kind.
|
||||
//
|
||||
// This should never happen because we check for this during initialization
|
||||
// and flag and filter metrics whose kinds we don't understand.
|
||||
panic("unexpected unsupported metric kind")
|
||||
panic(fmt.Sprintf("unexpected unsupported metric: %v", v.Kind()))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user