builtin_fallback.go (1326B)
1 package tplc 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "strconv" 8 9 "github.com/valyala/bytebufferpool" 10 11 "go.lair.cx/go-core/net/htmlx" 12 "go.lair.cx/tpls/internal/stacks" 13 ) 14 15 // TagFallback skips current tag. 16 func TagFallback(name string) TagHandler { 17 return func(_ *Builder, w *Writer, t *htmlx.Tokenizer) error { 18 if t.CurrentType() == htmlx.SelfClosingTagToken { 19 return nil 20 } 21 22 stackBuf := bytebufferpool.Get() 23 defer bytebufferpool.Put(stackBuf) 24 25 stack := stacks.NewByteStack(stackBuf.B) 26 27 loop: 28 for { 29 switch t.Next() { 30 case htmlx.StartTagToken: 31 tagName, _ := t.TagName() 32 stack.Put(tagName) 33 34 case htmlx.EndTagToken: 35 tagName, _ := t.TagName() 36 37 // Remove items in stack until the matched starting tag is found. 38 // If the tag is not exists in stack, 39 // - Root tag's closing. 40 // - Unexpected closing tag. 41 for { 42 item, ok := stack.Pop() 43 if !ok { // Tag is not found: 44 if string(tagName) == name { 45 break loop 46 } 47 return fmt.Errorf( 48 "unexpected closing tag: %s", 49 strconv.Quote(string(tagName)), 50 ) 51 } 52 if bytes.Equal(item, tagName) { 53 break 54 } 55 } 56 57 case htmlx.ErrorToken: 58 err := t.Err() 59 if err == io.EOF { 60 return io.ErrUnexpectedEOF 61 } else { 62 return err 63 } 64 } 65 } 66 67 return nil 68 } 69 }