summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@lukeshu.com>2019-02-16 20:41:20 -0500
committerLuke Shumaker <lukeshu@lukeshu.com>2019-02-16 20:41:20 -0500
commit97c4b4053ab187a75a1a79854df2941522cdab09 (patch)
treef57c1ee4d8430ed86884bd7b8924cef7c66db349
parent2495e17b6c2d4323b0c989b8ef27248f29eb9548 (diff)
better type checking
-rw-r--r--fi-filefilter/main.go225
-rw-r--r--fiutil/.gitignore1
-rw-r--r--fiutil/Makefile40
-rw-r--r--fiutil/cli.go27
-rw-r--r--fiutil/doc.go20
-rwxr-xr-xfiutil/handler.go.gen106
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