github.com/rcowham/go-libgitfastimport@v0.1.6/frontend.go (about) 1 // Copyright (C) 2017-2018, 2021 Luke Shumaker <lukeshu@lukeshu.com> 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as published by 5 // the Free Software Foundation, either version 3 of the License, or 6 // (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <https://www.gnu.org/licenses/>. 15 16 package libfastimport 17 18 import ( 19 "bufio" 20 "io" 21 "os" 22 23 "github.com/rcowham/go-libgitfastimport/textproto" 24 ) 25 26 type UnsupportedCommand string 27 28 func (e UnsupportedCommand) Error() string { 29 return "Unsupported command: " + string(e) 30 } 31 32 // A Frontend is something that produces a fast-import stream; the 33 // Frontend object provides methods for reading from it. A program 34 // that writes to a Frontend would itself be a backend. 35 // 36 // You may think of a "Frontend" object as a "Reader" object, though 37 // it was not given that name because the RespondGetMark, 38 // RespondCatBlob, and RespondLs methods actually write information; 39 // it isn't a read-only object. 40 // 41 // The parser is a bit more forgiving than git's own parser. It 42 // allows extra newlines anywhere it allows comments, whereas git's 43 // own parser is quite strict about newlines. It allows 'cat-blob' 44 // and 'get-mark' commands anywhere it allows comments, which git used 45 // to allow, but was made stricter in git v2.22.0. 46 type Frontend struct { 47 fastImport *parser 48 catBlobWrite *textproto.CatBlobWriter 49 catBlobFlush *bufio.Writer 50 51 onErr func(error) error 52 } 53 54 // NewFrontend creates a new Frontend object that reads from the given 55 // io.Reader. 56 // 57 // Optionally, you may also provide an io.Writer that responses to 58 // "cat-blob", "get-mark", and "ls" commands can be written to. 59 // 60 // Optionally, you may also provide an onErr function that can bue 61 // used to handle or transform errors when they are encountered. 62 func NewFrontend(fastImport io.Reader, catBlob io.Writer, onErr func(error) error) *Frontend { 63 ret := &Frontend{} 64 65 ret.fastImport = newParser(textproto.NewFIReader(fastImport)) 66 67 if catBlob == nil { 68 catBlob = os.Stdout 69 } 70 ret.catBlobFlush = bufio.NewWriter(catBlob) 71 ret.catBlobWrite = textproto.NewCatBlobWriter(ret.catBlobFlush) 72 73 if onErr == nil { 74 onErr = func(e error) error { return e } 75 } 76 ret.onErr = onErr 77 78 return ret 79 } 80 81 // ReadCmd reads a command from the Frontend. 82 func (f *Frontend) ReadCmd() (Cmd, error) { 83 cmd, err := f.fastImport.ReadCmd() 84 if err != nil { 85 err = f.onErr(err) 86 } 87 return cmd, err 88 } 89 90 // RespondGetMark sends to the Frontend a response to a "get-mark" 91 // command. 92 // 93 // It is an error (panic) to call RespondGetMark if NewFrontend did 94 // not have a cat-blob writer passed to it. 95 func (f *Frontend) RespondGetMark(sha1 string) error { 96 err := f.catBlobWrite.WriteLine(sha1) 97 if err != nil { 98 return err 99 } 100 return f.catBlobFlush.Flush() 101 } 102 103 // RespondCatBlob sends to the Frontend a response to a "cat-blob" 104 // command. 105 // 106 // It is an error (panic) to call RespondCatBlob if NewFrontend did 107 // not have a cat-blob writer passed to it. 108 func (f *Frontend) RespondCatBlob(sha1 string, data string) error { 109 err := f.catBlobWrite.WriteBlob(sha1, data) 110 if err != nil { 111 return err 112 } 113 return f.catBlobFlush.Flush() 114 } 115 116 // RespondLs sends to the Frontend a response to a "ls" command. 117 // 118 // It is an error (panic) to call RespondLs if NewFrontend did not 119 // have a cat-blob writer passed to it. 120 func (f *Frontend) RespondLs(mode Mode, dataref string, path Path) error { 121 var err error 122 if mode == 0 { 123 err = f.catBlobWrite.WriteLine("missing", path) 124 } else { 125 var t string 126 switch mode { 127 case ModeDir: 128 t = "tree" 129 case ModeGit: 130 t = "commit" 131 default: 132 t = "blob" 133 } 134 err = f.catBlobWrite.WriteLine(mode, t, dataref+"\t"+PathEscape(path)) 135 } 136 if err != nil { 137 return err 138 } 139 return f.catBlobFlush.Flush() 140 }