diff options
author | Luke Shumaker <lukeshu@lukeshu.com> | 2019-02-16 20:41:20 -0500 |
---|---|---|
committer | Luke Shumaker <lukeshu@lukeshu.com> | 2019-02-16 20:41:20 -0500 |
commit | 97c4b4053ab187a75a1a79854df2941522cdab09 (patch) | |
tree | f57c1ee4d8430ed86884bd7b8924cef7c66db349 | |
parent | 2495e17b6c2d4323b0c989b8ef27248f29eb9548 (diff) |
better type checking
-rw-r--r-- | fi-filefilter/main.go | 225 | ||||
-rw-r--r-- | fiutil/.gitignore | 1 | ||||
-rw-r--r-- | fiutil/Makefile | 40 | ||||
-rw-r--r-- | fiutil/cli.go | 27 | ||||
-rw-r--r-- | fiutil/doc.go | 20 | ||||
-rwxr-xr-x | fiutil/handler.go.gen | 106 |
6 files changed, 295 insertions, 124 deletions
diff --git a/fi-filefilter/main.go b/fi-filefilter/main.go index ae992ba..6731c1e 100644 --- a/fi-filefilter/main.go +++ b/fi-filefilter/main.go @@ -26,6 +26,8 @@ import ( "git.lukeshu.com/go/libfastimport" "github.com/pkg/errors" + + "git.parabola.nu/~lukeshu/fastimport-go-utils/fiutil" ) func usage(w io.Writer) { @@ -48,138 +50,113 @@ func main() { frontend := libfastimport.NewFrontend(os.Stdin, os.Stdin, nil) backend := libfastimport.NewBackend(os.Stdout, os.Stdout, nil) - if err := filefilter(backend, frontend, re); err != nil { + filter := &FileFilter{ + backend: backend, + re: re, + beg: time.Now(), + } + + if err := fiutil.RunHandler(frontend, filter); err != nil { fmt.Fprintf(os.Stderr, "%s: error: %v\n", os.Args[0], err) os.Exit(1) } } -func filefilter(backend *libfastimport.Backend, frontend *libfastimport.Frontend, re *regexp.Regexp) error { - commits := 0 - filesIn := 0 - filesOut := 0 - beg := time.Now() - status := func() { - fmt.Fprintf(os.Stderr, - "%d commits (%d => %d files) (%.2f commit/s)\r", - commits, filesIn, filesOut, - float64(commits)/time.Since(beg).Seconds()) +type FileFilter struct { + backend *libfastimport.Backend + re *regexp.Regexp + + // statistics + commits int + filesIn int + filesOut int + beg time.Time +} + +// file commands /////////////////////////////////////////////////////////////// +func (h *FileFilter) FileModify(cmd libfastimport.FileModify) error { + h.filesIn++ + if !h.re.MatchString(string(cmd.Path)) { + return nil } + h.filesOut++ + return h.backend.Do(cmd) +} +func (h *FileFilter) FileModifyInline(cmd libfastimport.FileModifyInline) error { + h.filesIn++ + if !h.re.MatchString(string(cmd.Path)) { + return nil + } + h.filesOut++ + return h.backend.Do(cmd) +} +func (h *FileFilter) FileDelete(cmd libfastimport.FileDelete) error { + h.filesIn++ + if !h.re.MatchString(string(cmd.Path)) { + return nil + } + h.filesOut++ + return h.backend.Do(cmd) +} +func (h *FileFilter) FileDeleteAll(cmd libfastimport.FileDeleteAll) error { + return h.backend.Do(cmd) +} +func (h *FileFilter) FileCopy(cmd libfastimport.FileCopy) error { + return errors.New("unexpected \"filecopy\" command: this filter requires --full-tree") +} +func (h *FileFilter) FileRename(cmd libfastimport.FileRename) error { + return errors.New("unexpected \"filerename\" command: this filter requires --full-tree") +} - for { - cmd, err := frontend.ReadCmd() - if err != nil { - if err == io.EOF { - break - } - return err - } +// statistics ////////////////////////////////////////////////////////////////// +func (h *FileFilter) CmdCommitEnd(cmd libfastimport.CmdCommitEnd) error { + h.commits++ + fmt.Fprintf(os.Stderr, + "%d commits (%d => %d files) (%.2f commit/s)\r", + h.commits, h.filesIn, h.filesOut, + float64(h.commits)/time.Since(h.beg).Seconds()) + return nil +} + +// note commands /////////////////////////////////////////////////////////////// +func (h *FileFilter) NoteModify(cmd libfastimport.NoteModify) error { + return h.backend.Do(cmd) +} +func (h *FileFilter) NoteModifyInline(cmd libfastimport.NoteModifyInline) error { + return h.backend.Do(cmd) +} + +// other commands ////////////////////////////////////////////////////////////// +func (h *FileFilter) CmdBlob(cmd libfastimport.CmdBlob) error { return h.backend.Do(cmd) } +func (h *FileFilter) CmdCheckpoint(cmd libfastimport.CmdCheckpoint) error { return h.backend.Do(cmd) } +func (h *FileFilter) CmdComment(cmd libfastimport.CmdComment) error { return h.backend.Do(cmd) } +func (h *FileFilter) CmdCommit(cmd libfastimport.CmdCommit) error { return h.backend.Do(cmd) } +func (h *FileFilter) CmdDone(cmd libfastimport.CmdDone) error { return h.backend.Do(cmd) } +func (h *FileFilter) CmdOption(cmd libfastimport.CmdOption) error { return h.backend.Do(cmd) } +func (h *FileFilter) CmdProgress(cmd libfastimport.CmdProgress) error { return h.backend.Do(cmd) } +func (h *FileFilter) CmdReset(cmd libfastimport.CmdReset) error { return h.backend.Do(cmd) } +func (h *FileFilter) CmdTag(cmd libfastimport.CmdTag) error { return h.backend.Do(cmd) } + +func (h *FileFilter) CmdCatBlob(cmd libfastimport.CmdCatBlob) (sha1 string, data string, err error) { + return h.backend.CatBlob(cmd) +} +func (h *FileFilter) CmdGetMark(cmd libfastimport.CmdGetMark) (sha1 string, err error) { + return h.backend.GetMark(cmd) +} +func (h *FileFilter) CmdLs(cmd libfastimport.CmdLs) (mode libfastimport.Mode, dataref string, path libfastimport.Path, err error) { + return h.backend.Ls(cmd) +} - switch cmdt := cmd.(type) { - - // commit ////////////////////////////////////////////// - case libfastimport.CmdCommit: - if err := backend.Do(cmd); err != nil { - return err - } - case libfastimport.CmdCommitEnd: - commits++ - status() - - // file commands /////////////////////////////////////// - case libfastimport.FileModify: - filesIn++ - if re.MatchString(string(cmdt.Path)) { - filesOut++ - if err := backend.Do(cmd); err != nil { - return err - } - } - case libfastimport.FileModifyInline: - filesIn++ - if re.MatchString(string(cmdt.Path)) { - filesOut++ - if err := backend.Do(cmd); err != nil { - return err - } - } - case libfastimport.FileDelete: - filesIn++ - if re.MatchString(string(cmdt.Path)) { - filesOut++ - if err := backend.Do(cmd); err != nil { - return err - } - } - case libfastimport.FileDeleteAll: - if err := backend.Do(cmd); err != nil { - return err - } - case libfastimport.FileCopy: - return errors.New("unexpected \"filecopy\" command: this filter requires --full-tree") - case libfastimport.FileRename: - return errors.New("unexpected \"filerename\" command: this filter requires --full-tree") - - // note commands /////////////////////////////////////// - case libfastimport.NoteModify, libfastimport.NoteModifyInline: - if err := backend.Do(cmd); err != nil { - return err - } - - // regular commands //////////////////////////////////// - case libfastimport.CmdBlob, libfastimport.CmdCheckpoint, libfastimport.CmdComment, libfastimport.CmdDone, libfastimport.CmdProgress, libfastimport.CmdReset, libfastimport.CmdTag: - if err := backend.Do(cmd); err != nil { - return err - } - - // special commands //////////////////////////////////// - case libfastimport.CmdOption: - if err := backend.Do(cmd); err != nil { - return err - } - case libfastimport.CmdFeature: - switch cmdt.Feature { - case "date-format": - if cmdt.Argument != "raw" { - return errors.Errorf("date-format=%q: only supports the %q format", cmdt.Argument, "raw") - } - if err := backend.Do(cmd); err != nil { - return err - } - case "export-marks", "relative-marks", "no-relative-marks", "force", "import-marks", "import-marks-if-exists", "get-mark", "cat-blob", "ls", "notes", "done": - if err := backend.Do(cmd); err != nil { - return err - } - default: - return errors.Errorf("unknown feature %q", cmdt.Feature) - } - case libfastimport.CmdCatBlob: - sha1, data, err := backend.CatBlob(cmdt) - if err != nil { - return err - } - if err := frontend.RespondCatBlob(sha1, data); err != nil { - return err - } - case libfastimport.CmdGetMark: - sha1, err := backend.GetMark(cmdt) - if err != nil { - return err - } - if err := frontend.RespondGetMark(sha1); err != nil { - return err - } - case libfastimport.CmdLs: - mode, dataref, path, err := backend.Ls(cmdt) - if err != nil { - return err - } - if err := frontend.RespondLs(mode, dataref, path); err != nil { - return err - } - default: - return errors.Errorf("unexpected command: %[1]T(%#[1]v)", cmd) +func (h *FileFilter) CmdFeature(cmd libfastimport.CmdFeature) error { + switch cmd.Feature { + case "date-format": + if cmd.Argument != "raw" { + return errors.Errorf("date-format=%q: only supports the %q format", cmd.Argument, "raw") } + return h.backend.Do(cmd) + case "export-marks", "relative-marks", "no-relative-marks", "force", "import-marks", "import-marks-if-exists", "get-mark", "cat-blob", "ls", "notes", "done": + return h.backend.Do(cmd) + default: + return errors.Errorf("unknown feature %q", cmd.Feature) } - return nil } diff --git a/fiutil/.gitignore b/fiutil/.gitignore new file mode 100644 index 0000000..37724f0 --- /dev/null +++ b/fiutil/.gitignore @@ -0,0 +1 @@ +/handler.go diff --git a/fiutil/Makefile b/fiutil/Makefile new file mode 100644 index 0000000..c74bd8b --- /dev/null +++ b/fiutil/Makefile @@ -0,0 +1,40 @@ +# Copyright 2015-2017, 2019 Luke Shumaker <lukeshu@parabola.nu> +# +# 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 <http://www.gnu.org/licenses/>. + +# header +ifneq ($(topsrcdir),) +include $(topsrcdir)/build-aux/Makefile.head.mk +else +srcdir = . +.DEFAULT_GOAL = files.generate +endif + +# body + +files.src.gen += handler.go + +$(srcdir)/%.go: $(srcdir)/%.go.gen + cd $(@D) && ./$(^F) > $(@F) + +# footer +ifneq ($(topsrcdir),) +include $(topsrcdir)/build-aux/Makefile.tail.mk +else +files.generate: $(files.src.gen) +maintainer-clean: + rm -f -- $(files.src.gen) $(files.src.int) +.PHONY: files.generate maintainer-clean +.DELETE_ON_ERROR: +endif diff --git a/fiutil/cli.go b/fiutil/cli.go new file mode 100644 index 0000000..649aee4 --- /dev/null +++ b/fiutil/cli.go @@ -0,0 +1,27 @@ +// Copyright 2019 Luke Shumaker <lukeshu@parabola.nu> +// +// 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 <http://www.gnu.org/licenses/>. + +package fiutil + +import ( + "fmt" + "os" +) + +func ErrUsage(msg string) { + fmt.Fprintln(os.Stderr, "%s: %s\n", os.Args[0], msg) + fmt.Fprintf(os.Stderr, "Try '%s --help' for more information\n", os.Args[0]) + os.Exit(2) +} diff --git a/fiutil/doc.go b/fiutil/doc.go new file mode 100644 index 0000000..4d6d5f9 --- /dev/null +++ b/fiutil/doc.go @@ -0,0 +1,20 @@ +// Copyright 2019 Luke Shumaker <lukeshu@parabola.nu> +// +// 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 <http://www.gnu.org/licenses/>. + +//go:generate make + +// Package fiutil provides utilities for implementing fast-import +// filters on top of git.lukeshu.com/go/libfastimport. +package fiutil diff --git a/fiutil/handler.go.gen b/fiutil/handler.go.gen new file mode 100755 index 0000000..9f7d054 --- /dev/null +++ b/fiutil/handler.go.gen @@ -0,0 +1,106 @@ +#!/usr/bin/env bash +# Copyright 2019 Luke Shumaker <lukeshu@parabola.nu> +# +# 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 <http://www.gnu.org/licenses/>. + +cmds=( + CmdBlob + #CmdCatBlob + CmdCheckpoint + CmdComment + CmdCommit + CmdCommitEnd + CmdDone + CmdFeature + #CmdGetMark + #CmdLs + CmdOption + CmdProgress + CmdReset + CmdTag + FileCopy + FileDelete + FileDeleteAll + FileModify + FileModifyInline + FileRename + NoteModify + NoteModifyInline +) + +{ + cat <<-EOT + //$(printf ' %q' "$0" "$@") + // Code generated by the above command; DO NOT EDIT. + + package fiutil + + import ( + "io" + + "git.lukeshu.com/go/libfastimport" + "github.com/pkg/errors" + ) + + type Handler interface{ + $(for cmd in "${cmds[@]}"; do + echo "$cmd(libfastimport.$cmd) error" + done) + CmdCatBlob(libfastimport.CmdCatBlob) (sha1 string, data string, err error) + CmdGetMark(libfastimport.CmdGetMark) (sha1 string, err error) + CmdLs(libfastimport.CmdLs) (mode libfastimport.Mode, dataref string, path libfastimport.Path, err error) + } + + func RunHandler(reader *libfastimport.Frontend, handler Handler) error { + for { + cmd, err := reader.ReadCmd() + if err != nil { + if err == io.EOF { + return nil + } + return err + } + + switch cmdt := cmd.(type) { + $(for cmd in "${cmds[@]}"; do + echo "case libfastimport.$cmd:" + echo " if err := handler.$cmd(cmdt); err != nil {" + echo " return err" + echo " }" + done) + case libfastimport.CmdCatBlob: + sha1, data, err := handler.CmdCatBlob(cmdt) + if err != nil { + return err + } + return reader.RespondCatBlob(sha1, data) + case libfastimport.CmdGetMark: + sha1, err := handler.CmdGetMark(cmdt) + if err != nil { + return err + } + return reader.RespondGetMark(sha1) + case libfastimport.CmdLs: + mode, dataref, path, err := handler.CmdLs(cmdt) + if err != nil { + return err + } + return reader.RespondLs(mode, dataref, path) + default: + return errors.Errorf("unexpected command object: %[1]T(%#[1]v)", cmd) + } + } + } + EOT +} | gofmt |