diff options
author | xengineering <me@xengineering.eu> | 2023-06-29 21:39:54 +0200 |
---|---|---|
committer | xengineering <me@xengineering.eu> | 2023-06-29 21:39:54 +0200 |
commit | c8544b19df055235b9106ff296f0a5fe7cb1fe91 (patch) | |
tree | b3da91a3d41746620ca67891797b55df8edfdc09 | |
parent | 9afa580f3f3207ac449be86c1a305cb716b77f76 (diff) | |
parent | 3df1a88c726c08704e8b71c467bd8e11c9a52db6 (diff) | |
download | limox-c8544b19df055235b9106ff296f0a5fe7cb1fe91.tar limox-c8544b19df055235b9106ff296f0a5fe7cb1fe91.tar.zst limox-c8544b19df055235b9106ff296f0a5fe7cb1fe91.zip |
Merge branch 'element-buffer'
This adds xengineering.eu/limox/xmpp/elementBuffer which is a buffer
for a collection of XML tokens which are further processed after a full
XML element is collected.
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | xmpp/element_buffer.go | 61 | ||||
-rw-r--r-- | xmpp/element_buffer_test.go | 55 | ||||
-rw-r--r-- | xmpp/stream_pair.go | 20 |
4 files changed, 135 insertions, 3 deletions
@@ -48,6 +48,6 @@ install: $(LIMOX) install -Dm 644 $(DESKTOP) $(DESKTOP_INSTALL) tests: - go test $(LIMOX_GO_PACKAGE)/... + go test -test.v $(LIMOX_GO_PACKAGE)/... imgs: $(IMGS) diff --git a/xmpp/element_buffer.go b/xmpp/element_buffer.go new file mode 100644 index 0000000..7792db2 --- /dev/null +++ b/xmpp/element_buffer.go @@ -0,0 +1,61 @@ +package xmpp + +import ( + "encoding/xml" +) + +// elementBuffer is a struct to store multiple values of type xml.Token until +// they form a complete XML element with opening and closing tag which is +// suitable to be passed to an appropriate handler function. +type elementBuffer struct { + tokens []xml.Token + end xml.EndElement + level int +} + +// newElementBuffer returns a new initialized elementBuffer struct. +func newElementBuffer() elementBuffer { + buf := elementBuffer{} + buf.reset() + return buf +} + +// FIXME this function needs essential error handling for corner cases! +// +// add is able to add a new xml.Token to the buffer. There are some rules +// checked to ensure a correct and consistent elementBuffer which are checked. +// If one of these checks fail the token is not added and a corresponding error +// is returned. +func (e *elementBuffer) add(t xml.Token) error { + switch t.(type) { + case xml.StartElement: + e.level += 1 + case xml.EndElement: + e.level -= 1 + } + e.tokens = append(e.tokens, t) + return nil +} + +// FIXME isComplete would be true if a stream with only one XML comment is +// passed to the buffer. This might be unexpected behaviour. +// +// isComplete returns true if the buffer contains a slice of XML tokens which +// form a complete XML element starting with an xml.StartElement and closing +// with the corresponding xml.EndElement. +func (e *elementBuffer) isComplete() bool { + return (len(e.tokens) > 0 && e.level == 0) +} + +// reset returns the content of the buffer as a slice of XML tokens and resets +// the buffer to the initial state. This function can be used to initialize the +// elementBuffer struct. In that case the return value can be ignored. +func (e *elementBuffer) reset() (buf []xml.Token) { + retval := e.tokens + + e.tokens = make([]xml.Token, 0) + e.end = xml.EndElement{} + e.level = 0 + + return retval +} diff --git a/xmpp/element_buffer_test.go b/xmpp/element_buffer_test.go new file mode 100644 index 0000000..113a2c4 --- /dev/null +++ b/xmpp/element_buffer_test.go @@ -0,0 +1,55 @@ +package xmpp + +import ( + "encoding/xml" + "strings" + "testing" +) + +// bufTest is a struct containing a test point for the +// xengineering.eu/limox/xmpp.elementBuffer. It contains a test XML string +// which has to be exactly one XML element and an array of indentation levels +// which have to be checked after each token which is parsed. +type bufTest struct { + xml string + levels []int +} + +func TestElementBuffer(t *testing.T) { + tests := []bufTest{ + bufTest{`<stream></stream>`, []int{1, 0}}, + bufTest{`<stream/>`, []int{1, 0}}, + bufTest{`<a><b>testing</b></a>`, []int{1, 2, 2, 1, 0}}, + bufTest{`<a><!-- comment --><b>testing</b></a>`, []int{1, 1, 2, 2, 1, 0}}, + bufTest{`<!-- comment --><a><b>testing</b></a>`, []int{0, 1, 2, 2, 1, 0}}, + } + + for _, v := range tests { + r := strings.NewReader(v.xml) + d := xml.NewDecoder(r) + b := newElementBuffer() + + i := 0 + + for { + token, err := d.Token() + if err != nil { + if i != len(v.levels) { + t.Fatalf("Stopped parsing at unexpected index due to error `%v`!\n", err) + } + break + } + + err = b.add(token) + if err != nil { + t.Fatalf("add(token) failed with error `%v`!\n", err) + } + + if b.level != v.levels[i] { + t.Fatalf("Indent level of xmpp.elementBuffer %d does not match value given by test data %d!\n", b.level, v.levels[i]) + } + + i += 1 + } + } +} diff --git a/xmpp/stream_pair.go b/xmpp/stream_pair.go index e02cca6..b13d8a3 100644 --- a/xmpp/stream_pair.go +++ b/xmpp/stream_pair.go @@ -9,6 +9,8 @@ func runStreamPair(s *session) { openStream(s) defer closeStream(s) + buf := newElementBuffer() + for { select { case data := <-s.in: @@ -18,8 +20,22 @@ func runStreamPair(s *session) { default: log.Printf("Unknown data '%d'!\n", data) } - case _ = <-s.rx: - // TODO route received XML token here + case t := <-s.rx: + err := buf.add(t) + if err != nil { + log.Printf("Could not add XML token to buffer: %v\n", err) + return + } + if buf.isComplete() { + element := buf.reset() + // TODO handle XML element here - this is just a dummy: + switch start := element[0].(type) { + case xml.StartElement: + log.Printf("Got XML element `%s`\n", start.Name.Local) + default: + log.Println("No xml.StartElement at start of element buffer!") + } + } } } } |