ipfs / go-cid

Content ID v1 implemented in go
MIT License
156 stars 47 forks source link

Support parse (github.com/ipfs/go-path).Path #130

Closed trim21 closed 1 year ago

trim21 commented 3 years ago
    r, err := ipns.ResolveIPNS(context.TODO(), p.NameResolver(), name)
    if err != nil {
        return errors.Wrap(err, "failed to resolve ipns")
    }

    c, err := cid.Parse(r) <- here
    if err != nil {
        return errors.Wrapf(err, "failed to parse CID %s", r)
    }
welcome[bot] commented 3 years ago

Thank you for submitting your first issue to this repository! A maintainer will be here shortly to triage and review. In the meantime, please double-check that you have provided all the necessary information to make this process easy! Any information that can help save additional round trips is useful! We currently aim to give initial feedback within two business days. If this does not happen, feel free to leave a comment. Please keep an eye on how this issue will be labeled, as labels give an overview of priorities, assignments and additional actions requested by the maintainers:

Finally, remember to use https://discuss.ipfs.io if you just need general support.

Stebalien commented 3 years ago

Well, it's not a cid. It's a path (e.g., /ipfs/CID). It usually contains a CID, but it might not (could be a dnslink, could be some other system).

trim21 commented 3 years ago

Well, it's not a cid. It's a path (e.g., /ipfs/CID). It usually contains a CID, but it might not (could be a dnslink, could be some other system).

yes, but string "/ipfs/CID" works for "cid.Parse"

That's what I'd like to see path.Path("/ipfs/did") works, too.

It could still throw a error if it's not a valid cid

Stebalien commented 3 years ago

That's... moderately terrible. But I see your point.

BigLep commented 2 years ago

@schomatis : this is a quick item to tackle we believe. @Jorropo will be the code reviewer.

trim21 commented 2 years ago

Simplest way it to check if it's a interface fmt.Stringer

Jorropo commented 2 years ago

Sounds even more of a hack.

trim21 commented 2 years ago

this is because you can't import github.com/ipfs/go-path in go-cid, go-path already import go-cid. will cause a cycle import.

schomatis commented 2 years ago

I get the general need but I'm not sure what's the request in terms of implementation. Do we want to create a "parse path" API here? Is knowing paths a responsibility of the CID package? Normally the path contains (and it's aware of) the CID (the cycle import is already flagging that).

I'd expect to be the path package the owner of this. In addition to having a FromCid API it could also have the reciprocal ToCid, and let path be the one to tell you if it contains a consumable CID or not, instead of forcing the CID to figure it out from an opaque string.

I'm fine going in any direction here but need more clear input from anyone in this thread, blocking until then.

Jorropo commented 2 years ago

@schomatis

We already have:

func Parse(v interface{}) (Cid, error) {
    switch v2 := v.(type) {
    case string:
        if strings.Contains(v2, "/ipfs/") {
            return Decode(strings.Split(v2, "/ipfs/")[1])
        }
        return Decode(v2)
    // ...

I personally dislike this code, because if I give it /ipfs/Qmfoo/abc it's gonna return Qmfoo instead of an error.

However what is being asked here is to unify the behaviour of string and path.Path.

The canonically solution is:

 func Parse(v interface{}) (Cid, error) {
    switch v2 := v.(type) {
    case string:
        if strings.Contains(v2, "/ipfs/") {
            return Decode(strings.Split(v2, "/ipfs/")[1])
        }
        return Decode(v2)
+   case path.Path:
+       return Parse(string(v2))
    case []byte:
        return Cast(v2)
    case mh.Multihash:
        return tryNewCidV0(v2)
    case Cid:
        return v2, nil
    default:
        return Undef, fmt.Errorf("can't parse %+v as Cid", v2)
    }
 }

but this does not work because of import cycles issue.

A "simple" solution could be:

+import "github.com/nofeaturesonlybugs/set/coerce"
 // ...
 func Parse(v interface{}) (Cid, error) {
+   if v2, err := coerce.String(v); err != nil {
+       if strings.Contains(v2, "/ipfs/") {
+           return Decode(strings.Split(v2, "/ipfs/")[1])
+       }
+       return Decode(v2)
+   }
    switch v2 := v.(type) {
-   case string:
-       if strings.Contains(v2, "/ipfs/") {
-           return Decode(strings.Split(v2, "/ipfs/")[1])
-       }
-       return Decode(v2)
    case []byte:
        return Cast(v2)
    case mh.Multihash:
        return tryNewCidV0(v2)
    case Cid:
        return v2, nil
    default:
        return Undef, fmt.Errorf("can't parse %+v as Cid", v2)
    }
 }

This will work thx to reflect.Type.Kind which will be string for path.Path.

I agree that this behaviour is weird and probably not what we want, but I can't help you on that because I don't know what it should be instead either (I guess it's your job to figure it out if you want to fix it :slightly_smiling_face:).

schomatis commented 2 years ago

@Jorropo

(I guess it's your job to figure it out if you want to fix it slightly_smiling_face).

I've already done my job and told you: (*Path) ToCid() (Cid, error), in github.com/ipfs/go-path, not here.

You're the reviewer so it's your call how to move forward.

Jorropo commented 2 years ago

@schomatis oh nvm, the solution here (in go-cid) would be:

type cidable interface{
  ToCid() (Cid, error)
}

 func Parse(v interface{}) (Cid, error) {
    switch v2 := v.(type) {
    case string:
        if strings.Contains(v2, "/ipfs/") {
            return Decode(strings.Split(v2, "/ipfs/")[1])
        }
        return Decode(v2)
+   case cidable:
+       return v2.ToCid()
    case []byte:
        return Cast(v2)
    case mh.Multihash:
        return tryNewCidV0(v2)
    case Cid:
        return v2, nil
    default:
        return Undef, fmt.Errorf("can't parse %+v as Cid", v2)
    }
 }

? (if yes then I'm good with this)

schomatis commented 2 years ago

I think I have converged with @Jorropo to put this in go-path then.

@Trim21 Would something like this work for your use case?

    r, err := ipns.ResolveIPNS(context.TODO(), p.NameResolver(), name)
    if err != nil {
        return errors.Wrap(err, "failed to resolve ipns")
    }

    // c, err := cid.Parse(r) <- here
    c, err := r.ToCid()
    if err != nil {
        return errors.Wrapf(err, "failed to convert path %s to CID", r)
    }
trim21 commented 2 years ago

lgtm

schomatis commented 2 years ago

Working on this in go-path, will close this when that lands over there.

rvagg commented 1 year ago

Going to close since https://github.com/ipfs/go-path/pull/61 should be the appropriate way to solve this - if you're expecting something more than just a CID then use a higher-level API to solve it, in this case go-path seems to be the most appropriate wrapper to deal with this.