diff --git a/CHANGES.md b/CHANGES.md index 045438c..a992e00 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,8 @@ +### v0.9.0 + + - adds Break(key,value,...) for temporary runtime inspection. + - add optional filename to Dump() + ### v0.8.1 - Explore replaces object with same root label. diff --git a/README.md b/README.md index 77b5463..3804b4f 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,14 @@ Note: if the list contains just one structural value then selecting it can be sk ## explore while debugging +### Break + +The following instruction will start the explorer on a struct, opens a Browser and provides a `resume` button to stop the explorer and resume the Go-routine that started it. + + structexplorer.Break("myStruct", myStruct) + +### Dump + Currently, the standard Go debugger `delve` stops all goroutines while in a debugging session. This means that if you have started the `structexplorer` service in your program, it will not respond to any HTTP requests during that session. @@ -57,10 +65,14 @@ The explorer can also be asked to dump an HTML page with the current state of va s := structexplorer.NewService() s.Explore("yours", yourStruct) s.ExplorePath("yours.field") // dotted path of fields starting with an explore label - s.Dump() + s.Dump() + // or s.Dump("yourfile.html") Another method is to use a special test case which starts an explorer at the end of a test and then run it with a longer acceptable timeout. ## examples See folder `examples` for simple programs demonstrating each feature. + + +© 2025. https://ernestmicklei.com. MIT License \ No newline at end of file diff --git a/examples/break/main.go b/examples/break/main.go new file mode 100644 index 0000000..8c280c2 --- /dev/null +++ b/examples/break/main.go @@ -0,0 +1,20 @@ +package main + +import ( + "log" + + "github.com/emicklei/structexplorer" +) + +// go run . +func main() { + greeting := map[string]any{} + hello := struct{ Field string }{Field: "hello"} + greeting["hi"] = hello + + log.Println("before opening the explorer to see state") + + structexplorer.Break("map", greeting) + + log.Println("after opening the explorer to see state") +} diff --git a/examples/break/main_test.go b/examples/break/main_test.go new file mode 100644 index 0000000..7a93611 --- /dev/null +++ b/examples/break/main_test.go @@ -0,0 +1,22 @@ +package main + +import ( + "log" + "testing" + + "github.com/emicklei/structexplorer" +) + +func TestWithBreak(t *testing.T) { + target := struct{ Field string }{Field: "hello"} + + log.Println("before opening the explorer to see state") + + structexplorer.Break("debugging", target) + + log.Println("after opening the explorer to see state") + + target.Field = "world" + + structexplorer.Break("debugging", target) +} diff --git a/explore_options.go b/explore_options.go index 37ff392..9fe30df 100644 --- a/explore_options.go +++ b/explore_options.go @@ -20,7 +20,7 @@ func RowColumn(row, column int) ExploreOption { func Column(column int) ExploreOption { return ExploreOption{ placement: func(e *explorer) (newRow, newColumn int) { - return e.nextFreeRow(column) + 1, column + return e.nextFreeRow(column), column }, } } @@ -29,7 +29,7 @@ func Column(column int) ExploreOption { func Row(row int) ExploreOption { return ExploreOption{ placement: func(e *explorer) (newRow, newColumn int) { - return row, e.nextFreeColumn(row) + 1 + return row, e.nextFreeColumn(row) }, } } diff --git a/explorer.go b/explorer.go index 14010c6..cee7a11 100644 --- a/explorer.go +++ b/explorer.go @@ -32,17 +32,17 @@ type explorer struct { } func (e *explorer) nextFreeColumn(row int) int { - max := 0 cols, ok := e.accessMap[row] if !ok { return 0 } + max := -1 for col := range cols { if col > max { max = col } } - return max + return max + 1 } func (e *explorer) nextFreeRow(column int) int { @@ -54,7 +54,13 @@ func (e *explorer) nextFreeRow(column int) int { return row } } - return 0 + max := -1 + for row := range e.accessMap { + if row > max { + max = row + } + } + return max + 1 } func (e *explorer) rootKeys() (list []string) { @@ -164,6 +170,9 @@ func (e *explorer) putObjectStartingAt(row, col int, access objectAccess, option } func (e *explorer) buildIndexData(b *indexDataBuilder) indexData { + // was it starting using Break? + b.data.IsBreaking = b.isBreaking + for row, each := range e.accessMap { for col, access := range each { info := b.build(row, col, access) diff --git a/explorer_test.go b/explorer_test.go index d189e76..74d0454 100644 --- a/explorer_test.go +++ b/explorer_test.go @@ -5,6 +5,27 @@ import ( "time" ) +func TestExplorerFreeColumn(t *testing.T) { + x := newExplorerOnAll() + r := x.nextFreeRow(0) + if got, want := r, 0; got != want { + t.Errorf("got [%[1]v:%[1]T] want [%[2]v:%[2]T]", got, want) + } + c := x.nextFreeColumn(0) + if got, want := c, 0; got != want { + t.Errorf("got [%[1]v:%[1]T] want [%[2]v:%[2]T]", got, want) + } + x.putObjectStartingAt(1, 1, objectAccess{}, Row(0)) + r = x.nextFreeRow(0) + if got, want := r, 1; got != want { + t.Errorf("got [%[1]v:%[1]T] want [%[2]v:%[2]T]", got, want) + } + c = x.nextFreeColumn(1) + if got, want := c, 2; got != want { + t.Errorf("got [%[1]v:%[1]T] want [%[2]v:%[2]T]", got, want) + } +} + func TestExplorer(t *testing.T) { x := newExplorerOnAll("indexData", indexData{}) d := x.buildIndexData(newIndexDataBuilder()) @@ -57,7 +78,7 @@ func TestExplorerTable(t *testing.T) { if o2.object != o3.object { t.Fail() } - if got, want := x.nextFreeColumn(1), 1; got != want { + if got, want := x.nextFreeColumn(1), 2; got != want { t.Errorf("got [%v]:%T want [%v]:%T", got, got, want, want) } if !x.canRemoveObjectAt(1, 1) { diff --git a/index_builder.go b/index_builder.go index c622227..d90f452 100644 --- a/index_builder.go +++ b/index_builder.go @@ -10,10 +10,11 @@ import ( ) type indexDataBuilder struct { - data indexData - seq int - notLive bool - selectID string // id of the added fieldList (select element) + data indexData + seq int + notLive bool + isBreaking bool // service is started with Break(...) + selectID string // id of the added fieldList (select element) } func newIndexDataBuilder() *indexDataBuilder { diff --git a/index_data.go b/index_data.go index 70d1b0f..ae4cb15 100644 --- a/index_data.go +++ b/index_data.go @@ -14,9 +14,10 @@ var styleCSS string type ( indexData struct { - Rows []tableRow - Script template.JS - Style template.CSS + Rows []tableRow + Script template.JS + Style template.CSS + IsBreaking bool } tableRow struct { Cells []fieldList diff --git a/index_tmpl.html b/index_tmpl.html index 0503364..beaa2c7 100644 --- a/index_tmpl.html +++ b/index_tmpl.html @@ -121,6 +121,11 @@ 🔄 + {{- if .IsBreaking }} + + {{- end }}