format.go 3.78 KB
Newer Older
1
package cidutil
2 3 4 5 6

import (
	"bytes"
	"fmt"

7
	c "github.com/ipfs/go-cid"
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
	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
38
func Format(fmtStr string, base mb.Encoding, cid c.Cid) (string, error) {
39
	p := cid.Prefix()
40
	var out bytes.Buffer
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
	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
63
			fmt.Fprintf(&out, "cidv%d", p.Version)
64
		case 'V': // version num
65
			fmt.Fprintf(&out, "%d", p.Version)
66 67 68
		case 'c': // codec name
			out.WriteString(codecToString(p.Codec))
		case 'C': // codec code
69
			fmt.Fprintf(&out, "%d", p.Codec)
70 71 72
		case 'h': // hash fun name
			out.WriteString(hashToString(p.MhType))
		case 'H': // hash fun code
73
			fmt.Fprintf(&out, "%d", p.MhType)
74
		case 'L': // hash length
75
			fmt.Fprintf(&out, "%d", p.MhLength)
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
		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
93
			fmt.Fprintf(&out, "cidv%d-%s-%s-%d",
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
				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 {
131
	name, ok := c.CodecToStr[num]
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
	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
}