diff --git a/cid-fmt/main_test.go b/cid-fmt/main_test.go index ce66c0a..609bb98 100644 --- a/cid-fmt/main_test.go +++ b/cid-fmt/main_test.go @@ -43,3 +43,4 @@ func TestBadCidConv(t *testing.T) { t.Fatal("expected failure") } } + diff --git a/cidenc/encoder.go b/cidenc/encoder.go new file mode 100644 index 0000000..80d3bf4 --- /dev/null +++ b/cidenc/encoder.go @@ -0,0 +1,59 @@ +package cidenc + +import ( + cid "github.com/ipfs/go-cid" + mbase "github.com/multiformats/go-multibase" +) + +// Encoder is a basic Encoder that will encode Cid's using a specifed +// base and optionally upgrade a CIDv0 to CIDv1 +type Encoder struct { + Base mbase.Encoder // The multibase to use + Upgrade bool // If true upgrade CIDv0 to CIDv1 when encoding +} + +// Default return a new default encoder +func Default() Encoder { + return Encoder{Base: mbase.MustNewEncoder(mbase.Base58BTC)} +} + +// Encode encoded the cid using the parameters of the Encoder +func (enc Encoder) Encode(c cid.Cid) string { + if enc.Upgrade && c.Version() == 0 { + c = cid.NewCidV1(c.Type(), c.Hash()) + } + return c.Encode(enc.Base) +} + +// Recode reencodes the cid string to match the parameters of the +// encoder +func (enc Encoder) Recode(v string) (string, error) { + skip, err := enc.noopRecode(v) + if skip || err != nil { + return v, err + } + + c, err := cid.Decode(v) + if err != nil { + return v, err + } + + return enc.Encode(c), nil +} + +func (enc Encoder) noopRecode(v string) (bool, error) { + if len(v) < 2 { + return false, cid.ErrCidTooShort + } + ver := cidVer(v) + skip := ver == 0 && !enc.Upgrade || ver == 1 && v[0] == byte(enc.Base.Encoding()) + return skip, nil +} + +func cidVer(v string) int { + if len(v) == 46 && v[:2] == "Qm" { + return 0 + } else { + return 1 + } +} diff --git a/cidenc/encoder_test.go b/cidenc/encoder_test.go new file mode 100644 index 0000000..a42ef34 --- /dev/null +++ b/cidenc/encoder_test.go @@ -0,0 +1,62 @@ +package cidenc + +import ( + "testing" + + cid "github.com/ipfs/go-cid" + mbase "github.com/multiformats/go-multibase" +) + +func TestCidEncoder(t *testing.T) { + cidv0str := "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n" + cidv1str := "zdj7Wkkhxcu2rsiN6GUyHCLsSLL47kdUNfjbFqBUUhMFTZKBi" + cidb32str := "bafybeihdwdcefgh4dqkjv67uzcmw7ojee6xedzdetojuzjevtenxquvyku" + cidv0, _ := cid.Decode(cidv0str) + cidv1, _ := cid.Decode(cidv1str) + + testEncode := func(enc Encoder, cid cid.Cid, expect string) { + actual := enc.Encode(cid) + if actual != expect { + t.Errorf("%+v.Encode(%s): expected %s but got %s", enc, cid, expect, actual) + } + } + + testRecode := func(enc Encoder, cid string, expect string) { + actual, err := enc.Recode(cid) + if err != nil { + t.Errorf("%+v.Recode(%s): %s", enc, cid, err) + return + } + if actual != expect { + t.Errorf("%+v.Recode(%s): expected %s but got %s", enc, cid, expect, actual) + } + } + + enc := Encoder{Base: mbase.MustNewEncoder(mbase.Base58BTC), Upgrade: false} + testEncode(enc, cidv0, cidv0str) + testEncode(enc, cidv1, cidv1str) + testRecode(enc, cidv0str, cidv0str) + testRecode(enc, cidv1str, cidv1str) + testRecode(enc, cidb32str, cidv1str) + + enc = Encoder{Base: mbase.MustNewEncoder(mbase.Base58BTC), Upgrade: true} + testEncode(enc, cidv0, cidv1str) + testEncode(enc, cidv1, cidv1str) + testRecode(enc, cidv0str, cidv1str) + testRecode(enc, cidv1str, cidv1str) + testRecode(enc, cidb32str, cidv1str) + + enc = Encoder{Base: mbase.MustNewEncoder(mbase.Base32), Upgrade: false} + testEncode(enc, cidv0, cidv0str) + testEncode(enc, cidv1, cidb32str) + testRecode(enc, cidv0str, cidv0str) + testRecode(enc, cidv1str, cidb32str) + testRecode(enc, cidb32str, cidb32str) + + enc = Encoder{Base: mbase.MustNewEncoder(mbase.Base32), Upgrade: true} + testEncode(enc, cidv0, cidb32str) + testEncode(enc, cidv1, cidb32str) + testRecode(enc, cidv0str, cidb32str) + testRecode(enc, cidv1str, cidb32str) + testRecode(enc, cidb32str, cidb32str) +}