apt/message_reader.go (91 lines of code) (raw):
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package apt
import (
"bufio"
"context"
"errors"
"fmt"
"strconv"
"strings"
)
var errEmptyMessage = errors.New("empty message")
// MessageReader supports reading Apt messages.
type MessageReader struct {
reader *bufio.Reader
message *Message
}
// NewAptMessageReader returns an AptMessageReader.
func NewAptMessageReader(r *bufio.Reader) *MessageReader {
return &MessageReader{reader: r}
}
// ReadMessage reads lines from `reader` until a complete message is received.
func (r *MessageReader) ReadMessage(ctx context.Context) (*Message, error) {
for {
select {
case <-ctx.Done():
return nil, ctx.Err()
default:
}
line, err := r.reader.ReadString('\n')
if err != nil {
return nil, err
}
line = strings.TrimSpace(line)
if line == "" {
if r.message == nil {
return nil, errEmptyMessage
}
// Message is done, return and reset.
msg := r.message
r.message = nil
return msg, nil
}
if r.message == nil {
r.message = &Message{}
if err := r.parseHeader(line); err != nil {
return nil, err
}
} else {
if err := r.parseField(line); err != nil {
return nil, err
}
}
}
}
func (r *MessageReader) parseHeader(line string) error {
if line == "" {
return errors.New("empty message header")
}
if r.message.code != 0 || r.message.description != "" {
return errors.New("double parsing header")
}
line = strings.TrimSpace(line)
parts := strings.SplitN(line, " ", 2)
if len(parts) != 2 {
return fmt.Errorf("malformed header %q, not enough parts", line)
}
code, err := strconv.Atoi(strings.TrimSpace(parts[0]))
if err != nil {
return fmt.Errorf("malformed header %q, code is not an integer", line)
}
r.message.code = code
r.message.description = strings.TrimSpace(parts[1])
return nil
}
func (r *MessageReader) parseField(line string) error {
if line == "" {
return errors.New("empty message field")
}
line = strings.TrimSpace(line)
parts := strings.SplitN(line, ":", 2)
if len(parts) < 2 {
return fmt.Errorf("malformed field %q, not enough parts", line)
}
if r.message.fields == nil {
r.message.fields = make(map[string][]string)
}
key := strings.TrimSpace(parts[0])
value := strings.TrimSpace(parts[1])
if key == "" || value == "" {
return fmt.Errorf("malformed field %q, empty key or value", line)
}
fieldlist := r.message.fields[key]
fieldlist = append(fieldlist, value)
r.message.fields[key] = fieldlist
return nil
}