diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..c7afe1a --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,24 @@ +name: Release + +on: + push: + branches: + - '**' + + +jobs: + dagger-go-flow: + name: dagger-go-flow + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/Checkout@v3 + - name: Dagger Go Flow + uses: dagger/dagger-for-github@v6 + with: + version: "latest" + verb: call + module: github.com/felipecruz91/daggerverse/go + args: test --source=. --go-version=1.23 stdout + #args: --help + diff --git a/.releaserc.yaml b/.releaserc.yaml new file mode 100644 index 0000000..625af82 --- /dev/null +++ b/.releaserc.yaml @@ -0,0 +1,15 @@ +branches: + - "feat/update-generation-settings" +plugins: + - - "@semantic-release/commit-analyzer" + - preset: conventionalcommits + - - "@semantic-release/release-notes-generator" + - preset: conventionalcommits + - "@semantic-release/github" + - - "@semantic-release/changelog" + - assets: + - CHANGELOG.md + - - "@semantic-release/git" + - assets: + - CHANGELOG.md +tagFormat: ${version} diff --git a/Makefile b/Makefile index ddec718..638a54d 100644 --- a/Makefile +++ b/Makefile @@ -46,3 +46,8 @@ bench: task-example: go run examples/task/main.go + +### ACT ### +act: + act -P ubuntu-latest=catthehacker/ubuntu:act-latest + diff --git a/dagger.json b/dagger.json new file mode 100644 index 0000000..14d6017 --- /dev/null +++ b/dagger.json @@ -0,0 +1,23 @@ +{ + "name": "dagger", + "engineVersion": "v0.15.1", + "sdk": "go", + "dependencies": [ + { + "name": "node", + "source": "github.com/dagger/dagger/sdk/typescript/dev/node", + "pin": "180573cf2a1359a7ce371f92a6b6d5fdde62c5a4" + }, + { + "name": "goreleaser", + "source": "github.com/goreleaser/goreleaser@v2.4.8", + "pin": "377981ebd76e1bbb0dbe07d5428239ec8c5381a8" + }, + { + "name": "nsv", + "source": "github.com/purpleclay/daggerverse/nsv@nsv/v0.10.1", + "pin": "424af485b7e79437e0a0f799d083edd3e843128e" + } + ], + "source": "dagger" +} diff --git a/dagger/.gitattributes b/dagger/.gitattributes new file mode 100644 index 0000000..3a45493 --- /dev/null +++ b/dagger/.gitattributes @@ -0,0 +1,4 @@ +/dagger.gen.go linguist-generated +/internal/dagger/** linguist-generated +/internal/querybuilder/** linguist-generated +/internal/telemetry/** linguist-generated diff --git a/dagger/.gitignore b/dagger/.gitignore new file mode 100644 index 0000000..7ebabcc --- /dev/null +++ b/dagger/.gitignore @@ -0,0 +1,4 @@ +/dagger.gen.go +/internal/dagger +/internal/querybuilder +/internal/telemetry diff --git a/dagger/go.mod b/dagger/go.mod new file mode 100644 index 0000000..b97da30 --- /dev/null +++ b/dagger/go.mod @@ -0,0 +1,50 @@ +module dagger/dagger + +go 1.23.2 + +require ( + github.com/99designs/gqlgen v0.17.55 + github.com/Khan/genqlient v0.7.0 + github.com/vektah/gqlparser/v2 v2.5.17 + go.opentelemetry.io/otel v1.27.0 + go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.0.0-20240518090000-14441aefdf88 + go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.3.0 + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.27.0 + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.27.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0 + go.opentelemetry.io/otel/log v0.3.0 + go.opentelemetry.io/otel/metric v1.27.0 + go.opentelemetry.io/otel/sdk v1.27.0 + go.opentelemetry.io/otel/sdk/log v0.3.0 + go.opentelemetry.io/otel/sdk/metric v1.27.0 + go.opentelemetry.io/otel/trace v1.27.0 + go.opentelemetry.io/proto/otlp v1.3.1 + golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa + golang.org/x/sync v0.8.0 + google.golang.org/grpc v1.65.0 +) + +require ( + github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect + github.com/sosodev/duration v1.3.1 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0 // indirect + golang.org/x/net v0.29.0 // indirect + golang.org/x/sys v0.26.0 // indirect + golang.org/x/text v0.18.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect + google.golang.org/protobuf v1.34.2 // indirect +) + +replace go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc => go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.0.0-20240518090000-14441aefdf88 + +replace go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp => go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.3.0 + +replace go.opentelemetry.io/otel/log => go.opentelemetry.io/otel/log v0.3.0 + +replace go.opentelemetry.io/otel/sdk/log => go.opentelemetry.io/otel/sdk/log v0.3.0 diff --git a/dagger/go.sum b/dagger/go.sum new file mode 100644 index 0000000..c52dd24 --- /dev/null +++ b/dagger/go.sum @@ -0,0 +1,83 @@ +github.com/99designs/gqlgen v0.17.55 h1:3vzrNWYyzSZjGDFo68e5j9sSauLxfKvLp+6ioRokVtM= +github.com/99designs/gqlgen v0.17.55/go.mod h1:3Bq768f8hgVPGZxL8aY9MaYmbxa6llPM/qu1IGH1EJo= +github.com/Khan/genqlient v0.7.0 h1:GZ1meyRnzcDTK48EjqB8t3bcfYvHArCUUvgOwpz1D4w= +github.com/Khan/genqlient v0.7.0/go.mod h1:HNyy3wZvuYwmW3Y7mkoQLZsa/R5n5yIRajS1kPBvSFM= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +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/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I= +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.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= +github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= +github.com/sosodev/duration v1.3.1 h1:qtHBDMQ6lvMQsL15g4aopM4HEfOaYuhWBw3NPTtlqq4= +github.com/sosodev/duration v1.3.1/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg= +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= +github.com/vektah/gqlparser/v2 v2.5.17 h1:9At7WblLV7/36nulgekUgIaqHZWn5hxqluxrxGUhOmI= +github.com/vektah/gqlparser/v2 v2.5.17/go.mod h1:1lz1OeCqgQbQepsGxPVywrjdBHW2T08PUS3pJqepRww= +go.opentelemetry.io/otel v1.27.0 h1:9BZoF3yMK/O1AafMiQTVu0YDj5Ea4hPhxCs7sGva+cg= +go.opentelemetry.io/otel v1.27.0/go.mod h1:DMpAK8fzYRzs+bi3rS5REupisuqTheUlSZJ1WnZaPAQ= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.0.0-20240518090000-14441aefdf88 h1:oM0GTNKGlc5qHctWeIGTVyda4iFFalOzMZ3Ehj5rwB4= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.0.0-20240518090000-14441aefdf88/go.mod h1:JGG8ebaMO5nXOPnvKEl+DiA4MGwFjCbjsxT1WHIEBPY= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.3.0 h1:ccBrA8nCY5mM0y5uO7FT0ze4S0TuFcWdDB2FxGMTjkI= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.3.0/go.mod h1:/9pb6634zi2Lk8LYg9Q0X8Ar6jka4dkFOylBLbVQPCE= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.27.0 h1:bFgvUr3/O4PHj3VQcFEuYKvRZJX1SJDQ+11JXuSB3/w= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.27.0/go.mod h1:xJntEd2KL6Qdg5lwp97HMLQDVeAhrYxmzFseAMDPQ8I= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.27.0 h1:CIHWikMsN3wO+wq1Tp5VGdVRTcON+DmOJSfDjXypKOc= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.27.0/go.mod h1:TNupZ6cxqyFEpLXAZW7On+mLFL0/g0TE3unIYL91xWc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0 h1:R9DE4kQ4k+YtfLI2ULwX82VtNQ2J8yZmA7ZIF/D+7Mc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0/go.mod h1:OQFyQVrDlbe+R7xrEyDr/2Wr67Ol0hRUgsfA+V5A95s= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 h1:qFffATk0X+HD+f1Z8lswGiOQYKHRlzfmdJm0wEaVrFA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0 h1:QY7/0NeRPKlzusf40ZE4t1VlMKbqSNT7cJRYzWuja0s= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0/go.mod h1:HVkSiDhTM9BoUJU8qE6j2eSWLLXvi1USXjyd2BXT8PY= +go.opentelemetry.io/otel/log v0.3.0 h1:kJRFkpUFYtny37NQzL386WbznUByZx186DpEMKhEGZs= +go.opentelemetry.io/otel/log v0.3.0/go.mod h1:ziCwqZr9soYDwGNbIL+6kAvQC+ANvjgG367HVcyR/ys= +go.opentelemetry.io/otel/metric v1.27.0 h1:hvj3vdEKyeCi4YaYfNjv2NUje8FqKqUY8IlF0FxV/ik= +go.opentelemetry.io/otel/metric v1.27.0/go.mod h1:mVFgmRlhljgBiuk/MP/oKylr4hs85GZAylncepAX/ak= +go.opentelemetry.io/otel/sdk v1.27.0 h1:mlk+/Y1gLPLn84U4tI8d3GNJmGT/eXe3ZuOXN9kTWmI= +go.opentelemetry.io/otel/sdk v1.27.0/go.mod h1:Ha9vbLwJE6W86YstIywK2xFfPjbWlCuwPtMkKdz/Y4A= +go.opentelemetry.io/otel/sdk/log v0.3.0 h1:GEjJ8iftz2l+XO1GF2856r7yYVh74URiF9JMcAacr5U= +go.opentelemetry.io/otel/sdk/log v0.3.0/go.mod h1:BwCxtmux6ACLuys1wlbc0+vGBd+xytjmjajwqqIul2g= +go.opentelemetry.io/otel/sdk/metric v1.27.0 h1:5uGNOlpXi+Hbo/DRoI31BSb1v+OGcpv2NemcCrOL8gI= +go.opentelemetry.io/otel/sdk/metric v1.27.0/go.mod h1:we7jJVrYN2kh3mVBlswtPU22K0SA+769l93J6bsyvqw= +go.opentelemetry.io/otel/trace v1.27.0 h1:IqYb813p7cmbHk0a5y6pD5JPakbVfftRXABGt5/Rscw= +go.opentelemetry.io/otel/trace v1.27.0/go.mod h1:6RiD1hkAprV4/q+yd2ln1HG9GoPx39SuvvstaLBl+l4= +go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= +go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= +golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 h1:wKguEg1hsxI2/L3hUYrpo1RVi48K+uTyzKqprwLXsb8= +google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142/go.mod h1:d6be+8HhtEtucleCbxpPW9PA9XwISACu8nvpPqF0BVo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 h1:e7S5W7MGGLaSu8j3YjdezkZ+m1/Nm0uRVRMEMGk26Xs= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= +google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/dagger/main.go b/dagger/main.go new file mode 100644 index 0000000..6dbdf4d --- /dev/null +++ b/dagger/main.go @@ -0,0 +1,56 @@ +// A generated module for Dagger functions +// +// This module has been generated via dagger init and serves as a reference to +// basic module structure as you get started with Dagger. +// +// Two functions have been pre-created. You can modify, delete, or add to them, +// as needed. They demonstrate usage of arguments and return types using simple +// echo and grep commands. The functions can be called from the dagger CLI or +// from one of the SDKs. +// +// The first line in this comment block is a short description line and the +// rest is a long description with more detail on the module's purpose or usage, +// if appropriate. All modules should have a short description. + +package main + +import ( + "context" + "dagger/dagger/internal/dagger" +) + +type Dagger struct{} + +// Returns a container that echoes whatever string argument is provided +func (m *Dagger) Release(ctx context.Context, source *dagger.Directory) (string, error){ + + + return dag.Node(dagger.NodeOpts{ + Version: "21"}). + WithNpm(). + WithSource(source). + Container(). + WithExec([]string{"npm", "install","--save-dev","@semantic-release/git"}). + WithExec([]string{"npm","install","--save-dev","@semantic-release/changelog"}). + WithExec([]string{"npx","semantic-release"}). + Stdout(ctx) + + // Commands(). + // Run([]string{"npx", "semantic-release", "--version"}). + // Stdout(ctx) + + + + // version, err := dag.Nsv(source).Next(ctx, dagger.NsvNextOpts{Show: true}) + // if err != nil { + // return "", err + // } + // + // fmt.Println("Version: ", version) + // + // return dag.Goreleaser(dagger.GoreleaserOpts{Source: source}).Test().Output(ctx) + + + + +} diff --git a/dreflect/dreflect.go b/dreflect/dreflect.go index cfaa69a..3cc3e3b 100644 --- a/dreflect/dreflect.go +++ b/dreflect/dreflect.go @@ -3,11 +3,47 @@ package dreflect import ( + "crypto/sha256" "fmt" "reflect" "unsafe" ) +// GetTypeHash recursively generates a hash based on struct type +func GetTypeHash(v interface{}) string { + t := reflect.TypeOf(v) // Get type + + // If it's a pointer, dereference + if t.Kind() == reflect.Ptr { + t = t.Elem() + } + + // Base case: If it's not a struct, just return type name + if t.Kind() != reflect.Struct { + return t.String() + } + + // Construct type signature + typeSignature := t.String() + "{" + for i := 0; i < t.NumField(); i++ { + field := t.Field(i) + fieldType := field.Type + + // If field is a struct, recurse + if fieldType.Kind() == reflect.Struct { + typeSignature += field.Name + ":" + GetTypeHash( + reflect.New(fieldType).Elem().Interface(), + ) + ";" + } else { + typeSignature += field.Name + ":" + fieldType.String() + ";" + } + } + typeSignature += "}" + // Compute SHA-256 hash + hash := sha256.Sum256([]byte(typeSignature)) + return fmt.Sprintf("%x", hash) // Return as hex string +} + // GetPointerOfValueType returns a pointer to a new value of the same type as the input value `val`. // The new value is initialized to the input value. func GetPointerOfValueType(val any) any { diff --git a/dstruct.generated.struct.go b/dstruct.generated.struct.go index ea2099b..381090d 100644 --- a/dstruct.generated.struct.go +++ b/dstruct.generated.struct.go @@ -338,7 +338,7 @@ func (gs *DStructGeneratedStruct[T]) addCustomTypes() { func (gs *DStructGeneratedStruct[T]) AddCustomType(customType CustomType) { // TODO: restrict some types from being added such as nil, ints etc - gs.customTypes[reflect.TypeOf(customType.Value).String()] = customType.FunctionHolder + gs.customTypes[dreflect.GetTypeHash(customType.Value)] = customType.FunctionHolder // the function holder kind is the find that the function retrusn which could either be an existing kind or a new kind i.ie time.Time would be a new kind // TODO:idea: GenerationsFunction key should be type of CustomKind and not reflect.Kind gs.structConfig.GenerationFunctions[customType.FunctionHolder.Kind()] = customType.FunctionHolder @@ -353,7 +353,7 @@ func (gs *DStructGeneratedStruct[T]) createGeneratedField( field.data.tag, gs.structConfig.Copy(kind), gs.customTypes, - field.data.goType) + field.data.typeHash) return v } @@ -363,7 +363,7 @@ func (gs *DStructGeneratedStruct[T]) populateGeneratedFields(node *Node[StructFi continue } - if customType := gs.customTypes[field.data.goType]; customType != nil { + if customType := gs.customTypes[field.data.typeHash]; customType != nil { gs.fieldContexts[field.data.qualifiedName] = core.NewGeneratedFieldContext( gs.createGeneratedField(field, customType.Kind()), ) @@ -383,7 +383,7 @@ func (gs *DStructGeneratedStruct[T]) propagateConfig( ) { for _, field := range node.children { // Don't propagate changes to children nodes if the field is a custom type - if field.HasChildren() && gs.customTypes[field.data.goType] == nil { + if field.HasChildren() && gs.customTypes[field.data.typeHash] == nil { gs.propagateConfig(field, cfg) } else { gs.fieldContexts[field.data.qualifiedName].GeneratedField.SetConfig(cfg) @@ -396,7 +396,7 @@ func (gs *DStructGeneratedStruct[T]) propagateSettings( settings config.GenerationSettings, ) { for _, field := range node.children { - if field.HasChildren() && gs.customTypes[field.data.goType] == nil { + if field.HasChildren() && gs.customTypes[field.data.typeHash] == nil { gs.propagateSettings(field, settings) } else { gs.fieldContexts[field.data.qualifiedName].GeneratedField.SetGenerationSettings(settings) diff --git a/examples/builder/adding-a-field/main.go b/examples/builder/adding-a-field/main.go index a87c6ca..25eff42 100644 --- a/examples/builder/adding-a-field/main.go +++ b/examples/builder/adding-a-field/main.go @@ -57,7 +57,7 @@ func errorExample_2() { defer examples.RecoverFromPanic() builder := dstruct.NewBuilder(). AddField("Person", Person{Name: "Martin", Age: 25, Address: Address{Street: "Jackson Street"}}, `json:"person"`). - AddField("Job Titile", "Software Developer", "") + AddField("Job Title", "Software Developer", "") - builder.Build() // This will panic because the field "Job Titile" is an invalid struct field name + builder.Build() // This will panic because the field "Job Title" is an invalid struct field name } diff --git a/examples/task/main.go b/examples/task/main.go index fc08d6f..62f6103 100644 --- a/examples/task/main.go +++ b/examples/task/main.go @@ -99,28 +99,28 @@ type Test struct { } func main() { - generatedStuct := dstruct.NewGeneratedStructWithConfig( + generatedStruct := dstruct.NewGeneratedStructWithConfig( Test{Cpoint: new(int)}, config.NewDstructConfig().SetSliceLength(3, 3), config.DefaultGenerationSettings(), ) gt := NewGenInt32Task() generator.AddTask(gt) - if err := generatedStuct.Set("Person.P", Person{}); err != nil { + if err := generatedStruct.Set("Person.P", Person{}); err != nil { panic(err) } - generatedStuct.SetFieldGenerationConfig( + generatedStruct.SetFieldGenerationConfig( "Person.Value", config.NewDstructConfig().SetIntRange(800, 1000), ) gti := gt.Instance("20", "30").(*GenInt32TaskInstance) - generatedStuct.SetFieldFromTaskInstance("C", gti) - generatedStuct.Generate() + generatedStruct.SetFieldFromTaskInstance("C", gti) + generatedStruct.Generate() - fmt.Printf("%+v\n", generatedStuct) + fmt.Printf("%+v\n", generatedStruct) gti.SetParameters("100", "200") - generatedStuct.Generate() + generatedStruct.Generate() - fmt.Printf("%+v\n", generatedStuct) + fmt.Printf("%v\n", generatedStruct) } diff --git a/generator/config/generation.settings.go b/generator/config/generation.settings.go index 13a6a53..1298fa0 100644 --- a/generator/config/generation.settings.go +++ b/generator/config/generation.settings.go @@ -5,8 +5,12 @@ type ConfigType uint type ValueGenerationType uint8 const ( - Generate ValueGenerationType = iota // will generate all field - GenerateOnce // will generate the fields once + // Generate will generated all fields. + Generate ValueGenerationType = iota + // GenerateOnce will generate the fields once. + GenerateOnce + // UseDefaults will use the default value of the field based on the default tag. If the default tag is not set, the + // field will use the field type's default generation function to generate a value for the field. UseDefaults ) @@ -25,11 +29,6 @@ type GenerationSettings struct { RecursiveDefinition RecursiveDefinition } -func (gs *GenerationSettings) WithNonRequiredFields(required bool) GenerationSettings { - gs.SetNonRequiredFields = required - return *gs -} - // DefaultGenerationSettings returns a default configuration for generation values. func DefaultGenerationSettings() GenerationSettings { return GenerationSettings{ @@ -41,3 +40,28 @@ func DefaultGenerationSettings() GenerationSettings { }, } } + +// NewGenerationSettings returns a new instance of GenerationSettings. +func NewGenerationSettings() *GenerationSettings { + gs := DefaultGenerationSettings() + return &gs +} + +func (gs *GenerationSettings) WithNonRequiredFields(required bool) GenerationSettings { + gs.SetNonRequiredFields = required + return *gs +} + +func (gs *GenerationSettings) WithValueGenerationType( + valueGenerationType ValueGenerationType, +) GenerationSettings { + gs.ValueGenerationType = valueGenerationType + return *gs +} + +func (gs *GenerationSettings) WithRecursiveDefinition( + recursiveDefinition RecursiveDefinition, +) GenerationSettings { + gs.RecursiveDefinition = recursiveDefinition + return *gs +} diff --git a/generator/core/generated.field.go b/generator/core/generated.field.go index f362793..09f2adb 100644 --- a/generator/core/generated.field.go +++ b/generator/core/generated.field.go @@ -50,7 +50,7 @@ type GeneratedField struct { Parent *GeneratedField PointerValue *reflect.Value customTypes map[string]FunctionHolder - goType string + typeHash string currentGenerationFunction generator.GenerationFunction } @@ -59,7 +59,7 @@ func NewGeneratedField(fqn string, tag reflect.StructTag, config GeneratedFieldConfig, customTypes map[string]FunctionHolder, - goType string, + typeHash string, ) *GeneratedField { generateField := &GeneratedField{ Name: fqn, @@ -67,7 +67,7 @@ func NewGeneratedField(fqn string, Tag: tag, Config: config, customTypes: customTypes, - goType: goType, + typeHash: typeHash, } // TODO: add custom type to GenerationFunctions if value.Kind() == reflect.Slice { @@ -93,11 +93,11 @@ func NewGeneratedField(fqn string, } func (field *GeneratedField) IsCustomType() bool { - return field.customTypes[field.goType] != nil + return field.customTypes[field.typeHash] != nil } func (field *GeneratedField) customTypeFunctionHolder() FunctionHolder { - return field.customTypes[field.goType] + return field.customTypes[field.typeHash] } func (field *GeneratedField) SetConfig(cfg config.Config) { @@ -122,7 +122,7 @@ func (field *GeneratedField) SetGenerationFunction( functionHolder FunctionHolder, ) { if field.IsCustomType() { - field.customTypes[field.goType] = functionHolder + field.customTypes[field.typeHash] = functionHolder } else if field.Config.GenerationFunctions[field.Value.Kind()] != nil { field.Tag = reflect.StructTag("") // remove tags to ensure the field is generated with the new function field.Config.GenerationFunctions[field.Value.Kind()] = functionHolder @@ -167,7 +167,7 @@ func (field *GeneratedField) checkForRecursiveDefinition(fail bool) bool { func (field *GeneratedField) SetValue() bool { // check if the current field is a custom type with it's own generation function - if customType := field.customTypes[field.goType]; customType != nil { + if customType := field.customTypes[field.typeHash]; customType != nil { field.Value.Set(reflect.ValueOf(customType.GetFunction().Generate())) return false } @@ -205,7 +205,7 @@ func (field *GeneratedField) setStructValues() { Config: field.Config.Copy(field.Value.Field(j).Kind()), Parent: field, customTypes: field.customTypes, - goType: field.Value.Field(j).Type().Name(), + typeHash: field.Value.Field(j).Type().Name(), } structField.SetValue() } @@ -219,8 +219,8 @@ func (field *GeneratedField) getGenerationFunction() generator.GenerationFunctio } // check if field is a custom type with it's own generation function - if field.customTypes[field.goType] != nil { - return field.customTypes[field.goType].GetFunction() + if field.customTypes[field.typeHash] != nil { + return field.customTypes[field.typeHash].GetFunction() } kind := field.Value.Kind() diff --git a/generator/core/slice.go b/generator/core/slice.go index 3222f3b..03ba20b 100644 --- a/generator/core/slice.go +++ b/generator/core/slice.go @@ -4,6 +4,7 @@ import ( "fmt" "reflect" + "github.com/MartinSimango/dstruct/dreflect" "github.com/MartinSimango/dstruct/generator" "github.com/MartinSimango/dstruct/generator/config" ) @@ -40,7 +41,7 @@ func GenerateSliceFunc( Config: fieldConfig, Parent: field, customTypes: field.customTypes, - goType: elemValue.Type().Name(), + typeHash: dreflect.GetTypeHash(elemValue.Interface()), } newField.SetValue() diff --git a/struct.field.go b/struct.field.go index 59e9d71..03dee39 100644 --- a/struct.field.go +++ b/struct.field.go @@ -11,7 +11,7 @@ type StructField struct { tag reflect.StructTag value reflect.Value dstructType reflect.Type - goType string + typeHash string pkgPath string anonymous bool jsonName string @@ -45,9 +45,9 @@ func (f StructField) GetDstructType() reflect.Type { return f.dstructType } -// GetGoType returns the Go type of the field. -func (f StructField) GetGoType() string { - return f.goType +// GetTypeHash returns the hash of the type of the field. +func (f StructField) GetTypeHash() string { + return f.typeHash } // GetQualifiedName returns the fully qualified name of the field. diff --git a/tests/dstruct_test/tree.builder_test.go b/tests/dstruct_test/tree.builder_test.go index c1b60b6..17b929e 100644 --- a/tests/dstruct_test/tree.builder_test.go +++ b/tests/dstruct_test/tree.builder_test.go @@ -1,12 +1,14 @@ package dstruct_test import ( + "fmt" "reflect" "testing" + "github.com/stretchr/testify/assert" + "github.com/MartinSimango/dstruct" "github.com/MartinSimango/dstruct/dreflect" - "github.com/stretchr/testify/assert" ) type TestExtendData struct { @@ -17,7 +19,6 @@ type TestExtendData struct { } func TestExtend(t *testing.T) { - type Embedded struct { Name string } @@ -43,7 +44,7 @@ func TestExtend(t *testing.T) { Name string unexportedStruct } - var tests = []TestExtendData{ + tests := []TestExtendData{ {"ExtendInt", 2, true, nil}, {"ExtendString", "hello", true, nil}, {"ExtendReflectValue", reflect.ValueOf(2), true, reflect.ValueOf(2)}, @@ -53,7 +54,12 @@ func TestExtend(t *testing.T) { {"ExtendStruct", testStruct1{Age: 20}, false, testStruct1{Age: 20}}, {"ExtendPointerToStruct", &testStruct1{Age: 20}, false, &testStruct1{Age: 20}}, {"ExtendStructWithEmbeddedField", testStructEmbedded{}, false, testStructEmbedded{}}, - {"ExtendStructWithUnexportedEmbeddedField", testStructUnexportedEmbedded{}, true, testStructUnexportedEmbedded{}}, + { + "ExtendStructWithUnexportedEmbeddedField", + testStructUnexportedEmbedded{}, + true, + testStructUnexportedEmbedded{}, + }, } assert := assert.New(t) @@ -65,11 +71,11 @@ func TestExtend(t *testing.T) { }() dynamicStruct := dstruct.ExtendStruct(test.input).Build().Instance() - dynamicStructConverted := dreflect.Convert(reflect.ValueOf(dynamicStruct), reflect.TypeOf(test.expected)).Interface() + dynamicStructConverted := dreflect.Convert(reflect.ValueOf(dynamicStruct), reflect.TypeOf(test.expected)). + Interface() assert.EqualValues(test.expected, dynamicStructConverted) }) } - } type TestAddFieldData struct { @@ -88,7 +94,10 @@ func TestAddField(t *testing.T) { instance := b.Build().Instance() - assert.EqualValues(expected, dreflect.Convert(reflect.ValueOf(instance), reflect.TypeOf(expected)).Interface()) + assert.EqualValues( + expected, + dreflect.Convert(reflect.ValueOf(instance), reflect.TypeOf(expected)).Interface(), + ) } func TestAddEmbeddedField(t *testing.T) { @@ -108,8 +117,10 @@ func TestAddEmbeddedField(t *testing.T) { instance := b.Build().Instance() - assert.EqualValues(expected, dreflect.Convert(reflect.ValueOf(instance), reflect.TypeOf(expected)).Interface()) - + assert.EqualValues( + expected, + dreflect.Convert(reflect.ValueOf(instance), reflect.TypeOf(expected)).Interface(), + ) } func TestGetField(t *testing.T) { @@ -131,10 +142,19 @@ func TestGetField(t *testing.T) { instance := b.GetField("Person").AddField("Cool", 2, "").Build() - assert.EqualValues(expected, dreflect.Convert(reflect.ValueOf(instance.Instance()), reflect.TypeOf(expected)).Interface()) - // Original builder must also be altered and have new field - assert.EqualValues(expected, dreflect.Convert(reflect.ValueOf(b.GetField("Person").Build().Instance()), reflect.TypeOf(expected)).Interface()) + fmt.Println(b.Build().GetFields()) + assert.EqualValues( + expected, + dreflect.Convert(reflect.ValueOf(instance.Instance()), reflect.TypeOf(expected)). + Interface(), + ) + // Original builder must also be altered and have new field + assert.EqualValues( + expected, + dreflect.Convert(reflect.ValueOf(b.GetField("Person").Build().Instance()), reflect.TypeOf(expected)). + Interface(), + ) } // GetNewBuilderFromField returns a new builder instance where the subfield of the struct "field" is the root of the struct. @@ -163,10 +183,17 @@ func TestNewBuilderFromField(t *testing.T) { instance := b.NewBuilderFromField("Person").AddField("Cool", 2, "").Build() - assert.EqualValues(expected, dreflect.Convert(reflect.ValueOf(instance.Instance()), reflect.TypeOf(expected)).Interface()) + assert.EqualValues( + expected, + dreflect.Convert(reflect.ValueOf(instance.Instance()), reflect.TypeOf(expected)). + Interface(), + ) // Original builder must NOT be altered and have new field - assert.EqualValues(expectedOld, dreflect.Convert(reflect.ValueOf(b.GetField("Person").Build().Instance()), reflect.TypeOf(expectedOld)).Interface()) - + assert.EqualValues( + expectedOld, + dreflect.Convert(reflect.ValueOf(b.GetField("Person").Build().Instance()), reflect.TypeOf(expectedOld)). + Interface(), + ) } func TestRemoveField(t *testing.T) { @@ -189,5 +216,8 @@ func TestRemoveField(t *testing.T) { instance := b.Build().Instance() - assert.EqualValues(expected, dreflect.Convert(reflect.ValueOf(instance), reflect.TypeOf(expected)).Interface()) + assert.EqualValues( + expected, + dreflect.Convert(reflect.ValueOf(instance), reflect.TypeOf(expected)).Interface(), + ) } diff --git a/tree.builder.go b/tree.builder.go index 3188ec2..e5a7f86 100644 --- a/tree.builder.go +++ b/tree.builder.go @@ -82,25 +82,38 @@ func resetNodeFieldsFQN(node *Node[StructField]) *Node[StructField] { } func (dsb *treeBuilderImpl) AddField(name string, value interface{}, tag string) Builder { - if dsb.root.HasChild(name) { + dsb.addField(false, name, value, tag, dsb.root) + return dsb +} + +func (dsb *treeBuilderImpl) AddEmbeddedField(value interface{}, tag string) Builder { + ptrValue, _ := getPtrValue(reflect.ValueOf(value), 0) + name := reflect.TypeOf(ptrValue.Interface()).Name() + + dsb.addField(true, name, value, tag, dsb.root) + + return dsb +} + +func (dsb *treeBuilderImpl) addField( + anonymous bool, + name string, + value interface{}, + tag string, + root *Node[StructField], +) { + if root.HasChild(name) { panic(fmt.Sprintf("Field '%s' already exists", name)) } + dsb.root.data.dstructType = dsb.addFieldToTree( name, value, "", - false, + anonymous, reflect.StructTag(tag), dsb.root, ) - return dsb -} - -func (dsb *treeBuilderImpl) AddEmbeddedField(value interface{}, tag string) Builder { - ptrValue, _ := getPtrValue(reflect.ValueOf(value), 0) - name := reflect.TypeOf(ptrValue.Interface()).Name() - dsb.addFieldToTree(name, value, "", true, reflect.StructTag(tag), dsb.root) - return dsb } func (dsb *treeBuilderImpl) RemoveField(name string) Builder { @@ -133,9 +146,7 @@ func (dsb *treeBuilderImpl) GetField(field string) Builder { } else if node.data.dstructType.Kind() != reflect.Struct { panic(fmt.Sprintf("Cannot get field '%s' because it is not a struct or does not fully derefence to a struct value", field)) } - // if node.data.ptrKind != reflect.Struct { - // node. / return newBuilderFromNode(node, false) } panic(fmt.Sprintf("Field '%s' does not exist", field)) @@ -214,17 +225,12 @@ func (dsb *treeBuilderImpl) addFieldToTree( *root.data.numberOfSubFields++ } - var goType string - if goType = tag.Get("goType"); goType == "" { - goType = value.Type().Name() - } - field := &StructField{ name: name, value: value, // this will initally be unaddressable until the struct is built tag: tag, dstructType: value.Type(), - goType: goType, + typeHash: dreflect.GetTypeHash(typ), pkgPath: pkgPath, anonymous: anonymous, jsonName: strings.Split(tag.Get("json"), ",")[0], @@ -245,7 +251,9 @@ func (dsb *treeBuilderImpl) addFieldToTree( field.dstructType = dsb.addPtrField(value, root.children[name]) } - return field.dstructType + // return field.dstructType + // We need to rebuild the stryct to get it's new type as a result of adding a new field + return reflect.TypeOf(dsb.Build().Instance()) } func sortKeys(root *Node[StructField]) (keys []string) { @@ -365,14 +373,6 @@ func (dsb *treeBuilderImpl) addStructFields( fieldName := strct.Type().Field(i).Name fieldTag := strct.Type().Field(i).Tag - if fieldTag.Get("goType") == "" { - fieldTag = (reflect.StructTag)(strings.TrimSpace(fmt.Sprintf( - "%s goType:\"%s\"", - fieldTag, - strct.Field(i).Type(), - ))) - } - var fieldValue any if strct.Type().Field(i).IsExported() { fieldValue = strct.Field(i).Interface() @@ -399,6 +399,8 @@ func (dsb *treeBuilderImpl) addStructFields( } + // If the struct is a pointer, we need to create a pointer to the struct + retStruct := reflect.New(reflect.StructOf(structFields)).Elem() for i := 0; i < ptrDepth; i++ { retStruct = reflect.New(retStruct.Type())