1
0
mirror of https://github.com/taigrr/wtf synced 2025-01-18 04:03:14 -08:00

feat: update to go modules

This commit is contained in:
retgits 2019-04-26 16:39:44 -07:00
parent 66a16af97d
commit e03b65b442
No known key found for this signature in database
GPG Key ID: 9D3AD75D6182395C
1471 changed files with 207 additions and 466677 deletions

View File

@ -1,7 +1,7 @@
language: go
go:
- "1.10.x"
- "1.11.x"
- "1.12.x"
sudo: false
before_install:
# Make sure travis builds work for forks
@ -9,5 +9,6 @@ before_install:
- test ! -d $GOPATH/src/github.com/wtfutil/wtf && mv $TRAVIS_BUILD_DIR $GOPATH/src/github.com/wtfutil/wtf || true
- export TRAVIS_BUILD_DIR=$HOME/gopath/src/github.com/wtfutil/wtf
- cd $HOME/gopath/src/github.com/wtfutil/wtf
- export GOPROXY="https://gocenter.io" && export GO111MODULE=on
script: go get ./... && go get github.com/go-test/deep && go test -v github.com/wtfutil/wtf/wtf_tests/...

472
Gopkg.lock generated
View File

@ -1,472 +0,0 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
digest = "1:5ad08b0e14866764a6d7475eb11c9cf05cad9a52c442593bdfa544703ff77f61"
name = "cloud.google.com/go"
packages = ["compute/metadata"]
pruneopts = "UT"
revision = "0ebda48a7f143b1cce9eb37a8c1106ac762a3430"
version = "v0.34.0"
[[projects]]
branch = "master"
digest = "1:d1a104572273cfb58e801812704dca4fe2eab80f3b0144592a722a76c280c598"
name = "code.cloudfoundry.org/bytefmt"
packages = ["."]
pruneopts = "UT"
revision = "2aa6f33b730c79971cfc3c742f279195b0abc627"
[[projects]]
digest = "1:3bb331d3045ee55402a226f6ab0f005cbc09527df714278c3925d4700db79260"
name = "github.com/PagerDuty/go-pagerduty"
packages = ["."]
pruneopts = "UT"
revision = "b4a4067bdbde86de1d3fdf15c3c01e76bee9127c"
version = "1.0.4"
[[projects]]
digest = "1:e92f5581902c345eb4ceffdcd4a854fb8f73cf436d47d837d1ec98ef1fe0a214"
name = "github.com/StackExchange/wmi"
packages = ["."]
pruneopts = "UT"
revision = "5d049714c4a64225c3c79a7cf7d02f7fb5b96338"
version = "1.0.0"
[[projects]]
branch = "master"
digest = "1:636ac9f696c988f0038afd43592f7a0fff29038588ab1064ba8ba3476bb41091"
name = "github.com/adlio/trello"
packages = ["."]
pruneopts = "UT"
revision = "e4cc07c871d0ee17c5579131fc1c786efbb5fe2a"
[[projects]]
digest = "1:aa94227e54ee105fcd03f25c85d3e5bbe13d2f2d5eb02379b5b4db5320dfb371"
name = "github.com/alecthomas/chroma"
packages = [
".",
"formatters",
"formatters/html",
"lexers",
"lexers/a",
"lexers/b",
"lexers/c",
"lexers/circular",
"lexers/d",
"lexers/e",
"lexers/f",
"lexers/g",
"lexers/h",
"lexers/i",
"lexers/internal",
"lexers/j",
"lexers/k",
"lexers/l",
"lexers/m",
"lexers/n",
"lexers/o",
"lexers/p",
"lexers/q",
"lexers/r",
"lexers/s",
"lexers/t",
"lexers/v",
"lexers/w",
"lexers/x",
"lexers/y",
"styles",
]
pruneopts = "UT"
revision = "881a441774f9d707d3b7852025b7f2149a556182"
version = "v0.6.2"
[[projects]]
branch = "master"
digest = "1:a7955b3402449f2e9ef351d0d6d9d42af3e0b24967340b738078d80c90deb6c2"
name = "github.com/andygrunwald/go-gerrit"
packages = ["."]
pruneopts = "UT"
revision = "f48c3d16616fdb85d96efcead2402bfd339d8c89"
[[projects]]
branch = "master"
digest = "1:6d7646b01dbbacd8c50be0cbddbcb633619f819e51f47a4114c8d4518ae7ac02"
name = "github.com/briandowns/openweathermap"
packages = ["."]
pruneopts = "UT"
revision = "5f41b7c9d92de5d74bf32f4486375c7547bc8a3c"
[[projects]]
digest = "1:166438587ed45ac211dab8a3ecebf4fa0c186d0db63430fb9127bbc2e5fcdc67"
name = "github.com/cenkalti/backoff"
packages = ["."]
pruneopts = "UT"
revision = "1e4cf3da559842a91afcb6ea6141451e6c30c618"
version = "v2.1.1"
[[projects]]
branch = "master"
digest = "1:710109527a119c813d4fac773712dd92f449ed7c2f7b81f49822f94d290c8f72"
name = "github.com/danwakefield/fnmatch"
packages = ["."]
pruneopts = "UT"
revision = "cbb64ac3d964b81592e64f957ad53df015803288"
[[projects]]
branch = "master"
digest = "1:e3e85f98942fe3b51b8f9316d8d1dc1925353b26639d456030368ae278bb0475"
name = "github.com/darkSasori/todoist"
packages = ["."]
pruneopts = "UT"
revision = "ec6b38b374ab9c60cc9716d2083ae66eb9383d03"
[[projects]]
digest = "1:ffe9824d294da03b391f44e1ae8281281b4afc1bdaa9588c9097785e3af10cec"
name = "github.com/davecgh/go-spew"
packages = ["spew"]
pruneopts = "UT"
revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73"
version = "v1.1.1"
[[projects]]
digest = "1:72dc2b6056e7097f829260e4a2ff08d32fec6017df1982a66e110ab4128486f8"
name = "github.com/dlclark/regexp2"
packages = [
".",
"syntax",
]
pruneopts = "UT"
revision = "487489b64fb796de2e55f4e8a4ad1e145f80e957"
version = "v1.1.6"
[[projects]]
digest = "1:6f9339c912bbdda81302633ad7e99a28dfa5a639c864061f1929510a9a64aa74"
name = "github.com/dustin/go-humanize"
packages = ["."]
pruneopts = "UT"
revision = "9f541cc9db5d55bce703bd99987c9d5cb8eea45e"
version = "v1.0.0"
[[projects]]
digest = "1:0e16b03c72bd42739ca68d055c10369e60d17838428f512f3b29ae115ba788e0"
name = "github.com/gdamore/encoding"
packages = ["."]
pruneopts = "UT"
revision = "6289cdc94c00ac4aa177771c5fce7af2f96b626d"
version = "v1.0.0"
[[projects]]
branch = "master"
digest = "1:5caa7f0f760a7b380cfa4102a44b6aef61bd68506f62f49880cc59d5c1ee72fd"
name = "github.com/gdamore/tcell"
packages = [
".",
"terminfo",
]
pruneopts = "UT"
revision = "dcf1bb30770eb1158b67005e1e472de6d74f055d"
[[projects]]
digest = "1:c96d16a4451e48e2c44b2c3531fd8ec9248d822637f1911a88959ca0bcae4a64"
name = "github.com/go-ole/go-ole"
packages = [
".",
"oleutil",
]
pruneopts = "UT"
revision = "39dc8486bd0952279431257138bc428275b86797"
version = "v1.2.2"
[[projects]]
digest = "1:b3d20bcdedab2050e6bc58e52f4fdc46f710b4c74e1a1ecee262ebec1aee7b6e"
name = "github.com/godbus/dbus"
packages = ["."]
pruneopts = "UT"
revision = "2ff6f7ffd60f0f2410b3105864bdd12c7894f844"
version = "v5.0.1"
[[projects]]
digest = "1:97df918963298c287643883209a2c3f642e6593379f97ab400c2a2e219ab647d"
name = "github.com/golang/protobuf"
packages = ["proto"]
pruneopts = "UT"
revision = "aa810b61a9c79d51363740d207bb46cf8e620ed5"
version = "v1.2.0"
[[projects]]
branch = "master"
digest = "1:fc71ff099a9d9e73a66556c3115c78ca64cd7f01e08a7eddcaddcba5e8fa02fd"
name = "github.com/google/go-github"
packages = ["github"]
pruneopts = "UT"
revision = "ce65dacfeac482f2a13b59e19c5a25947282c171"
[[projects]]
digest = "1:a63cff6b5d8b95638bfe300385d93b2a6d9d687734b863da8e09dc834510a690"
name = "github.com/google/go-querystring"
packages = ["query"]
pruneopts = "UT"
revision = "44c6ddd0a2342c386950e880b658017258da92fc"
version = "v1.0.0"
[[projects]]
digest = "1:a2cff208d4759f6ba1b1cd228587b0a1869f95f22542ec9cd17fff64430113c7"
name = "github.com/jessevdk/go-flags"
packages = ["."]
pruneopts = "UT"
revision = "c6ca198ec95c841fdb89fc0de7496fed11ab854e"
version = "v1.4.0"
[[projects]]
digest = "1:c65a16ac77d0b1aefc7009cabb6ac5ad05def02025f5be85f450c03f52cc6f86"
name = "github.com/lucasb-eyer/go-colorful"
packages = ["."]
pruneopts = "UT"
revision = "345fbb3dbcdb252d9985ee899a84963c0fa24c82"
version = "v1.0"
[[projects]]
digest = "1:0356f3312c9bd1cbeda81505b7fd437501d8e778ab66998ef69f00d7f9b3a0d7"
name = "github.com/mattn/go-runewidth"
packages = ["."]
pruneopts = "UT"
revision = "3ee7d812e62a0804a7d0a324e0249ca2db3476d3"
version = "v0.0.4"
[[projects]]
branch = "master"
digest = "1:2277d06c9dcd34d70119b624115f3d74672647322349bb76a8adedbc623a3955"
name = "github.com/olebedev/config"
packages = ["."]
pruneopts = "UT"
revision = "57f804269e64d41bfe46efb76232c47f49d40902"
[[projects]]
digest = "1:cf31692c14422fa27c83a05292eb5cbe0fb2775972e8f1f8446a71549bd8980b"
name = "github.com/pkg/errors"
packages = ["."]
pruneopts = "UT"
revision = "ba968bfe8b2f7e042a574c888954fccecfa385b4"
version = "v0.8.1"
[[projects]]
digest = "1:cfa0d7741863a0e1d30e0ccdd4b48a96a471cdb47892303de8b92c3713af3e77"
name = "github.com/pkg/profile"
packages = ["."]
pruneopts = "UT"
revision = "5b67d428864e92711fcbd2f8629456121a56d91f"
version = "v1.2.1"
[[projects]]
digest = "1:0028cb19b2e4c3112225cd871870f2d9cf49b9b4276531f03438a88e94be86fe"
name = "github.com/pmezard/go-difflib"
packages = ["difflib"]
pruneopts = "UT"
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
version = "v1.0.0"
[[projects]]
digest = "1:46f5782e57d221d76a19fb9c9c4f2b139f1d85ae7115a95e945a79221e1a7031"
name = "github.com/radovskyb/watcher"
packages = ["."]
pruneopts = "UT"
revision = "3818ec23ec59ea15084fe26bfb114b3bb58aa132"
version = "v1.0.5"
[[projects]]
branch = "master"
digest = "1:0f44ebd1757c5725456f2d6c564b196dd96593e68fda7236e345642940b9a88a"
name = "github.com/rivo/tview"
packages = ["."]
pruneopts = "UT"
revision = "36893a66979260e9758701ae306ec0d69c0f53c8"
[[projects]]
digest = "1:afee7cbe8cde64304b8bd397d5832b0e60c93f8b158e4102254558d2f862b0a6"
name = "github.com/shirou/gopsutil"
packages = [
"cpu",
"internal/common",
"mem",
]
pruneopts = "UT"
revision = "ccc1c1016bc5d10e803189ee43417c50cdde7f1b"
version = "v2.18.12"
[[projects]]
branch = "master"
digest = "1:93ff598337cb35c40079a1ba766729c20eb1756e37f5740eac4c8e943f59664d"
name = "github.com/sticreations/spotigopher"
packages = ["spotigopher"]
pruneopts = "UT"
revision = "98632f6f94b087f2582be76980f76bc070d37176"
[[projects]]
digest = "1:972c2427413d41a1e06ca4897e8528e5a1622894050e2f527b38ddf0f343f759"
name = "github.com/stretchr/testify"
packages = ["assert"]
pruneopts = "UT"
revision = "ffdc059bfe9ce6a4e144ba849dbedead332c6053"
version = "v1.3.0"
[[projects]]
branch = "master"
digest = "1:fc5ea767f62a8a35b136db9e8629dc7718aea555c8480e44695a7d0894ae8303"
name = "github.com/xanzy/go-gitlab"
packages = ["."]
pruneopts = "UT"
revision = "d85a1530126065c71277db28196ec196daafc9dc"
[[projects]]
branch = "master"
digest = "1:3b19ea715e070a74397f3cfe950f0b555e66a3c23461e553777f52112a81b0e6"
name = "github.com/yfronto/newrelic"
packages = ["."]
pruneopts = "UT"
revision = "7c9c2852e8f9e69a80bff4f4f1fe4cdd15eeba19"
[[projects]]
branch = "master"
digest = "1:1ff6915a45fb06d272e614e72219aa058aefc545f597a708eb0cc6cb778c344a"
name = "github.com/zmb3/spotify"
packages = ["."]
pruneopts = "UT"
revision = "a4bd83f60e06ab58f3516c61680d0532aabf470e"
[[projects]]
branch = "master"
digest = "1:0af28605549b0c849891b2ea4e99de9fe20d2f48d3ae5d42f9b06391e02a466e"
name = "github.com/zorkian/go-datadog-api"
packages = ["."]
pruneopts = "UT"
revision = "66f4fd4202d9d3e543608051a91a09eebc870966"
[[projects]]
branch = "master"
digest = "1:f8b491a7c25030a895a0e579742d07136e6958e77ef2d46e769db8eec4e58fcd"
name = "golang.org/x/net"
packages = [
"context",
"context/ctxhttp",
]
pruneopts = "UT"
revision = "915654e7eabcea33ae277abbecf52f0d8b7a9fdc"
[[projects]]
branch = "master"
digest = "1:c58bbfb91df5a7eae08cb9894f9897165f254e459f07666986cd448f9be4ad35"
name = "golang.org/x/oauth2"
packages = [
".",
"google",
"internal",
"jws",
"jwt",
]
pruneopts = "UT"
revision = "36a7019397c4c86cf59eeab3bc0d188bac444277"
[[projects]]
branch = "master"
digest = "1:5ee4df7ab18e945607ac822de8d10b180baea263b5e8676a1041727543b9c1e4"
name = "golang.org/x/sys"
packages = [
"unix",
"windows",
]
pruneopts = "UT"
revision = "48ac38b7c8cbedd50b1613c0fccacfc7d88dfcdf"
[[projects]]
digest = "1:37672ad5821719e2df8509c2edd4ba5ae192463237c73c3a2d24ef8b2bc9e36f"
name = "golang.org/x/text"
packages = [
"encoding",
"encoding/internal/identifier",
"internal/gen",
"transform",
"unicode/cldr",
]
pruneopts = "UT"
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
version = "v0.3.0"
[[projects]]
branch = "master"
digest = "1:4b5fd36b449e451b5b4a4e8b31870c9230647f409413f13169ab93af268311da"
name = "google.golang.org/api"
packages = [
"calendar/v3",
"gensupport",
"googleapi",
"googleapi/internal/uritemplates",
"sheets/v4",
]
pruneopts = "UT"
revision = "455dee39f703f70d67ceea7c56a61041712c1447"
[[projects]]
digest = "1:fa026a5c59bd2df343ec4a3538e6288dcf4e2ec5281d743ae82c120affe6926a"
name = "google.golang.org/appengine"
packages = [
".",
"internal",
"internal/app_identity",
"internal/base",
"internal/datastore",
"internal/log",
"internal/modules",
"internal/remote_api",
"internal/urlfetch",
"urlfetch",
]
pruneopts = "UT"
revision = "e9657d882bb81064595ca3b56cbe2546bbabf7b1"
version = "v1.4.0"
[[projects]]
digest = "1:4d2e5a73dc1500038e504a8d78b986630e3626dc027bc030ba5c75da257cdb96"
name = "gopkg.in/yaml.v2"
packages = ["."]
pruneopts = "UT"
revision = "51d6538a90f86fe93ac480b35f37b2be17fef232"
version = "v2.2.2"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
input-imports = [
"code.cloudfoundry.org/bytefmt",
"github.com/PagerDuty/go-pagerduty",
"github.com/adlio/trello",
"github.com/alecthomas/chroma/formatters",
"github.com/alecthomas/chroma/lexers",
"github.com/alecthomas/chroma/styles",
"github.com/andygrunwald/go-gerrit",
"github.com/briandowns/openweathermap",
"github.com/darkSasori/todoist",
"github.com/dustin/go-humanize",
"github.com/gdamore/tcell",
"github.com/google/go-github/github",
"github.com/jessevdk/go-flags",
"github.com/olebedev/config",
"github.com/pkg/profile",
"github.com/radovskyb/watcher",
"github.com/rivo/tview",
"github.com/shirou/gopsutil/cpu",
"github.com/shirou/gopsutil/mem",
"github.com/sticreations/spotigopher/spotigopher",
"github.com/stretchr/testify/assert",
"github.com/xanzy/go-gitlab",
"github.com/yfronto/newrelic",
"github.com/zmb3/spotify",
"github.com/zorkian/go-datadog-api",
"golang.org/x/oauth2",
"golang.org/x/oauth2/google",
"google.golang.org/api/calendar/v3",
"google.golang.org/api/sheets/v4",
"gopkg.in/yaml.v2",
]
solver-name = "gps-cdcl"
solver-version = 1

View File

@ -1,107 +0,0 @@
# Gopkg.toml example
#
# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
#
# [prune]
# non-go = false
# go-tests = true
# unused-packages = true
[[constraint]]
name = "github.com/briandowns/openweathermap"
branch = "master"
[[constraint]]
name = "github.com/gdamore/tcell"
branch = "master"
#[[constraint]]
#name = "github.com/go-test/deep"
#version = "1.0.1"
[[constraint]]
name = "github.com/google/go-github"
branch = "master"
[[constraint]]
name = "github.com/xanzy/go-gitlab"
branch = "master"
[[constraint]]
name = "github.com/andygrunwald/go-gerrit"
branch = "master"
[[constraint]]
name = "github.com/jessevdk/go-flags"
version = "1.4.0"
[[constraint]]
name = "github.com/olebedev/config"
branch = "master"
[[constraint]]
name = "github.com/radovskyb/watcher"
version = "1.0.2"
[[constraint]]
name = "github.com/rivo/tview"
branch = "master"
[[constraint]]
name = "github.com/yfronto/newrelic"
branch = "master"
[[constraint]]
name = "golang.org/x/oauth2"
branch = "master"
[[constraint]]
name = "google.golang.org/api"
branch = "master"
[[constraint]]
name = "gopkg.in/yaml.v2"
version = "2.2.1"
[[constraint]]
name = "github.com/adlio/trello"
branch = "master"
[[constraint]]
name = "github.com/darkSasori/todoist"
branch = "master"
[prune]
go-tests = true
unused-packages = true
[[constraint]]
branch = "master"
name = "github.com/zorkian/go-datadog-api"
[[constraint]]
branch = "master"
name = "code.cloudfoundry.org/bytefmt"
[[constraint]]
name = "github.com/shirou/gopsutil"
version = "2.18.10"

View File

@ -16,6 +16,10 @@ ifndef $(GOPATH)
endif
endif
# Set go modules to on and use GoCenter for immutable modules
GO111MODULE=on
GOPROXY=https://gocenter.io
build:
go build -o bin/wtf
@ -35,7 +39,7 @@ run: build
bin/wtf
size:
loc --exclude vendor/ _sample_configs/ _site/ docs/ Makefile *.md *.toml
loc --exclude _sample_configs/ _site/ docs/ Makefile *.md
test: build
go test ./...

51
go.mod Normal file
View File

@ -0,0 +1,51 @@
module github.com/wtfutil/wtf
go 1.12
require (
cloud.google.com/go v0.34.0
code.cloudfoundry.org/bytefmt v0.0.0-20180906201452-2aa6f33b730c
github.com/PagerDuty/go-pagerduty v0.0.0-20180528123509-b4a4067bdbde
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6
github.com/adlio/trello v0.0.0-20181009211147-e4cc07c871d0
github.com/alecthomas/chroma v0.6.2
github.com/andygrunwald/go-gerrit v0.0.0-20190110184452-f48c3d16616f
github.com/briandowns/openweathermap v0.0.0-20180804155945-5f41b7c9d92d
github.com/cenkalti/backoff v2.1.1+incompatible
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964
github.com/darkSasori/todoist v0.0.0-20180703032645-ec6b38b374ab
github.com/davecgh/go-spew v1.1.1
github.com/dlclark/regexp2 v1.1.6
github.com/dustin/go-humanize v1.0.0
github.com/gdamore/encoding v1.0.0
github.com/gdamore/tcell v0.0.0-20190412054914-dcf1bb30770e
github.com/go-ole/go-ole v1.2.2
github.com/go-test/deep v1.0.1 // indirect
github.com/godbus/dbus v0.0.0-20181101234600-2ff6f7ffd60f
github.com/golang/protobuf v1.2.0
github.com/google/go-github/v25 v25.0.1
github.com/google/go-querystring v1.0.0
github.com/jessevdk/go-flags v1.4.0
github.com/lucasb-eyer/go-colorful v1.0.2
github.com/mattn/go-runewidth v0.0.4
github.com/olebedev/config v0.0.0-20180910155717-57f804269e64
github.com/pkg/errors v0.8.1
github.com/pkg/profile v1.2.1
github.com/pmezard/go-difflib v1.0.0
github.com/radovskyb/watcher v1.0.5
github.com/rivo/tview v0.0.0-20181226202439-36893a669792
github.com/shirou/gopsutil v2.18.12+incompatible
github.com/sticreations/spotigopher v0.0.0-20181009182052-98632f6f94b0
github.com/stretchr/testify v1.3.0
github.com/xanzy/go-gitlab v0.0.0-20190111184316-d85a15301260
github.com/yfronto/newrelic v0.0.0-20180622232530-7c9c2852e8f9
github.com/zmb3/spotify v0.0.0-20180925143944-a4bd83f60e06
github.com/zorkian/go-datadog-api v2.19.0+incompatible
golang.org/x/net v0.0.0-20190311183353-d8887717615a
golang.org/x/oauth2 v0.0.0-20190111185915-36a7019397c4
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a
golang.org/x/text v0.3.0
google.golang.org/api v0.0.0-20190111181425-455dee39f703
google.golang.org/appengine v1.4.0
gopkg.in/yaml.v2 v2.2.2
)

147
go.sum Normal file
View File

