// Copyright (C) 2017-2018, 2021 Luke Shumaker // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . package libfastimport import ( "bufio" "io" "os" "git.lukeshu.com/go/libfastimport/textproto" ) type UnsupportedCommand string func (e UnsupportedCommand) Error() string { return "Unsupported command: " + string(e) } // A Frontend is something that produces a fast-import stream; the // Frontend object provides methods for reading from it. A program // that writes to a Frontend would itself be a backend. // // You may think of a "Frontend" object as a "Reader" object, though // it was not given that name because the RespondGetMark, // RespondCatBlob, and RespondLs methods actually write information; // it isn't a read-only object. // // The parser is a bit more forgiving than git's own parser. It // allows extra newlines anywhere it allows comments, whereas git's // own parser is quite strict about newlines. It allows 'cat-blob' // and 'get-mark' commands anywhere it allows comments, which git used // to allow, but was made stricter in git v2.22.0. type Frontend struct { fastImport *parser catBlobWrite *textproto.CatBlobWriter catBlobFlush *bufio.Writer onErr func(error) error } // NewFrontend creates a new Frontend object that reads from the given // io.Reader. // // Optionally, you may also provide an io.Writer that responses to // "cat-blob", "get-mark", and "ls" commands can be written to. // // Optionally, you may also provide an onErr function that can bue // used to handle or transform errors when they are encountered. func NewFrontend(fastImport io.Reader, catBlob io.Writer, onErr func(error) error) *Frontend { ret := &Frontend{} ret.fastImport = newParser(textproto.NewFIReader(fastImport)) if catBlob == nil { catBlob = os.Stdout } ret.catBlobFlush = bufio.NewWriter(catBlob) ret.catBlobWrite = textproto.NewCatBlobWriter(ret.catBlobFlush) if onErr == nil { onErr = func(e error) error { return e } } ret.onErr = onErr return ret } // ReadCmd reads a command from the Frontend. func (f *Frontend) ReadCmd() (Cmd, error) { cmd, err := f.fastImport.ReadCmd() if err != nil { err = f.onErr(err) } return cmd, err } // RespondGetMark sends to the Frontend a response to a "get-mark" // command. // // It is an error (panic) to call RespondGetMark if NewFrontend did // not have a cat-blob writer passed to it. func (f *Frontend) RespondGetMark(sha1 string) error { err := f.catBlobWrite.WriteLine(sha1) if err != nil { return err } return f.catBlobFlush.Flush() } // RespondCatBlob sends to the Frontend a response to a "cat-blob" // command. // // It is an error (panic) to call RespondCatBlob if NewFrontend did // not have a cat-blob writer passed to it. func (f *Frontend) RespondCatBlob(sha1 string, data string) error { err := f.catBlobWrite.WriteBlob(sha1, data) if err != nil { return err } return f.catBlobFlush.Flush() } // RespondLs sends to the Frontend a response to a "ls" command. // // It is an error (panic) to call RespondLs if NewFrontend did not // have a cat-blob writer passed to it. func (f *Frontend) RespondLs(mode Mode, dataref string, path Path) error { var err error if mode == 0 { err = f.catBlobWrite.WriteLine("missing", path) } else { var t string switch mode { case ModeDir: t = "tree" case ModeGit: t = "commit" default: t = "blob" } err = f.catBlobWrite.WriteLine(mode, t, dataref+"\t"+PathEscape(path)) } if err != nil { return err } return f.catBlobFlush.Flush() }