From 3efa1fc8a98d2d90c204c7bd5a0e2519e1ac7745 Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Wed, 22 Nov 2017 22:04:14 -0500 Subject: split the parser up, and have read methods like write emethods --- frontend.go | 369 ++++-------------------------------------------------------- 1 file changed, 24 insertions(+), 345 deletions(-) (limited to 'frontend.go') diff --git a/frontend.go b/frontend.go index 604e7af..285bbd9 100644 --- a/frontend.go +++ b/frontend.go @@ -2,11 +2,8 @@ package libfastimport import ( "bufio" - "fmt" "io" "os" - "strconv" - "strings" "git.lukeshu.com/go/libfastimport/textproto" ) @@ -20,378 +17,60 @@ func (e UnsupportedCommand) Error() string { // A Frontend is something that produces a fast-import stream; the // Frontend object provides methods for reading from it. type Frontend struct { - fir *textproto.FIReader - cbw *textproto.CatBlobWriter - w *bufio.Writer + fastImport *parser + catBlobWrite *textproto.CatBlobWriter + catBlobFlush *bufio.Writer - inCommit bool - - cmd chan Cmd - err error + onErr func(error) error } func NewFrontend(fastImport io.Reader, catBlob io.Writer, onErr func(error) error) *Frontend { ret := &Frontend{} - ret.fir = textproto.NewFIReader(fastImport) + + ret.fastImport = newParser(textproto.NewFIReader(fastImport)) + if catBlob == nil { catBlob = os.Stdout } - ret.w = bufio.NewWriter(catBlob) - ret.cbw = textproto.NewCatBlobWriter(ret.w) - ret.cmd = make(chan Cmd) - go func() { - ret.err = ret.parse() - if onErr != nil { - ret.err = onErr(ret.err) - } - close(ret.cmd) - }() - return ret -} - -func (f *Frontend) nextLine() (line string, err error) { - for { - line, err = f.fir.ReadLine() - if err != nil { - return - } - switch { - case strings.HasPrefix(line, "#"): - f.cmd <- CmdComment{Comment: line[1:]} - case strings.HasPrefix(line, "cat-blob "): - // 'cat-blob' SP LF - f.cmd <- CmdCatBlob{DataRef: trimLinePrefix(line, "cat-blob ")} - case strings.HasPrefix(line, "get-mark :"): - // 'get-mark' SP ':' LF - c := CmdGetMark{} - c.Mark, err = strconv.Atoi(trimLinePrefix(line, "get-mark :")) - if err != nil { - line = "" - err = fmt.Errorf("get-mark: %v", err) - return - } - f.cmd <- c - default: - return - } - } -} + ret.catBlobFlush = bufio.NewWriter(catBlob) + ret.catBlobWrite = textproto.NewCatBlobWriter(ret.catBlobFlush) -func (f *Frontend) parse() error { - line, err := f.nextLine() - if err != nil { - return err + if onErr == nil { + onErr = func(e error) error { return e } } - for { - switch { - case line == "blob\n": - // 'blob' LF - // mark? - // data - c := CmdBlob{} - line, err = f.nextLine() - if err != nil { - return err - } - if strings.HasPrefix(line, "mark :") { - c.Mark, err = strconv.Atoi(trimLinePrefix(line, "mark :")) - if err != nil { - return err - } - line, err = f.nextLine() - if err != nil { - return err - } - } - if !strings.HasPrefix(line, "data ") { - return fmt.Errorf("Unexpected command in blob: %q", line) - } - c.Data, err = parse_data(line) - if err != nil { - return err - } - f.cmd <- c - case line == "checkpoint\n": - f.cmd <- CmdCheckpoint{} - case line == "done\n": - f.cmd <- CmdDone{} - case strings.HasPrefix(line, "commit "): - // 'commit' SP LF - // mark? - // ('author' (SP )? SP LT GT SP LF)? - // 'committer' (SP )? SP LT GT SP LF - // data - // ('from' SP LF)? - // ('merge' SP LF)* - // (filemodify | filedelete | filecopy | filerename | filedeleteall | notemodify)* - c := CmdCommit{Ref: trimLinePrefix(line, "commit ")} + ret.onErr = onErr - line, err = f.nextLine() - if err != nil { - return err - } - if strings.HasPrefix(line, "mark :") { - c.Mark, err = strconv.Atoi(trimLinePrefix(line, "mark :")) - if err != nil { - return err - } - line, err = f.nextLine() - if err != nil { - return err - } - } - if strings.HasPrefix(line, "author ") { - author, err := textproto.ParseIdent(trimLinePrefix(line, "author ")) - if err != nil { - return err - } - c.Author = &author - line, err = f.nextLine() - if err != nil { - return err - } - } - if !strings.HasPrefix(line, "committer ") { - return fmt.Errorf("commit: expected committer command: %v", line) - } - c.Committer, err = textproto.ParseIdent(trimLinePrefix(line, "committer ")) - if err != nil { - return err - } - - line, err = f.nextLine() - if err != nil { - return err - } - if !strings.HasPrefix(line, "data ") { - return fmt.Errorf("commit: expected data command: %v", line) - } - c.Msg, err = parse_data(line) - if err != nil { - return err - } - - line, err = f.nextLine() - if err != nil { - return err - } - if strings.HasPrefix(line, "from ") { - c.From = trimLinePrefix(line, "from ") - line, err = f.nextLine() - if err != nil { - return err - } - } - for strings.HasPrefix(line, "merge ") { - c.Merge = append(c.Merge, trimLinePrefix(line, "merge ")) - line, err = f.nextLine() - if err != nil { - return err - } - } - f.cmd <- c - case strings.HasPrefix(line, "M "): - fmt.Printf("line: %q\n", line) - str := trimLinePrefix(line, "M ") - sp1 := strings.IndexByte(str, ' ') - sp2 := strings.IndexByte(str[sp1+1:], ' ') - if sp1 < 0 || sp2 < 0 { - return fmt.Errorf("commit: malformed modify command: %v", line) - } - nMode, err := strconv.ParseUint(str[:sp1], 8, 18) - if err != nil { - return err - } - ref := str[sp1+1 : sp2] - path := textproto.PathUnescape(str[sp2+1:]) - if ref == "inline" { - line, err = f.nextLine() - if err != nil { - return err - } - if !strings.HasPrefix(line, "data ") { - return fmt.Errorf("commit: modify: expected data command: %v", line) - } - data, err := parse_data(line) - if err != nil { - return err - } - f.cmd <- FileModifyInline{Mode: textproto.Mode(nMode), Path: path, Data: data} - } else { - f.cmd <- FileModify{Mode: textproto.Mode(nMode), Path: path, DataRef: ref} - } - case strings.HasPrefix(line, "D "): - f.cmd <- FileDelete{Path: textproto.PathUnescape(trimLinePrefix(line, "D "))} - case strings.HasPrefix(line, "C "): - // BUG(lukeshu): TODO: commit C not implemented - panic("TODO: commit C not implemented") - case strings.HasPrefix(line, "R "): - // BUG(lukeshu): TODO: commit R not implemented - panic("TODO: commit R not implemented") - case strings.HasPrefix(line, "N "): - str := trimLinePrefix(line, "N ") - sp := strings.IndexByte(str, ' ') - if sp < 0 { - return fmt.Errorf("commit: malformed notemodify command: %v", line) - } - ref := str[:sp] - commitish := str[sp+1:] - if ref == "inline" { - line, err = f.nextLine() - if err != nil { - return err - } - if !strings.HasPrefix(line, "data ") { - return fmt.Errorf("commit: notemodify: expected data command: %v", line) - } - data, err := parse_data(line) - if err != nil { - return err - } - f.cmd <- NoteModifyInline{CommitIsh: commitish, Data: data} - } else { - f.cmd <- NoteModify{CommitIsh: commitish, DataRef: ref} - } - case line == "deleteall\n": - f.cmd <- FileDeleteAll{} - case strings.HasPrefix(line, "feature "): - // 'feature' SP ('=' )? LF - str := trimLinePrefix(line, "feature ") - eq := strings.IndexByte(str, '=') - if eq < 0 { - f.cmd <- CmdFeature{ - Feature: str, - } - } else { - f.cmd <- CmdFeature{ - Feature: str[:eq], - Argument: str[eq+1:], - } - } - case strings.HasPrefix(line, "ls "): - // 'ls' SP SP LF - str := trimLinePrefix(line, "ls ") - sp := -1 - if !strings.HasPrefix(str, "\"") { - sp = strings.IndexByte(line, ' ') - } - c := CmdLs{} - c.Path = textproto.PathUnescape(str[sp+1:]) - if sp >= 0 { - c.DataRef = str[:sp] - } - f.cmd <- c - case strings.HasPrefix(line, "option "): - // 'option' SP