@ -0,0 +1,147 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0 h1:eOI3/cP2VTU6uZLDYAoic+eyzzB9YyGmJ7eIjl8rOPg=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
code.cloudfoundry.org/bytefmt v0.0.0-20180906201452-2aa6f33b730c h1:VzwteSWGbW9mxXTEkH+kpnao5jbgLynw3hq742juQh8=
code.cloudfoundry.org/bytefmt v0.0.0-20180906201452-2aa6f33b730c/go.mod h1:wN/zk7mhREp/oviagqUXY3EwuHhWyOvAdsn5Y4CzOrc=
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
github.com/PagerDuty/go-pagerduty v0.0.0-20180528123509-b4a4067bdbde h1:2UaB3Cr5WaSgxuZQEOpaiIQSked1+5DQ9U3Cm5Wg8Jw=
github.com/PagerDuty/go-pagerduty v0.0.0-20180528123509-b4a4067bdbde/go.mod h1:6hH58nzwYc9mw+TPyM1anW0ivbI0ti4lYc+ZBaKmWts=
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/adlio/trello v0.0.0-20181009211147-e4cc07c871d0 h1:mI+BqWX9HJIRw68D6yUBunwIHs6oJkqqwFKEogkHKkk=
github.com/adlio/trello v0.0.0-20181009211147-e4cc07c871d0/go.mod h1:VjzhFGdnEJGih1AsIWi/yU6y01yZh0DuEWCNAyOJ1WE=
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI=
github.com/alecthomas/chroma v0.6.2 h1:aV6n3C/Womqo1zPZ7eyI0viybDslfbgqTUqxMMyCrDM=
github.com/alecthomas/chroma v0.6.2/go.mod h1:quT2EpvJNqkuPi6DmBHB+E33FXBgBBPzyH5++Dn1LPc=
github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0=
github.com/alecthomas/kong v0.1.15/go.mod h1:0m2VYms8rH0qbCqVB2gvGHk74bqLIq0HXjCs5bNbNQU=
github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ=
github.com/andygrunwald/go-gerrit v0.0.0-20190110184452-f48c3d16616f h1:R5XFuvUUbmwDtR2posQv+6vvKfty2+vL7IFsU6/TrNY=
github.com/andygrunwald/go-gerrit v0.0.0-20190110184452-f48c3d16616f/go.mod h1:0iuRQp6WJ44ts+iihy5E/WlPqfg5RNeQxOmzRkxCdtk=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/briandowns/openweathermap v0.0.0-20180804155945-5f41b7c9d92d h1:28xWzPQ9bdGxKAAwQpZipZZ9Xz8kQcgMPF9cZnvMeuI=
github.com/briandowns/openweathermap v0.0.0-20180804155945-5f41b7c9d92d/go.mod h1:8g1Bgq9PbPpXIA3sdlWmWf2JpiWGJee/O4Q+ddYO6+k=
github.com/cenkalti/backoff v2.1.1+incompatible h1:tKJnvO2kl0zmb/jA5UKAt4VoEVw1qxKWjE/Bpp46npY=
github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ=
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk=
github.com/darkSasori/todoist v0.0.0-20180703032645-ec6b38b374ab h1:T9EEtA6FSJMVypkNlKjrRo04u1j5Tk+gghymflyivDw=
github.com/darkSasori/todoist v0.0.0-20180703032645-ec6b38b374ab/go.mod h1:dD/81kphWGY78UVdEu2sqkrUMYfvtdKAMaACFYHGkfU=
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/dlclark/regexp2 v1.1.6 h1:CqB4MjHw0MFCDj+PHHjiESmHX+N7t0tJzKvC6M97BRg=
github.com/dlclark/regexp2 v1.1.6/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
github.com/gdamore/tcell v0.0.0-20190412054914-dcf1bb30770e h1:T33mNvmeXRJzfz+m4RokVI2jFsbheIAEN6E2SO97FCo=
github.com/gdamore/tcell v0.0.0-20190412054914-dcf1bb30770e/go.mod h1:h3kq4HO9l2On+V9ed8w8ewqQEmGCSSHOgQ+2h8uzurE=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-ole/go-ole v1.2.2/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
github.com/go-test/deep v1.0.1 h1:UQhStjbkDClarlmv0am7OXXO4/GaPdCGiUiMTvi28sg=
github.com/go-test/deep v1.0.1/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/godbus/dbus v0.0.0-20181101234600-2ff6f7ffd60f h1:zlOR3rOlPAVvtfuxGKoghCmop5B0TRyu/ZieziZuGiM=
github.com/godbus/dbus v0.0.0-20181101234600-2ff6f7ffd60f/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-github/v25 v25.0.1 h1:s405kPD52lKa1MVxiEumod/E6/+0pvQ8Ed/sT65DjKc=
github.com/google/go-github/v25 v25.0.1/go.mod h1:6z5pC69qHtrPJ0sXPsj4BLnd82b+r6sLB7qcBoRZqpw=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/lucasb-eyer/go-colorful v1.0.2 h1:mCMFu6PgSozg9tDNMMK3g18oJBX7oYGrC09mS6CXfO4=
github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/olebedev/config v0.0.0-20180910155717-57f804269e64 h1:bS1cVBGfBJgWMkgvmyZ1E2uFuKACfsVZLWrfm0qTPc4=
github.com/olebedev/config v0.0.0-20180910155717-57f804269e64/go.mod h1:RL5+WRxWTAXqqCi9i+eZlHrUtO7AQujUqWi+xMohmc4=
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/profile v1.2.1 h1:F++O52m40owAmADcojzM+9gyjmMOY/T4oYJkgFDH8RE=
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
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.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/radovskyb/watcher v1.0.5 h1:wqt7gb+HjGacvFoLTKeT44C+XVPxu7bvHvKT1IvZ7rw=
github.com/radovskyb/watcher v1.0.5/go.mod h1:78okwvY5wPdzcb1UYnip1pvrZNIVEIh/Cm+ZuvsUYIg=
github.com/rivo/tview v0.0.0-20181226202439-36893a669792 h1:3Jm4JCmIZchgwc405ygGCzM5eEiYb9sV3qLHWwz9uxo=
github.com/rivo/tview v0.0.0-20181226202439-36893a669792/go.mod h1:J4W+hErFfITUbyFAEXizpmkuxX7ZN56dopxHB4XQhMw=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/shirou/gopsutil v2.18.12+incompatible h1:1eaJvGomDnH74/5cF4CTmTbLHAriGFsTZppLXDX93OM=
github.com/shirou/gopsutil v2.18.12+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/sticreations/spotigopher v0.0.0-20181009182052-98632f6f94b0 h1:WWyBZL7bRdl7Do39EvkJmBFXT11uXLACy0cJHHOZ7IE=
github.com/sticreations/spotigopher v0.0.0-20181009182052-98632f6f94b0/go.mod h1:DjuRbAVIoxD4Lv7aE12Km2XYYYKrtXXNbpivYwXv2HE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/xanzy/go-gitlab v0.0.0-20190111184316-d85a15301260 h1:IkRgG4jOWEJBmapnDlFMjmDnZpmgCrrSiCEJIWstPl0=
github.com/xanzy/go-gitlab v0.0.0-20190111184316-d85a15301260/go.mod h1:8zdQa/ri1dfn8eS3Ir1SyfvOKlw7WBJ8DVThkpGiXrs=
github.com/yfronto/newrelic v0.0.0-20180622232530-7c9c2852e8f9 h1:MYjp+3xEmdmEs41pec7vbPtMg4Fe+za4FFA1lXV6RLo=
github.com/yfronto/newrelic v0.0.0-20180622232530-7c9c2852e8f9/go.mod h1:0AAHfU8E7myJ9nLrWxqgrUDCCqNjTQ4/7cRLvYTHBnw=
github.com/zmb3/spotify v0.0.0-20180925143944-a4bd83f60e06 h1:TcLBlInEqIrXAISLEaTF1jwHsRH7GBIMADkYYyqxzf8=
github.com/zmb3/spotify v0.0.0-20180925143944-a4bd83f60e06/go.mod h1:pHsWAmY9PfX7i/uwPZkmWrebc8JbK8FppKbvyevwzSU=
github.com/zorkian/go-datadog-api v2.19.0+incompatible h1:G17NtfeHwrsQ2degevkwiVEirZENSOjbGOjLLT/kwa4=
github.com/zorkian/go-datadog-api v2.19.0+incompatible/go.mod h1:PkXwHX9CUQa/FpB9ZwAD45N1uhCW4MT/Wj7m36PbKss=
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
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-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181108082009-03003ca0c849/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-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190111185915-36a7019397c4 h1:Xi5aaGtyrfSB/gXS4Kal2NNpB7uzffL3yzWi2kByI18=
golang.org/x/oauth2 v0.0.0-20190111185915-36a7019397c4/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/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-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.0.0-20190111181425-455dee39f703 h1:enSundpQpZE5iBW667ebOGpan2JiQibes23PDtdkVkU=
google.golang.org/api v0.0.0-20190111181425-455dee39f703/go.mod h1:HyxHN0BdhQ9jLjP1ppRm1JHMruzksPklmzI2+T+z1jM=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@ -3,7 +3,7 @@ package github
import (
"fmt"
"github.com/google/go-github/github"
"github.com/google/go-github/v25/github"
)
func (widget *Widget) display() {

View File

@ -4,7 +4,7 @@ import (
"context"
"net/http"
ghb "github.com/google/go-github/github"
ghb "github.com/google/go-github/v25/github"
"github.com/wtfutil/wtf/wtf"
"golang.org/x/oauth2"
)

15
vendor/cloud.google.com/go/AUTHORS generated vendored
View File

@ -1,15 +0,0 @@
# This is the official list of cloud authors for copyright purposes.
# This file is distinct from the CONTRIBUTORS files.
# See the latter for an explanation.
# Names should be added to this file as:
# Name or Organization <email address>
# The email address is not required for organizations.
Filippo Valsorda <hi@filippo.io>
Google Inc.
Ingo Oeser <nightlyone@googlemail.com>
Palm Stone Games, Inc.
Paweł Knap <pawelknap88@gmail.com>
Péter Szilágyi <peterke@gmail.com>
Tyler Treat <ttreat31@gmail.com>

View File

@ -1,40 +0,0 @@
# People who have agreed to one of the CLAs and can contribute patches.
# The AUTHORS file lists the copyright holders; this file
# lists people. For example, Google employees are listed here
# but not in AUTHORS, because Google holds the copyright.
#
# https://developers.google.com/open-source/cla/individual
# https://developers.google.com/open-source/cla/corporate
#
# Names should be added to this file as:
# Name <email address>
# Keep the list alphabetically sorted.
Alexis Hunt <lexer@google.com>
Andreas Litt <andreas.litt@gmail.com>
Andrew Gerrand <adg@golang.org>
Brad Fitzpatrick <bradfitz@golang.org>
Burcu Dogan <jbd@google.com>
Dave Day <djd@golang.org>
David Sansome <me@davidsansome.com>
David Symonds <dsymonds@golang.org>
Filippo Valsorda <hi@filippo.io>
Glenn Lewis <gmlewis@google.com>
Ingo Oeser <nightlyone@googlemail.com>
James Hall <james.hall@shopify.com>
Johan Euphrosine <proppy@google.com>
Jonathan Amsterdam <jba@google.com>
Kunpei Sakai <namusyaka@gmail.com>
Luna Duclos <luna.duclos@palmstonegames.com>
Magnus Hiie <magnus.hiie@gmail.com>
Mario Castro <mariocaster@gmail.com>
Michael McGreevy <mcgreevy@golang.org>
Omar Jarjur <ojarjur@google.com>
Paweł Knap <pawelknap88@gmail.com>
Péter Szilágyi <peterke@gmail.com>
Sarah Adams <shadams@google.com>
Thanatat Tamtan <acoshift@gmail.com>
Toby Burress <kurin@google.com>
Tuo Shan <shantuo@google.com>
Tyler Treat <ttreat31@gmail.com>

202
vendor/cloud.google.com/go/LICENSE generated vendored
View File

@ -1,202 +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.

View File

@ -1,501 +0,0 @@
// Copyright 2014 Google LLC
//
// 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 metadata provides access to Google Compute Engine (GCE)
// metadata and API service accounts.
//
// This package is a wrapper around the GCE metadata service,
// as documented at https://developers.google.com/compute/docs/metadata.
package metadata // import "cloud.google.com/go/compute/metadata"
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"net"
"net/http"
"net/url"
"os"
"runtime"
"strings"
"sync"
"time"
)
const (
// metadataIP is the documented metadata server IP address.
metadataIP = "169.254.169.254"
// metadataHostEnv is the environment variable specifying the
// GCE metadata hostname. If empty, the default value of
// metadataIP ("169.254.169.254") is used instead.
// This is variable name is not defined by any spec, as far as
// I know; it was made up for the Go package.
metadataHostEnv = "GCE_METADATA_HOST"
userAgent = "gcloud-golang/0.1"
)
type cachedValue struct {
k string
trim bool
mu sync.Mutex
v string
}
var (
projID = &cachedValue{k: "project/project-id", trim: true}
projNum = &cachedValue{k: "project/numeric-project-id", trim: true}
instID = &cachedValue{k: "instance/id", trim: true}
)
var (
defaultClient = &Client{hc: &http.Client{
Transport: &http.Transport{
Dial: (&net.Dialer{
Timeout: 2 * time.Second,
KeepAlive: 30 * time.Second,
}).Dial,
ResponseHeaderTimeout: 2 * time.Second,
},
}}
subscribeClient = &Client{hc: &http.Client{
Transport: &http.Transport{
Dial: (&net.Dialer{
Timeout: 2 * time.Second,
KeepAlive: 30 * time.Second,
}).Dial,
},
}}
)
// NotDefinedError is returned when requested metadata is not defined.
//
// The underlying string is the suffix after "/computeMetadata/v1/".
//
// This error is not returned if the value is defined to be the empty
// string.
type NotDefinedError string
func (suffix NotDefinedError) Error() string {
return fmt.Sprintf("metadata: GCE metadata %q not defined", string(suffix))
}
func (c *cachedValue) get(cl *Client) (v string, err error) {
defer c.mu.Unlock()
c.mu.Lock()
if c.v != "" {
return c.v, nil
}
if c.trim {
v, err = cl.getTrimmed(c.k)
} else {
v, err = cl.Get(c.k)
}
if err == nil {
c.v = v
}
return
}
var (
onGCEOnce sync.Once
onGCE bool
)
// OnGCE reports whether this process is running on Google Compute Engine.
func OnGCE() bool {
onGCEOnce.Do(initOnGCE)
return onGCE
}
func initOnGCE() {
onGCE = testOnGCE()
}
func testOnGCE() bool {
// The user explicitly said they're on GCE, so trust them.
if os.Getenv(metadataHostEnv) != "" {
return true
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
resc := make(chan bool, 2)
// Try two strategies in parallel.
// See https://github.com/GoogleCloudPlatform/google-cloud-go/issues/194
go func() {
req, _ := http.NewRequest("GET", "http://"+metadataIP, nil)
req.Header.Set("User-Agent", userAgent)
res, err := defaultClient.hc.Do(req.WithContext(ctx))
if err != nil {
resc <- false
return
}
defer res.Body.Close()
resc <- res.Header.Get("Metadata-Flavor") == "Google"
}()
go func() {
addrs, err := net.LookupHost("metadata.google.internal")
if err != nil || len(addrs) == 0 {
resc <- false
return
}
resc <- strsContains(addrs, metadataIP)
}()
tryHarder := systemInfoSuggestsGCE()
if tryHarder {
res := <-resc
if res {
// The first strategy succeeded, so let's use it.
return true
}
// Wait for either the DNS or metadata server probe to
// contradict the other one and say we are running on
// GCE. Give it a lot of time to do so, since the system
// info already suggests we're running on a GCE BIOS.
timer := time.NewTimer(5 * time.Second)
defer timer.Stop()
select {
case res = <-resc:
return res
case <-timer.C:
// Too slow. Who knows what this system is.
return false
}
}
// There's no hint from the system info that we're running on
// GCE, so use the first probe's result as truth, whether it's
// true or false. The goal here is to optimize for speed for
// users who are NOT running on GCE. We can't assume that
// either a DNS lookup or an HTTP request to a blackholed IP
// address is fast. Worst case this should return when the
// metaClient's Transport.ResponseHeaderTimeout or
// Transport.Dial.Timeout fires (in two seconds).
return <-resc
}
// systemInfoSuggestsGCE reports whether the local system (without
// doing network requests) suggests that we're running on GCE. If this
// returns true, testOnGCE tries a bit harder to reach its metadata
// server.
func systemInfoSuggestsGCE() bool {
if runtime.GOOS != "linux" {
// We don't have any non-Linux clues available, at least yet.
return false
}
slurp, _ := ioutil.ReadFile("/sys/class/dmi/id/product_name")
name := strings.TrimSpace(string(slurp))
return name == "Google" || name == "Google Compute Engine"
}
// Subscribe calls Client.Subscribe on a client designed for subscribing (one with no
// ResponseHeaderTimeout).
func Subscribe(suffix string, fn func(v string, ok bool) error) error {
return subscribeClient.Subscribe(suffix, fn)
}
// Get calls Client.Get on the default client.
func Get(suffix string) (string, error) { return defaultClient.Get(suffix) }
// ProjectID returns the current instance's project ID string.
func ProjectID() (string, error) { return defaultClient.ProjectID() }
// NumericProjectID returns the current instance's numeric project ID.
func NumericProjectID() (string, error) { return defaultClient.NumericProjectID() }
// InternalIP returns the instance's primary internal IP address.
func InternalIP() (string, error) { return defaultClient.InternalIP() }
// ExternalIP returns the instance's primary external (public) IP address.
func ExternalIP() (string, error) { return defaultClient.ExternalIP() }
// Hostname returns the instance's hostname. This will be of the form
// "<instanceID>.c.<projID>.internal".
func Hostname() (string, error) { return defaultClient.Hostname() }
// InstanceTags returns the list of user-defined instance tags,
// assigned when initially creating a GCE instance.
func InstanceTags() ([]string, error) { return defaultClient.InstanceTags() }
// InstanceID returns the current VM's numeric instance ID.
func InstanceID() (string, error) { return defaultClient.InstanceID() }
// InstanceName returns the current VM's instance ID string.
func InstanceName() (string, error) { return defaultClient.InstanceName() }
// Zone returns the current VM's zone, such as "us-central1-b".
func Zone() (string, error) { return defaultClient.Zone() }
// InstanceAttributes calls Client.InstanceAttributes on the default client.
func InstanceAttributes() ([]string, error) { return defaultClient.InstanceAttributes() }
// ProjectAttributes calls Client.ProjectAttributes on the default client.
func ProjectAttributes() ([]string, error) { return defaultClient.ProjectAttributes() }
// InstanceAttributeValue calls Client.InstanceAttributeValue on the default client.
func InstanceAttributeValue(attr string) (string, error) {
return defaultClient.InstanceAttributeValue(attr)
}
// ProjectAttributeValue calls Client.ProjectAttributeValue on the default client.
func ProjectAttributeValue(attr string) (string, error) {
return defaultClient.ProjectAttributeValue(attr)
}
// Scopes calls Client.Scopes on the default client.
func Scopes(serviceAccount string) ([]string, error) { return defaultClient.Scopes(serviceAccount) }
func strsContains(ss []string, s string) bool {
for _, v := range ss {
if v == s {
return true
}
}
return false
}
// A Client provides metadata.
type Client struct {
hc *http.Client
}
// NewClient returns a Client that can be used to fetch metadata. All HTTP requests
// will use the given http.Client instead of the default client.
func NewClient(c *http.Client) *Client {
return &Client{hc: c}
}
// getETag returns a value from the metadata service as well as the associated ETag.
// This func is otherwise equivalent to Get.
func (c *Client) getETag(suffix string) (value, etag string, err error) {
// Using a fixed IP makes it very difficult to spoof the metadata service in
// a container, which is an important use-case for local testing of cloud
// deployments. To enable spoofing of the metadata service, the environment
// variable GCE_METADATA_HOST is first inspected to decide where metadata
// requests shall go.
host := os.Getenv(metadataHostEnv)
if host == "" {
// Using 169.254.169.254 instead of "metadata" here because Go
// binaries built with the "netgo" tag and without cgo won't
// know the search suffix for "metadata" is
// ".google.internal", and this IP address is documented as
// being stable anyway.
host = metadataIP
}
url := "http://" + host + "/computeMetadata/v1/" + suffix
req, _ := http.NewRequest("GET", url, nil)
req.Header.Set("Metadata-Flavor", "Google")
req.Header.Set("User-Agent", userAgent)
res, err := c.hc.Do(req)
if err != nil {
return "", "", err
}
defer res.Body.Close()
if res.StatusCode == http.StatusNotFound {
return "", "", NotDefinedError(suffix)
}
if res.StatusCode != 200 {
return "", "", fmt.Errorf("status code %d trying to fetch %s", res.StatusCode, url)
}
all, err := ioutil.ReadAll(res.Body)
if err != nil {
return "", "", err
}
return string(all), res.Header.Get("Etag"), nil
}
// Get returns a value from the metadata service.
// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/".
//
// If the GCE_METADATA_HOST environment variable is not defined, a default of
// 169.254.169.254 will be used instead.
//
// If the requested metadata is not defined, the returned error will
// be of type NotDefinedError.
func (c *Client) Get(suffix string) (string, error) {
val, _, err := c.getETag(suffix)
return val, err
}
func (c *Client) getTrimmed(suffix string) (s string, err error) {
s, err = c.Get(suffix)
s = strings.TrimSpace(s)
return
}
func (c *Client) lines(suffix string) ([]string, error) {
j, err := c.Get(suffix)
if err != nil {
return nil, err
}
s := strings.Split(strings.TrimSpace(j), "\n")
for i := range s {
s[i] = strings.TrimSpace(s[i])
}
return s, nil
}
// ProjectID returns the current instance's project ID string.
func (c *Client) ProjectID() (string, error) { return projID.get(c) }
// NumericProjectID returns the current instance's numeric project ID.
func (c *Client) NumericProjectID() (string, error) { return projNum.get(c) }
// InstanceID returns the current VM's numeric instance ID.
func (c *Client) InstanceID() (string, error) { return instID.get(c) }
// InternalIP returns the instance's primary internal IP address.
func (c *Client) InternalIP() (string, error) {
return c.getTrimmed("instance/network-interfaces/0/ip")
}
// ExternalIP returns the instance's primary external (public) IP address.
func (c *Client) ExternalIP() (string, error) {
return c.getTrimmed("instance/network-interfaces/0/access-configs/0/external-ip")
}
// Hostname returns the instance's hostname. This will be of the form
// "<instanceID>.c.<projID>.internal".
func (c *Client) Hostname() (string, error) {
return c.getTrimmed("instance/hostname")
}
// InstanceTags returns the list of user-defined instance tags,
// assigned when initially creating a GCE instance.
func (c *Client) InstanceTags() ([]string, error) {
var s []string
j, err := c.Get("instance/tags")
if err != nil {
return nil, err
}
if err := json.NewDecoder(strings.NewReader(j)).Decode(&s); err != nil {
return nil, err
}
return s, nil
}
// InstanceName returns the current VM's instance ID string.
func (c *Client) InstanceName() (string, error) {
host, err := c.Hostname()
if err != nil {
return "", err
}
return strings.Split(host, ".")[0], nil
}
// Zone returns the current VM's zone, such as "us-central1-b".
func (c *Client) Zone() (string, error) {
zone, err := c.getTrimmed("instance/zone")
// zone is of the form "projects/<projNum>/zones/<zoneName>".
if err != nil {
return "", err
}
return zone[strings.LastIndex(zone, "/")+1:], nil
}
// InstanceAttributes returns the list of user-defined attributes,
// assigned when initially creating a GCE VM instance. The value of an
// attribute can be obtained with InstanceAttributeValue.
func (c *Client) InstanceAttributes() ([]string, error) { return c.lines("instance/attributes/") }
// ProjectAttributes returns the list of user-defined attributes
// applying to the project as a whole, not just this VM. The value of
// an attribute can be obtained with ProjectAttributeValue.
func (c *Client) ProjectAttributes() ([]string, error) { return c.lines("project/attributes/") }
// InstanceAttributeValue returns the value of the provided VM
// instance attribute.
//
// If the requested attribute is not defined, the returned error will
// be of type NotDefinedError.
//
// InstanceAttributeValue may return ("", nil) if the attribute was
// defined to be the empty string.
func (c *Client) InstanceAttributeValue(attr string) (string, error) {
return c.Get("instance/attributes/" + attr)
}
// ProjectAttributeValue returns the value of the provided
// project attribute.
//
// If the requested attribute is not defined, the returned error will
// be of type NotDefinedError.
//
// ProjectAttributeValue may return ("", nil) if the attribute was
// defined to be the empty string.
func (c *Client) ProjectAttributeValue(attr string) (string, error) {
return c.Get("project/attributes/" + attr)
}
// Scopes returns the service account scopes for the given account.
// The account may be empty or the string "default" to use the instance's
// main account.
func (c *Client) Scopes(serviceAccount string) ([]string, error) {
if serviceAccount == "" {
serviceAccount = "default"
}
return c.lines("instance/service-accounts/" + serviceAccount + "/scopes")
}
// Subscribe subscribes to a value from the metadata service.
// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/".
// The suffix may contain query parameters.
//
// Subscribe calls fn with the latest metadata value indicated by the provided
// suffix. If the metadata value is deleted, fn is called with the empty string
// and ok false. Subscribe blocks until fn returns a non-nil error or the value
// is deleted. Subscribe returns the error value returned from the last call to
// fn, which may be nil when ok == false.
func (c *Client) Subscribe(suffix string, fn func(v string, ok bool) error) error {
const failedSubscribeSleep = time.Second * 5
// First check to see if the metadata value exists at all.
val, lastETag, err := c.getETag(suffix)
if err != nil {
return err
}
if err := fn(val, true); err != nil {
return err
}
ok := true
if strings.ContainsRune(suffix, '?') {
suffix += "&wait_for_change=true&last_etag="
} else {
suffix += "?wait_for_change=true&last_etag="
}
for {
val, etag, err := c.getETag(suffix + url.QueryEscape(lastETag))
if err != nil {
if _, deleted := err.(NotDefinedError); !deleted {
time.Sleep(failedSubscribeSleep)
continue // Retry on other errors.
}
ok = false
}
lastETag = etag
if err := fn(val, ok); err != nil || !ok {
return err
}
}
}

View File

@ -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.

View File

@ -1,20 +0,0 @@
Copyright (c) 2015-Present CloudFoundry.org Foundation, Inc. All Rights Reserved.
This project contains software that is Copyright (c) 2013-2015 Pivotal Software, Inc.
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.
This project may include a number of subcomponents with separate
copyright notices and license terms. Your use of these subcomponents
is subject to the terms and conditions of each subcomponent's license,
as noted in the LICENSE file.

View File

@ -1,15 +0,0 @@
bytefmt
=======
**Note**: This repository should be imported as `code.cloudfoundry.org/bytefmt`.
Human-readable byte formatter.
Example:
```go
bytefmt.ByteSize(100.5*bytefmt.MEGABYTE) // returns "100.5M"
bytefmt.ByteSize(uint64(1024)) // returns "1K"
```
For documentation, please see http://godoc.org/code.cloudfoundry.org/bytefmt

View File

@ -1,105 +0,0 @@
// Package bytefmt contains helper methods and constants for converting to and from a human-readable byte format.
//
// bytefmt.ByteSize(100.5*bytefmt.MEGABYTE) // "100.5M"
// bytefmt.ByteSize(uint64(1024)) // "1K"
//
package bytefmt
import (
"errors"
"strconv"
"strings"
"unicode"
)
const (
BYTE = 1 << (10 * iota)
KILOBYTE
MEGABYTE
GIGABYTE
TERABYTE
)
var invalidByteQuantityError = errors.New("byte quantity must be a positive integer with a unit of measurement like M, MB, MiB, G, GiB, or GB")
// ByteSize returns a human-readable byte string of the form 10M, 12.5K, and so forth. The following units are available:
// T: Terabyte
// G: Gigabyte
// M: Megabyte
// K: Kilobyte
// B: Byte
// The unit that results in the smallest number greater than or equal to 1 is always chosen.
func ByteSize(bytes uint64) string {
unit := ""
value := float64(bytes)
switch {
case bytes >= TERABYTE:
unit = "T"
value = value / TERABYTE
case bytes >= GIGABYTE:
unit = "G"
value = value / GIGABYTE
case bytes >= MEGABYTE:
unit = "M"
value = value / MEGABYTE
case bytes >= KILOBYTE:
unit = "K"
value = value / KILOBYTE
case bytes >= BYTE:
unit = "B"
case bytes == 0:
return "0"
}
result := strconv.FormatFloat(value, 'f', 1, 64)
result = strings.TrimSuffix(result, ".0")
return result + unit
}
// ToMegabytes parses a string formatted by ByteSize as megabytes.
func ToMegabytes(s string) (uint64, error) {
bytes, err := ToBytes(s)
if err != nil {
return 0, err
}
return bytes / MEGABYTE, nil
}
// ToBytes parses a string formatted by ByteSize as bytes. Note binary-prefixed and SI prefixed units both mean a base-2 units
// KB = K = KiB = 1024
// MB = M = MiB = 1024 * K
// GB = G = GiB = 1024 * M
// TB = T = TiB = 1024 * G
func ToBytes(s string) (uint64, error) {
s = strings.TrimSpace(s)
s = strings.ToUpper(s)
i := strings.IndexFunc(s, unicode.IsLetter)
if i == -1 {
return 0, invalidByteQuantityError
}
bytesString, multiple := s[:i], s[i:]
bytes, err := strconv.ParseFloat(bytesString, 64)
if err != nil || bytes <= 0 {
return 0, invalidByteQuantityError
}
switch multiple {
case "T", "TB", "TIB":
return uint64(bytes * TERABYTE), nil
case "G", "GB", "GIB":
return uint64(bytes * GIGABYTE), nil
case "M", "MB", "MIB":
return uint64(bytes * MEGABYTE), nil
case "K", "KB", "KIB":
return uint64(bytes * KILOBYTE), nil
case "B":
return uint64(bytes), nil
default:
return 0, invalidByteQuantityError
}
}

View File

@ -1 +0,0 @@
package bytefmt // import "code.cloudfoundry.org/bytefmt"

View File

@ -1,2 +0,0 @@
bin/*
*.swp

View File

@ -1,33 +0,0 @@
project_name: go-pagerduty
release:
github:
owner: PagerDuty
name: go-pagerduty
draft: false
prerelease: true
name_template: "{{.ProjectName}}-v{{.Version}}"
builds:
- goos:
- linux
- darwin
- windows
goarch:
- amd64
- 386
main: ./command/*.go
binary: pd
archive:
format: tar.gz
format_overrides:
- goos: windows
format: zip
replacements:
amd64: 64-bit
darwin: macOS
linux: Tux
name_template: "{{.Binary}}_{{.Version}}_{{.Os}}-{{.Arch}}"
files:
- LICENSE.txt
- README.md
- examples/*
- CHANGELOG.md

View File

@ -1,24 +0,0 @@
language: go
addons:
apt:
packages:
# needed for the nfpm pipe:
- rpm
# needed for the docker pipe
# services:
# - docker
# calls goreleaser
deploy:
skip_cleanup: true
provider: script
script: make deploy
on:
tags: true
notifications:
email: false
on:
tags: true
condition: $TRAVIS_OS_NAME = linux

View File

@ -1,89 +0,0 @@
# Change Log
## [1.0.0](https://github.com/PagerDuty/go-pagerduty/tree/1.0.0) (2018-05-28)
**Fixed bugs:**
- Escalation Policy's repeat\_enabled Is Ignored [\#57](https://github.com/PagerDuty/go-pagerduty/issues/57)
- Problems running freshly built pd utility [\#39](https://github.com/PagerDuty/go-pagerduty/issues/39)
- Manage Incident gives error [\#32](https://github.com/PagerDuty/go-pagerduty/issues/32)
- Added missing slash to delete integration method url [\#59](https://github.com/PagerDuty/go-pagerduty/pull/59) ([jescochu](https://github.com/jescochu))
**Closed issues:**
- Trouble creating an integration [\#102](https://github.com/PagerDuty/go-pagerduty/issues/102)
- Client does not trigger events [\#101](https://github.com/PagerDuty/go-pagerduty/issues/101)
- Paging help [\#94](https://github.com/PagerDuty/go-pagerduty/issues/94)
- Help with incident creation API [\#89](https://github.com/PagerDuty/go-pagerduty/issues/89)
- Memory leak because of response body is not closed [\#66](https://github.com/PagerDuty/go-pagerduty/issues/66)
- Since and Until don't work for log\_entries [\#61](https://github.com/PagerDuty/go-pagerduty/issues/61)
- service: auto\_resolve\_timeout & acknowledgement\_timeout cannot be set to null [\#51](https://github.com/PagerDuty/go-pagerduty/issues/51)
- Possible to create new service and integration together [\#42](https://github.com/PagerDuty/go-pagerduty/issues/42)
- Documentation does not match code [\#16](https://github.com/PagerDuty/go-pagerduty/issues/16)
- Typo in repo description [\#15](https://github.com/PagerDuty/go-pagerduty/issues/15)
- Webhook decoder [\#14](https://github.com/PagerDuty/go-pagerduty/issues/14)
- incident\_key for create\_event [\#13](https://github.com/PagerDuty/go-pagerduty/issues/13)
**Merged pull requests:**
- Fix pagination for ListOnCalls [\#90](https://github.com/PagerDuty/go-pagerduty/pull/90) ([IainCole](https://github.com/IainCole))
- Revert "Fix inconsistency with some REST Options objects passed by reference …" [\#88](https://github.com/PagerDuty/go-pagerduty/pull/88) ([mimato](https://github.com/mimato))
- Adding travis config, fixup Makefile [\#87](https://github.com/PagerDuty/go-pagerduty/pull/87) ([mimato](https://github.com/mimato))
- Fixed invalid JSON descriptor for FirstTriggerLogEntry [\#86](https://github.com/PagerDuty/go-pagerduty/pull/86) ([mwisniewski0](https://github.com/mwisniewski0))
- \[incidents\] fix entries typo in a few places [\#85](https://github.com/PagerDuty/go-pagerduty/pull/85) ([joeyparsons](https://github.com/joeyparsons))
- Fix inconsistency with some REST Options objects passed by reference … [\#79](https://github.com/PagerDuty/go-pagerduty/pull/79) ([lowesoftware](https://github.com/lowesoftware))
- Explicit JSON reference to schedules [\#77](https://github.com/PagerDuty/go-pagerduty/pull/77) ([domudall](https://github.com/domudall))
- Adding AlertCreation to Service struct [\#76](https://github.com/PagerDuty/go-pagerduty/pull/76) ([domudall](https://github.com/domudall))
- Add support for escalation rules [\#71](https://github.com/PagerDuty/go-pagerduty/pull/71) ([heimweh](https://github.com/heimweh))
- Fix maintenance window JSON [\#69](https://github.com/PagerDuty/go-pagerduty/pull/69) ([domudall](https://github.com/domudall))
- Fixing Maintenance typo [\#68](https://github.com/PagerDuty/go-pagerduty/pull/68) ([domudall](https://github.com/domudall))
- Update event.go - fix a memory leak [\#65](https://github.com/PagerDuty/go-pagerduty/pull/65) ([AngelRefael](https://github.com/AngelRefael))
- Add query to vendor [\#64](https://github.com/PagerDuty/go-pagerduty/pull/64) ([heimweh](https://github.com/heimweh))
- Fix JSON decode \(errorObject\) [\#63](https://github.com/PagerDuty/go-pagerduty/pull/63) ([heimweh](https://github.com/heimweh))
- fix since and until by adding them to url scheme [\#60](https://github.com/PagerDuty/go-pagerduty/pull/60) ([ethansommer](https://github.com/ethansommer))
- fix webhook struct member name [\#58](https://github.com/PagerDuty/go-pagerduty/pull/58) ([pgray](https://github.com/pgray))
- Incident - Add status field to incident [\#56](https://github.com/PagerDuty/go-pagerduty/pull/56) ([heimweh](https://github.com/heimweh))
- enable fetch log entries via incident api [\#55](https://github.com/PagerDuty/go-pagerduty/pull/55) ([flyinprogrammer](https://github.com/flyinprogrammer))
- Allow service timeouts to be disabled [\#53](https://github.com/PagerDuty/go-pagerduty/pull/53) ([heimweh](https://github.com/heimweh))
- Schedule restriction - Add support for start\_day\_of\_week [\#52](https://github.com/PagerDuty/go-pagerduty/pull/52) ([heimweh](https://github.com/heimweh))
- Add vendor support [\#49](https://github.com/PagerDuty/go-pagerduty/pull/49) ([heimweh](https://github.com/heimweh))
- Add schedules listing [\#46](https://github.com/PagerDuty/go-pagerduty/pull/46) ([Marc-Morata-Fite](https://github.com/Marc-Morata-Fite))
- dont declare main twice in examples [\#45](https://github.com/PagerDuty/go-pagerduty/pull/45) ([ranjib](https://github.com/ranjib))
- add service show [\#44](https://github.com/PagerDuty/go-pagerduty/pull/44) ([cmluciano](https://github.com/cmluciano))
- \(feat\)implement integration creation [\#43](https://github.com/PagerDuty/go-pagerduty/pull/43) ([ranjib](https://github.com/ranjib))
- \(chore\)add create event example [\#41](https://github.com/PagerDuty/go-pagerduty/pull/41) ([ranjib](https://github.com/ranjib))
- \(bug\)Add test. fix version issue [\#40](https://github.com/PagerDuty/go-pagerduty/pull/40) ([ranjib](https://github.com/ranjib))
- Remove subdomain argument from escalation\_policy example. [\#38](https://github.com/PagerDuty/go-pagerduty/pull/38) ([cmluciano](https://github.com/cmluciano))
- Skip JSON encoding if no payload was given [\#37](https://github.com/PagerDuty/go-pagerduty/pull/37) ([heimweh](https://github.com/heimweh))
- \(feat\)add ability API and CLI [\#36](https://github.com/PagerDuty/go-pagerduty/pull/36) ([ranjib](https://github.com/ranjib))
- Make updates to Escalation Policies work [\#35](https://github.com/PagerDuty/go-pagerduty/pull/35) ([heimweh](https://github.com/heimweh))
- Fix misspelling in User struct and add JSON tags [\#34](https://github.com/PagerDuty/go-pagerduty/pull/34) ([heimweh](https://github.com/heimweh))
- \(bug\)allow passing headers in http do call. fix manage incident call [\#33](https://github.com/PagerDuty/go-pagerduty/pull/33) ([ranjib](https://github.com/ranjib))
- \(chore\)get rid of logrus from all core structs except CLI entries. fix schedule override command [\#31](https://github.com/PagerDuty/go-pagerduty/pull/31) ([ranjib](https://github.com/ranjib))
- \(bug\)rename override struct [\#30](https://github.com/PagerDuty/go-pagerduty/pull/30) ([ranjib](https://github.com/ranjib))
- \(bug\)implement schedule override [\#29](https://github.com/PagerDuty/go-pagerduty/pull/29) ([ranjib](https://github.com/ranjib))
- fix misspelling in trigger\_summary\_data's JSON key. [\#28](https://github.com/PagerDuty/go-pagerduty/pull/28) ([tomwans](https://github.com/tomwans))
- Correctly set meta flag for incident list [\#26](https://github.com/PagerDuty/go-pagerduty/pull/26) ([afirth](https://github.com/afirth))
- Add \*.swp to gitignore [\#25](https://github.com/PagerDuty/go-pagerduty/pull/25) ([afirth](https://github.com/afirth))
- Support the /oncalls endpoint in the CLI [\#24](https://github.com/PagerDuty/go-pagerduty/pull/24) ([afirth](https://github.com/afirth))
- Refactor to work correctly with V2 API [\#23](https://github.com/PagerDuty/go-pagerduty/pull/23) ([dthagard](https://github.com/dthagard))
- \(feat\)Add webhook decoding capability [\#22](https://github.com/PagerDuty/go-pagerduty/pull/22) ([ranjib](https://github.com/ranjib))
- \(chore\)Decode event API response. [\#21](https://github.com/PagerDuty/go-pagerduty/pull/21) ([ranjib](https://github.com/ranjib))
- \(bug\)add incident\_key field in event api client [\#20](https://github.com/PagerDuty/go-pagerduty/pull/20) ([ranjib](https://github.com/ranjib))
- \(chore\)nuke sub domain, v2 api does not need one [\#19](https://github.com/PagerDuty/go-pagerduty/pull/19) ([ranjib](https://github.com/ranjib))
- Implement list users CLI [\#17](https://github.com/PagerDuty/go-pagerduty/pull/17) ([ranjib](https://github.com/ranjib))
- Add team\_ids\[\] query string arg [\#12](https://github.com/PagerDuty/go-pagerduty/pull/12) ([marklap](https://github.com/marklap))
- Incidents fix [\#11](https://github.com/PagerDuty/go-pagerduty/pull/11) ([jareksm](https://github.com/jareksm))
- Added APIListObject to Option types to allow setting offset and [\#10](https://github.com/PagerDuty/go-pagerduty/pull/10) ([jareksm](https://github.com/jareksm))
- fix typo [\#9](https://github.com/PagerDuty/go-pagerduty/pull/9) ([sjansen](https://github.com/sjansen))
- implement incident list cli. event posting api [\#8](https://github.com/PagerDuty/go-pagerduty/pull/8) ([ranjib](https://github.com/ranjib))
- CLI for create escalation policy, maintainenance window , schedule ov… [\#7](https://github.com/PagerDuty/go-pagerduty/pull/7) ([ranjib](https://github.com/ranjib))
- \(feat\)implement create service cli [\#6](https://github.com/PagerDuty/go-pagerduty/pull/6) ([ranjib](https://github.com/ranjib))
- \(feat\)list service cli [\#5](https://github.com/PagerDuty/go-pagerduty/pull/5) ([ranjib](https://github.com/ranjib))
- \(feat\)implement addon update/delete [\#4](https://github.com/PagerDuty/go-pagerduty/pull/4) ([ranjib](https://github.com/ranjib))
- \(feat\)Show addon cli [\#3](https://github.com/PagerDuty/go-pagerduty/pull/3) ([ranjib](https://github.com/ranjib))
- \(feat\) addon list api. create cli [\#2](https://github.com/PagerDuty/go-pagerduty/pull/2) ([ranjib](https://github.com/ranjib))
- \(chore\) list addon [\#1](https://github.com/PagerDuty/go-pagerduty/pull/1) ([ranjib](https://github.com/ranjib))
\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*

View File

@ -1,4 +0,0 @@
FROM golang
ADD . /go/src/github.com/PagerDuty/go-pagerduty
WORKDIR /go/src/github.com/PagerDuty/go-pagerduty
RUN go get ./... && go test -v -race -cover ./...

View File

@ -1,14 +0,0 @@
Copyright:: Copyright (c) 2016 PagerDuty, Inc.
License:: Apache License, Version 2.0
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.

View File

@ -1,23 +0,0 @@
# SOURCEDIR=.
# SOURCES = $(shell find $(SOURCEDIR) -name '*.go')
# VERSION=$(git describe --always --tags)
# BINARY=bin/pd
# bin: $(BINARY)
# $(BINARY): $(SOURCES)
# go build -o $(BINARY) command/*
.PHONY: build
build:
go get ./...
# go test -v -race -cover ./...
# go tool vet $(SOURCES)
.PHONY: test
test:
go test ./...
deploy:
- curl -sL https://git.io/goreleaser | bash

View File

@ -1,73 +0,0 @@
# go-pagerduty
go-pagerduty is a CLI and [go](https://golang.org/) client library for [PagerDuty v2 API](https://v2.developer.pagerduty.com/v2/page/api-reference).
[godoc](http://godoc.org/github.com/PagerDuty/go-pagerduty)
## Installation
```
go get github.com/PagerDuty/go-pagerduty
```
## Usage
### CLI
The CLI requires authentication token, which can be sepcified in `.pd.yml`
file in home directory of the user, or passed as command line argument.
Example of config file:
```yaml
---
authtoken: fooBar
```
`pd` command provides a single entrypoint for all the API endpoints, with individual
API represented by their own sub commands. For an exhaustive list of sub-commands, try:
```
pd --help
```
An example of the `service` sub-command
```
pd service list
```
### From golang libraries
```go
package main
import (
"fmt"
"github.com/PagerDuty/go-pagerduty"
)
var authtoken = "" // Set your auth token here
func main() {
var opts pagerduty.ListEscalationPoliciesOptions
client := pagerduty.NewClient(authtoken)
if eps, err := client.ListEscalationPolicies(opts); err != nil {
panic(err)
} else {
for _, p := range eps.EscalationPolicies {
fmt.Println(p.Name)
}
}
}
```
## License
[Apache 2](http://www.apache.org/licenses/LICENSE-2.0)
## Contributing
1. Fork it ( https://github.com/PagerDuty/go-pagerduty/fork )
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create a new Pull Request

View File

@ -1,22 +0,0 @@
package pagerduty
// ListAbilityResponse is the response when calling the ListAbility API endpoint.
type ListAbilityResponse struct {
Abilities []string `json:"abilities"`
}
// ListAbilities lists all abilities on your account.
func (c *Client) ListAbilities() (*ListAbilityResponse, error) {
resp, err := c.get("/abilities")
if err != nil {
return nil, err
}
var result ListAbilityResponse
return &result, c.decodeJSON(resp, &result)
}
// TestAbility Check if your account has the given ability.
func (c *Client) TestAbility(ability string) error {
_, err := c.get("/abilities/" + ability)
return err
}

View File

@ -1,96 +0,0 @@
package pagerduty
import (
"fmt"
"github.com/google/go-querystring/query"
"net/http"
)
// Addon is a third-party add-on to PagerDuty's UI.
type Addon struct {
APIObject
Name string `json:"name,omitempty"`
Src string `json:"src,omitempty"`
Services []APIObject `json:"services,omitempty"`
}
// ListAddonOptions are the options available when calling the ListAddons API endpoint.
type ListAddonOptions struct {
APIListObject
Includes []string `url:"include,omitempty,brackets"`
ServiceIDs []string `url:"service_ids,omitempty,brackets"`
Filter string `url:"filter,omitempty"`
}
// ListAddonResponse is the response when calling the ListAddons API endpoint.
type ListAddonResponse struct {
APIListObject
Addons []Addon `json:"addons"`
}
// ListAddons lists all of the add-ons installed on your account.
func (c *Client) ListAddons(o ListAddonOptions) (*ListAddonResponse, error) {
v, err := query.Values(o)
if err != nil {
return nil, err
}
resp, err := c.get("/addons?" + v.Encode())
if err != nil {
return nil, err
}
var result ListAddonResponse
return &result, c.decodeJSON(resp, &result)
}
// InstallAddon installs an add-on for your account.
func (c *Client) InstallAddon(a Addon) (*Addon, error) {
data := make(map[string]Addon)
data["addon"] = a
resp, err := c.post("/addons", data)
defer resp.Body.Close()
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusCreated {
return nil, fmt.Errorf("Failed to create. HTTP Status code: %d", resp.StatusCode)
}
return getAddonFromResponse(c, resp)
}
// DeleteAddon deletes an add-on from your account.
func (c *Client) DeleteAddon(id string) error {
_, err := c.delete("/addons/" + id)
return err
}
// GetAddon gets details about an existing add-on.
func (c *Client) GetAddon(id string) (*Addon, error) {
resp, err := c.get("/addons/" + id)
if err != nil {
return nil, err
}
return getAddonFromResponse(c, resp)
}
// UpdateAddon updates an existing add-on.
func (c *Client) UpdateAddon(id string, a Addon) (*Addon, error) {
v := make(map[string]Addon)
v["addon"] = a
resp, err := c.put("/addons/"+id, v, nil)
if err != nil {
return nil, err
}
return getAddonFromResponse(c, resp)
}
func getAddonFromResponse(c *Client, resp *http.Response) (*Addon, error) {
var result map[string]Addon
if err := c.decodeJSON(resp, &result); err != nil {
return nil, err
}
a, ok := result["addon"]
if !ok {
return nil, fmt.Errorf("JSON response does not have 'addon' field")
}
return &a, nil
}

View File

@ -1,132 +0,0 @@
package pagerduty
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
const (
apiEndpoint = "https://api.pagerduty.com"
)
// APIObject represents generic api json response that is shared by most
// domain object (like escalation
type APIObject struct {
ID string `json:"id,omitempty"`
Type string `json:"type,omitempty"`
Summary string `json:"summary,omitempty"`
Self string `json:"self,omitempty"`
HTMLURL string `json:"html_url,omitempty"`
}
// APIListObject are the fields used to control pagination when listing objects.
type APIListObject struct {
Limit uint `url:"limit,omitempty"`
Offset uint `url:"offset,omitempty"`
More bool `url:"more,omitempty"`
Total uint `url:"total,omitempty"`
}
// APIReference are the fields required to reference another API object.
type APIReference struct {
ID string `json:"id,omitempty"`
Type string `json:"type,omitempty"`
}
type errorObject struct {
Code int `json:"code,omitempty"`
Message string `json:"message,omitempty"`
Errors interface{} `json:"errors,omitempty"`
}
// Client wraps http client
type Client struct {
authToken string
}
// NewClient creates an API client
func NewClient(authToken string) *Client {
return &Client{
authToken: authToken,
}
}
func (c *Client) delete(path string) (*http.Response, error) {
return c.do("DELETE", path, nil, nil)
}
func (c *Client) put(path string, payload interface{}, headers *map[string]string) (*http.Response, error) {
if payload != nil {
data, err := json.Marshal(payload)
if err != nil {
return nil, err
}
return c.do("PUT", path, bytes.NewBuffer(data), headers)
}
return c.do("PUT", path, nil, headers)
}
func (c *Client) post(path string, payload interface{}) (*http.Response, error) {
data, err := json.Marshal(payload)
if err != nil {
return nil, err
}
return c.do("POST", path, bytes.NewBuffer(data), nil)
}
func (c *Client) get(path string) (*http.Response, error) {
return c.do("GET", path, nil, nil)
}
func (c *Client) do(method, path string, body io.Reader, headers *map[string]string) (*http.Response, error) {
endpoint := apiEndpoint + path
req, _ := http.NewRequest(method, endpoint, body)
req.Header.Set("Accept", "application/vnd.pagerduty+json;version=2")
if headers != nil {
for k, v := range *headers {
req.Header.Set(k, v)
}
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Token token="+c.authToken)
resp, err := http.DefaultClient.Do(req)
return c.checkResponse(resp, err)
}
func (c *Client) decodeJSON(resp *http.Response, payload interface{}) error {
defer resp.Body.Close()
decoder := json.NewDecoder(resp.Body)
return decoder.Decode(payload)
}
func (c *Client) checkResponse(resp *http.Response, err error) (*http.Response, error) {
if err != nil {
return resp, fmt.Errorf("Error calling the API endpoint: %v", err)
}
if 199 >= resp.StatusCode || 300 <= resp.StatusCode {
var eo *errorObject
var getErr error
if eo, getErr = c.getErrorFromResponse(resp); getErr != nil {
return resp, fmt.Errorf("Response did not contain formatted error: %s. HTTP response code: %v. Raw response: %+v", getErr, resp.StatusCode, resp)
}
return resp, fmt.Errorf("Failed call API endpoint. HTTP response code: %v. Error: %v", resp.StatusCode, eo)
}
return resp, nil
}
func (c *Client) getErrorFromResponse(resp *http.Response) (*errorObject, error) {
var result map[string]errorObject
if err := c.decodeJSON(resp, &result); err != nil {
return nil, fmt.Errorf("Could not decode JSON response: %v", err)
}
s, ok := result["error"]
if !ok {
return nil, fmt.Errorf("JSON response does not have error field")
}
return &s, nil
}

View File

@ -1,186 +0,0 @@
package pagerduty
import (
"fmt"
"net/http"
"github.com/google/go-querystring/query"
)
const (
escPath = "/escalation_policies"
)
// EscalationRule is a rule for an escalation policy to trigger.
type EscalationRule struct {
ID string `json:"id,omitempty"`
Delay uint `json:"escalation_delay_in_minutes,omitempty"`
Targets []APIObject `json:"targets"`
}
// EscalationPolicy is a collection of escalation rules.
type EscalationPolicy struct {
APIObject
Name string `json:"name,omitempty"`
EscalationRules []EscalationRule `json:"escalation_rules,omitempty"`
Services []APIReference `json:"services,omitempty"`
NumLoops uint `json:"num_loops,omitempty"`
Teams []APIReference `json:"teams,omitempty"`
Description string `json:"description,omitempty"`
RepeatEnabled bool `json:"repeat_enabled,omitempty"`
}
// ListEscalationPoliciesResponse is the data structure returned from calling the ListEscalationPolicies API endpoint.
type ListEscalationPoliciesResponse struct {
APIListObject
EscalationPolicies []EscalationPolicy `json:"escalation_policies"`
}
type ListEscalationRulesResponse struct {
APIListObject
EscalationRules []EscalationRule `json:"escalation_rules"`
}
// ListEscalationPoliciesOptions is the data structure used when calling the ListEscalationPolicies API endpoint.
type ListEscalationPoliciesOptions struct {
APIListObject
Query string `url:"query,omitempty"`
UserIDs []string `url:"user_ids,omitempty,brackets"`
TeamIDs []string `url:"team_ids,omitempty,brackets"`
Includes []string `url:"include,omitempty,brackets"`
SortBy string `url:"sort_by,omitempty"`
}
// GetEscalationRuleOptions is the data structure used when calling the GetEscalationRule API endpoint.
type GetEscalationRuleOptions struct {
Includes []string `url:"include,omitempty,brackets"`
}
// ListEscalationPolicies lists all of the existing escalation policies.
func (c *Client) ListEscalationPolicies(o ListEscalationPoliciesOptions) (*ListEscalationPoliciesResponse, error) {
v, err := query.Values(o)
if err != nil {
return nil, err
}
resp, err := c.get(escPath + "?" + v.Encode())
if err != nil {
return nil, err
}
var result ListEscalationPoliciesResponse
return &result, c.decodeJSON(resp, &result)
}
// CreateEscalationPolicy creates a new escalation policy.
func (c *Client) CreateEscalationPolicy(e EscalationPolicy) (*EscalationPolicy, error) {
data := make(map[string]EscalationPolicy)
data["escalation_policy"] = e
resp, err := c.post(escPath, data)
return getEscalationPolicyFromResponse(c, resp, err)
}
// DeleteEscalationPolicy deletes an existing escalation policy and rules.
func (c *Client) DeleteEscalationPolicy(id string) error {
_, err := c.delete(escPath + "/" + id)
return err
}
// GetEscalationPolicyOptions is the data structure used when calling the GetEscalationPolicy API endpoint.
type GetEscalationPolicyOptions struct {
Includes []string `url:"include,omitempty,brackets"`
}
// GetEscalationPolicy gets information about an existing escalation policy and its rules.
func (c *Client) GetEscalationPolicy(id string, o *GetEscalationPolicyOptions) (*EscalationPolicy, error) {
v, err := query.Values(o)
if err != nil {
return nil, err
}
resp, err := c.get(escPath + "/" + id + "?" + v.Encode())
return getEscalationPolicyFromResponse(c, resp, err)
}
// UpdateEscalationPolicy updates an existing escalation policy and its rules.
func (c *Client) UpdateEscalationPolicy(id string, e *EscalationPolicy) (*EscalationPolicy, error) {
data := make(map[string]EscalationPolicy)
data["escalation_policy"] = *e
resp, err := c.put(escPath+"/"+id, data, nil)
return getEscalationPolicyFromResponse(c, resp, err)
}
// CreateEscalationRule creates a new escalation rule for an escalation policy
// and appends it to the end of the existing escalation rules.
func (c *Client) CreateEscalationRule(escID string, e EscalationRule) (*EscalationRule, error) {
data := make(map[string]EscalationRule)
data["escalation_rule"] = e
resp, err := c.post(escPath+"/"+escID+"/escalation_rules", data)
return getEscalationRuleFromResponse(c, resp, err)
}
// GetEscalationRule gets information about an existing escalation rule.
func (c *Client) GetEscalationRule(escID string, id string, o *GetEscalationRuleOptions) (*EscalationRule, error) {
v, err := query.Values(o)
if err != nil {
return nil, err
}
resp, err := c.get(escPath + "/" + escID + "/escalation_rules/" + id + "?" + v.Encode())
return getEscalationRuleFromResponse(c, resp, err)
}
// DeleteEscalationRule deletes an existing escalation rule.
func (c *Client) DeleteEscalationRule(escID string, id string) error {
_, err := c.delete(escPath + "/" + escID + "/escalation_rules/" + id)
return err
}
// UpdateEscalationRule updates an existing escalation rule.
func (c *Client) UpdateEscalationRule(escID string, id string, e *EscalationRule) (*EscalationRule, error) {
data := make(map[string]EscalationRule)
data["escalation_rule"] = *e
resp, err := c.put(escPath+"/"+escID+"/escalation_rules/"+id, data, nil)
return getEscalationRuleFromResponse(c, resp, err)
}
// ListEscalationRules lists all of the escalation rules for an existing escalation policy.
func (c *Client) ListEscalationRules(escID string) (*ListEscalationRulesResponse, error) {
resp, err := c.get(escPath + "/" + escID + "/escalation_rules")
if err != nil {
return nil, err
}
var result ListEscalationRulesResponse
return &result, c.decodeJSON(resp, &result)
}
func getEscalationRuleFromResponse(c *Client, resp *http.Response, err error) (*EscalationRule, error) {
defer resp.Body.Close()
if err != nil {
return nil, err
}
var target map[string]EscalationRule
if dErr := c.decodeJSON(resp, &target); dErr != nil {
return nil, fmt.Errorf("Could not decode JSON response: %v", dErr)
}
rootNode := "escalation_rule"
t, nodeOK := target[rootNode]
if !nodeOK {
return nil, fmt.Errorf("JSON response does not have %s field", rootNode)
}
return &t, nil
}
func getEscalationPolicyFromResponse(c *Client, resp *http.Response, err error) (*EscalationPolicy, error) {
defer resp.Body.Close()
if err != nil {
return nil, err
}
var target map[string]EscalationPolicy
if dErr := c.decodeJSON(resp, &target); dErr != nil {
return nil, fmt.Errorf("Could not decode JSON response: %v", dErr)
}
rootNode := "escalation_policy"
t, nodeOK := target[rootNode]
if !nodeOK {
return nil, fmt.Errorf("JSON response does not have %s field", rootNode)
}
return &t, nil
}

View File

@ -1,52 +0,0 @@
package pagerduty
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
)
const eventEndPoint = "https://events.pagerduty.com/generic/2010-04-15/create_event.json"
// Event stores data for problem reporting, acknowledgement, and resolution.
type Event struct {
ServiceKey string `json:"service_key"`
Type string `json:"event_type"`
IncidentKey string `json:"incident_key,omitempty"`
Description string `json:"description"`
Client string `json:"client,omitempty"`
ClientURL string `json:"client_url,omitempty"`
Details interface{} `json:"details,omitempty"`
Contexts []interface{} `json:"contexts,omitempty"`
}
// EventResponse is the data returned from the CreateEvent API endpoint.
type EventResponse struct {
Status string `json:"status"`
Message string `json:"message"`
IncidentKey string `json:"incident_key"`
}
// CreateEvent sends PagerDuty an event to report, acknowledge, or resolve a problem.
func CreateEvent(e Event) (*EventResponse, error) {
data, err := json.Marshal(e)
if err != nil {
return nil, err
}
req, _ := http.NewRequest("POST", eventEndPoint, bytes.NewBuffer(data))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("HTTP Status Code: %d", resp.StatusCode)
}
var eventResponse EventResponse
if err := json.NewDecoder(resp.Body).Decode(&eventResponse); err != nil {
return nil, err
}
return &eventResponse, nil
}

View File

@ -1,177 +0,0 @@
package pagerduty
import (
"fmt"
"github.com/google/go-querystring/query"
)
// Acknowledgement is the data structure of an acknoledgement of an incident.
type Acknowledgement struct {
At string
Acknowledger APIObject
}
// PendingAction is the data structure for any pending actions on an incident.
type PendingAction struct {
Type string
At string
}
// Assignment is the data structure for an assignment of an incident
type Assignment struct {
At string
Assignee APIObject
}
// Incident is a normalized, de-duplicated event generated by a PagerDuty integration.
type Incident struct {
APIObject
IncidentNumber uint `json:"incident_number,omitempty"`
CreatedAt string `json:"created_at,omitempty"`
PendingActions []PendingAction `json:"pending_actions,omitempty"`
IncidentKey string `json:"incident_key,omitempty"`
Service APIObject `json:"service,omitempty"`
Assignments []Assignment `json:"assignments,omitempty"`
Acknowledgements []Acknowledgement `json:"acknowledgements,omitempty"`
LastStatusChangeAt string `json:"last_status_change_at,omitempty"`
LastStatusChangeBy APIObject `json:"last_status_change_by,omitempty"`
FirstTriggerLogEntry APIObject `json:"first_trigger_log_entry,omitempty"`
EscalationPolicy APIObject `json:"escalation_policy,omitempty"`
Teams []APIObject `json:"teams,omitempty"`
Urgency string `json:"urgency,omitempty"`
Status string `json:"status,omitempty"`
}
// ListIncidentsResponse is the response structure when calling the ListIncident API endpoint.
type ListIncidentsResponse struct {
APIListObject
Incidents []Incident `json:"incidents,omitempty"`
}
// ListIncidentsOptions is the structure used when passing parameters to the ListIncident API endpoint.
type ListIncidentsOptions struct {
APIListObject
Since string `url:"since,omitempty"`
Until string `url:"until,omitempty"`
DateRange string `url:"date_range,omitempty"`
Statuses []string `url:"statuses,omitempty,brackets"`
IncidentKey string `url:"incident_key,omitempty"`
ServiceIDs []string `url:"service_ids,omitempty,brackets"`
TeamIDs []string `url:"team_ids,omitempty,brackets"`
UserIDs []string `url:"user_ids,omitempty,brackets"`
Urgencies []string `url:"urgencies,omitempty,brackets"`
TimeZone string `url:"time_zone,omitempty"`
SortBy string `url:"sort_by,omitempty"`
Includes []string `url:"include,omitempty,brackets"`
}
// ListIncidents lists existing incidents.
func (c *Client) ListIncidents(o ListIncidentsOptions) (*ListIncidentsResponse, error) {
v, err := query.Values(o)
if err != nil {
return nil, err
}
resp, err := c.get("/incidents?" + v.Encode())
if err != nil {
return nil, err
}
var result ListIncidentsResponse
return &result, c.decodeJSON(resp, &result)
}
// ManageIncidents acknowledges, resolves, escalates, or reassigns one or more incidents.
func (c *Client) ManageIncidents(from string, incidents []Incident) error {
r := make(map[string][]Incident)
headers := make(map[string]string)
headers["From"] = from
r["incidents"] = incidents
_, e := c.put("/incidents", r, &headers)
return e
}
// GetIncident shows detailed information about an incident.
func (c *Client) GetIncident(id string) (*Incident, error) {
resp, err := c.get("/incidents/" + id)
if err != nil {
return nil, err
}
var result map[string]Incident
if err := c.decodeJSON(resp, &result); err != nil {
return nil, err
}
i, ok := result["incident"]
if !ok {
return nil, fmt.Errorf("JSON response does not have incident field")
}
return &i, nil
}
// IncidentNote is a note for the specified incident.
type IncidentNote struct {
ID string `json:"id,omitempty"`
User APIObject `json:"user,omitempty"`
Content string `json:"content,omitempty"`
CreatedAt string `json:"created_at,omitempty"`
}
// ListIncidentNotes lists existing notes for the specified incident.
func (c *Client) ListIncidentNotes(id string) ([]IncidentNote, error) {
resp, err := c.get("/incidents/" + id + "/notes")
if err != nil {
return nil, err
}
var result map[string][]IncidentNote
if err := c.decodeJSON(resp, &result); err != nil {
return nil, err
}
notes, ok := result["notes"]
if !ok {
return nil, fmt.Errorf("JSON response does not have notes field")
}
return notes, nil
}
// CreateIncidentNote creates a new note for the specified incident.
func (c *Client) CreateIncidentNote(id string, note IncidentNote) error {
data := make(map[string]IncidentNote)
data["note"] = note
_, err := c.post("/incidents/"+id+"/notes", data)
return err
}
// SnoozeIncident sets an incident to not alert for a specified period of time.
func (c *Client) SnoozeIncident(id string, duration uint) error {
data := make(map[string]uint)
data["duration"] = duration
_, err := c.post("/incidents/"+id+"/snooze", data)
return err
}
// ListIncidentLogEntriesResponse is the response structure when calling the ListIncidentLogEntries API endpoint.
type ListIncidentLogEntriesResponse struct {
APIListObject
LogEntries []LogEntry `json:"log_entries,omitempty"`
}
// ListIncidentLogEntriesOptions is the structure used when passing parameters to the ListIncidentLogEntries API endpoint.
type ListIncidentLogEntriesOptions struct {
APIListObject
Includes []string `url:"include,omitempty,brackets"`
IsOverview bool `url:"is_overview,omitempty"`
TimeZone string `url:"time_zone,omitempty"`
}
// ListIncidentLogEntries lists existing log entries for the specified incident.
func (c *Client) ListIncidentLogEntries(id string, o ListIncidentLogEntriesOptions) (*ListIncidentLogEntriesResponse, error) {
v, err := query.Values(o)
if err != nil {
return nil, err
}
resp, err := c.get("/incidents/" + id + "/log_entries?" + v.Encode())
if err != nil {
return nil, err
}
var result ListIncidentLogEntriesResponse
return &result, c.decodeJSON(resp, &result)
}

View File

@ -1,94 +0,0 @@
package pagerduty
import (
"fmt"
"github.com/google/go-querystring/query"
)
// Agent is the actor who carried out the action.
type Agent APIObject
// Channel is the means by which the action was carried out.
type Channel struct {
Type string
}
// Context are to be included with the trigger such as links to graphs or images.
type Context struct {
Alt string
Href string
Src string
Text string
Type string
}
// LogEntry is a list of all of the events that happened to an incident.
type LogEntry struct {
APIObject
CreatedAt string `json:"created_at"`
Agent Agent
Channel Channel
Incident Incident
Teams []Team
Contexts []Context
AcknowledgementTimeout int `json:"acknowledgement_timeout"`
EventDetails map[string]string
}
// ListLogEntryResponse is the response data when calling the ListLogEntry API endpoint.
type ListLogEntryResponse struct {
APIListObject
LogEntries []LogEntry `json:"log_entries"`
}
// ListLogEntriesOptions is the data structure used when calling the ListLogEntry API endpoint.
type ListLogEntriesOptions struct {
APIListObject
TimeZone string `url:"time_zone"`
Since string `url:"since,omitempty"`
Until string `url:"until,omitempty"`
IsOverview bool `url:"is_overview,omitempty"`
Includes []string `url:"include,omitempty,brackets"`
}
// ListLogEntries lists all of the incident log entries across the entire account.
func (c *Client) ListLogEntries(o ListLogEntriesOptions) (*ListLogEntryResponse, error) {
v, err := query.Values(o)
if err != nil {
return nil, err
}
resp, err := c.get("/log_entries?" + v.Encode())
if err != nil {
return nil, err
}
var result ListLogEntryResponse
return &result, c.decodeJSON(resp, &result)
}
// GetLogEntryOptions is the data structure used when calling the GetLogEntry API endpoint.
type GetLogEntryOptions struct {
TimeZone string `url:"timezone,omitempty"`
Includes []string `url:"include,omitempty,brackets"`
}
// GetLogEntry list log entries for the specified incident.
func (c *Client) GetLogEntry(id string, o GetLogEntryOptions) (*LogEntry, error) {
v, err := query.Values(o)
if err != nil {
return nil, err
}
resp, err := c.get("/log_entries/" + id + "?" + v.Encode())
if err != nil {
return nil, err
}
var result map[string]LogEntry
if err := c.decodeJSON(resp, &result); err != nil {
return nil, err
}
le, ok := result["log_entry"]
if !ok {
return nil, fmt.Errorf("JSON response does not have log_entry field")
}
return &le, nil
}

View File

@ -1,100 +0,0 @@
package pagerduty
import (
"fmt"
"github.com/google/go-querystring/query"
"net/http"
)
// MaintenanceWindow is used to temporarily disable one or more services for a set period of time.
type MaintenanceWindow struct {
APIObject
SequenceNumber uint `json:"sequence_number,omitempty"`
StartTime string `json:"start_time"`
EndTime string `json:"end_time"`
Description string `json:"description"`
Services []APIObject `json:"services"`
Teams []APIListObject `json:"teams"`
CreatedBy APIListObject `json:"created_by"`
}
// ListMaintenanceWindowsResponse is the data structur returned from calling the ListMaintenanceWindows API endpoint.
type ListMaintenanceWindowsResponse struct {
APIListObject
MaintenanceWindows []MaintenanceWindow `json:"maintenance_windows"`
}
// ListMaintenanceWindowsOptions is the data structure used when calling the ListMaintenanceWindows API endpoint.
type ListMaintenanceWindowsOptions struct {
APIListObject
Query string `url:"query,omitempty"`
Includes []string `url:"include,omitempty,brackets"`
TeamIDs []string `url:"team_ids,omitempty,brackets"`
ServiceIDs []string `url:"service_ids,omitempty,brackets"`
Filter string `url:"filter,omitempty,brackets"`
}
// ListMaintenanceWindows lists existing maintenance windows, optionally filtered by service and/or team, or whether they are from the past, present or future.
func (c *Client) ListMaintenanceWindows(o ListMaintenanceWindowsOptions) (*ListMaintenanceWindowsResponse, error) {
v, err := query.Values(o)
if err != nil {
return nil, err
}
resp, err := c.get("/maintenance_windows?" + v.Encode())
if err != nil {
return nil, err
}
var result ListMaintenanceWindowsResponse
return &result, c.decodeJSON(resp, &result)
}
// CreateMaintenanceWindows creates a new maintenance window for the specified services.
func (c *Client) CreateMaintenanceWindows(m MaintenanceWindow) (*MaintenanceWindow, error) {
data := make(map[string]MaintenanceWindow)
data["maintenance_window"] = m
resp, err := c.post("/maintenance_windows", data)
return getMaintenanceWindowFromResponse(c, resp, err)
}
// DeleteMaintenanceWindow deletes an existing maintenance window if it's in the future, or ends it if it's currently on-going.
func (c *Client) DeleteMaintenanceWindow(id string) error {
_, err := c.delete("/maintenance_windows/" + id)
return err
}
// GetMaintenanceWindowOptions is the data structure used when calling the GetMaintenanceWindow API endpoint.
type GetMaintenanceWindowOptions struct {
Includes []string `url:"include,omitempty,brackets"`
}
// GetMaintenanceWindow gets an existing maintenance window.
func (c *Client) GetMaintenanceWindow(id string, o GetMaintenanceWindowOptions) (*MaintenanceWindow, error) {
v, err := query.Values(o)
if err != nil {
return nil, err
}
resp, err := c.get("/maintenance_windows/" + id + "?" + v.Encode())
return getMaintenanceWindowFromResponse(c, resp, err)
}
// UpdateMaintenanceWindow updates an existing maintenance window.
func (c *Client) UpdateMaintenanceWindow(m MaintenanceWindow) (*MaintenanceWindow, error) {
resp, err := c.put("/maintenance_windows/"+m.ID, m, nil)
return getMaintenanceWindowFromResponse(c, resp, err)
}
func getMaintenanceWindowFromResponse(c *Client, resp *http.Response, err error) (*MaintenanceWindow, error) {
if err != nil {
return nil, err
}
var target map[string]MaintenanceWindow
if dErr := c.decodeJSON(resp, &target); dErr != nil {
return nil, fmt.Errorf("Could not decode JSON response: %v", dErr)
}
rootNode := "maintenance_window"
t, nodeOK := target[rootNode]
if !nodeOK {
return nil, fmt.Errorf("JSON response does not have %s field", rootNode)
}
return &t, nil
}

View File

@ -1,44 +0,0 @@
package pagerduty
import (
"github.com/google/go-querystring/query"
)
// Notification is a message containing the details of the incident.
type Notification struct {
ID string `json:"id"`
Type string
StartedAt string `json:"started_at"`
Address string
User APIObject
}
// ListNotificationOptions is the data structure used when calling the ListNotifications API endpoint.
type ListNotificationOptions struct {
APIListObject
TimeZone string `url:"time_zone,omitempty"`
Since string `url:"since,omitempty"`
Until string `url:"until,omitempty"`
Filter string `url:"filter,omitempty"`
Includes []string `url:"include,omitempty"`
}
// ListNotificationsResponse is the data structure returned from the ListNotifications API endpoint.
type ListNotificationsResponse struct {
APIListObject
Notifications []Notification
}
// ListNotifications lists notifications for a given time range, optionally filtered by type (sms_notification, email_notification, phone_notification, or push_notification).
func (c *Client) ListNotifications(o ListNotificationOptions) (*ListNotificationsResponse, error) {
v, err := query.Values(o)
if err != nil {
return nil, err
}
resp, err := c.get("/notifications?" + v.Encode())
if err != nil {
return nil, err
}
var result ListNotificationsResponse
return &result, c.decodeJSON(resp, &result)
}

View File

@ -1,48 +0,0 @@
package pagerduty
import (
"github.com/google/go-querystring/query"
)
// OnCall represents a contiguous unit of time for which a user will be on call for a given escalation policy and escalation rule.
type OnCall struct {
User APIObject `json:"user,omitempty"`
Schedule APIObject `json:"schedule,omitempty"`
EscalationPolicy APIObject `json:"escalation_policy,omitempty"`
EscalationLevel uint `json:"escalation_level,omitempty"`
Start string `json:"start,omitempty"`
End string `json:"end,omitempty"`
}
// ListOnCallsResponse is the data structure returned from calling the ListOnCalls API endpoint.
type ListOnCallsResponse struct {
APIListObject
OnCalls []OnCall `json:"oncalls"`
}
// ListOnCallOptions is the data structure used when calling the ListOnCalls API endpoint.
type ListOnCallOptions struct {
APIListObject
TimeZone string `url:"time_zone,omitempty"`
Includes []string `url:"include,omitempty,brackets"`
UserIDs []string `url:"user_ids,omitempty,brackets"`
EscalationPolicyIDs []string `url:"escalation_policy_ids,omitempty,brackets"`
ScheduleIDs []string `url:"schedule_ids,omitempty,brackets"`
Since string `url:"since,omitempty"`
Until string `url:"until,omitempty"`
Earliest bool `url:"earliest,omitempty"`
}
// ListOnCalls list the on-call entries during a given time range.
func (c *Client) ListOnCalls(o ListOnCallOptions) (*ListOnCallsResponse, error) {
v, err := query.Values(o)
if err != nil {
return nil, err
}
resp, err := c.get("/oncalls?" + v.Encode())
if err != nil {
return nil, err
}
var result ListOnCallsResponse
return &result, c.decodeJSON(resp, &result)
}

View File

@ -1,264 +0,0 @@
package pagerduty
import (
"fmt"
"net/http"
"github.com/google/go-querystring/query"
)
// Restriction limits on-call responsibility for a layer to certain times of the day or week.
type Restriction struct {
Type string `json:"type,omitempty"`
StartTimeOfDay string `json:"start_time_of_day,omitempty"`
StartDayOfWeek uint `json:"start_day_of_week,omitempty"`
DurationSeconds uint `json:"duration_seconds,omitempty"`
}
// RenderedScheduleEntry represents the computed set of schedule layer entries that put users on call for a schedule, and cannot be modified directly.
type RenderedScheduleEntry struct {
Start string `json:"start,omitempty"`
End string `json:"end,omitempty"`
User APIObject `json:"user,omitempty"`
}
// ScheduleLayer is an entry that puts users on call for a schedule.
type ScheduleLayer struct {
APIObject
Name string `json:"name,omitempty"`
Start string `json:"start,omitempty"`
End string `json:"end,omitempty"`
RotationVirtualStart string `json:"rotation_virtual_start,omitempty"`
RotationTurnLengthSeconds uint `json:"rotation_turn_length_seconds,omitempty"`
Users []UserReference `json:"users,omitempty"`
Restrictions []Restriction `json:"restrictions,omitempty"`
RenderedScheduleEntries []RenderedScheduleEntry `json:"rendered_schedule_entries,omitempty"`
RenderedCoveragePercentage float64 `json:"rendered_coverage_percentage,omitempty"`
}
// Schedule determines the time periods that users are on call.
type Schedule struct {
APIObject
Name string `json:"name,omitempty"`
TimeZone string `json:"time_zone,omitempty"`
Description string `json:"description,omitempty"`
EscalationPolicies []APIObject `json:"escalation_policies,omitempty"`
Users []APIObject `json:"users,omitempty"`
ScheduleLayers []ScheduleLayer `json:"schedule_layers,omitempty"`
OverrideSubschedule ScheduleLayer `json:"override_subschedule,omitempty"`
FinalSchedule ScheduleLayer `json:"final_schedule,omitempty"`
}
// ListSchedulesOptions is the data structure used when calling the ListSchedules API endpoint.
type ListSchedulesOptions struct {
APIListObject
Query string `url:"query,omitempty"`
}
// ListSchedulesResponse is the data structure returned from calling the ListSchedules API endpoint.
type ListSchedulesResponse struct {
APIListObject
Schedules []Schedule `json:"schedules"`
}
// UserReference is a reference to an authorized PagerDuty user.
type UserReference struct {
User APIObject `json:"user"`
}
// ListSchedules lists the on-call schedules.
func (c *Client) ListSchedules(o ListSchedulesOptions) (*ListSchedulesResponse, error) {
v, err := query.Values(o)
if err != nil {
return nil, err
}
resp, err := c.get("/schedules?" + v.Encode())
if err != nil {
return nil, err
}
var result ListSchedulesResponse
return &result, c.decodeJSON(resp, &result)
}
// CreateSchedule creates a new on-call schedule.
func (c *Client) CreateSchedule(s Schedule) (*Schedule, error) {
data := make(map[string]Schedule)
data["schedule"] = s
resp, err := c.post("/schedules", data)
if err != nil {
return nil, err
}
return getScheduleFromResponse(c, resp)
}
// PreviewScheduleOptions is the data structure used when calling the PreviewSchedule API endpoint.
type PreviewScheduleOptions struct {
APIListObject
Since string `url:"since,omitempty"`
Until string `url:"until,omitempty"`
Overflow bool `url:"overflow,omitempty"`
}
// PreviewSchedule previews what an on-call schedule would look like without saving it.
func (c *Client) PreviewSchedule(s Schedule, o PreviewScheduleOptions) error {
v, err := query.Values(o)
if err != nil {
return err
}
var data map[string]Schedule
data["schedule"] = s
_, e := c.post("/schedules/preview?"+v.Encode(), data)
return e
}
// DeleteSchedule deletes an on-call schedule.
func (c *Client) DeleteSchedule(id string) error {
_, err := c.delete("/schedules/" + id)
return err
}
// GetScheduleOptions is the data structure used when calling the GetSchedule API endpoint.
type GetScheduleOptions struct {
APIListObject
TimeZone string `url:"time_zone,omitempty"`
Since string `url:"since,omitempty"`
Until string `url:"until,omitempty"`
}
// GetSchedule shows detailed information about a schedule, including entries for each layer and sub-schedule.
func (c *Client) GetSchedule(id string, o GetScheduleOptions) (*Schedule, error) {
v, err := query.Values(o)
if err != nil {
return nil, fmt.Errorf("Could not parse values for query: %v", err)
}
resp, err := c.get("/schedules/" + id + "?" + v.Encode())
if err != nil {
return nil, err
}
return getScheduleFromResponse(c, resp)
}
// UpdateScheduleOptions is the data structure used when calling the UpdateSchedule API endpoint.
type UpdateScheduleOptions struct {
Overflow bool `url:"overflow,omitempty"`
}
// UpdateSchedule updates an existing on-call schedule.
func (c *Client) UpdateSchedule(id string, s Schedule) (*Schedule, error) {
v := make(map[string]Schedule)
v["schedule"] = s
resp, err := c.put("/schedules/"+id, v, nil)
if err != nil {
return nil, err
}
return getScheduleFromResponse(c, resp)
}
// ListOverridesOptions is the data structure used when calling the ListOverrides API endpoint.
type ListOverridesOptions struct {
APIListObject
Since string `url:"since,omitempty"`
Until string `url:"until,omitempty"`
Editable bool `url:"editable,omitempty"`
Overflow bool `url:"overflow,omitempty"`
}
// Overrides are any schedule layers from the override layer.
type Override struct {
ID string `json:"id,omitempty"`
Start string `json:"start,omitempty"`
End string `json:"end,omitempty"`
User APIObject `json:"user,omitempty"`
}
// ListOverrides lists overrides for a given time range.
func (c *Client) ListOverrides(id string, o ListOverridesOptions) ([]Override, error) {
v, err := query.Values(o)
if err != nil {
return nil, err
}
resp, err := c.get("/schedules/" + id + "/overrides?" + v.Encode())
if err != nil {
return nil, err
}
var result map[string][]Override
if err := c.decodeJSON(resp, &result); err != nil {
return nil, err
}
overrides, ok := result["overrides"]
if !ok {
return nil, fmt.Errorf("JSON response does not have overrides field")
}
return overrides, nil
}
// CreateOverride creates an override for a specific user covering the specified time range.
func (c *Client) CreateOverride(id string, o Override) (*Override, error) {
data := make(map[string]Override)
data["override"] = o
resp, err := c.post("/schedules/"+id+"/overrides", data)
if err != nil {
return nil, err
}
return getOverrideFromResponse(c, resp)
}
// DeleteOverride removes an override.
func (c *Client) DeleteOverride(scheduleID, overrideID string) error {
_, err := c.delete("/schedules/" + scheduleID + "/overrides/" + overrideID)
return err
}
// ListOnCallUsersOptions is the data structure used when calling the ListOnCallUsers API endpoint.
type ListOnCallUsersOptions struct {
APIListObject
Since string `url:"since,omitempty"`
Until string `url:"until,omitempty"`
}
// ListOnCallUsers lists all of the users on call in a given schedule for a given time range.
func (c *Client) ListOnCallUsers(id string, o ListOnCallUsersOptions) ([]User, error) {
v, err := query.Values(o)
if err != nil {
return nil, err
}
resp, err := c.get("/schedules/" + id + "/users?" + v.Encode())
if err != nil {
return nil, err
}
var result map[string][]User
if err := c.decodeJSON(resp, &result); err != nil {
return nil, err
}
u, ok := result["users"]
if !ok {
return nil, fmt.Errorf("JSON response does not have users field")
}
return u, nil
}
func getScheduleFromResponse(c *Client, resp *http.Response) (*Schedule, error) {
var target map[string]Schedule
if dErr := c.decodeJSON(resp, &target); dErr != nil {
return nil, fmt.Errorf("Could not decode JSON response: %v", dErr)
}
rootNode := "schedule"
t, nodeOK := target[rootNode]
if !nodeOK {
return nil, fmt.Errorf("JSON response does not have %s field", rootNode)
}
return &t, nil
}
func getOverrideFromResponse(c *Client, resp *http.Response) (*Override, error) {
var target map[string]Override
if dErr := c.decodeJSON(resp, &target); dErr != nil {
return nil, fmt.Errorf("Could not decode JSON response: %v", dErr)
}
rootNode := "override"
o, nodeOK := target[rootNode]
if !nodeOK {
return nil, fmt.Errorf("JSON response does not have %s field", rootNode)
}
return &o, nil
}

View File

@ -1,204 +0,0 @@
package pagerduty
import (
"fmt"
"net/http"
"github.com/google/go-querystring/query"
)
// Integration is an endpoint (like Nagios, email, or an API call) that generates events, which are normalized and de-duplicated by PagerDuty to create incidents.
type Integration struct {
APIObject
Name string `json:"name,omitempty"`
Service *APIObject `json:"service,omitempty"`
CreatedAt string `json:"created_at,omitempty"`
Vendor *APIObject `json:"vendor,omitempty"`
Type string `json:"type,omitempty"`
IntegrationKey string `json:"integration_key,omitempty"`
IntegrationEmail string `json:"integration_email,omitempty"`
}
// InlineModel represents when a scheduled action will occur.
type InlineModel struct {
Type string `json:"type,omitempty"`
Name string `json:"name,omitempty"`
}
// ScheduledAction contains scheduled actions for the service.
type ScheduledAction struct {
Type string `json:"type,omitempty"`
At InlineModel `json:"at,omitempty"`
ToUrgency string `json:"to_urgency"`
}
// IncidentUrgencyType are the incidents urgency during or outside support hours.
type IncidentUrgencyType struct {
Type string `json:"type,omitempty"`
Urgency string `json:"urgency,omitempty"`
}
// SupportHours are the support hours for the service.
type SupportHours struct {
Type string `json:"type,omitempty"`
Timezone string `json:"time_zone,omitempty"`
StartTime string `json:"start_time,omitempty"`
EndTime string `json:"end_time,omitempty"`
DaysOfWeek []uint `json:"days_of_week,omitempty"`
}
// IncidentUrgencyRule is the default urgency for new incidents.
type IncidentUrgencyRule struct {
Type string `json:"type,omitempty"`
Urgency string `json:"urgency,omitempty"`
DuringSupportHours *IncidentUrgencyType `json:"during_support_hours,omitempty"`
OutsideSupportHours *IncidentUrgencyType `json:"outside_support_hours,omitempty"`
}
// Service represents something you monitor (like a web service, email service, or database service).
type Service struct {
APIObject
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
AutoResolveTimeout *uint `json:"auto_resolve_timeout"`
AcknowledgementTimeout *uint `json:"acknowledgement_timeout"`
CreateAt string `json:"created_at,omitempty"`
Status string `json:"status,omitempty"`
LastIncidentTimestamp string `json:"last_incident_timestamp,omitempty"`
Integrations []Integration `json:"integrations,omitempty"`
EscalationPolicy EscalationPolicy `json:"escalation_policy,omitempty"`
Teams []Team `json:"teams,omitempty"`
IncidentUrgencyRule *IncidentUrgencyRule `json:"incident_urgency_rule,omitempty"`
SupportHours *SupportHours `json:"support_hours,omitempty"`
ScheduledActions []ScheduledAction `json:"scheduled_actions,omitempty"`
AlertCreation string `json:"alert_creation,omitempty"`
}
// ListServiceOptions is the data structure used when calling the ListServices API endpoint.
type ListServiceOptions struct {
APIListObject
TeamIDs []string `url:"team_ids,omitempty,brackets"`
TimeZone string `url:"time_zone,omitempty"`
SortBy string `url:"sort_by,omitempty"`
Query string `url:"query,omitempty"`
Includes []string `url:"include,omitempty,brackets"`
}
// ListServiceResponse is the data structure returned from calling the ListServices API endpoint.
type ListServiceResponse struct {
APIListObject
Services []Service
}
// ListServices lists existing services.
func (c *Client) ListServices(o ListServiceOptions) (*ListServiceResponse, error) {
v, err := query.Values(o)
if err != nil {
return nil, err
}
resp, err := c.get("/services?" + v.Encode())
if err != nil {
return nil, err
}
var result ListServiceResponse
return &result, c.decodeJSON(resp, &result)
}
// GetServiceOptions is the data structure used when calling the GetService API endpoint.
type GetServiceOptions struct {
Includes []string `url:"include,brackets,omitempty"`
}
// GetService gets details about an existing service.
func (c *Client) GetService(id string, o *GetServiceOptions) (*Service, error) {
v, err := query.Values(o)
resp, err := c.get("/services/" + id + "?" + v.Encode())
return getServiceFromResponse(c, resp, err)
}
// CreateService creates a new service.
func (c *Client) CreateService(s Service) (*Service, error) {
data := make(map[string]Service)
data["service"] = s
resp, err := c.post("/services", data)
return getServiceFromResponse(c, resp, err)
}
// UpdateService updates an existing service.
func (c *Client) UpdateService(s Service) (*Service, error) {
resp, err := c.put("/services/"+s.ID, s, nil)
return getServiceFromResponse(c, resp, err)
}
// DeleteService deletes an existing service.
func (c *Client) DeleteService(id string) error {
_, err := c.delete("/services/" + id)
return err
}
// CreateIntegration creates a new integration belonging to a service.
func (c *Client) CreateIntegration(id string, i Integration) (*Integration, error) {
data := make(map[string]Integration)
data["integration"] = i
resp, err := c.post("/services/"+id+"/integrations", data)
return getIntegrationFromResponse(c, resp, err)
}
// GetIntegrationOptions is the data structure used when calling the GetIntegration API endpoint.
type GetIntegrationOptions struct {
Includes []string `url:"include,omitempty,brackets"`
}
// GetIntegration gets details about an integration belonging to a service.
func (c *Client) GetIntegration(serviceID, integrationID string, o GetIntegrationOptions) (*Integration, error) {
v, queryErr := query.Values(o)
if queryErr != nil {
return nil, queryErr
}
resp, err := c.get("/services/" + serviceID + "/integrations/" + integrationID + "?" + v.Encode())
return getIntegrationFromResponse(c, resp, err)
}
// UpdateIntegration updates an integration belonging to a service.
func (c *Client) UpdateIntegration(serviceID string, i Integration) (*Integration, error) {
resp, err := c.put("/services/"+serviceID+"/integrations/"+i.ID, i, nil)
return getIntegrationFromResponse(c, resp, err)
}
// DeleteIntegration deletes an existing integration.
func (c *Client) DeleteIntegration(serviceID string, integrationID string) error {
_, err := c.delete("/services/" + serviceID + "/integrations/" + integrationID)
return err
}
func getServiceFromResponse(c *Client, resp *http.Response, err error) (*Service, error) {
if err != nil {
return nil, err
}
var target map[string]Service
if dErr := c.decodeJSON(resp, &target); dErr != nil {
return nil, fmt.Errorf("Could not decode JSON response: %v", dErr)
}
rootNode := "service"
t, nodeOK := target[rootNode]
if !nodeOK {
return nil, fmt.Errorf("JSON response does not have %s field", rootNode)
}
return &t, nil
}
func getIntegrationFromResponse(c *Client, resp *http.Response, err error) (*Integration, error) {
if err != nil {
return nil, err
}
var target map[string]Integration
if dErr := c.decodeJSON(resp, &target); dErr != nil {
return nil, fmt.Errorf("Could not decode JSON response: %v", err)
}
rootNode := "integration"
t, nodeOK := target[rootNode]
if !nodeOK {
return nil, fmt.Errorf("JSON response does not have %s field", rootNode)
}
return &t, nil
}

View File

@ -1,105 +0,0 @@
package pagerduty
import (
"fmt"
"github.com/google/go-querystring/query"
"net/http"
)
// Team is a collection of users and escalation policies that represent a group of people within an organization.
type Team struct {
APIObject
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
}
// ListTeamResponse is the structure used when calling the ListTeams API endpoint.
type ListTeamResponse struct {
APIListObject
Teams []Team
}
// ListTeamOptions are the input parameters used when calling the ListTeams API endpoint.
type ListTeamOptions struct {
APIListObject
Query string `url:"query,omitempty"`
}
// ListTeams lists teams of your PagerDuty account, optionally filtered by a search query.
func (c *Client) ListTeams(o ListTeamOptions) (*ListTeamResponse, error) {
v, err := query.Values(o)
if err != nil {
return nil, err
}
resp, err := c.get("/teams?" + v.Encode())
if err != nil {
return nil, err
}
var result ListTeamResponse
return &result, c.decodeJSON(resp, &result)
}
// CreateTeam creates a new team.
func (c *Client) CreateTeam(t *Team) (*Team, error) {
resp, err := c.post("/teams", t)
return getTeamFromResponse(c, resp, err)
}
// DeleteTeam removes an existing team.
func (c *Client) DeleteTeam(id string) error {
_, err := c.delete("/teams/" + id)
return err
}
// GetTeam gets details about an existing team.
func (c *Client) GetTeam(id string) (*Team, error) {
resp, err := c.get("/teams/" + id)
return getTeamFromResponse(c, resp, err)
}
// UpdateTeam updates an existing team.
func (c *Client) UpdateTeam(id string, t *Team) (*Team, error) {
resp, err := c.put("/teams/"+id, t, nil)
return getTeamFromResponse(c, resp, err)
}
// RemoveEscalationPolicyFromTeam removes an escalation policy from a team.
func (c *Client) RemoveEscalationPolicyFromTeam(teamID, epID string) error {
_, err := c.delete("/teams/" + teamID + "/escalation_policies/" + epID)
return err
}
// AddEscalationPolicyToTeam adds an escalation policy to a team.
func (c *Client) AddEscalationPolicyToTeam(teamID, epID string) error {
_, err := c.put("/teams/"+teamID+"/escalation_policies/"+epID, nil, nil)
return err
}
// RemoveUserFromTeam removes a user from a team.
func (c *Client) RemoveUserFromTeam(teamID, userID string) error {
_, err := c.delete("/teams/" + teamID + "/users/" + userID)
return err
}
// AddUserToTeam adds a user to a team.
func (c *Client) AddUserToTeam(teamID, userID string) error {
_, err := c.put("/teams/"+teamID+"/users/"+userID, nil, nil)
return err
}
func getTeamFromResponse(c *Client, resp *http.Response, err error) (*Team, error) {
if err != nil {
return nil, err
}
var target map[string]Team
if dErr := c.decodeJSON(resp, &target); dErr != nil {
return nil, fmt.Errorf("Could not decode JSON response: %v", dErr)
}
rootNode := "team"
t, nodeOK := target[rootNode]
if !nodeOK {
return nil, fmt.Errorf("JSON response does not have %s field", rootNode)
}
return &t, nil
}

View File

@ -1,124 +0,0 @@
package pagerduty
import (
"fmt"
"github.com/google/go-querystring/query"
"net/http"
)
// ContactMethod is a way of contacting the user.
type ContactMethod struct {
ID string
Label string
Address string
Type string
SendShortEmail bool `json:"send_short_email"`
}
// NotificationRule is a rule for notifying the user.
type NotificationRule struct {
ID string
StartDelayInMinutes uint `json:"start_delay_in_minutes"`
CreatedAt string `json:"created_at"`
ContactMethod ContactMethod `json:"contact_method"`
Urgency string
Type string
}
// User is a member of a PagerDuty account that has the ability to interact with incidents and other data on the account.
type User struct {
APIObject
Name string `json:"name"`
Email string `json:"email"`
Timezone string `json:"timezone,omitempty"`
Color string `json:"color,omitempty"`
Role string `json:"role,omitempty"`
AvatarURL string `json:"avatar_url,omitempty"`
Description string `json:"description,omitempty"`
InvitationSent bool
ContactMethods []ContactMethod `json:"contact_methods"`
NotificationRules []NotificationRule `json:"notification_rules"`
JobTitle string `json:"job_title,omitempty"`
Teams []Team
}
// ListUsersResponse is the data structure returned from calling the ListUsers API endpoint.
type ListUsersResponse struct {
APIListObject
Users []User
}
// ListUsersOptions is the data structure used when calling the ListUsers API endpoint.
type ListUsersOptions struct {
APIListObject
Query string `url:"query,omitempty"`
TeamIDs []string `url:"team_ids,omitempty,brackets"`
Includes []string `url:"include,omitempty,brackets"`
}
// GetUserOptions is the data structure used when calling the GetUser API endpoint.
type GetUserOptions struct {
Includes []string `url:"include,omitempty,brackets"`
}
// ListUsers lists users of your PagerDuty account, optionally filtered by a search query.
func (c *Client) ListUsers(o ListUsersOptions) (*ListUsersResponse, error) {
v, err := query.Values(o)
if err != nil {
return nil, err
}
resp, err := c.get("/users?" + v.Encode())
if err != nil {
return nil, err
}
var result ListUsersResponse
return &result, c.decodeJSON(resp, &result)
}
// CreateUser creates a new user.
func (c *Client) CreateUser(u User) (*User, error) {
data := make(map[string]User)
data["user"] = u
resp, err := c.post("/users", data)
return getUserFromResponse(c, resp, err)
}
// DeleteUser deletes a user.
func (c *Client) DeleteUser(id string) error {
_, err := c.delete("/users/" + id)
return err
}
// GetUser gets details about an existing user.
func (c *Client) GetUser(id string, o GetUserOptions) (*User, error) {
v, err := query.Values(o)
if err != nil {
return nil, err
}
resp, err := c.get("/users/" + id + "?" + v.Encode())
return getUserFromResponse(c, resp, err)
}
// UpdateUser updates an existing user.
func (c *Client) UpdateUser(u User) (*User, error) {
v := make(map[string]User)
v["user"] = u
resp, err := c.put("/users/"+u.ID, v, nil)
return getUserFromResponse(c, resp, err)
}
func getUserFromResponse(c *Client, resp *http.Response, err error) (*User, error) {
if err != nil {
return nil, err
}
var target map[string]User
if dErr := c.decodeJSON(resp, &target); dErr != nil {
return nil, fmt.Errorf("Could not decode JSON response: %v", dErr)
}
rootNode := "user"
t, nodeOK := target[rootNode]
if !nodeOK {
return nil, fmt.Errorf("JSON response does not have %s field", rootNode)
}
return &t, nil
}

View File

@ -1,74 +0,0 @@
package pagerduty
import (
"fmt"
"net/http"
"github.com/google/go-querystring/query"
)
// Vendor represents a specific type of integration. AWS Cloudwatch, Splunk, Datadog, etc are all examples of vendors that can be integrated in PagerDuty by making an integration.
type Vendor struct {
APIObject
Name string `json:"name,omitempty"`
LogoURL string `json:"logo_url,omitempty"`
LongName string `json:"long_name,omitempty"`
WebsiteURL string `json:"website_url,omitempty"`
Description string `json:"description,omitempty"`
Connectable bool `json:"connectable,omitempty"`
ThumbnailURL string `json:"thumbnail_url,omitempty"`
GenericServiceType string `json:"generic_service_type,omitempty"`
IntegrationGuideURL string `json:"integration_guide_url,omitempty"`
}
// ListVendorResponse is the data structure returned from calling the ListVendors API endpoint.
type ListVendorResponse struct {
APIListObject
Vendors []Vendor
}
// ListVendorOptions is the data structure used when calling the ListVendors API endpoint.
type ListVendorOptions struct {
APIListObject
Query string `url:"query,omitempty"`
}
// ListVendors lists existing vendors.
func (c *Client) ListVendors(o ListVendorOptions) (*ListVendorResponse, error) {
v, err := query.Values(o)
if err != nil {
return nil, err
}
resp, err := c.get("/vendors?" + v.Encode())
if err != nil {
return nil, err
}
var result ListVendorResponse
return &result, c.decodeJSON(resp, &result)
}
// GetVendor gets details about an existing vendor.
func (c *Client) GetVendor(id string) (*Vendor, error) {
resp, err := c.get("/vendors/" + id)
return getVendorFromResponse(c, resp, err)
}
func getVendorFromResponse(c *Client, resp *http.Response, err error) (*Vendor, error) {
if err != nil {
return nil, err
}
var target map[string]Vendor
if dErr := c.decodeJSON(resp, &target); dErr != nil {
return nil, fmt.Errorf("Could not decode JSON response: %v", dErr)
}
rootNode := "vendor"
t, nodeOK := target[rootNode]
if !nodeOK {
return nil, fmt.Errorf("JSON response does not have %s field", rootNode)
}
return &t, nil
}

View File

@ -1,37 +0,0 @@
package pagerduty
import (
"encoding/json"
"io"
)
// IncidentDetail contains a representation of the incident associated with the action that caused this webhook message.
type IncidentDetail struct {
ID string `json:"id"`
IncidentNumber uint `json:"incident_number"`
CreatedOn string `json:"created_on"`
Status string `json:"status"`
HTMLUrl string `json:"html_url"`
Service string `json:"service"`
AssignedToUser *json.RawMessage `json:"assigned_to_user"`
AssignedTo []string `json:"assigned_to"`
TriggerSummaryData *json.RawMessage `json:"trigger_summary_data"`
TriggerDetailsHTMLUrl string `json:"trigger_details_html_url"`
}
// WebhookPayload is a single message array for a webhook.
type WebhookPayload struct {
ID string `json:"id"`
Type string `json:"type"`
CreatedOn string `json:"created_on"`
Data *json.RawMessage `json:"data"`
}
// DecodeWebhook decodes a webhook from a response object.
func DecodeWebhook(r io.Reader) (*WebhookPayload, error) {
var payload WebhookPayload
if err := json.NewDecoder(r).Decode(&payload); err != nil {
return nil, err
}
return &payload, nil
}

View File

@ -1,20 +0,0 @@
The MIT License (MIT)
Copyright (c) 2013 Stack Exchange
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,6 +0,0 @@
wmi
===
Package wmi provides a WQL interface to Windows WMI.
Note: It interfaces with WMI on the local machine, therefore it only runs on Windows.

View File

@ -1,260 +0,0 @@
// +build windows
package wmi
import (
"fmt"
"reflect"
"runtime"
"sync"
"github.com/go-ole/go-ole"
"github.com/go-ole/go-ole/oleutil"
)
// SWbemServices is used to access wmi. See https://msdn.microsoft.com/en-us/library/aa393719(v=vs.85).aspx
type SWbemServices struct {
//TODO: track namespace. Not sure if we can re connect to a different namespace using the same instance
cWMIClient *Client //This could also be an embedded struct, but then we would need to branch on Client vs SWbemServices in the Query method
sWbemLocatorIUnknown *ole.IUnknown
sWbemLocatorIDispatch *ole.IDispatch
queries chan *queryRequest
closeError chan error
lQueryorClose sync.Mutex
}
type queryRequest struct {
query string
dst interface{}
args []interface{}
finished chan error
}
// InitializeSWbemServices will return a new SWbemServices object that can be used to query WMI
func InitializeSWbemServices(c *Client, connectServerArgs ...interface{}) (*SWbemServices, error) {
//fmt.Println("InitializeSWbemServices: Starting")
//TODO: implement connectServerArgs as optional argument for init with connectServer call
s := new(SWbemServices)
s.cWMIClient = c
s.queries = make(chan *queryRequest)
initError := make(chan error)
go s.process(initError)
err, ok := <-initError
if ok {
return nil, err //Send error to caller
}
//fmt.Println("InitializeSWbemServices: Finished")
return s, nil
}
// Close will clear and release all of the SWbemServices resources
func (s *SWbemServices) Close() error {
s.lQueryorClose.Lock()
if s == nil || s.sWbemLocatorIDispatch == nil {
s.lQueryorClose.Unlock()
return fmt.Errorf("SWbemServices is not Initialized")
}
if s.queries == nil {
s.lQueryorClose.Unlock()
return fmt.Errorf("SWbemServices has been closed")
}
//fmt.Println("Close: sending close request")
var result error
ce := make(chan error)
s.closeError = ce //Race condition if multiple callers to close. May need to lock here
close(s.queries) //Tell background to shut things down
s.lQueryorClose.Unlock()
err, ok := <-ce
if ok {
result = err
}
//fmt.Println("Close: finished")
return result
}
func (s *SWbemServices) process(initError chan error) {
//fmt.Println("process: starting background thread initialization")
//All OLE/WMI calls must happen on the same initialized thead, so lock this goroutine
runtime.LockOSThread()
defer runtime.LockOSThread()
err := ole.CoInitializeEx(0, ole.COINIT_MULTITHREADED)
if err != nil {
oleCode := err.(*ole.OleError).Code()
if oleCode != ole.S_OK && oleCode != S_FALSE {
initError <- fmt.Errorf("ole.CoInitializeEx error: %v", err)
return
}
}
defer ole.CoUninitialize()
unknown, err := oleutil.CreateObject("WbemScripting.SWbemLocator")
if err != nil {
initError <- fmt.Errorf("CreateObject SWbemLocator error: %v", err)
return
} else if unknown == nil {
initError <- ErrNilCreateObject
return
}
defer unknown.Release()
s.sWbemLocatorIUnknown = unknown
dispatch, err := s.sWbemLocatorIUnknown.QueryInterface(ole.IID_IDispatch)
if err != nil {
initError <- fmt.Errorf("SWbemLocator QueryInterface error: %v", err)
return
}
defer dispatch.Release()
s.sWbemLocatorIDispatch = dispatch
// we can't do the ConnectServer call outside the loop unless we find a way to track and re-init the connectServerArgs
//fmt.Println("process: initialized. closing initError")
close(initError)
//fmt.Println("process: waiting for queries")
for q := range s.queries {
//fmt.Printf("process: new query: len(query)=%d\n", len(q.query))
errQuery := s.queryBackground(q)
//fmt.Println("process: s.queryBackground finished")
if errQuery != nil {
q.finished <- errQuery
}
close(q.finished)
}
//fmt.Println("process: queries channel closed")
s.queries = nil //set channel to nil so we know it is closed
//TODO: I think the Release/Clear calls can panic if things are in a bad state.
//TODO: May need to recover from panics and send error to method caller instead.
close(s.closeError)
}
// Query runs the WQL query using a SWbemServices instance and appends the values to dst.
//
// dst must have type *[]S or *[]*S, for some struct type S. Fields selected in
// the query must have the same name in dst. Supported types are all signed and
// unsigned integers, time.Time, string, bool, or a pointer to one of those.
// Array types are not supported.
//
// By default, the local machine and default namespace are used. These can be
// changed using connectServerArgs. See
// http://msdn.microsoft.com/en-us/library/aa393720.aspx for details.
func (s *SWbemServices) Query(query string, dst interface{}, connectServerArgs ...interface{}) error {
s.lQueryorClose.Lock()
if s == nil || s.sWbemLocatorIDispatch == nil {
s.lQueryorClose.Unlock()
return fmt.Errorf("SWbemServices is not Initialized")
}
if s.queries == nil {
s.lQueryorClose.Unlock()
return fmt.Errorf("SWbemServices has been closed")
}
//fmt.Println("Query: Sending query request")
qr := queryRequest{
query: query,
dst: dst,
args: connectServerArgs,
finished: make(chan error),
}
s.queries <- &qr
s.lQueryorClose.Unlock()
err, ok := <-qr.finished
if ok {
//fmt.Println("Query: Finished with error")
return err //Send error to caller
}
//fmt.Println("Query: Finished")
return nil
}
func (s *SWbemServices) queryBackground(q *queryRequest) error {
if s == nil || s.sWbemLocatorIDispatch == nil {
return fmt.Errorf("SWbemServices is not Initialized")
}
wmi := s.sWbemLocatorIDispatch //Should just rename in the code, but this will help as we break things apart
//fmt.Println("queryBackground: Starting")
dv := reflect.ValueOf(q.dst)
if dv.Kind() != reflect.Ptr || dv.IsNil() {
return ErrInvalidEntityType
}
dv = dv.Elem()
mat, elemType := checkMultiArg(dv)
if mat == multiArgTypeInvalid {
return ErrInvalidEntityType
}
// service is a SWbemServices
serviceRaw, err := oleutil.CallMethod(wmi, "ConnectServer", q.args...)
if err != nil {
return err
}
service := serviceRaw.ToIDispatch()
defer serviceRaw.Clear()
// result is a SWBemObjectSet
resultRaw, err := oleutil.CallMethod(service, "ExecQuery", q.query)
if err != nil {
return err
}
result := resultRaw.ToIDispatch()
defer resultRaw.Clear()
count, err := oleInt64(result, "Count")
if err != nil {
return err
}
enumProperty, err := result.GetProperty("_NewEnum")
if err != nil {
return err
}
defer enumProperty.Clear()
enum, err := enumProperty.ToIUnknown().IEnumVARIANT(ole.IID_IEnumVariant)
if err != nil {
return err
}
if enum == nil {
return fmt.Errorf("can't get IEnumVARIANT, enum is nil")
}
defer enum.Release()
// Initialize a slice with Count capacity
dv.Set(reflect.MakeSlice(dv.Type(), 0, int(count)))
var errFieldMismatch error
for itemRaw, length, err := enum.Next(1); length > 0; itemRaw, length, err = enum.Next(1) {
if err != nil {
return err
}
err := func() error {
// item is a SWbemObject, but really a Win32_Process
item := itemRaw.ToIDispatch()
defer item.Release()
ev := reflect.New(elemType)
if err = s.cWMIClient.loadEntity(ev.Interface(), item); err != nil {
if _, ok := err.(*ErrFieldMismatch); ok {
// We continue loading entities even in the face of field mismatch errors.
// If we encounter any other error, that other error is returned. Otherwise,
// an ErrFieldMismatch is returned.
errFieldMismatch = err
} else {
return err
}
}
if mat != multiArgTypeStructPtr {
ev = ev.Elem()
}
dv.Set(reflect.Append(dv, ev))
return nil
}()
if err != nil {
return err
}
}
//fmt.Println("queryBackground: Finished")
return errFieldMismatch
}

View File

@ -1,486 +0,0 @@
// +build windows
/*
Package wmi provides a WQL interface for WMI on Windows.
Example code to print names of running processes:
type Win32_Process struct {
Name string
}
func main() {
var dst []Win32_Process
q := wmi.CreateQuery(&dst, "")
err := wmi.Query(q, &dst)
if err != nil {
log.Fatal(err)
}
for i, v := range dst {
println(i, v.Name)
}
}
*/
package wmi
import (
"bytes"
"errors"
"fmt"
"log"
"os"
"reflect"
"runtime"
"strconv"
"strings"
"sync"
"time"
"github.com/go-ole/go-ole"
"github.com/go-ole/go-ole/oleutil"
)
var l = log.New(os.Stdout, "", log.LstdFlags)
var (
ErrInvalidEntityType = errors.New("wmi: invalid entity type")
// ErrNilCreateObject is the error returned if CreateObject returns nil even
// if the error was nil.
ErrNilCreateObject = errors.New("wmi: create object returned nil")
lock sync.Mutex
)
// S_FALSE is returned by CoInitializeEx if it was already called on this thread.
const S_FALSE = 0x00000001
// QueryNamespace invokes Query with the given namespace on the local machine.
func QueryNamespace(query string, dst interface{}, namespace string) error {
return Query(query, dst, nil, namespace)
}
// Query runs the WQL query and appends the values to dst.
//
// dst must have type *[]S or *[]*S, for some struct type S. Fields selected in
// the query must have the same name in dst. Supported types are all signed and
// unsigned integers, time.Time, string, bool, or a pointer to one of those.
// Array types are not supported.
//
// By default, the local machine and default namespace are used. These can be
// changed using connectServerArgs. See
// http://msdn.microsoft.com/en-us/library/aa393720.aspx for details.
//
// Query is a wrapper around DefaultClient.Query.
func Query(query string, dst interface{}, connectServerArgs ...interface{}) error {
if DefaultClient.SWbemServicesClient == nil {
return DefaultClient.Query(query, dst, connectServerArgs...)
}
return DefaultClient.SWbemServicesClient.Query(query, dst, connectServerArgs...)
}
// A Client is an WMI query client.
//
// Its zero value (DefaultClient) is a usable client.
type Client struct {
// NonePtrZero specifies if nil values for fields which aren't pointers
// should be returned as the field types zero value.
//
// Setting this to true allows stucts without pointer fields to be used
// without the risk failure should a nil value returned from WMI.
NonePtrZero bool
// PtrNil specifies if nil values for pointer fields should be returned
// as nil.
//
// Setting this to true will set pointer fields to nil where WMI
// returned nil, otherwise the types zero value will be returned.
PtrNil bool
// AllowMissingFields specifies that struct fields not present in the
// query result should not result in an error.
//
// Setting this to true allows custom queries to be used with full
// struct definitions instead of having to define multiple structs.
AllowMissingFields bool
// SWbemServiceClient is an optional SWbemServices object that can be
// initialized and then reused across multiple queries. If it is null
// then the method will initialize a new temporary client each time.
SWbemServicesClient *SWbemServices
}
// DefaultClient is the default Client and is used by Query, QueryNamespace
var DefaultClient = &Client{}
// Query runs the WQL query and appends the values to dst.
//
// dst must have type *[]S or *[]*S, for some struct type S. Fields selected in
// the query must have the same name in dst. Supported types are all signed and
// unsigned integers, time.Time, string, bool, or a pointer to one of those.
// Array types are not supported.
//
// By default, the local machine and default namespace are used. These can be
// changed using connectServerArgs. See
// http://msdn.microsoft.com/en-us/library/aa393720.aspx for details.
func (c *Client) Query(query string, dst interface{}, connectServerArgs ...interface{}) error {
dv := reflect.ValueOf(dst)
if dv.Kind() != reflect.Ptr || dv.IsNil() {
return ErrInvalidEntityType
}
dv = dv.Elem()
mat, elemType := checkMultiArg(dv)
if mat == multiArgTypeInvalid {
return ErrInvalidEntityType
}
lock.Lock()
defer lock.Unlock()
runtime.LockOSThread()
defer runtime.UnlockOSThread()
err := ole.CoInitializeEx(0, ole.COINIT_MULTITHREADED)
if err != nil {
oleCode := err.(*ole.OleError).Code()
if oleCode != ole.S_OK && oleCode != S_FALSE {
return err
}
}
defer ole.CoUninitialize()
unknown, err := oleutil.CreateObject("WbemScripting.SWbemLocator")
if err != nil {
return err
} else if unknown == nil {
return ErrNilCreateObject
}
defer unknown.Release()
wmi, err := unknown.QueryInterface(ole.IID_IDispatch)
if err != nil {
return err
}
defer wmi.Release()
// service is a SWbemServices
serviceRaw, err := oleutil.CallMethod(wmi, "ConnectServer", connectServerArgs...)
if err != nil {
return err
}
service := serviceRaw.ToIDispatch()
defer serviceRaw.Clear()
// result is a SWBemObjectSet
resultRaw, err := oleutil.CallMethod(service, "ExecQuery", query)
if err != nil {
return err
}
result := resultRaw.ToIDispatch()
defer resultRaw.Clear()
count, err := oleInt64(result, "Count")
if err != nil {
return err
}
enumProperty, err := result.GetProperty("_NewEnum")
if err != nil {
return err
}
defer enumProperty.Clear()
enum, err := enumProperty.ToIUnknown().IEnumVARIANT(ole.IID_IEnumVariant)
if err != nil {
return err
}
if enum == nil {
return fmt.Errorf("can't get IEnumVARIANT, enum is nil")
}
defer enum.Release()
// Initialize a slice with Count capacity
dv.Set(reflect.MakeSlice(dv.Type(), 0, int(count)))
var errFieldMismatch error
for itemRaw, length, err := enum.Next(1); length > 0; itemRaw, length, err = enum.Next(1) {
if err != nil {
return err
}
err := func() error {
// item is a SWbemObject, but really a Win32_Process
item := itemRaw.ToIDispatch()
defer item.Release()
ev := reflect.New(elemType)
if err = c.loadEntity(ev.Interface(), item); err != nil {
if _, ok := err.(*ErrFieldMismatch); ok {
// We continue loading entities even in the face of field mismatch errors.
// If we encounter any other error, that other error is returned. Otherwise,
// an ErrFieldMismatch is returned.
errFieldMismatch = err
} else {
return err
}
}
if mat != multiArgTypeStructPtr {
ev = ev.Elem()
}
dv.Set(reflect.Append(dv, ev))
return nil
}()
if err != nil {
return err
}
}
return errFieldMismatch
}
// ErrFieldMismatch is returned when a field is to be loaded into a different
// type than the one it was stored from, or when a field is missing or
// unexported in the destination struct.
// StructType is the type of the struct pointed to by the destination argument.
type ErrFieldMismatch struct {
StructType reflect.Type
FieldName string
Reason string
}
func (e *ErrFieldMismatch) Error() string {
return fmt.Sprintf("wmi: cannot load field %q into a %q: %s",
e.FieldName, e.StructType, e.Reason)
}
var timeType = reflect.TypeOf(time.Time{})
// loadEntity loads a SWbemObject into a struct pointer.
func (c *Client) loadEntity(dst interface{}, src *ole.IDispatch) (errFieldMismatch error) {
v := reflect.ValueOf(dst).Elem()
for i := 0; i < v.NumField(); i++ {
f := v.Field(i)
of := f
isPtr := f.Kind() == reflect.Ptr
if isPtr {
ptr := reflect.New(f.Type().Elem())
f.Set(ptr)
f = f.Elem()
}
n := v.Type().Field(i).Name
if !f.CanSet() {
return &ErrFieldMismatch{
StructType: of.Type(),
FieldName: n,
Reason: "CanSet() is false",
}
}
prop, err := oleutil.GetProperty(src, n)
if err != nil {
if !c.AllowMissingFields {
errFieldMismatch = &ErrFieldMismatch{
StructType: of.Type(),
FieldName: n,
Reason: "no such struct field",
}
}
continue
}
defer prop.Clear()
switch val := prop.Value().(type) {
case int8, int16, int32, int64, int:
v := reflect.ValueOf(val).Int()
switch f.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
f.SetInt(v)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
f.SetUint(uint64(v))
default:
return &ErrFieldMismatch{
StructType: of.Type(),
FieldName: n,
Reason: "not an integer class",
}
}
case uint8, uint16, uint32, uint64:
v := reflect.ValueOf(val).Uint()
switch f.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
f.SetInt(int64(v))
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
f.SetUint(v)
default:
return &ErrFieldMismatch{
StructType: of.Type(),
FieldName: n,
Reason: "not an integer class",
}
}
case string:
switch f.Kind() {
case reflect.String:
f.SetString(val)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
iv, err := strconv.ParseInt(val, 10, 64)
if err != nil {
return err
}
f.SetInt(iv)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
uv, err := strconv.ParseUint(val, 10, 64)
if err != nil {
return err
}
f.SetUint(uv)
case reflect.Struct:
switch f.Type() {
case timeType:
if len(val) == 25 {
mins, err := strconv.Atoi(val[22:])
if err != nil {
return err
}
val = val[:22] + fmt.Sprintf("%02d%02d", mins/60, mins%60)
}
t, err := time.Parse("20060102150405.000000-0700", val)
if err != nil {
return err
}
f.Set(reflect.ValueOf(t))
}
}
case bool:
switch f.Kind() {
case reflect.Bool:
f.SetBool(val)
default:
return &ErrFieldMismatch{
StructType: of.Type(),
FieldName: n,
Reason: "not a bool",
}
}
case float32:
switch f.Kind() {
case reflect.Float32:
f.SetFloat(float64(val))
default:
return &ErrFieldMismatch{
StructType: of.Type(),
FieldName: n,
Reason: "not a Float32",
}
}
default:
if f.Kind() == reflect.Slice {
switch f.Type().Elem().Kind() {
case reflect.String:
safeArray := prop.ToArray()
if safeArray != nil {
arr := safeArray.ToValueArray()
fArr := reflect.MakeSlice(f.Type(), len(arr), len(arr))
for i, v := range arr {
s := fArr.Index(i)
s.SetString(v.(string))
}
f.Set(fArr)
}
case reflect.Uint8:
safeArray := prop.ToArray()
if safeArray != nil {
arr := safeArray.ToValueArray()
fArr := reflect.MakeSlice(f.Type(), len(arr), len(arr))
for i, v := range arr {
s := fArr.Index(i)
s.SetUint(reflect.ValueOf(v).Uint())
}
f.Set(fArr)
}
default:
return &ErrFieldMismatch{
StructType: of.Type(),
FieldName: n,
Reason: fmt.Sprintf("unsupported slice type (%T)", val),
}
}
} else {
typeof := reflect.TypeOf(val)
if typeof == nil && (isPtr || c.NonePtrZero) {
if (isPtr && c.PtrNil) || (!isPtr && c.NonePtrZero) {
of.Set(reflect.Zero(of.Type()))
}
break
}
return &ErrFieldMismatch{
StructType: of.Type(),
FieldName: n,
Reason: fmt.Sprintf("unsupported type (%T)", val),
}
}
}
}
return errFieldMismatch
}
type multiArgType int
const (
multiArgTypeInvalid multiArgType = iota
multiArgTypeStruct
multiArgTypeStructPtr
)
// checkMultiArg checks that v has type []S, []*S for some struct type S.
//
// It returns what category the slice's elements are, and the reflect.Type
// that represents S.
func checkMultiArg(v reflect.Value) (m multiArgType, elemType reflect.Type) {
if v.Kind() != reflect.Slice {
return multiArgTypeInvalid, nil
}
elemType = v.Type().Elem()
switch elemType.Kind() {
case reflect.Struct:
return multiArgTypeStruct, elemType
case reflect.Ptr:
elemType = elemType.Elem()
if elemType.Kind() == reflect.Struct {
return multiArgTypeStructPtr, elemType
}
}
return multiArgTypeInvalid, nil
}
func oleInt64(item *ole.IDispatch, prop string) (int64, error) {
v, err := oleutil.GetProperty(item, prop)
if err != nil {
return 0, err
}
defer v.Clear()
i := int64(v.Val)
return i, nil
}
// CreateQuery returns a WQL query string that queries all columns of src. where
// is an optional string that is appended to the query, to be used with WHERE
// clauses. In such a case, the "WHERE" string should appear at the beginning.
func CreateQuery(src interface{}, where string) string {
var b bytes.Buffer
b.WriteString("SELECT ")
s := reflect.Indirect(reflect.ValueOf(src))
t := s.Type()
if s.Kind() == reflect.Slice {
t = t.Elem()
}
if t.Kind() != reflect.Struct {
return ""
}
var fields []string
for i := 0; i < t.NumField(); i++ {
fields = append(fields, t.Field(i).Name)
}
b.WriteString(strings.Join(fields, ", "))
b.WriteString(" FROM ")
b.WriteString(t.Name())
b.WriteString(" " + where)
return b.String()
}

