package cty import ( "fmt" "reflect" ) type capsuleType struct { typeImplSigil Name string GoType reflect.Type } func (t *capsuleType) Equals(other Type) bool { if otherP, ok := other.typeImpl.(*capsuleType); ok { // capsule types compare by pointer identity return otherP == t } return false } func (t *capsuleType) FriendlyName(mode friendlyTypeNameMode) string { return t.Name } func (t *capsuleType) GoString() string { // To get a useful representation of our native type requires some // shenanigans. victimVal := reflect.Zero(t.GoType) return fmt.Sprintf("cty.Capsule(%q, reflect.TypeOf(%#v))", t.Name, victimVal.Interface()) } // Capsule creates a new Capsule type. // // A Capsule type is a special type that can be used to transport arbitrary // Go native values of a given type through the cty type system. A language // that uses cty as its type system might, for example, provide functions // that return capsule-typed values and then other functions that operate // on those values. // // From cty's perspective, Capsule types have a few interesting characteristics, // described in the following paragraphs. // // Each capsule type has an associated Go native type that it is able to // transport. Capsule types compare by identity, so each call to the // Capsule function creates an entirely-distinct cty Type, even if two calls // use the same native type. // // Each capsule-typed value contains a pointer to a value of the given native // type. A capsule-typed value supports no operations except equality, and // equality is implemented by pointer identity of the encapsulated pointer. // // The given name is used as the new type's "friendly name". This can be any // string in principle, but will usually be a short, all-lowercase name aimed // at users of the embedding language (i.e. not mention Go-specific details) // and will ideally not create ambiguity with any predefined cty type. // // Capsule types are never introduced by any standard cty operation, so a // calling application opts in to including them within its own type system // by creating them and introducing them via its own functions. At that point, // the application is responsible for dealing with any capsule-typed values // that might be returned. func Capsule(name string, nativeType reflect.Type) Type { return Type{ &capsuleType{ Name: name, GoType: nativeType, }, } } // IsCapsuleType returns true if this type is a capsule type, as created // by cty.Capsule . func (t Type) IsCapsuleType() bool { _, ok := t.typeImpl.(*capsuleType) return ok } // EncapsulatedType returns the encapsulated native type of a capsule type, // or panics if the receiver is not a Capsule type. // // Is IsCapsuleType to determine if this method is safe to call. func (t Type) EncapsulatedType() reflect.Type { impl, ok := t.typeImpl.(*capsuleType) if !ok { panic("not a capsule type") } return impl.GoType }