Merge pull request #69 from ipfs/kevina/extract
Extract non-core functionality from go-cid into go-cidutil
This commit is contained in:
128
cid-fmt/main.go
128
cid-fmt/main.go
@@ -1,128 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
c "github.com/ipfs/go-cid"
|
||||
|
||||
mb "github.com/multiformats/go-multibase"
|
||||
)
|
||||
|
||||
func usage() {
|
||||
fmt.Fprintf(os.Stderr, "usage: %s [-b multibase-code] [-v cid-version] <fmt-str> <cid> ...\n\n", os.Args[0])
|
||||
fmt.Fprintf(os.Stderr, "<fmt-str> is either 'prefix' or a printf style format string:\n%s", c.FormatRef)
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 2 {
|
||||
usage()
|
||||
}
|
||||
newBase := mb.Encoding(-1)
|
||||
var verConv func(cid *c.Cid) (*c.Cid, error)
|
||||
args := os.Args[1:]
|
||||
outer:
|
||||
for {
|
||||
switch args[0] {
|
||||
case "-b":
|
||||
if len(args) < 2 {
|
||||
usage()
|
||||
}
|
||||
encoder, err := mb.EncoderByName(args[1])
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: %s\n", err.Error())
|
||||
os.Exit(2)
|
||||
}
|
||||
newBase = encoder.Encoding()
|
||||
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(2)
|
||||
}
|
||||
args = args[2:]
|
||||
default:
|
||||
break outer
|
||||
}
|
||||
}
|
||||
if len(args) < 2 {
|
||||
usage()
|
||||
}
|
||||
fmtStr := args[0]
|
||||
switch fmtStr {
|
||||
case "prefix":
|
||||
fmtStr = "%P"
|
||||
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:] {
|
||||
cid, err := c.Decode(cidStr)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stdout, "!INVALID_CID!\n")
|
||||
errorMsg("%s: %v", cidStr, err)
|
||||
// Don't abort on a bad cid
|
||||
continue
|
||||
}
|
||||
base := newBase
|
||||
if newBase == -1 {
|
||||
base, _ = c.ExtractEncoding(cidStr)
|
||||
}
|
||||
if verConv != nil {
|
||||
cid, err = verConv(cid)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stdout, "!ERROR!\n")
|
||||
errorMsg("%s: %v", cidStr, err)
|
||||
// Don't abort on a bad conversion
|
||||
continue
|
||||
}
|
||||
}
|
||||
str, err := c.Format(fmtStr, base, cid)
|
||||
switch err.(type) {
|
||||
case c.FormatStringError:
|
||||
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||
os.Exit(2)
|
||||
default:
|
||||
fmt.Fprintf(os.Stdout, "!ERROR!\n")
|
||||
errorMsg("%s: %v", cidStr, err)
|
||||
// Don't abort on cid specific errors
|
||||
continue
|
||||
case nil:
|
||||
// no error
|
||||
}
|
||||
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 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
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
c "github.com/ipfs/go-cid"
|
||||
)
|
||||
|
||||
func TestCidConv(t *testing.T) {
|
||||
cidv0 := "QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn"
|
||||
cidv1 := "zdj7WbTaiJT1fgatdet9Ei9iDB5hdCxkbVyhyh8YTUnXMiwYi"
|
||||
cid, err := c.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 := c.Decode(cidv1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cid, err = toCidV0(cid)
|
||||
if err == nil {
|
||||
t.Fatal("expected failure")
|
||||
}
|
||||
}
|
||||
151
format.go
151
format.go
@@ -1,151 +0,0 @@
|
||||
package cid
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
mb "github.com/multiformats/go-multibase"
|
||||
mh "github.com/multiformats/go-multihash"
|
||||
)
|
||||
|
||||
// FormatRef is a string documenting the format string for the Format function
|
||||
const FormatRef = `
|
||||
%% 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.
|
||||
`
|
||||
|
||||
// Format formats a cid according to the format specificer as
|
||||
// documented in the FormatRef constant
|
||||
func Format(fmtStr string, base mb.Encoding, cid *Cid) (string, error) {
|
||||
p := cid.Prefix()
|
||||
var out bytes.Buffer
|
||||
var err error
|
||||
encoder, err := mb.NewEncoder(base)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for i := 0; i < len(fmtStr); i++ {
|
||||
if fmtStr[i] != '%' {
|
||||
out.WriteByte(fmtStr[i])
|
||||
continue
|
||||
}
|
||||
i++
|
||||
if i >= len(fmtStr) {
|
||||
return "", FormatStringError{"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(encoder, cid.Hash(), fmtStr[i] == 'M'))
|
||||
case 'd', 'D': // hash digest encoded in base %b
|
||||
dec, err := mh.Decode(cid.Hash())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
out.WriteString(encode(encoder, dec.Digest, fmtStr[i] == 'D'))
|
||||
case 's': // cid string encoded in base %b
|
||||
str, err := cid.StringOfBase(base)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
out.WriteString(str)
|
||||
case 'S': // cid string without base prefix
|
||||
out.WriteString(encode(encoder, 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 "", FormatStringError{"unrecognized specifier in format string", fmtStr[i-1 : i+1]}
|
||||
}
|
||||
|
||||
}
|
||||
return out.String(), err
|
||||
}
|
||||
|
||||
// FormatStringError is the error return from Format when the format
|
||||
// string is ill formed
|
||||
type FormatStringError struct {
|
||||
Message string
|
||||
Specifier string
|
||||
}
|
||||
|
||||
func (e FormatStringError) Error() string {
|
||||
if e.Specifier == "" {
|
||||
return e.Message
|
||||
} else {
|
||||
return fmt.Sprintf("%s: %s", e.Message, e.Specifier)
|
||||
}
|
||||
}
|
||||
|
||||
func baseToString(base mb.Encoding) string {
|
||||
baseStr, ok := mb.EncodingToStr[base]
|
||||
if !ok {
|
||||
return fmt.Sprintf("base?%c", base)
|
||||
}
|
||||
return baseStr
|
||||
}
|
||||
|
||||
func codecToString(num uint64) string {
|
||||
name, ok := CodecToStr[num]
|
||||
if !ok {
|
||||
return fmt.Sprintf("codec?%d", num)
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
func hashToString(num uint64) string {
|
||||
name, ok := mh.Codes[num]
|
||||
if !ok {
|
||||
return fmt.Sprintf("hash?%d", num)
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
func encode(base mb.Encoder, data []byte, strip bool) string {
|
||||
str := base.Encode(data)
|
||||
if strip {
|
||||
return str[1:]
|
||||
}
|
||||
return str
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
package cid
|
||||
|
||||
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"},
|
||||
{"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) {
|
||||
cid, err := Decode(cidStr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
base := newBase
|
||||
if newBase == -1 {
|
||||
base, _ = ExtractEncoding(cidStr)
|
||||
}
|
||||
str, err := Format(fmtStr, base, cid)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if str != result {
|
||||
t.Error(fmt.Sprintf("expected: %s; but got: %s", result, str))
|
||||
}
|
||||
}
|
||||
34
set.go
34
set.go
@@ -1,9 +1,5 @@
|
||||
package cid
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
// Set is a implementation of a set of Cids, that is, a structure
|
||||
// to which holds a single copy of every Cids that is added to it.
|
||||
type Set struct {
|
||||
@@ -70,33 +66,3 @@ func (s *Set) ForEach(f func(c *Cid) error) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// StreamingSet is an extension of Set which allows to implement back-pressure
|
||||
// for the Visit function
|
||||
type StreamingSet struct {
|
||||
Set *Set
|
||||
New chan *Cid
|
||||
}
|
||||
|
||||
// NewStreamingSet initializes and returns new Set.
|
||||
func NewStreamingSet() *StreamingSet {
|
||||
return &StreamingSet{
|
||||
Set: NewSet(),
|
||||
New: make(chan *Cid),
|
||||
}
|
||||
}
|
||||
|
||||
// Visitor creates new visitor which adds a Cids to the set and emits them to
|
||||
// the set.New channel
|
||||
func (s *StreamingSet) Visitor(ctx context.Context) func(c *Cid) bool {
|
||||
return func(c *Cid) bool {
|
||||
if s.Set.Visit(c) {
|
||||
select {
|
||||
case s.New <- c:
|
||||
case <-ctx.Done():
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user