View File

@ -1,28 +0,0 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof
coverage.sh
coverage.out
coverage.html

View File

@ -1,11 +0,0 @@
language: go
sudo: false
go:
- 1.7
- 1.8
- tip
before_install:
- go get github.com/mattn/goveralls
script:
- $HOME/gopath/bin/goveralls -service=travis-ci

View File

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2016 Aaron Longwell
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,232 +0,0 @@
Go Trello API
================
[![Trello Logo](https://raw.githubusercontent.com/adlio/trello/master/trello-logo.png)](https://www.trello.com)
[![GoDoc](https://godoc.org/github.com/adlio/trello?status.svg)](http://godoc.org/github.com/adlio/trello)
[![Build Status](https://travis-ci.org/adlio/trello.svg)](https://travis-ci.org/adlio/trello)
[![Coverage Status](https://coveralls.io/repos/github/adlio/trello/badge.svg?branch=master)](https://coveralls.io/github/adlio/trello?branch=master)
A #golang package to access the [Trello API](https://developers.trello.com/v1.0/reference). Nearly 100% of the
read-only surface area of the API is covered, as is creation and modification of Cards.
Low-level infrastructure for features to modify Lists and Boards are easy to add... just not
done yet.
Pull requests are welcome for missing features.
My current development focus is documentation, especially enhancing this README.md with more
example use cases.
## Installation
The Go Trello API has been Tested compatible with Go 1.7 on up. Its only dependency is
the `github.com/pkg/errors` package. It otherwise relies only on the Go standard library.
```
go get github.com/adlio/trello
```
## Basic Usage
All interaction starts with a `trello.Client`. Create one with your appKey and token:
```Go
client := trello.NewClient(appKey, token)
```
All API requests accept a trello.Arguments object. This object is a simple
`map[string]string`, converted to query string arguments in the API call.
Trello has sane defaults on API calls. We have a `trello.Defaults()` utility function
which can be used when you desire the default Trello arguments. Internally,
`trello.Defaults()` is an empty map, which translates to an empty query string.
```Go
board, err := client.GetBoard("bOaRdID", trello.Defaults())
if err != nil {
// Handle error
}
```
## Client Longevity
When getting Lists from Boards or Cards from Lists, the original `trello.Client` pointer
is carried along in returned child objects. It's important to realize that this enables
you to make additional API calls via functions invoked on the objects you receive:
```Go
client := trello.NewClient(appKey, token)
board, err := client.GetBoard("ID", trello.Defaults())
// GetLists makes an API call to /boards/:id/lists using credentials from `client`
lists, err := board.GetLists(trello.Defaults())
for _, list := range lists {
// GetCards makes an API call to /lists/:id/cards using credentials from `client`
cards, err := list.GetCards(trello.Defaults())
}
```
## Get Trello Boards for a User
Boards can be retrieved directly by their ID (see example above), or by asking
for all boards for a member:
```Go
member, err := client.GetMember("usernameOrId", trello.Defaults())
if err != nil {
// Handle error
}
boards, err := member.GetBoards(trello.Defaults())
if err != nil {
// Handle error
}
```
## Get Trello Lists on a Board
```Go
board, err := client.GetBoard("bOaRdID", trello.Defaults())
if err != nil {
// Handle error
}
lists, err := board.GetLists(trello.Defaults())
if err != nil {
// Handle error
}
```
## Get Trello Cards on a Board
```Go
board, err := client.GetBoard("bOaRdID", trello.Defaults())
if err != nil {
// Handle error
}
cards, err := board.GetCards(trello.Defaults())
if err != nil {
// Handle error
}
```
## Get Trello Cards on a List
```Go
list, err := client.GetList("lIsTID", trello.Defaults())
if err != nil {
// Handle error
}
cards, err := list.GetCards(trello.Defaults())
if err != nil {
// Handle error
}
```
## Creating a Card
The API provides several mechanisms for creating new cards.
### Creating Cards from Scratch on the Client
This approach requires the most input data on the card:
```Go
card := trello.Card{
Name: "Card Name",
Desc: "Card description",
Pos: 12345.678,
IDList: "iDOfaLiSt",
IDLabels: []string{"labelID1", "labelID2"},
}
err := client.CreateCard(card, trello.Defaults())
```
### Creating Cards On a List
```Go
list, err := client.GetList("lIsTID", trello.Defaults())
list.AddCard(trello.Card{ Name: "Card Name", Description: "Card description" }, trello.Defaults())
```
### Creating a Card by Copying Another Card
```Go
err := card.CopyToList("listIdNUmber", trello.Defaults())
```
## Get Actions on a Board
```Go
board, err := client.GetBoard("bOaRdID", trello.Defaults())
if err != nil {
// Handle error
}
actions, err := board.GetActions(trello.Defaults())
if err != nil {
// Handle error
}
```
## Get Actions on a Card
```Go
card, err := client.GetCard("cArDID", trello.Defaults())
if err != nil {
// Handle error
}
actions, err := card.GetActions(trello.Defaults())
if err != nil {
// Handle error
}
```
## Rearrange Cards Within a List
```Go
err := card.MoveToTopOfList()
err = card.MoveToBottomOfList()
err = card.SetPos(12345.6789)
```
## Moving a Card to Another List
```Go
err := card.MoveToList("listIdNUmber", trello.Defaults())
```
## Card Ancestry
Trello provides ancestry tracking when cards are created as copies of other cards. This package
provides functions for working with this data:
```Go
// ancestors will hold a slice of *trello.Cards, with the first
// being the card's parent, and the last being parent's parent's parent...
ancestors, err := card.GetAncestorCards(trello.Defaults())
// GetOriginatingCard() is an alias for the last element in the slice
// of ancestor cards.
ultimateParentCard, err := card.GetOriginatingCard(trello.Defaults())
```
## Debug Logging
If you'd like to see all API calls logged, you can attach a `.Logger` (implementing `Debugf(string, ...interface{})`)
to your client. The interface for the logger mimics logrus. Example usage:
```Go
logger := logrus.New()
logger.SetLevel(logrus.DebugLevel)
client := trello.NewClient(appKey, token)
client.Logger = logger
```

View File

@ -1,5 +0,0 @@
- Create List
- Delete Card
- Archive Card
- Reorder Cards in List

View File

@ -1,57 +0,0 @@
package trello
import (
"sort"
)
// ActionCollection is an alias of []*Action, which sorts by the Action's ID.
// Which is the same as sorting by the Action's time of occurrence
type ActionCollection []*Action
func (c ActionCollection) Len() int { return len(c) }
func (c ActionCollection) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
func (c ActionCollection) Less(i, j int) bool { return c[i].ID < c[j].ID }
func (actions ActionCollection) FirstCardCreateAction() *Action {
sort.Sort(actions)
for _, action := range actions {
if action.DidCreateCard() {
return action
}
}
return nil
}
func (actions ActionCollection) ContainsCardCreation() bool {
return actions.FirstCardCreateAction() != nil
}
func (c ActionCollection) FilterToCardCreationActions() ActionCollection {
newSlice := make(ActionCollection, 0, len(c))
for _, action := range c {
if action.DidCreateCard() {
newSlice = append(newSlice, action)
}
}
return newSlice
}
func (c ActionCollection) FilterToListChangeActions() ActionCollection {
newSlice := make(ActionCollection, 0, len(c))
for _, action := range c {
if action.DidChangeListForCard() {
newSlice = append(newSlice, action)
}
}
return newSlice
}
func (c ActionCollection) FilterToCardMembershipChangeActions() ActionCollection {
newSlice := make(ActionCollection, 0, len(c))
for _, action := range c {
if action.DidChangeCardMembership() || action.DidArchiveCard() || action.DidUnarchiveCard() {
newSlice = append(newSlice, action)
}
}
return newSlice
}

View File

@ -1,157 +0,0 @@
// Copyright © 2016 Aaron Longwell
//
// Use of this source code is governed by an MIT licese.
// Details in the LICENSE file.
package trello
import (
"fmt"
"time"
)
type Action struct {
ID string `json:"id"`
IDMemberCreator string `json:"idMemberCreator"`
Type string `json:"type"`
Date time.Time `json:"date"`
Data *ActionData `json:"data,omitempty"`
MemberCreator *Member `json:"memberCreator,omitempty"`
Member *Member `json:"member,omitempty"`
}
type ActionData struct {
Text string `json:"text,omitempty"`
List *List `json:"list,omitempty"`
Card *ActionDataCard `json:"card,omitempty"`
CardSource *ActionDataCard `json:"cardSource,omitempty"`
Board *Board `json:"board,omitempty"`
Old *ActionDataCard `json:"old,omitempty"`
ListBefore *List `json:"listBefore,omitempty"`
ListAfter *List `json:"listAfter,omitempty"`
DateLastEdited time.Time `json:"dateLastEdited"`
CheckItem *CheckItem `json:"checkItem"`
Checklist *Checklist `json:"checklist"`
}
type ActionDataCard struct {
ID string `json:"id"`
Name string `json:"name"`
IDShort int `json:"idShort"`
ShortLink string `json:"shortLink"`
Pos float64 `json:"pos"`
Closed bool `json:"closed"`
}
func (b *Board) GetActions(args Arguments) (actions ActionCollection, err error) {
path := fmt.Sprintf("boards/%s/actions", b.ID)
err = b.client.Get(path, args, &actions)
return
}
func (l *List) GetActions(args Arguments) (actions ActionCollection, err error) {
path := fmt.Sprintf("lists/%s/actions", l.ID)
err = l.client.Get(path, args, &actions)
return
}
func (c *Card) GetActions(args Arguments) (actions ActionCollection, err error) {
path := fmt.Sprintf("cards/%s/actions", c.ID)
err = c.client.Get(path, args, &actions)
return
}
// GetListChangeActions retrieves a slice of Actions which resulted in changes
// to the card's active List. This includes the createCard and copyCard action (which
// place the card in its first list, and the updateCard:closed action, which remove it
// from its last list.
//
// This function is just an alias for:
// card.GetActions(Arguments{"filter": "createCard,copyCard,updateCard:idList,updateCard:closed", "limit": "1000"})
//
func (c *Card) GetListChangeActions() (actions ActionCollection, err error) {
return c.GetActions(Arguments{"filter": "createCard,copyCard,updateCard:idList,updateCard:closed"})
}
func (c *Card) GetMembershipChangeActions() (actions ActionCollection, err error) {
// We include updateCard:closed as if the member is implicitly removed from the card when it's closed.
// This allows us to "close out" the duration length.
return c.GetActions(Arguments{"filter": "addMemberToCard,removeMemberFromCard,updateCard:closed"})
}
// DidCreateCard() returns true if this action created a card, false otherwise.
func (a *Action) DidCreateCard() bool {
switch a.Type {
case "createCard", "emailCard", "copyCard", "convertToCardFromCheckItem":
return true
case "moveCardToBoard":
return true // Unsure about this one
default:
return false
}
}
func (a *Action) DidArchiveCard() bool {
return (a.Type == "updateCard") && a.Data != nil && a.Data.Card != nil && a.Data.Card.Closed
}
func (a *Action) DidUnarchiveCard() bool {
return (a.Type == "updateCard") && a.Data != nil && a.Data.Old != nil && a.Data.Old.Closed
}
// Returns true if this action created the card (in which case it caused it to enter its
// first list), archived the card (in which case it caused it to leave its last List),
// or was an updateCard action involving a change to the list. This is supporting
// functionality for ListDuration.
//
func (a *Action) DidChangeListForCard() bool {
if a.DidCreateCard() {
return true
}
if a.DidArchiveCard() {
return true
}
if a.DidUnarchiveCard() {
return true
}
if a.Type == "updateCard" {
if a.Data != nil && a.Data.ListAfter != nil {
return true
}
}
return false
}
func (a *Action) DidChangeCardMembership() bool {
switch a.Type {
case "addMemberToCard":
return true
case "removeMemberFromCard":
return true
default:
return false
}
}
// ListAfterAction calculates which List the card ended up in after this action
// completed. Returns nil when the action resulted in the card being archived (in
// which case we consider it to not be in a list anymore), or when the action isn't
// related to a list at all (in which case this is a nonsensical question to ask).
//
func ListAfterAction(a *Action) *List {
switch a.Type {
case "createCard", "copyCard", "emailCard", "convertToCardFromCheckItem":
return a.Data.List
case "updateCard":
if a.DidArchiveCard() {
return nil
} else if a.DidUnarchiveCard() {
return a.Data.List
}
if a.Data.ListAfter != nil {
return a.Data.ListAfter
}
}
return nil
}

View File

@ -1,24 +0,0 @@
// Copyright © 2016 Aaron Longwell
//
// Use of this source code is governed by an MIT licese.
// Details in the LICENSE file.
package trello
import (
"net/url"
)
type Arguments map[string]string
func Defaults() Arguments {
return make(Arguments)
}
func (args Arguments) ToURLValues() url.Values {
v := url.Values{}
for key, value := range args {
v.Set(key, value)
}
return v
}

View File

@ -1,29 +0,0 @@
// Copyright © 2016 Aaron Longwell
//
// Use of this source code is governed by an MIT licese.
// Details in the LICENSE file.
package trello
type Attachment struct {
ID string `json:"id"`
Name string `json:"name"`
Pos float32 `json:"pos"`
Bytes int `json:"int"`
Date string `json:"date"`
EdgeColor string `json:"edgeColor"`
IDMember string `json:"idMember"`
IsUpload bool `json:"isUpload"`
MimeType string `json:"mimeType"`
Previews []AttachmentPreview `json:"previews"`
URL string `json:"url"`
}
type AttachmentPreview struct {
ID string `json:"_id"`
URL string `json:"url"`
Width int `json:"width"`
Height int `json:"height"`
Bytes int `json:"bytes"`
Scaled bool `json:"scaled"`
}

View File

@ -1,90 +0,0 @@
// Copyright © 2016 Aaron Longwell
//
// Use of this source code is governed by an MIT licese.
// Details in the LICENSE file.
package trello
import (
"fmt"
"time"
)
type Board struct {
client *Client
ID string `json:"id"`
Name string `json:"name"`
Desc string `json:"desc"`
Closed bool `json:"closed"`
IdOrganization string `json:"idOrganization"`
Pinned bool `json:"pinned"`
Url string `json:"url"`
ShortUrl string `json:"shortUrl"`
Prefs struct {
PermissionLevel string `json:"permissionLevel"`
Voting string `json:"voting"`
Comments string `json:"comments"`
Invitations string `json:"invitations"`
SelfJoin bool `json:"selfjoin"`
CardCovers bool `json:"cardCovers"`
CardAging string `json:"cardAging"`
CalendarFeedEnabled bool `json:"calendarFeedEnabled"`
Background string `json:"background"`
BackgroundColor string `json:"backgroundColor"`
BackgroundImage string `json:"backgroundImage"`
BackgroundImageScaled []BackgroundImage `json:"backgroundImageScaled"`
BackgroundTile bool `json:"backgroundTile"`
BackgroundBrightness string `json:"backgroundBrightness"`
CanBePublic bool `json:"canBePublic"`
CanBeOrg bool `json:"canBeOrg"`
CanBePrivate bool `json:"canBePrivate"`
CanInvite bool `json:"canInvite"`
} `json:"prefs"`
LabelNames struct {
Black string `json:"black,omitempty"`
Blue string `json:"blue,omitempty"`
Green string `json:"green,omitempty"`
Lime string `json:"lime,omitempty"`
Orange string `json:"orange,omitempty"`
Pink string `json:"pink,omitempty"`
Purple string `json:"purple,omitempty"`
Red string `json:"red,omitempty"`
Sky string `json:"sky,omitempty"`
Yellow string `json:"yellow,omitempty"`
} `json:"labelNames"`
Lists []*List `json:"lists"`
Actions []*Action `json:"actions"`
Organization Organization `json:"organization"`
}
type BackgroundImage struct {
Width int `json:"width"`
Height int `json:"height"`
URL string `json:"url"`
}
func (b *Board) CreatedAt() time.Time {
t, _ := IDToTime(b.ID)
return t
}
/**
* Board retrieves a Trello board by its ID.
*/
func (c *Client) GetBoard(boardID string, args Arguments) (board *Board, err error) {
path := fmt.Sprintf("boards/%s", boardID)
err = c.Get(path, args, &board)
if board != nil {
board.client = c
}
return
}
func (m *Member) GetBoards(args Arguments) (boards []*Board, err error) {
path := fmt.Sprintf("members/%s/boards", m.ID)
err = m.client.Get(path, args, &boards)
for i := range boards {
boards[i].client = m.client
}
return
}

View File

@ -1,451 +0,0 @@
// Copyright © 2016 Aaron Longwell
//
// Use of this source code is governed by an MIT licese.
// Details in the LICENSE file.
package trello
import (
"fmt"
"strconv"
"strings"
"time"
"github.com/pkg/errors"
)
type Card struct {
client *Client
// Key metadata
ID string `json:"id"`
IDShort int `json:"idShort"`
Name string `json:"name"`
Pos float64 `json:"pos"`
Email string `json:"email"`
ShortLink string `json:"shortLink"`
ShortUrl string `json:"shortUrl"`
Url string `json:"url"`
Desc string `json:"desc"`
Due *time.Time `json:"due"`
DueComplete bool `json:"dueComplete"`
Closed bool `json:"closed"`
Subscribed bool `json:"subscribed"`
DateLastActivity *time.Time `json:"dateLastActivity"`
// Board
Board *Board
IDBoard string `json:"idBoard"`
// List
List *List
IDList string `json:"idList"`
// Badges
Badges struct {
Votes int `json:"votes"`
ViewingMemberVoted bool `json:"viewingMemberVoted"`
Subscribed bool `json:"subscribed"`
Fogbugz string `json:"fogbugz,omitempty"`
CheckItems int `json:"checkItems"`
CheckItemsChecked int `json:"checkItemsChecked"`
Comments int `json:"comments"`
Attachments int `json:"attachments"`
Description bool `json:"description"`
Due *time.Time `json:"due,omitempty"`
} `json:"badges"`
// Actions
Actions ActionCollection `json:"actions,omitempty"`
// Checklists
IDCheckLists []string `json:"idCheckLists"`
Checklists []*Checklist `json:"checklists,omitempty"`
CheckItemStates []*CheckItemState `json:"checkItemStates,omitempty"`
// Members
IDMembers []string `json:"idMembers,omitempty"`
IDMembersVoted []string `json:"idMembersVoted,omitempty"`
Members []*Member `json:"members,omitempty"`
// Attachments
IDAttachmentCover string `json:"idAttachmentCover"`
ManualCoverAttachment bool `json:"manualCoverAttachment"`
Attachments []*Attachment `json:"attachments,omitempty"`
// Labels
IDLabels []string `json:"idLabels,omitempty"`
Labels []*Label `json:"labels,omitempty"`
// Custom Fields
CustomFieldItems []*CustomFieldItem `json:"customFieldItems",omitempty`
customFieldMap *map[string]interface{}
}
func (c *Card) CreatedAt() time.Time {
t, _ := IDToTime(c.ID)
return t
}
func (c *Card) CustomFields(boardCustomFields []*CustomField) (map[string]interface{}) {
cfm := c.customFieldMap
if cfm == nil {
cfm = &(map[string]interface{} {})
// bcfOptionNames[CustomField ID] = Custom Field Name
bcfOptionNames := map[string]string{}
// bcfOptionsMap[CustomField ID][ID of the option] = Value of the option
bcfOptionsMap := map[string] map[string]interface{}{}
for _, bcf := range boardCustomFields {
bcfOptionNames[bcf.ID] = bcf.Name
for _, cf := range bcf.Options {
// create 2nd level map when not available yet
map2, ok := bcfOptionsMap[cf.IDCustomField]
if !ok {
map2 = map[string]interface{}{}
bcfOptionsMap[bcf.ID] = map2
}
bcfOptionsMap[bcf.ID][cf.ID] = cf.Value.Text
}
}
for _, cf := range c.CustomFieldItems {
name := bcfOptionNames[cf.IDCustomField]
// create 2nd level map when not available yet
map2, ok := bcfOptionsMap[cf.IDCustomField]
if !ok {
continue
}
value, ok := map2[cf.IDValue]
if ok {
(*cfm)[name] = value
}
}
c.customFieldMap = cfm
}
return *cfm
}
func (c *Card) MoveToList(listID string, args Arguments) error {
path := fmt.Sprintf("cards/%s", c.ID)
args["idList"] = listID
return c.client.Put(path, args, &c)
}
func (c *Card) SetPos(newPos float64) error {
path := fmt.Sprintf("cards/%s", c.ID)
return c.client.Put(path, Arguments{"pos": fmt.Sprintf("%f", newPos)}, c)
}
func (c *Card) RemoveMember(memberID string) error {
path := fmt.Sprintf("cards/%s/idMembers/%s", c.ID, memberID)
return c.client.Delete(path, Defaults(), nil)
}
func (c *Card) AddMemberID(memberID string) (member []*Member, err error) {
path := fmt.Sprintf("cards/%s/idMembers", c.ID)
err = c.client.Post(path, Arguments{"value": memberID}, &member)
return member, err
}
func (c *Card) RemoveIDLabel(labelID string, label *Label) error {
path := fmt.Sprintf("cards/%s/idLabels/%s", c.ID, labelID)
return c.client.Delete(path, Defaults(), label)
}
func (c *Card) AddIDLabel(labelID string) error {
path := fmt.Sprintf("cards/%s/idLabels", c.ID)
err := c.client.Post(path, Arguments{"value": labelID}, &c.IDLabels)
return err
}
func (c *Card) MoveToTopOfList() error {
path := fmt.Sprintf("cards/%s", c.ID)
return c.client.Put(path, Arguments{"pos": "top"}, c)
}
func (c *Card) MoveToBottomOfList() error {
path := fmt.Sprintf("cards/%s", c.ID)
return c.client.Put(path, Arguments{"pos": "bottom"}, c)
}
func (c *Card) Update(args Arguments) error {
path := fmt.Sprintf("cards/%s", c.ID)
return c.client.Put(path, args, c)
}
func (c *Client) CreateCard(card *Card, extraArgs Arguments) error {
path := "cards"
args := Arguments{
"name": card.Name,
"desc": card.Desc,
"pos": strconv.FormatFloat(card.Pos, 'g', -1, 64),
"idList": card.IDList,
"idMembers": strings.Join(card.IDMembers, ","),
"idLabels": strings.Join(card.IDLabels, ","),
}
if card.Due != nil {
args["due"] = card.Due.Format(time.RFC3339)
}
// Allow overriding the creation position with 'top' or 'botttom'
if pos, ok := extraArgs["pos"]; ok {
args["pos"] = pos
}
err := c.Post(path, args, &card)
if err == nil {
card.client = c
}
return err
}
func (l *List) AddCard(card *Card, extraArgs Arguments) error {
path := fmt.Sprintf("lists/%s/cards", l.ID)
args := Arguments{
"name": card.Name,
"desc": card.Desc,
"idMembers": strings.Join(card.IDMembers, ","),
"idLabels": strings.Join(card.IDLabels, ","),
}
if card.Due != nil {
args["due"] = card.Due.Format(time.RFC3339)
}
// Allow overwriting the creation position with 'top' or 'bottom'
if pos, ok := extraArgs["pos"]; ok {
args["pos"] = pos
}
err := l.client.Post(path, args, &card)
if err == nil {
card.client = l.client
} else {
err = errors.Wrapf(err, "Error adding card to list %s", l.ID)
}
return err
}
// Try these Arguments
//
// Arguments["keepFromSource"] = "all"
// Arguments["keepFromSource"] = "none"
// Arguments["keepFromSource"] = "attachments,checklists,comments"
//
func (c *Card) CopyToList(listID string, args Arguments) (*Card, error) {
path := "cards"
args["idList"] = listID
args["idCardSource"] = c.ID
newCard := Card{}
err := c.client.Post(path, args, &newCard)
if err == nil {
newCard.client = c.client
} else {
err = errors.Wrapf(err, "Error copying card '%s' to list '%s'.", c.ID, listID)
}
return &newCard, err
}
func (c *Card) AddComment(comment string, args Arguments) (*Action, error) {
path := fmt.Sprintf("cards/%s/actions/comments", c.ID)
args["text"] = comment
action := Action{}
err := c.client.Post(path, args, &action)
if err != nil {
err = errors.Wrapf(err, "Error commenting on card %s", c.ID)
}
return &action, err
}
// If this Card was created from a copy of another Card, this func retrieves
// the originating Card. Returns an error only when a low-level failure occurred.
// If this Card has no parent, a nil card and nil error are returned. In other words, the
// non-existence of a parent is not treated as an error.
//
func (c *Card) GetParentCard(args Arguments) (*Card, error) {
// Hopefully the card came pre-loaded with Actions including the card creation
action := c.Actions.FirstCardCreateAction()
if action == nil {
// No luck. Go get copyCard actions for this card.
c.client.log("Creation action wasn't supplied before GetParentCard() on '%s'. Getting copyCard actions.", c.ID)
actions, err := c.GetActions(Arguments{"filter": "copyCard"})
if err != nil {
err = errors.Wrapf(err, "GetParentCard() failed to GetActions() for card '%s'", c.ID)
return nil, err
}
action = actions.FirstCardCreateAction()
}
if action != nil && action.Data != nil && action.Data.CardSource != nil {
card, err := c.client.GetCard(action.Data.CardSource.ID, args)
return card, err
}
return nil, nil
}
func (c *Card) GetAncestorCards(args Arguments) (ancestors []*Card, err error) {
// Get the first parent
parent, err := c.GetParentCard(args)
if IsNotFound(err) || IsPermissionDenied(err) {
c.client.log("[trello] Can't get details about the parent of card '%s' due to lack of permissions or card deleted.", c.ID)
return ancestors, nil
}
for parent != nil {
ancestors = append(ancestors, parent)
parent, err = parent.GetParentCard(args)
if IsNotFound(err) || IsPermissionDenied(err) {
c.client.log("[trello] Can't get details about the parent of card '%s' due to lack of permissions or card deleted.", c.ID)
return ancestors, nil
} else if err != nil {
return ancestors, err
}
}
return ancestors, err
}
func (c *Card) GetOriginatingCard(args Arguments) (*Card, error) {
ancestors, err := c.GetAncestorCards(args)
if err != nil {
return c, err
}
if len(ancestors) > 0 {
return ancestors[len(ancestors)-1], nil
} else {
return c, nil
}
}
func (c *Card) CreatorMember() (*Member, error) {
var actions ActionCollection
var err error
if len(c.Actions) == 0 {
c.Actions, err = c.GetActions(Arguments{"filter": "all", "limit": "1000", "memberCreator_fields": "all"})
if err != nil {
err = errors.Wrapf(err, "GetActions() call failed.")
return nil, err
}
}
actions = c.Actions.FilterToCardCreationActions()
if len(actions) > 0 {
return actions[0].MemberCreator, nil
}
return nil, errors.Errorf("No card creation actions on Card %s with a .MemberCreator", c.ID)
}
func (c *Card) CreatorMemberID() (string, error) {
var actions ActionCollection
var err error
if len(c.Actions) == 0 {
c.client.log("[trello] CreatorMemberID() called on card '%s' without any Card.Actions. Fetching fresh.", c.ID)
c.Actions, err = c.GetActions(Defaults())
if err != nil {
err = errors.Wrapf(err, "GetActions() call failed.")
}
}
actions = c.Actions.FilterToCardCreationActions()
if len(actions) > 0 {
if actions[0].IDMemberCreator != "" {
return actions[0].IDMemberCreator, err
}
}
return "", errors.Wrapf(err, "No Actions on card '%s' could be used to find its creator.", c.ID)
}
func (b *Board) ContainsCopyOfCard(cardID string, args Arguments) (bool, error) {
args["filter"] = "copyCard"
actions, err := b.GetActions(args)
if err != nil {
err := errors.Wrapf(err, "GetCards() failed inside ContainsCopyOf() for board '%s' and card '%s'.", b.ID, cardID)
return false, err
}
for _, action := range actions {
if action.Data != nil && action.Data.CardSource != nil && action.Data.CardSource.ID == cardID {
return true, nil
}
}
return false, nil
}
func (c *Client) GetCard(cardID string, args Arguments) (card *Card, err error) {
path := fmt.Sprintf("cards/%s", cardID)
err = c.Get(path, args, &card)
if card != nil {
card.client = c
}
return card, err
}
/**
* Retrieves all Cards on a Board
*
* If before
*/
func (b *Board) GetCards(args Arguments) (cards []*Card, err error) {
path := fmt.Sprintf("boards/%s/cards", b.ID)
err = b.client.Get(path, args, &cards)
// Naive implementation would return here. To make sure we get all
// cards, we begin
if len(cards) > 0 {
moreCards := true
for moreCards == true {
nextCardBatch := make([]*Card, 0)
args["before"] = EarliestCardID(cards)
err = b.client.Get(path, args, &nextCardBatch)
if len(nextCardBatch) > 0 {
cards = append(cards, nextCardBatch...)
} else {
moreCards = false
}
}
}
for i := range cards {
cards[i].client = b.client
}
return
}
/**
* Retrieves all Cards in a List
*/
func (l *List) GetCards(args Arguments) (cards []*Card, err error) {
path := fmt.Sprintf("lists/%s/cards", l.ID)
err = l.client.Get(path, args, &cards)
for i := range cards {
cards[i].client = l.client
}
return
}
func EarliestCardID(cards []*Card) string {
if len(cards) == 0 {
return ""
}
earliest := cards[0].ID
for _, card := range cards {
if card.ID < earliest {
earliest = card.ID
}
}
return earliest
}

View File

@ -1,30 +0,0 @@
// Copyright © 2016 Aaron Longwell
//
// Use of this source code is governed by an MIT licese.
// Details in the LICENSE file.
package trello
type Checklist struct {
ID string `json:"id"`
Name string `json:"name"`
IDBoard string `json:"idBoard,omitempty"`
IDCard string `json:"idCard,omitempty"`
Pos float64 `json:"pos,omitempty"`
CheckItems []CheckItem `json:"checkItems,omitempty"`
}
type CheckItem struct {
ID string `json:"id"`
Name string `json:"name"`
State string `json:"state"`
IDChecklist string `json:"idChecklist,omitempty"`
Pos float64 `json:"pos,omitempty"`
}
// Manifestation of CheckItem when it appears in CheckItemStates
// on a Card.
type CheckItemState struct {
IDCheckItem string `json:"idCheckItem"`
State string `json:"state"`
}

View File

@ -1,239 +0,0 @@
// Copyright © 2016 Aaron Longwell
//
// Use of this source code is governed by an MIT licese.
// Details in the LICENSE file.
package trello
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"time"
"github.com/pkg/errors"
)
const DEFAULT_BASEURL = "https://api.trello.com/1"
type Client struct {
Client *http.Client
Logger logger
BaseURL string
Key string
Token string
throttle <-chan time.Time
testMode bool
ctx context.Context
}
type logger interface {
Debugf(string, ...interface{})
}
func NewClient(key, token string) *Client {
return &Client{
Client: http.DefaultClient,
BaseURL: DEFAULT_BASEURL,
Key: key,
Token: token,
throttle: time.Tick(time.Second / 8), // Actually 10/second, but we're extra cautious
testMode: false,
ctx: context.Background(),
}
}
func (c *Client) WithContext(ctx context.Context) *Client {
newC := *c
newC.ctx = ctx
return &newC
}
func (c *Client) Throttle() {
if !c.testMode {
<-c.throttle
}
}
func (c *Client) Get(path string, args Arguments, target interface{}) error {
// Trello prohibits more than 10 seconds/second per token
c.Throttle()
params := args.ToURLValues()
c.log("[trello] GET %s?%s", path, params.Encode())
if c.Key != "" {
params.Set("key", c.Key)
}
if c.Token != "" {
params.Set("token", c.Token)
}
url := fmt.Sprintf("%s/%s", c.BaseURL, path)
urlWithParams := fmt.Sprintf("%s?%s", url, params.Encode())
req, err := http.NewRequest("GET", urlWithParams, nil)
if err != nil {
return errors.Wrapf(err, "Invalid GET request %s", url)
}
req = req.WithContext(c.ctx)
resp, err := c.Client.Do(req)
if err != nil {
return errors.Wrapf(err, "HTTP request failure on %s", url)
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return makeHttpClientError(url, resp)
}
decoder := json.NewDecoder(resp.Body)
err = decoder.Decode(target)
if err != nil {
return errors.Wrapf(err, "JSON decode failed on %s", url)
}
return nil
}
func (c *Client) Put(path string, args Arguments, target interface{}) error {
// Trello prohibits more than 10 seconds/second per token
c.Throttle()
params := args.ToURLValues()
c.log("[trello] PUT %s?%s", path, params.Encode())
if c.Key != "" {
params.Set("key", c.Key)
}
if c.Token != "" {
params.Set("token", c.Token)
}
url := fmt.Sprintf("%s/%s", c.BaseURL, path)
urlWithParams := fmt.Sprintf("%s?%s", url, params.Encode())
req, err := http.NewRequest("PUT", urlWithParams, nil)
if err != nil {
return errors.Wrapf(err, "Invalid PUT request %s", url)
}
resp, err := c.Client.Do(req)
if err != nil {
return errors.Wrapf(err, "HTTP request failure on %s", url)
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return makeHttpClientError(url, resp)
}
decoder := json.NewDecoder(resp.Body)
err = decoder.Decode(target)
if err != nil {
return errors.Wrapf(err, "JSON decode failed on %s", url)
}
return nil
}
func (c *Client) Post(path string, args Arguments, target interface{}) error {
// Trello prohibits more than 10 seconds/second per token
c.Throttle()
params := args.ToURLValues()
c.log("[trello] POST %s?%s", path, params.Encode())
if c.Key != "" {
params.Set("key", c.Key)
}
if c.Token != "" {
params.Set("token", c.Token)
}
url := fmt.Sprintf("%s/%s", c.BaseURL, path)
urlWithParams := fmt.Sprintf("%s?%s", url, params.Encode())
req, err := http.NewRequest("POST", urlWithParams, nil)
if err != nil {
return errors.Wrapf(err, "Invalid POST request %s", url)
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
resp, err := c.Client.Do(req)
if err != nil {
return errors.Wrapf(err, "HTTP request failure on %s", url)
}
defer resp.Body.Close()
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
return errors.Wrapf(err, "HTTP Read error on response for %s", url)
}
decoder := json.NewDecoder(bytes.NewBuffer(b))
err = decoder.Decode(target)
if err != nil {
return errors.Wrapf(err, "JSON decode failed on %s:\n%s", url, string(b))
}
return nil
}
func (c *Client) Delete(path string, args Arguments, target interface{}) error {
c.Throttle()
params := args.ToURLValues()
c.log("[trello] DELETE %s?%s", path, params.Encode())
if c.Key != "" {
params.Set("key", c.Key)
}
if c.Token != "" {
params.Set("token", c.Token)
}
url := fmt.Sprintf("%s/%s", c.BaseURL, path)
urlWithParams := fmt.Sprintf("%s?%s", url, params.Encode())
req, err := http.NewRequest("DELETE", urlWithParams, nil)
if err != nil {
return errors.Wrapf(err, "Invalid DELETE request %s", url)
}
resp, err := c.Client.Do(req)
if err != nil {
return errors.Wrapf(err, "HTTP request failure on %s", url)
}
defer resp.Body.Close()
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
return errors.Wrapf(err, "HTTP Read error on response for %s", url)
}
decoder := json.NewDecoder(bytes.NewBuffer(b))
err = decoder.Decode(target)
if err != nil {
return errors.Wrapf(err, "JSON decode failed on %s:\n%s", url, string(b))
}
return nil
}
func (c *Client) log(format string, args ...interface{}) {
if c.Logger != nil {
c.Logger.Debugf(format, args...)
}
}

View File

@ -1,49 +0,0 @@
package trello
import "fmt"
type CustomFieldItem struct {
ID string `json:"id"`
IDValue string `json:"idValue"`
IDCustomField string `json:"idCustomField"`
IDModel string `json:"idModel"`
IDModelType string `json:"modelType,omitempty"`
}
type CustomField struct {
ID string `json:"id"`
IDModel string `json:"idModel"`
IDModelType string `json:"modelType,omitempty"`
FieldGroup string `json:"fieldGroup"`
Name string `json:"name"`
Pos int `json:"pos"`
Display struct {
CardFront bool `json:"cardfront"`
} `json:"display"`
Type string `json:"type"`
Options []*CustomFieldOption `json:"options"`
}
type CustomFieldOption struct {
ID string `json:"id"`
IDCustomField string `json:"idCustomField"`
Value struct {
Text string `json:"text"`
} `json:"value"`
Color string `json:"color,omitempty"`
Pos int `json:"pos"`
}
func (c *Client) GetCustomField(fieldID string, args Arguments) (customField *CustomField, err error) {
path := fmt.Sprintf("customFields/%s", fieldID)
err = c.Get(path, args, &customField)
return
}
func (b *Board) GetCustomFields(args Arguments) (customFields []*CustomField, err error) {
path := fmt.Sprintf("boards/%s/customFields", b.ID)
err = b.client.Get(path, args, &customFields)
return
}

View File

@ -1,55 +0,0 @@
package trello
import (
"fmt"
"io/ioutil"
"net/http"
)
type notFoundError interface {
IsNotFound() bool
}
type rateLimitError interface {
IsRateLimit() bool
}
type permissionDeniedError interface {
IsPermissionDenied() bool
}
type httpClientError struct {
msg string
code int
}
func makeHttpClientError(url string, resp *http.Response) error {
body, _ := ioutil.ReadAll(resp.Body)
msg := fmt.Sprintf("HTTP request failure on %s:\n%d: %s", url, resp.StatusCode, string(body))
return &httpClientError{
msg: msg,
code: resp.StatusCode,
}
}
func (e *httpClientError) Error() string { return e.msg }
func (e *httpClientError) IsRateLimit() bool { return e.code == 429 }
func (e *httpClientError) IsNotFound() bool { return e.code == 404 }
func (e *httpClientError) IsPermissionDenied() bool { return e.code == 401 }
func IsRateLimit(err error) bool {
re, ok := err.(rateLimitError)
return ok && re.IsRateLimit()
}
func IsNotFound(err error) bool {
nf, ok := err.(notFoundError)
return ok && nf.IsNotFound()
}
func IsPermissionDenied(err error) bool {
pd, ok := err.(permissionDeniedError)
return ok && pd.IsPermissionDenied()
}

View File

@ -1,28 +0,0 @@
// Copyright © 2016 Aaron Longwell
//
// Use of this source code is governed by an MIT licese.
// Details in the LICENSE file.
package trello
import "fmt"
type Label struct {
ID string `json:"id"`
IDBoard string `json:"idBoard"`
Name string `json:"name"`
Color string `json:"color"`
Uses int `json:"uses"`
}
func (c *Client) GetLabel(labelID string, args Arguments) (label *Label, err error) {
path := fmt.Sprintf("labels/%s", labelID)
err = c.Get(path, args, &label)
return
}
func (b *Board) GetLabels(args Arguments) (labels []*Label, err error) {
path := fmt.Sprintf("boards/%s/labels", b.ID)
err = b.client.Get(path, args, &labels)
return
}

View File

@ -1,9 +0,0 @@
package trello
type ByFirstEntered []*ListDuration
func (durs ByFirstEntered) Len() int { return len(durs) }
func (durs ByFirstEntered) Less(i, j int) bool {
return durs[i].FirstEntered.Before(durs[j].FirstEntered)
}
func (durs ByFirstEntered) Swap(i, j int) { durs[i], durs[j] = durs[j], durs[i] }

View File

@ -1,82 +0,0 @@
package trello
import (
"sort"
"time"
"github.com/pkg/errors"
)
type ListDuration struct {
ListID string
ListName string
Duration time.Duration
FirstEntered time.Time
TimesInList int
}
func (l *ListDuration) AddDuration(d time.Duration) {
l.Duration = l.Duration + d
l.TimesInList++
}
// Analytzes a Cards actions to figure out how long it was in each List
func (c *Card) GetListDurations() (durations []*ListDuration, err error) {
var actions ActionCollection
if len(c.Actions) == 0 {
// Get all actions which affected the Card's List
c.client.log("[trello] GetListDurations() called on card '%s' without any Card.Actions. Fetching fresh.", c.ID)
actions, err = c.GetListChangeActions()
if err != nil {
err = errors.Wrap(err, "GetListChangeActions() call failed.")
return
}
} else {
actions = c.Actions.FilterToListChangeActions()
}
return actions.GetListDurations()
}
func (actions ActionCollection) GetListDurations() (durations []*ListDuration, err error) {
sort.Sort(actions)
var prevTime time.Time
var prevList *List
durs := make(map[string]*ListDuration)
for _, action := range actions {
if action.DidChangeListForCard() {
if prevList != nil {
duration := action.Date.Sub(prevTime)
_, durExists := durs[prevList.ID]
if !durExists {
durs[prevList.ID] = &ListDuration{ListID: prevList.ID, ListName: prevList.Name, Duration: duration, TimesInList: 1, FirstEntered: prevTime}
} else {
durs[prevList.ID].AddDuration(duration)
}
}
prevList = ListAfterAction(action)
prevTime = action.Date
}
}
if prevList != nil {
duration := time.Now().Sub(prevTime)
_, durExists := durs[prevList.ID]
if !durExists {
durs[prevList.ID] = &ListDuration{ListID: prevList.ID, ListName: prevList.Name, Duration: duration, TimesInList: 1, FirstEntered: prevTime}
} else {
durs[prevList.ID].AddDuration(duration)
}
}
durations = make([]*ListDuration, 0, len(durs))
for _, ld := range durs {
durations = append(durations, ld)
}
sort.Sort(ByFirstEntered(durations))
return durations, nil
}

View File

@ -1,51 +0,0 @@
// Copyright © 2016 Aaron Longwell
//
// Use of this source code is governed by an MIT licese.
// Details in the LICENSE file.
package trello
import (
"fmt"
"time"
)
type List struct {
client *Client
ID string `json:"id"`
Name string `json:"name"`
IDBoard string `json:"idBoard,omitempty"`
Closed bool `json:"closed"`
Pos float32 `json:"pos,omitempty"`
Board *Board `json:"board,omitempty"`
Cards []*Card `json:"cards,omitempty"`
}
func (l *List) CreatedAt() time.Time {
t, _ := IDToTime(l.ID)
return t
}
func (c *Client) GetList(listID string, args Arguments) (list *List, err error) {
path := fmt.Sprintf("lists/%s", listID)
err = c.Get(path, args, &list)
if list != nil {
list.client = c
for i := range list.Cards {
list.Cards[i].client = c
}
}
return
}
func (b *Board) GetLists(args Arguments) (lists []*List, err error) {
path := fmt.Sprintf("boards/%s/lists", b.ID)
err = b.client.Get(path, args, &lists)
for i := range lists {
lists[i].client = b.client
for j := range lists[i].Cards {
lists[i].Cards[j].client = b.client
}
}
return
}

View File

@ -1,118 +0,0 @@
package trello
import (
"sort"
"time"
"github.com/pkg/errors"
)
// Used to track the periods of time which a user (member) is attached to a card.
//
type MemberDuration struct {
MemberID string
MemberName string
FirstAdded time.Time
Duration time.Duration
active bool
lastAdded time.Time
}
type ByLongestDuration []*MemberDuration
func (d ByLongestDuration) Len() int { return len(d) }
func (d ByLongestDuration) Less(i, j int) bool { return d[i].Duration > d[j].Duration }
func (d ByLongestDuration) Swap(i, j int) { d[i], d[j] = d[j], d[i] }
func (d *MemberDuration) addAsOf(t time.Time) {
d.active = true
if d.FirstAdded.IsZero() {
d.FirstAdded = t
}
d.startTimerAsOf(t)
}
func (d *MemberDuration) startTimerAsOf(t time.Time) {
if d.active {
d.lastAdded = t
}
}
func (d *MemberDuration) removeAsOf(t time.Time) {
d.stopTimerAsOf(t)
d.active = false
d.lastAdded = time.Time{}
}
func (d *MemberDuration) stopTimerAsOf(t time.Time) {
if d.active {
d.Duration = d.Duration + t.Sub(d.lastAdded)
}
}
func (c *Card) GetMemberDurations() (durations []*MemberDuration, err error) {
var actions ActionCollection
if len(c.Actions) == 0 {
c.client.log("[trello] GetMemberDurations() called on card '%s' without any Card.Actions. Fetching fresh.", c.ID)
actions, err = c.GetMembershipChangeActions()
if err != nil {
err = errors.Wrap(err, "GetMembershipChangeActions() call failed.")
return
}
} else {
actions = c.Actions.FilterToCardMembershipChangeActions()
}
return actions.GetMemberDurations()
}
// Similar to GetListDurations(), this function returns a slice of MemberDuration objects,
// which describes the length of time each member was attached to this card. Durations are
// calculated such that being added to a card starts a timer for that member, and being removed
// starts it again (so that if a person is added and removed multiple times, the duration
// captures only the times which they were attached). Archiving the card also stops the timer.
//
func (actions ActionCollection) GetMemberDurations() (durations []*MemberDuration, err error) {
sort.Sort(actions)
durs := make(map[string]*MemberDuration)
for _, action := range actions {
if action.DidChangeCardMembership() {
_, durExists := durs[action.Member.ID]
if !durExists {
switch action.Type {
case "addMemberToCard":
durs[action.Member.ID] = &MemberDuration{MemberID: action.Member.ID, MemberName: action.Member.FullName}
durs[action.Member.ID].addAsOf(action.Date)
case "removeMemberFromCard":
// Surprisingly, this is possible. If a card was copied, and members were preserved, those
// members exist on the card without a corresponding addMemberToCard action.
t, _ := IDToTime(action.Data.Card.ID)
durs[action.Member.ID] = &MemberDuration{MemberID: action.Member.ID, MemberName: action.Member.FullName, lastAdded: t}
durs[action.Member.ID].removeAsOf(action.Date)
}
} else {
switch action.Type {
case "addMemberToCard":
durs[action.Member.ID].addAsOf(action.Date)
case "removeMemberFromCard":
durs[action.Member.ID].removeAsOf(action.Date)
}
}
} else if action.DidArchiveCard() {
for id, _ := range durs {
durs[id].stopTimerAsOf(action.Date)
}
} else if action.DidUnarchiveCard() {
for id, _ := range durs {
durs[id].startTimerAsOf(action.Date)
}
}
}
durations = make([]*MemberDuration, 0, len(durs))
for _, md := range durs {
durations = append(durations, md)
}
// sort.Sort(ByLongestDuration(durations))
return durations, nil
}

View File

@ -1,56 +0,0 @@
// Copyright © 2016 Aaron Longwell
//
// Use of this source code is governed by an MIT licese.
// Details in the LICENSE file.
package trello
import (
"fmt"
)
type Member struct {
client *Client
ID string `json:"id"`
Username string `json:"username"`
FullName string `json:"fullName"`
Initials string `json:"initials"`
AvatarHash string `json:"avatarHash"`
Email string `json:"email"`
}
func (c *Client) GetMember(memberID string, args Arguments) (member *Member, err error) {
path := fmt.Sprintf("members/%s", memberID)
err = c.Get(path, args, &member)
if err == nil {
member.client = c
}
return
}
func (o *Organization) GetMembers(args Arguments) (members []*Member, err error) {
path := fmt.Sprintf("organizations/%s/members", o.ID)
err = o.client.Get(path, args, &members)
for i := range members {
members[i].client = o.client
}
return
}
func (b *Board) GetMembers(args Arguments) (members []*Member, err error) {
path := fmt.Sprintf("boards/%s/members", b.ID)
err = b.client.Get(path, args, &members)
for i := range members {
members[i].client = b.client
}
return
}
func (c *Card) GetMembers(args Arguments) (members []*Member, err error) {
path := fmt.Sprintf("cards/%s/members", c.ID)
err = c.client.Get(path, args, &members)
for i := range members {
members[i].client = c.client
}
return
}

View File

@ -1,31 +0,0 @@
// Copyright © 2016 Aaron Longwell
//
// Use of this source code is governed by an MIT licese.
// Details in the LICENSE file.
package trello
import (
"fmt"
)
type Organization struct {
client *Client
ID string `json:"id"`
Name string `json:"name"`
DisplayName string `json:"displayName"`
Desc string `json:"desc"`
URL string `json:"url"`
Website string `json:"website"`
Products []string `json:"products"`
PowerUps []string `json:"powerUps"`
}
func (c *Client) GetOrganization(orgID string, args Arguments) (organization *Organization, err error) {
path := fmt.Sprintf("organizations/%s", orgID)
err = c.Get(path, args, &organization)
if organization != nil {
organization.client = c
}
return
}

View File

@ -1,57 +0,0 @@
// Copyright © 2016 Aaron Longwell
//
// Use of this source code is governed by an MIT licese.
// Details in the LICENSE file.
package trello
type SearchResult struct {
Options SearchOptions `json:"options"`
Actions []*Action `json:"actions,omitempty"`
Cards []*Card `json:"cards,omitempty"`
Boards []*Board `json:"boards,omitempty"`
Members []*Member `json:"members,omitempty"`
}
type SearchOptions struct {
Terms []SearchTerm `json:"terms"`
Modifiers []SearchModifier `json:"modifiers,omitempty"`
ModelTypes []string `json:"modelTypes,omitempty"`
Partial bool `json:"partial"`
}
type SearchModifier struct {
Text string `json:"text"`
}
type SearchTerm struct {
Text string `json:"text"`
Negated bool `json:"negated,omitempty"`
}
func (c *Client) SearchCards(query string, args Arguments) (cards []*Card, err error) {
args["query"] = query
args["modelTypes"] = "cards"
res := SearchResult{}
err = c.Get("search", args, &res)
cards = res.Cards
return
}
func (c *Client) SearchBoards(query string, args Arguments) (boards []*Board, err error) {
args["query"] = query
args["modelTypes"] = "boards"
res := SearchResult{}
err = c.Get("search", args, &res)
boards = res.Boards
for _, board := range boards {
board.client = c
}
return
}
func (c *Client) SearchMembers(query string, args Arguments) (members []*Member, err error) {
args["query"] = query
err = c.Get("search/members", args, &members)
return
}

View File

@ -1,37 +0,0 @@
// Copyright © 2016 Aaron Longwell
//
// Use of this source code is governed by an MIT licese.
// Details in the LICENSE file.
package trello
import (
"fmt"
"time"
)
type Token struct {
client *Client
ID string `json:"id"`
DateCreated time.Time `json:"dateCreated"`
DateExpires *time.Time `json:"dateExpires"`
IDMember string `json:"idMember"`
Identifier string `json:"identifier"`
Permissions []Permission `json:"permissions"`
}
type Permission struct {
IDModel string `json:"idModel"`
ModelType string `json:"modelType"`
Read bool `json:"read"`
Write bool `json:"write"`
}
func (c *Client) GetToken(tokenID string, args Arguments) (token *Token, err error) {
path := fmt.Sprintf("tokens/%s", tokenID)
err = c.Get(path, args, &token)
if token != nil {
token.client = c
}
return
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

View File

@ -1,27 +0,0 @@
// Copyright © 2016 Aaron Longwell
//
// Use of this source code is governed by an MIT licese.
// Details in the LICENSE file.
package trello
import (
"strconv"
"time"
"github.com/pkg/errors"
)
func IDToTime(id string) (t time.Time, err error) {
if id == "" {
return time.Time{}, nil
}
// The first 8 characters in the object ID are a Unix timestamp
ts, err := strconv.ParseUint(id[:8], 16, 64)
if err != nil {
err = errors.Wrapf(err, "ID '%s' failed to convert to timestamp.", id)
} else {
t = time.Unix(int64(ts), 0)
}
return
}

View File

@ -1,111 +0,0 @@
// Copyright © 2016 Aaron Longwell
//
// Use of this source code is governed by an MIT licese.
// Details in the LICENSE file.
package trello
import (
"encoding/json"
"fmt"
"net/http"
"github.com/pkg/errors"
)
// Webhook is the Go representation of a webhook registered in Trello's systems.
// Used when creating, modifying or deleting webhooks.
//
type Webhook struct {
client *Client
ID string `json:"id,omitempty"`
IDModel string `json:"idModel"`
Description string `json:"description"`
CallbackURL string `json:"callbackURL"`
Active bool `json:"active"`
}
// BoardWebhookRequest is the object sent by Trello to a Webhook for Board-triggered
// webhooks.
//
type BoardWebhookRequest struct {
Model *Board
Action *Action
}
// ListWebhookRequest is the object sent by Trello to a Webhook for List-triggered
// webhooks.
//
type ListWebhookRequest struct {
Model *List
Action *Action
}
// CardWebhookRequest is the object sent by Trello to a Webhook for Card-triggered
// webhooks.
//
type CardWebhookRequest struct {
Model *Card
Action *Action
}
func (c *Client) CreateWebhook(webhook *Webhook) error {
path := "webhooks"
args := Arguments{"idModel": webhook.IDModel, "description": webhook.Description, "callbackURL": webhook.CallbackURL}
err := c.Post(path, args, webhook)
if err == nil {
webhook.client = c
}
return err
}
func (c *Client) GetWebhook(webhookID string, args Arguments) (webhook *Webhook, err error) {
path := fmt.Sprintf("webhooks/%s", webhookID)
err = c.Get(path, args, &webhook)
if webhook != nil {
webhook.client = c
}
return
}
func (t *Token) GetWebhooks(args Arguments) (webhooks []*Webhook, err error) {
path := fmt.Sprintf("tokens/%s/webhooks", t.ID)
err = t.client.Get(path, args, &webhooks)
return
}
func GetBoardWebhookRequest(r *http.Request) (whr *BoardWebhookRequest, err error) {
if r.Method == "HEAD" {
return &BoardWebhookRequest{}, nil
}
decoder := json.NewDecoder(r.Body)
err = decoder.Decode(&whr)
if err != nil {
err = errors.Wrapf(err, "GetBoardWebhookRequest() failed to decode '%s'.", r.URL)
}
return
}
func GetListWebhookRequest(r *http.Request) (whr *ListWebhookRequest, err error) {
if r.Method == "HEAD" {
return &ListWebhookRequest{}, nil
}
decoder := json.NewDecoder(r.Body)
err = decoder.Decode(&whr)
if err != nil {
err = errors.Wrapf(err, "GetListWebhookRequest() failed to decode '%s'.", r.URL)
}
return
}
func GetCardWebhookRequest(r *http.Request) (whr *CardWebhookRequest, err error) {
if r.Method == "HEAD" {
return &CardWebhookRequest{}, nil
}
decoder := json.NewDecoder(r.Body)
err = decoder.Decode(&whr)
if err != nil {
err = errors.Wrapf(err, "GetCardWebhookRequest() failed to decode '%s'.", r.URL)
}
return
}

View File

@ -1,18 +0,0 @@
# Binaries for programs and plugins
*.exe
*.dll
*.so
*.dylib
/cmd/chroma/chroma
# Test binary, build with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
.glide/
_models/

View File

@ -1,31 +0,0 @@
project_name: chroma
release:
github:
owner: alecthomas
name: chroma
brew:
install: bin.install "chroma"
builds:
- goos:
- linux
- darwin
- windows
goarch:
- amd64
- "386"
goarm:
- "6"
main: ./cmd/chroma/main.go
ldflags: -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}
binary: chroma
archive:
format: tar.gz
name_template: '{{ .Binary }}-{{ .Version }}-{{ .Os }}-{{ .Arch }}{{ if .Arm }}v{{
.Arm }}{{ end }}'
files:
- COPYING
- README*
snapshot:
name_template: SNAPSHOT-{{ .Commit }}
checksum:
name_template: '{{ .ProjectName }}-{{ .Version }}-checksums.txt'

View File

@ -1,5 +0,0 @@
sudo: false
language: go
after_success:
go get github.com/goreleaser/goreleaser && goreleaser

View File

@ -1,19 +0,0 @@
Copyright (C) 2017 Alec Thomas
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,235 +0,0 @@
# Chroma - A general purpose syntax highlighter in pure Go [![](https://godoc.org/github.com/alecthomas/chroma?status.svg)](http://godoc.org/github.com/alecthomas/chroma) [![Build Status](https://travis-ci.org/alecthomas/chroma.png)](https://travis-ci.org/alecthomas/chroma) [![Gitter chat](https://badges.gitter.im/alecthomas.png)](https://gitter.im/alecthomas/Lobby)
> **NOTE:** As Chroma has just been released, its API is still in flux. That said, the high-level interface should not change significantly.
Chroma takes source code and other structured text and converts it into syntax
highlighted HTML, ANSI-coloured text, etc.
Chroma is based heavily on [Pygments](http://pygments.org/), and includes
translators for Pygments lexers and styles.
## Table of Contents
<!-- MarkdownTOC -->
1. [Supported languages](#supported-languages)
1. [Using the library](#using-the-library)
1. [Quick start](#quick-start)
1. [Identifying the language](#identifying-the-language)
1. [Formatting the output](#formatting-the-output)
1. [The HTML formatter](#the-html-formatter)
1. [More detail](#more-detail)
1. [Lexers](#lexers)
1. [Formatters](#formatters)
1. [Styles](#styles)
1. [Command-line interface](#command-line-interface)
1. [What's missing compared to Pygments?](#whats-missing-compared-to-pygments)
<!-- /MarkdownTOC -->
## Supported languages
Prefix | Language
:----: | --------
A | ABNF, ActionScript, ActionScript 3, Ada, Angular2, ANTLR, ApacheConf, APL, AppleScript, Awk
B | Ballerina, Base Makefile, Bash, Batchfile, BlitzBasic, BNF, Brainfuck
C | C, C#, C++, Cassandra CQL, CFEngine3, cfstatement/ColdFusion, CMake, COBOL, CSS, Cap'n Proto, Ceylon, ChaiScript, Cheetah, Clojure, CoffeeScript, Common Lisp, Coq, Crystal, Cython
D | Dart, Diff, Django/Jinja, Docker, DTD
E | EBNF, Elixir, Elm, EmacsLisp, Erlang
F | Factor, Fish, Forth, Fortran, FSharp
G | GAS, GDScript, GLSL, Genshi, Genshi HTML, Genshi Text, Gnuplot, Go, Go HTML Template, Go Text Template, Groovy
H | Handlebars, Haskell, Haxe, Hexdump, HTML, HTTP, Hy
I | Idris, INI, Io
J | Java, JavaScript, JSON, Jsx, Julia, Jungle
K | Kotlin
L | Lighttpd configuration file, LLVM, Lua
M | Mako, Markdown, Mason, Mathematica, MiniZinc, Modula-2, MonkeyC, MorrowindScript, Myghty, MySQL
N | NASM, Newspeak, Nginx configuration file, Nim, Nix
O | Objective-C, OCaml, Octave, OpenSCAD, Org Mode
P | PacmanConf, Perl, PHP, Pig, PkgConfig, Plaintext, PL/pgSQL, PostgreSQL SQL dialect, PostScript, POVRay, PowerShell, Prolog, Protocol Buffer, Puppet, Python, Python 3
Q | QBasic
R | R, Racket, Ragel, reg, reStructuredText, Rexx, Ruby, Rust
S | Sass, Scala, Scheme, Scilab, SCSS, Smalltalk, Smarty, Snobol, Solidity, SPARQL, SQL, SquidConf, Swift, systemd, Systemverilog
T | TASM, Tcl, Tcsh, Termcap, Terminfo, Terraform, TeX, Thrift, TOML, TradingView, Transact-SQL, Turtle, Twig, TypeScript, TypoScript, TypoScriptCssData, TypoScriptHtmlData
V | verilog, VHDL, VimL
W | WDTE
X | XML, Xorg
Y | YAML
_I will attempt to keep this section up to date, but an authoritative list can be
displayed with `chroma --list`._
## Using the library
Chroma, like Pygments, has the concepts of
[lexers](https://github.com/alecthomas/chroma/tree/master/lexers),
[formatters](https://github.com/alecthomas/chroma/tree/master/formatters) and
[styles](https://github.com/alecthomas/chroma/tree/master/styles).
Lexers convert source text into a stream of tokens, styles specify how token
types are mapped to colours, and formatters convert tokens and styles into
formatted output.
A package exists for each of these, containing a global `Registry` variable
with all of the registered implementations. There are also helper functions
for using the registry in each package, such as looking up lexers by name or
matching filenames, etc.
In all cases, if a lexer, formatter or style can not be determined, `nil` will
be returned. In this situation you may want to default to the `Fallback`
value in each respective package, which provides sane defaults.
### Quick start
A convenience function exists that can be used to simply format some source
text, without any effort:
```go
err := quick.Highlight(os.Stdout, someSourceCode, "go", "html", "monokai")
```
### Identifying the language
To highlight code, you'll first have to identify what language the code is
written in. There are three primary ways to do that:
1. Detect the language from its filename.
```go
lexer := lexers.Match("foo.go")
```
3. Explicitly specify the language by its Chroma syntax ID (a full list is available from `lexers.Names()`).
```go
lexer := lexers.Get("go")
```
3. Detect the language from its content.
```go
lexer := lexers.Analyse("package main\n\nfunc main()\n{\n}\n")
```
In all cases, `nil` will be returned if the language can not be identified.
```go
if lexer == nil {
lexer = lexers.Fallback
}
```
At this point, it should be noted that some lexers can be extremely chatty. To
mitigate this, you can use the coalescing lexer to coalesce runs of identical
token types into a single token:
```go
lexer = chroma.Coalesce(lexer)
```
### Formatting the output
Once a language is identified you will need to pick a formatter and a style (theme).
```go
style := styles.Get("swapoff")
if style == nil {
style = styles.Fallback
}
formatter := formatters.Get("html")
if formatter == nil {
formatter = formatters.Fallback
}
```
Then obtain an iterator over the tokens:
```go
contents, err := ioutil.ReadAll(r)
iterator, err := lexer.Tokenise(nil, string(contents))
```
And finally, format the tokens from the iterator:
```go
err := formatter.Format(w, style, iterator)
```
### The HTML formatter
By default the `html` registered formatter generates standalone HTML with
embedded CSS. More flexibility is available through the `formatters/html` package.
Firstly, the output generated by the formatter can be customised with the
following constructor options:
- `Standalone()` - generate standalone HTML with embedded CSS.
- `WithClasses()` - use classes rather than inlined style attributes.
- `ClassPrefix(prefix)` - prefix each generated CSS class.
- `TabWidth(width)` - Set the rendered tab width, in characters.
- `WithLineNumbers()` - Render line numbers (style with `LineNumbers`).
- `HighlightLines(ranges)` - Highlight lines in these ranges (style with `LineHighlight`).
- `LineNumbersInTable()` - Use a table for formatting line numbers and code, rather than spans.
If `WithClasses()` is used, the corresponding CSS can be obtained from the formatter with:
```go
formatter := html.New(html.WithClasses())
err := formatter.WriteCSS(w, style)
```
## More detail
### Lexers
See the [Pygments documentation](http://pygments.org/docs/lexerdevelopment/)
for details on implementing lexers. Most concepts apply directly to Chroma,
but see existing lexer implementations for real examples.
In many cases lexers can be automatically converted directly from Pygments by
using the included Python 3 script `pygments2chroma.py`. I use something like
the following:
```
python3 ~/Projects/chroma/_tools/pygments2chroma.py \
pygments.lexers.jvm.KotlinLexer \
> ~/Projects/chroma/lexers/kotlin.go \
&& gofmt -s -w ~/Projects/chroma/lexers/*.go
```
See notes in [pygments-lexers.go](https://github.com/alecthomas/chroma/blob/master/pygments-lexers.txt)
for a list of lexers, and notes on some of the issues importing them.
### Formatters
Chroma supports HTML output, as well as terminal output in 8 colour, 256 colour, and true-colour.
A `noop` formatter is included that outputs the token text only, and a `tokens`
formatter outputs raw tokens. The latter is useful for debugging lexers.
### Styles
Chroma styles use the [same syntax](http://pygments.org/docs/styles/) as Pygments.
All Pygments styles have been converted to Chroma using the `_tools/style.py` script.
For a quick overview of the available styles and how they look, check out the [Chroma Style Gallery](https://xyproto.github.io/splash/docs/).
## Command-line interface
A command-line interface to Chroma is included. It can be installed with:
```
go get -u github.com/alecthomas/chroma/cmd/chroma
```
## What's missing compared to Pygments?
- Quite a few lexers, for various reasons (pull-requests welcome):
- Pygments lexers for complex languages often include custom code to
handle certain aspects, such as Perl6's ability to nest code inside
regular expressions. These require time and effort to convert.
- I mostly only converted languages I had heard of, to reduce the porting cost.
- Some more esoteric features of Pygments are omitted for simplicity.
- Though the Chroma API supports content detection, very few languages support them.
I have plans to implement a statistical analyser at some point, but not enough time.

View File

@ -1,35 +0,0 @@
package chroma
// Coalesce is a Lexer interceptor that collapses runs of common types into a single token.
func Coalesce(lexer Lexer) Lexer { return &coalescer{lexer} }
type coalescer struct{ Lexer }
func (d *coalescer) Tokenise(options *TokeniseOptions, text string) (Iterator, error) {
var prev Token
it, err := d.Lexer.Tokenise(options, text)
if err != nil {
return nil, err
}
return func() Token {
for token := it(); token != (EOF); token = it() {
if len(token.Value) == 0 {
continue
}
if prev == EOF {
prev = token
} else {
if prev.Type == token.Type && len(prev.Value) < 8192 {
prev.Value += token.Value
} else {
out := prev
prev = token
return out
}
}
}
out := prev
prev = EOF
return out
}, nil
}

View File

@ -1,155 +0,0 @@
package chroma
import (
"fmt"
"math"
"strconv"
"strings"
)
// ANSI2RGB maps ANSI colour names, as supported by Chroma, to hex RGB values.
var ANSI2RGB = map[string]string{
"#ansiblack": "000000",
"#ansidarkred": "7f0000",
"#ansidarkgreen": "007f00",
"#ansibrown": "7f7fe0",
"#ansidarkblue": "00007f",
"#ansipurple": "7f007f",
"#ansiteal": "007f7f",
"#ansilightgray": "e5e5e5",
// Normal
"#ansidarkgray": "555555",
"#ansired": "ff0000",
"#ansigreen": "00ff00",
"#ansiyellow": "ffff00",
"#ansiblue": "0000ff",
"#ansifuchsia": "ff00ff",
"#ansiturquoise": "00ffff",
"#ansiwhite": "ffffff",
// Aliases without the "ansi" prefix, because...why?
"#black": "000000",
"#darkred": "7f0000",
"#darkgreen": "007f00",
"#brown": "7f7fe0",
"#darkblue": "00007f",
"#purple": "7f007f",
"#teal": "007f7f",
"#lightgray": "e5e5e5",
// Normal
"#darkgray": "555555",
"#red": "ff0000",
"#green": "00ff00",
"#yellow": "ffff00",
"#blue": "0000ff",
"#fuchsia": "ff00ff",
"#turquoise": "00ffff",
"#white": "ffffff",
}
// Colour represents an RGB colour.
type Colour int32
// NewColour creates a Colour directly from RGB values.
func NewColour(r, g, b uint8) Colour {
return ParseColour(fmt.Sprintf("%02x%02x%02x", r, g, b))
}
// Distance between this colour and another.
//
// This uses the approach described here (https://www.compuphase.com/cmetric.htm).
// This is not as accurate as LAB, et. al. but is *vastly* simpler and sufficient for our needs.
func (c Colour) Distance(e2 Colour) float64 {
ar, ag, ab := int64(c.Red()), int64(c.Green()), int64(c.Blue())
br, bg, bb := int64(e2.Red()), int64(e2.Green()), int64(e2.Blue())
rmean := (ar + br) / 2
r := ar - br
g := ag - bg
b := ab - bb
return math.Sqrt(float64((((512 + rmean) * r * r) >> 8) + 4*g*g + (((767 - rmean) * b * b) >> 8)))
}
// Brighten returns a copy of this colour with its brightness adjusted.
//
// If factor is negative, the colour is darkened.
//
// Uses approach described here (http://www.pvladov.com/2012/09/make-color-lighter-or-darker.html).
func (c Colour) Brighten(factor float64) Colour {
r := float64(c.Red())
g := float64(c.Green())
b := float64(c.Blue())
if factor < 0 {
factor++
r *= factor
g *= factor
b *= factor
} else {
r = (255-r)*factor + r
g = (255-g)*factor + g
b = (255-b)*factor + b
}
return NewColour(uint8(r), uint8(g), uint8(b))
}
// Brightness of the colour (roughly) in the range 0.0 to 1.0
func (c Colour) Brightness() float64 {
return (float64(c.Red()) + float64(c.Green()) + float64(c.Blue())) / 255.0 / 3.0
}
// ParseColour in the forms #rgb, #rrggbb, #ansi<colour>, or #<colour>.
// Will return an "unset" colour if invalid.
func ParseColour(colour string) Colour {
colour = normaliseColour(colour)
n, err := strconv.ParseUint(colour, 16, 32)
if err != nil {
return 0
}
return Colour(n + 1)
}
// MustParseColour is like ParseColour except it panics if the colour is invalid.
//
// Will panic if colour is in an invalid format.
func MustParseColour(colour string) Colour {
parsed := ParseColour(colour)
if !parsed.IsSet() {
panic(fmt.Errorf("invalid colour %q", colour))
}
return parsed
}
func (c Colour) IsSet() bool { return c != 0 }
func (c Colour) String() string { return fmt.Sprintf("#%06x", int(c-1)) }
func (c Colour) GoString() string { return fmt.Sprintf("Colour(0x%06x)", int(c-1)) }
// Red component of colour.
func (c Colour) Red() uint8 { return uint8(((c - 1) >> 16) & 0xff) }
// Green component of colour.
func (c Colour) Green() uint8 { return uint8(((c - 1) >> 8) & 0xff) }
// Blue component of colour.
func (c Colour) Blue() uint8 { return uint8((c - 1) & 0xff) }
// Colours is an orderable set of colours.
type Colours []Colour
func (c Colours) Len() int { return len(c) }
func (c Colours) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
func (c Colours) Less(i, j int) bool { return c[i] < c[j] }
// Convert colours to #rrggbb.
func normaliseColour(colour string) string {
if ansi, ok := ANSI2RGB[colour]; ok {
return ansi
}
if strings.HasPrefix(colour, "#") {
colour = colour[1:]
if len(colour) == 3 {
return colour[0:1] + colour[0:1] + colour[1:2] + colour[1:2] + colour[2:3] + colour[2:3]
}
}
return colour
}

View File

@ -1,137 +0,0 @@
package chroma
import (
"bytes"
)
type delegatingLexer struct {
root Lexer
language Lexer
}
// DelegatingLexer combines two lexers to handle the common case of a language embedded inside another, such as PHP
// inside HTML or PHP inside plain text.
//
// It takes two lexer as arguments: a root lexer and a language lexer. First everything is scanned using the language
// lexer, which must return "Other" for unrecognised tokens. Then all "Other" tokens are lexed using the root lexer.
// Finally, these two sets of tokens are merged.
//
// The lexers from the template lexer package use this base lexer.
func DelegatingLexer(root Lexer, language Lexer) Lexer {
return &delegatingLexer{
root: root,
language: language,
}
}
func (d *delegatingLexer) Config() *Config {
return d.language.Config()
}
// An insertion is the character range where language tokens should be inserted.
type insertion struct {
start, end int
tokens []Token
}
func (d *delegatingLexer) Tokenise(options *TokeniseOptions, text string) (Iterator, error) {
tokens, err := Tokenise(Coalesce(d.language), options, text)
if err != nil {
return nil, err
}
// Compute insertions and gather "Other" tokens.
others := &bytes.Buffer{}
insertions := []*insertion{}
var insert *insertion
offset := 0
var last Token
for _, t := range tokens {
if t.Type == Other {
if last != EOF && insert != nil && last.Type != Other {
insert.end = offset
}
others.WriteString(t.Value)
} else {
if last == EOF || last.Type == Other {
insert = &insertion{start: offset}
insertions = append(insertions, insert)
}
insert.tokens = append(insert.tokens, t)
}
last = t
offset += len(t.Value)
}
if len(insertions) == 0 {
return d.root.Tokenise(options, text)
}
// Lex the other tokens.
rootTokens, err := Tokenise(Coalesce(d.root), options, others.String())
if err != nil {
return nil, err
}
// Interleave the two sets of tokens.
var out []Token
offset = 0 // Offset into text.
tokenIndex := 0
nextToken := func() Token {
if tokenIndex >= len(rootTokens) {
return EOF
}
t := rootTokens[tokenIndex]
tokenIndex++
return t
}
insertionIndex := 0
nextInsertion := func() *insertion {
if insertionIndex >= len(insertions) {
return nil
}
i := insertions[insertionIndex]
insertionIndex++
return i
}
t := nextToken()
i := nextInsertion()
for t != EOF || i != nil {
// fmt.Printf("%d->%d:%q %d->%d:%q\n", offset, offset+len(t.Value), t.Value, i.start, i.end, Stringify(i.tokens...))
if t == EOF || (i != nil && i.start < offset+len(t.Value)) {
var l Token
l, t = splitToken(t, i.start-offset)
if l != EOF {
out = append(out, l)
offset += len(l.Value)
}
out = append(out, i.tokens...)
offset += i.end - i.start
if t == EOF {
t = nextToken()
}
i = nextInsertion()
} else {
out = append(out, t)
offset += len(t.Value)
t = nextToken()
}
}
return Literator(out...), nil
}
func splitToken(t Token, offset int) (l Token, r Token) {
if t == EOF {
return EOF, EOF
}
if offset == 0 {
return EOF, t
}
if offset == len(t.Value) {
return t, EOF
}
l = t.Clone()
r = t.Clone()
l.Value = l.Value[:offset]
r.Value = r.Value[offset:]
return
}

View File

@ -1,7 +0,0 @@
// Package chroma takes source code and other structured text and converts it into syntax highlighted HTML, ANSI-
// coloured text, etc.
//
// Chroma is based heavily on Pygments, and includes translators for Pygments lexers and styles.
//
// For more information, go here: https://github.com/alecthomas/chroma
package chroma

View File

@ -1,43 +0,0 @@
package chroma
import (
"io"
)
// A Formatter for Chroma lexers.
type Formatter interface {
// Format returns a formatting function for tokens.
//
// If the iterator panics, the Formatter should recover.
Format(w io.Writer, style *Style, iterator Iterator) error
}
// A FormatterFunc is a Formatter implemented as a function.
//
// Guards against iterator panics.
type FormatterFunc func(w io.Writer, style *Style, iterator Iterator) error
func (f FormatterFunc) Format(w io.Writer, s *Style, it Iterator) (err error) {
defer func() {
if perr := recover(); perr != nil {
err = perr.(error)
}
}()
return f(w, s, it)
}
type recoveringFormatter struct {
Formatter
}
func (r recoveringFormatter) Format(w io.Writer, s *Style, it Iterator) (err error) {
defer func() {
if perr := recover(); perr != nil {
err = perr.(error)
}
}()
return r.Formatter.Format(w, s, it)
}
// RecoveringFormatter wraps a formatter with panic recovery.
func RecoveringFormatter(formatter Formatter) Formatter { return recoveringFormatter{formatter} }

View File

@ -1,55 +0,0 @@
package formatters
import (
"io"
"sort"
"github.com/alecthomas/chroma"
"github.com/alecthomas/chroma/formatters/html"
)
var (
// NoOp formatter.
NoOp = Register("noop", chroma.FormatterFunc(func(w io.Writer, s *chroma.Style, iterator chroma.Iterator) error {
for t := iterator(); t != chroma.EOF; t = iterator() {
if _, err := io.WriteString(w, t.Value); err != nil {
return err
}
}
return nil
}))
// Default HTML formatter outputs self-contained HTML.
htmlFull = Register("html", html.New(html.Standalone(), html.WithClasses()))
)
// Fallback formatter.
var Fallback = NoOp
// Registry of Formatters.
var Registry = map[string]chroma.Formatter{}
// Names of registered formatters.
func Names() []string {
out := []string{}
for name := range Registry {
out = append(out, name)
}
sort.Strings(out)
return out
}
// Get formatter by name.
//
// If the given formatter is not found, the Fallback formatter will be returned.
func Get(name string) chroma.Formatter {
if f, ok := Registry[name]; ok {
return f
}
return Fallback
}
// Register a named formatter.
func Register(name string, formatter chroma.Formatter) chroma.Formatter {
Registry[name] = formatter
return formatter
}

View File

@ -1,392 +0,0 @@
package html
import (
"fmt"
"html"
"io"
"sort"
"strings"
"github.com/alecthomas/chroma"
)
// Option sets an option of the HTML formatter.
type Option func(f *Formatter)
// Standalone configures the HTML formatter for generating a standalone HTML document.
func Standalone() Option { return func(f *Formatter) { f.standalone = true } }
// ClassPrefix sets the CSS class prefix.
func ClassPrefix(prefix string) Option { return func(f *Formatter) { f.prefix = prefix } }
// WithClasses emits HTML using CSS classes, rather than inline styles.
func WithClasses() Option { return func(f *Formatter) { f.Classes = true } }
// TabWidth sets the number of characters for a tab. Defaults to 8.
func TabWidth(width int) Option { return func(f *Formatter) { f.tabWidth = width } }
// PreventSurroundingPre prevents the surrounding pre tags around the generated code
func PreventSurroundingPre() Option { return func(f *Formatter) { f.preventSurroundingPre = true } }
// WithLineNumbers formats output with line numbers.
func WithLineNumbers() Option {
return func(f *Formatter) {
f.lineNumbers = true
}
}
// LineNumbersInTable will, when combined with WithLineNumbers, separate the line numbers
// and code in table td's, which make them copy-and-paste friendly.
func LineNumbersInTable() Option {
return func(f *Formatter) {
f.lineNumbersInTable = true
}
}
// HighlightLines higlights the given line ranges with the Highlight style.
//
// A range is the beginning and ending of a range as 1-based line numbers, inclusive.
func HighlightLines(ranges [][2]int) Option {
return func(f *Formatter) {
f.highlightRanges = ranges
sort.Sort(f.highlightRanges)
}
}
// BaseLineNumber sets the initial number to start line numbering at. Defaults to 1.
func BaseLineNumber(n int) Option {
return func(f *Formatter) {
f.baseLineNumber = n
}
}
// New HTML formatter.
func New(options ...Option) *Formatter {
f := &Formatter{
baseLineNumber: 1,
}
for _, option := range options {
option(f)
}
return f
}
// Formatter that generates HTML.
type Formatter struct {
standalone bool
prefix string
Classes bool // Exported field to detect when classes are being used
preventSurroundingPre bool
tabWidth int
lineNumbers bool
lineNumbersInTable bool
highlightRanges highlightRanges
baseLineNumber int
}
type highlightRanges [][2]int
func (h highlightRanges) Len() int { return len(h) }
func (h highlightRanges) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h highlightRanges) Less(i, j int) bool { return h[i][0] < h[j][0] }
func (f *Formatter) Format(w io.Writer, style *chroma.Style, iterator chroma.Iterator) (err error) {
defer func() {
if perr := recover(); perr != nil {
err = perr.(error)
}
}()
return f.writeHTML(w, style, iterator.Tokens())
}
func brightenOrDarken(colour chroma.Colour, factor float64) chroma.Colour {
if colour.Brightness() < 0.5 {
return colour.Brighten(factor)
}
return colour.Brighten(-factor)
}
// Ensure that style entries exist for highlighting, etc.
func (f *Formatter) restyle(style *chroma.Style) (*chroma.Style, error) {
builder := style.Builder()
bg := builder.Get(chroma.Background)
// If we don't have a line highlight colour, make one that is 10% brighter/darker than the background.
if !style.Has(chroma.LineHighlight) {
highlight := chroma.StyleEntry{Background: bg.Background}
highlight.Background = brightenOrDarken(highlight.Background, 0.1)
builder.AddEntry(chroma.LineHighlight, highlight)
}
// If we don't have line numbers, use the text colour but 20% brighter/darker
if !style.Has(chroma.LineNumbers) {
text := chroma.StyleEntry{Colour: bg.Colour}
text.Colour = brightenOrDarken(text.Colour, 0.5)
builder.AddEntry(chroma.LineNumbers, text)
builder.AddEntry(chroma.LineNumbersTable, text)
}
return builder.Build()
}
// We deliberately don't use html/template here because it is two orders of magnitude slower (benchmarked).
//
// OTOH we need to be super careful about correct escaping...
func (f *Formatter) writeHTML(w io.Writer, style *chroma.Style, tokens []chroma.Token) (err error) { // nolint: gocyclo
style, err = f.restyle(style)
if err != nil {
return err
}
css := f.styleToCSS(style)
if !f.Classes {
for t, style := range css {
css[t] = compressStyle(style)
}
}
if f.standalone {
fmt.Fprint(w, "<html>\n")
if f.Classes {
fmt.Fprint(w, "<style type=\"text/css\">\n")
f.WriteCSS(w, style)
fmt.Fprintf(w, "body { %s; }\n", css[chroma.Background])
fmt.Fprint(w, "</style>")
}
fmt.Fprintf(w, "<body%s>\n", f.styleAttr(css, chroma.Background))
}
wrapInTable := f.lineNumbers && f.lineNumbersInTable
lines := chroma.SplitTokensIntoLines(tokens)
lineDigits := len(fmt.Sprintf("%d", len(lines)))
highlightIndex := 0
if wrapInTable {
// List line numbers in its own <td>
fmt.Fprintf(w, "<div%s>\n", f.styleAttr(css, chroma.Background))
fmt.Fprintf(w, "<table%s><tr>", f.styleAttr(css, chroma.LineTable))
fmt.Fprintf(w, "<td%s>\n", f.styleAttr(css, chroma.LineTableTD))
if !f.preventSurroundingPre {
fmt.Fprintf(w, "<pre%s>", f.styleAttr(css, chroma.Background))
}
for index := range lines {
line := f.baseLineNumber + index
highlight, next := f.shouldHighlight(highlightIndex, line)
if next {
highlightIndex++
}
if highlight {
fmt.Fprintf(w, "<span%s>", f.styleAttr(css, chroma.LineHighlight))
}
fmt.Fprintf(w, "<span%s>%*d\n</span>", f.styleAttr(css, chroma.LineNumbersTable), lineDigits, line)
if highlight {
fmt.Fprintf(w, "</span>")
}
}
if !f.preventSurroundingPre {
fmt.Fprint(w, "</pre>")
}
fmt.Fprint(w, "</td>\n")
fmt.Fprintf(w, "<td%s>\n", f.styleAttr(css, chroma.LineTableTD))
}
if !f.preventSurroundingPre {
fmt.Fprintf(w, "<pre%s>", f.styleAttr(css, chroma.Background))
}
highlightIndex = 0
for index, tokens := range lines {
// 1-based line number.
line := f.baseLineNumber + index
highlight, next := f.shouldHighlight(highlightIndex, line)
if next {
highlightIndex++
}
if highlight {
fmt.Fprintf(w, "<span%s>", f.styleAttr(css, chroma.LineHighlight))
}
if f.lineNumbers && !wrapInTable {
fmt.Fprintf(w, "<span%s>%*d</span>", f.styleAttr(css, chroma.LineNumbers), lineDigits, line)
}
for _, token := range tokens {
html := html.EscapeString(token.String())
attr := f.styleAttr(css, token.Type)
if attr != "" {
html = fmt.Sprintf("<span%s>%s</span>", attr, html)
}
fmt.Fprint(w, html)
}
if highlight {
fmt.Fprintf(w, "</span>")
}
}
if !f.preventSurroundingPre {
fmt.Fprint(w, "</pre>")
}
if wrapInTable {
fmt.Fprint(w, "</td></tr></table>\n")
fmt.Fprint(w, "</div>\n")
}
if f.standalone {
fmt.Fprint(w, "\n</body>\n")
fmt.Fprint(w, "</html>\n")
}
return nil
}
func (f *Formatter) shouldHighlight(highlightIndex, line int) (bool, bool) {
next := false
for highlightIndex < len(f.highlightRanges) && line > f.highlightRanges[highlightIndex][1] {
highlightIndex++
next = true
}
if highlightIndex < len(f.highlightRanges) {
hrange := f.highlightRanges[highlightIndex]
if line >= hrange[0] && line <= hrange[1] {
return true, next
}
}
return false, next
}
func (f *Formatter) class(t chroma.TokenType) string {
for t != 0 {
if cls, ok := chroma.StandardTypes[t]; ok {
if cls != "" {
return f.prefix + cls
}
return ""
}
t = t.Parent()
}
if cls := chroma.StandardTypes[t]; cls != "" {
return f.prefix + cls
}
return ""
}
func (f *Formatter) styleAttr(styles map[chroma.TokenType]string, tt chroma.TokenType) string {
if f.Classes {
cls := f.class(tt)
if cls == "" {
return ""
}
return string(fmt.Sprintf(` class="%s"`, cls))
}
if _, ok := styles[tt]; !ok {
tt = tt.SubCategory()
if _, ok := styles[tt]; !ok {
tt = tt.Category()
if _, ok := styles[tt]; !ok {
return ""
}
}
}
return fmt.Sprintf(` style="%s"`, styles[tt])
}
func (f *Formatter) tabWidthStyle() string {
if f.tabWidth != 0 && f.tabWidth != 8 {
return fmt.Sprintf("; -moz-tab-size: %[1]d; -o-tab-size: %[1]d; tab-size: %[1]d", f.tabWidth)
}
return ""
}
// WriteCSS writes CSS style definitions (without any surrounding HTML).
func (f *Formatter) WriteCSS(w io.Writer, style *chroma.Style) error {
css := f.styleToCSS(style)
// Special-case background as it is mapped to the outer ".chroma" class.
if _, err := fmt.Fprintf(w, "/* %s */ .%schroma { %s }\n", chroma.Background, f.prefix, css[chroma.Background]); err != nil {
return err
}
// Special-case code column of table to expand width.
if f.lineNumbers && f.lineNumbersInTable {
if _, err := fmt.Fprintf(w, "/* %s */ .%schroma .%s:last-child { width: 100%%; }",
chroma.LineTableTD, f.prefix, f.class(chroma.LineTableTD)); err != nil {
return err
}
}
tts := []int{}
for tt := range css {
tts = append(tts, int(tt))
}
sort.Ints(tts)
for _, ti := range tts {
tt := chroma.TokenType(ti)
if tt == chroma.Background {
continue
}
styles := css[tt]
if _, err := fmt.Fprintf(w, "/* %s */ .%schroma .%s { %s }\n", tt, f.prefix, f.class(tt), styles); err != nil {
return err
}
}
return nil
}
func (f *Formatter) styleToCSS(style *chroma.Style) map[chroma.TokenType]string {
classes := map[chroma.TokenType]string{}
bg := style.Get(chroma.Background)
// Convert the style.
for t := range chroma.StandardTypes {
entry := style.Get(t)
if t != chroma.Background {
entry = entry.Sub(bg)
}
if entry.IsZero() {
continue
}
classes[t] = StyleEntryToCSS(entry)
}
classes[chroma.Background] += f.tabWidthStyle()
lineNumbersStyle := "margin-right: 0.4em; padding: 0 0.4em 0 0.4em;"
// All rules begin with default rules followed by user provided rules
classes[chroma.LineNumbers] = lineNumbersStyle + classes[chroma.LineNumbers]
classes[chroma.LineNumbersTable] = lineNumbersStyle + classes[chroma.LineNumbersTable]
classes[chroma.LineHighlight] = "display: block; width: 100%;" + classes[chroma.LineHighlight]
classes[chroma.LineTable] = "border-spacing: 0; padding: 0; margin: 0; border: 0; width: auto; overflow: auto; display: block;" + classes[chroma.LineTable]
classes[chroma.LineTableTD] = "vertical-align: top; padding: 0; margin: 0; border: 0;" + classes[chroma.LineTableTD]
return classes
}
// StyleEntryToCSS converts a chroma.StyleEntry to CSS attributes.
func StyleEntryToCSS(e chroma.StyleEntry) string {
styles := []string{}
if e.Colour.IsSet() {
styles = append(styles, "color: "+e.Colour.String())
}
if e.Background.IsSet() {
styles = append(styles, "background-color: "+e.Background.String())
}
if e.Bold == chroma.Yes {
styles = append(styles, "font-weight: bold")
}
if e.Italic == chroma.Yes {
styles = append(styles, "font-style: italic")
}
if e.Underline == chroma.Yes {
styles = append(styles, "text-decoration: underline")
}
return strings.Join(styles, "; ")
}
// Compress CSS attributes - remove spaces, transform 6-digit colours to 3.
func compressStyle(s string) string {
parts := strings.Split(s, ";")
out := []string{}
for _, p := range parts {
p = strings.Join(strings.Fields(p), " ")
p = strings.Replace(p, ": ", ":", 1)
if strings.Contains(p, "#") {
c := p[len(p)-6:]
if c[0] == c[1] && c[2] == c[3] && c[4] == c[5] {
p = p[:len(p)-6] + c[0:1] + c[2:3] + c[4:5]
}
}
out = append(out, p)
}
return strings.Join(out, ";")
}

View File

@ -1,31 +0,0 @@
package formatters
import (
"encoding/json"
"fmt"
"io"
"github.com/alecthomas/chroma"
)
// JSON formatter outputs the raw token structures as JSON.
var JSON = Register("json", chroma.FormatterFunc(func(w io.Writer, s *chroma.Style, it chroma.Iterator) error {
fmt.Fprintln(w, "[")
i := 0
for t := it(); t != chroma.EOF; t = it() {
if i > 0 {
fmt.Fprintln(w, ",")
}
i++
bytes, err := json.Marshal(t)
if err != nil {
return err
}
if _, err := fmt.Fprint(w, " "+string(bytes)); err != nil {
return err
}
}
fmt.Fprintln(w)
fmt.Fprintln(w, "]")
return nil
}))

View File

@ -1,18 +0,0 @@
package formatters
import (
"fmt"
"io"
"github.com/alecthomas/chroma"
)
// Tokens formatter outputs the raw token structures.
var Tokens = Register("tokens", chroma.FormatterFunc(func(w io.Writer, s *chroma.Style, it chroma.Iterator) error {
for t := it(); t != chroma.EOF; t = it() {
if _, err := fmt.Fprintln(w, t.GoString()); err != nil {
return err
}
}
return nil
}))

View File

@ -1,250 +0,0 @@
package formatters
import (
"fmt"
"io"
"math"
"github.com/alecthomas/chroma"
)
type ttyTable struct {
foreground map[chroma.Colour]string
background map[chroma.Colour]string
}
var c = chroma.MustParseColour
var ttyTables = map[int]*ttyTable{
8: {
foreground: map[chroma.Colour]string{
c("#000000"): "\033[30m", c("#7f0000"): "\033[31m", c("#007f00"): "\033[32m", c("#7f7fe0"): "\033[33m",
c("#00007f"): "\033[34m", c("#7f007f"): "\033[35m", c("#007f7f"): "\033[36m", c("#e5e5e5"): "\033[37m",
c("#555555"): "\033[90m", c("#ff0000"): "\033[91m", c("#00ff00"): "\033[92m", c("#ffff00"): "\033[93m",
c("#0000ff"): "\033[94m", c("#ff00ff"): "\033[95m", c("#00ffff"): "\033[96m", c("#ffffff"): "\033[97m",
},
background: map[chroma.Colour]string{
c("#000000"): "\033[40m", c("#7f0000"): "\033[41m", c("#007f00"): "\033[42m", c("#7f7fe0"): "\033[43m",
c("#00007f"): "\033[44m", c("#7f007f"): "\033[45m", c("#007f7f"): "\033[46m", c("#e5e5e5"): "\033[47m",
c("#555555"): "\033[100m", c("#ff0000"): "\033[101m", c("#00ff00"): "\033[102m", c("#ffff00"): "\033[103m",
c("#0000ff"): "\033[104m", c("#ff00ff"): "\033[105m", c("#00ffff"): "\033[106m", c("#ffffff"): "\033[107m",
},
},
256: {
foreground: map[chroma.Colour]string{
c("#000000"): "\033[38;5;0m", c("#800000"): "\033[38;5;1m", c("#008000"): "\033[38;5;2m", c("#808000"): "\033[38;5;3m",
c("#000080"): "\033[38;5;4m", c("#800080"): "\033[38;5;5m", c("#008080"): "\033[38;5;6m", c("#c0c0c0"): "\033[38;5;7m",
c("#808080"): "\033[38;5;8m", c("#ff0000"): "\033[38;5;9m", c("#00ff00"): "\033[38;5;10m", c("#ffff00"): "\033[38;5;11m",
c("#0000ff"): "\033[38;5;12m", c("#ff00ff"): "\033[38;5;13m", c("#00ffff"): "\033[38;5;14m", c("#ffffff"): "\033[38;5;15m",
c("#000000"): "\033[38;5;16m", c("#00005f"): "\033[38;5;17m", c("#000087"): "\033[38;5;18m", c("#0000af"): "\033[38;5;19m",
c("#0000d7"): "\033[38;5;20m", c("#0000ff"): "\033[38;5;21m", c("#005f00"): "\033[38;5;22m", c("#005f5f"): "\033[38;5;23m",
c("#005f87"): "\033[38;5;24m", c("#005faf"): "\033[38;5;25m", c("#005fd7"): "\033[38;5;26m", c("#005fff"): "\033[38;5;27m",
c("#008700"): "\033[38;5;28m", c("#00875f"): "\033[38;5;29m", c("#008787"): "\033[38;5;30m", c("#0087af"): "\033[38;5;31m",
c("#0087d7"): "\033[38;5;32m", c("#0087ff"): "\033[38;5;33m", c("#00af00"): "\033[38;5;34m", c("#00af5f"): "\033[38;5;35m",
c("#00af87"): "\033[38;5;36m", c("#00afaf"): "\033[38;5;37m", c("#00afd7"): "\033[38;5;38m", c("#00afff"): "\033[38;5;39m",
c("#00d700"): "\033[38;5;40m", c("#00d75f"): "\033[38;5;41m", c("#00d787"): "\033[38;5;42m", c("#00d7af"): "\033[38;5;43m",
c("#00d7d7"): "\033[38;5;44m", c("#00d7ff"): "\033[38;5;45m", c("#00ff00"): "\033[38;5;46m", c("#00ff5f"): "\033[38;5;47m",
c("#00ff87"): "\033[38;5;48m", c("#00ffaf"): "\033[38;5;49m", c("#00ffd7"): "\033[38;5;50m", c("#00ffff"): "\033[38;5;51m",
c("#5f0000"): "\033[38;5;52m", c("#5f005f"): "\033[38;5;53m", c("#5f0087"): "\033[38;5;54m", c("#5f00af"): "\033[38;5;55m",
c("#5f00d7"): "\033[38;5;56m", c("#5f00ff"): "\033[38;5;57m", c("#5f5f00"): "\033[38;5;58m", c("#5f5f5f"): "\033[38;5;59m",
c("#5f5f87"): "\033[38;5;60m", c("#5f5faf"): "\033[38;5;61m", c("#5f5fd7"): "\033[38;5;62m", c("#5f5fff"): "\033[38;5;63m",
c("#5f8700"): "\033[38;5;64m", c("#5f875f"): "\033[38;5;65m", c("#5f8787"): "\033[38;5;66m", c("#5f87af"): "\033[38;5;67m",
c("#5f87d7"): "\033[38;5;68m", c("#5f87ff"): "\033[38;5;69m", c("#5faf00"): "\033[38;5;70m", c("#5faf5f"): "\033[38;5;71m",
c("#5faf87"): "\033[38;5;72m", c("#5fafaf"): "\033[38;5;73m", c("#5fafd7"): "\033[38;5;74m", c("#5fafff"): "\033[38;5;75m",
c("#5fd700"): "\033[38;5;76m", c("#5fd75f"): "\033[38;5;77m", c("#5fd787"): "\033[38;5;78m", c("#5fd7af"): "\033[38;5;79m",
c("#5fd7d7"): "\033[38;5;80m", c("#5fd7ff"): "\033[38;5;81m", c("#5fff00"): "\033[38;5;82m", c("#5fff5f"): "\033[38;5;83m",
c("#5fff87"): "\033[38;5;84m", c("#5fffaf"): "\033[38;5;85m", c("#5fffd7"): "\033[38;5;86m", c("#5fffff"): "\033[38;5;87m",
c("#870000"): "\033[38;5;88m", c("#87005f"): "\033[38;5;89m", c("#870087"): "\033[38;5;90m", c("#8700af"): "\033[38;5;91m",
c("#8700d7"): "\033[38;5;92m", c("#8700ff"): "\033[38;5;93m", c("#875f00"): "\033[38;5;94m", c("#875f5f"): "\033[38;5;95m",
c("#875f87"): "\033[38;5;96m", c("#875faf"): "\033[38;5;97m", c("#875fd7"): "\033[38;5;98m", c("#875fff"): "\033[38;5;99m",
c("#878700"): "\033[38;5;100m", c("#87875f"): "\033[38;5;101m", c("#878787"): "\033[38;5;102m", c("#8787af"): "\033[38;5;103m",
c("#8787d7"): "\033[38;5;104m", c("#8787ff"): "\033[38;5;105m", c("#87af00"): "\033[38;5;106m", c("#87af5f"): "\033[38;5;107m",
c("#87af87"): "\033[38;5;108m", c("#87afaf"): "\033[38;5;109m", c("#87afd7"): "\033[38;5;110m", c("#87afff"): "\033[38;5;111m",
c("#87d700"): "\033[38;5;112m", c("#87d75f"): "\033[38;5;113m", c("#87d787"): "\033[38;5;114m", c("#87d7af"): "\033[38;5;115m",
c("#87d7d7"): "\033[38;5;116m", c("#87d7ff"): "\033[38;5;117m", c("#87ff00"): "\033[38;5;118m", c("#87ff5f"): "\033[38;5;119m",
c("#87ff87"): "\033[38;5;120m", c("#87ffaf"): "\033[38;5;121m", c("#87ffd7"): "\033[38;5;122m", c("#87ffff"): "\033[38;5;123m",
c("#af0000"): "\033[38;5;124m", c("#af005f"): "\033[38;5;125m", c("#af0087"): "\033[38;5;126m", c("#af00af"): "\033[38;5;127m",
c("#af00d7"): "\033[38;5;128m", c("#af00ff"): "\033[38;5;129m", c("#af5f00"): "\033[38;5;130m", c("#af5f5f"): "\033[38;5;131m",
c("#af5f87"): "\033[38;5;132m", c("#af5faf"): "\033[38;5;133m", c("#af5fd7"): "\033[38;5;134m", c("#af5fff"): "\033[38;5;135m",
c("#af8700"): "\033[38;5;136m", c("#af875f"): "\033[38;5;137m", c("#af8787"): "\033[38;5;138m", c("#af87af"): "\033[38;5;139m",
c("#af87d7"): "\033[38;5;140m", c("#af87ff"): "\033[38;5;141m", c("#afaf00"): "\033[38;5;142m", c("#afaf5f"): "\033[38;5;143m",
c("#afaf87"): "\033[38;5;144m", c("#afafaf"): "\033[38;5;145m", c("#afafd7"): "\033[38;5;146m", c("#afafff"): "\033[38;5;147m",
c("#afd700"): "\033[38;5;148m", c("#afd75f"): "\033[38;5;149m", c("#afd787"): "\033[38;5;150m", c("#afd7af"): "\033[38;5;151m",
c("#afd7d7"): "\033[38;5;152m", c("#afd7ff"): "\033[38;5;153m", c("#afff00"): "\033[38;5;154m", c("#afff5f"): "\033[38;5;155m",
c("#afff87"): "\033[38;5;156m", c("#afffaf"): "\033[38;5;157m", c("#afffd7"): "\033[38;5;158m", c("#afffff"): "\033[38;5;159m",
c("#d70000"): "\033[38;5;160m", c("#d7005f"): "\033[38;5;161m", c("#d70087"): "\033[38;5;162m", c("#d700af"): "\033[38;5;163m",
c("#d700d7"): "\033[38;5;164m", c("#d700ff"): "\033[38;5;165m", c("#d75f00"): "\033[38;5;166m", c("#d75f5f"): "\033[38;5;167m",
c("#d75f87"): "\033[38;5;168m", c("#d75faf"): "\033[38;5;169m", c("#d75fd7"): "\033[38;5;170m", c("#d75fff"): "\033[38;5;171m",
c("#d78700"): "\033[38;5;172m", c("#d7875f"): "\033[38;5;173m", c("#d78787"): "\033[38;5;174m", c("#d787af"): "\033[38;5;175m",
c("#d787d7"): "\033[38;5;176m", c("#d787ff"): "\033[38;5;177m", c("#d7af00"): "\033[38;5;178m", c("#d7af5f"): "\033[38;5;179m",
c("#d7af87"): "\033[38;5;180m", c("#d7afaf"): "\033[38;5;181m", c("#d7afd7"): "\033[38;5;182m", c("#d7afff"): "\033[38;5;183m",
c("#d7d700"): "\033[38;5;184m", c("#d7d75f"): "\033[38;5;185m", c("#d7d787"): "\033[38;5;186m", c("#d7d7af"): "\033[38;5;187m",
c("#d7d7d7"): "\033[38;5;188m", c("#d7d7ff"): "\033[38;5;189m", c("#d7ff00"): "\033[38;5;190m", c("#d7ff5f"): "\033[38;5;191m",
c("#d7ff87"): "\033[38;5;192m", c("#d7ffaf"): "\033[38;5;193m", c("#d7ffd7"): "\033[38;5;194m", c("#d7ffff"): "\033[38;5;195m",
c("#ff0000"): "\033[38;5;196m", c("#ff005f"): "\033[38;5;197m", c("#ff0087"): "\033[38;5;198m", c("#ff00af"): "\033[38;5;199m",
c("#ff00d7"): "\033[38;5;200m", c("#ff00ff"): "\033[38;5;201m", c("#ff5f00"): "\033[38;5;202m", c("#ff5f5f"): "\033[38;5;203m",
c("#ff5f87"): "\033[38;5;204m", c("#ff5faf"): "\033[38;5;205m", c("#ff5fd7"): "\033[38;5;206m", c("#ff5fff"): "\033[38;5;207m",
c("#ff8700"): "\033[38;5;208m", c("#ff875f"): "\033[38;5;209m", c("#ff8787"): "\033[38;5;210m", c("#ff87af"): "\033[38;5;211m",
c("#ff87d7"): "\033[38;5;212m", c("#ff87ff"): "\033[38;5;213m", c("#ffaf00"): "\033[38;5;214m", c("#ffaf5f"): "\033[38;5;215m",
c("#ffaf87"): "\033[38;5;216m", c("#ffafaf"): "\033[38;5;217m", c("#ffafd7"): "\033[38;5;218m", c("#ffafff"): "\033[38;5;219m",
c("#ffd700"): "\033[38;5;220m", c("#ffd75f"): "\033[38;5;221m", c("#ffd787"): "\033[38;5;222m", c("#ffd7af"): "\033[38;5;223m",
c("#ffd7d7"): "\033[38;5;224m", c("#ffd7ff"): "\033[38;5;225m", c("#ffff00"): "\033[38;5;226m", c("#ffff5f"): "\033[38;5;227m",
c("#ffff87"): "\033[38;5;228m", c("#ffffaf"): "\033[38;5;229m", c("#ffffd7"): "\033[38;5;230m", c("#ffffff"): "\033[38;5;231m",
c("#080808"): "\033[38;5;232m", c("#121212"): "\033[38;5;233m", c("#1c1c1c"): "\033[38;5;234m", c("#262626"): "\033[38;5;235m",
c("#303030"): "\033[38;5;236m", c("#3a3a3a"): "\033[38;5;237m", c("#444444"): "\033[38;5;238m", c("#4e4e4e"): "\033[38;5;239m",
c("#585858"): "\033[38;5;240m", c("#626262"): "\033[38;5;241m", c("#6c6c6c"): "\033[38;5;242m", c("#767676"): "\033[38;5;243m",
c("#808080"): "\033[38;5;244m", c("#8a8a8a"): "\033[38;5;245m", c("#949494"): "\033[38;5;246m", c("#9e9e9e"): "\033[38;5;247m",
c("#a8a8a8"): "\033[38;5;248m", c("#b2b2b2"): "\033[38;5;249m", c("#bcbcbc"): "\033[38;5;250m", c("#c6c6c6"): "\033[38;5;251m",
c("#d0d0d0"): "\033[38;5;252m", c("#dadada"): "\033[38;5;253m", c("#e4e4e4"): "\033[38;5;254m", c("#eeeeee"): "\033[38;5;255m",
},
background: map[chroma.Colour]string{
c("#000000"): "\033[48;5;0m", c("#800000"): "\033[48;5;1m", c("#008000"): "\033[48;5;2m", c("#808000"): "\033[48;5;3m",
c("#000080"): "\033[48;5;4m", c("#800080"): "\033[48;5;5m", c("#008080"): "\033[48;5;6m", c("#c0c0c0"): "\033[48;5;7m",
c("#808080"): "\033[48;5;8m", c("#ff0000"): "\033[48;5;9m", c("#00ff00"): "\033[48;5;10m", c("#ffff00"): "\033[48;5;11m",
c("#0000ff"): "\033[48;5;12m", c("#ff00ff"): "\033[48;5;13m", c("#00ffff"): "\033[48;5;14m", c("#ffffff"): "\033[48;5;15m",
c("#000000"): "\033[48;5;16m", c("#00005f"): "\033[48;5;17m", c("#000087"): "\033[48;5;18m", c("#0000af"): "\033[48;5;19m",
c("#0000d7"): "\033[48;5;20m", c("#0000ff"): "\033[48;5;21m", c("#005f00"): "\033[48;5;22m", c("#005f5f"): "\033[48;5;23m",
c("#005f87"): "\033[48;5;24m", c("#005faf"): "\033[48;5;25m", c("#005fd7"): "\033[48;5;26m", c("#005fff"): "\033[48;5;27m",
c("#008700"): "\033[48;5;28m", c("#00875f"): "\033[48;5;29m", c("#008787"): "\033[48;5;30m", c("#0087af"): "\033[48;5;31m",
c("#0087d7"): "\033[48;5;32m", c("#0087ff"): "\033[48;5;33m", c("#00af00"): "\033[48;5;34m", c("#00af5f"): "\033[48;5;35m",
c("#00af87"): "\033[48;5;36m", c("#00afaf"): "\033[48;5;37m", c("#00afd7"): "\033[48;5;38m", c("#00afff"): "\033[48;5;39m",
c("#00d700"): "\033[48;5;40m", c("#00d75f"): "\033[48;5;41m", c("#00d787"): "\033[48;5;42m", c("#00d7af"): "\033[48;5;43m",
c("#00d7d7"): "\033[48;5;44m", c("#00d7ff"): "\033[48;5;45m", c("#00ff00"): "\033[48;5;46m", c("#00ff5f"): "\033[48;5;47m",
c("#00ff87"): "\033[48;5;48m", c("#00ffaf"): "\033[48;5;49m", c("#00ffd7"): "\033[48;5;50m", c("#00ffff"): "\033[48;5;51m",
c("#5f0000"): "\033[48;5;52m", c("#5f005f"): "\033[48;5;53m", c("#5f0087"): "\033[48;5;54m", c("#5f00af"): "\033[48;5;55m",
c("#5f00d7"): "\033[48;5;56m", c("#5f00ff"): "\033[48;5;57m", c("#5f5f00"): "\033[48;5;58m", c("#5f5f5f"): "\033[48;5;59m",
c("#5f5f87"): "\033[48;5;60m", c("#5f5faf"): "\033[48;5;61m", c("#5f5fd7"): "\033[48;5;62m", c("#5f5fff"): "\033[48;5;63m",
c("#5f8700"): "\033[48;5;64m", c("#5f875f"): "\033[48;5;65m", c("#5f8787"): "\033[48;5;66m", c("#5f87af"): "\033[48;5;67m",
c("#5f87d7"): "\033[48;5;68m", c("#5f87ff"): "\033[48;5;69m", c("#5faf00"): "\033[48;5;70m", c("#5faf5f"): "\033[48;5;71m",
c("#5faf87"): "\033[48;5;72m", c("#5fafaf"): "\033[48;5;73m", c("#5fafd7"): "\033[48;5;74m", c("#5fafff"): "\033[48;5;75m",
c("#5fd700"): "\033[48;5;76m", c("#5fd75f"): "\033[48;5;77m", c("#5fd787"): "\033[48;5;78m", c("#5fd7af"): "\033[48;5;79m",
c("#5fd7d7"): "\033[48;5;80m", c("#5fd7ff"): "\033[48;5;81m", c("#5fff00"): "\033[48;5;82m", c("#5fff5f"): "\033[48;5;83m",
c("#5fff87"): "\033[48;5;84m", c("#5fffaf"): "\033[48;5;85m", c("#5fffd7"): "\033[48;5;86m", c("#5fffff"): "\033[48;5;87m",
c("#870000"): "\033[48;5;88m", c("#87005f"): "\033[48;5;89m", c("#870087"): "\033[48;5;90m", c("#8700af"): "\033[48;5;91m",
c("#8700d7"): "\033[48;5;92m", c("#8700ff"): "\033[48;5;93m", c("#875f00"): "\033[48;5;94m", c("#875f5f"): "\033[48;5;95m",
c("#875f87"): "\033[48;5;96m", c("#875faf"): "\033[48;5;97m", c("#875fd7"): "\033[48;5;98m", c("#875fff"): "\033[48;5;99m",
c("#878700"): "\033[48;5;100m", c("#87875f"): "\033[48;5;101m", c("#878787"): "\033[48;5;102m", c("#8787af"): "\033[48;5;103m",
c("#8787d7"): "\033[48;5;104m", c("#8787ff"): "\033[48;5;105m", c("#87af00"): "\033[48;5;106m", c("#87af5f"): "\033[48;5;107m",
c("#87af87"): "\033[48;5;108m", c("#87afaf"): "\033[48;5;109m", c("#87afd7"): "\033[48;5;110m", c("#87afff"): "\033[48;5;111m",
c("#87d700"): "\033[48;5;112m", c("#87d75f"): "\033[48;5;113m", c("#87d787"): "\033[48;5;114m", c("#87d7af"): "\033[48;5;115m",
c("#87d7d7"): "\033[48;5;116m", c("#87d7ff"): "\033[48;5;117m", c("#87ff00"): "\033[48;5;118m", c("#87ff5f"): "\033[48;5;119m",
c("#87ff87"): "\033[48;5;120m", c("#87ffaf"): "\033[48;5;121m", c("#87ffd7"): "\033[48;5;122m", c("#87ffff"): "\033[48;5;123m",
c("#af0000"): "\033[48;5;124m", c("#af005f"): "\033[48;5;125m", c("#af0087"): "\033[48;5;126m", c("#af00af"): "\033[48;5;127m",
c("#af00d7"): "\033[48;5;128m", c("#af00ff"): "\033[48;5;129m", c("#af5f00"): "\033[48;5;130m", c("#af5f5f"): "\033[48;5;131m",
c("#af5f87"): "\033[48;5;132m", c("#af5faf"): "\033[48;5;133m", c("#af5fd7"): "\033[48;5;134m", c("#af5fff"): "\033[48;5;135m",
c("#af8700"): "\033[48;5;136m", c("#af875f"): "\033[48;5;137m", c("#af8787"): "\033[48;5;138m", c("#af87af"): "\033[48;5;139m",
c("#af87d7"): "\033[48;5;140m", c("#af87ff"): "\033[48;5;141m", c("#afaf00"): "\033[48;5;142m", c("#afaf5f"): "\033[48;5;143m",
c("#afaf87"): "\033[48;5;144m", c("#afafaf"): "\033[48;5;145m", c("#afafd7"): "\033[48;5;146m", c("#afafff"): "\033[48;5;147m",
c("#afd700"): "\033[48;5;148m", c("#afd75f"): "\033[48;5;149m", c("#afd787"): "\033[48;5;150m", c("#afd7af"): "\033[48;5;151m",
c("#afd7d7"): "\033[48;5;152m", c("#afd7ff"): "\033[48;5;153m", c("#afff00"): "\033[48;5;154m", c("#afff5f"): "\033[48;5;155m",
c("#afff87"): "\033[48;5;156m", c("#afffaf"): "\033[48;5;157m", c("#afffd7"): "\033[48;5;158m", c("#afffff"): "\033[48;5;159m",
c("#d70000"): "\033[48;5;160m", c("#d7005f"): "\033[48;5;161m", c("#d70087"): "\033[48;5;162m", c("#d700af"): "\033[48;5;163m",
c("#d700d7"): "\033[48;5;164m", c("#d700ff"): "\033[48;5;165m", c("#d75f00"): "\033[48;5;166m", c("#d75f5f"): "\033[48;5;167m",
c("#d75f87"): "\033[48;5;168m", c("#d75faf"): "\033[48;5;169m", c("#d75fd7"): "\033[48;5;170m", c("#d75fff"): "\033[48;5;171m",
c("#d78700"): "\033[48;5;172m", c("#d7875f"): "\033[48;5;173m", c("#d78787"): "\033[48;5;174m", c("#d787af"): "\033[48;5;175m",
c("#d787d7"): "\033[48;5;176m", c("#d787ff"): "\033[48;5;177m", c("#d7af00"): "\033[48;5;178m", c("#d7af5f"): "\033[48;5;179m",
c("#d7af87"): "\033[48;5;180m", c("#d7afaf"): "\033[48;5;181m", c("#d7afd7"): "\033[48;5;182m", c("#d7afff"): "\033[48;5;183m",
c("#d7d700"): "\033[48;5;184m", c("#d7d75f"): "\033[48;5;185m", c("#d7d787"): "\033[48;5;186m", c("#d7d7af"): "\033[48;5;187m",
c("#d7d7d7"): "\033[48;5;188m", c("#d7d7ff"): "\033[48;5;189m", c("#d7ff00"): "\033[48;5;190m", c("#d7ff5f"): "\033[48;5;191m",
c("#d7ff87"): "\033[48;5;192m", c("#d7ffaf"): "\033[48;5;193m", c("#d7ffd7"): "\033[48;5;194m", c("#d7ffff"): "\033[48;5;195m",
c("#ff0000"): "\033[48;5;196m", c("#ff005f"): "\033[48;5;197m", c("#ff0087"): "\033[48;5;198m", c("#ff00af"): "\033[48;5;199m",
c("#ff00d7"): "\033[48;5;200m", c("#ff00ff"): "\033[48;5;201m", c("#ff5f00"): "\033[48;5;202m", c("#ff5f5f"): "\033[48;5;203m",
c("#ff5f87"): "\033[48;5;204m", c("#ff5faf"): "\033[48;5;205m", c("#ff5fd7"): "\033[48;5;206m", c("#ff5fff"): "\033[48;5;207m",
c("#ff8700"): "\033[48;5;208m", c("#ff875f"): "\033[48;5;209m", c("#ff8787"): "\033[48;5;210m", c("#ff87af"): "\033[48;5;211m",
c("#ff87d7"): "\033[48;5;212m", c("#ff87ff"): "\033[48;5;213m", c("#ffaf00"): "\033[48;5;214m", c("#ffaf5f"): "\033[48;5;215m",
c("#ffaf87"): "\033[48;5;216m", c("#ffafaf"): "\033[48;5;217m", c("#ffafd7"): "\033[48;5;218m", c("#ffafff"): "\033[48;5;219m",
c("#ffd700"): "\033[48;5;220m", c("#ffd75f"): "\033[48;5;221m", c("#ffd787"): "\033[48;5;222m", c("#ffd7af"): "\033[48;5;223m",
c("#ffd7d7"): "\033[48;5;224m", c("#ffd7ff"): "\033[48;5;225m", c("#ffff00"): "\033[48;5;226m", c("#ffff5f"): "\033[48;5;227m",
c("#ffff87"): "\033[48;5;228m", c("#ffffaf"): "\033[48;5;229m", c("#ffffd7"): "\033[48;5;230m", c("#ffffff"): "\033[48;5;231m",
c("#080808"): "\033[48;5;232m", c("#121212"): "\033[48;5;233m", c("#1c1c1c"): "\033[48;5;234m", c("#262626"): "\033[48;5;235m",
c("#303030"): "\033[48;5;236m", c("#3a3a3a"): "\033[48;5;237m", c("#444444"): "\033[48;5;238m", c("#4e4e4e"): "\033[48;5;239m",
c("#585858"): "\033[48;5;240m", c("#626262"): "\033[48;5;241m", c("#6c6c6c"): "\033[48;5;242m", c("#767676"): "\033[48;5;243m",
c("#808080"): "\033[48;5;244m", c("#8a8a8a"): "\033[48;5;245m", c("#949494"): "\033[48;5;246m", c("#9e9e9e"): "\033[48;5;247m",
c("#a8a8a8"): "\033[48;5;248m", c("#b2b2b2"): "\033[48;5;249m", c("#bcbcbc"): "\033[48;5;250m", c("#c6c6c6"): "\033[48;5;251m",
c("#d0d0d0"): "\033[48;5;252m", c("#dadada"): "\033[48;5;253m", c("#e4e4e4"): "\033[48;5;254m", c("#eeeeee"): "\033[48;5;255m",
},
},
}
func entryToEscapeSequence(table *ttyTable, entry chroma.StyleEntry) string {
out := ""
if entry.Bold == chroma.Yes {
out += "\033[1m"
}
if entry.Underline == chroma.Yes {
out += "\033[4m"
}
if entry.Colour.IsSet() {
out += table.foreground[findClosest(table, entry.Colour)]
}
if entry.Background.IsSet() {
out += table.background[findClosest(table, entry.Background)]
}
return out
}
func findClosest(table *ttyTable, seeking chroma.Colour) chroma.Colour {
closestColour := chroma.Colour(0)
closest := float64(math.MaxFloat64)
for colour := range table.foreground {
distance := colour.Distance(seeking)
if distance < closest {
closest = distance
closestColour = colour
}
}
return closestColour
}
func styleToEscapeSequence(table *ttyTable, style *chroma.Style) map[chroma.TokenType]string {
out := map[chroma.TokenType]string{}
for _, ttype := range style.Types() {
entry := style.Get(ttype)
out[ttype] = entryToEscapeSequence(table, entry)
}
return out
}
type indexedTTYFormatter struct {
table *ttyTable
}
func (c *indexedTTYFormatter) Format(w io.Writer, style *chroma.Style, it chroma.Iterator) (err error) {
defer func() {
if perr := recover(); perr != nil {
err = perr.(error)
}
}()
theme := styleToEscapeSequence(c.table, style)
for token := it(); token != chroma.EOF; token = it() {
// TODO: Cache token lookups?
clr, ok := theme[token.Type]
if !ok {
clr, ok = theme[token.Type.SubCategory()]
if !ok {
clr = theme[token.Type.Category()]
// if !ok {
// clr = theme[chroma.InheritStyle]
// }
}
}
if clr != "" {
fmt.Fprint(w, clr)
}
fmt.Fprint(w, token.Value)
if clr != "" {
fmt.Fprintf(w, "\033[0m")
}
}
return nil
}
// TTY8 is an 8-colour terminal formatter.
//
// The Lab colour space is used to map RGB values to the most appropriate index colour.
var TTY8 = Register("terminal", &indexedTTYFormatter{ttyTables[8]})
// TTY256 is a 256-colour terminal formatter.
//
// The Lab colour space is used to map RGB values to the most appropriate index colour.
var TTY256 = Register("terminal256", &indexedTTYFormatter{ttyTables[256]})

View File

@ -1,38 +0,0 @@
package formatters
import (
"fmt"
"io"
"github.com/alecthomas/chroma"
)
// TTY16m is a true-colour terminal formatter.
var TTY16m = Register("terminal16m", chroma.FormatterFunc(trueColourFormatter))
func trueColourFormatter(w io.Writer, style *chroma.Style, it chroma.Iterator) error {
for token := it(); token != chroma.EOF; token = it() {
entry := style.Get(token.Type)
if !entry.IsZero() {
out := ""
if entry.Bold == chroma.Yes {
out += "\033[1m"
}
if entry.Underline == chroma.Yes {
out += "\033[4m"
}
if entry.Colour.IsSet() {
out += fmt.Sprintf("\033[38;2;%d;%d;%dm", entry.Colour.Red(), entry.Colour.Green(), entry.Colour.Blue())
}
if entry.Background.IsSet() {
out += fmt.Sprintf("\033[48;2;%d;%d;%dm", entry.Background.Red(), entry.Background.Green(), entry.Background.Blue())
}
fmt.Fprint(w, out)
}
fmt.Fprint(w, token.Value)
if !entry.IsZero() {
fmt.Fprint(w, "\033[0m")
}
}
return nil
}

View File

@ -1,14 +0,0 @@
module github.com/alecthomas/chroma
require (
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38
github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721 // indirect
github.com/alecthomas/kong v0.1.15
github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897 // indirect
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964
github.com/dlclark/regexp2 v1.1.6
github.com/mattn/go-colorable v0.0.9
github.com/mattn/go-isatty v0.0.4
github.com/sergi/go-diff v1.0.0 // indirect
golang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35 // indirect
)

View File

@ -1,26 +0,0 @@
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 h1:smF2tmSOzy2Mm+0dGI2AIUHY+w0BUc+4tn40djz7+6U=
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI=
github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721 h1:JHZL0hZKJ1VENNfmXvHbgYlbUOvpzYzvy2aZU5gXVeo=
github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0=
github.com/alecthomas/kong v0.1.15 h1:IWBg+KrLvoHBicD50OzMI8fKjrtAa1okMR9g38HVM/s=
github.com/alecthomas/kong v0.1.15/go.mod h1:0m2VYms8rH0qbCqVB2gvGHk74bqLIq0HXjCs5bNbNQU=
github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897 h1:p9Sln00KOTlrYkxI1zYWl1QLnEqAqEARBEYa8FQnQcY=
github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ=
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ=
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk=
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/dlclark/regexp2 v1.1.6 h1:CqB4MjHw0MFCDj+PHHjiESmHX+N7t0tJzKvC6M97BRg=
github.com/dlclark/regexp2 v1.1.6/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
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/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
golang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35 h1:YAFjXN64LMvktoUZH9zgY4lGc/msGN7HQfoSuKCgaDU=
golang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=

View File

@ -1,75 +0,0 @@
package chroma
import "strings"
// An Iterator across tokens.
//
// nil will be returned at the end of the Token stream.
//
// If an error occurs within an Iterator, it may propagate this in a panic. Formatters should recover.
type Iterator func() Token
// Tokens consumes all tokens from the iterator and returns them as a slice.
func (i Iterator) Tokens() []Token {
var out []Token
for t := i(); t != EOF; t = i() {
out = append(out, t)
}
return out
}
// Concaterator concatenates tokens from a series of iterators.
func Concaterator(iterators ...Iterator) Iterator {
return func() Token {
for len(iterators) > 0 {
t := iterators[0]()
if t != EOF {
return t
}
iterators = iterators[1:]
}
return EOF
}
}
// Literator converts a sequence of literal Tokens into an Iterator.
func Literator(tokens ...Token) Iterator {
return func() Token {
if len(tokens) == 0 {
return EOF
}
token := tokens[0]
tokens = tokens[1:]
return token
}
}
func SplitTokensIntoLines(tokens []Token) (out [][]Token) {
var line []Token
for _, token := range tokens {
for strings.Contains(token.Value, "\n") {
parts := strings.SplitAfterN(token.Value, "\n", 2)
// Token becomes the tail.
token.Value = parts[1]
// Append the head to the line and flush the line.
clone := token.Clone()
clone.Value = parts[0]
line = append(line, clone)
out = append(out, line)
line = nil
}
line = append(line, token)
}
if len(line) > 0 {
out = append(out, line)
}
// Strip empty trailing token line.
if len(out) > 0 {
last := out[len(out)-1]
if len(last) == 1 && last[0].Value == "" {
out = out[:len(out)-1]
}
}
return
}

View File

@ -1,117 +0,0 @@
package chroma
import (
"fmt"
)
var (
defaultOptions = &TokeniseOptions{
State: "root",
}
)
// Config for a lexer.
type Config struct {
// Name of the lexer.
Name string
// Shortcuts for the lexer
Aliases []string
// File name globs
Filenames []string
// Secondary file name globs
AliasFilenames []string
// MIME types
MimeTypes []string
// Regex matching is case-insensitive.
CaseInsensitive bool
// Regex matches all characters.
DotAll bool
// Regex does not match across lines ($ matches EOL).
//
// Defaults to multiline.
NotMultiline bool
// Don't strip leading and trailing newlines from the input.
// DontStripNL bool
// Strip all leading and trailing whitespace from the input
// StripAll bool
// Make sure that the input ends with a newline. This
// is required for some lexers that consume input linewise.
EnsureNL bool
// If given and greater than 0, expand tabs in the input.
// TabSize int
// Priority of lexer.
//
// If this is 0 it will be treated as a default of 1.
Priority float32
}
// Token output to formatter.
type Token struct {
Type TokenType `json:"type"`
Value string `json:"value"`
}
func (t *Token) String() string { return t.Value }
func (t *Token) GoString() string { return fmt.Sprintf("&Token{%s, %q}", t.Type, t.Value) }
func (t *Token) Clone() Token {
return *t
}
var EOF Token
type TokeniseOptions struct {
// State to start tokenisation in. Defaults to "root".
State string
// Nested tokenisation.
Nested bool
}
// A Lexer for tokenising source code.
type Lexer interface {
// Config describing the features of the Lexer.
Config() *Config
// Tokenise returns an Iterator over tokens in text.
Tokenise(options *TokeniseOptions, text string) (Iterator, error)
}
// Lexers is a slice of lexers sortable by name.
type Lexers []Lexer
func (l Lexers) Len() int { return len(l) }
func (l Lexers) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
func (l Lexers) Less(i, j int) bool { return l[i].Config().Name < l[j].Config().Name }
// PrioritisedLexers is a slice of lexers sortable by priority.
type PrioritisedLexers []Lexer
func (l PrioritisedLexers) Len() int { return len(l) }
func (l PrioritisedLexers) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
func (l PrioritisedLexers) Less(i, j int) bool {
ip := l[i].Config().Priority
if ip == 0 {
ip = 1
}
jp := l[j].Config().Priority
if jp == 0 {
jp = 1
}
return ip > jp
}
// Analyser determines how appropriate this lexer is for the given text.
type Analyser interface {
AnalyseText(text string) float32
}

View File

@ -1,38 +0,0 @@
package a
import (
. "github.com/alecthomas/chroma" // nolint
"github.com/alecthomas/chroma/lexers/internal"
)
// Abnf lexer.
var Abnf = internal.Register(MustNewLexer(
&Config{
Name: "ABNF",
Aliases: []string{"abnf"},
Filenames: []string{"*.abnf"},
MimeTypes: []string{"text/x-abnf"},
},
Rules{
"root": {
{`;.*$`, CommentSingle, nil},
{`(%[si])?"[^"]*"`, Literal, nil},
{`%b[01]+\-[01]+\b`, Literal, nil},
{`%b[01]+(\.[01]+)*\b`, Literal, nil},
{`%d[0-9]+\-[0-9]+\b`, Literal, nil},
{`%d[0-9]+(\.[0-9]+)*\b`, Literal, nil},
{`%x[0-9a-fA-F]+\-[0-9a-fA-F]+\b`, Literal, nil},
{`%x[0-9a-fA-F]+(\.[0-9a-fA-F]+)*\b`, Literal, nil},
{`\b[0-9]+\*[0-9]+`, Operator, nil},
{`\b[0-9]+\*`, Operator, nil},
{`\b[0-9]+`, Operator, nil},
{`\*`, Operator, nil},
{Words(``, `\b`, `ALPHA`, `BIT`, `CHAR`, `CR`, `CRLF`, `CTL`, `DIGIT`, `DQUOTE`, `HEXDIG`, `HTAB`, `LF`, `LWSP`, `OCTET`, `SP`, `VCHAR`, `WSP`), Keyword, nil},
{`[a-zA-Z][a-zA-Z0-9-]+\b`, NameClass, nil},
{`(=/|=|/)`, Operator, nil},
{`[\[\]()]`, Punctuation, nil},
{`\s+`, Text, nil},
{`.`, Text, nil},
},
},
))

View File

@ -1,39 +0,0 @@
package a
import (
. "github.com/alecthomas/chroma" // nolint
"github.com/alecthomas/chroma/lexers/internal"
)
// Actionscript lexer.
var Actionscript = internal.Register(MustNewLexer(
&Config{
Name: "ActionScript",
Aliases: []string{"as", "actionscript"},
Filenames: []string{"*.as"},
MimeTypes: []string{"application/x-actionscript", "text/x-actionscript", "text/actionscript"},
NotMultiline: true,
DotAll: true,
},
Rules{
"root": {
{`\s+`, Text, nil},
{`//.*?\n`, CommentSingle, nil},
{`/\*.*?\*/`, CommentMultiline, nil},
{`/(\\\\|\\/|[^/\n])*/[gim]*`, LiteralStringRegex, nil},
{`[~^*!%&<>|+=:;,/?\\-]+`, Operator, nil},
{`[{}\[\]();.]+`, Punctuation, nil},
{Words(``, `\b`, `case`, `default`, `for`, `each`, `in`, `while`, `do`, `break`, `return`, `continue`, `if`, `else`, `throw`, `try`, `catch`, `var`, `with`, `new`, `typeof`, `arguments`, `instanceof`, `this`, `switch`), Keyword, nil},
{Words(``, `\b`, `class`, `public`, `final`, `internal`, `native`, `override`, `private`, `protected`, `static`, `import`, `extends`, `implements`, `interface`, `intrinsic`, `return`, `super`, `dynamic`, `function`, `const`, `get`, `namespace`, `package`, `set`), KeywordDeclaration, nil},
{`(true|false|null|NaN|Infinity|-Infinity|undefined|Void)\b`, KeywordConstant, nil},
{Words(``, `\b`, `Accessibility`, `AccessibilityProperties`, `ActionScriptVersion`, `ActivityEvent`, `AntiAliasType`, `ApplicationDomain`, `AsBroadcaster`, `Array`, `AsyncErrorEvent`, `AVM1Movie`, `BevelFilter`, `Bitmap`, `BitmapData`, `BitmapDataChannel`, `BitmapFilter`, `BitmapFilterQuality`, `BitmapFilterType`, `BlendMode`, `BlurFilter`, `Boolean`, `ByteArray`, `Camera`, `Capabilities`, `CapsStyle`, `Class`, `Color`, `ColorMatrixFilter`, `ColorTransform`, `ContextMenu`, `ContextMenuBuiltInItems`, `ContextMenuEvent`, `ContextMenuItem`, `ConvultionFilter`, `CSMSettings`, `DataEvent`, `Date`, `DefinitionError`, `DeleteObjectSample`, `Dictionary`, `DisplacmentMapFilter`, `DisplayObject`, `DisplacmentMapFilterMode`, `DisplayObjectContainer`, `DropShadowFilter`, `Endian`, `EOFError`, `Error`, `ErrorEvent`, `EvalError`, `Event`, `EventDispatcher`, `EventPhase`, `ExternalInterface`, `FileFilter`, `FileReference`, `FileReferenceList`, `FocusDirection`, `FocusEvent`, `Font`, `FontStyle`, `FontType`, `FrameLabel`, `FullScreenEvent`, `Function`, `GlowFilter`, `GradientBevelFilter`, `GradientGlowFilter`, `GradientType`, `Graphics`, `GridFitType`, `HTTPStatusEvent`, `IBitmapDrawable`, `ID3Info`, `IDataInput`, `IDataOutput`, `IDynamicPropertyOutputIDynamicPropertyWriter`, `IEventDispatcher`, `IExternalizable`, `IllegalOperationError`, `IME`, `IMEConversionMode`, `IMEEvent`, `int`, `InteractiveObject`, `InterpolationMethod`, `InvalidSWFError`, `InvokeEvent`, `IOError`, `IOErrorEvent`, `JointStyle`, `Key`, `Keyboard`, `KeyboardEvent`, `KeyLocation`, `LineScaleMode`, `Loader`, `LoaderContext`, `LoaderInfo`, `LoadVars`, `LocalConnection`, `Locale`, `Math`, `Matrix`, `MemoryError`, `Microphone`, `MorphShape`, `Mouse`, `MouseEvent`, `MovieClip`, `MovieClipLoader`, `Namespace`, `NetConnection`, `NetStatusEvent`, `NetStream`, `NewObjectSample`, `Number`, `Object`, `ObjectEncoding`, `PixelSnapping`, `Point`, `PrintJob`, `PrintJobOptions`, `PrintJobOrientation`, `ProgressEvent`, `Proxy`, `QName`, `RangeError`, `Rectangle`, `ReferenceError`, `RegExp`, `Responder`, `Sample`, `Scene`, `ScriptTimeoutError`, `Security`, `SecurityDomain`, `SecurityError`, `SecurityErrorEvent`, `SecurityPanel`, `Selection`, `Shape`, `SharedObject`, `SharedObjectFlushStatus`, `SimpleButton`, `Socket`, `Sound`, `SoundChannel`, `SoundLoaderContext`, `SoundMixer`, `SoundTransform`, `SpreadMethod`, `Sprite`, `StackFrame`, `StackOverflowError`, `Stage`, `StageAlign`, `StageDisplayState`, `StageQuality`, `StageScaleMode`, `StaticText`, `StatusEvent`, `String`, `StyleSheet`, `SWFVersion`, `SyncEvent`, `SyntaxError`, `System`, `TextColorType`, `TextField`, `TextFieldAutoSize`, `TextFieldType`, `TextFormat`, `TextFormatAlign`, `TextLineMetrics`, `TextRenderer`, `TextSnapshot`, `Timer`, `TimerEvent`, `Transform`, `TypeError`, `uint`, `URIError`, `URLLoader`, `URLLoaderDataFormat`, `URLRequest`, `URLRequestHeader`, `URLRequestMethod`, `URLStream`, `URLVariabeles`, `VerifyError`, `Video`, `XML`, `XMLDocument`, `XMLList`, `XMLNode`, `XMLNodeType`, `XMLSocket`, `XMLUI`), NameBuiltin, nil},
{Words(``, `\b`, `decodeURI`, `decodeURIComponent`, `encodeURI`, `escape`, `eval`, `isFinite`, `isNaN`, `isXMLName`, `clearInterval`, `fscommand`, `getTimer`, `getURL`, `getVersion`, `parseFloat`, `parseInt`, `setInterval`, `trace`, `updateAfterEvent`, `unescape`), NameFunction, nil},
{`[$a-zA-Z_]\w*`, NameOther, nil},
{`[0-9][0-9]*\.[0-9]+([eE][0-9]+)?[fd]?`, LiteralNumberFloat, nil},
{`0x[0-9a-f]+`, LiteralNumberHex, nil},
{`[0-9]+`, LiteralNumberInteger, nil},
{`"(\\\\|\\"|[^"])*"`, LiteralStringDouble, nil},
{`'(\\\\|\\'|[^'])*'`, LiteralStringSingle, nil},
},
},
))

View File

@ -1,56 +0,0 @@
package a
import (
. "github.com/alecthomas/chroma" // nolint
"github.com/alecthomas/chroma/lexers/internal"
)
// Actionscript 3 lexer.
var Actionscript3 = internal.Register(MustNewLexer(
&Config{
Name: "ActionScript 3",
Aliases: []string{"as3", "actionscript3"},
Filenames: []string{"*.as"},
MimeTypes: []string{"application/x-actionscript3", "text/x-actionscript3", "text/actionscript3"},
DotAll: true,
},
Rules{
"root": {
{`\s+`, Text, nil},
{`(function\s+)([$a-zA-Z_]\w*)(\s*)(\()`, ByGroups(KeywordDeclaration, NameFunction, Text, Operator), Push("funcparams")},
{`(var|const)(\s+)([$a-zA-Z_]\w*)(\s*)(:)(\s*)([$a-zA-Z_]\w*(?:\.<\w+>)?)`, ByGroups(KeywordDeclaration, Text, Name, Text, Punctuation, Text, KeywordType), nil},
{`(import|package)(\s+)((?:[$a-zA-Z_]\w*|\.)+)(\s*)`, ByGroups(Keyword, Text, NameNamespace, Text), nil},
{`(new)(\s+)([$a-zA-Z_]\w*(?:\.<\w+>)?)(\s*)(\()`, ByGroups(Keyword, Text, KeywordType, Text, Operator), nil},
{`//.*?\n`, CommentSingle, nil},
{`/\*.*?\*/`, CommentMultiline, nil},
{`/(\\\\|\\/|[^\n])*/[gisx]*`, LiteralStringRegex, nil},
{`(\.)([$a-zA-Z_]\w*)`, ByGroups(Operator, NameAttribute), nil},
{`(case|default|for|each|in|while|do|break|return|continue|if|else|throw|try|catch|with|new|typeof|arguments|instanceof|this|switch|import|include|as|is)\b`, Keyword, nil},
{`(class|public|final|internal|native|override|private|protected|static|import|extends|implements|interface|intrinsic|return|super|dynamic|function|const|get|namespace|package|set)\b`, KeywordDeclaration, nil},
{`(true|false|null|NaN|Infinity|-Infinity|undefined|void)\b`, KeywordConstant, nil},
{`(decodeURI|decodeURIComponent|encodeURI|escape|eval|isFinite|isNaN|isXMLName|clearInterval|fscommand|getTimer|getURL|getVersion|isFinite|parseFloat|parseInt|setInterval|trace|updateAfterEvent|unescape)\b`, NameFunction, nil},
{`[$a-zA-Z_]\w*`, Name, nil},
{`[0-9][0-9]*\.[0-9]+([eE][0-9]+)?[fd]?`, LiteralNumberFloat, nil},
{`0x[0-9a-f]+`, LiteralNumberHex, nil},
{`[0-9]+`, LiteralNumberInteger, nil},
{`"(\\\\|\\"|[^"])*"`, LiteralStringDouble, nil},
{`'(\\\\|\\'|[^'])*'`, LiteralStringSingle, nil},
{`[~^*!%&<>|+=:;,/?\\{}\[\]().-]+`, Operator, nil},
},
"funcparams": {
{`\s+`, Text, nil},
{`(\s*)(\.\.\.)?([$a-zA-Z_]\w*)(\s*)(:)(\s*)([$a-zA-Z_]\w*(?:\.<\w+>)?|\*)(\s*)`, ByGroups(Text, Punctuation, Name, Text, Operator, Text, KeywordType, Text), Push("defval")},
{`\)`, Operator, Push("type")},
},
"type": {
{`(\s*)(:)(\s*)([$a-zA-Z_]\w*(?:\.<\w+>)?|\*)`, ByGroups(Text, Operator, Text, KeywordType), Pop(2)},
{`\s+`, Text, Pop(2)},
Default(Pop(2)),
},
"defval": {
{`(=)(\s*)([^(),]+)(\s*)(,?)`, ByGroups(Operator, Text, UsingSelf("root"), Text, Operator), Pop(1)},
{`,`, Operator, Pop(1)},
Default(Pop(1)),
},
},
))

View File

@ -1,114 +0,0 @@
package a
import (
. "github.com/alecthomas/chroma" // nolint
"github.com/alecthomas/chroma/lexers/internal"
)
// Ada lexer.
var Ada = internal.Register(MustNewLexer(
&Config{
Name: "Ada",
Aliases: []string{"ada", "ada95", "ada2005"},
Filenames: []string{"*.adb", "*.ads", "*.ada"},
MimeTypes: []string{"text/x-ada"},
CaseInsensitive: true,
},
Rules{
"root": {
{`[^\S\n]+`, Text, nil},
{`--.*?\n`, CommentSingle, nil},
{`[^\S\n]+`, Text, nil},
{`function|procedure|entry`, KeywordDeclaration, Push("subprogram")},
{`(subtype|type)(\s+)(\w+)`, ByGroups(KeywordDeclaration, Text, KeywordType), Push("type_def")},
{`task|protected`, KeywordDeclaration, nil},
{`(subtype)(\s+)`, ByGroups(KeywordDeclaration, Text), nil},
{`(end)(\s+)`, ByGroups(KeywordReserved, Text), Push("end")},
{`(pragma)(\s+)(\w+)`, ByGroups(KeywordReserved, Text, CommentPreproc), nil},
{`(true|false|null)\b`, KeywordConstant, nil},
{Words(``, `\b`, `Address`, `Byte`, `Boolean`, `Character`, `Controlled`, `Count`, `Cursor`, `Duration`, `File_Mode`, `File_Type`, `Float`, `Generator`, `Integer`, `Long_Float`, `Long_Integer`, `Long_Long_Float`, `Long_Long_Integer`, `Natural`, `Positive`, `Reference_Type`, `Short_Float`, `Short_Integer`, `Short_Short_Float`, `Short_Short_Integer`, `String`, `Wide_Character`, `Wide_String`), KeywordType, nil},
{`(and(\s+then)?|in|mod|not|or(\s+else)|rem)\b`, OperatorWord, nil},
{`generic|private`, KeywordDeclaration, nil},
{`package`, KeywordDeclaration, Push("package")},
{`array\b`, KeywordReserved, Push("array_def")},
{`(with|use)(\s+)`, ByGroups(KeywordNamespace, Text), Push("import")},
{`(\w+)(\s*)(:)(\s*)(constant)`, ByGroups(NameConstant, Text, Punctuation, Text, KeywordReserved), nil},
{`<<\w+>>`, NameLabel, nil},
{`(\w+)(\s*)(:)(\s*)(declare|begin|loop|for|while)`, ByGroups(NameLabel, Text, Punctuation, Text, KeywordReserved), nil},
{Words(`\b`, `\b`, `abort`, `abs`, `abstract`, `accept`, `access`, `aliased`, `all`, `array`, `at`, `begin`, `body`, `case`, `constant`, `declare`, `delay`, `delta`, `digits`, `do`, `else`, `elsif`, `end`, `entry`, `exception`, `exit`, `interface`, `for`, `goto`, `if`, `is`, `limited`, `loop`, `new`, `null`, `of`, `or`, `others`, `out`, `overriding`, `pragma`, `protected`, `raise`, `range`, `record`, `renames`, `requeue`, `return`, `reverse`, `select`, `separate`, `subtype`, `synchronized`, `task`, `tagged`, `terminate`, `then`, `type`, `until`, `when`, `while`, `xor`), KeywordReserved, nil},
{`"[^"]*"`, LiteralString, nil},
Include("attribute"),
Include("numbers"),
{`'[^']'`, LiteralStringChar, nil},
{`(\w+)(\s*|[(,])`, ByGroups(Name, UsingSelf("root")), nil},
{`(<>|=>|:=|[()|:;,.'])`, Punctuation, nil},
{`[*<>+=/&-]`, Operator, nil},
{`\n+`, Text, nil},
},
"numbers": {
{`[0-9_]+#[0-9a-f]+#`, LiteralNumberHex, nil},
{`[0-9_]+\.[0-9_]*`, LiteralNumberFloat, nil},
{`[0-9_]+`, LiteralNumberInteger, nil},
},
"attribute": {
{`(')(\w+)`, ByGroups(Punctuation, NameAttribute), nil},
},
"subprogram": {
{`\(`, Punctuation, Push("#pop", "formal_part")},
{`;`, Punctuation, Pop(1)},
{`is\b`, KeywordReserved, Pop(1)},
{`"[^"]+"|\w+`, NameFunction, nil},
Include("root"),
},
"end": {
{`(if|case|record|loop|select)`, KeywordReserved, nil},
{`"[^"]+"|[\w.]+`, NameFunction, nil},
{`\s+`, Text, nil},
{`;`, Punctuation, Pop(1)},
},
"type_def": {
{`;`, Punctuation, Pop(1)},
{`\(`, Punctuation, Push("formal_part")},
{`with|and|use`, KeywordReserved, nil},
{`array\b`, KeywordReserved, Push("#pop", "array_def")},
{`record\b`, KeywordReserved, Push("record_def")},
{`(null record)(;)`, ByGroups(KeywordReserved, Punctuation), Pop(1)},
Include("root"),
},
"array_def": {
{`;`, Punctuation, Pop(1)},
{`(\w+)(\s+)(range)`, ByGroups(KeywordType, Text, KeywordReserved), nil},
Include("root"),
},
"record_def": {
{`end record`, KeywordReserved, Pop(1)},
Include("root"),
},
"import": {
{`[\w.]+`, NameNamespace, Pop(1)},
Default(Pop(1)),
},
"formal_part": {
{`\)`, Punctuation, Pop(1)},
{`\w+`, NameVariable, nil},
{`,|:[^=]`, Punctuation, nil},
{`(in|not|null|out|access)\b`, KeywordReserved, nil},
Include("root"),
},
"package": {
{`body`, KeywordDeclaration, nil},
{`is\s+new|renames`, KeywordReserved, nil},
{`is`, KeywordReserved, Pop(1)},
{`;`, Punctuation, Pop(1)},
{`\(`, Punctuation, Push("package_instantiation")},
{`([\w.]+)`, NameClass, nil},
Include("root"),
},
"package_instantiation": {
{`("[^"]+"|\w+)(\s+)(=>)`, ByGroups(NameVariable, Text, Punctuation), nil},
{`[\w.\'"]`, Text, nil},
{`\)`, Punctuation, Pop(1)},
Include("root"),
},
},
))

View File

@ -1,42 +0,0 @@
package a
import (
. "github.com/alecthomas/chroma" // nolint
"github.com/alecthomas/chroma/lexers/internal"
)
// Angular2 lexer.
var Angular2 = internal.Register(MustNewLexer(
&Config{
Name: "Angular2",
Aliases: []string{"ng2"},
Filenames: []string{},
MimeTypes: []string{},
},
Rules{
"root": {
{`[^{([*#]+`, Other, nil},
{`(\{\{)(\s*)`, ByGroups(CommentPreproc, Text), Push("ngExpression")},
{`([([]+)([\w:.-]+)([\])]+)(\s*)(=)(\s*)`, ByGroups(Punctuation, NameAttribute, Punctuation, Text, Operator, Text), Push("attr")},
{`([([]+)([\w:.-]+)([\])]+)(\s*)`, ByGroups(Punctuation, NameAttribute, Punctuation, Text), nil},
{`([*#])([\w:.-]+)(\s*)(=)(\s*)`, ByGroups(Punctuation, NameAttribute, Punctuation, Operator), Push("attr")},
{`([*#])([\w:.-]+)(\s*)`, ByGroups(Punctuation, NameAttribute, Punctuation), nil},
},
"ngExpression": {
{`\s+(\|\s+)?`, Text, nil},
{`\}\}`, CommentPreproc, Pop(1)},
{`:?(true|false)`, LiteralStringBoolean, nil},
{`:?"(\\\\|\\"|[^"])*"`, LiteralStringDouble, nil},
{`:?'(\\\\|\\'|[^'])*'`, LiteralStringSingle, nil},
{`[0-9](\.[0-9]*)?(eE[+-][0-9])?[flFLdD]?|0[xX][0-9a-fA-F]+[Ll]?`, LiteralNumber, nil},
{`[a-zA-Z][\w-]*(\(.*\))?`, NameVariable, nil},
{`\.[\w-]+(\(.*\))?`, NameVariable, nil},
{`(\?)(\s*)([^}\s]+)(\s*)(:)(\s*)([^}\s]+)(\s*)`, ByGroups(Operator, Text, LiteralString, Text, Operator, Text, LiteralString, Text), nil},
},
"attr": {
{`".*?"`, LiteralString, Pop(1)},
{`'.*?'`, LiteralString, Pop(1)},
{`[^\s>]+`, LiteralString, Pop(1)},
},
},
))

View File

@ -1,101 +0,0 @@
package a
import (
. "github.com/alecthomas/chroma" // nolint
"github.com/alecthomas/chroma/lexers/internal"
)
// ANTLR lexer.
var ANTLR = internal.Register(MustNewLexer(
&Config{
Name: "ANTLR",
Aliases: []string{"antlr"},
Filenames: []string{},
MimeTypes: []string{},
},
Rules{
"whitespace": {
{`\s+`, TextWhitespace, nil},
},
"comments": {
{`//.*$`, Comment, nil},
{`/\*(.|\n)*?\*/`, Comment, nil},
},
"root": {
Include("whitespace"),
Include("comments"),
{`(lexer|parser|tree)?(\s*)(grammar\b)(\s*)([A-Za-z]\w*)(;)`, ByGroups(Keyword, TextWhitespace, Keyword, TextWhitespace, NameClass, Punctuation), nil},
{`options\b`, Keyword, Push("options")},
{`tokens\b`, Keyword, Push("tokens")},
{`(scope)(\s*)([A-Za-z]\w*)(\s*)(\{)`, ByGroups(Keyword, TextWhitespace, NameVariable, TextWhitespace, Punctuation), Push("action")},
{`(catch|finally)\b`, Keyword, Push("exception")},
{`(@[A-Za-z]\w*)(\s*)(::)?(\s*)([A-Za-z]\w*)(\s*)(\{)`, ByGroups(NameLabel, TextWhitespace, Punctuation, TextWhitespace, NameLabel, TextWhitespace, Punctuation), Push("action")},
{`((?:protected|private|public|fragment)\b)?(\s*)([A-Za-z]\w*)(!)?`, ByGroups(Keyword, TextWhitespace, NameLabel, Punctuation), Push("rule-alts", "rule-prelims")},
},
"exception": {
{`\n`, TextWhitespace, Pop(1)},
{`\s`, TextWhitespace, nil},
Include("comments"),
{`\[`, Punctuation, Push("nested-arg-action")},
{`\{`, Punctuation, Push("action")},
},
"rule-prelims": {
Include("whitespace"),
Include("comments"),
{`returns\b`, Keyword, nil},
{`\[`, Punctuation, Push("nested-arg-action")},
{`\{`, Punctuation, Push("action")},
{`(throws)(\s+)([A-Za-z]\w*)`, ByGroups(Keyword, TextWhitespace, NameLabel), nil},
{`(,)(\s*)([A-Za-z]\w*)`, ByGroups(Punctuation, TextWhitespace, NameLabel), nil},
{`options\b`, Keyword, Push("options")},
{`(scope)(\s+)(\{)`, ByGroups(Keyword, TextWhitespace, Punctuation), Push("action")},
{`(scope)(\s+)([A-Za-z]\w*)(\s*)(;)`, ByGroups(Keyword, TextWhitespace, NameLabel, TextWhitespace, Punctuation), nil},
{`(@[A-Za-z]\w*)(\s*)(\{)`, ByGroups(NameLabel, TextWhitespace, Punctuation), Push("action")},
{`:`, Punctuation, Pop(1)},
},
"rule-alts": {
Include("whitespace"),
Include("comments"),
{`options\b`, Keyword, Push("options")},
{`:`, Punctuation, nil},
{`'(\\\\|\\'|[^'])*'`, LiteralString, nil},
{`"(\\\\|\\"|[^"])*"`, LiteralString, nil},
{`<<([^>]|>[^>])>>`, LiteralString, nil},
{`\$?[A-Z_]\w*`, NameConstant, nil},
{`\$?[a-z_]\w*`, NameVariable, nil},
{`(\+|\||->|=>|=|\(|\)|\.\.|\.|\?|\*|\^|!|\#|~)`, Operator, nil},
{`,`, Punctuation, nil},
{`\[`, Punctuation, Push("nested-arg-action")},
{`\{`, Punctuation, Push("action")},
{`;`, Punctuation, Pop(1)},
},
"tokens": {
Include("whitespace"),
Include("comments"),
{`\{`, Punctuation, nil},
{`([A-Z]\w*)(\s*)(=)?(\s*)(\'(?:\\\\|\\\'|[^\']*)\')?(\s*)(;)`, ByGroups(NameLabel, TextWhitespace, Punctuation, TextWhitespace, LiteralString, TextWhitespace, Punctuation), nil},
{`\}`, Punctuation, Pop(1)},
},
"options": {
Include("whitespace"),
Include("comments"),
{`\{`, Punctuation, nil},
{`([A-Za-z]\w*)(\s*)(=)(\s*)([A-Za-z]\w*|\'(?:\\\\|\\\'|[^\']*)\'|[0-9]+|\*)(\s*)(;)`, ByGroups(NameVariable, TextWhitespace, Punctuation, TextWhitespace, Text, TextWhitespace, Punctuation), nil},
{`\}`, Punctuation, Pop(1)},
},
"action": {
{`([^${}\'"/\\]+|"(\\\\|\\"|[^"])*"|'(\\\\|\\'|[^'])*'|//.*$\n?|/\*(.|\n)*?\*/|/(?!\*)(\\\\|\\/|[^/])*/|\\(?!%)|/)+`, Other, nil},
{`(\\)(%)`, ByGroups(Punctuation, Other), nil},
{`(\$[a-zA-Z]+)(\.?)(text|value)?`, ByGroups(NameVariable, Punctuation, NameProperty), nil},
{`\{`, Punctuation, Push()},
{`\}`, Punctuation, Pop(1)},
},
"nested-arg-action": {
{`([^$\[\]\'"/]+|"(\\\\|\\"|[^"])*"|'(\\\\|\\'|[^'])*'|//.*$\n?|/\*(.|\n)*?\*/|/(?!\*)(\\\\|\\/|[^/])*/|/)+`, Other, nil},
{`\[`, Punctuation, Push()},
{`\]`, Punctuation, Pop(1)},
{`(\$[a-zA-Z]+)(\.?)(text|value)?`, ByGroups(NameVariable, Punctuation, NameProperty), nil},
{`(\\\\|\\\]|\\\[|[^\[\]])+`, Other, nil},
},
},
))

View File

@ -1,38 +0,0 @@
package a
import (
. "github.com/alecthomas/chroma" // nolint
"github.com/alecthomas/chroma/lexers/internal"
)
// Apacheconf lexer.
var Apacheconf = internal.Register(MustNewLexer(
&Config{
Name: "ApacheConf",
Aliases: []string{"apacheconf", "aconf", "apache"},
Filenames: []string{".htaccess", "apache.conf", "apache2.conf"},
MimeTypes: []string{"text/x-apacheconf"},
CaseInsensitive: true,
},
Rules{
"root": {
{`\s+`, Text, nil},
{`(#.*?)$`, Comment, nil},
{`(<[^\s>]+)(?:(\s+)(.*?))?(>)`, ByGroups(NameTag, Text, LiteralString, NameTag), nil},
{`([a-z]\w*)(\s+)`, ByGroups(NameBuiltin, Text), Push("value")},
{`\.+`, Text, nil},
},
"value": {
{`\\\n`, Text, nil},
{`$`, Text, Pop(1)},
{`\\`, Text, nil},
{`[^\S\n]+`, Text, nil},
{`\d+\.\d+\.\d+\.\d+(?:/\d+)?`, LiteralNumber, nil},
{`\d+`, LiteralNumber, nil},
{`/([a-z0-9][\w./-]+)`, LiteralStringOther, nil},
{`(on|off|none|any|all|double|email|dns|min|minimal|os|productonly|full|emerg|alert|crit|error|warn|notice|info|debug|registry|script|inetd|standalone|user|group)\b`, Keyword, nil},
{`"([^"\\]*(?:\\.[^"\\]*)*)"`, LiteralStringDouble, nil},
{`[^\s"\\]+`, Text, nil},
},
},
))

View File

@ -1,36 +0,0 @@
package a
import (
. "github.com/alecthomas/chroma" // nolint
"github.com/alecthomas/chroma/lexers/internal"
)
// Apl lexer.
var Apl = internal.Register(MustNewLexer(
&Config{
Name: "APL",
Aliases: []string{"apl"},
Filenames: []string{"*.apl"},
MimeTypes: []string{},
},
Rules{
"root": {
{`\s+`, Text, nil},
{`[⍝#].*$`, CommentSingle, nil},
{`\'((\'\')|[^\'])*\'`, LiteralStringSingle, nil},
{`"(("")|[^"])*"`, LiteralStringDouble, nil},
{`[⋄◇()]`, Punctuation, nil},
{`[\[\];]`, LiteralStringRegex, nil},
{`⎕[A-Za-zΔ∆⍙][A-Za-zΔ∆⍙_¯0-9]*`, NameFunction, nil},
{`[A-Za-zΔ∆⍙][A-Za-zΔ∆⍙_¯0-9]*`, NameVariable, nil},
{`¯?(0[Xx][0-9A-Fa-f]+|[0-9]*\.?[0-9]+([Ee][+¯]?[0-9]+)?|¯|∞)([Jj]¯?(0[Xx][0-9A-Fa-f]+|[0-9]*\.?[0-9]+([Ee][+¯]?[0-9]+)?|¯|∞))?`, LiteralNumber, nil},
{`[\.\\/⌿⍀¨⍣⍨⍠⍤∘]`, NameAttribute, nil},
{`[+\-×÷⌈⌊∣|?*⍟○!⌹<≤=>≥≠≡≢∊⍷∪∩~∨∧⍱⍲⍴,⍪⌽⊖⍉↑↓⊂⊃⌷⍋⍒⊤⊥⍕⍎⊣⊢⍁⍂≈⌸⍯↗]`, Operator, nil},
{``, NameConstant, nil},
{`[⎕⍞]`, NameVariableGlobal, nil},
{`[←→]`, KeywordDeclaration, nil},
{`[⍺⍵⍶⍹∇:]`, NameBuiltinPseudo, nil},
{`[{}]`, KeywordType, nil},
},
},
))

Some files were not shown because too many files have changed in this diff Show More