From f62e35b87aa7e220f97136207d7d5e4ccd27678d Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Mon, 14 Aug 2017 01:27:38 -0400 Subject: [PATCH 1/4] Implement basic 'cid-fmt' utility. Currently only implements printing the Cid prefix. --- cid-fmt/main.go | 69 +++++++++++++++++++++++++++++++++++++++++++++++++ cid.go | 21 +++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 cid-fmt/main.go diff --git a/cid-fmt/main.go b/cid-fmt/main.go new file mode 100644 index 0000000..814e96d --- /dev/null +++ b/cid-fmt/main.go @@ -0,0 +1,69 @@ +package main + +import ( + "fmt" + "os" + + c "github.com/ipfs/go-cid" + + mh "github.com/multiformats/go-multihash" +) + +func main() { + if len(os.Args) < 2 { + fmt.Fprintf(os.Stderr, "usage: %s prefix ...\n", os.Args[0]) + os.Exit(1) + } + switch os.Args[1] { + case "prefix": + err := prefixCmd(os.Args[2:]) + if err != nil { + fmt.Fprintf(os.Stderr, "%v\n", err) + os.Exit(1) + } + default: + fmt.Fprintf(os.Stderr, "usage: %s prefix ...\n") + os.Exit(1) + } +} + +func prefixCmd(args []string) error { + for _, cid := range args { + p, err := prefix(cid) + if err != nil { + return err + } + fmt.Fprintf(os.Stdout, "%s\n", p) + } + return nil +} + +func prefix(str string) (string, error) { + cid, err := c.Decode(str) + if err != nil { + return "", err + } + p := cid.Prefix() + return fmt.Sprintf("cidv%d-%s-%s-%d", + p.Version, + codecToStr(p.Codec), + mhToStr(p.MhType), + p.MhLength, + ), nil +} + +func codecToStr(num uint64) string { + name, ok := c.CodecToStr[num] + if !ok { + return fmt.Sprintf("c?%d", num) + } + return name +} + +func mhToStr(num uint64) string { + name, ok := mh.Codes[num] + if !ok { + return fmt.Sprintf("h?%d", num) + } + return name +} diff --git a/cid.go b/cid.go index eb426ed..72fa326 100644 --- a/cid.go +++ b/cid.go @@ -101,6 +101,27 @@ var Codecs = map[string]uint64{ "zcash-tx": ZcashTx, } +// CodecToStr maps the numeric codec to its name +var CodecToStr = map[uint64]string{ + Raw: "raw", + DagProtobuf: "protobuf", + DagCBOR: "cbor", + GitRaw: "git-raw", + EthBlock: "eth-block", + EthBlockList: "eth-block-list", + EthTxTrie: "eth-tx-trie", + EthTx: "eth-tx", + EthTxReceiptTrie: "eth-tx-receipt-trie", + EthTxReceipt: "eth-tx-receipt", + EthStateTrie: "eth-state-trie", + EthAccountSnapshot: "eth-account-snapshot", + EthStorageTrie: "eth-storage-trie", + BitcoinBlock: "bitcoin-block", + BitcoinTx: "bitcoin-tx", + ZcashBlock: "zcash-block", + ZcashTx: "zcash-tx", +} + // NewCidV0 returns a Cid-wrapped multihash. // They exist to allow IPFS to work with Cids while keeping // compatibility with the plain-multihash format used used in IPFS. From 7333c60a00aaf8d07a0a4b979e80529feb691f1e Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Tue, 15 Aug 2017 14:26:24 -0400 Subject: [PATCH 2/4] Implement 'cid-fmt' utility. --- cid-fmt/main.go | 214 +++++++++++++++++++++++++++++++++++++------ cid-fmt/main_test.go | 73 +++++++++++++++ 2 files changed, 257 insertions(+), 30 deletions(-) create mode 100644 cid-fmt/main_test.go diff --git a/cid-fmt/main.go b/cid-fmt/main.go index 814e96d..b09d28a 100644 --- a/cid-fmt/main.go +++ b/cid-fmt/main.go @@ -1,69 +1,223 @@ package main import ( + "bytes" "fmt" "os" + "strings" c "github.com/ipfs/go-cid" + mb "github.com/multiformats/go-multibase" mh "github.com/multiformats/go-multihash" ) +func usage() { + fmt.Fprintf(os.Stderr, "usage: %s [-b multibase-code] ...\n\n", os.Args[0]) + fmt.Fprintf(os.Stderr, " is either 'prefix' or a printf style format string:\n%s", fmtRef) + os.Exit(1) +} + +const fmtRef = ` + %% literal % + %b multibase name + %B multibase code + %v version string + %V version number + %c codec name + %C codec code + %h multihash name + %H multihash code + %L hash digest length + %m multihash encoded in base %b (with multibase prefix) + %M multihash encoded in base %b without multibase prefix + %d hash digest encoded in base %b (with multibase prefix) + %D hash digest encoded in base %b without multibase prefix + %s cid string encoded in base %b (1) + %s cid string encoded in base %b without multibase prefix + %P cid prefix: %v-%c-%h-%L + +(1) For CID version 0 the multibase must be base58btc and no prefix is +used. For Cid version 1 the multibase prefix is included. +` + func main() { if len(os.Args) < 2 { - fmt.Fprintf(os.Stderr, "usage: %s prefix ...\n", os.Args[0]) - os.Exit(1) + usage() } - switch os.Args[1] { + newBase := mb.Encoding(-1) + args := os.Args[1:] + if args[0] == "-b" { + if len(args) < 2 { + usage() + } + if len(args[1]) != 1 { + fmt.Fprintf(os.Stderr, "Error: Invalid multibase code: %s\n", args[1]) + } + newBase = mb.Encoding(args[1][0]) + args = args[2:] + } + if len(args) < 2 { + usage() + } + fmtStr := args[0] + switch fmtStr { case "prefix": - err := prefixCmd(os.Args[2:]) + fmtStr = "%P" + default: + if strings.IndexByte(fmtStr, '%') == -1 { + fmt.Fprintf(os.Stderr, "Error: Invalid format string: %s\n", fmtStr) + } + } + for _, cidStr := range args[1:] { + base, cid, err := decode(cidStr) + if newBase != -1 { + base = newBase + } if err != nil { - fmt.Fprintf(os.Stderr, "%v\n", err) + fmt.Fprintf(os.Stderr, "Error: %s: %v\n", cidStr, err) + fmt.Fprintf(os.Stdout, "!INVALID_CID!\n") + // Don't abort on a bad cid + continue + } + str, err := fmtCid(fmtStr, base, cid) + if err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + // An error here means a bad format string, no point in continuing os.Exit(1) } - default: - fmt.Fprintf(os.Stderr, "usage: %s prefix ...\n") - os.Exit(1) + fmt.Fprintf(os.Stdout, "%s\n", str) } } -func prefixCmd(args []string) error { - for _, cid := range args { - p, err := prefix(cid) +func decode(v string) (mb.Encoding, *c.Cid, error) { + if len(v) < 2 { + return 0, nil, c.ErrCidTooShort + } + + if len(v) == 46 && v[:2] == "Qm" { + hash, err := mh.FromB58String(v) if err != nil { - return err + return 0, nil, err } - fmt.Fprintf(os.Stdout, "%s\n", p) - } - return nil -} -func prefix(str string) (string, error) { - cid, err := c.Decode(str) + return mb.Base58BTC, c.NewCidV0(hash), nil + } + + base, data, err := mb.Decode(v) if err != nil { - return "", err + return 0, nil, err } - p := cid.Prefix() - return fmt.Sprintf("cidv%d-%s-%s-%d", - p.Version, - codecToStr(p.Codec), - mhToStr(p.MhType), - p.MhLength, - ), nil + + cid, err := c.Cast(data) + + return base, cid, err } -func codecToStr(num uint64) string { +const ERR_STR = "!ERROR!" + +func fmtCid(fmtStr string, base mb.Encoding, cid *c.Cid) (string, error) { + p := cid.Prefix() + out := new(bytes.Buffer) + for i := 0; i < len(fmtStr); i++ { + if fmtStr[i] != '%' { + out.WriteByte(fmtStr[i]) + continue + } + i++ + if i >= len(fmtStr) { + return "", fmt.Errorf("premature end of format string") + } + switch fmtStr[i] { + case '%': + out.WriteByte('%') + case 'b': // base name + out.WriteString(baseToString(base)) + case 'B': // base code + out.WriteByte(byte(base)) + case 'v': // version string + fmt.Fprintf(out, "cidv%d", p.Version) + case 'V': // version num + fmt.Fprintf(out, "%d", p.Version) + case 'c': // codec name + out.WriteString(codecToString(p.Codec)) + case 'C': // codec code + fmt.Fprintf(out, "%d", p.Codec) + case 'h': // hash fun name + out.WriteString(hashToString(p.MhType)) + case 'H': // hash fun code + fmt.Fprintf(out, "%d", p.MhType) + case 'L': // hash length + fmt.Fprintf(out, "%d", p.MhLength) + case 'm', 'M': // multihash encoded in base %b + out.WriteString(encode(base, cid.Hash(), fmtStr[i] == 'M')) + case 'd', 'D': // hash digest encoded in base %b + dec, err := mh.Decode(cid.Hash()) + if err != nil { + out.WriteString(ERR_STR) + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + continue + } + out.WriteString(encode(base, dec.Digest, fmtStr[i] == 'D')) + case 's': // cid string encoded in base %b + str, err := cid.StringOfBase(base) + if err != nil { + out.WriteString(ERR_STR) + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + continue + } + out.WriteString(str) + case 'S': // cid string without base prefix + out.WriteString(encode(base, cid.Bytes(), true)) + case 'P': // prefix + fmt.Fprintf(out, "cidv%d-%s-%s-%d", + p.Version, + codecToString(p.Codec), + hashToString(p.MhType), + p.MhLength, + ) + default: + return "", fmt.Errorf("unrecognized specifier in format string: %c\n", fmtStr[i]) + } + + } + return out.String(), nil +} + +func baseToString(base mb.Encoding) string { + // FIXME: Use lookup tables when they are added to go-multibase + switch base { + case mb.Base58BTC: + return "base58btc" + default: + return fmt.Sprintf("base?%c", base) + } +} + +func codecToString(num uint64) string { name, ok := c.CodecToStr[num] if !ok { - return fmt.Sprintf("c?%d", num) + return fmt.Sprintf("codec?%d", num) } return name } -func mhToStr(num uint64) string { +func hashToString(num uint64) string { name, ok := mh.Codes[num] if !ok { - return fmt.Sprintf("h?%d", num) + return fmt.Sprintf("hash?%d", num) } return name } + +func encode(base mb.Encoding, data []byte, strip bool) string { + str, err := mb.Encode(base, data) + if err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + return ERR_STR + } + if strip { + return str[1:] + } + return str +} diff --git a/cid-fmt/main_test.go b/cid-fmt/main_test.go new file mode 100644 index 0000000..2d69eb1 --- /dev/null +++ b/cid-fmt/main_test.go @@ -0,0 +1,73 @@ +package main + +import ( + "fmt" + "testing" + + mb "github.com/multiformats/go-multibase" +) + +func TestFmt(t *testing.T) { + cids := map[string]string{ + "cidv0": "QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn", + "cidv1": "zdj7WfLr9DhLrb1hsoSi4fSdjjxuZmeqgEtBPWxMLtPbDNbFD", + } + tests := []struct { + cidId string + newBase mb.Encoding + fmtStr string + result string + }{ + {"cidv0", -1, "%P", "cidv0-protobuf-sha2-256-32"}, + {"cidv0", -1, "%b-%v-%c-%h-%L", "base58btc-cidv0-protobuf-sha2-256-32"}, + {"cidv0", -1, "%s", "QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn"}, + {"cidv0", -1, "%S", "QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn"}, + {"cidv0", -1, "ver#%V/#%C/#%H/%L", "ver#0/#112/#18/32"}, + {"cidv0", -1, "%m", "zQmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn"}, + {"cidv0", -1, "%M", "QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn"}, + {"cidv0", -1, "%d", "z72gdmFAgRzYHkJzKiL8MgMMRW3BTSCGyDHroPxJbxMJn"}, + {"cidv0", -1, "%D", "72gdmFAgRzYHkJzKiL8MgMMRW3BTSCGyDHroPxJbxMJn"}, + {"cidv0", 'B', "%S", "CIQFTFEEHEDF6KLBT32BFAGLXEZL4UWFNWM4LFTLMXQBCERZ6CMLX3Y"}, + {"cidv0", 'B', "%B%S", "BCIQFTFEEHEDF6KLBT32BFAGLXEZL4UWFNWM4LFTLMXQBCERZ6CMLX3Y"}, + {"cidv1", -1, "%P", "cidv1-protobuf-sha2-256-32"}, + {"cidv1", -1, "%b-%v-%c-%h-%L", "base58btc-cidv1-protobuf-sha2-256-32"}, + {"cidv1", -1, "%s", "zdj7WfLr9DhLrb1hsoSi4fSdjjxuZmeqgEtBPWxMLtPbDNbFD"}, + {"cidv1", -1, "%S", "dj7WfLr9DhLrb1hsoSi4fSdjjxuZmeqgEtBPWxMLtPbDNbFD"}, + {"cidv1", -1, "ver#%V/#%C/#%H/%L", "ver#1/#112/#18/32"}, + {"cidv1", -1, "%m", "zQmYFbmndVP7QqAVWyKhpmMuQHMaD88pkK57RgYVimmoh5H"}, + {"cidv1", -1, "%M", "QmYFbmndVP7QqAVWyKhpmMuQHMaD88pkK57RgYVimmoh5H"}, + {"cidv1", -1, "%d", "zAux4gVVsLRMXtsZ9fd3tFEZN4jGYB6kP37fgoZNTc11H"}, + {"cidv1", -1, "%D", "Aux4gVVsLRMXtsZ9fd3tFEZN4jGYB6kP37fgoZNTc11H"}, + {"cidv1", 'B', "%s", "bAFYBEIETJGSRL3EQPQPCABV3G6IUBYTSIFVQ24XRRHD3JUETSKLTGQ7DJA"}, + // note: ^ "bAFYB.." should probably be "BAFYB.." (upper case b) + {"cidv1", 'B', "%S", "AFYBEIETJGSRL3EQPQPCABV3G6IUBYTSIFVQ24XRRHD3JUETSKLTGQ7DJA"}, + {"cidv1", 'B', "%B%S", "BAFYBEIETJGSRL3EQPQPCABV3G6IUBYTSIFVQ24XRRHD3JUETSKLTGQ7DJA"}, + } + for _, tc := range tests { + name := fmt.Sprintf("%s/%s", tc.cidId, tc.fmtStr) + if tc.newBase != -1 { + name = fmt.Sprintf("%s/%c", name, tc.newBase) + } + cidStr := cids[tc.cidId] + t.Run(name, func(t *testing.T) { + testFmt(t, cidStr, tc.newBase, tc.fmtStr, tc.result) + }) + } +} + +func testFmt(t *testing.T, cidStr string, newBase mb.Encoding, fmtStr string, result string) { + base, cid, err := decode(cidStr) + if newBase != -1 { + base = newBase + } + if err != nil { + t.Fatal(err) + } + str, err := fmtCid(fmtStr, base, cid) + if err != nil { + t.Fatal(err) + } + if str != result { + t.Error(fmt.Sprintf("expected: %s; but got: %s", result, str)) + } +} From 68abb41a9b6fcbd6b61ee1066580b7719b988b42 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Tue, 15 Aug 2017 14:47:23 -0400 Subject: [PATCH 3/4] Add ability to change CID version. --- cid-fmt/main.go | 66 ++++++++++++++++++++++++++++++++++++-------- cid-fmt/main_test.go | 37 +++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 12 deletions(-) diff --git a/cid-fmt/main.go b/cid-fmt/main.go index b09d28a..cc870dd 100644 --- a/cid-fmt/main.go +++ b/cid-fmt/main.go @@ -13,7 +13,7 @@ import ( ) func usage() { - fmt.Fprintf(os.Stderr, "usage: %s [-b multibase-code] ...\n\n", os.Args[0]) + fmt.Fprintf(os.Stderr, "usage: %s [-b multibase-code] [-v cid-version] ...\n\n", os.Args[0]) fmt.Fprintf(os.Stderr, " is either 'prefix' or a printf style format string:\n%s", fmtRef) os.Exit(1) } @@ -46,16 +46,38 @@ func main() { usage() } newBase := mb.Encoding(-1) + var verConv func(cid *c.Cid) (*c.Cid, error) args := os.Args[1:] - if args[0] == "-b" { - if len(args) < 2 { - usage() +outer: + for { + switch args[0] { + case "-b": + if len(args) < 2 { + usage() + } + if len(args[1]) != 1 { + fmt.Fprintf(os.Stderr, "Error: Invalid multibase code: %s\n", args[1]) + os.Exit(1) + } + newBase = mb.Encoding(args[1][0]) + args = args[2:] + case "-v": + if len(args) < 2 { + usage() + } + switch args[1] { + case "0": + verConv = toCidV0 + case "1": + verConv = toCidV1 + default: + fmt.Fprintf(os.Stderr, "Error: Invalid cid version: %s\n", args[1]) + os.Exit(1) + } + args = args[2:] + default: + break outer } - if len(args[1]) != 1 { - fmt.Fprintf(os.Stderr, "Error: Invalid multibase code: %s\n", args[1]) - } - newBase = mb.Encoding(args[1][0]) - args = args[2:] } if len(args) < 2 { usage() @@ -71,15 +93,24 @@ func main() { } for _, cidStr := range args[1:] { base, cid, err := decode(cidStr) - if newBase != -1 { - base = newBase - } if err != nil { fmt.Fprintf(os.Stderr, "Error: %s: %v\n", cidStr, err) fmt.Fprintf(os.Stdout, "!INVALID_CID!\n") // Don't abort on a bad cid continue } + if newBase != -1 { + base = newBase + } + if verConv != nil { + cid, err = verConv(cid) + if err != nil { + fmt.Fprintf(os.Stderr, "Error: %s: %v\n", cidStr, err) + fmt.Fprintf(os.Stdout, "!CONVERSION_ERROR!\n") + // Don't abort on a bad conversion + continue + } + } str, err := fmtCid(fmtStr, base, cid) if err != nil { fmt.Fprintf(os.Stderr, "Error: %v\n", err) @@ -221,3 +252,14 @@ func encode(base mb.Encoding, data []byte, strip bool) string { } return str } + +func toCidV0(cid *c.Cid) (*c.Cid, error) { + if cid.Type() != c.DagProtobuf { + return nil, fmt.Errorf("can't convert non-protobuf nodes to cidv0") + } + return c.NewCidV0(cid.Hash()), nil +} + +func toCidV1(cid *c.Cid) (*c.Cid, error) { + return c.NewCidV1(cid.Type(), cid.Hash()), nil +} diff --git a/cid-fmt/main_test.go b/cid-fmt/main_test.go index 2d69eb1..4d4713c 100644 --- a/cid-fmt/main_test.go +++ b/cid-fmt/main_test.go @@ -71,3 +71,40 @@ func testFmt(t *testing.T, cidStr string, newBase mb.Encoding, fmtStr string, re t.Error(fmt.Sprintf("expected: %s; but got: %s", result, str)) } } + +func TestCidConv(t *testing.T) { + cidv0 := "QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn" + cidv1 := "zdj7WbTaiJT1fgatdet9Ei9iDB5hdCxkbVyhyh8YTUnXMiwYi" + _, cid, err := decode(cidv0) + if err != nil { + t.Fatal(err) + } + cid, err = toCidV1(cid) + if err != nil { + t.Fatal(err) + } + if cid.String() != cidv1 { + t.Fatal("conversion failure") + } + cid, err = toCidV0(cid) + if err != nil { + t.Fatal(err) + } + cidStr := cid.String() + if cidStr != cidv0 { + t.Error(fmt.Sprintf("conversion failure, expected: %s; but got: %s", cidv0, cidStr)) + } +} + +func TestBadCidConv(t *testing.T) { + // this cid is a raw leaf and should not be able to convert to cidv0 + cidv1 := "zb2rhhzX7uSKrtQ2ZZXFAabKiKFYZrJqKY2KE1cJ8yre2GSWZ" + _, cid, err := decode(cidv1) + if err != nil { + t.Fatal(err) + } + cid, err = toCidV0(cid) + if err == nil { + t.Fatal("expected failure") + } +} From db11d7248a501edf9dbd23c53df79c7eb2a08681 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Tue, 15 Aug 2017 17:23:44 -0400 Subject: [PATCH 4/4] Use an exit code of 1 on non-fatal errors and 2 on fatal errors. --- cid-fmt/main.go | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/cid-fmt/main.go b/cid-fmt/main.go index cc870dd..7dacb8a 100644 --- a/cid-fmt/main.go +++ b/cid-fmt/main.go @@ -15,7 +15,7 @@ import ( func usage() { fmt.Fprintf(os.Stderr, "usage: %s [-b multibase-code] [-v cid-version] ...\n\n", os.Args[0]) fmt.Fprintf(os.Stderr, " is either 'prefix' or a printf style format string:\n%s", fmtRef) - os.Exit(1) + os.Exit(2) } const fmtRef = ` @@ -57,7 +57,7 @@ outer: } if len(args[1]) != 1 { fmt.Fprintf(os.Stderr, "Error: Invalid multibase code: %s\n", args[1]) - os.Exit(1) + os.Exit(2) } newBase = mb.Encoding(args[1][0]) args = args[2:] @@ -72,7 +72,7 @@ outer: verConv = toCidV1 default: fmt.Fprintf(os.Stderr, "Error: Invalid cid version: %s\n", args[1]) - os.Exit(1) + os.Exit(2) } args = args[2:] default: @@ -89,13 +89,14 @@ outer: default: if strings.IndexByte(fmtStr, '%') == -1 { fmt.Fprintf(os.Stderr, "Error: Invalid format string: %s\n", fmtStr) + os.Exit(2) } } for _, cidStr := range args[1:] { base, cid, err := decode(cidStr) if err != nil { - fmt.Fprintf(os.Stderr, "Error: %s: %v\n", cidStr, err) fmt.Fprintf(os.Stdout, "!INVALID_CID!\n") + errorMsg("%s: %v", cidStr, err) // Don't abort on a bad cid continue } @@ -105,8 +106,8 @@ outer: if verConv != nil { cid, err = verConv(cid) if err != nil { - fmt.Fprintf(os.Stderr, "Error: %s: %v\n", cidStr, err) - fmt.Fprintf(os.Stdout, "!CONVERSION_ERROR!\n") + fmt.Fprintf(os.Stdout, "!ERROR!\n") + errorMsg("%s: %v", cidStr, err) // Don't abort on a bad conversion continue } @@ -115,10 +116,20 @@ outer: if err != nil { fmt.Fprintf(os.Stderr, "Error: %v\n", err) // An error here means a bad format string, no point in continuing - os.Exit(1) + os.Exit(2) } fmt.Fprintf(os.Stdout, "%s\n", str) } + os.Exit(exitCode) +} + +var exitCode = 0 + +func errorMsg(fmtStr string, a ...interface{}) { + fmt.Fprintf(os.Stderr, "Error: ") + fmt.Fprintf(os.Stderr, fmtStr, a...) + fmt.Fprintf(os.Stderr, "\n") + exitCode = 1 } func decode(v string) (mb.Encoding, *c.Cid, error) { @@ -150,6 +161,7 @@ const ERR_STR = "!ERROR!" func fmtCid(fmtStr string, base mb.Encoding, cid *c.Cid) (string, error) { p := cid.Prefix() out := new(bytes.Buffer) + var err error for i := 0; i < len(fmtStr); i++ { if fmtStr[i] != '%' { out.WriteByte(fmtStr[i]) @@ -186,7 +198,7 @@ func fmtCid(fmtStr string, base mb.Encoding, cid *c.Cid) (string, error) { dec, err := mh.Decode(cid.Hash()) if err != nil { out.WriteString(ERR_STR) - fmt.Fprintf(os.Stderr, "Error: %v\n", err) + errorMsg("%v", err) continue } out.WriteString(encode(base, dec.Digest, fmtStr[i] == 'D')) @@ -194,7 +206,7 @@ func fmtCid(fmtStr string, base mb.Encoding, cid *c.Cid) (string, error) { str, err := cid.StringOfBase(base) if err != nil { out.WriteString(ERR_STR) - fmt.Fprintf(os.Stderr, "Error: %v\n", err) + errorMsg("%v", err) continue } out.WriteString(str) @@ -208,11 +220,11 @@ func fmtCid(fmtStr string, base mb.Encoding, cid *c.Cid) (string, error) { p.MhLength, ) default: - return "", fmt.Errorf("unrecognized specifier in format string: %c\n", fmtStr[i]) + return "", fmt.Errorf("unrecognized specifier in format string: %c", fmtStr[i]) } } - return out.String(), nil + return out.String(), err } func baseToString(base mb.Encoding) string { @@ -244,7 +256,7 @@ func hashToString(num uint64) string { func encode(base mb.Encoding, data []byte, strip bool) string { str, err := mb.Encode(base, data) if err != nil { - fmt.Fprintf(os.Stderr, "Error: %v\n", err) + errorMsg("%v", err) return ERR_STR } if strip {