意外と簡単でした(Lua に慣れてる人は、コールバック関数を戻り値として返すというやり方はおなじみかも)
uncozip のパッケージは、ZIPファイルの各ファイルをリストアップする関数として (*CorruptedZip) Scan() bool
というメソッドがあります。この実装は、内部の繰り返し関数 (*CorruptedZip) scan() error
を次のようにラッピングしているだけの関数です。
// Scan advances the CorruptedZip to the next single file in a ZIP archive. func (cz *CorruptedZip) Scan() bool { err := cz.scan() if err == io.EOF { cz.err = nil // means no error } else { cz.err = err } return err == nil }
これをちょっと組み変えた関数 Each を用意します。(*CorruptedZip) Scan()
は繰り返し要素のデータは元々自分自身のメソッドで提供していたので、Each
は自分自身をコールバック関数に渡すという手抜きをしています。
( これ、将来的には、Scan は廃止して、各要素は本体のフィールドから外部に追い出すように最適化したいものですが、とりあえずは簡単対応で )
// Each is the function for rangefunc. func (cz *CorruptedZip) Each(f func(*CorruptedZip) bool) { for { err := cz.scan() if err != nil { if err != io.EOF { cz.err = err } break } if !f(cz) { break } } }
呼び出し側を少々書き変えます。
diff --git a/cmd/uncozip/main.go b/cmd/uncozip/main.go index 40d1c6bd23...4f777afa23 100644 --- a/cmd/uncozip/main.go +++ b/cmd/uncozip/main.go @@ -165,13 +165,13 @@ }) } - for cz.Scan() { + for entry := range cz.Each { var err error var checksum uint32 if *flagTest { - checksum, err = testEntry(cz, patterns) + checksum, err = testEntry(entry, patterns) } else { - checksum, err = extractEntry(cz, patterns) + checksum, err = extractEntry(entry, patterns) } if err == errSkipEntry { continue @@ -179,17 +179,17 @@ if err != nil { return err } - if checksum != cz.CRC32() { + if checksum != entry.CRC32() { if *flagStrict { return fmt.Errorf("%s: CRC32 is expected %X in header, but %X", - cz.Name(), cz.CRC32(), checksum) + entry.Name(), entry.CRC32(), checksum) } fmt.Fprintf(os.Stderr, "NG: %s: CRC32 is expected %X in header, but %X\n", - cz.Name(), cz.CRC32(), checksum) + entry.Name(), entry.CRC32(), checksum) } else if *flagDebug { fmt.Fprintf(os.Stderr, "%s: CRC32: header=%X , body=%X\n", - cz.Name(), cz.CRC32(), checksum) + entry.Name(), entry.CRC32(), checksum) } } if err := cz.Err(); err != io.EOF {
最後に Makefile を修正します。
--- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ all: go fmt $(SET) "CGO_ENABLED=0" && go build $(GOOPT) - cd "cmd/uncozip" && go fmt && $(SET) "CGO_ENABLED=0" && go build -o ../../$(NAME)$(EXE) $(GOOPT) + cd "cmd/uncozip" && go fmt && $(SET) "CGO_ENABLED=0" && $(SET) "GOEXPERIMENT=rangefunc" & go build -o ../../$(NAME)$(EXE) $(GOOPT) _dist: $(MAKE) all
ね、簡単でしょ? (そりゃ、手抜きだし…)
この対応では、実行ファイル作成部分では GOEXPERIMENT=rangefunc を指定していますが、パッケージ部分では rangefunc が要求する型の関数を用意しているだけで、言語の拡張部分は使っていません。それゆえ、Go 1.22以前のコードからでも従来どおり普通に import して使えます。