path.go 2.51 KB
Newer Older
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
1 2 3
package path

import (
4
	"errors"
5 6
	"path"
	"strings"
7

8
	key "github.com/ipfs/go-ipfs/blocks/key"
9 10 11

	b58 "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-base58"
	mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
12 13
)

14 15 16
// ErrBadPath is returned when a given path is incorrectly formatted
var ErrBadPath = errors.New("invalid ipfs ref path")

Jeromy's avatar
Jeromy committed
17 18 19
// TODO: debate making this a private struct wrapped in a public interface
// would allow us to control creation, and cache segments.
type Path string
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
20

21 22 23 24 25 26
// FromString safely converts a string type to a Path type
func FromString(s string) Path {
	return Path(s)
}

// FromKey safely converts a Key type to a Path type
27
func FromKey(k key.Key) Path {
28
	return Path("/ipfs/" + k.String())
29 30
}

Jeromy's avatar
Jeromy committed
31 32 33
func (p Path) Segments() []string {
	cleaned := path.Clean(string(p))
	segments := strings.Split(cleaned, "/")
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
34

Jeromy's avatar
Jeromy committed
35 36 37
	// Ignore leading slash
	if len(segments[0]) == 0 {
		segments = segments[1:]
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
38 39
	}

Jeromy's avatar
Jeromy committed
40
	return segments
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
41 42
}

Jeromy's avatar
Jeromy committed
43 44
func (p Path) String() string {
	return string(p)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
45
}
46

47 48 49 50 51 52
// IsJustAKey returns true if the path is of the form <key> or /ipfs/<key>.
func (p Path) IsJustAKey() bool {
	parts := p.Segments()
	return (len(parts) == 2 && parts[0] == "ipfs")
}

53 54
func FromSegments(prefix string, seg ...string) (Path, error) {
	return ParsePath(prefix + strings.Join(seg, "/"))
55
}
56 57 58

func ParsePath(txt string) (Path, error) {
	parts := strings.Split(txt, "/")
Jeromy's avatar
Jeromy committed
59 60 61 62 63 64
	if len(parts) == 1 {
		kp, err := ParseKeyToPath(txt)
		if err == nil {
			return kp, nil
		}
	}
65

66 67
	// if the path doesnt being with a '/'
	// we expect this to start with a hash, and be an 'ipfs' path
68
	if parts[0] != "" {
69 70 71 72 73
		if _, err := ParseKeyToPath(parts[0]); err != nil {
			return "", ErrBadPath
		}
		// The case when the path starts with hash without a protocol prefix
		return Path("/ipfs/" + txt), nil
74 75
	}

76 77 78 79
	if len(parts) < 3 {
		return "", ErrBadPath
	}

80
	if parts[1] == "ipfs" {
81
		if _, err := ParseKeyToPath(parts[2]); err != nil {
82 83 84
			return "", err
		}
	} else if parts[1] != "ipns" {
85 86 87 88 89 90 91
		return "", ErrBadPath
	}

	return Path(txt), nil
}

func ParseKeyToPath(txt string) (Path, error) {
92 93 94 95
	if txt == "" {
		return "", ErrNoComponents
	}

96 97 98 99 100
	chk := b58.Decode(txt)
	if len(chk) == 0 {
		return "", errors.New("not a key")
	}

101
	if _, err := mh.Cast(chk); err != nil {
102 103
		return "", err
	}
104
	return FromKey(key.Key(chk)), nil
105
}
Jeromy's avatar
Jeromy committed
106 107 108 109 110

func (p *Path) IsValid() error {
	_, err := ParsePath(p.String())
	return err
}
111 112 113 114

func Join(pths []string) string {
	return strings.Join(pths, "/")
}
rht's avatar
rht committed
115 116 117 118

func SplitList(pth string) []string {
	return strings.Split(pth, "/")
}