Code

client: Added support for querying the server version.
[sysdb/go.git] / proto / proto.go
index 5aadf9aee1885e45f8cc9157545683f6292d57f4..a58725d6f81a6c7ad451390974e311b63a08b0f7 100644 (file)
@@ -30,7 +30,10 @@ package proto
 
 import (
        "encoding/binary"
+       "encoding/json"
+       "fmt"
        "io"
+       "strings"
 )
 
 // Network byte order.
@@ -77,8 +80,25 @@ const (
        // 'TIMESERIES' command in the server.
        ConnectionTimeseries = Status(7)
 
-       // ConnectionExpr is the internal state for expression parsing.
-       ConnectionExpr = Status(100)
+       // ConnectionMatcher is the internal state for parsing matchers.
+       ConnectionMatcher = Status(100)
+       // ConnectionExpr is the internal state for parsing expressions.
+       ConnectionExpr = Status(101)
+
+       // ConnectionServerVersion is the state requesting the server version.
+       ConnectionServerVersion = Status(1000)
+)
+
+// The DataType describes the type of data in a ConnectionData message.
+type DataType int
+
+const (
+       // A HostList can be unmarshaled to []sysdb.Host.
+       HostList DataType = iota
+       // A Host can be unmarshaled to sysdb.Host.
+       Host
+       // A Timeseries can be unmarshaled to sysdb.Timeseries.
+       Timeseries
 )
 
 // A Message represents a raw message of the SysDB front-end protocol.
@@ -87,13 +107,14 @@ type Message struct {
        Raw  []byte
 }
 
-// Decodes reads a raw message encoded in the SysDB wire format from r. The
-// raw body of the message will still be encoded in the wire format.
+// Read reads a raw message encoded in the SysDB wire format from r. The
+// function parses the header but the raw body of the message will still be
+// encoded in the wire format.
 //
 // The reader has to be in blocking mode. Otherwise, the client and server
 // will be out of sync after reading a partial message and cannot recover from
 // that.
-func Decode(r io.Reader) (*Message, error) {
+func Read(r io.Reader) (*Message, error) {
        var header [8]byte
        if _, err := io.ReadFull(r, header[:]); err != nil {
                return nil, err
@@ -109,13 +130,13 @@ func Decode(r io.Reader) (*Message, error) {
        return &Message{Status(typ), msg}, nil
 }
 
-// Encode writes a raw message to w. The raw body of m has to be encoded in
-// the SysDB wire format.
+// Write writes a raw message to w. The raw body of m has to be encoded in the
+// SysDB wire format. The function adds the right header to the message.
 //
 // The writer has to be in blocking mode. Otherwise, the client and server
 // will be out of sync after writing a partial message and cannot recover from
 // that.
-func Encode(w io.Writer, m *Message) error {
+func Write(w io.Writer, m *Message) error {
        var header [8]byte
        nbo.PutUint32(header[:4], uint32(m.Type))
        nbo.PutUint32(header[4:], uint32(len(m.Raw)))
@@ -129,4 +150,44 @@ func Encode(w io.Writer, m *Message) error {
        return nil
 }
 
+// DataType determines the type of data in a ConnectionData message.
+func (m Message) DataType() (DataType, error) {
+       if m.Type != ConnectionData {
+               return 0, fmt.Errorf("message is not of type DATA")
+       }
+
+       typ := nbo.Uint32(m.Raw[:4])
+       switch Status(typ) {
+       case ConnectionList, ConnectionLookup:
+               return HostList, nil
+       case ConnectionFetch:
+               return Host, nil
+       case ConnectionTimeseries:
+               return Timeseries, nil
+       }
+       return 0, fmt.Errorf("unknown DATA type %d", typ)
+}
+
+// Unmarshal parses the raw body of m and stores the result in the value
+// pointed to by v which has to match the type of the message and its data.
+func Unmarshal(m *Message, v interface{}) error {
+       if m.Type != ConnectionData {
+               return fmt.Errorf("unmarshaling message of type %d not supported", m.Type)
+       }
+       if len(m.Raw) == 0 { // empty command
+               return nil
+       } else if len(m.Raw) < 4 {
+               return fmt.Errorf("DATA message body too short")
+       }
+       return json.Unmarshal(m.Raw[4:], v)
+}
+
+// EscapeString returns the quoted and escaped string s suitable for use
+// in a query.
+func EscapeString(s string) string {
+       // Currently, the server only handles double-quotes.
+       // Backslashes do not serve any special purpose.
+       return "'" + strings.Replace(s, "'", "''", -1) + "'"
+}
+
 // vim: set tw=78 sw=4 sw=4 noexpandtab :