cidbase.go 3.43 KB
Newer Older
1 2 3
package cmdenv

import (
4
	"fmt"
5
	"strings"
6

7
	cid "gx/ipfs/QmR8BauakNcBa3RbE4nbQu76PDiJgoQgz8AJdhJuiU4TAw/go-cid"
Jakub Sztandera's avatar
Jakub Sztandera committed
8
	cmds "gx/ipfs/QmUkb7H2WRutVR91pzoHZPwhbAMFgZMiuGZ2CZ3StuWsJ1/go-ipfs-cmds"
9 10 11 12 13 14
	cidenc "gx/ipfs/QmdPQx9fvN5ExVwMhRmh7YpCQJzJrFhd1AjVBwJmRMFJeX/go-cidutil/cidenc"
	cmdkit "gx/ipfs/Qmde5VP1qUkyQXKCfmEUA7bP64V2HAptbJ7phuPp7jXWwg/go-ipfs-cmdkit"
	mbase "gx/ipfs/QmekxXDhCxCJRNuzmHreuaT3BsuJcsjcXWNrtV9C8DRHtd/go-multibase"
)

var OptionCidBase = cmdkit.StringOption("cid-base", "Multibase encoding used for version 1 CIDs in output.")
15
var OptionUpgradeCidV0InOutput = cmdkit.BoolOption("upgrade-cidv0-in-output", "Upgrade version 0 to version 1 CIDs in output.")
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30

// GetCidEncoder processes the `cid-base` and `output-cidv1` options and
// returns a encoder to use based on those parameters.
func GetCidEncoder(req *cmds.Request) (cidenc.Encoder, error) {
	return getCidBase(req, true)
}

// GetLowLevelCidEncoder is like GetCidEncoder but meant to be used by
// lower level commands.  It differs from GetCidEncoder in that CIDv0
// are not, by default, auto-upgraded to CIDv1.
func GetLowLevelCidEncoder(req *cmds.Request) (cidenc.Encoder, error) {
	return getCidBase(req, false)
}

func getCidBase(req *cmds.Request, autoUpgrade bool) (cidenc.Encoder, error) {
31
	base, _ := req.Options[OptionCidBase.Name()].(string)
32
	upgrade, upgradeDefined := req.Options[OptionUpgradeCidV0InOutput.Name()].(bool)
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63

	e := cidenc.Default()

	if base != "" {
		var err error
		e.Base, err = mbase.EncoderByName(base)
		if err != nil {
			return e, err
		}
		if autoUpgrade {
			e.Upgrade = true
		}
	}

	if upgradeDefined {
		e.Upgrade = upgrade
	}

	return e, nil
}

// CidBaseDefined returns true if the `cid-base` option is specified
// on the command line
func CidBaseDefined(req *cmds.Request) bool {
	base, _ := req.Options["cid-base"].(string)
	return base != ""
}

// CidEncoderFromPath creates a new encoder that is influenced from
// the encoded Cid in a Path.  For CidV0 the multibase from the base
// encoder is used and automatic upgrades are disabled.  For CidV1 the
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
// multibase from the CID is used and upgrades are enabled.
//
// This logic is intentionally fuzzy and will match anything of the form
// `CidLike`, `CidLike/...`, or `/namespace/CidLike/...`.
//
// For example:
//
// * Qm...
// * Qm.../...
// * /ipfs/Qm...
// * /ipns/bafybeiahnxfi7fpmr5wtxs2imx4abnyn7fdxeiox7xxjem6zuiioqkh6zi/...
// * /bzz/bafybeiahnxfi7fpmr5wtxs2imx4abnyn7fdxeiox7xxjem6zuiioqkh6zi/...
func CidEncoderFromPath(p string) (cidenc.Encoder, error) {
	components := strings.SplitN(p, "/", 4)

	var maybeCid string
	if components[0] != "" {
		// No leading slash, first component is likely CID-like.
		maybeCid = components[0]
	} else if len(components) < 3 {
		// Not enough components to include a CID.
		return cidenc.Encoder{}, fmt.Errorf("no cid in path: %s", p)
	} else {
		maybeCid = components[2]
88
	}
89
	c, err := cid.Decode(maybeCid)
90
	if err != nil {
91 92
		// Ok, not a CID-like thing. Keep the current encoder.
		return cidenc.Encoder{}, fmt.Errorf("no cid in path: %s", p)
93
	}
94 95 96
	if c.Version() == 0 {
		// Version 0, use the base58 non-upgrading encoder.
		return cidenc.Default(), nil
97 98
	}

99 100 101 102 103
	// Version 1+, extract multibase encoding.
	encoding, _, err := mbase.Decode(maybeCid)
	if err != nil {
		// This should be impossible, we've already decoded the cid.
		panic(fmt.Sprintf("BUG: failed to get multibase decoder for CID %s", maybeCid))
104
	}
105 106

	return cidenc.Encoder{Base: mbase.MustNewEncoder(encoding), Upgrade: true}, nil
107
}