WIP Factor page, sizer, menu to render package, cachemap to page
This commit is contained in:
parent
6a1611a0c8
commit
7e01f2725d
131
go/cache/cache.go
vendored
131
go/cache/cache.go
vendored
@ -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
|
||||
}
|
||||
|
82
go/cache/cache_test.go
vendored
82
go/cache/cache_test.go
vendored
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,7 @@
|
||||
package menu
|
||||
package render
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
)
|
||||
|
||||
// BrowseConfig defines the availability and display parameters for page browsing.
|
||||
@ -33,6 +32,7 @@ type Menu struct {
|
||||
pageCount uint16
|
||||
canNext bool
|
||||
canPrevious bool
|
||||
outputSize uint16
|
||||
}
|
||||
|
||||
// NewMenu creates a new Menu with an explicit page count.
|
||||
@ -46,17 +46,21 @@ func(m *Menu) WithPageCount(pageCount uint16) *Menu {
|
||||
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})
|
||||
log.Printf("menu %v", m.menu)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -64,6 +68,11 @@ func(m *Menu) Put(selector string, title string) error {
|
||||
//
|
||||
// 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
|
||||
@ -81,28 +90,30 @@ func(m *Menu) Render(idx uint16) (string, error) {
|
||||
}
|
||||
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)
|
||||
}
|
||||
if m.browse.NextAvailable {
|
||||
m.canNext = true
|
||||
}
|
||||
if m.browse.PreviousAvailable {
|
||||
m.canPrevious = true
|
||||
}
|
||||
|
||||
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 {
|
||||
@ -129,4 +140,15 @@ func(m *Menu) shiftMenu() (string, string, error) {
|
||||
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
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package menu
|
||||
package render
|
||||
|
||||
import (
|
||||
"testing"
|
||||
@ -23,6 +23,12 @@ func TestMenuInit(t *testing.T) {
|
||||
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) {
|
||||
@ -48,14 +54,6 @@ func TestMenuBrowse(t *testing.T) {
|
||||
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)
|
||||
@ -68,14 +66,6 @@ func TestMenuBrowse(t *testing.T) {
|
||||
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)
|
289
go/render/page.go
Normal file
289
go/render/page.go
Normal file
@ -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)
|
||||
}
|
||||
|
||||
|
@ -1,12 +1,10 @@
|
||||
package resource
|
||||
package render
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"git.defalsify.org/festive/state"
|
||||
)
|
||||
|
||||
type Sizer struct {
|
||||
@ -18,28 +16,34 @@ type Sizer struct {
|
||||
sink string
|
||||
}
|
||||
|
||||
func SizerFromState(st *state.State) (Sizer, error){
|
||||
sz := Sizer{
|
||||
outputSize: st.GetOutputSize(),
|
||||
menuSize: st.GetMenuSize(),
|
||||
func NewSizer(outputSize uint32) *Sizer {
|
||||
return &Sizer{
|
||||
outputSize: outputSize,
|
||||
memberSizes: make(map[string]uint16),
|
||||
}
|
||||
}
|
||||
|
||||
sizes, err := st.Sizes()
|
||||
if err != nil {
|
||||
return sz, err
|
||||
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)
|
||||
}
|
||||
for k, v := range sizes {
|
||||
if v == 0 {
|
||||
sz.sink = k
|
||||
}
|
||||
sz.memberSizes[k] = v
|
||||
sz.totalMemberSize += uint32(v)
|
||||
szr.memberSizes[key] = size
|
||||
if size == 0 {
|
||||
szr.sink = key
|
||||
}
|
||||
return sz, nil
|
||||
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 {
|
181
go/render/size_test.go
Normal file
181
go/render/size_test.go
Normal file
@ -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)
|
||||
}
|
||||
|
||||
}
|
@ -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)
|
||||
|
@ -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
|
||||
//}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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":
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user