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  }