diff --git a/app/cmd/main.go b/app/cmd/main.go index ae33a50..4655d24 100644 --- a/app/cmd/main.go +++ b/app/cmd/main.go @@ -3,7 +3,6 @@ package main import ( "flag" "fmt" - "github.com/fthvgb1/wp-go/app/cmd/route" "github.com/fthvgb1/wp-go/app/mail" "github.com/fthvgb1/wp-go/app/pkg/cache" "github.com/fthvgb1/wp-go/app/pkg/config" @@ -11,6 +10,7 @@ import ( "github.com/fthvgb1/wp-go/app/pkg/logs" "github.com/fthvgb1/wp-go/app/plugins" "github.com/fthvgb1/wp-go/app/plugins/wphandle" + "github.com/fthvgb1/wp-go/app/route" "github.com/fthvgb1/wp-go/app/theme" "github.com/fthvgb1/wp-go/app/wpconfig" "github.com/fthvgb1/wp-go/cache/cachemanager" @@ -117,7 +117,7 @@ func reloads() { err = wpconfig.InitTerms() logs.IfError(err, "获取WpTerms表失败") wphandle.LoadPlugins() - reload.Reload() + reload.Reloads("themeArgAndConfig") flushCache() log.Println("reload complete") } diff --git a/app/cmd/sitetest/main.go b/app/cmd/sitetest/main.go new file mode 100644 index 0000000..ac4f218 --- /dev/null +++ b/app/cmd/sitetest/main.go @@ -0,0 +1,111 @@ +package main + +import ( + "flag" + "fmt" + "github.com/fthvgb1/wp-go/helper/httptool" + strings2 "github.com/fthvgb1/wp-go/helper/strings" + "github.com/fthvgb1/wp-go/safety" + "github.com/fthvgb1/wp-go/taskPools" + "net/http" + "os" + "regexp" + "strings" + "sync" +) + +var reg = regexp.MustCompile(``) +var m = safety.NewMap[string, bool]() + +var mut = sync.Mutex{} + +func parseHtml(ss string) { + r := reg.FindAllStringSubmatch(ss, -1) + + for _, href := range r { + if href[1] == "/" { + continue + } + if strings.ContainsAny(href[1], ".") { + continue + } + if string([]rune(href[1])[0:3]) == "http" { + continue + } + mut.Lock() + if _, ok := m.Load(href[1]); !ok { + m.Store(href[1], false) + } + mut.Unlock() + } +} + +func siteFetch(c int, u string) { + u = strings.TrimRight(u, "/") + ss, code, err := httptool.GetString(u, nil) + if err != nil || code != http.StatusOK { + panic(err) + } + parseHtml(ss) + + p := taskPools.NewPools(c) + for { + m.Range(func(key string, value bool) bool { + if value { + return true + } + u := strings2.Join(u, key) + p.Execute(func() { + ss, code, err := httptool.GetString(u, nil) + fmt.Println(u, code) + if err != nil || code != http.StatusOK { + panic(err) + return + } + parseHtml(ss) + m.Store(key, true) + }) + return true + }) + var x bool + m.Range(func(key string, value bool) bool { + if !value { + x = true + return false + } + return true + }) + if !x { + break + } + } + p.Wait() + m.Flush() +} + +var c int +var u string +var t int + +func main() { + flag.IntVar(&c, "c", 10, "concurrency num") + flag.StringVar(&u, "url", "http://127.0.0.1:8081", "test url") + flag.IntVar(&t, "t", 1, "request full site times") + flag.Parse() + if u == "" { + fmt.Println("url can't emtpy") + os.Exit(2) + } + if c < 1 { + fmt.Println("concurrency num must >= 1") + os.Exit(2) + } + if t < 1 { + for { + siteFetch(c, u) + } + } + for i := 0; i < t; i++ { + siteFetch(c, u) + } +} diff --git a/app/cmd/route/route.go b/app/route/route.go similarity index 100% rename from app/cmd/route/route.go rename to app/route/route.go diff --git a/app/theme/templateFuncs.go b/app/theme/templateFuncs.go index 60b4f1a..e7529f2 100644 --- a/app/theme/templateFuncs.go +++ b/app/theme/templateFuncs.go @@ -25,6 +25,9 @@ var comFn = template.FuncMap{ "exec": func(fn func() string) template.HTML { return template.HTML(fn()) }, + "callFuncString": func(fn func(string) string, s string) template.HTML { + return template.HTML(fn(s)) + }, } func postsFn(fn func(models.Posts) string, a models.Posts) string { diff --git a/app/theme/twentyfifteen/twentyfifteen.go b/app/theme/twentyfifteen/twentyfifteen.go index cb40476..2dd146e 100644 --- a/app/theme/twentyfifteen/twentyfifteen.go +++ b/app/theme/twentyfifteen/twentyfifteen.go @@ -3,11 +3,13 @@ package twentyfifteen import ( "github.com/fthvgb1/wp-go/app/pkg/constraints" "github.com/fthvgb1/wp-go/app/pkg/constraints/widgets" + "github.com/fthvgb1/wp-go/app/pkg/models" "github.com/fthvgb1/wp-go/app/plugins" "github.com/fthvgb1/wp-go/app/theme/wp" "github.com/fthvgb1/wp-go/app/theme/wp/components" "github.com/fthvgb1/wp-go/app/theme/wp/middleware" "github.com/fthvgb1/wp-go/app/wpconfig" + "github.com/fthvgb1/wp-go/cache/reload" "strings" ) @@ -28,7 +30,7 @@ func configs(h *wp.Handle) { h.PushCacheGroupHeadScript(constraints.AllScene, "colorSchemeCss", 10.0056, colorSchemeCss) h.CommonComponents() components.WidgetArea(h) - h.SetData("customHeader", customHeader(h)) + h.PushRender(constraints.AllScene, wp.NewHandleFn(renderCustomHeader, 20.5, "renderCustomHeader")) wp.PushIndexHandler(constraints.PipeRender, h, wp.NewHandleFn(wp.IndexRender, 50.005, "wp.IndexRender")) h.PushRender(constraints.Detail, wp.NewHandleFn(wp.DetailRender, 50.005, "wp.DetailRender")) h.PushRender(constraints.Detail, wp.NewHandleFn(postThumb, 60.005, "postThumb")) @@ -54,6 +56,16 @@ func setPaginationAndRender(h *wp.Handle) { func postThumb(h *wp.Handle) { d := h.GetDetailHandle() if d.Post.Thumbnail.Path != "" { - d.Post.Thumbnail = wpconfig.Thumbnail(d.Post.Thumbnail.OriginAttachmentData, "post-thumbnail", "") + d.Post.Thumbnail = getPostThumbs(d.Post.Id, d.Post) } } + +var getPostThumbs = reload.BuildMapFn[uint64]("twentyFifteen-post-thumbnail", postThumbs) + +func postThumbs(post models.Posts) models.PostThumbnail { + return wpconfig.Thumbnail(post.Thumbnail.OriginAttachmentData, "post-thumbnail", "") +} + +func renderCustomHeader(h *wp.Handle) { + h.SetData("customHeader", customHeader(h)) +} diff --git a/app/theme/twentyseventeen/twentyseventeen.go b/app/theme/twentyseventeen/twentyseventeen.go index 716130d..74e3785 100644 --- a/app/theme/twentyseventeen/twentyseventeen.go +++ b/app/theme/twentyseventeen/twentyseventeen.go @@ -112,13 +112,19 @@ func errorsHandle(h *wp.Handle) { func postThumb(h *wp.Handle) { d := h.GetDetailHandle() if d.Post.Thumbnail.Path != "" { - img := wpconfig.Thumbnail(d.Post.Thumbnail.OriginAttachmentData, "full", "", "thumbnail", "post-thumbnail") - img.Sizes = "100vw" - img.Srcset = fmt.Sprintf("%s %dw, %s", img.Path, img.Width, img.Srcset) - d.Post.Thumbnail = img + d.Post.Thumbnail = getPostThumbs(d.Post.Id, d.Post) } } +var getPostThumbs = reload.BuildMapFn[uint64]("post-thumbnail", postThumbs) + +func postThumbs(post models.Posts) models.PostThumbnail { + img := wpconfig.Thumbnail(post.Thumbnail.OriginAttachmentData, "full", "", "thumbnail", "post-thumbnail") + img.Sizes = "100vw" + img.Srcset = fmt.Sprintf("%s %dw, %s", img.Path, img.Width, img.Srcset) + return img +} + var commentFormat = comment{} type comment struct { diff --git a/app/theme/wp/components.go b/app/theme/wp/components.go index 52e6368..1009a74 100644 --- a/app/theme/wp/components.go +++ b/app/theme/wp/components.go @@ -11,52 +11,59 @@ import ( ) var handleComponents = safety.NewMap[string, map[string][]Components[string]]() -var handleComponentHook = safety.NewMap[string, []func(Components[string]) (Components[string], bool)]() +var handleComponentHook = safety.NewMap[string, map[string][]func(Components[string]) (Components[string], bool)]() var componentsArgs = safety.NewMap[string, any]() var componentFilterFns = safety.NewMap[string, []func(*Handle, string, ...any) string]() -func (h *Handle) DeleteComponents(scene, name string) { - v, _ := handleComponentHook.Load(scene) - v = append(v, func(c Components[string]) (Components[string], bool) { - return c, c.Name != name +func (h *Handle) DeleteComponents(scene, componentKey, componentName string) { + v, ok := handleComponentHook.Load(scene) + if !ok { + v = make(map[string][]func(Components[string]) (Components[string], bool)) + } + + v[componentKey] = append(v[componentKey], func(c Components[string]) (Components[string], bool) { + return c, c.Name != componentName }) handleComponentHook.Store(scene, v) } -func (h *Handle) ReplaceComponents(scene, name string, components Components[string]) { - v, _ := handleComponentHook.Load(scene) - v = append(v, func(c Components[string]) (Components[string], bool) { - if c.Name == name { +func (h *Handle) ReplaceComponents(scene, componentKey, componentName string, components Components[string]) { + v, ok := handleComponentHook.Load(scene) + if !ok { + v = make(map[string][]func(Components[string]) (Components[string], bool)) + } + v[componentKey] = append(v[componentKey], func(c Components[string]) (Components[string], bool) { + if c.Name == componentName { c = components } return c, true }) handleComponentHook.Store(scene, v) } -func (h *Handle) HookComponents(scene string, fn func(Components[string]) (Components[string], bool)) { - v, _ := handleComponentHook.Load(scene) - v = append(v, fn) +func (h *Handle) PushComponentHooks(scene, componentKey string, fn func(Components[string]) (Components[string], bool)) { + v, ok := handleComponentHook.Load(scene) + if !ok { + v = make(map[string][]func(Components[string]) (Components[string], bool)) + } + v[componentKey] = append(v[componentKey], fn) handleComponentHook.Store(scene, v) } -var GetComponents = reload.BuildMapFn[string]("scene-components", getComponent) -var HookComponents = reload.BuildMapFnWithAnyParams[string]("calComponents", hookComponent) +var GetAndHookComponents = reload.BuildMapFnWithAnyParams[string]("calComponents", HookComponent) -func hookComponent(a ...any) []Components[string] { - k := a[0].(string) - components := a[1].([]Components[string]) +func HookComponent(a ...any) []Components[string] { + componentKey := a[0].(string) + scene := a[1].(string) mut := reload.GetGlobeMutex() mut.Lock() - allHooks := slice.FilterAndToMap(components, func(t Components[string], _ int) (string, []func(Components[string]) (Components[string], bool), bool) { - fn, ok := handleComponentHook.Load(k) - return k, fn, ok - }) - mut.Unlock() + defer mut.Unlock() + components := GetComponents(scene, componentKey) r := slice.FilterAndMap(components, func(component Components[string]) (Components[string], bool) { - hooks, ok := allHooks[k] + keyHooks, ok := handleComponentHook.Load(scene) if !ok { return component, true } + hooks := keyHooks[componentKey] for _, fn := range hooks { hookedComponent, ok := fn(component) if !ok { // DeleteComponents fn @@ -72,35 +79,23 @@ func hookComponent(a ...any) []Components[string] { return r } -func getComponent(h *Handle) map[string][]Components[string] { - mut := reload.GetGlobeMutex() - mut.Lock() - sceneComponents, _ := handleComponents.Load(h.scene) +func GetComponents(scene, key string) (r []Components[string]) { + sceneComponents, _ := handleComponents.Load(scene) allSceneComponents, _ := handleComponents.Load(constraints.AllScene) - - mut.Unlock() - return maps.MergeBy(func(k string, c1, c2 []Components[string]) ([]Components[string], bool) { - vv := append(c1, c2...) - return vv, vv != nil - }, nil, sceneComponents, allSceneComponents) + r = append(sceneComponents[key], allSceneComponents[key]...) + return } -type cacheComponentParm[T any] struct { - Components[T] - h *Handle +var CacheComponent = reload.BuildMapFnWithAnyParams[string]("cacheComponents", cacheComponentFn) + +func cacheComponentFn(a ...any) string { + return a[0].(Components[string]).Fn(a[1].(*Handle)) } -var cacheComponentsFn = reload.BuildMapFn[string]("cacheComponents", cacheComponentFn) - -func cacheComponentFn(a cacheComponentParm[string]) string { - return a.Fn(a.h) -} - -func CalComponents(h *Handle) { - allComponents := GetComponents(str.Join("allScene-", h.scene), h) - for k, components := range allComponents { - key := str.Join("calComponents-", h.theme, "-", h.scene, "-", k) - hookedComponents := HookComponents(key, k, components) +func CalComponent(h *Handle) func(string) string { + return func(componentKey string) string { + cacheKey := str.Join("get-hook-Components-", h.scene, "-", componentKey) + hookedComponents := GetAndHookComponents(cacheKey, componentKey, h.scene) var s = make([]string, 0, len(hookedComponents)) for _, component := range hookedComponents { if component.Val != "" { @@ -110,7 +105,8 @@ func CalComponents(h *Handle) { if component.Fn != nil { v := "" if component.Cached { - v = cacheComponentsFn(component.Name, cacheComponentParm[string]{component, h}) + key := str.Join(h.scene, "-", componentKey, "-", component.Name) + v = CacheComponent(key, component, h) } else { v = component.Fn(h) } @@ -119,7 +115,7 @@ func CalComponents(h *Handle) { } } } - h.ginH[k] = strings.Join(s, "\n") + return strings.Join(s, "\n") } } diff --git a/app/theme/wp/components/widget/archive.go b/app/theme/wp/components/widget/archive.go index 6a2ad37..bd1bd74 100644 --- a/app/theme/wp/components/widget/archive.go +++ b/app/theme/wp/components/widget/archive.go @@ -41,7 +41,7 @@ var archivesConfig = map[any]any{ } var GetArchiveConf = BuildconfigFn(archivesConfig, "widget_archives", int64(2)) -var GetArchiveArgs = reload.BuildValFnWithAnyParams("", archiveArgsFn) +var GetArchiveArgs = reload.BuildValFnWithAnyParams("widget_archive-args", archiveArgsFn) func archiveArgsFn(a ...any) map[string]string { h := a[0].(*wp.Handle) diff --git a/app/theme/wp/template.gohtml b/app/theme/wp/template.gohtml index eebb3ae..0844ac3 100644 --- a/app/theme/wp/template.gohtml +++ b/app/theme/wp/template.gohtml @@ -1,8 +1,6 @@ {{define "common/head"}} - {{if .headScript}} - {{.headScript|unescaped}} - {{end}} + {{ callFuncString .calComponent "headScript"}} {{if .externHead}} {{.externHead|unescaped}} @@ -10,9 +8,7 @@ {{end}} {{define "common/footer"}} - {{if .footerScript}} - {{.footerScript|unescaped}} - {{end}} + {{ callFuncString .calComponent "footerScript"}} {{if .externFooter}} {{.externFooter|unescaped}} {{end}} @@ -20,9 +16,7 @@ {{define "common/sidebarWidget"}} - {{if .sidebarsWidgets}} - {{.sidebarsWidgets|unescaped}} - {{end}} + {{ callFuncString .calComponent "sidebarsWidgets"}} {{end}} {{define "common/colophon"}} diff --git a/app/theme/wp/wp.go b/app/theme/wp/wp.go index 1d7b37a..4b2f3ec 100644 --- a/app/theme/wp/wp.go +++ b/app/theme/wp/wp.go @@ -56,7 +56,7 @@ func (h *Handle) Components() *safety.Map[string, map[string][]Components[string return handleComponents } -func (h *Handle) ComponentHook() *safety.Map[string, []func(Components[string]) (Components[string], bool)] { +func (h *Handle) ComponentHook() *safety.Map[string, map[string][]func(Components[string]) (Components[string], bool)] { return handleComponentHook } @@ -99,9 +99,6 @@ func SetConfigHandle(a ...any) Handle { configFn := a[0].(func(*Handle)) hh := a[1].(*Handle) h := &Handle{} - mut := reload.GetGlobeMutex() - mut.Lock() - defer mut.Unlock() handleComponents.Flush() componentsArgs.Flush() handleComponentHook.Flush() @@ -120,10 +117,11 @@ func SetConfigHandle(a ...any) Handle { if ok { pluginFn(h) } + reload.Reload() return *h } -var GetInitHandleFn = reload.BuildValFnWithAnyParams("themeArgAndConfig", SetConfigHandle, 100.01) +var GetInitHandleFn = reload.BuildValFnWithAnyParams("themeArgAndConfig", SetConfigHandle, false) type ConfigParm struct { ConfigFn func(*Handle) @@ -136,6 +134,7 @@ func InitHandle(configFn func(*Handle), h *Handle) { h.ginH["calPostClass"] = postClass(h) h.ginH["calBodyClass"] = bodyClass(h) h.ginH["customLogo"] = customLogo(h) + h.ginH["calComponent"] = CalComponent(h) h.isInited = true } @@ -246,7 +245,6 @@ func (h *Handle) PushHandlers(pipeScene string, call HandleCall, statsOrScene .. func (h *Handle) CommonComponents() { h.PushCacheGroupHeadScript(constraints.AllScene, "siteIconAndCustomCss", 0, CalSiteIcon, CalCustomCss) - h.PushRender(constraints.AllStats, NewHandleFn(CalComponents, 10.001, "wp.CalComponents")) h.PushRender(constraints.AllStats, NewHandleFn(PreRenderTemplate, 0, "wp.PreRenderTemplate")) ReplyCommentJs(h) AdditionScript(h) diff --git a/cache/reload/reload.go b/cache/reload/reload.go index 547e166..7b1fdba 100644 --- a/cache/reload/reload.go +++ b/cache/reload/reload.go @@ -61,7 +61,9 @@ func DeleteMapVal[T any](namespace string, key ...T) { fn(key) } -func Specifies(namespaces ...string) { +func Reloads(namespaces ...string) { + mut.Lock() + defer mut.Unlock() for _, namespace := range namespaces { fn, ok := callMap.Load(namespace) if !ok { @@ -93,7 +95,7 @@ func BuildMapFnWithConfirm[K comparable, V, A any](namespace string, fn func(A) } } -// BuildMapFn build given fn with a new fn which returned value can be saved and flushed when called Reload or Specifies +// BuildMapFn build given fn with a new fn which returned value can be saved and flushed when called Reload or Reloads // with namespace // // if give a float then can be reloaded early or lately, more bigger more earlier @@ -153,8 +155,7 @@ func BuildSafetyMap[K comparable, V, A any](namespace string, args ...any) *Safe return m } m = &SafetyMap[K, V, A]{safety.NewMap[K, V](), sync.Mutex{}} - ord, _ := parseArgs(args...) - autoFlush := helper.ParseArgs(true, args...) + args = append(args, namespace) deleteMapFn.Store(namespace, func(a any) { k, ok := a.([]K) if !ok && len(k) > 0 { @@ -164,13 +165,8 @@ func BuildSafetyMap[K comparable, V, A any](namespace string, args ...any) *Safe m.Val.Delete(key) } }) - if autoFlush { - Push(func() { - m.Val.Flush() - }, ord, namespace) - } + Push(m.Val.Flush, args...) safetyMaps.Store(namespace, m) - return m } @@ -198,22 +194,19 @@ func BuildAnyVal[T, A any](namespace string, counter bool, args ...any) *SafetyV vvv, ok := safetyMaps.Load(namespace) if ok { vv = vvv.(*SafetyVar[T, A]) - } else { - safetyMapLock.Lock() - defer safetyMapLock.Unlock() - vvv, ok = safetyMaps.Load(namespace) - if ok { - vv = vvv.(*SafetyVar[T, A]) - } else { - v := Val[T]{} - vv = &SafetyVar[T, A]{safety.NewVar(v), sync.Mutex{}} - ord, _ := parseArgs(args...) - Push(func() { - vv.Val.Flush() - }, ord, namespace) - safetyMaps.Store(namespace, vv) - } } + safetyMapLock.Lock() + defer safetyMapLock.Unlock() + vvv, ok = safetyMaps.Load(namespace) + if ok { + vv = vvv.(*SafetyVar[T, A]) + return vv + } + v := Val[T]{} + vv = &SafetyVar[T, A]{safety.NewVar(v), sync.Mutex{}} + args = append(args, namespace) + Push(vv.Val.Flush, args...) + safetyMaps.Store(namespace, vv) return vv } @@ -272,14 +265,14 @@ func BuildValFnWithConfirm[T, A any](namespace string, fn func(A) (T, bool), arg } } -// BuildValFn build given fn a new fn which return value can be saved and flushed when called Reload or Specifies +// BuildValFn build given fn a new fn which return value can be saved and flushed when called Reload or Reloads // with namespace. // // note: namespace should be not same as BuildMapFn and related fn, they stored same safety.Map[string,any]. // // if give a float then can be reloaded early or lately, more bigger more earlier // -// if give a bool false will not flushed when called Reload, then can called GetValMap to flush manually +// if give a bool false will not flushed when called Reload, but can call GetValMap or Reloads to flush manually func BuildValFn[T, A any](namespace string, fn func(A) T, args ...any) func(A) T { var vv = BuildAnyVal[T, A](namespace, false, args...) return func(a A) T { @@ -325,15 +318,17 @@ func BuildValFnWithAnyParams[T any](namespace string, fn func(...any) T, args .. // // args same as Push // -// if give a name, then can be flushed by calls Specifies +// if give a name, then can be flushed by calls Reloads // // if give a float then can be reloaded early or lately, more bigger more earlier +// +// if give a bool false will not flushed when called Reload, but can call GetValMap or Reloads to flush manually func Vars[T any](defaults T, args ...any) *safety.Var[T] { ss := safety.NewVar(defaults) - ord, name := parseArgs(args...) + Push(func() { ss.Store(defaults) - }, ord, name) + }, args...) return ss } @@ -356,13 +351,12 @@ func parseArgs(a ...any) (ord float64, name string) { // VarsBy // // args same as Push -// if give a name, then can be flushed by calls Specifies +// if give a name, then can be flushed by calls Reloads func VarsBy[T any](fn func() T, args ...any) *safety.Var[T] { ss := safety.NewVar(fn()) - ord, name := parseArgs(args...) Push(func() { ss.Store(fn()) - }, ord, name) + }, args...) return ss } func MapBy[K comparable, T any](fn func(*safety.Map[K, T]), args ...any) *safety.Map[K, T] { @@ -370,32 +364,35 @@ func MapBy[K comparable, T any](fn func(*safety.Map[K, T]), args ...any) *safety if fn != nil { fn(m) } - ord, name := parseArgs(args...) Push(func() { m.Flush() if fn != nil { fn(m) } - }, ord, name) + }, args...) return m } func SafeMap[K comparable, T any](args ...any) *safety.Map[K, T] { m := safety.NewMap[K, T]() - ord, name := parseArgs(args...) - Push(func() { - m.Flush() - }, ord, name) + Push(m.Flush, args...) return m } // Push the func that will be called whenever Reload called // -// if give a name, then can be called by called Specifies +// if give a name, then can be called by called Reloads // // if give a float then can be called early or lately when called Reload, more bigger more earlier +// +// if give a bool false will not flushed when called Reload, then can called GetValMap to flush manually func Push(fn func(), a ...any) { ord, name := parseArgs(a...) + auto := helper.ParseArgs(true, a...) + if name != "" && !auto { + callMap.Store(name, fn) + return + } waitReloadCalls.Append(queue{fn, ord, name}) if name != "" { callMap.Store(name, fn) @@ -405,7 +402,6 @@ func Push(fn func(), a ...any) { func Reload() { mut.Lock() defer mut.Unlock() - callMap.Flush() deleteMapFn.Flush() reloadCalls := waitReloadCalls.Load() slice.SimpleSort(reloadCalls, slice.DESC, func(t queue) float64 { diff --git a/cache/reload/reload_test.go b/cache/reload/reload_test.go index 3bc0b82..1730331 100644 --- a/cache/reload/reload_test.go +++ b/cache/reload/reload_test.go @@ -20,7 +20,7 @@ func TestFlushMapVal(t *testing.T) { return 33, true }) fmt.Println(v) - Specifies("key") + Reloads("key") v = GetAnyValMapBy[int, int, struct{}]("key", 2, struct{}{}, func(a struct{}) (int, bool) { fmt.Println("yyyy") return 33, true