commit 7e01f2725d9c90b00f0bb5e624cb70d5e635e581
parent 6a1611a0c87098f2e627843de9f319ad6e6e8bca
Author: lash <dev@holbrook.no>
Date: Sat, 8 Apr 2023 23:35:13 +0100
WIP Factor page, sizer, menu to render package, cachemap to page
Diffstat:
17 files changed, 886 insertions(+), 894 deletions(-)
diff --git a/go/cache/cache.go b/go/cache/cache.go
@@ -9,11 +9,10 @@ type Cache struct {
CacheSize uint32 // Total allowed cumulative size of values (not code) in cache
CacheUseSize uint32 // Currently used bytes by all values (not code) in cache
Cache []map[string]string // All loaded cache items
- CacheMap map[string]string // Mapped
- menuSize uint16 // Max size of menu
- outputSize uint32 // Max size of output
+ //CacheMap map[string]string // Mapped
+ //outputSize uint32 // Max size of output
sizes map[string]uint16 // Size limits for all loaded symbols.
- sink *string
+ //sink *string
}
// NewCache creates a new ready-to-use cache object
@@ -22,7 +21,7 @@ func NewCache() *Cache {
Cache: []map[string]string{make(map[string]string)},
sizes: make(map[string]uint16),
}
- ca.resetCurrent()
+ //ca.resetCurrent()
return ca
}
@@ -32,12 +31,6 @@ func(ca *Cache) WithCacheSize(cacheSize uint32) *Cache {
return ca
}
-// WithCacheSize applies a cumulative cache size limitation for all cached items.
-func(ca *Cache) WithOutputSize(outputSize uint32) *Cache {
- ca.outputSize = outputSize
- return ca
-}
-
// Add adds a cache value under a cache symbol key.
//
// Also stores the size limitation of for key for later updates.
@@ -72,6 +65,15 @@ func(ca *Cache) Add(key string, value string, sizeLimit uint16) error {
return nil
}
+// ReservedSize returns the maximum byte size available for the given symbol.
+func(ca *Cache) ReservedSize(key string) (uint16, error) {
+ v, ok := ca.sizes[key]
+ if !ok {
+ return 0, fmt.Errorf("unknown symbol: %s", key)
+ }
+ return v, nil
+}
+
// Update sets a new value for an existing key.
//
// Uses the size limitation from when the key was added.
@@ -95,9 +97,9 @@ func(ca *Cache) Update(key string, value string) error {
r := ca.Cache[checkFrame][key]
l := uint32(len(r))
ca.Cache[checkFrame][key] = ""
- if ca.CacheMap[key] != "" {
- ca.CacheMap[key] = value
- }
+ //if ca.CacheMap[key] != "" {
+ // ca.CacheMap[key] = value
+ //}
ca.CacheUseSize -= l
sz := ca.checkCapacity(value)
if sz == 0 {
@@ -117,58 +119,6 @@ func(ca *Cache) Get() (map[string]string, error) {
return ca.Cache[len(ca.Cache)-1], nil
}
-func(ca *Cache) Sizes() (map[string]uint16, error) {
- if len(ca.Cache) == 0 {
- return nil, fmt.Errorf("get at top frame")
- }
- sizes := make(map[string]uint16)
- var haveSink bool
- for k, _ := range ca.CacheMap {
- l, ok := ca.sizes[k]
- if !ok {
- panic(fmt.Sprintf("missing size for %v", k))
- }
- if l == 0 {
- if haveSink {
- panic(fmt.Sprintf("duplicate sink for %v", k))
- }
- haveSink = true
- }
- sizes[k] = l
- }
- return sizes, nil
-}
-
-// Map marks the given key for retrieval.
-//
-// After this, Val() will return the value for the key, and Size() will include the value size and limitations in its calculations.
-//
-// Only one symbol with no size limitation may be mapped at the current level.
-func(ca *Cache) Map(key string) error {
- m, err := ca.Get()
- if err != nil {
- return err
- }
- l := ca.sizes[key]
- if l == 0 {
- if ca.sink != nil {
- return fmt.Errorf("sink already set to symbol '%v'", *ca.sink)
- }
- ca.sink = &key
- }
- ca.CacheMap[key] = m[key]
- return nil
-}
-
-// Fails if key is not mapped.
-func(ca *Cache) Val(key string) (string, error) {
- r := ca.CacheMap[key]
- if len(r) == 0 {
- return "", fmt.Errorf("key %v not mapped", key)
- }
- return r, nil
-}
-
// Reset flushes all state contents below the top level.
func(ca *Cache) Reset() {
if len(ca.Cache) == 0 {
@@ -179,36 +129,11 @@ func(ca *Cache) Reset() {
return
}
-// Size returns size used by values and menu, and remaining size available
-func(ca *Cache) Usage() (uint32, uint32) {
- var l int
- var c uint16
- for k, v := range ca.CacheMap {
- l += len(v)
- c += ca.sizes[k]
- }
- r := uint32(l)
- r += uint32(ca.menuSize)
- return r, uint32(c)-r
-}
-
-// return 0-indexed frame number where key is defined. -1 if not defined
-func(ca *Cache) frameOf(key string) int {
- for i, m := range ca.Cache {
- for k, _ := range m {
- if k == key {
- return i
- }
- }
- }
- return -1
-}
-
// Push adds a new level to the cache.
func (ca *Cache) Push() error {
m := make(map[string]string)
ca.Cache = append(ca.Cache, m)
- ca.resetCurrent()
+ //ca.resetCurrent()
return nil
}
@@ -228,7 +153,7 @@ func (ca *Cache) Pop() error {
log.Printf("free frame %v key %v value size %v", l, k, sz)
}
ca.Cache = ca.Cache[:l]
- ca.resetCurrent()
+ //ca.resetCurrent()
return nil
}
@@ -238,10 +163,10 @@ func(ca *Cache) Check(key string) bool {
}
// flush relveant properties for level change
-func(ca *Cache) resetCurrent() {
- ca.sink = nil
- ca.CacheMap = make(map[string]string)
-}
+//func(ca *Cache) resetCurrent() {
+// ca.sink = nil
+// ca.CacheMap = make(map[string]string)
+//}
// bytes that will be added to cache use size for string
// returns 0 if capacity would be exceeded
@@ -255,3 +180,15 @@ func(ca *Cache) checkCapacity(v string) uint32 {
}
return sz
}
+
+// return 0-indexed frame number where key is defined. -1 if not defined
+func(ca *Cache) frameOf(key string) int {
+ for i, m := range ca.Cache {
+ for k, _ := range m {
+ if k == key {
+ return i
+ }
+ }
+ }
+ return -1
+}
diff --git a/go/cache/cache_test.go b/go/cache/cache_test.go
@@ -4,7 +4,6 @@ import (
"testing"
)
-
func TestNewCache(t *testing.T) {
ca := NewCache()
if ca.CacheSize != 0 {
@@ -34,7 +33,6 @@ func TestStateCacheUse(t *testing.T) {
}
}
-
func TestStateDownUp(t *testing.T) {
ca := NewCache()
err := ca.Push()
@@ -104,83 +102,3 @@ func TestCacheLoadDup(t *testing.T) {
}
}
-func TestCacheCurrentSize(t *testing.T) {
- ca := NewCache()
- err := ca.Push()
- if err != nil {
- t.Error(err)
- }
- err = ca.Add("foo", "inky", 0)
- if err != nil {
- t.Error(err)
- }
- err = ca.Push()
- err = ca.Add("bar", "pinky", 10)
- if err != nil {
- t.Error(err)
- }
- err = ca.Map("bar")
- if err != nil {
- t.Error(err)
- }
- err = ca.Add("baz", "tinkywinkydipsylalapoo", 51)
- if err != nil {
- t.Error(err)
- }
- err = ca.Map("baz")
- if err != nil {
- t.Error(err)
- }
-
- l, c := ca.Usage()
- if l != 27 {
- t.Errorf("expected actual length 27, got %v", l)
- }
- if c != 34 {
- t.Errorf("expected remaining length 34, got %v", c)
- }
-}
-
-func TestStateMapSink(t *testing.T) {
- ca := NewCache()
- ca.Push()
- err := ca.Add("foo", "bar", 0)
- if err != nil {
- t.Error(err)
- }
- ca.Push()
- err = ca.Add("bar", "xyzzy", 6)
- if err != nil {
- t.Error(err)
- }
- err = ca.Add("baz", "bazbaz", 18)
- if err != nil {
- t.Error(err)
- }
- err = ca.Add("xyzzy", "plugh", 0)
- if err != nil {
- t.Error(err)
- }
- err = ca.Map("foo")
- if err != nil {
- t.Error(err)
- }
- err = ca.Map("xyzzy")
- if err == nil {
- t.Errorf("Expected fail on duplicate sink")
- }
- err = ca.Map("baz")
- if err != nil {
- t.Error(err)
- }
- ca.Push()
- err = ca.Map("foo")
- if err != nil {
- t.Error(err)
- }
- ca.Pop()
- err = ca.Map("foo")
- if err != nil {
- t.Error(err)
- }
-}
diff --git a/go/menu/menu.go b/go/menu/menu.go
@@ -1,132 +0,0 @@
-package menu
-
-import (
- "fmt"
- "log"
-)
-
-// BrowseConfig defines the availability and display parameters for page browsing.
-type BrowseConfig struct {
- NextAvailable bool
- NextSelector string
- NextTitle string
- PreviousAvailable bool
- PreviousSelector string
- PreviousTitle string
-}
-
-// Default browse settings for convenience.
-func DefaultBrowseConfig() BrowseConfig {
- return BrowseConfig{
- NextAvailable: true,
- NextSelector: "11",
- NextTitle: "next",
- PreviousAvailable: true,
- PreviousSelector: "22",
- PreviousTitle: "previous",
- }
-}
-
-type Menu struct {
- menu [][2]string
- browse BrowseConfig
- pageCount uint16
- canNext bool
- canPrevious bool
-}
-
-// NewMenu creates a new Menu with an explicit page count.
-func NewMenu() *Menu {
- return &Menu{}
-}
-
-// WithBrowseConfig defines the criteria for page browsing.
-func(m *Menu) WithPageCount(pageCount uint16) *Menu {
- m.pageCount = pageCount
- return m
-}
-
-// WithBrowseConfig defines the criteria for page browsing.
-func(m *Menu) WithBrowseConfig(cfg BrowseConfig) *Menu {
- m.browse = cfg
-
- return m
-}
-
-// Put adds a menu option to the menu rendering.
-func(m *Menu) Put(selector string, title string) error {
- m.menu = append(m.menu, [2]string{selector, title})
- log.Printf("menu %v", m.menu)
- return nil
-}
-
-// Render returns the full current state of the menu as a string.
-//
-// After this has been executed, the state of the menu will be empty.
-func(m *Menu) Render(idx uint16) (string, error) {
- err := m.applyPage(idx)
- if err != nil {
- return "", err
- }
-
- r := ""
- for true {
- l := len(r)
- choice, title, err := m.shiftMenu()
- if err != nil {
- break
- }
- if l > 0 {
- r += "\n"
- }
- r += fmt.Sprintf("%s:%s", choice, title)
- }
- return r, nil
-}
-
-// add available browse options.
-func(m *Menu) applyPage(idx uint16) error {
- if m.pageCount == 0 {
- return nil
- } else if idx >= m.pageCount {
- return fmt.Errorf("index %v out of bounds (%v)", idx, m.pageCount)
- }
- if m.browse.NextAvailable {
- m.canNext = true
- }
- if m.browse.PreviousAvailable {
- m.canPrevious = true
- }
- if idx == m.pageCount - 1 {
- m.canNext = false
- }
- if idx == 0 {
- m.canPrevious = false
- }
- if m.canNext {
- err := m.Put(m.browse.NextSelector, m.browse.NextTitle)
- if err != nil {
- return err
- }
- }
- if m.canPrevious {
- err := m.Put(m.browse.PreviousSelector, m.browse.PreviousTitle)
- if err != nil {
- return err
- }
- }
- return nil
-}
-
-// removes and returns the first of remaining menu options.
-// fails if menu is empty.
-func(m *Menu) shiftMenu() (string, string, error) {
- if len(m.menu) == 0 {
- return "", "", fmt.Errorf("menu is empty")
- }
- r := m.menu[0]
- m.menu = m.menu[1:]
- return r[0], r[1], nil
-}
-
-
diff --git a/go/menu/menu_test.go b/go/menu/menu_test.go
@@ -1,94 +0,0 @@
-package menu
-
-import (
- "testing"
-)
-
-func TestMenuInit(t *testing.T) {
- m := NewMenu()
- err := m.Put("1", "foo")
- if err != nil {
- t.Fatal(err)
- }
- err = m.Put("2", "bar")
- if err != nil {
- t.Fatal(err)
- }
- r, err := m.Render(0)
- if err != nil {
- t.Fatal(err)
- }
- expect := `1:foo
-2:bar`
- if r != expect {
- t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", expect, r)
- }
-}
-
-func TestMenuBrowse(t *testing.T) {
- cfg := DefaultBrowseConfig()
- m := NewMenu().WithPageCount(3).WithBrowseConfig(cfg)
- err := m.Put("1", "foo")
- if err != nil {
- t.Fatal(err)
- }
- err = m.Put("2", "bar")
- if err != nil {
- t.Fatal(err)
- }
-
- r, err := m.Render(0)
- if err != nil {
- t.Fatal(err)
- }
- expect := `1:foo
-2:bar
-11:next`
- if r != expect {
- t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", expect, r)
- }
-
- err = m.Put("1", "foo")
- if err != nil {
- t.Fatal(err)
- }
- err = m.Put("2", "bar")
- if err != nil {
- t.Fatal(err)
- }
- r, err = m.Render(1)
- if err != nil {
- t.Fatal(err)
- }
- expect = `1:foo
-2:bar
-11:next
-22:previous`
- if r != expect {
- t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", expect, r)
- }
-
- err = m.Put("1", "foo")
- if err != nil {
- t.Fatal(err)
- }
- err = m.Put("2", "bar")
- if err != nil {
- t.Fatal(err)
- }
- r, err = m.Render(2)
- if err != nil {
- t.Fatal(err)
- }
- expect = `1:foo
-2:bar
-22:previous`
- if r != expect {
- t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", expect, r)
- }
-
- _, err = m.Render(3)
- if err == nil {
- t.Fatalf("expected render fail")
- }
-}
diff --git a/go/render/menu.go b/go/render/menu.go
@@ -0,0 +1,154 @@
+package render
+
+import (
+ "fmt"
+)
+
+// BrowseConfig defines the availability and display parameters for page browsing.
+type BrowseConfig struct {
+ NextAvailable bool
+ NextSelector string
+ NextTitle string
+ PreviousAvailable bool
+ PreviousSelector string
+ PreviousTitle string
+}
+
+// Default browse settings for convenience.
+func DefaultBrowseConfig() BrowseConfig {
+ return BrowseConfig{
+ NextAvailable: true,
+ NextSelector: "11",
+ NextTitle: "next",
+ PreviousAvailable: true,
+ PreviousSelector: "22",
+ PreviousTitle: "previous",
+ }
+}
+
+type Menu struct {
+ menu [][2]string
+ browse BrowseConfig
+ pageCount uint16
+ canNext bool
+ canPrevious bool
+ outputSize uint16
+}
+
+// NewMenu creates a new Menu with an explicit page count.
+func NewMenu() *Menu {
+ return &Menu{}
+}
+
+// WithBrowseConfig defines the criteria for page browsing.
+func(m *Menu) WithPageCount(pageCount uint16) *Menu {
+ m.pageCount = pageCount
+ return m
+}
+
+// WithSize defines the maximum byte size of the rendered menu.
+func(m *Menu) WithOutputSize(outputSize uint16) *Menu {
+ m.outputSize = outputSize
+ return m
+}
+
+// WithBrowseConfig defines the criteria for page browsing.
+func(m *Menu) WithBrowseConfig(cfg BrowseConfig) *Menu {
+ m.browse = cfg
+ return m
+}
+
+// Put adds a menu option to the menu rendering.
+func(m *Menu) Put(selector string, title string) error {
+ m.menu = append(m.menu, [2]string{selector, title})
+ return nil
+}
+
+// Render returns the full current state of the menu as a string.
+//
+// After this has been executed, the state of the menu will be empty.
+func(m *Menu) Render(idx uint16) (string, error) {
+ var menuCopy [][2]string
+ for _, v := range m.menu {
+ menuCopy = append(menuCopy, v)
+ }
+
+ err := m.applyPage(idx)
+ if err != nil {
+ return "", err
+ }
+
+ r := ""
+ for true {
+ l := len(r)
+ choice, title, err := m.shiftMenu()
+ if err != nil {
+ break
+ }
+ if l > 0 {
+ r += "\n"
+ }
+ r += fmt.Sprintf("%s:%s", choice, title)
+ }
+ m.menu = menuCopy
+ return r, nil
+}
+
+// add available browse options.
+func(m *Menu) applyPage(idx uint16) error {
+ if m.pageCount == 0 {
+ if idx > 0 {
+ return fmt.Errorf("index %v > 0 for non-paged menu", idx)
+ }
+ return nil
+ } else if idx >= m.pageCount {
+ return fmt.Errorf("index %v out of bounds (%v)", idx, m.pageCount)
+ }
+
+ m.reset()
+
+ if idx == m.pageCount - 1 {
+ m.canNext = false
+ }
+ if idx == 0 {
+ m.canPrevious = false
+ }
+
+ if m.canNext {
+ err := m.Put(m.browse.NextSelector, m.browse.NextTitle)
+ if err != nil {
+ return err
+ }
+ }
+ if m.canPrevious {
+ err := m.Put(m.browse.PreviousSelector, m.browse.PreviousTitle)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// removes and returns the first of remaining menu options.
+// fails if menu is empty.
+func(m *Menu) shiftMenu() (string, string, error) {
+ if len(m.menu) == 0 {
+ return "", "", fmt.Errorf("menu is empty")
+ }
+ r := m.menu[0]
+ m.menu = m.menu[1:]
+ return r[0], r[1], nil
+}
+
+func(m *Menu) reset() {
+ if m.browse.NextAvailable {
+ m.canNext = true
+ }
+ if m.browse.PreviousAvailable {
+ m.canPrevious = true
+ }
+}
+
+func(m *Menu) ReservedSize() uint16 {
+ return m.outputSize
+}
diff --git a/go/render/menu_test.go b/go/render/menu_test.go
@@ -0,0 +1,84 @@
+package render
+
+import (
+ "testing"
+)
+
+func TestMenuInit(t *testing.T) {
+ m := NewMenu()
+ err := m.Put("1", "foo")
+ if err != nil {
+ t.Fatal(err)
+ }
+ err = m.Put("2", "bar")
+ if err != nil {
+ t.Fatal(err)
+ }
+ r, err := m.Render(0)
+ if err != nil {
+ t.Fatal(err)
+ }
+ expect := `1:foo
+2:bar`
+ if r != expect {
+ t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", expect, r)
+ }
+
+ r, err = m.Render(1)
+ if err == nil {
+ t.Fatalf("expected render fail")
+ }
+
+}
+
+func TestMenuBrowse(t *testing.T) {
+ cfg := DefaultBrowseConfig()
+ m := NewMenu().WithPageCount(3).WithBrowseConfig(cfg)
+ err := m.Put("1", "foo")
+ if err != nil {
+ t.Fatal(err)
+ }
+ err = m.Put("2", "bar")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ r, err := m.Render(0)
+ if err != nil {
+ t.Fatal(err)
+ }
+ expect := `1:foo
+2:bar
+11:next`
+ if r != expect {
+ t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", expect, r)
+ }
+
+ r, err = m.Render(1)
+ if err != nil {
+ t.Fatal(err)
+ }
+ expect = `1:foo
+2:bar
+11:next
+22:previous`
+ if r != expect {
+ t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", expect, r)
+ }
+
+ r, err = m.Render(2)
+ if err != nil {
+ t.Fatal(err)
+ }
+ expect = `1:foo
+2:bar
+22:previous`
+ if r != expect {
+ t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", expect, r)
+ }
+
+ _, err = m.Render(3)
+ if err == nil {
+ t.Fatalf("expected render fail")
+ }
+}
diff --git a/go/render/page.go b/go/render/page.go
@@ -0,0 +1,289 @@
+package render
+
+import (
+ "bytes"
+ "fmt"
+ "log"
+ "strings"
+ "text/template"
+
+ "git.defalsify.org/festive/cache"
+ "git.defalsify.org/festive/resource"
+)
+
+type Page struct {
+ cacheMap map[string]string // Mapped
+ cache *cache.Cache
+ resource resource.Resource
+ menu *Menu
+ sink *string
+ sinkSize uint16
+ sizer *Sizer
+ sinkProcessed bool
+}
+
+func NewPage(cache *cache.Cache, rs resource.Resource) *Page {
+ return &Page{
+ cache: cache,
+ cacheMap: make(map[string]string),
+ resource: rs,
+ }
+}
+
+func(pg *Page) WithMenu(menu *Menu) *Page {
+ pg.menu = menu
+ if pg.sizer != nil {
+ pg.sizer = pg.sizer.WithMenuSize(pg.menu.ReservedSize())
+ }
+ return pg
+}
+
+func(pg *Page) WithSizer(sizer *Sizer) *Page {
+ pg.sizer = sizer
+ if pg.menu != nil {
+ pg.sizer = pg.sizer.WithMenuSize(pg.menu.ReservedSize())
+ }
+ return pg
+}
+
+// Size returns size used by values and menu, and remaining size available
+func(pg *Page) Usage() (uint32, uint32, error) {
+ var l int
+ var c uint16
+ for k, v := range pg.cacheMap {
+ l += len(v)
+ sz, err := pg.cache.ReservedSize(k)
+ if err != nil {
+ return 0, 0, err
+ }
+ c += sz
+ }
+ r := uint32(l)
+ if pg.menu != nil {
+ r += uint32(pg.menu.ReservedSize())
+ }
+ return r, uint32(c)-r, nil
+}
+
+// Map marks the given key for retrieval.
+//
+// After this, Val() will return the value for the key, and Size() will include the value size and limitations in its calculations.
+//
+// Only one symbol with no size limitation may be mapped at the current level.
+func(pg *Page) Map(key string) error {
+ m, err := pg.cache.Get()
+ if err != nil {
+ return err
+ }
+ l, err := pg.cache.ReservedSize(key)
+ if err != nil {
+ return err
+ }
+ if l == 0 {
+ if pg.sink != nil {
+ return fmt.Errorf("sink already set to symbol '%v'", *pg.sink)
+ }
+ pg.sink = &key
+ }
+ pg.cacheMap[key] = m[key]
+ if pg.sizer != nil {
+ err := pg.sizer.Set(key, l)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// Fails if key is not mapped.
+func(pg *Page) Val(key string) (string, error) {
+ r := pg.cacheMap[key]
+ if len(r) == 0 {
+ return "", fmt.Errorf("key %v not mapped", key)
+ }
+ return r, nil
+}
+
+// Moved from cache, MAP should hook to this object
+func(pg *Page) Sizes() (map[string]uint16, error) {
+ sizes := make(map[string]uint16)
+ var haveSink bool
+ for k, _ := range pg.cacheMap {
+ l, err := pg.cache.ReservedSize(k)
+ if err != nil {
+ return nil, err
+ }
+ if l == 0 {
+ if haveSink {
+ panic(fmt.Sprintf("duplicate sink for %v", k))
+ }
+ haveSink = true
+ }
+ pg.sinkSize = l
+ }
+ return sizes, nil
+}
+
+// DefaultRenderTemplate is an adapter to implement the builtin golang text template renderer as resource.RenderTemplate.
+func(pg *Page) RenderTemplate(sym string, values map[string]string, idx uint16) (string, error) {
+ tpl, err := pg.resource.GetTemplate(sym)
+ if err != nil {
+ return "", err
+ }
+ if pg.sizer != nil {
+ values, err = pg.sizer.GetAt(values, idx)
+ if err != nil {
+ return "", err
+ }
+ } else if idx > 0 {
+ return "", fmt.Errorf("sizer needed for indexed render")
+ }
+ log.Printf("render for index: %v", idx)
+
+ tp, err := template.New("tester").Option("missingkey=error").Parse(tpl)
+ if err != nil {
+ return "", err
+ }
+
+ b := bytes.NewBuffer([]byte{})
+ err = tp.Execute(b, values)
+ if err != nil {
+ return "", err
+ }
+ return b.String(), err
+}
+
+// render menu and all syms except sink, split sink into display chunks
+func(pg *Page) prepare(sym string, values map[string]string, idx uint16) (map[string]string, error) {
+ var sink string
+ var sinkValues []string
+ noSinkValues := make(map[string]string)
+ for k, v := range values {
+ sz, err := pg.cache.ReservedSize(k)
+ if err != nil {
+ return nil, err
+ }
+ if sz == 0 {
+ sink = k
+ sinkValues = strings.Split(v, "\n")
+ v = ""
+ log.Printf("found sink %s with field count %v", k, len(sinkValues))
+ }
+ noSinkValues[k] = v
+ }
+
+ if sink == "" {
+ log.Printf("no sink found for sym %s", sym)
+ return values, nil
+ }
+
+ pg.sizer.AddCursor(0)
+ s, err := pg.render(sym, noSinkValues, 0)
+ if err != nil {
+ return nil, err
+ }
+
+ remaining, ok := pg.sizer.Check(s)
+ if !ok {
+ return nil, fmt.Errorf("capacity exceeded")
+ }
+
+ log.Printf("%v bytes available for sink split", remaining)
+
+ l := 0
+ var count uint16
+ tb := strings.Builder{}
+ rb := strings.Builder{}
+
+ for i, v := range sinkValues {
+ log.Printf("processing sinkvalue %v: %s", i, v)
+ l += len(v)
+ if uint32(l) > remaining {
+ if tb.Len() == 0 {
+ return nil, fmt.Errorf("capacity insufficient for sink field %v", i)
+ }
+ rb.WriteString(tb.String())
+ rb.WriteRune('\n')
+ c := uint32(rb.Len())
+ pg.sizer.AddCursor(c)
+ tb.Reset()
+ l = 0
+ count += 1
+ }
+ if tb.Len() > 0 {
+ tb.WriteByte(byte(0x00))
+ }
+ tb.WriteString(v)
+ }
+
+ if tb.Len() > 0 {
+ rb.WriteString(tb.String())
+ count += 1
+ }
+
+ r := rb.String()
+ r = strings.TrimRight(r, "\n")
+
+ noSinkValues[sink] = r
+
+ if pg.menu != nil {
+ pg.menu = pg.menu.WithPageCount(count)
+ }
+
+ for i, v := range strings.Split(r, "\n") {
+ log.Printf("nosinkvalue %v: %s", i, v)
+ }
+
+ return noSinkValues, nil
+}
+
+func(pg *Page) render(sym string, values map[string]string, idx uint16) (string, error) {
+ var ok bool
+ r := ""
+ s, err := pg.RenderTemplate(sym, values, idx)
+ if err != nil {
+ return "", err
+ }
+ log.Printf("rendered %v bytes for template", len(s))
+ r += s
+ if pg.sizer != nil {
+ _, ok = pg.sizer.Check(r)
+ if !ok {
+ return "", fmt.Errorf("limit exceeded: %v", pg.sizer)
+ }
+ }
+ s, err = pg.menu.Render(idx)
+ if err != nil {
+ return "", err
+ }
+ log.Printf("rendered %v bytes for menu", len(s))
+ r += "\n" + s
+ if pg.sizer != nil {
+ _, ok = pg.sizer.Check(r)
+ if !ok {
+ return "", fmt.Errorf("limit exceeded: %v", pg.sizer)
+ }
+ }
+ return r, nil
+}
+
+func(pg *Page) Render(sym string, values map[string]string, idx uint16) (string, error) {
+ var err error
+
+ values, err = pg.prepare(sym, values, idx)
+ if err != nil {
+ return "", err
+ }
+
+ log.Printf("nosink %v", values)
+ return pg.render(sym, values, idx)
+}
+
+func(pg *Page) Reset() {
+ pg.sink = nil
+ pg.sinkSize = 0
+ pg.sinkProcessed = false
+ pg.cacheMap = make(map[string]string)
+}
+
+
diff --git a/go/render/size.go b/go/render/size.go
@@ -0,0 +1,102 @@
+package render
+
+import (
+ "bytes"
+ "fmt"
+ "log"
+ "strings"
+)
+
+type Sizer struct {
+ outputSize uint32
+ menuSize uint16
+ memberSizes map[string]uint16
+ totalMemberSize uint32
+ crsrs []uint32
+ sink string
+}
+
+func NewSizer(outputSize uint32) *Sizer {
+ return &Sizer{
+ outputSize: outputSize,
+ memberSizes: make(map[string]uint16),
+ }
+}
+
+func(szr *Sizer) WithMenuSize(menuSize uint16) *Sizer {
+ szr.menuSize = menuSize
+ return szr
+}
+
+func(szr *Sizer) Set(key string, size uint16) error {
+ var ok bool
+ _, ok = szr.memberSizes[key]
+ if ok {
+ return fmt.Errorf("already have key %s", key)
+ }
+ szr.memberSizes[key] = size
+ if size == 0 {
+ szr.sink = key
+ }
+ szr.totalMemberSize += uint32(size)
+ return nil
+}
+
+func(szr *Sizer) Check(s string) (uint32, bool) {
+ log.Printf("sizercheck %s", s)
+ l := uint32(len(s))
+ if szr.outputSize > 0 {
+ if l > szr.outputSize {
+ log.Printf("sizer check fails with length %v: %s", l, szr)
+ return 0, false
+ }
+ l = szr.outputSize - l
+ }
+ return l, true
+}
+
+func(szr *Sizer) String() string {
+ var diff uint32
+ if szr.outputSize > 0 {
+ diff = szr.outputSize - szr.totalMemberSize - uint32(szr.menuSize)
+ }
+ return fmt.Sprintf("output: %v, member: %v, menu: %v, diff: %v", szr.outputSize, szr.totalMemberSize, szr.menuSize, diff)
+}
+
+func(szr *Sizer) Size(s string) (uint16, error) {
+ r, ok := szr.memberSizes[s]
+ if !ok {
+ return 0, fmt.Errorf("unknown member: %s", s)
+ }
+ return r, nil
+}
+
+func(szr *Sizer) AddCursor(c uint32) {
+ log.Printf("added cursor: %v", c)
+ szr.crsrs = append(szr.crsrs, c)
+}
+
+func(szr *Sizer) GetAt(values map[string]string, idx uint16) (map[string]string, error) {
+ if szr.sink == "" {
+ return values, nil
+ }
+ outValues := make(map[string]string)
+ for k, v := range values {
+ if szr.sink == k {
+ if idx >= uint16(len(szr.crsrs)) {
+ return nil, fmt.Errorf("no more values in index")
+ }
+ c := szr.crsrs[idx]
+ v = v[c:]
+ nl := strings.Index(v, "\n")
+ log.Printf("k %v v %v c %v nl %v", k, v, c, nl)
+ if nl > 0 {
+ v = v[:nl]
+ }
+ b := bytes.ReplaceAll([]byte(v), []byte{0x00}, []byte{0x0a})
+ v = string(b)
+ }
+ outValues[k] = v
+ }
+ return outValues, nil
+}
diff --git a/go/render/size_test.go b/go/render/size_test.go
@@ -0,0 +1,181 @@
+package render
+
+import (
+ "context"
+ "fmt"
+ "testing"
+
+ "git.defalsify.org/festive/state"
+ "git.defalsify.org/festive/resource"
+ "git.defalsify.org/festive/cache"
+)
+
+type TestSizeResource struct {
+ *resource.MenuResource
+}
+
+func getTemplate(sym string) (string, error) {
+ var tpl string
+ switch sym {
+ case "small":
+ tpl = "one {{.foo}} two {{.bar}} three {{.baz}}"
+ case "toobig":
+ tpl = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus in mattis lorem. Aliquam erat volutpat. Ut vitae metus."
+ case "pages":
+ tpl = "one {{.foo}} two {{.bar}} three {{.baz}}\n{{.xyzzy}}"
+ }
+ return tpl, nil
+}
+
+func funcFor(sym string) (resource.EntryFunc, error) {
+ switch sym {
+ case "foo":
+ return getFoo, nil
+ case "bar":
+ return getBar, nil
+ case "baz":
+ return getBaz, nil
+ case "xyzzy":
+ return getXyzzy, nil
+ }
+ return nil, fmt.Errorf("unknown func: %s", sym)
+}
+
+func getFoo(ctx context.Context) (string, error) {
+ return "inky", nil
+}
+
+func getBar(ctx context.Context) (string, error) {
+ return "pinky", nil
+}
+
+func getBaz(ctx context.Context) (string, error) {
+ return "blinky", nil
+}
+
+func getXyzzy(ctx context.Context) (string, error) {
+ return "inky pinky\nblinky clyde sue\ntinkywinky dipsy\nlala poo\none two three four five six seven\neight nine ten\neleven twelve", nil
+}
+
+func TestSizeCheck(t *testing.T) {
+ szr := NewSizer(16)
+ l, ok := szr.Check("foobar")
+ if !ok {
+ t.Fatalf("expected ok")
+ }
+ if l != 10 {
+ t.Fatalf("expected 10, got %v", l)
+ }
+
+ l, ok = szr.Check("inkypinkyblinkyclyde")
+ if ok {
+ t.Fatalf("expected not ok")
+ }
+ if l != 0 {
+ t.Fatalf("expected 0, got %v", l)
+ }
+}
+
+func TestSizeLimit(t *testing.T) {
+ st := state.NewState(0)
+ ca := cache.NewCache()
+ mn := NewMenu().WithOutputSize(32)
+ mrs := resource.NewMenuResource().WithEntryFuncGetter(funcFor).WithTemplateGetter(getTemplate)
+ rs := TestSizeResource{
+ mrs,
+ }
+ szr := NewSizer(128)
+ pg := NewPage(ca, rs).WithMenu(mn).WithSizer(szr)
+ ca.Push()
+ st.Down("test")
+ ca.Add("foo", "inky", 4)
+ ca.Add("bar", "pinky", 10)
+ ca.Add("baz", "blinky", 0)
+ pg.Map("foo")
+ pg.Map("bar")
+ pg.Map("baz")
+
+ mn.Put("1", "foo the foo")
+ mn.Put("2", "go to bar")
+
+ vals, err := ca.Get()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ _, err = pg.Render("small", vals, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ mn.Put("1", "foo the foo")
+ mn.Put("2", "go to bar")
+
+ _, err = pg.Render("toobig", vals, 0)
+ if err == nil {
+ t.Fatalf("expected size exceeded")
+ }
+}
+
+func TestSizePages(t *testing.T) {
+ st := state.NewState(0)
+ ca := cache.NewCache()
+ mn := NewMenu().WithOutputSize(32)
+ mrs := resource.NewMenuResource().WithEntryFuncGetter(funcFor).WithTemplateGetter(getTemplate)
+ rs := TestSizeResource{
+ mrs,
+ }
+ szr := NewSizer(128)
+ pg := NewPage(ca, rs).WithSizer(szr).WithMenu(mn)
+ ca.Push()
+ st.Down("test")
+ ca.Add("foo", "inky", 4)
+ ca.Add("bar", "pinky", 10)
+ ca.Add("baz", "blinky", 20)
+ ca.Add("xyzzy", "inky pinky\nblinky clyde sue\ntinkywinky dipsy\nlala poo\none two three four five six seven\neight nine ten\neleven twelve", 0)
+ pg.Map("foo")
+ pg.Map("bar")
+ pg.Map("baz")
+ pg.Map("xyzzy")
+
+ vals, err := ca.Get()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ mn.Put("1", "foo the foo")
+ mn.Put("2", "go to bar")
+
+ r, err := pg.Render("pages", vals, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ expect := `one inky two pinky three blinky
+inky pinky
+blinky clyde sue
+tinkywinky dipsy
+lala poo
+1:foo the foo
+2:go to bar`
+
+
+ if r != expect {
+ t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", expect, r)
+ }
+ r, err = pg.Render("pages", vals, 1)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ expect = `one inky two pinky three blinky
+one two three four five six seven
+eight nine ten
+eleven twelve
+1:foo the foo
+2:go to bar`
+ if r != expect {
+ t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", expect, r)
+ }
+
+}
diff --git a/go/resource/fs.go b/go/resource/fs.go
@@ -23,7 +23,7 @@ func NewFsResource(path string) (FsResource) {
}
}
-func(fs FsResource) GetTemplate(sym string, sizer *Sizer) (string, error) {
+func(fs FsResource) GetTemplate(sym string) (string, error) {
fp := path.Join(fs.Path, sym)
r, err := ioutil.ReadFile(fp)
s := string(r)
diff --git a/go/resource/render.go b/go/resource/render.go
@@ -1,40 +1,40 @@
package resource
-import (
- "bytes"
- "fmt"
- "log"
- "text/template"
-)
+//import (
+// "bytes"
+// "fmt"
+// "log"
+// "text/template"
+//)
-// DefaultRenderTemplate is an adapter to implement the builtin golang text template renderer as resource.RenderTemplate.
-func DefaultRenderTemplate(r Resource, sym string, values map[string]string, idx uint16, sizer *Sizer) (string, error) {
- v, err := r.GetTemplate(sym, nil)
- if err != nil {
- return "", err
- }
-
- if sizer != nil {
- values, err = sizer.GetAt(values, idx)
- } else if idx > 0 {
- return "", fmt.Errorf("sizer needed for indexed render")
- }
- log.Printf("render for index: %v", idx)
-
- if err != nil {
- return "", err
- }
-
- tp, err := template.New("tester").Option("missingkey=error").Parse(v)
- if err != nil {
- return "", err
- }
-
- b := bytes.NewBuffer([]byte{})
- err = tp.Execute(b, values)
- if err != nil {
- return "", err
- }
- return b.String(), err
-}
+//// DefaultRenderTemplate is an adapter to implement the builtin golang text template renderer as resource.RenderTemplate.
+//func DefaultRenderTemplate(r Resource, sym string, values map[string]string, idx uint16, sizer *Sizer) (string, error) {
+// v, err := r.GetTemplate(sym, nil)
+// if err != nil {
+// return "", err
+// }
+//
+// if sizer != nil {
+// values, err = sizer.GetAt(values, idx)
+// } else if idx > 0 {
+// return "", fmt.Errorf("sizer needed for indexed render")
+// }
+// log.Printf("render for index: %v", idx)
+//
+// if err != nil {
+// return "", err
+// }
+//
+// tp, err := template.New("tester").Option("missingkey=error").Parse(v)
+// if err != nil {
+// return "", err
+// }
+//
+// b := bytes.NewBuffer([]byte{})
+// err = tp.Execute(b, values)
+// if err != nil {
+// return "", err
+// }
+// return b.String(), err
+//}
diff --git a/go/resource/resource.go b/go/resource/resource.go
@@ -2,23 +2,18 @@ package resource
import (
"context"
- "fmt"
- "log"
- "strings"
)
// EntryFunc is a function signature for retrieving value for a key
type EntryFunc func(ctx context.Context) (string, error)
type CodeFunc func(sym string) ([]byte, error)
-type TemplateFunc func(sym string, sizer *Sizer) (string, error)
+type TemplateFunc func(sym string) (string, error)
type FuncForFunc func(sym string) (EntryFunc, error)
// Resource implementation are responsible for retrieving values and templates for symbols, and can render templates from value dictionaries.
type Resource interface {
- GetTemplate(sym string, sizer *Sizer) (string, error) // Get the template for a given symbol.
+ GetTemplate(sym string) (string, error) // Get the template for a given symbol.
GetCode(sym string) ([]byte, error) // Get the bytecode for the given symbol.
- RenderTemplate(sym string, values map[string]string, idx uint16, sizer *Sizer) (string, error) // Render the given data map using the template of the symbol.
- Render(sym string, values map[string]string, idx uint16, sizer *Sizer) (string, error) // Render full output.
FuncFor(sym string) (EntryFunc, error) // Resolve symbol content point for.
}
@@ -52,87 +47,6 @@ func(m *MenuResource) WithTemplateGetter(templateGetter TemplateFunc) *MenuResou
return m
}
-// render menu and all syms except sink, split sink into display chunks
-func(m *MenuResource) prepare(sym string, values map[string]string, idx uint16, sizer *Sizer) (map[string]string, error) {
- var sink string
- var sinkValues []string
- noSinkValues := make(map[string]string)
- for k, v := range values {
- sz, err := sizer.Size(k)
- if err != nil {
- return nil, err
- }
- if sz == 0 {
- sink = k
- sinkValues = strings.Split(v, "\n")
- v = ""
- log.Printf("found sink %s with field count %v", k, len(sinkValues))
- }
- noSinkValues[k] = v
- }
-
- if sink == "" {
- log.Printf("no sink found for sym %s", sym)
- return values, nil
- }
-
- s, err := m.render(sym, noSinkValues, 0, nil)
- if err != nil {
- return nil, err
- }
-
- remaining, ok := sizer.Check(s)
- if !ok {
- return nil, fmt.Errorf("capacity exceeded")
- }
-
- log.Printf("%v bytes available for sink split", remaining)
-
- l := 0
- tb := strings.Builder{}
- rb := strings.Builder{}
-
- sizer.AddCursor(0)
- for i, v := range sinkValues {
- log.Printf("processing sinkvalue %v: %s", i, v)
- l += len(v)
- if uint32(l) > remaining {
- if tb.Len() == 0 {
- return nil, fmt.Errorf("capacity insufficient for sink field %v", i)
- }
- rb.WriteString(tb.String())
- rb.WriteRune('\n')
- c := uint32(rb.Len())
- sizer.AddCursor(c)
- tb.Reset()
- l = 0
- }
- if tb.Len() > 0 {
- tb.WriteByte(byte(0x00))
- }
- tb.WriteString(v)
- }
-
- if tb.Len() > 0 {
- rb.WriteString(tb.String())
- }
-
- r := rb.String()
- r = strings.TrimRight(r, "\n")
-
- noSinkValues[sink] = r
-
- for i, v := range strings.Split(r, "\n") {
- log.Printf("nosinkvalue %v: %s", i, v)
- }
-
- return noSinkValues, nil
-}
-
-func(m *MenuResource) RenderTemplate(sym string, values map[string]string, idx uint16, sizer *Sizer) (string, error) {
- return DefaultRenderTemplate(m, sym, values, idx, sizer)
-}
-
func(m *MenuResource) FuncFor(sym string) (EntryFunc, error) {
return m.funcFunc(sym)
}
@@ -141,47 +55,7 @@ func(m *MenuResource) GetCode(sym string) ([]byte, error) {
return m.codeFunc(sym)
}
-func(m *MenuResource) GetTemplate(sym string, sizer *Sizer) (string, error) {
- return m.templateFunc(sym, sizer)
+func(m *MenuResource) GetTemplate(sym string) (string, error) {
+ return m.templateFunc(sym)
}
-func(m *MenuResource) render(sym string, values map[string]string, idx uint16, sizer *Sizer) (string, error) {
- var ok bool
- r := ""
- s, err := m.RenderTemplate(sym, values, idx, sizer)
- if err != nil {
- return "", err
- }
- log.Printf("rendered %v bytes for template", len(s))
- r += s
- if sizer != nil {
- _, ok = sizer.Check(r)
- if !ok {
- return "", fmt.Errorf("limit exceeded: %v", sizer)
- }
- }
- s, err = m.RenderMenu(idx)
- if err != nil {
- return "", err
- }
- log.Printf("rendered %v bytes for menu", len(s))
- r += s
- if sizer != nil {
- _, ok = sizer.Check(r)
- if !ok {
- return "", fmt.Errorf("limit exceeded: %v", sizer)
- }
- }
- return r, nil
-}
-
-func(m *MenuResource) Render(sym string, values map[string]string, idx uint16, sizer *Sizer) (string, error) {
- var err error
-
- values, err = m.prepare(sym, values, idx, sizer)
- if err != nil {
- return "", err
- }
-
- return m.render(sym, values, idx, sizer)
-}
diff --git a/go/resource/resource_test.go b/go/resource/resource_test.go
@@ -10,7 +10,7 @@ type TestSizeResource struct {
*MenuResource
}
-func getTemplate(sym string, sizer *Sizer) (string, error) {
+func getTemplate(sym string) (string, error) {
var tpl string
switch sym {
case "small":
diff --git a/go/resource/size.go b/go/resource/size.go
@@ -1,98 +0,0 @@
-package resource
-
-import (
- "bytes"
- "fmt"
- "log"
- "strings"
-
- "git.defalsify.org/festive/state"
-)
-
-type Sizer struct {
- outputSize uint32
- menuSize uint16
- memberSizes map[string]uint16
- totalMemberSize uint32
- crsrs []uint32
- sink string
-}
-
-func SizerFromState(st *state.State) (Sizer, error){
- sz := Sizer{
- outputSize: st.GetOutputSize(),
- menuSize: st.GetMenuSize(),
- memberSizes: make(map[string]uint16),
- }
-
- sizes, err := st.Sizes()
- if err != nil {
- return sz, err
- }
- for k, v := range sizes {
- if v == 0 {
- sz.sink = k
- }
- sz.memberSizes[k] = v
- sz.totalMemberSize += uint32(v)
- }
- return sz, nil
-}
-
-func(szr *Sizer) Check(s string) (uint32, bool) {
- l := uint32(len(s))
- if szr.outputSize > 0 {
- if l > szr.outputSize {
- log.Printf("sizer check fails with length %v: %s", l, szr)
- return 0, false
- }
- l = szr.outputSize - l
- }
- return l, true
-}
-
-func(szr *Sizer) String() string {
- var diff uint32
- if szr.outputSize > 0 {
- diff = szr.outputSize - szr.totalMemberSize - uint32(szr.menuSize)
- }
- return fmt.Sprintf("output: %v, member: %v, menu: %v, diff: %v", szr.outputSize, szr.totalMemberSize, szr.menuSize, diff)
-}
-
-func(szr *Sizer) Size(s string) (uint16, error) {
- r, ok := szr.memberSizes[s]
- if !ok {
- return 0, fmt.Errorf("unknown member: %s", s)
- }
- return r, nil
-}
-
-func(szr *Sizer) AddCursor(c uint32) {
- log.Printf("added cursor: %v", c)
- szr.crsrs = append(szr.crsrs, c)
-}
-
-func(szr *Sizer) GetAt(values map[string]string, idx uint16) (map[string]string, error) {
- if szr.sink == "" {
- return values, nil
- }
- outValues := make(map[string]string)
- for k, v := range values {
- if szr.sink == k {
- if idx >= uint16(len(szr.crsrs)) {
- return nil, fmt.Errorf("no more values in index")
- }
- c := szr.crsrs[idx]
- v = v[c:]
- nl := strings.Index(v, "\n")
- log.Printf("k %v v %v c %v nl %v", k, v, c, nl)
- if nl > 0 {
- v = v[:nl]
- }
- b := bytes.ReplaceAll([]byte(v), []byte{0x00}, []byte{0x0a})
- v = string(b)
- }
- outValues[k] = v
- }
- return outValues, nil
-}
diff --git a/go/resource/size_test.go b/go/resource/size_test.go
@@ -1,121 +0,0 @@
-package resource
-
-import (
- "testing"
-
- "git.defalsify.org/festive/state"
-)
-
-func TestSizeLimit(t *testing.T) {
- st := state.NewState(0).WithOutputSize(128)
- mrs := NewMenuResource().WithEntryFuncGetter(funcFor).WithTemplateGetter(getTemplate)
- rs := TestSizeResource{
- mrs,
- }
- st.Down("test")
- st.Add("foo", "inky", 4)
- st.Add("bar", "pinky", 10)
- st.Add("baz", "blinky", 0)
- st.Map("foo")
- st.Map("bar")
- st.Map("baz")
- st.SetMenuSize(32)
- szr, err := SizerFromState(&st)
- if err != nil {
- t.Fatal(err)
- }
-
- rs.PutMenu("1", "foo the foo")
- rs.PutMenu("2", "go to bar")
-
- tpl, err := rs.GetTemplate("small", &szr)
- if err != nil {
- t.Fatal(err)
- }
-
- vals, err := st.Get()
- if err != nil {
- t.Fatal(err)
- }
- _ = tpl
-
- _, err = rs.Render("small", vals, 0, &szr)
- if err != nil {
- t.Fatal(err)
- }
-
- rs.PutMenu("1", "foo the foo")
- rs.PutMenu("2", "go to bar")
-
- _, err = rs.Render("toobig", vals, 0, &szr)
- if err == nil {
- t.Fatalf("expected size exceeded")
- }
-}
-
-func TestSizePages(t *testing.T) {
- st := state.NewState(0).WithOutputSize(128)
- mrs := NewMenuResource().WithEntryFuncGetter(funcFor).WithTemplateGetter(getTemplate)
- rs := TestSizeResource{
- mrs,
- }
- st.Down("test")
- st.Add("foo", "inky", 4)
- st.Add("bar", "pinky", 10)
- st.Add("baz", "blinky", 20)
- st.Add("xyzzy", "inky pinky\nblinky clyde sue\ntinkywinky dipsy\nlala poo\none two three four five six seven\neight nine ten\neleven twelve", 0)
- st.Map("foo")
- st.Map("bar")
- st.Map("baz")
- st.Map("xyzzy")
- st.SetMenuSize(32)
- szr, err := SizerFromState(&st)
- if err != nil {
- t.Fatal(err)
- }
-
- vals, err := st.Get()
- if err != nil {
- t.Fatal(err)
- }
-
- rs.PutMenu("1", "foo the foo")
- rs.PutMenu("2", "go to bar")
-
- r, err := rs.Render("pages", vals, 0, &szr)
- if err != nil {
- t.Fatal(err)
- }
-
- expect := `one inky two pinky three blinky
-inky pinky
-blinky clyde sue
-tinkywinky dipsy
-lala poo`
-
-
- if r != expect {
- t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", expect, r)
- }
-
- rs.PutMenu("1", "foo the foo")
- rs.PutMenu("2", "go to bar")
-
- szr, err = SizerFromState(&st)
- if err != nil {
- t.Fatal(err)
- }
- r, err = rs.Render("pages", vals, 1, &szr)
- if err != nil {
- t.Fatal(err)
- }
-
- expect = `one inky two pinky three blinky
-one two three four five six seven
-eight nine ten
-eleven twelve`
- if r != expect {
- t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", expect, r)
- }
-
-}
diff --git a/go/resource/state.go b/go/resource/state.go
@@ -27,18 +27,3 @@ func(sr *StateResource) WithState(st *state.State) *StateResource {
sr.st = st
return sr
}
-
-func(sr *StateResource) SetMenuBrowse(selector string, title string, back bool) error {
- var err error
- next, prev := sr.st.Sides()
-
- if back {
- if prev {
- err = sr.Resource.SetMenuBrowse(selector, title, true)
- }
- } else if next {
- err = sr.Resource.SetMenuBrowse(selector, title, false)
-
- }
- return err
-}
diff --git a/go/resource/state_test.go b/go/resource/state_test.go
@@ -12,90 +12,3 @@ func TestStateResourceInit(t *testing.T) {
_ = ToStateResource(rs).WithState(&st)
_ = NewStateResource(&st)
}
-
-func TestStateBrowseNoSink(t *testing.T) {
- st := state.NewState(0)
- st.Down("root")
-
- rs := NewStateResource(&st)
- rs.PutMenu("1", "foo")
- rs.PutMenu("2", "bar")
- err := rs.SetMenuBrowse("11", "next", false)
- if err != nil {
- t.Fatal(err)
- }
- err = rs.SetMenuBrowse("22", "prev", true)
- if err != nil {
- t.Fatal(err)
- }
- s, err := rs.RenderMenu(0)
- if err != nil {
- t.Fatal(err)
- }
-
- expect := `1:foo
-2:bar`
- if s != expect {
- t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", expect, s)
- }
-}
-
-
-func TestStateBrowseSink(t *testing.T) {
- st := state.NewState(0)
- st.Down("root")
-
- rs := NewStateResource(&st)
- rs.PutMenu("1", "foo")
- rs.PutMenu("2", "bar")
- err := rs.SetMenuBrowse("11", "next", false)
- if err != nil {
- t.Fatal(err)
- }
- err = rs.SetMenuBrowse("22", "prev", true)
- if err != nil {
- t.Fatal(err)
- }
- s, err := rs.RenderMenu(0)
- if err != nil {
- t.Fatal(err)
- }
-
- expect := `1:foo
-2:bar
-11:next`
- if s != expect {
- t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", expect, s)
- }
-
- idx, err := st.Next()
- if err != nil {
- t.Fatal(err)
- }
- if idx != 1 {
- t.Fatalf("expected idx 1, got %v", idx)
- }
- rs = NewStateResource(&st)
- rs.PutMenu("1", "foo")
- rs.PutMenu("2", "bar")
- err = rs.SetMenuBrowse("11", "next", false)
- if err != nil {
- t.Fatal(err)
- }
- err = rs.SetMenuBrowse("22", "prev", true)
- if err != nil {
- t.Fatal(err)
- }
- s, err = rs.RenderMenu(idx)
- if err != nil {
- t.Fatal(err)
- }
-
- expect = `1:foo
-2:bar
-11:next
-22:prev`
- if s != expect {
- t.Fatalf("expected:\n\t%s\ngot:\n\t%s\n", expect, s)
- }
-}