create single dpkg bufio reader

This commit is contained in:
Alex Goodman 2020-05-16 07:59:34 -04:00 committed by Alfredo Deza
parent 09cf5b9862
commit d56d157e90
2 changed files with 61 additions and 48 deletions

View file

@ -10,7 +10,7 @@ import (
) )
// TODO: consider keeping the remaining values as an embedded map // TODO: consider keeping the remaining values as an embedded map
type DpkgEntry struct { type Entry struct {
Package string `mapstructure:"Package"` Package string `mapstructure:"Package"`
Architecture string `mapstructure:"Architecture"` Architecture string `mapstructure:"Architecture"`
DependsPkgs string `mapstructure:"Depends"` DependsPkgs string `mapstructure:"Depends"`
@ -60,29 +60,49 @@ type DpkgEntry struct {
// Triggers-Awaited (internal) // Triggers-Awaited (internal)
// Triggers-Pending (internal) // Triggers-Pending (internal)
// Version // Version
//
var EndOfPackages = fmt.Errorf("no more packages to read") var endOfPackages = fmt.Errorf("no more packages to read")
func Read(reader io.Reader) (entry DpkgEntry, err error) { func ParseEntries(reader io.Reader) ([]Entry, error) {
buff := bufio.NewReader(reader) buffedReader := bufio.NewReader(reader)
var entries = make([]Entry, 0)
for {
entry, err := parseEntry(buffedReader)
if err != nil {
if err == endOfPackages {
break
}
return nil, err
}
entries = append(entries, entry)
}
return entries, nil
}
func parseEntry(reader *bufio.Reader) (entry Entry, err error) {
dpkgFields := make(map[string]string) dpkgFields := make(map[string]string)
var key string var key string
for { for {
line, ioerr := buff.ReadString('\n') line, err := reader.ReadString('\n')
fmt.Printf("line:'%+v' err:'%+v'\n", line, ioerr) if err != nil {
if ioerr != nil { if err == io.EOF {
if ioerr == io.EOF { return Entry{}, endOfPackages
return DpkgEntry{}, EndOfPackages
} }
return DpkgEntry{}, ioerr return Entry{}, err
} }
line = strings.TrimRight(line, "\n") line = strings.TrimRight(line, "\n")
// stop if there is no contents in line // empty line indicates end of entry
if len(line) == 0 { if len(line) == 0 {
// if the entry has not started, keep parsing lines
if len(dpkgFields) == 0{
continue
}
break break
} }
@ -90,12 +110,12 @@ func Read(reader io.Reader) (entry DpkgEntry, err error) {
case strings.HasPrefix(line, " "): case strings.HasPrefix(line, " "):
// a field-body continuation // a field-body continuation
if len(key) == 0 { if len(key) == 0 {
return DpkgEntry{}, fmt.Errorf("no match for continuation: line: '%s'", line) return Entry{}, fmt.Errorf("no match for continuation: line: '%s'", line)
} }
val, ok := dpkgFields[key] val, ok := dpkgFields[key]
if !ok { if !ok {
return DpkgEntry{}, fmt.Errorf("no previous key exists, expecting: %s", key) return Entry{}, fmt.Errorf("no previous key exists, expecting: %s", key)
} }
// concatenate onto previous value // concatenate onto previous value
val = fmt.Sprintf("%s\n %s", val, strings.TrimSpace(line)) val = fmt.Sprintf("%s\n %s", val, strings.TrimSpace(line))
@ -107,42 +127,21 @@ func Read(reader io.Reader) (entry DpkgEntry, err error) {
val := strings.TrimSpace(line[i+1:]) val := strings.TrimSpace(line[i+1:])
if _, ok := dpkgFields[key]; ok { if _, ok := dpkgFields[key]; ok {
return DpkgEntry{}, fmt.Errorf("duplicate key discovered: %s", key) return Entry{}, fmt.Errorf("duplicate key discovered: %s", key)
} }
dpkgFields[key] = val dpkgFields[key] = val
} else { } else {
return DpkgEntry{}, fmt.Errorf("cannot parse field from line: '%s'", line) return Entry{}, fmt.Errorf("cannot parse field from line: '%s'", line)
} }
} }
} }
fmt.Println("OUTOFLOOP")
// map -> struct
err = mapstructure.Decode(dpkgFields, &entry) err = mapstructure.Decode(dpkgFields, &entry)
if err != nil { if err != nil {
return DpkgEntry{}, err return Entry{}, err
} }
return entry, nil return entry, nil
} }
func ReadAllDpkgEntries(reader io.Reader) ([]DpkgEntry, error) {
var entries = make([]DpkgEntry, 0)
for {
// Read() until error
entry, err := Read(reader)
fmt.Printf("entry:'%+v'\n\terr:%+v\n", entry, err)
if err != nil {
if err == EndOfPackages {
break
}
return nil, err
}
entries = append(entries, entry)
}
return entries, nil
}

View file

@ -1,13 +1,14 @@
package dpkg package dpkg
import ( import (
"bufio"
"os" "os"
"testing" "testing"
"github.com/go-test/deep" "github.com/go-test/deep"
) )
func compareEntries(t *testing.T, left, right DpkgEntry) { func compareEntries(t *testing.T, left, right Entry) {
t.Helper() t.Helper()
if diff := deep.Equal(left, right); diff != nil { if diff := deep.Equal(left, right); diff != nil {
t.Error(diff) t.Error(diff)
@ -17,11 +18,11 @@ func compareEntries(t *testing.T, left, right DpkgEntry) {
func TestSinglePackage(t *testing.T) { func TestSinglePackage(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
expected DpkgEntry expected Entry
}{ }{
{ {
name: "Test Single Package", name: "Test Single Package",
expected: DpkgEntry{ expected: Entry{
Package: "apt", Package: "apt",
Status: "install ok installed", Status: "install ok installed",
Priority: "required", Priority: "required",
@ -49,8 +50,16 @@ func TestSinglePackage(t *testing.T) {
if err != nil { if err != nil {
t.Fatal("Unable to read test_fixtures/single: ", err) t.Fatal("Unable to read test_fixtures/single: ", err)
} }
defer func() {
err := file.Close()
if err != nil {
panic(err)
}
}()
entry, err := Read(file) reader := bufio.NewReader(file)
entry, err := parseEntry(reader)
if err != nil { if err != nil {
t.Fatal("Unable to read file contents: ", err) t.Fatal("Unable to read file contents: ", err)
} }
@ -61,14 +70,14 @@ func TestSinglePackage(t *testing.T) {
} }
} }
func TestMultiplePackage(t *testing.T) { func TestMultiplePackages(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
expected []DpkgEntry expected []Entry
}{ }{
{ {
name: "Test Multiple Package", name: "Test Multiple Package",
expected: []DpkgEntry{ expected: []Entry{
{ {
Package: "tzdata", Package: "tzdata",
Status: "install ok installed", Status: "install ok installed",
@ -110,9 +119,14 @@ func TestMultiplePackage(t *testing.T) {
if err != nil { if err != nil {
t.Fatal("Unable to read: ", err) t.Fatal("Unable to read: ", err)
} }
defer file.Close() defer func() {
err := file.Close()
if err != nil {
panic(err)
}
}()
entries, err := ReadAllDpkgEntries(file) entries, err := ParseEntries(file)
if err != nil { if err != nil {
t.Fatal("Unable to read file contents: ", err) t.Fatal("Unable to read file contents: ", err)
} }