mirror of
https://github.com/foomo/gocontemplate.git
synced 2025-10-16 12:35:36 +00:00
initial commit
This commit is contained in:
commit
1142d15bfc
12
.editorconfig
Normal file
12
.editorconfig
Normal file
@ -0,0 +1,12 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_size = 2
|
||||
indent_style = tab
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.{yaml,yml,md,mdx}]
|
||||
indent_style = space
|
||||
25
.github/dependabot.yml
vendored
Normal file
25
.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
version: 2
|
||||
|
||||
updates:
|
||||
- package-ecosystem: 'github-actions'
|
||||
directory: '/'
|
||||
schedule:
|
||||
day: 'sunday'
|
||||
interval: 'weekly'
|
||||
groups:
|
||||
github-actions:
|
||||
patterns:
|
||||
- '*'
|
||||
|
||||
- package-ecosystem: 'gomod'
|
||||
directory: '/'
|
||||
schedule:
|
||||
day: 'sunday'
|
||||
interval: 'weekly'
|
||||
groups:
|
||||
gomod-security:
|
||||
applies-to: security-updates
|
||||
patterns: ['*']
|
||||
gomod-update:
|
||||
applies-to: version-updates
|
||||
patterns: ['*']
|
||||
31
.github/workflows/release.yml
vendored
Normal file
31
.github/workflows/release.yml
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
name: Release Tag
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v*.*.*
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- run: git fetch --force --tags
|
||||
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 'stable'
|
||||
|
||||
- uses: goreleaser/goreleaser-action@v5
|
||||
with:
|
||||
version: latest
|
||||
args: release --clean
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
37
.github/workflows/test.yml
vendored
Normal file
37
.github/workflows/test.yml
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
name: Test Branch
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 'stable'
|
||||
|
||||
- uses: gotesttools/gotestfmt-action@v2
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- uses: golangci/golangci-lint-action@v5
|
||||
with:
|
||||
version: latest
|
||||
args: --timeout=5m
|
||||
|
||||
- run: make test
|
||||
|
||||
- uses: coverallsapp/github-action@v2
|
||||
with:
|
||||
file: coverage.out
|
||||
|
||||
13
.gitignore
vendored
Normal file
13
.gitignore
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
.*
|
||||
*.log
|
||||
*.out
|
||||
/tmp/
|
||||
!.github/
|
||||
!.husky/
|
||||
!.editorconfig
|
||||
!.gitignore
|
||||
!.golangci.yml
|
||||
!.goreleaser.yml
|
||||
!.husky.yaml
|
||||
go.work
|
||||
go.work.sum
|
||||
195
.golangci.yml
Normal file
195
.golangci.yml
Normal file
@ -0,0 +1,195 @@
|
||||
run:
|
||||
timeout: 5m
|
||||
|
||||
issues:
|
||||
exclude-dirs:
|
||||
- 'tmp'
|
||||
exclude-rules:
|
||||
- path: _test\.go
|
||||
linters:
|
||||
- forbidigo
|
||||
- forcetypeassert
|
||||
|
||||
linters-settings:
|
||||
# https://golangci-lint.run/usage/linters/#misspell
|
||||
misspell:
|
||||
mode: restricted
|
||||
# https://golangci-lint.run/usage/linters/#asasalint
|
||||
asasalint:
|
||||
ignore-test: true
|
||||
# https://golangci-lint.run/usage/linters/#exhaustive
|
||||
exhaustive:
|
||||
default-signifies-exhaustive: true
|
||||
# https://golangci-lint.run/usage/linters/#predeclared
|
||||
predeclared:
|
||||
ignore: "new,error"
|
||||
# https://golangci-lint.run/usage/linters/#gocritic
|
||||
gocritic:
|
||||
disabled-checks:
|
||||
- ifElseChain
|
||||
- singleCaseSwitch
|
||||
- commentFormatting
|
||||
# https://golangci-lint.run/usage/linters/#testifylint
|
||||
testifylint:
|
||||
disable:
|
||||
- float-compare
|
||||
# https://golangci-lint.run/usage/linters/#gosec
|
||||
gosec:
|
||||
confidence: medium
|
||||
# https://golangci-lint.run/usage/linters/#importas
|
||||
importas:
|
||||
no-unaliased: true
|
||||
# https://golangci-lint.run/usage/linters/#gomoddirectives
|
||||
gomoddirectives:
|
||||
replace-local: true
|
||||
# https://golangci-lint.run/usage/linters/#revive
|
||||
revive:
|
||||
ignore-generated-header: true
|
||||
enable-all-rules: true
|
||||
rules:
|
||||
- name: line-length-limit
|
||||
disabled: true
|
||||
- name: cognitive-complexity
|
||||
disabled: true
|
||||
- name: unused-parameter
|
||||
disabled: true
|
||||
- name: add-constant
|
||||
disabled: true
|
||||
- name: cyclomatic
|
||||
disabled: true
|
||||
- name: function-length
|
||||
disabled: true
|
||||
- name: function-result-limit
|
||||
disabled: true
|
||||
- name: flag-parameter
|
||||
disabled: true
|
||||
- name: unused-receiver
|
||||
disabled: true
|
||||
- name: argument-limit
|
||||
disabled: true
|
||||
- name: max-control-nesting
|
||||
disabled: true
|
||||
- name: comment-spacings
|
||||
disabled: true
|
||||
- name: struct-tag
|
||||
arguments:
|
||||
- "json,inline"
|
||||
- name: unhandled-error
|
||||
arguments:
|
||||
- "fmt.Println"
|
||||
- "io.Writer.Write"
|
||||
- "strings.Builder.WriteString"
|
||||
- name: unnecessary-stmt
|
||||
disabled: true
|
||||
|
||||
linters:
|
||||
disable-all: true
|
||||
enable:
|
||||
## Enabled by default linters:
|
||||
- errcheck # errcheck is a program for checking for unchecked errors in Go code. These unchecked errors can be critical bugs in some cases [fast: false, auto-fix: false]
|
||||
- gosimple # (megacheck) Linter for Go source code that specializes in simplifying code [fast: false, auto-fix: false]
|
||||
- govet # (vet, vetshadow) Vet examines Go source code and reports suspicious constructs. It is roughly the same as 'go vet' and uses its passes. [fast: false, auto-fix: false]
|
||||
- ineffassign # Detects when assignments to existing variables are not used [fast: true, auto-fix: false]
|
||||
- staticcheck # (megacheck) It's a set of rules from staticcheck. It's not the same thing as the staticcheck binary. The author of staticcheck doesn't support or approve the use of staticcheck as a library inside golangci-lint. [fast: false, auto-fix: false]
|
||||
- unused # (megacheck) Checks Go code for unused constants, variables, functions and types [fast: false, auto-fix: false]
|
||||
|
||||
## Disabled by your configuration linters:
|
||||
- asasalint # check for pass []any as any in variadic func(...any) [fast: false, auto-fix: false]
|
||||
- asciicheck # checks that all code identifiers does not have non-ASCII symbols in the name [fast: true, auto-fix: false]
|
||||
- bidichk # Checks for dangerous unicode character sequences [fast: true, auto-fix: false]
|
||||
- bodyclose # checks whether HTTP response body is closed successfully [fast: false, auto-fix: false]
|
||||
- containedctx # containedctx is a linter that detects struct contained context.Context field [fast: false, auto-fix: false]
|
||||
- contextcheck # check whether the function uses a non-inherited context [fast: false, auto-fix: false]
|
||||
- copyloopvar # (go >= 1.22) copyloopvar is a linter detects places where loop variables are copied [fast: true, auto-fix: false]
|
||||
- decorder # check declaration order and count of types, constants, variables and functions [fast: true, auto-fix: false]
|
||||
- durationcheck # check for two durations multiplied together [fast: false, auto-fix: false]
|
||||
- errchkjson # Checks types passed to the json encoding functions. Reports unsupported types and reports occations, where the check for the returned error can be omitted. [fast: false, auto-fix: false]
|
||||
- errname # Checks that sentinel errors are prefixed with the `Err` and error types are suffixed with the `Error`. [fast: false, auto-fix: false]
|
||||
- errorlint # errorlint is a linter for that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13. [fast: false, auto-fix: false]
|
||||
- exhaustive # check exhaustiveness of enum switch statements [fast: false, auto-fix: false]
|
||||
- exportloopref # checks for pointers to enclosing loop variables [fast: false, auto-fix: false]
|
||||
- forbidigo # Forbids identifiers [fast: false, auto-fix: false]
|
||||
- forcetypeassert # finds forced type assertions [fast: true, auto-fix: false]
|
||||
- gocheckcompilerdirectives # Checks that go compiler directive comments (//go:) are valid. [fast: true, auto-fix: false]
|
||||
- gochecksumtype # Run exhaustiveness checks on Go "sum types" [fast: false, auto-fix: false]
|
||||
- goconst # Finds repeated strings that could be replaced by a constant [fast: true, auto-fix: false]
|
||||
- gocritic # Provides diagnostics that check for bugs, performance and style issues. [fast: false, auto-fix: true]
|
||||
- gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification [fast: true, auto-fix: true]
|
||||
- goheader # Checks is file header matches to pattern [fast: true, auto-fix: true]
|
||||
- goimports # Check import statements are formatted according to the 'goimport' command. Reformat imports in autofix mode. [fast: true, auto-fix: true]
|
||||
- gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod. [fast: true, auto-fix: false]
|
||||
- gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations. [fast: true, auto-fix: false]
|
||||
- goprintffuncname # Checks that printf-like functions are named with `f` at the end. [fast: true, auto-fix: false]
|
||||
- gosec # (gas) Inspects source code for security problems [fast: false, auto-fix: false]
|
||||
- gosmopolitan # Report certain i18n/l10n anti-patterns in your Go codebase [fast: false, auto-fix: false]
|
||||
- grouper # Analyze expression groups. [fast: true, auto-fix: false]
|
||||
- importas # Enforces consistent import aliases [fast: false, auto-fix: false]
|
||||
- inamedparam # reports interfaces with unnamed method parameters [fast: true, auto-fix: false]
|
||||
- intrange # (go >= 1.22) intrange is a linter to find places where for loops could make use of an integer range. [fast: true, auto-fix: false]
|
||||
- loggercheck # (logrlint) Checks key value pairs for common logger libraries (kitlog,klog,logr,zap). [fast: false, auto-fix: false]
|
||||
- makezero # Finds slice declarations with non-zero initial length [fast: false, auto-fix: false]
|
||||
- misspell # Finds commonly misspelled English words [fast: true, auto-fix: true]
|
||||
- mirror # reports wrong mirror patterns of bytes/strings usage [fast: false, auto-fix: true]
|
||||
- musttag # enforce field tags in (un)marshaled structs [fast: false, auto-fix: false]
|
||||
- nakedret # Checks that functions with naked returns are not longer than a maximum size (can be zero). [fast: true, auto-fix: false]
|
||||
- nilerr # Finds the code that returns nil even if it checks that the error is not nil. [fast: false, auto-fix: false]
|
||||
- nilnil # Checks that there is no simultaneous return of `nil` error and an invalid value. [fast: false, auto-fix: false]
|
||||
- noctx # Finds sending http request without context.Context [fast: false, auto-fix: false]
|
||||
- nolintlint # Reports ill-formed or insufficient nolint directives [fast: true, auto-fix: true]
|
||||
- nonamedreturns # Reports all named returns [fast: false, auto-fix: false]
|
||||
- nosprintfhostport # Checks for misuse of Sprintf to construct a host with port in a URL. [fast: true, auto-fix: false]
|
||||
- paralleltest # Detects missing usage of t.Parallel() method in your Go test [fast: false, auto-fix: false]
|
||||
- predeclared # find code that shadows one of Go's predeclared identifiers [fast: true, auto-fix: false]
|
||||
- promlinter # Check Prometheus metrics naming via promlint [fast: true, auto-fix: false]
|
||||
- reassign # Checks that package variables are not reassigned [fast: false, auto-fix: false]
|
||||
- revive # Fast, configurable, extensible, flexible, and beautiful linter for Go. Drop-in replacement of golint. [fast: false, auto-fix: false]
|
||||
- rowserrcheck # checks whether Rows.Err of rows is checked successfully [fast: false, auto-fix: false]
|
||||
- spancheck # Checks for mistakes with OpenTelemetry/Census spans. [fast: false, auto-fix: false]
|
||||
- sqlclosecheck # Checks that sql.Rows, sql.Stmt, sqlx.NamedStmt, pgx.Query are closed. [fast: false, auto-fix: false]
|
||||
- stylecheck # Stylecheck is a replacement for golint [fast: false, auto-fix: false]
|
||||
- tenv # tenv is analyzer that detects using os.Setenv instead of t.Setenv since Go1.17 [fast: false, auto-fix: false]
|
||||
- testableexamples # linter checks if examples are testable (have an expected output) [fast: true, auto-fix: false]
|
||||
- testifylint # Checks usage of github.com/stretchr/testify. [fast: false, auto-fix: false]
|
||||
- testpackage # linter that makes you use a separate _test package [fast: true, auto-fix: false]
|
||||
- thelper # thelper detects tests helpers which is not start with t.Helper() method. [fast: false, auto-fix: false]
|
||||
- tparallel # tparallel detects inappropriate usage of t.Parallel() method in your Go test codes. [fast: false, auto-fix: false]
|
||||
- unconvert # Remove unnecessary type conversions [fast: false, auto-fix: false]
|
||||
- usestdlibvars # A linter that detect the possibility to use variables/constants from the Go standard library. [fast: true, auto-fix: false]
|
||||
- wastedassign # Finds wasted assignment statements [fast: false, auto-fix: false]
|
||||
- whitespace # Whitespace is a linter that checks for unnecessary newlines at the start and end of functions, if, for, etc. [fast: true, auto-fix: true]
|
||||
|
||||
## Don't enable
|
||||
#- cyclop # checks function and package cyclomatic complexity [fast: false, auto-fix: false]
|
||||
#- depguard # Go linter that checks if package imports are in a list of acceptable packages [fast: true, auto-fix: false]
|
||||
#- dupl # Tool for code clone detection [fast: true, auto-fix: false]
|
||||
#- dupword # checks for duplicate words in the source code [fast: true, auto-fix: false]
|
||||
#- dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f()) [fast: true, auto-fix: false]
|
||||
#- err113 # Go linter to check the errors handling expressions [fast: false, auto-fix: false]
|
||||
#- exhaustruct # Checks if all structure fields are initialized [fast: false, auto-fix: false]
|
||||
#- gci # Gci controls Go package import order and makes it always deterministic. [fast: true, auto-fix: true]
|
||||
#- gochecknoglobals # Check that no global variables exist. [fast: false, auto-fix: false]
|
||||
#- gochecknoinits # Checks that no init functions are present in Go code [fast: true, auto-fix: false]
|
||||
#- gocyclo # Computes and checks the cyclomatic complexity of functions [fast: true, auto-fix: false]
|
||||
#- godot # Check if comments end in a period [fast: true, auto-fix: true]
|
||||
#- godox # Tool for detection of FIXME, TODO and other comment keywords [fast: true, auto-fix: false]
|
||||
#- gofumpt # Gofumpt checks whether code was gofumpt-ed. [fast: true, auto-fix: true]
|
||||
#- funlen # Tool for detection of long functions [fast: true, auto-fix: false]
|
||||
#- ginkgolinter # enforces standards of using ginkgo and gomega [fast: false, auto-fix: false]
|
||||
#- gocognit # Computes and checks the cognitive complexity of functions [fast: true, auto-fix: false]
|
||||
#- interfacebloat # A linter that checks the number of methods inside an interface. [fast: true, auto-fix: false]
|
||||
#- lll # Reports long lines [fast: true, auto-fix: false]
|
||||
#- ireturn # Accept Interfaces, Return Concrete Types [fast: false, auto-fix: false]
|
||||
#- maintidx # maintidx measures the maintainability index of each function. [fast: true, auto-fix: false]
|
||||
#- nestif # Reports deeply nested if statements [fast: true, auto-fix: false]
|
||||
#- nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity [fast: true, auto-fix: false]
|
||||
#- perfsprint # Checks that fmt.Sprintf can be replaced with a faster alternative. [fast: false, auto-fix: false]
|
||||
#- prealloc # Finds slice declarations that could potentially be pre-allocated [fast: true, auto-fix: false]
|
||||
#- protogetter # Reports direct reads from proto message fields when getters should be used [fast: false, auto-fix: true]
|
||||
#- sloglint # ensure consistent code style when using log/slog [fast: false, auto-fix: false]
|
||||
#- tagalign # check that struct tags are well aligned [fast: true, auto-fix: true]
|
||||
#- tagliatelle # Checks the struct tags. [fast: true, auto-fix: false]
|
||||
#- unparam # Reports unused function parameters [fast: false, auto-fix: false]
|
||||
#- varnamelen # checks that the length of a variable's name matches its scope [fast: false, auto-fix: false]
|
||||
#- wrapcheck # Checks that errors returned from external packages are wrapped [fast: false, auto-fix: false]
|
||||
#- wsl # add or remove empty lines [fast: true, auto-fix: false]
|
||||
#- zerologlint # Detects the wrong usage of `zerolog` that a user forgets to dispatch with `Send` or `Msg` [fast: false, auto-fix: false]
|
||||
5
.goreleaser.yml
Normal file
5
.goreleaser.yml
Normal file
@ -0,0 +1,5 @@
|
||||
builds:
|
||||
- skip: true
|
||||
|
||||
changelog:
|
||||
use: github-native
|
||||
15
.husky.yaml
Normal file
15
.husky.yaml
Normal file
@ -0,0 +1,15 @@
|
||||
hooks:
|
||||
pre-commit:
|
||||
- golangci-lint run --fast
|
||||
- husky lint-staged
|
||||
commit-msg:
|
||||
# only execute if not in a merge
|
||||
- if [[ -z $(git rev-parse -q --verify MERGE_HEAD) ]]; then husky lint-commit; fi
|
||||
|
||||
lint-staged:
|
||||
'*.go':
|
||||
- goimports -l -w
|
||||
|
||||
lint-commit:
|
||||
types: '^(feat|fix|build|chore|docs|perf|refactor|revert|style|test|wip)$'
|
||||
header: '^(?P<type>\w+)(\((?P<scope>[\w/.-]+)\))?(?P<breaking>!)?:( +)?(?P<header>.+)'
|
||||
3
.husky/applypatch-msg
Executable file
3
.husky/applypatch-msg
Executable file
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
husky hook $(basename "$0") $*
|
||||
3
.husky/commit-msg
Executable file
3
.husky/commit-msg
Executable file
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
husky hook $(basename "$0") $*
|
||||
3
.husky/fsmonitor-watchman
Executable file
3
.husky/fsmonitor-watchman
Executable file
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
husky hook $(basename "$0") $*
|
||||
3
.husky/post-update
Executable file
3
.husky/post-update
Executable file
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
husky hook $(basename "$0") $*
|
||||
3
.husky/pre-applypatch
Executable file
3
.husky/pre-applypatch
Executable file
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
husky hook $(basename "$0") $*
|
||||
3
.husky/pre-commit
Executable file
3
.husky/pre-commit
Executable file
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
husky hook $(basename "$0") $*
|
||||
3
.husky/pre-merge-commit
Executable file
3
.husky/pre-merge-commit
Executable file
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
husky hook $(basename "$0") $*
|
||||
3
.husky/pre-push
Executable file
3
.husky/pre-push
Executable file
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
husky hook $(basename "$0") $*
|
||||
3
.husky/pre-rebase
Executable file
3
.husky/pre-rebase
Executable file
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
husky hook $(basename "$0") $*
|
||||
3
.husky/pre-receive
Executable file
3
.husky/pre-receive
Executable file
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
husky hook $(basename "$0") $*
|
||||
3
.husky/prepare-commit-msg
Executable file
3
.husky/prepare-commit-msg
Executable file
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
husky hook $(basename "$0") $*
|
||||
3
.husky/push-to-checkout
Executable file
3
.husky/push-to-checkout
Executable file
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
husky hook $(basename "$0") $*
|
||||
3
.husky/sendemail-validate
Executable file
3
.husky/sendemail-validate
Executable file
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
husky hook $(basename "$0") $*
|
||||
3
.husky/update
Executable file
3
.husky/update
Executable file
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
husky hook $(basename "$0") $*
|
||||
128
CODE_OF_CONDUCT.md
Normal file
128
CODE_OF_CONDUCT.md
Normal file
@ -0,0 +1,128 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
info@bestbytes.de.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series
|
||||
of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
permanent ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
the community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) Foomo web framework
|
||||
|
||||
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.
|
||||
84
Makefile
Normal file
84
Makefile
Normal file
@ -0,0 +1,84 @@
|
||||
.DEFAULT_GOAL:=help
|
||||
-include .makerc
|
||||
|
||||
# --- Targets -----------------------------------------------------------------
|
||||
|
||||
# This allows us to accept extra arguments
|
||||
%: .husky
|
||||
@:
|
||||
|
||||
.PHONY: .husky
|
||||
# Configure git hooks for husky
|
||||
.husky:
|
||||
@if ! command -v husky &> /dev/null; then \
|
||||
echo "ERROR: missing executeable 'husky', please run:"; \
|
||||
echo "\n$ go install github.com/go-courier/husky/cmd/husky@latest\n"; \
|
||||
fi
|
||||
@git config core.hooksPath .husky
|
||||
|
||||
## === Tasks ===
|
||||
|
||||
.PHONY: doc
|
||||
## Run tests
|
||||
doc:
|
||||
@open "http://localhost:6060/pkg/github.com/foomo/gocontemplate/"
|
||||
@godoc -http=localhost:6060 -play
|
||||
|
||||
.PHONY: test
|
||||
## Run tests
|
||||
test:
|
||||
@go test -coverprofile=coverage.out -race -json ./... | gotestfmt
|
||||
|
||||
.PHONY: lint
|
||||
## Run linter
|
||||
lint:
|
||||
@golangci-lint run
|
||||
|
||||
.PHONY: lint.fix
|
||||
## Fix lint violations
|
||||
lint.fix:
|
||||
@golangci-lint run --fix
|
||||
|
||||
.PHONY: tidy
|
||||
## Run go mod tidy
|
||||
tidy:
|
||||
@go mod tidy
|
||||
|
||||
.PHONY: outdated
|
||||
## Show outdated direct dependencies
|
||||
outdated:
|
||||
@go list -u -m -json all | go-mod-outdated -update -direct
|
||||
|
||||
## === Utils ===
|
||||
|
||||
## Show help text
|
||||
help:
|
||||
@awk '{ \
|
||||
if ($$0 ~ /^.PHONY: [a-zA-Z\-\_0-9]+$$/) { \
|
||||
helpCommand = substr($$0, index($$0, ":") + 2); \
|
||||
if (helpMessage) { \
|
||||
printf "\033[36m%-23s\033[0m %s\n", \
|
||||
helpCommand, helpMessage; \
|
||||
helpMessage = ""; \
|
||||
} \
|
||||
} else if ($$0 ~ /^[a-zA-Z\-\_0-9.]+:/) { \
|
||||
helpCommand = substr($$0, 0, index($$0, ":")); \
|
||||
if (helpMessage) { \
|
||||
printf "\033[36m%-23s\033[0m %s\n", \
|
||||
helpCommand, helpMessage"\n"; \
|
||||
helpMessage = ""; \
|
||||
} \
|
||||
} else if ($$0 ~ /^##/) { \
|
||||
if (helpMessage) { \
|
||||
helpMessage = helpMessage"\n "substr($$0, 3); \
|
||||
} else { \
|
||||
helpMessage = substr($$0, 3); \
|
||||
} \
|
||||
} else { \
|
||||
if (helpMessage) { \
|
||||
print "\n "helpMessage"\n" \
|
||||
} \
|
||||
helpMessage = ""; \
|
||||
} \
|
||||
}' \
|
||||
$(MAKEFILE_LIST)
|
||||
41
README.md
Normal file
41
README.md
Normal file
@ -0,0 +1,41 @@
|
||||
# Go Contemplate
|
||||
|
||||
[](https://github.com/foomo/gocontemplate/actions/workflows/test.yml)
|
||||
[](https://goreportcard.com/report/github.com/foomo/gocontemplate)
|
||||
[](https://godoc.org/github.com/foomo/gocontemplate)
|
||||
|
||||
> A code generation helper, that
|
||||
|
||||
Wrapper library around `golang.org/x/tools/go/packages` to filter only defined types and their dependencies.
|
||||
|
||||
## Example
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/foomo/gocontemplate"
|
||||
)
|
||||
|
||||
func main() {
|
||||
goctpl, err := gocontemplate.Load(&gocontemplate.Config{
|
||||
Packages: []*gocontemplate.ConfigPackage{
|
||||
{
|
||||
Path: "github.com/foomo/sesamy-go/event",
|
||||
Types: []string{"PageView"},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## How to Contribute
|
||||
|
||||
Make a pull request...
|
||||
|
||||
## License
|
||||
|
||||
Distributed under MIT License, please see license file within the code for more details.
|
||||
22
config.go
Normal file
22
config.go
Normal file
@ -0,0 +1,22 @@
|
||||
package gocontemplate
|
||||
|
||||
type Config struct {
|
||||
Packages []*ConfigPackage `json:"packages" yaml:"packages"`
|
||||
}
|
||||
|
||||
func (c *Config) Package(path string) *ConfigPackage {
|
||||
for _, value := range c.Packages {
|
||||
if value.Path == path {
|
||||
return value
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Config) PackagePaths() []string {
|
||||
ret := make([]string, len(c.Packages))
|
||||
for i, value := range c.Packages {
|
||||
ret[i] = value.Path
|
||||
}
|
||||
return ret
|
||||
}
|
||||
6
configpackage.go
Normal file
6
configpackage.go
Normal file
@ -0,0 +1,6 @@
|
||||
package gocontemplate
|
||||
|
||||
type ConfigPackage struct {
|
||||
Path string `json:"path" yaml:"path"`
|
||||
Types []string `json:"types" yaml:"types"`
|
||||
}
|
||||
171
contemplate.go
Normal file
171
contemplate.go
Normal file
@ -0,0 +1,171 @@
|
||||
package gocontemplate
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/types"
|
||||
"slices"
|
||||
|
||||
"golang.org/x/exp/maps"
|
||||
"golang.org/x/tools/go/packages"
|
||||
)
|
||||
|
||||
type Contemplate struct {
|
||||
cfg *Config
|
||||
Packages map[string]*Package
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ~ Public methods
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
func (s *Contemplate) Package(path string) *Package {
|
||||
return s.Packages[path]
|
||||
}
|
||||
|
||||
func (s *Contemplate) LookupExpr(name string) ast.Expr {
|
||||
for _, pkg := range s.Packages {
|
||||
if value := pkg.LookupExpr(name); value != nil {
|
||||
return value
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Contemplate) LookupTypesByType(obj types.Object) []types.Object {
|
||||
var ret []types.Object
|
||||
|
||||
expr := TC[*ast.Ident](s.LookupExpr(obj.Name()))
|
||||
if expr == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, pkg := range s.Packages {
|
||||
for _, object := range pkg.Types() {
|
||||
switch objectType := object.(type) {
|
||||
case *types.Const:
|
||||
if objectTypeNamed := TC[*types.Named](objectType.Type()); objectTypeNamed != nil {
|
||||
if objectTypeNamed.Obj() == obj {
|
||||
ret = append(ret, objectType)
|
||||
}
|
||||
}
|
||||
case *types.TypeName:
|
||||
if objectExpr := pkg.LookupExpr(object.Name()); objectExpr != nil {
|
||||
if objectExprIdent := TC[*ast.Ident](objectExpr); objectExprIdent != nil {
|
||||
if objectExprDecl := TC[*ast.TypeSpec](objectExprIdent.Obj.Decl); objectExprDecl != nil {
|
||||
if objectExprType, ok := pkg.pkg.TypesInfo.Types[objectExprDecl.Type]; ok {
|
||||
if objectExprTypeNamed := TC[*types.Named](objectExprType.Type); objectExprTypeNamed != nil {
|
||||
if objectExprTypeNamed.Obj() == obj {
|
||||
ret = append(ret, objectType)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
// fmt.Println("?")
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ~ Private methods
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
func (s *Contemplate) addPackages(pkgs ...*packages.Package) {
|
||||
for _, pkg := range pkgs {
|
||||
if _, ok := s.Packages[pkg.PkgPath]; !ok {
|
||||
s.Packages[pkg.PkgPath] = NewPackage(s, pkg)
|
||||
|
||||
s.addPackages(maps.Values(pkg.Imports)...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Contemplate) addPackagesConfigs(confs ...*ConfigPackage) {
|
||||
for _, conf := range confs {
|
||||
s.Package(conf.Path).AddScopeTypes(conf.Types...)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Contemplate) LookupAstIdentDefsByDeclType(input types.TypeAndValue) []types.Object {
|
||||
var pkgs []*packages.Package
|
||||
var addImports func(pkg *packages.Package)
|
||||
addImports = func(pkg *packages.Package) {
|
||||
for _, p := range pkg.Imports {
|
||||
if !slices.Contains(pkgs, p) {
|
||||
pkgs = append(pkgs, p)
|
||||
addImports(p)
|
||||
}
|
||||
}
|
||||
}
|
||||
// for _, p := range s.pkgs {
|
||||
// pkgs = append(pkgs, p)
|
||||
// addImports(p)
|
||||
// }
|
||||
|
||||
var ret []types.Object
|
||||
for _, p := range pkgs {
|
||||
for _, name := range p.Types.Scope().Names() {
|
||||
child := p.Types.Scope().Lookup(name)
|
||||
if child.Type() == input.Type {
|
||||
ret = append(ret, child)
|
||||
}
|
||||
}
|
||||
|
||||
// for defAstIdent, defTypeObject := range p.TypesInfo.Defs {
|
||||
// if defAstIdent != nil && defAstIdent.Obj != nil && defTypeObject != nil {
|
||||
// if declValueSpec := TC[*ast.ValueSpec](defAstIdent.Obj.Decl); declValueSpec != nil {
|
||||
// if declValueSpecIdent := TC[*ast.Ident](declValueSpec.Type); declValueSpecIdent != nil {
|
||||
// if declValueSpecIdent.Obj == input.Obj {
|
||||
// ret[defAstIdent] = defTypeObject
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (s *Contemplate) addPackageTypeNames(pkg *packages.Package, typeNames ...string) {
|
||||
if _, ok := s.Packages[pkg.PkgPath]; !ok {
|
||||
s.Packages[pkg.PkgPath] = NewPackage(s, pkg)
|
||||
}
|
||||
// add request scopes
|
||||
s.Packages[pkg.PkgPath].AddScopeTypes(typeNames...)
|
||||
|
||||
// for k, v := range s.Packages[pkg.PkgPath].Imports {
|
||||
// s.addPackageTypeNames(k, v...)
|
||||
// }
|
||||
// check underlying added scopes
|
||||
// for _, name := range added {
|
||||
// s.typesType(pkg, s.Packages[pkg.PkgPath].Scope[name].Underlying())
|
||||
// }
|
||||
}
|
||||
|
||||
// func (s *Loader) typesType(pkg *packages.Package, v types.Type) {
|
||||
// switch t := v.(type) {
|
||||
// case *types.Struct:
|
||||
// // iterate fields
|
||||
// for i := range t.NumFields() {
|
||||
// s.typesVar(pkg, t.Field(i))
|
||||
// }
|
||||
// default:
|
||||
// fmt.Println(t)
|
||||
// }
|
||||
// }
|
||||
|
||||
// func (s *Loader) typesVar(pkg *packages.Package, v *types.Var) {
|
||||
// if !v.Exported() {
|
||||
// return
|
||||
// }
|
||||
// switch t := v.Type().(type) {
|
||||
// case *types.Named:
|
||||
// if p, ok := pkg.Imports[v.Pkg().Path()]; ok {
|
||||
// s.addPackageTypeNames(p, t.Obj().Name())
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
47
contemplate_test.go
Normal file
47
contemplate_test.go
Normal file
@ -0,0 +1,47 @@
|
||||
package gocontemplate_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/foomo/gocontemplate"
|
||||
_ "github.com/foomo/sesamy-go" // force inclusion
|
||||
_ "github.com/foomo/sesamy-go/event/params" // force inclusion
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestNewLoader(t *testing.T) {
|
||||
t.Parallel()
|
||||
goctpl, err := gocontemplate.Load(&gocontemplate.Config{
|
||||
Packages: []*gocontemplate.ConfigPackage{
|
||||
{
|
||||
Path: "github.com/foomo/sesamy-go/event",
|
||||
Types: []string{"PageView"},
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Len(t, goctpl.Packages, 4)
|
||||
}
|
||||
|
||||
func TestLoader_LookupTypesByType(t *testing.T) {
|
||||
t.Parallel()
|
||||
goctpl, err := gocontemplate.Load(&gocontemplate.Config{
|
||||
Packages: []*gocontemplate.ConfigPackage{
|
||||
{
|
||||
Path: "github.com/foomo/sesamy-go/event",
|
||||
Types: []string{"PageView"},
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
pkg := goctpl.Package("github.com/foomo/sesamy-go")
|
||||
require.NotNil(t, pkg)
|
||||
pkgType := pkg.LookupType("Event")
|
||||
require.NotNil(t, pkgType)
|
||||
|
||||
pkgTypes := goctpl.LookupTypesByType(pkgType)
|
||||
assert.NotEmpty(t, pkgTypes)
|
||||
}
|
||||
23
go.mod
Normal file
23
go.mod
Normal file
@ -0,0 +1,23 @@
|
||||
module github.com/foomo/gocontemplate
|
||||
|
||||
go 1.22.2
|
||||
|
||||
toolchain go1.22.3
|
||||
|
||||
require (
|
||||
github.com/foomo/sesamy-go v0.1.34-0.20240520134733-71fc83a0eb94
|
||||
github.com/stretchr/testify v1.9.0
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842
|
||||
golang.org/x/tools v0.21.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/foomo/gostandards v0.1.0 // indirect
|
||||
github.com/kr/pretty v0.1.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
golang.org/x/mod v0.17.0 // indirect
|
||||
golang.org/x/sync v0.7.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
28
go.sum
Normal file
28
go.sum
Normal file
@ -0,0 +1,28 @@
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/foomo/gostandards v0.1.0 h1:dN6Yoj5un74W8hooC+boYcdbkTzF9jqU39q5kQCkzn4=
|
||||
github.com/foomo/gostandards v0.1.0/go.mod h1:eyoFzndWb1kuDfupR/qf567mHeHZRi5//m64khreVac=
|
||||
github.com/foomo/sesamy-go v0.1.34-0.20240520134733-71fc83a0eb94 h1:HuLhNAVAB7WNiKDemeDzHvj3+WvYJVa4V8e9LiPsACM=
|
||||
github.com/foomo/sesamy-go v0.1.34-0.20240520134733-71fc83a0eb94/go.mod h1:zeYfOTHDzH9cQF8UjWmOUrMoPUM6LlvmY7IrliA9roQ=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
|
||||
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw=
|
||||
golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
27
load.go
Normal file
27
load.go
Normal file
@ -0,0 +1,27 @@
|
||||
package gocontemplate
|
||||
|
||||
import (
|
||||
"golang.org/x/tools/go/packages"
|
||||
)
|
||||
|
||||
func Load(cfg *Config) (*Contemplate, error) {
|
||||
inst := &Contemplate{
|
||||
cfg: cfg,
|
||||
Packages: map[string]*Package{},
|
||||
}
|
||||
|
||||
// load packages
|
||||
pkgs, err := packages.Load(&packages.Config{
|
||||
Mode: packages.NeedName | packages.NeedTypesInfo |
|
||||
packages.NeedFiles | packages.NeedImports | packages.NeedDeps |
|
||||
packages.NeedModule | packages.NeedTypes | packages.NeedSyntax,
|
||||
}, cfg.PackagePaths()...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
inst.addPackages(pkgs...)
|
||||
inst.addPackagesConfigs(cfg.Packages...)
|
||||
|
||||
return inst, nil
|
||||
}
|
||||
202
package.go
Normal file
202
package.go
Normal file
@ -0,0 +1,202 @@
|
||||
package gocontemplate
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/types"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/packages"
|
||||
)
|
||||
|
||||
type Package struct {
|
||||
l *Contemplate
|
||||
pkg *packages.Package
|
||||
exprs map[string]ast.Expr
|
||||
types map[string]types.Object
|
||||
scopeExprs map[string]ast.Expr
|
||||
scopeTypes map[string]types.Object
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ~ Constructor
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
func NewPackage(l *Contemplate, pkg *packages.Package) *Package {
|
||||
exprs := make(map[string]ast.Expr)
|
||||
for expr, value := range pkg.TypesInfo.Defs {
|
||||
if value != nil {
|
||||
switch value.(type) {
|
||||
case *types.Const:
|
||||
exprs[value.Name()] = expr
|
||||
case *types.Func, *types.TypeName:
|
||||
exprs[value.Name()] = expr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typess := make(map[string]types.Object)
|
||||
for _, name := range pkg.Types.Scope().Names() {
|
||||
typess[name] = pkg.Types.Scope().Lookup(name)
|
||||
}
|
||||
|
||||
inst := &Package{
|
||||
l: l,
|
||||
pkg: pkg,
|
||||
types: typess,
|
||||
exprs: map[string]ast.Expr{},
|
||||
scopeExprs: map[string]ast.Expr{},
|
||||
scopeTypes: map[string]types.Object{},
|
||||
}
|
||||
|
||||
inst.addExprs(pkg.TypesInfo.Defs)
|
||||
|
||||
return inst
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ~ Getter
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
func (s *Package) Name() string {
|
||||
return s.pkg.Name
|
||||
}
|
||||
|
||||
func (s *Package) Path() string {
|
||||
return s.pkg.PkgPath
|
||||
}
|
||||
|
||||
func (s *Package) Exprs() map[string]ast.Expr {
|
||||
return s.exprs
|
||||
}
|
||||
|
||||
func (s *Package) Types() map[string]types.Object {
|
||||
return s.types
|
||||
}
|
||||
|
||||
func (s *Package) ScopeTypes() map[string]types.Object {
|
||||
return s.scopeTypes
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ~ Public methods
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
func (s *Package) AddScopeTypes(names ...string) {
|
||||
for _, name := range names {
|
||||
if _, ok := s.scopeTypes[name]; !ok {
|
||||
scopeType := s.LookupType(name)
|
||||
scopeExpr := s.LookupExpr(name)
|
||||
if scopeType != nil && scopeExpr != nil {
|
||||
s.scopeTypes[name] = scopeType
|
||||
s.scopeExprs[name] = scopeExpr
|
||||
s.addScopeTypeAstExpr(scopeExpr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Package) LookupExpr(name string) ast.Expr {
|
||||
return s.exprs[name]
|
||||
}
|
||||
|
||||
func (s *Package) LookupScopeExpr(name string) ast.Expr {
|
||||
return s.scopeExprs[name]
|
||||
}
|
||||
|
||||
func (s *Package) FilterExprsByTypeExpr(expr ast.Expr) []ast.Expr {
|
||||
var ret []ast.Expr
|
||||
if exprIdent := TC[*ast.Ident](expr); exprIdent != nil {
|
||||
for _, child := range s.exprs {
|
||||
if childIdent := TC[*ast.Ident](child); childIdent != nil && childIdent.Obj != nil {
|
||||
if childDecl := TC[*ast.ValueSpec](childIdent.Obj.Decl); childDecl != nil {
|
||||
if childDeclType := TC[*ast.Ident](childDecl.Type); childDeclType != nil {
|
||||
if childDeclType.Obj == exprIdent.Obj {
|
||||
ret = append(ret, child)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (s *Package) LookupType(name string) types.Object {
|
||||
return s.types[name]
|
||||
}
|
||||
|
||||
func (s *Package) LookupScopeType(name string) types.Object {
|
||||
return s.scopeTypes[name]
|
||||
}
|
||||
|
||||
func (s *Package) LookupAstIdentDef(typeName string) *ast.Ident {
|
||||
for defAstIdent, defTypeObject := range s.pkg.TypesInfo.Defs {
|
||||
if defTypeObject != nil && defTypeObject.Name() == typeName {
|
||||
return defAstIdent
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ~ Private methods
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
func (s *Package) addScopeTypeAstExpr(input ast.Expr) {
|
||||
switch t := input.(type) {
|
||||
case *ast.Ident:
|
||||
if t.Obj != nil {
|
||||
s.addScopeTypeAstObject(t.Obj.Decl)
|
||||
} else {
|
||||
s.l.addPackageTypeNames(s.pkg, t.Name)
|
||||
}
|
||||
case *ast.StructType:
|
||||
for _, field := range t.Fields.List {
|
||||
s.addScopeTypeAstExpr(field.Type)
|
||||
}
|
||||
case *ast.IndexExpr:
|
||||
s.addScopeTypeAstExpr(t.X)
|
||||
s.addScopeTypeAstExpr(t.Index)
|
||||
case *ast.SelectorExpr:
|
||||
s.addScopeTypeAstSelectorExpr(t)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Package) addScopeTypeAstSelectorExpr(input *ast.SelectorExpr) {
|
||||
if x := TC[*ast.Ident](input.X); x != nil {
|
||||
if xPkgName := TC[*types.PkgName](s.pkg.TypesInfo.Uses[x]); xPkgName != nil {
|
||||
if selIdent := TC[*ast.Ident](input.Sel); selIdent != nil {
|
||||
for node, object := range s.pkg.TypesInfo.Implicits {
|
||||
if object == xPkgName {
|
||||
if nodeImportSepc := TC[*ast.ImportSpec](node); nodeImportSepc != nil {
|
||||
v := strings.Trim(nodeImportSepc.Path.Value, "\"")
|
||||
s.l.addPackageTypeNames(s.pkg.Imports[v], selIdent.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Package) addScopeTypeAstObject(input any) {
|
||||
switch t := input.(type) {
|
||||
case *ast.TypeSpec:
|
||||
s.addScopeTypeAstExpr(t.Type)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Package) addExprs(source map[*ast.Ident]types.Object) {
|
||||
for expr, object := range source {
|
||||
if object != nil {
|
||||
switch object.(type) {
|
||||
case *types.Func:
|
||||
s.exprs[object.Name()] = expr
|
||||
case *types.Const:
|
||||
s.exprs[object.Name()] = expr
|
||||
case *types.TypeName:
|
||||
s.exprs[object.Name()] = expr
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user