package main import ( "fmt" "strings" ) type Message struct { Tags map[string]string Source string Command command Parameters []string Raw string parseIndex int } func (m *Message) ParseMessage() error { m.parseIndex = 0 m.consumeSpaces() if m.consume('@') { m.Tags, _ = m.parseTags() } m.consumeSpaces() if m.consume(':') { m.Source, _ = m.parseSource() } m.consumeSpaces() m.Command, _ = m.parseCommand() m.consumeSpaces() m.Parameters, _ = m.parseParameters() return nil } func (m *Message) parseTags() (map[string]string, error) { tags := make(map[string]string) for { next := m.findNext("=", ";") if next == -1 { break } else if m.Raw[m.parseIndex+next] == '=' { key := m.Raw[m.parseIndex : m.parseIndex+next] m.parseIndex += next + 1 eot := m.findNext(";", " ") tags[key] = m.Raw[m.parseIndex : m.parseIndex+eot] m.parseIndex += len(tags[key]) + 1 } else if m.Raw[m.parseIndex+next] == ';' { key := m.Raw[m.parseIndex : m.parseIndex+next] m.parseIndex += next + 1 tags[key] = "" } else { break } } return tags, nil } func (m *Message) parseSource() (string, error) { start := m.parseIndex endofparse := strings.Index(m.Raw[m.parseIndex:], " ") if endofparse == -1 { return "", fmt.Errorf("end of string encountered while parsing tags") } m.parseIndex += endofparse return m.Raw[start:m.parseIndex], nil } func (m *Message) parseCommand() (command, error) { start := m.parseIndex endofparse := strings.Index(m.Raw[m.parseIndex:], " ") if endofparse == -1 { return "", fmt.Errorf("end of string encountered while parsing command") } m.parseIndex += endofparse return command(m.Raw[start:m.parseIndex]), nil } func (m *Message) parseParameters() ([]string, error) { params := []string{} for m.parseIndex <= len(m.Raw) { m.consumeSpaces() if m.consume(':') { // "run until end of line" params = append(params, m.Raw[m.parseIndex:]) break } endofparse := strings.Index(m.Raw[m.parseIndex:], " ") if endofparse == -1 { // no further params after this one p := strings.TrimSuffix(m.Raw[m.parseIndex:], " ") if len(p) > 0 { // reject empty trailing params params = append(params, p) } break } params = append(params, m.Raw[m.parseIndex:m.parseIndex+endofparse]) m.parseIndex += endofparse } return params, nil } func (m *Message) consumeSpaces() { if len(m.Raw) <= m.parseIndex { return } for len(m.Raw) > m.parseIndex && m.Raw[m.parseIndex] == ' ' { m.parseIndex++ } } func (m *Message) consume(r byte) bool { if len(m.Raw) <= m.parseIndex { return false } if m.Raw[m.parseIndex] == r { m.parseIndex++ return true } return false } func (m *Message) findNext(bs ...string) int { if len(bs) == 0 { return 0 } r := -1 for _, b := range bs { i := strings.Index(m.Raw[m.parseIndex:], b) if r == -1 || (i != -1 && i < r) { r = i } } return r } func (m *Message) ToBytes() []byte { return nil }