diff --git a/proto/encore/parser/meta/v1/meta.pb.go b/proto/encore/parser/meta/v1/meta.pb.go index 0d274c9de6..0d4b9bbfa7 100644 --- a/proto/encore/parser/meta/v1/meta.pb.go +++ b/proto/encore/parser/meta/v1/meta.pb.go @@ -2485,9 +2485,10 @@ type PathSegment struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Type PathSegment_SegmentType `protobuf:"varint,1,opt,name=type,proto3,enum=encore.parser.meta.v1.PathSegment_SegmentType" json:"type,omitempty"` - Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` - ValueType PathSegment_ParamType `protobuf:"varint,3,opt,name=value_type,json=valueType,proto3,enum=encore.parser.meta.v1.PathSegment_ParamType" json:"value_type,omitempty"` + Type PathSegment_SegmentType `protobuf:"varint,1,opt,name=type,proto3,enum=encore.parser.meta.v1.PathSegment_SegmentType" json:"type,omitempty"` + Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` + ValueType PathSegment_ParamType `protobuf:"varint,3,opt,name=value_type,json=valueType,proto3,enum=encore.parser.meta.v1.PathSegment_ParamType" json:"value_type,omitempty"` + Validation *v1.ValidationExpr `protobuf:"bytes,4,opt,name=validation,proto3,oneof" json:"validation,omitempty"` } func (x *PathSegment) Reset() { @@ -2543,6 +2544,13 @@ func (x *PathSegment) GetValueType() PathSegment_ParamType { return PathSegment_STRING } +func (x *PathSegment) GetValidation() *v1.ValidationExpr { + if x != nil { + return x.Validation + } + return nil +} + type Gateway struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -4057,7 +4065,7 @@ var file_encore_parser_meta_v1_meta_proto_rawDesc = []byte{ 0x76, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x23, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x07, 0x0a, 0x03, 0x55, 0x52, 0x4c, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x43, 0x41, 0x43, 0x48, 0x45, 0x5f, 0x4b, 0x45, 0x59, - 0x53, 0x50, 0x41, 0x43, 0x45, 0x10, 0x01, 0x22, 0x92, 0x03, 0x0a, 0x0b, 0x50, 0x61, 0x74, 0x68, + 0x53, 0x50, 0x41, 0x43, 0x45, 0x10, 0x01, 0x22, 0xef, 0x03, 0x0a, 0x0b, 0x50, 0x61, 0x74, 0x68, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x42, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2e, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, @@ -4068,199 +4076,204 @@ var file_encore_parser_meta_v1_meta_proto_rawDesc = []byte{ 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x54, - 0x79, 0x70, 0x65, 0x52, 0x09, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x54, 0x79, 0x70, 0x65, 0x22, 0x41, - 0x0a, 0x0b, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, - 0x07, 0x4c, 0x49, 0x54, 0x45, 0x52, 0x41, 0x4c, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x50, 0x41, - 0x52, 0x41, 0x4d, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x57, 0x49, 0x4c, 0x44, 0x43, 0x41, 0x52, - 0x44, 0x10, 0x02, 0x12, 0x0c, 0x0a, 0x08, 0x46, 0x41, 0x4c, 0x4c, 0x42, 0x41, 0x43, 0x4b, 0x10, - 0x03, 0x22, 0x98, 0x01, 0x0a, 0x09, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x54, 0x79, 0x70, 0x65, 0x12, - 0x0a, 0x0a, 0x06, 0x53, 0x54, 0x52, 0x49, 0x4e, 0x47, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x42, - 0x4f, 0x4f, 0x4c, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x4e, 0x54, 0x38, 0x10, 0x02, 0x12, - 0x09, 0x0a, 0x05, 0x49, 0x4e, 0x54, 0x31, 0x36, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x49, 0x4e, - 0x54, 0x33, 0x32, 0x10, 0x04, 0x12, 0x09, 0x0a, 0x05, 0x49, 0x4e, 0x54, 0x36, 0x34, 0x10, 0x05, - 0x12, 0x07, 0x0a, 0x03, 0x49, 0x4e, 0x54, 0x10, 0x06, 0x12, 0x09, 0x0a, 0x05, 0x55, 0x49, 0x4e, - 0x54, 0x38, 0x10, 0x07, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x49, 0x4e, 0x54, 0x31, 0x36, 0x10, 0x08, - 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x49, 0x4e, 0x54, 0x33, 0x32, 0x10, 0x09, 0x12, 0x0a, 0x0a, 0x06, - 0x55, 0x49, 0x4e, 0x54, 0x36, 0x34, 0x10, 0x0a, 0x12, 0x08, 0x0a, 0x04, 0x55, 0x49, 0x4e, 0x54, - 0x10, 0x0b, 0x12, 0x08, 0x0a, 0x04, 0x55, 0x55, 0x49, 0x44, 0x10, 0x0c, 0x22, 0x8e, 0x02, 0x0a, - 0x07, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x6e, 0x63, 0x6f, - 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, - 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x48, 0x0a, 0x08, 0x65, 0x78, 0x70, - 0x6c, 0x69, 0x63, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x65, 0x6e, + 0x79, 0x70, 0x65, 0x52, 0x09, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x4c, + 0x0a, 0x0a, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, + 0x65, 0x72, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x61, 0x6c, + 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x70, 0x72, 0x48, 0x00, 0x52, 0x0a, 0x76, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x22, 0x41, 0x0a, 0x0b, + 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x4c, + 0x49, 0x54, 0x45, 0x52, 0x41, 0x4c, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x50, 0x41, 0x52, 0x41, + 0x4d, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x57, 0x49, 0x4c, 0x44, 0x43, 0x41, 0x52, 0x44, 0x10, + 0x02, 0x12, 0x0c, 0x0a, 0x08, 0x46, 0x41, 0x4c, 0x4c, 0x42, 0x41, 0x43, 0x4b, 0x10, 0x03, 0x22, + 0x98, 0x01, 0x0a, 0x09, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0a, 0x0a, + 0x06, 0x53, 0x54, 0x52, 0x49, 0x4e, 0x47, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x42, 0x4f, 0x4f, + 0x4c, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x4e, 0x54, 0x38, 0x10, 0x02, 0x12, 0x09, 0x0a, + 0x05, 0x49, 0x4e, 0x54, 0x31, 0x36, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x49, 0x4e, 0x54, 0x33, + 0x32, 0x10, 0x04, 0x12, 0x09, 0x0a, 0x05, 0x49, 0x4e, 0x54, 0x36, 0x34, 0x10, 0x05, 0x12, 0x07, + 0x0a, 0x03, 0x49, 0x4e, 0x54, 0x10, 0x06, 0x12, 0x09, 0x0a, 0x05, 0x55, 0x49, 0x4e, 0x54, 0x38, + 0x10, 0x07, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x49, 0x4e, 0x54, 0x31, 0x36, 0x10, 0x08, 0x12, 0x0a, + 0x0a, 0x06, 0x55, 0x49, 0x4e, 0x54, 0x33, 0x32, 0x10, 0x09, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x49, + 0x4e, 0x54, 0x36, 0x34, 0x10, 0x0a, 0x12, 0x08, 0x0a, 0x04, 0x55, 0x49, 0x4e, 0x54, 0x10, 0x0b, + 0x12, 0x08, 0x0a, 0x04, 0x55, 0x55, 0x49, 0x44, 0x10, 0x0c, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x76, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x8e, 0x02, 0x0a, 0x07, 0x47, 0x61, + 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x5f, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x6e, 0x63, 0x6f, + 0x72, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x48, 0x0a, 0x08, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, + 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, + 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, + 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x45, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, + 0x74, 0x48, 0x00, 0x52, 0x08, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x88, 0x01, 0x01, + 0x1a, 0x8a, 0x01, 0x0a, 0x08, 0x45, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x12, 0x21, 0x0a, + 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x4a, 0x0a, 0x0c, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x41, + 0x75, 0x74, 0x68, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x48, 0x00, 0x52, 0x0b, 0x61, 0x75, + 0x74, 0x68, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x88, 0x01, 0x01, 0x42, 0x0f, 0x0a, 0x0d, + 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x42, 0x0b, 0x0a, + 0x09, 0x5f, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x22, 0xac, 0x01, 0x0a, 0x07, 0x43, + 0x72, 0x6f, 0x6e, 0x4a, 0x6f, 0x62, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x15, 0x0a, 0x03, + 0x64, 0x6f, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, 0x64, 0x6f, 0x63, + 0x88, 0x01, 0x01, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x12, + 0x40, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x24, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, + 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x61, 0x6c, 0x69, 0x66, + 0x69, 0x65, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, + 0x74, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x64, 0x6f, 0x63, 0x22, 0x95, 0x02, 0x0a, 0x0b, 0x53, 0x51, + 0x4c, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x15, 0x0a, + 0x03, 0x64, 0x6f, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, 0x64, 0x6f, + 0x63, 0x88, 0x01, 0x01, 0x12, 0x31, 0x0a, 0x12, 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x72, 0x65, 0x6c, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x48, 0x01, 0x52, 0x10, 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, + 0x50, 0x61, 0x74, 0x68, 0x88, 0x01, 0x01, 0x12, 0x42, 0x0a, 0x0a, 0x6d, 0x69, 0x67, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, - 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x45, 0x78, 0x70, 0x6c, - 0x69, 0x63, 0x69, 0x74, 0x48, 0x00, 0x52, 0x08, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, - 0x88, 0x01, 0x01, 0x1a, 0x8a, 0x01, 0x0a, 0x08, 0x45, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, + 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x42, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x0a, 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x45, 0x0a, 0x1f, 0x61, + 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x6e, 0x6f, 0x6e, 0x5f, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x74, + 0x69, 0x61, 0x6c, 0x5f, 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x1c, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x4e, 0x6f, 0x6e, 0x53, 0x65, + 0x71, 0x75, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x64, 0x6f, 0x63, 0x42, 0x15, 0x0a, 0x13, 0x5f, 0x6d, + 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x6c, 0x5f, 0x70, 0x61, 0x74, + 0x68, 0x22, 0x63, 0x0a, 0x0b, 0x44, 0x42, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, + 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x6e, 0x75, + 0x6d, 0x62, 0x65, 0x72, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x71, 0x0a, 0x06, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, + 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x15, 0x0a, 0x03, 0x64, 0x6f, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x48, 0x00, 0x52, 0x03, 0x64, 0x6f, 0x63, 0x88, 0x01, 0x01, 0x12, 0x1c, 0x0a, 0x09, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6c, 0x69, + 0x63, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x64, 0x6f, 0x63, 0x22, 0xb8, 0x07, 0x0a, 0x0b, 0x50, 0x75, + 0x62, 0x53, 0x75, 0x62, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x15, 0x0a, + 0x03, 0x64, 0x6f, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, 0x64, 0x6f, + 0x63, 0x88, 0x01, 0x01, 0x12, 0x40, 0x0a, 0x0c, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x65, 0x6e, 0x63, + 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, + 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0b, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x63, 0x0a, 0x12, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, + 0x72, 0x79, 0x5f, 0x67, 0x75, 0x61, 0x72, 0x61, 0x6e, 0x74, 0x65, 0x65, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x34, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, + 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x62, 0x53, 0x75, + 0x62, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x47, + 0x75, 0x61, 0x72, 0x61, 0x6e, 0x74, 0x65, 0x65, 0x52, 0x11, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, + 0x72, 0x79, 0x47, 0x75, 0x61, 0x72, 0x61, 0x6e, 0x74, 0x65, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x6f, + 0x72, 0x64, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x4b, 0x65, 0x79, 0x12, 0x4c, + 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x72, 0x73, 0x18, 0x06, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, + 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x62, 0x53, 0x75, + 0x62, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x72, + 0x52, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x72, 0x73, 0x12, 0x55, 0x0a, 0x0d, + 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x07, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, + 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x62, 0x53, + 0x75, 0x62, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x1a, 0x2e, 0x0a, 0x09, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, - 0x61, 0x6d, 0x65, 0x12, 0x4a, 0x0a, 0x0c, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x68, 0x61, 0x6e, 0x64, - 0x6c, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65, 0x6e, 0x63, 0x6f, - 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, - 0x31, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x48, 0x00, 0x52, - 0x0b, 0x61, 0x75, 0x74, 0x68, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x88, 0x01, 0x01, 0x42, - 0x0f, 0x0a, 0x0d, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, - 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x22, 0xac, 0x01, - 0x0a, 0x07, 0x43, 0x72, 0x6f, 0x6e, 0x4a, 0x6f, 0x62, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, - 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, - 0x15, 0x0a, 0x03, 0x64, 0x6f, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, - 0x64, 0x6f, 0x63, 0x88, 0x01, 0x01, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, - 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, - 0x6c, 0x65, 0x12, 0x40, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, - 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x61, - 0x6c, 0x69, 0x66, 0x69, 0x65, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x08, 0x65, 0x6e, 0x64, 0x70, - 0x6f, 0x69, 0x6e, 0x74, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x64, 0x6f, 0x63, 0x22, 0x95, 0x02, 0x0a, - 0x0b, 0x53, 0x51, 0x4c, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x12, 0x15, 0x0a, 0x03, 0x64, 0x6f, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, - 0x03, 0x64, 0x6f, 0x63, 0x88, 0x01, 0x01, 0x12, 0x31, 0x0a, 0x12, 0x6d, 0x69, 0x67, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x6c, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x10, 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x65, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x88, 0x01, 0x01, 0x12, 0x42, 0x0a, 0x0a, 0x6d, 0x69, - 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, + 0x61, 0x6d, 0x65, 0x1a, 0xaa, 0x02, 0x0a, 0x0c, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x61, + 0x63, 0x6b, 0x5f, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x0b, 0x61, 0x63, 0x6b, 0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x2b, + 0x0a, 0x11, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x52, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x51, 0x0a, 0x0c, 0x72, + 0x65, 0x74, 0x72, 0x79, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x2e, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, + 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, + 0x54, 0x6f, 0x70, 0x69, 0x63, 0x2e, 0x52, 0x65, 0x74, 0x72, 0x79, 0x50, 0x6f, 0x6c, 0x69, 0x63, + 0x79, 0x52, 0x0b, 0x72, 0x65, 0x74, 0x72, 0x79, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x2c, + 0x0a, 0x0f, 0x6d, 0x61, 0x78, 0x5f, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, + 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x48, 0x00, 0x52, 0x0e, 0x6d, 0x61, 0x78, 0x43, 0x6f, + 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x88, 0x01, 0x01, 0x42, 0x12, 0x0a, 0x10, + 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, + 0x1a, 0x70, 0x0a, 0x0b, 0x52, 0x65, 0x74, 0x72, 0x79, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, + 0x1f, 0x0a, 0x0b, 0x6d, 0x69, 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x6d, 0x69, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, + 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x61, 0x78, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x6d, 0x61, 0x78, 0x42, 0x61, 0x63, 0x6b, 0x6f, 0x66, + 0x66, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x6d, 0x61, 0x78, 0x52, 0x65, 0x74, 0x72, 0x69, + 0x65, 0x73, 0x22, 0x38, 0x0a, 0x11, 0x44, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x47, 0x75, + 0x61, 0x72, 0x61, 0x6e, 0x74, 0x65, 0x65, 0x12, 0x11, 0x0a, 0x0d, 0x41, 0x54, 0x5f, 0x4c, 0x45, + 0x41, 0x53, 0x54, 0x5f, 0x4f, 0x4e, 0x43, 0x45, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x45, 0x58, + 0x41, 0x43, 0x54, 0x4c, 0x59, 0x5f, 0x4f, 0x4e, 0x43, 0x45, 0x10, 0x01, 0x42, 0x06, 0x0a, 0x04, + 0x5f, 0x64, 0x6f, 0x63, 0x22, 0x9a, 0x03, 0x0a, 0x0c, 0x43, 0x61, 0x63, 0x68, 0x65, 0x43, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x6f, 0x63, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x64, 0x6f, 0x63, 0x12, 0x4a, 0x0a, 0x09, 0x6b, + 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, - 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x42, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x0a, 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x45, - 0x0a, 0x1f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x6e, 0x6f, 0x6e, 0x5f, 0x73, 0x65, 0x71, 0x75, - 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1c, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x4e, 0x6f, - 0x6e, 0x53, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4d, 0x69, 0x67, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x64, 0x6f, 0x63, 0x42, 0x15, 0x0a, - 0x13, 0x5f, 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x6c, 0x5f, - 0x70, 0x61, 0x74, 0x68, 0x22, 0x63, 0x0a, 0x0b, 0x44, 0x42, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x12, - 0x16, 0x0a, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, - 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x71, 0x0a, 0x06, 0x42, 0x75, 0x63, - 0x6b, 0x65, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x15, 0x0a, 0x03, 0x64, 0x6f, 0x63, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, 0x64, 0x6f, 0x63, 0x88, 0x01, 0x01, 0x12, 0x1c, - 0x0a, 0x09, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x09, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, - 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x70, 0x75, - 0x62, 0x6c, 0x69, 0x63, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x64, 0x6f, 0x63, 0x22, 0xb8, 0x07, 0x0a, - 0x0b, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x12, 0x12, 0x0a, 0x04, + 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x63, 0x68, 0x65, 0x43, 0x6c, 0x75, 0x73, + 0x74, 0x65, 0x72, 0x2e, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x09, 0x6b, 0x65, + 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x65, 0x76, 0x69, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0e, 0x65, 0x76, 0x69, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x1a, 0xee, 0x01, 0x0a, 0x08, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x38, 0x0a, + 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, + 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x07, + 0x6b, 0x65, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x3c, 0x0a, 0x0a, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x65, 0x6e, + 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x73, 0x63, 0x68, 0x65, + 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x09, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, + 0x10, 0x0a, 0x03, 0x64, 0x6f, 0x63, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x64, 0x6f, + 0x63, 0x12, 0x3e, 0x0a, 0x0c, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, + 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, + 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, + 0x50, 0x61, 0x74, 0x68, 0x52, 0x0b, 0x70, 0x61, 0x74, 0x68, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, + 0x6e, 0x22, 0xbb, 0x03, 0x0a, 0x06, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x12, 0x15, 0x0a, 0x03, 0x64, 0x6f, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, - 0x03, 0x64, 0x6f, 0x63, 0x88, 0x01, 0x01, 0x12, 0x40, 0x0a, 0x0c, 0x6d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, - 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x73, 0x63, - 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0b, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x63, 0x0a, 0x12, 0x64, 0x65, 0x6c, - 0x69, 0x76, 0x65, 0x72, 0x79, 0x5f, 0x67, 0x75, 0x61, 0x72, 0x61, 0x6e, 0x74, 0x65, 0x65, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x34, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, - 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, - 0x62, 0x53, 0x75, 0x62, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x2e, 0x44, 0x65, 0x6c, 0x69, 0x76, 0x65, - 0x72, 0x79, 0x47, 0x75, 0x61, 0x72, 0x61, 0x6e, 0x74, 0x65, 0x65, 0x52, 0x11, 0x64, 0x65, 0x6c, - 0x69, 0x76, 0x65, 0x72, 0x79, 0x47, 0x75, 0x61, 0x72, 0x61, 0x6e, 0x74, 0x65, 0x65, 0x12, 0x21, - 0x0a, 0x0c, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x4b, 0x65, - 0x79, 0x12, 0x4c, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x72, 0x73, 0x18, - 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, - 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, - 0x62, 0x53, 0x75, 0x62, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, - 0x68, 0x65, 0x72, 0x52, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x72, 0x73, 0x12, - 0x55, 0x0a, 0x0d, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, - 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x50, - 0x75, 0x62, 0x53, 0x75, 0x62, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x2e, 0x0a, 0x09, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, - 0x68, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x1a, 0xaa, 0x02, 0x0a, 0x0c, 0x53, 0x75, 0x62, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x73, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x21, - 0x0a, 0x0c, 0x61, 0x63, 0x6b, 0x5f, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x61, 0x63, 0x6b, 0x44, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, - 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x72, 0x65, 0x74, - 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x51, - 0x0a, 0x0c, 0x72, 0x65, 0x74, 0x72, 0x79, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, - 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x62, - 0x53, 0x75, 0x62, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x2e, 0x52, 0x65, 0x74, 0x72, 0x79, 0x50, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0b, 0x72, 0x65, 0x74, 0x72, 0x79, 0x50, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x12, 0x2c, 0x0a, 0x0f, 0x6d, 0x61, 0x78, 0x5f, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, - 0x65, 0x6e, 0x63, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x48, 0x00, 0x52, 0x0e, 0x6d, 0x61, - 0x78, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x88, 0x01, 0x01, 0x42, - 0x12, 0x0a, 0x10, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x63, 0x79, 0x1a, 0x70, 0x0a, 0x0b, 0x52, 0x65, 0x74, 0x72, 0x79, 0x50, 0x6f, 0x6c, 0x69, - 0x63, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x69, 0x6e, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x6f, 0x66, - 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x6d, 0x69, 0x6e, 0x42, 0x61, 0x63, 0x6b, - 0x6f, 0x66, 0x66, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x61, 0x78, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x6f, - 0x66, 0x66, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x6d, 0x61, 0x78, 0x42, 0x61, 0x63, - 0x6b, 0x6f, 0x66, 0x66, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x65, 0x74, 0x72, - 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x6d, 0x61, 0x78, 0x52, 0x65, - 0x74, 0x72, 0x69, 0x65, 0x73, 0x22, 0x38, 0x0a, 0x11, 0x44, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, - 0x79, 0x47, 0x75, 0x61, 0x72, 0x61, 0x6e, 0x74, 0x65, 0x65, 0x12, 0x11, 0x0a, 0x0d, 0x41, 0x54, - 0x5f, 0x4c, 0x45, 0x41, 0x53, 0x54, 0x5f, 0x4f, 0x4e, 0x43, 0x45, 0x10, 0x00, 0x12, 0x10, 0x0a, - 0x0c, 0x45, 0x58, 0x41, 0x43, 0x54, 0x4c, 0x59, 0x5f, 0x4f, 0x4e, 0x43, 0x45, 0x10, 0x01, 0x42, - 0x06, 0x0a, 0x04, 0x5f, 0x64, 0x6f, 0x63, 0x22, 0x9a, 0x03, 0x0a, 0x0c, 0x43, 0x61, 0x63, 0x68, - 0x65, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, - 0x64, 0x6f, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x64, 0x6f, 0x63, 0x12, 0x4a, - 0x0a, 0x09, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x2c, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, - 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x63, 0x68, 0x65, 0x43, - 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, - 0x09, 0x6b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x65, 0x76, - 0x69, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0e, 0x65, 0x76, 0x69, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, - 0x69, 0x63, 0x79, 0x1a, 0xee, 0x01, 0x0a, 0x08, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x12, 0x38, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, - 0x65, 0x72, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x79, 0x70, - 0x65, 0x52, 0x07, 0x6b, 0x65, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x3c, 0x0a, 0x0a, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, - 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x73, - 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x09, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x6f, 0x63, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x64, 0x6f, 0x63, 0x12, 0x3e, 0x0a, 0x0c, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x70, 0x61, 0x74, - 0x74, 0x65, 0x72, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x65, 0x6e, 0x63, - 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, - 0x76, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x52, 0x0b, 0x70, 0x61, 0x74, 0x68, 0x50, 0x61, 0x74, - 0x74, 0x65, 0x72, 0x6e, 0x22, 0xbb, 0x03, 0x0a, 0x06, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, - 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x12, 0x3f, 0x0a, 0x0a, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x74, 0x79, 0x70, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, - 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, - 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x74, 0x69, 0x6e, 0x52, 0x09, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x54, 0x79, 0x70, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x6f, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x64, 0x6f, 0x63, 0x12, 0x3c, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x0e, 0x32, 0x28, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, - 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, - 0x72, 0x69, 0x63, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x04, - 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x26, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0b, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, 0x3b, 0x0a, 0x06, - 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x65, - 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, - 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x4c, 0x61, 0x62, 0x65, - 0x6c, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x1a, 0x61, 0x0a, 0x05, 0x4c, 0x61, 0x62, - 0x65, 0x6c, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x6b, 0x65, 0x79, 0x12, 0x34, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, - 0x65, 0x72, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x69, - 0x6c, 0x74, 0x69, 0x6e, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x6f, - 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x64, 0x6f, 0x63, 0x22, 0x33, 0x0a, 0x0a, - 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x4f, - 0x55, 0x4e, 0x54, 0x45, 0x52, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x47, 0x41, 0x55, 0x47, 0x45, - 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x48, 0x49, 0x53, 0x54, 0x4f, 0x47, 0x52, 0x41, 0x4d, 0x10, - 0x02, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x2a, 0x1e, 0x0a, 0x04, 0x4c, 0x61, 0x6e, 0x67, 0x12, 0x06, 0x0a, 0x02, 0x47, 0x4f, - 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x54, 0x59, 0x50, 0x45, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, - 0x10, 0x01, 0x42, 0x26, 0x5a, 0x24, 0x65, 0x6e, 0x63, 0x72, 0x2e, 0x64, 0x65, 0x76, 0x2f, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x70, 0x61, 0x72, 0x73, - 0x65, 0x72, 0x2f, 0x6d, 0x65, 0x74, 0x61, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x12, 0x3f, 0x0a, 0x0a, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, + 0x72, 0x73, 0x65, 0x72, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x42, + 0x75, 0x69, 0x6c, 0x74, 0x69, 0x6e, 0x52, 0x09, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x54, 0x79, 0x70, + 0x65, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x6f, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x64, 0x6f, 0x63, 0x12, 0x3c, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x28, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, + 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, + 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x04, 0x6b, 0x69, 0x6e, + 0x64, 0x12, 0x26, 0x0a, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, 0x3b, 0x0a, 0x06, 0x6c, 0x61, 0x62, + 0x65, 0x6c, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x65, 0x6e, 0x63, 0x6f, + 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x76, + 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x52, 0x06, + 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x1a, 0x61, 0x0a, 0x05, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x12, + 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, + 0x79, 0x12, 0x34, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x20, 0x2e, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2e, + 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x74, 0x69, + 0x6e, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x6f, 0x63, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x64, 0x6f, 0x63, 0x22, 0x33, 0x0a, 0x0a, 0x4d, 0x65, 0x74, + 0x72, 0x69, 0x63, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x4f, 0x55, 0x4e, 0x54, + 0x45, 0x52, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x47, 0x41, 0x55, 0x47, 0x45, 0x10, 0x01, 0x12, + 0x0d, 0x0a, 0x09, 0x48, 0x49, 0x53, 0x54, 0x4f, 0x47, 0x52, 0x41, 0x4d, 0x10, 0x02, 0x42, 0x0f, + 0x0a, 0x0d, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x2a, + 0x1e, 0x0a, 0x04, 0x4c, 0x61, 0x6e, 0x67, 0x12, 0x06, 0x0a, 0x02, 0x47, 0x4f, 0x10, 0x00, 0x12, + 0x0e, 0x0a, 0x0a, 0x54, 0x59, 0x50, 0x45, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x10, 0x01, 0x42, + 0x26, 0x5a, 0x24, 0x65, 0x6e, 0x63, 0x72, 0x2e, 0x64, 0x65, 0x76, 0x2f, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x2f, 0x65, 0x6e, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x70, 0x61, 0x72, 0x73, 0x65, 0x72, 0x2f, + 0x6d, 0x65, 0x74, 0x61, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -4331,7 +4344,8 @@ var file_encore_parser_meta_v1_meta_proto_goTypes = []interface{}{ (*v1.Decl)(nil), // 50: encore.parser.schema.v1.Decl (*v1.Type)(nil), // 51: encore.parser.schema.v1.Type (*v1.Loc)(nil), // 52: encore.parser.schema.v1.Loc - (v1.Builtin)(0), // 53: encore.parser.schema.v1.Builtin + (*v1.ValidationExpr)(nil), // 53: encore.parser.schema.v1.ValidationExpr + (v1.Builtin)(0), // 54: encore.parser.schema.v1.Builtin } var file_encore_parser_meta_v1_meta_proto_depIdxs = []int32{ 50, // 0: encore.parser.meta.v1.Data.decls:type_name -> encore.parser.schema.v1.Decl @@ -4386,29 +4400,30 @@ var file_encore_parser_meta_v1_meta_proto_depIdxs = []int32{ 6, // 49: encore.parser.meta.v1.Path.type:type_name -> encore.parser.meta.v1.Path.Type 7, // 50: encore.parser.meta.v1.PathSegment.type:type_name -> encore.parser.meta.v1.PathSegment.SegmentType 8, // 51: encore.parser.meta.v1.PathSegment.value_type:type_name -> encore.parser.meta.v1.PathSegment.ParamType - 44, // 52: encore.parser.meta.v1.Gateway.explicit:type_name -> encore.parser.meta.v1.Gateway.Explicit - 12, // 53: encore.parser.meta.v1.CronJob.endpoint:type_name -> encore.parser.meta.v1.QualifiedName - 36, // 54: encore.parser.meta.v1.SQLDatabase.migrations:type_name -> encore.parser.meta.v1.DBMigration - 51, // 55: encore.parser.meta.v1.PubSubTopic.message_type:type_name -> encore.parser.schema.v1.Type - 9, // 56: encore.parser.meta.v1.PubSubTopic.delivery_guarantee:type_name -> encore.parser.meta.v1.PubSubTopic.DeliveryGuarantee - 45, // 57: encore.parser.meta.v1.PubSubTopic.publishers:type_name -> encore.parser.meta.v1.PubSubTopic.Publisher - 46, // 58: encore.parser.meta.v1.PubSubTopic.subscriptions:type_name -> encore.parser.meta.v1.PubSubTopic.Subscription - 48, // 59: encore.parser.meta.v1.CacheCluster.keyspaces:type_name -> encore.parser.meta.v1.CacheCluster.Keyspace - 53, // 60: encore.parser.meta.v1.Metric.value_type:type_name -> encore.parser.schema.v1.Builtin - 10, // 61: encore.parser.meta.v1.Metric.kind:type_name -> encore.parser.meta.v1.Metric.MetricKind - 49, // 62: encore.parser.meta.v1.Metric.labels:type_name -> encore.parser.meta.v1.Metric.Label - 42, // 63: encore.parser.meta.v1.RPC.ExposeEntry.value:type_name -> encore.parser.meta.v1.RPC.ExposeOptions - 18, // 64: encore.parser.meta.v1.Gateway.Explicit.auth_handler:type_name -> encore.parser.meta.v1.AuthHandler - 47, // 65: encore.parser.meta.v1.PubSubTopic.Subscription.retry_policy:type_name -> encore.parser.meta.v1.PubSubTopic.RetryPolicy - 51, // 66: encore.parser.meta.v1.CacheCluster.Keyspace.key_type:type_name -> encore.parser.schema.v1.Type - 51, // 67: encore.parser.meta.v1.CacheCluster.Keyspace.value_type:type_name -> encore.parser.schema.v1.Type - 31, // 68: encore.parser.meta.v1.CacheCluster.Keyspace.path_pattern:type_name -> encore.parser.meta.v1.Path - 53, // 69: encore.parser.meta.v1.Metric.Label.type:type_name -> encore.parser.schema.v1.Builtin - 70, // [70:70] is the sub-list for method output_type - 70, // [70:70] is the sub-list for method input_type - 70, // [70:70] is the sub-list for extension type_name - 70, // [70:70] is the sub-list for extension extendee - 0, // [0:70] is the sub-list for field type_name + 53, // 52: encore.parser.meta.v1.PathSegment.validation:type_name -> encore.parser.schema.v1.ValidationExpr + 44, // 53: encore.parser.meta.v1.Gateway.explicit:type_name -> encore.parser.meta.v1.Gateway.Explicit + 12, // 54: encore.parser.meta.v1.CronJob.endpoint:type_name -> encore.parser.meta.v1.QualifiedName + 36, // 55: encore.parser.meta.v1.SQLDatabase.migrations:type_name -> encore.parser.meta.v1.DBMigration + 51, // 56: encore.parser.meta.v1.PubSubTopic.message_type:type_name -> encore.parser.schema.v1.Type + 9, // 57: encore.parser.meta.v1.PubSubTopic.delivery_guarantee:type_name -> encore.parser.meta.v1.PubSubTopic.DeliveryGuarantee + 45, // 58: encore.parser.meta.v1.PubSubTopic.publishers:type_name -> encore.parser.meta.v1.PubSubTopic.Publisher + 46, // 59: encore.parser.meta.v1.PubSubTopic.subscriptions:type_name -> encore.parser.meta.v1.PubSubTopic.Subscription + 48, // 60: encore.parser.meta.v1.CacheCluster.keyspaces:type_name -> encore.parser.meta.v1.CacheCluster.Keyspace + 54, // 61: encore.parser.meta.v1.Metric.value_type:type_name -> encore.parser.schema.v1.Builtin + 10, // 62: encore.parser.meta.v1.Metric.kind:type_name -> encore.parser.meta.v1.Metric.MetricKind + 49, // 63: encore.parser.meta.v1.Metric.labels:type_name -> encore.parser.meta.v1.Metric.Label + 42, // 64: encore.parser.meta.v1.RPC.ExposeEntry.value:type_name -> encore.parser.meta.v1.RPC.ExposeOptions + 18, // 65: encore.parser.meta.v1.Gateway.Explicit.auth_handler:type_name -> encore.parser.meta.v1.AuthHandler + 47, // 66: encore.parser.meta.v1.PubSubTopic.Subscription.retry_policy:type_name -> encore.parser.meta.v1.PubSubTopic.RetryPolicy + 51, // 67: encore.parser.meta.v1.CacheCluster.Keyspace.key_type:type_name -> encore.parser.schema.v1.Type + 51, // 68: encore.parser.meta.v1.CacheCluster.Keyspace.value_type:type_name -> encore.parser.schema.v1.Type + 31, // 69: encore.parser.meta.v1.CacheCluster.Keyspace.path_pattern:type_name -> encore.parser.meta.v1.Path + 54, // 70: encore.parser.meta.v1.Metric.Label.type:type_name -> encore.parser.schema.v1.Builtin + 71, // [71:71] is the sub-list for method output_type + 71, // [71:71] is the sub-list for method input_type + 71, // [71:71] is the sub-list for extension type_name + 71, // [71:71] is the sub-list for extension extendee + 0, // [0:71] is the sub-list for field type_name } func init() { file_encore_parser_meta_v1_meta_proto_init() } @@ -4890,6 +4905,7 @@ func file_encore_parser_meta_v1_meta_proto_init() { (*TraceNode_MiddlewareDef)(nil), (*TraceNode_CacheKeyspace)(nil), } + file_encore_parser_meta_v1_meta_proto_msgTypes[21].OneofWrappers = []interface{}{} file_encore_parser_meta_v1_meta_proto_msgTypes[22].OneofWrappers = []interface{}{} file_encore_parser_meta_v1_meta_proto_msgTypes[23].OneofWrappers = []interface{}{} file_encore_parser_meta_v1_meta_proto_msgTypes[24].OneofWrappers = []interface{}{} diff --git a/proto/encore/parser/meta/v1/meta.proto b/proto/encore/parser/meta/v1/meta.proto index a369252e59..ad56d6120d 100644 --- a/proto/encore/parser/meta/v1/meta.proto +++ b/proto/encore/parser/meta/v1/meta.proto @@ -314,6 +314,8 @@ message PathSegment { SegmentType type = 1; string value = 2; ParamType value_type = 3; + + optional schema.v1.ValidationExpr validation = 4; } message Gateway { diff --git a/runtimes/core/src/api/endpoint.rs b/runtimes/core/src/api/endpoint.rs index e6b3db41f4..255458a5a4 100644 --- a/runtimes/core/src/api/endpoint.rs +++ b/runtimes/core/src/api/endpoint.rs @@ -340,6 +340,7 @@ pub fn endpoints_from_meta( r#type: meta::path_segment::SegmentType::Literal as i32, value_type: meta::path_segment::ParamType::String as i32, value: format!("/{}.{}", ep.ep.service_name, ep.ep.name), + validation: None, }], }), handshake, diff --git a/runtimes/core/src/api/jsonschema/meta.rs b/runtimes/core/src/api/jsonschema/meta.rs index 52b0f586fb..55fffc3c85 100644 --- a/runtimes/core/src/api/jsonschema/meta.rs +++ b/runtimes/core/src/api/jsonschema/meta.rs @@ -238,9 +238,6 @@ impl BuilderCtx<'_, '_> { #[inline] fn struct_field<'c>(&mut self, f: &'c schema::Field) -> Result<(&'c String, Field)> { - // Note: Our JS/TS support don't include the ability to change - // the JSON name from the field name, so we use the field name unconditionally. - let typ = self.typ(&f.typ)?; let value = match typ { Value::Basic(basic) => BasicOrValue::Basic(basic), diff --git a/runtimes/core/src/api/jsonschema/mod.rs b/runtimes/core/src/api/jsonschema/mod.rs index 1f8a58c6b0..f62ae06fc6 100644 --- a/runtimes/core/src/api/jsonschema/mod.rs +++ b/runtimes/core/src/api/jsonschema/mod.rs @@ -12,7 +12,7 @@ mod de; mod meta; mod parse; mod ser; -mod validation; +pub mod validation; use crate::api::jsonschema::parse::ParseWithSchema; use crate::api::APIResult; diff --git a/runtimes/core/src/api/paths.rs b/runtimes/core/src/api/paths.rs index 2a460cc5fd..ed2ca5ae30 100644 --- a/runtimes/core/src/api/paths.rs +++ b/runtimes/core/src/api/paths.rs @@ -176,6 +176,7 @@ mod tests { r#type: typ as i32, value: value.to_string(), value_type: meta::path_segment::ParamType::String as i32, + validation: None, } } diff --git a/runtimes/core/src/api/schema/encoding.rs b/runtimes/core/src/api/schema/encoding.rs index 4d92c7e16c..0e40699dd9 100644 --- a/runtimes/core/src/api/schema/encoding.rs +++ b/runtimes/core/src/api/schema/encoding.rs @@ -398,6 +398,7 @@ pub fn handshake_encoding( value: format!("{}.{}", rpc.service_name, rpc.name), r#type: SegmentType::Literal as i32, value_type: meta::path_segment::ParamType::String as i32, + validation: None, }], r#type: meta::path::Type::Url as i32, }; @@ -489,6 +490,7 @@ pub fn request_encoding( value: format!("{}.{}", rpc.service_name, rpc.name), r#type: SegmentType::Literal as i32, value_type: meta::path_segment::ParamType::String as i32, + validation: None, }], r#type: meta::path::Type::Url as i32, }; diff --git a/runtimes/core/src/api/schema/path.rs b/runtimes/core/src/api/schema/path.rs index dd7f84ea23..1d4097a92d 100644 --- a/runtimes/core/src/api/schema/path.rs +++ b/runtimes/core/src/api/schema/path.rs @@ -21,7 +21,7 @@ use crate::encore::parser::meta::v1::path_segment::ParamType; pub struct Path { /// The path segments. segments: Vec, - dynamic_segments: Vec, + dynamic_segments: Vec<(Basic, Option)>, /// The capacity to use for generating requests. capacity: usize, @@ -32,6 +32,15 @@ impl Path { let mut segments = Vec::with_capacity(path.segments.len()); for seg in &path.segments { use meta::path_segment::SegmentType; + let validation = seg + .validation + .as_ref() + .map(|v| { + jsonschema::validation::Expr::try_from(v) + .context("invalid path segment validation") + }) + .transpose()?; + match SegmentType::try_from(seg.r#type).context("invalid path segment type")? { SegmentType::Literal => { segments.push(Segment::Literal(seg.value.clone().into_boxed_str())) @@ -59,14 +68,17 @@ impl Path { segments.push(Segment::Param { name: name.clone().into_boxed_str(), typ, + validation, }); } SegmentType::Wildcard => segments.push(Segment::Wildcard { name: seg.clone().value.into_boxed_str(), + validation, }), SegmentType::Fallback => segments.push(Segment::Fallback { name: seg.clone().value.into_boxed_str(), + validation, }), } } @@ -82,14 +94,16 @@ impl Path { capacity += 1; // slash match seg { Literal(lit) => capacity += lit.len(), - Param { typ, .. } => { + Param { + typ, validation, .. + } => { capacity += 10; // assume path parameters on average are 10 characters long - dynamic_segments.push(*typ); + dynamic_segments.push((*typ, validation.clone())); } - Wildcard { .. } | Fallback { .. } => { + Wildcard { validation, .. } | Fallback { validation, .. } => { // Assume path parameters on average are 10 characters long. capacity += 10; - dynamic_segments.push(jsonschema::Basic::String); + dynamic_segments.push((jsonschema::Basic::String, validation.clone())); } } } @@ -110,12 +124,15 @@ pub enum Segment { name: Box, /// The type of the path parameter. typ: jsonschema::Basic, + validation: Option, }, Wildcard { name: Box, + validation: Option, }, Fallback { name: Box, + validation: Option, }, } @@ -129,7 +146,7 @@ impl Path { use Segment::*; match seg { Literal(lit) => path.push_str(lit), - Param { name, .. } | Wildcard { name } | Fallback { name } => { + Param { name, .. } | Wildcard { name, .. } | Fallback { name, .. } => { let Some(payload) = payload else { return Err(api::Error { code: api::ErrCode::InvalidArgument, @@ -220,7 +237,7 @@ impl Path { // For each param, find the corresponding segment and deserialize it. for (idx, (name, val)) in params.into_iter().enumerate() { - if let Some(typ) = self.dynamic_segments.get(idx) { + if let Some((typ, validation)) = self.dynamic_segments.get(idx) { // Decode it into the correct type based on the type. let val = match &typ { // For strings and any, use the value directly. @@ -270,6 +287,19 @@ impl Path { Basic::Null => PValue::Null, }; + // Validate the value, if we have a validation expression. + if let Some(validation) = validation.as_ref() { + if let Err(err) = validation.validate_pval(&val) { + return Err(api::Error { + code: api::ErrCode::InvalidArgument, + message: format!("invalid path parameter {}: {}", name, err), + internal_message: None, + stack: None, + details: None, + }); + } + } + map.insert(name, val); } } diff --git a/runtimes/go/storage/sqldb/db.go b/runtimes/go/storage/sqldb/db.go index c5875e9bf1..a8c9abcd34 100644 --- a/runtimes/go/storage/sqldb/db.go +++ b/runtimes/go/storage/sqldb/db.go @@ -326,6 +326,7 @@ func (db *Database) Begin(ctx context.Context) (*Tx, error) { // this will be made with backwards compatibility in mind, providing ample notice and // time to migrate in an opt-in fashion. func Driver[T SupportedDrivers](db *Database) T { + db.init() if db.noopDB { var zero T return zero diff --git a/tsparser/src/app/mod.rs b/tsparser/src/app/mod.rs index b917c2d56e..3dfa894a0f 100644 --- a/tsparser/src/app/mod.rs +++ b/tsparser/src/app/mod.rs @@ -1,16 +1,20 @@ -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use matchit::InsertError; use swc_common::errors::HANDLER; +use swc_common::Span; use crate::encore::parser::meta::v1; use crate::legacymeta::compute_meta; use crate::parser::parser::{ParseContext, ParseResult}; -use crate::parser::resources::apis::api::{Method, Methods}; +use crate::parser::resources::apis::api::{Endpoint, Method, Methods}; use crate::parser::resources::Resource; use crate::parser::respath::Path; +use crate::parser::types::visitor::VisitWith; +use crate::parser::types::{validation, visitor, ObjectId, ResolveState, Type, Validated}; use crate::parser::Range; -use litparser::ParseResult as PResult; +use crate::span_err::ErrReporter; +use litparser::Sp; #[derive(Debug)] pub struct AppDesc { @@ -60,9 +64,31 @@ impl Router { } } -impl AppDesc { +pub fn validate_and_describe(pc: &ParseContext, parse: ParseResult) -> Option { + AppValidator { pc, parse: &parse }.validate(); + + if pc.errs.has_errors() { + return None; + } + + match compute_meta(pc, &parse) { + Ok(meta) => Some(AppDesc { parse, meta }), + Err(err) => { + err.report(); + None + } + } +} + +struct AppValidator<'a> { + pc: &'a ParseContext, + parse: &'a ParseResult, +} + +impl AppValidator<'_> { fn validate(&self) { - self.validate_apis() + self.validate_apis(); + self.validate_pubsub() } fn validate_apis(&self) { @@ -72,17 +98,74 @@ impl AppDesc { if let Resource::APIEndpoint(endpoint) = &bind.resource { let encoding = &endpoint.encoding; router.try_add(&encoding.methods, &encoding.path, endpoint.range); + + self.validate_endpoint(endpoint); } } } } -} -pub fn validate_and_describe(pc: &ParseContext, parse: ParseResult) -> PResult { - let meta = compute_meta(pc, &parse)?; - let desc = AppDesc { parse, meta }; + fn validate_endpoint(&self, ep: &Endpoint) { + if let Some(schema) = &ep.encoding.raw_req_schema { + self.validate_validations(schema); + } + if let Some(schema) = &ep.encoding.raw_resp_schema { + self.validate_validations(schema); + } + } + + fn validate_validations(&self, schema: &Sp) { + struct Visitor<'a> { + state: &'a ResolveState, + span: Span, + seen_decls: HashSet, + } + + impl visitor::Visit for Visitor<'_> { + fn resolve_state(&self) -> &ResolveState { + self.state + } + fn seen_decls(&mut self) -> &mut HashSet { + &mut self.seen_decls + } - desc.validate(); + fn visit_validated(&mut self, node: &Validated) { + if let Err(err) = node.expr.supports_type(&node.typ) { + let s = err.to_string(); + self.span.err(&s); + } else { + // Don't recurse into the validation expression, as it would report an error + // below as if the expression was standalone. + node.typ.visit_with(self); + } + } - Ok(desc) + fn visit_validation(&mut self, node: &validation::Expr) { + HANDLER.with(|h| { + h.struct_span_err( + self.span, + &format!("unsupported standalone validation expression: {}", node), + ) + .note("validation expressions must be attached to a regular type using '&'") + .emit() + }); + } + } + + let state = self.pc.type_checker.state(); + let mut visitor = Visitor { + state, + span: schema.span(), + seen_decls: HashSet::new(), + }; + schema.visit_with(&mut visitor); + } + + fn validate_pubsub(&self) { + for res in self.parse.resources.iter() { + if let Resource::PubSubTopic(topic) = &res { + self.validate_validations(&topic.message_type); + } + } + } } diff --git a/tsparser/src/builder/parse.rs b/tsparser/src/builder/parse.rs index 95c00240dd..e65296d218 100644 --- a/tsparser/src/builder/parse.rs +++ b/tsparser/src/builder/parse.rs @@ -35,13 +35,7 @@ impl Builder<'_> { let parser = Parser::new(pc, pass1); let result = parser.parse(); - let desc = match validate_and_describe(pc, result) { - Ok(desc) => desc, - Err(err) => { - err.report(); - return None; - } - }; + let desc = validate_and_describe(pc, result)?; if pc.errs.has_errors() { None diff --git a/tsparser/src/legacymeta/mod.rs b/tsparser/src/legacymeta/mod.rs index 4e4dabe52e..032eb83c83 100644 --- a/tsparser/src/legacymeta/mod.rs +++ b/tsparser/src/legacymeta/mod.rs @@ -12,6 +12,7 @@ use crate::parser::resources::apis::{authhandler, gateway}; use crate::parser::resources::infra::cron::CronJobSchedule; use crate::parser::resources::infra::{cron, objects, pubsub_subscription, pubsub_topic, sqldb}; use crate::parser::resources::Resource; +use crate::parser::types::validation; use crate::parser::types::{Object, ObjectId}; use crate::parser::usageparser::Usage; use crate::parser::{respath, FilePath, Range}; @@ -696,8 +697,13 @@ impl respath::Path { r#type: SegmentType::Literal as i32, value_type: ParamType::String as i32, value: lit.clone(), + validation: None, }, - Segment::Param { name, value_type } => v1::PathSegment { + Segment::Param { + name, + value_type, + validation, + } => v1::PathSegment { r#type: SegmentType::Param as i32, value_type: match value_type { ValueType::String => ParamType::String as i32, @@ -705,16 +711,19 @@ impl respath::Path { ValueType::Bool => ParamType::Bool as i32, }, value: name.clone(), + validation: validation.as_ref().map(validation::Expr::to_pb), }, - Segment::Wildcard { name } => v1::PathSegment { + Segment::Wildcard { name, validation } => v1::PathSegment { r#type: SegmentType::Wildcard as i32, value_type: ParamType::String as i32, value: name.clone(), + validation: validation.as_ref().map(validation::Expr::to_pb), }, - Segment::Fallback { name } => v1::PathSegment { + Segment::Fallback { name, validation } => v1::PathSegment { r#type: SegmentType::Fallback as i32, value_type: ParamType::String as i32, value: name.clone(), + validation: validation.as_ref().map(validation::Expr::to_pb), }, }) .collect(), diff --git a/tsparser/src/legacymeta/schema.rs b/tsparser/src/legacymeta/schema.rs index bd48eb398b..fcee134e37 100644 --- a/tsparser/src/legacymeta/schema.rs +++ b/tsparser/src/legacymeta/schema.rs @@ -7,16 +7,16 @@ use itertools::Itertools; use litparser::{ParseResult, ToParseErr}; use swc_common::errors::HANDLER; -use crate::encore::parser::schema::v1 as schema; use crate::encore::parser::schema::v1::r#type as styp; +use crate::encore::parser::schema::v1::{self as schema}; use crate::legacymeta::api_schema::strip_path_params; use crate::parser::parser::ParseContext; use crate::parser::resources::apis::api::Endpoint; -use crate::parser::types::custom::{resolve_custom_type_named, CustomType}; +use crate::parser::resources::apis::encoding::resolve_wire_spec; use crate::parser::types::{ - drop_empty_or_void, Basic, EnumValue, FieldName, Generic, Interface, Literal, Named, ObjectId, - Type, + drop_empty_or_void, unwrap_validated, Basic, Custom, EnumValue, FieldName, Generic, Interface, + Literal, Named, ObjectId, Type, Union, WireLocation, }; use crate::parser::{FilePath, FileSet, Range}; @@ -87,7 +87,7 @@ impl BuilderCtx<'_, '_> { Ok(match typ { Type::Basic(tt) => self.basic(tt), Type::Array(tt) => { - let elem = self.typ(tt)?; + let elem = self.typ(&tt.0)?; schema::Type { typ: Some(styp::Typ::List(Box::new(schema::List { elem: Some(Box::new(elem)), @@ -118,9 +118,9 @@ impl BuilderCtx<'_, '_> { validation: None, }, - Type::Union(types) => schema::Type { + Type::Union(union) => schema::Type { typ: Some(styp::Typ::Union(schema::Union { - types: self.types(types)?, + types: self.types(&union.types)?, })), validation: None, }, @@ -153,7 +153,7 @@ impl BuilderCtx<'_, '_> { } } Type::Optional(_) => anyhow::bail!("optional types are not yet supported in schemas"), - Type::This => anyhow::bail!("this types are not yet supported in schemas"), + Type::This(_) => anyhow::bail!("this types are not yet supported in schemas"), Type::Generic(typ) => match typ { Generic::TypeParam(param) => { let decl_id = self @@ -183,13 +183,15 @@ impl BuilderCtx<'_, '_> { ) } - Type::Validated((typ, expr)) => { - let mut typ = self.typ(typ)?; + Type::Validated(validated) => { + let mut typ = self.typ(&validated.typ)?; // Simplify the validation expression, if possible. - let expr = expr.clone().simplify(); + let expr = validated.expr.clone().simplify(); typ.validation = Some(expr.to_pb()); typ } + + Type::Custom(Custom::WireSpec(spec)) => self.typ(&spec.underlying)?, }) } @@ -246,8 +248,6 @@ impl BuilderCtx<'_, '_> { } fn interface(&mut self, typ: &Interface) -> Result { - let ctx = self.builder.pc.type_checker.state(); - // Is this an index signature? if let Some((key, value)) = typ.index.as_ref() { if !typ.fields.is_empty() { @@ -270,32 +270,6 @@ impl BuilderCtx<'_, '_> { let (tt, had_undefined) = drop_undefined_union(&f.typ); let optional = f.optional || had_undefined; - let custom: Option = if let Type::Named(named) = tt.as_ref() { - resolve_custom_type_named(ctx, named)? - } else { - None - }; - - let (typ, wire) = match &custom { - None => (self.typ(&tt)?, None), - Some(CustomType::Header { typ, name }) => ( - self.typ(typ)?, - Some(schema::WireSpec { - location: Some(schema::wire_spec::Location::Header( - schema::wire_spec::Header { name: name.clone() }, - )), - }), - ), - Some(CustomType::Query { typ, name }) => ( - self.typ(typ)?, - Some(schema::WireSpec { - location: Some(schema::wire_spec::Location::Query( - schema::wire_spec::Query { name: name.clone() }, - )), - }), - ), - }; - let mut tags = vec![]; // Tag it as `encore:"optional"` if the field is optional. @@ -309,31 +283,57 @@ impl BuilderCtx<'_, '_> { let mut query_string_name = String::new(); - match custom { - None => {} - Some(CustomType::Header { name, .. }) => tags.push(schema::Tag { - key: "header".into(), - name: name.unwrap_or(field_name.clone()), - options: if f.optional { - vec!["optional".into()] - } else { - vec![] - }, - }), - Some(CustomType::Query { name, .. }) => { - query_string_name = name.unwrap_or(field_name.clone()); - tags.push(schema::Tag { - key: "query".into(), - name: query_string_name.clone(), - options: if f.optional { - vec!["optional".into()] - } else { - vec![] - }, - }) - } + // Resolve any wire spec overrides. + let (tt, validation_expr) = unwrap_validated(&tt); + let (mut typ, wire) = if let Some(spec) = resolve_wire_spec(tt) { + ( + self.typ(&spec.underlying)?, + Some(schema::WireSpec { + location: Some(match &spec.location { + WireLocation::Header => { + tags.push(schema::Tag { + key: "header".into(), + name: spec.name_override.clone().unwrap_or(field_name.clone()), + options: if f.optional { + vec!["optional".into()] + } else { + vec![] + }, + }); + + schema::wire_spec::Location::Header(schema::wire_spec::Header { + name: spec.name_override.clone(), + }) + } + WireLocation::Query => { + query_string_name = + spec.name_override.clone().unwrap_or(field_name.clone()); + tags.push(schema::Tag { + key: "query".into(), + name: query_string_name.clone(), + options: if f.optional { + vec!["optional".into()] + } else { + vec![] + }, + }); + + schema::wire_spec::Location::Query(schema::wire_spec::Query { + name: spec.name_override.clone(), + }) + } + }), + }), + ) + } else { + (self.typ(tt)?, None) }; + // Propagate the validation expression to the field. + if let Some(expr) = validation_expr { + typ.validation = Some(expr.to_pb()); + } + let raw_tag = tags .iter() .map(|tag| { @@ -539,16 +539,16 @@ impl BuilderCtx<'_, '_> { /// union and `true` to indicate the type included "| undefined". /// Otherwise, returns the original type and `false`. fn drop_undefined_union(typ: &Type) -> (Cow<'_, Type>, bool) { - if let Type::Union(types) = &typ { - for (i, t) in types.iter().enumerate() { + if let Type::Union(union) = &typ { + for (i, t) in union.types.iter().enumerate() { if let Type::Basic(Basic::Undefined) = &t { // If we have a union with only two types, return the other type. - return if types.len() == 2 { - (Cow::Borrowed(&types[1 - i]), true) + return if union.types.len() == 2 { + (Cow::Borrowed(&union.types[1 - i]), true) } else { - let mut types = types.clone(); + let mut types = union.types.clone(); types.swap_remove(i); - (Cow::Owned(Type::Union(types)), true) + (Cow::Owned(Type::Union(Union { types })), true) }; } } diff --git a/tsparser/src/parser/resources/apis/encoding.rs b/tsparser/src/parser/resources/apis/encoding.rs index ebe53db447..5e3178706a 100644 --- a/tsparser/src/parser/resources/apis/encoding.rs +++ b/tsparser/src/parser/resources/apis/encoding.rs @@ -6,10 +6,9 @@ use thiserror::Error; use crate::parser::resources::apis::api::{Method, Methods}; use crate::parser::respath::Path; -use crate::parser::types::custom::{resolve_custom_type_named, CustomType}; use crate::parser::types::{ - drop_empty_or_void, unwrap_promise, Basic, FieldName, Interface, InterfaceField, ResolveState, - Type, TypeChecker, + drop_empty_or_void, unwrap_promise, unwrap_validated, validation, Basic, Custom, FieldName, + Interface, InterfaceField, ResolveState, Type, TypeChecker, WireLocation, WireSpec, }; use crate::parser::Range; use crate::span_err::{ErrorWithSpanExt, SpErr}; @@ -427,7 +426,7 @@ pub struct Field { name: String, typ: Type, optional: bool, - custom: Option, + custom: Option, range: Range, } @@ -453,19 +452,11 @@ pub(crate) fn iface_fields<'a>( tc: &'a TypeChecker, typ: &'a Sp, ) -> Result> { - fn to_fields<'a>( - state: &'a ResolveState, - iface: &'a Interface, - ) -> Result> { + fn to_fields(iface: &Interface) -> Result> { let mut map = HashMap::new(); for f in &iface.fields { if let FieldName::String(name) = &f.name { - map.insert( - name.clone(), - rewrite_custom_type_field(state, f, name) - .map_err(Error::InvalidCustomType) - .map_err(|e| e.with_span(f.range.into()))?, - ); + map.insert(name.clone(), rewrite_custom_type_field(f, name)); } } Ok(map) @@ -475,7 +466,7 @@ pub(crate) fn iface_fields<'a>( let typ = unwrap_promise(tc.state(), typ); match typ { Type::Basic(Basic::Void) => Ok(HashMap::new()), - Type::Interface(iface) => to_fields(tc.state(), iface), + Type::Interface(iface) => to_fields(iface), Type::Named(named) => { let underlying = Sp::new(span, tc.underlying(named.obj.module_id, typ)); iface_fields(tc, &underlying) @@ -510,8 +501,13 @@ fn extract_loc_params(fields: &FieldMap, default_loc: ParamLocation) -> ParseRes // Determine the location. let (loc, loc_name) = match &f.custom { - Some(CustomType::Header { name, .. }) => (ParamLocation::Header, name.clone()), - Some(CustomType::Query { name, .. }) => (ParamLocation::Query, name.clone()), + Some(spec) => ( + match spec.location { + WireLocation::Header => ParamLocation::Header, + WireLocation::Query => ParamLocation::Query, + }, + spec.name_override.clone(), + ), None => (default_loc, None), }; @@ -549,42 +545,64 @@ fn rewrite_path_types(req: &RequestEncoding, path: Path, raw: bool) -> ParseResu // Get the path params into a map, keyed by name. let path_params = req .path() - .map(|param| (¶m.name, param)) + .map(|param| (param.name.as_str(), param)) .collect::>(); - let typ_to_value_type = |param: &Param| match ¶m.typ { - Type::Basic(Basic::String) => Ok(ValueType::String), - Type::Basic(Basic::Boolean) => Ok(ValueType::Bool), - Type::Basic(Basic::Number | Basic::BigInt) => Ok(ValueType::Int), - typ => Err(param - .range - .to_span() - .parse_err(format!("unsupported path parameter type: {:?}", typ))), + fn typ_to_value_type(param: &Param) -> ParseResult<(ValueType, Option)> { + // Unwrap any validation expression before we check the type. + let (typ, expr) = unwrap_validated(¶m.typ); + if let Some(expr) = &expr { + if let Err(err) = expr.supports_type(typ) { + return Err(param.range.parse_err(err.to_string())); + } + } + + match typ { + Type::Basic(Basic::String) => Ok((ValueType::String, expr.cloned())), + Type::Basic(Basic::Boolean) => Ok((ValueType::Bool, expr.cloned())), + Type::Basic(Basic::Number | Basic::BigInt) => Ok((ValueType::Int, expr.cloned())), + typ => Err(param + .range + .to_span() + .parse_err(format!("unsupported path parameter type: {:?}", typ))), + } + } + + let resolve_value_type = |span: Span, name: &str| { + match path_params.get(name) { + Some(param) => typ_to_value_type(param), + None => { + // Raw endpoints assume path params are strings. + if raw { + Ok((ValueType::String, None)) + } else { + Err(span.parse_err("path parameter not found in request schema")) + } + } + } }; let mut segments = Vec::with_capacity(path.segments.len()); for seg in path.segments.into_iter() { let (seg_span, seg) = seg.split(); let seg = match seg { + Segment::Literal(_) => seg, Segment::Param { name, .. } => { - // Get the value type of the path parameter. - let value_type = match path_params.get(&name) { - Some(param) => typ_to_value_type(param)?, - None => { - // Raw endpoints assume path params are strings. - if raw { - ValueType::String - } else { - return Err( - seg_span.parse_err("path parameter not found in request schema") - ); - } - } - }; - - Segment::Param { name, value_type } + let (value_type, validation) = resolve_value_type(seg_span, &name)?; + Segment::Param { + name, + value_type, + validation, + } + } + Segment::Wildcard { name, .. } => { + let (_, validation) = resolve_value_type(seg_span, &name)?; + Segment::Wildcard { name, validation } + } + Segment::Fallback { name, .. } => { + let (_, validation) = resolve_value_type(seg_span, &name)?; + Segment::Fallback { name, validation } } - Segment::Literal(_) | Segment::Wildcard { .. } | Segment::Fallback { .. } => seg, }; segments.push(Sp::new(seg_span, seg)); } @@ -595,39 +613,27 @@ fn rewrite_path_types(req: &RequestEncoding, path: Path, raw: bool) -> ParseResu }) } -fn rewrite_custom_type_field( - ctx: &ResolveState, - field: &InterfaceField, - field_name: &str, -) -> anyhow::Result { - let standard_field = Field { +pub fn resolve_wire_spec(typ: &Type) -> Option<&WireSpec> { + match typ { + Type::Custom(Custom::WireSpec(spec)) => Some(spec), + Type::Validated(v) => resolve_wire_spec(&v.typ), + Type::Optional(opt) => resolve_wire_spec(&opt.0), + _ => None, + } +} + +fn rewrite_custom_type_field(field: &InterfaceField, field_name: &str) -> Field { + let mut standard_field = Field { name: field_name.to_string(), typ: field.typ.clone(), optional: field.optional, custom: None, range: field.range, }; - let Type::Named(named) = &field.typ else { - return Ok(standard_field); + + if let Some(spec) = resolve_wire_spec(&field.typ) { + standard_field.custom = Some(spec.clone()); }; - Ok(match resolve_custom_type_named(ctx, named)? { - None => standard_field, - Some(CustomType::Header { typ, name }) => Field { - custom: Some(CustomType::Query { - typ: typ.clone(), - name, - }), - typ, - ..standard_field - }, - Some(CustomType::Query { typ, name }) => Field { - custom: Some(CustomType::Query { - typ: typ.clone(), - name, - }), - typ, - ..standard_field - }, - }) + standard_field } diff --git a/tsparser/src/parser/respath.rs b/tsparser/src/parser/respath.rs index 99cd9a278d..a72a6b415a 100644 --- a/tsparser/src/parser/respath.rs +++ b/tsparser/src/parser/respath.rs @@ -5,6 +5,8 @@ use swc_common::{BytePos, Span}; use crate::span_err::{ErrorWithSpanExt, SpErr}; +use super::types::validation; + #[derive(Debug, Clone)] pub struct Path { pub span: Span, @@ -39,9 +41,19 @@ impl std::fmt::Display for Path { #[derive(Debug, Clone, PartialEq)] pub enum Segment { Literal(String), - Param { name: String, value_type: ValueType }, - Wildcard { name: String }, - Fallback { name: String }, + Param { + name: String, + value_type: ValueType, + validation: Option, + }, + Wildcard { + name: String, + validation: Option, + }, + Fallback { + name: String, + validation: Option, + }, } impl Segment { @@ -58,8 +70,8 @@ impl Segment { match self { Segment::Literal(s) => s, Segment::Param { name, .. } => name, - Segment::Wildcard { name } => name, - Segment::Fallback { name } => name, + Segment::Wildcard { name, .. } => name, + Segment::Fallback { name, .. } => name, } } @@ -163,12 +175,15 @@ impl Path { Some(':') => Segment::Param { name: val[1..].to_string(), value_type: ValueType::String, + validation: None, }, Some('*') if opts.allow_wildcard => Segment::Wildcard { name: val[1..].to_string(), + validation: None, }, Some('!') if opts.allow_wildcard => Segment::Fallback { name: val[1..].to_string(), + validation: None, }, _ => Segment::Literal(val.to_string()), }; @@ -190,7 +205,7 @@ impl Path { Segment::Param { name, .. } if name.is_empty() => { return Err(PathParseError::UnnamedParam.with_span(seg.span())); } - Segment::Wildcard { name } if name.is_empty() => { + Segment::Wildcard { name, .. } if name.is_empty() => { return Err(PathParseError::UnnamedParam.with_span(seg.span())); } Segment::Wildcard { .. } if idx != segments.len() - 1 => { @@ -268,9 +283,11 @@ mod tests { Segment::Param { name: "foo".to_string(), value_type: ValueType::String, + validation: None, }, Segment::Wildcard { name: "bar".to_string(), + validation: None, }, ]), ), @@ -288,6 +305,7 @@ mod tests { Segment::Literal("foo".to_string()), Segment::Fallback { name: "fallback".to_string(), + validation: None, }, ]), ), diff --git a/tsparser/src/parser/types/custom.rs b/tsparser/src/parser/types/custom.rs deleted file mode 100644 index f3d8c7d2ad..0000000000 --- a/tsparser/src/parser/types/custom.rs +++ /dev/null @@ -1,73 +0,0 @@ -use anyhow::Result; - -use crate::parser::types::{typ, Basic, Literal, ResolveState, Type}; - -pub enum CustomType { - Header { name: Option, typ: Type }, - Query { name: Option, typ: Type }, -} - -pub fn resolve_custom_type_named( - ctx: &ResolveState, - named: &typ::Named, -) -> Result> { - if !ctx.is_module_path(named.obj.module_id, "encore.dev/api") { - return Ok(None); - } - - match &named.obj.name.as_deref() { - Some("Header") => resolve_header_type(named).map(Some), - Some("Query") => resolve_query_type(named).map(Some), - _ => Ok(None), - } -} - -fn resolve_header_type(named: &typ::Named) -> Result { - let (typ, name) = match (named.type_arguments.first(), named.type_arguments.get(1)) { - (None, None) => (Type::Basic(Basic::String), None), - - (Some(first), None) => { - // If we only have a single argument, check its type. - // If it's a string literal it's the name, otherwise it's the type. - match first { - Type::Literal(Literal::String(lit)) => { - (Type::Basic(Basic::String), Some(lit.to_string())) - } - _ => (first.clone(), None), - } - } - - (Some(typ), Some(name)) => (typ.clone(), Some(resolve_str_lit(name)?)), - (None, Some(_)) => unreachable!(), - }; - Ok(CustomType::Header { typ, name }) -} - -fn resolve_query_type(named: &typ::Named) -> Result { - let (typ, name) = match (named.type_arguments.first(), named.type_arguments.get(1)) { - (None, None) => (Type::Basic(Basic::String), None), - - (Some(first), None) => { - // If we only have a single argument, check its type. - // If it's a string literal it's the name, otherwise it's the type. - match first { - Type::Literal(Literal::String(lit)) => { - (Type::Basic(Basic::String), Some(lit.to_string())) - } - _ => (first.clone(), None), - } - } - - (Some(typ), Some(name)) => (typ.clone(), Some(resolve_str_lit(name)?)), - (None, Some(_)) => unreachable!(), - }; - - Ok(CustomType::Query { typ, name }) -} - -fn resolve_str_lit(typ: &Type) -> Result { - match typ { - Type::Literal(Literal::String(s)) => Ok(s.clone()), - _ => anyhow::bail!("expected string literal"), - } -} diff --git a/tsparser/src/parser/types/mod.rs b/tsparser/src/parser/types/mod.rs index d67805195c..8632de1bed 100644 --- a/tsparser/src/parser/types/mod.rs +++ b/tsparser/src/parser/types/mod.rs @@ -1,10 +1,11 @@ mod ast_id; mod binding; -pub mod custom; mod object; mod typ; mod type_resolve; +mod type_string; mod utils; +pub mod visitor; mod resolved; #[cfg(test)] @@ -12,9 +13,6 @@ mod tests; pub mod validation; pub use object::{Object, ObjectId, ObjectKind, ResolveState}; -pub use typ::{ - Basic, ClassType, EnumMember, EnumType, EnumValue, FieldName, Generic, Interface, - InterfaceField, Literal, Named, Type, TypeArgId, -}; +pub use typ::*; pub use type_resolve::TypeChecker; pub use utils::*; diff --git a/tsparser/src/parser/types/snapshots/encore_tsparser__parser__types__tests__resolve_types@basic.ts.snap b/tsparser/src/parser/types/snapshots/encore_tsparser__parser__types__tests__resolve_types@basic.ts.snap index 41848f0739..d0c7858be6 100644 --- a/tsparser/src/parser/types/snapshots/encore_tsparser__parser__types__tests__resolve_types@basic.ts.snap +++ b/tsparser/src/parser/types/snapshots/encore_tsparser__parser__types__tests__resolve_types@basic.ts.snap @@ -40,18 +40,20 @@ input_file: tsparser/src/parser/types/testdata/basic.ts }, ), "Exclude1": Union( - [ - Literal( - String( - "bar", + Union { + types: [ + Literal( + String( + "bar", + ), ), - ), - Literal( - String( - "optional", + Literal( + String( + "optional", + ), ), - ), - ], + ], + }, ), "Pick1": Interface( Interface { @@ -183,14 +185,16 @@ input_file: tsparser/src/parser/types/testdata/basic.ts String, ), Union( - [ - Basic( - Boolean, - ), - Basic( - Number, - ), - ], + Union { + types: [ + Basic( + Boolean, + ), + Basic( + Number, + ), + ], + }, ), ), ), @@ -356,37 +360,39 @@ input_file: tsparser/src/parser/types/testdata/basic.ts }, ), "EnumFields": Union( - [ - Literal( - String( - "A", + Union { + types: [ + Literal( + String( + "A", + ), ), - ), - Literal( - String( - "B", + Literal( + String( + "B", + ), ), - ), - Literal( - String( - "C", + Literal( + String( + "C", + ), ), - ), - Literal( - String( - "D", + Literal( + String( + "D", + ), ), - ), - Literal( - String( - "E", + Literal( + String( + "E", + ), ), - ), - Literal( - String( - "F", + Literal( + String( + "F", + ), ), - ), - ], + ], + }, ), } diff --git a/tsparser/src/parser/types/snapshots/encore_tsparser__parser__types__tests__resolve_types@extends.ts.snap b/tsparser/src/parser/types/snapshots/encore_tsparser__parser__types__tests__resolve_types@extends.ts.snap index 0c071df39f..8fda36b7ac 100644 --- a/tsparser/src/parser/types/snapshots/encore_tsparser__parser__types__tests__resolve_types@extends.ts.snap +++ b/tsparser/src/parser/types/snapshots/encore_tsparser__parser__types__tests__resolve_types@extends.ts.snap @@ -13,17 +13,19 @@ input_file: tsparser/src/parser/types/testdata/extends.ts ), optional: false, typ: Union( - [ - Basic( - String, - ), - Basic( - Number, - ), - Basic( - Null, - ), - ], + Union { + types: [ + Basic( + String, + ), + Basic( + Number, + ), + Basic( + Null, + ), + ], + }, ), }, InterfaceField { @@ -58,14 +60,16 @@ input_file: tsparser/src/parser/types/testdata/extends.ts ), optional: false, typ: Union( - [ - Basic( - String, - ), - Basic( - Null, - ), - ], + Union { + types: [ + Basic( + String, + ), + Basic( + Null, + ), + ], + }, ), }, InterfaceField { @@ -109,19 +113,21 @@ input_file: tsparser/src/parser/types/testdata/extends.ts ), optional: false, typ: Union( - [ - Generic( - TypeParam( - TypeParam { - idx: 0, - constraint: None, - }, + Union { + types: [ + Generic( + TypeParam( + TypeParam { + idx: 0, + constraint: None, + }, + ), + ), + Basic( + Null, ), - ), - Basic( - Null, - ), - ], + ], + }, ), }, ], @@ -147,21 +153,25 @@ input_file: tsparser/src/parser/types/testdata/extends.ts ), optional: false, typ: Union( - [ - Union( - [ - Basic( - String, - ), - Basic( - Number, - ), - ], - ), - Basic( - Null, - ), - ], + Union { + types: [ + Union( + Union { + types: [ + Basic( + String, + ), + Basic( + Number, + ), + ], + }, + ), + Basic( + Null, + ), + ], + }, ), }, ], @@ -178,16 +188,18 @@ input_file: tsparser/src/parser/types/testdata/extends.ts ), optional: false, typ: Union( - [ - Literal( - Number( - 5.0, + Union { + types: [ + Literal( + Number( + 5.0, + ), + ), + Basic( + Null, ), - ), - Basic( - Null, - ), - ], + ], + }, ), }, ], diff --git a/tsparser/src/parser/types/snapshots/encore_tsparser__parser__types__tests__resolve_types@infer.txt.snap b/tsparser/src/parser/types/snapshots/encore_tsparser__parser__types__tests__resolve_types@infer.txt.snap index d80036cebc..068b868385 100644 --- a/tsparser/src/parser/types/snapshots/encore_tsparser__parser__types__tests__resolve_types@infer.txt.snap +++ b/tsparser/src/parser/types/snapshots/encore_tsparser__parser__types__tests__resolve_types@infer.txt.snap @@ -25,7 +25,9 @@ input_file: tsparser/src/parser/types/testdata/infer.txt optional: false, typ: Generic( Inferred( - 0, + Inferred( + 0, + ), ), ), }, @@ -36,7 +38,9 @@ input_file: tsparser/src/parser/types/testdata/infer.txt ), true_type: Generic( Inferred( - 0, + Inferred( + 0, + ), ), ), false_type: Basic( diff --git a/tsparser/src/parser/types/snapshots/encore_tsparser__parser__types__tests__resolve_types@validation.ts.snap b/tsparser/src/parser/types/snapshots/encore_tsparser__parser__types__tests__resolve_types@validation.ts.snap index 676821f940..d3bf6356f8 100644 --- a/tsparser/src/parser/types/snapshots/encore_tsparser__parser__types__tests__resolve_types@validation.ts.snap +++ b/tsparser/src/parser/types/snapshots/encore_tsparser__parser__types__tests__resolve_types@validation.ts.snap @@ -13,11 +13,11 @@ input_file: tsparser/src/parser/types/testdata/validation.ts ), optional: false, typ: Validated( - ( - Basic( + Validated { + typ: Basic( Number, ), - And( + expr: And( [ Rule( MinVal( @@ -35,7 +35,7 @@ input_file: tsparser/src/parser/types/testdata/validation.ts ), ], ), - ), + }, ), }, InterfaceField { @@ -44,11 +44,11 @@ input_file: tsparser/src/parser/types/testdata/validation.ts ), optional: false, typ: Validated( - ( - Basic( + Validated { + typ: Basic( String, ), - And( + expr: And( [ Rule( MinLen( @@ -62,7 +62,7 @@ input_file: tsparser/src/parser/types/testdata/validation.ts ), ], ), - ), + }, ), }, InterfaceField { @@ -71,11 +71,11 @@ input_file: tsparser/src/parser/types/testdata/validation.ts ), optional: false, typ: Validated( - ( - Basic( + Validated { + typ: Basic( String, ), - Or( + expr: Or( [ Rule( Is( @@ -89,7 +89,7 @@ input_file: tsparser/src/parser/types/testdata/validation.ts ), ], ), - ), + }, ), }, InterfaceField { @@ -98,27 +98,29 @@ input_file: tsparser/src/parser/types/testdata/validation.ts ), optional: false, typ: Validated( - ( - Array( - Validated( - ( - Basic( - String, - ), - Rule( - Is( - Email, + Validated { + typ: Array( + Array( + Validated( + Validated { + typ: Basic( + String, ), - ), + expr: Rule( + Is( + Email, + ), + ), + }, ), ), ), - Rule( + expr: Rule( MaxLen( 10, ), ), - ), + }, ), }, ], diff --git a/tsparser/src/parser/types/snapshots/encore_tsparser__parser__types__tests__resolve_types@wirespec.ts.snap b/tsparser/src/parser/types/snapshots/encore_tsparser__parser__types__tests__resolve_types@wirespec.ts.snap new file mode 100644 index 0000000000..8bdf358415 --- /dev/null +++ b/tsparser/src/parser/types/snapshots/encore_tsparser__parser__types__tests__resolve_types@wirespec.ts.snap @@ -0,0 +1,114 @@ +--- +source: tsparser/src/parser/types/tests.rs +expression: result +input_file: tsparser/src/parser/types/testdata/wirespec.ts +--- +{ + "Foo": Interface( + Interface { + fields: [ + InterfaceField { + name: String( + "a", + ), + optional: false, + typ: Custom( + WireSpec( + WireSpec { + location: Query, + underlying: Basic( + Number, + ), + name_override: None, + }, + ), + ), + }, + InterfaceField { + name: String( + "b", + ), + optional: false, + typ: Validated( + Validated { + typ: Custom( + WireSpec( + WireSpec { + location: Query, + underlying: Basic( + Number, + ), + name_override: None, + }, + ), + ), + expr: Rule( + MaxVal( + N( + 10.0, + ), + ), + ), + }, + ), + }, + InterfaceField { + name: String( + "c", + ), + optional: false, + typ: Validated( + Validated { + typ: Custom( + WireSpec( + WireSpec { + location: Query, + underlying: Basic( + String, + ), + name_override: None, + }, + ), + ), + expr: Rule( + MinLen( + 3, + ), + ), + }, + ), + }, + InterfaceField { + name: String( + "d", + ), + optional: false, + typ: Validated( + Validated { + typ: Custom( + WireSpec( + WireSpec { + location: Header, + underlying: Basic( + String, + ), + name_override: Some( + "X-Header", + ), + }, + ), + ), + expr: Rule( + MinLen( + 3, + ), + ), + }, + ), + }, + ], + index: None, + call: None, + }, + ), +} diff --git a/tsparser/src/parser/types/testdata/basic.ts b/tsparser/src/parser/types/testdata/basic.ts index 5064698cd3..9d39f109aa 100644 --- a/tsparser/src/parser/types/testdata/basic.ts +++ b/tsparser/src/parser/types/testdata/basic.ts @@ -1,8 +1,8 @@ // Basic interface export interface Interface { - foo: string; - bar: number; - optional?: boolean; + foo: string; + bar: number; + optional?: boolean; } // Utility types @@ -15,22 +15,22 @@ export type Omit2 = Omit; export type Partial1 = Partial; // Index signatures -export type Index = { [key: string]: boolean | number}; +export type Index = { [key: string]: boolean | number }; // Intersections -export type Intersect1 = {foo: string} & {bar: number}; -export type Intersect2 = {foo: string} & {foo: "literal"}; -export type Intersect3 = {foo: string} & {foo: number}; -export type Intersect4 = {foo?: "optional"} & {foo: string}; -export type Intersect5 = {a: string; b: string; c: string} & {a: any; b: unknown; c: never}; +export type Intersect1 = { foo: string } & { bar: number }; +export type Intersect2 = { foo: string } & { foo: "literal" }; +export type Intersect3 = { foo: string } & { foo: number }; +export type Intersect4 = { foo?: "optional" } & { foo: string }; +export type Intersect5 = { a: string; b: string; c: string } & { a: any; b: unknown; c: never }; // Enums export enum Enum1 { - A, - B, - C, - D = "foo", - E = 5, - F, + A, + B, + C, + D = "foo", + E = 5, + F, } export type EnumFields = keyof typeof Enum1; diff --git a/tsparser/src/parser/types/testdata/wirespec.ts b/tsparser/src/parser/types/testdata/wirespec.ts new file mode 100644 index 0000000000..530bc36ec8 --- /dev/null +++ b/tsparser/src/parser/types/testdata/wirespec.ts @@ -0,0 +1,9 @@ +import { Query, Header } from "encore.dev/api"; +import { MinLen, Max } from "encore.dev/validate"; + +export interface Foo { + a: Query; + b: Query & Max<10>; + c: Query & MinLen<3>; + d: Header<"X-Header"> & MinLen<3>; +} diff --git a/tsparser/src/parser/types/typ.rs b/tsparser/src/parser/types/typ.rs index 9be1507ad5..068415459b 100644 --- a/tsparser/src/parser/types/typ.rs +++ b/tsparser/src/parser/types/typ.rs @@ -6,7 +6,7 @@ use itertools::Itertools; use serde::Serialize; use std::borrow::Cow; use std::collections::HashMap; -use std::fmt::Debug; +use std::fmt::{Debug, Display}; use std::hash::{Hash, Hasher}; use std::rc::Rc; use swc_common::errors::HANDLER; @@ -19,13 +19,13 @@ pub enum Type { /// strings, etc Basic(Basic), /// T[], Array - Array(Box), + Array(Array), /// { foo: string } Interface(Interface), /// a | b | c - Union(Vec), + Union(Union), /// [string, number] - Tuple(Vec), + Tuple(Tuple), /// "foo" Literal(Literal), /// class Foo {} @@ -37,10 +37,10 @@ pub enum Type { Named(Named), /// e.g. "string?" in tuples - Optional(Box), + Optional(Optional), /// "this", see https://www.typescriptlang.org/docs/handbook/advanced-types.html#polymorphic-this-types - This, + This(This), Generic(Generic), @@ -48,7 +48,72 @@ pub enum Type { Validation(validation::Expr), /// A type with validation applied to it. - Validated((Box, validation::Expr)), + Validated(Validated), + + // A custom type of some kind. + Custom(Custom), +} + +#[derive(Debug, Clone, Hash, Serialize)] +pub struct Array(pub Box); + +#[derive(Debug, Clone, Hash, Serialize)] +pub struct Optional(pub Box); + +#[derive(Debug, Clone, Hash, Serialize)] +pub struct Tuple { + pub types: Vec, +} + +#[derive(Debug, Clone, Hash, Serialize)] +pub struct Union { + pub types: Vec, +} + +#[derive(Debug, Clone, Hash, Serialize)] +pub struct Validated { + pub typ: Box, + pub expr: validation::Expr, +} + +#[derive(Debug, Clone, Serialize, Hash)] +pub enum Custom { + /// A specification of how the type should be encoded on the wire. + WireSpec(WireSpec), +} + +impl Custom { + pub fn identical(&self, other: &Custom) -> bool { + match (self, other) { + (Custom::WireSpec(a), Custom::WireSpec(b)) => a.identical(b), + } + } +} + +#[derive(Debug, Clone, Serialize, Hash)] +pub struct WireSpec { + /// What location in the request should be used for this field. + pub location: WireLocation, + + /// The underlying type this is. + pub underlying: Box, + + /// Whether this specifies a name override. + pub name_override: Option, +} + +#[derive(Debug, Clone, Serialize, Hash, PartialEq, Eq)] +pub enum WireLocation { + Query, + Header, +} + +impl WireSpec { + pub fn identical(&self, other: &WireSpec) -> bool { + self.location == other.location + && self.name_override == other.name_override + && self.underlying.identical(&other.underlying) + } } impl Type { @@ -61,17 +126,22 @@ impl Type { pub fn identical(&self, other: &Type) -> bool { match (self, other) { (Type::Basic(a), Type::Basic(b)) => a == b, - (Type::Array(a), Type::Array(b)) => a.identical(b), + (Type::Array(a), Type::Array(b)) => a.0.identical(&b.0), (Type::Interface(a), Type::Interface(b)) => a.identical(b), - (Type::Union(a), Type::Union(b)) => a.iter().zip(b).all(|(a, b)| a.identical(b)), - (Type::Tuple(a), Type::Tuple(b)) => a.iter().zip(b).all(|(a, b)| a.identical(b)), + (Type::Union(a), Type::Union(b)) => { + a.types.iter().zip(&b.types).all(|(a, b)| a.identical(b)) + } + (Type::Tuple(a), Type::Tuple(b)) => { + a.types.iter().zip(&b.types).all(|(a, b)| a.identical(b)) + } (Type::Literal(a), Type::Literal(b)) => a == b, (Type::Class(a), Type::Class(b)) => a.identical(b), (Type::Named(a), Type::Named(b)) => a.identical(b), - (Type::Optional(a), Type::Optional(b)) => a.identical(b), - (Type::This, Type::This) => true, + (Type::Optional(a), Type::Optional(b)) => a.0.identical(&b.0), + (Type::This(This), Type::This(This)) => true, (Type::Generic(a), Type::Generic(b)) => a.identical(b), (Type::Enum(a), Type::Enum(b)) => a.identical(b), + (Type::Custom(a), Type::Custom(b)) => a.identical(b), _ => false, } } @@ -97,9 +167,12 @@ impl Type { (Type::Validation(a), Type::Validation(b)) => { Some(Type::Validation(a.clone().or(b.clone()))) } - (Type::Validated((typ, a)), Type::Validation(b)) - | (Type::Validation(a), Type::Validated((typ, b))) => { - Some(Type::Validated((typ.to_owned(), a.clone().or(b.clone())))) + (Type::Validated(validated), Type::Validation(expr)) + | (Type::Validation(expr), Type::Validated(validated)) => { + Some(Type::Validated(Validated { + typ: validated.typ.to_owned(), + expr: validated.expr.clone().or(expr.clone()), + })) } // TODO more rules? @@ -115,7 +188,9 @@ impl Type { pub(super) fn simplify_or_union(self, other: Type) -> Type { match self.union_merge(&other) { Some(typ) => typ, - None => Type::Union(vec![self, other]), + None => Type::Union(Union { + types: vec![self, other], + }), } } } @@ -137,6 +212,27 @@ pub enum Basic { Never, } +impl Display for Basic { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let s: &'static str = match self { + Basic::Any => "any", + Basic::String => "string", + Basic::Boolean => "boolean", + Basic::Number => "number", + Basic::Object => "object", + Basic::BigInt => "bigint", + Basic::Date => "Date", + Basic::Symbol => "symbol", + Basic::Undefined => "undefined", + Basic::Null => "null", + Basic::Void => "void", + Basic::Unknown => "unknown", + Basic::Never => "never", + }; + f.write_str(s) + } +} + #[derive(Debug, Clone, Serialize)] pub enum Literal { String(String), @@ -389,15 +485,15 @@ pub enum Generic { TypeParam(TypeParam), /// An index lookup, like `T[U]`, where at least one of the types is a generic. - Index((Box, Box)), + Index(Index), /// A mapped type. Mapped(Mapped), /// A reference to the 'key' type when evaluating a mapped type. - MappedKeyType, + MappedKeyType(MappedKeyType), - Keyof(Box), + Keyof(Keyof), Conditional(Conditional), // A reference to the 'as' type when evaluating a mapped type. // MappedAsType, @@ -407,9 +503,29 @@ pub enum Generic { /// A reference to an inferred type parameter, /// referencing its index in infer_type_params. - Inferred(usize), + Inferred(Inferred), +} + +#[derive(Debug, Clone, Hash, Serialize)] +pub struct Index { + pub source: Box, + pub index: Box, } +/// A reference to an inferred type parameter, +/// referencing its index in infer_type_params. +#[derive(Debug, Clone, Hash, Serialize)] +pub struct Inferred(pub usize); + +#[derive(Debug, Clone, Hash, Serialize)] +pub struct Keyof(pub Box); + +#[derive(Debug, Clone, Hash, Serialize)] +pub struct This; + +#[derive(Debug, Clone, Hash, Serialize)] +pub struct MappedKeyType; + #[derive(Debug, Clone, Hash, Serialize)] pub struct TypeParam { // The index of the type parameter in the current scope. @@ -488,24 +604,26 @@ impl Generic { impl Type { pub fn iter_unions<'a>(&'a self) -> Box + 'a> { match self { - Type::Union(types) => Box::new(types.iter().flat_map(|t| t.iter_unions())), + Type::Union(union) => Box::new(union.types.iter().flat_map(|t| t.iter_unions())), Type::Optional(tt) => Box::new( - tt.iter_unions() + tt.0.iter_unions() .chain(std::iter::once(&Type::Basic(Basic::Undefined))), ), - Type::Validated((inner, _)) => inner.iter_unions(), + Type::Validated(v) => v.typ.iter_unions(), _ => Box::new(std::iter::once(self)), } } pub fn into_iter_unions(self) -> Box> { match self { - Type::Union(types) => Box::new(types.into_iter().flat_map(|t| t.into_iter_unions())), + Type::Union(union) => { + Box::new(union.types.into_iter().flat_map(|t| t.into_iter_unions())) + } Type::Optional(tt) => Box::new( - tt.into_iter_unions() + tt.0.into_iter_unions() .chain(std::iter::once(Type::Basic(Basic::Undefined))), ), - Type::Validated((inner, _)) => inner.into_iter_unions(), + Type::Validated(v) => v.typ.into_iter_unions(), _ => Box::new(std::iter::once(self)), } } @@ -540,26 +658,24 @@ impl Type { | (Literal::BigInt(_), Basic::BigInt) )), - (Type::Validated((inner, _)), _) | (_, Type::Validated((inner, _))) => { - inner.assignable(state, other) - } + (Type::Validated(v), _) | (_, Type::Validated(v)) => v.typ.assignable(state, other), (this, Type::Optional(other)) => { if matches!(this, Type::Basic(Basic::Undefined)) { Some(true) } else { - this.assignable(state, other) + this.assignable(state, &other.0) } } (Type::Tuple(this), other) => match other { Type::Tuple(other) => { - if this.len() != other.len() { + if this.types.len() != other.types.len() { return Some(false); } let mut found_none = false; - for (this, other) in this.iter().zip(other) { + for (this, other) in this.types.iter().zip(&other.types) { match this.assignable(state, other) { Some(true) => {} Some(false) => return Some(false), @@ -575,8 +691,8 @@ impl Type { Type::Array(other) => { // Ensure every element in `this` is a subtype of `other`. - for this in this { - match this.assignable(state, other) { + for this in &this.types { + match this.assignable(state, &other.0) { Some(true) => {} Some(false) => return Some(false), None => return None, @@ -662,7 +778,7 @@ impl Type { // Is every element in `this` assignable to `other`? 'ThisLoop: for t in this.iter_unions() { let mut found_none = false; - for o in other { + for o in &other.types { match t.assignable(state, o) { // Found a match; check the next element in `this`. Some(true) => continue 'ThisLoop, @@ -721,7 +837,9 @@ impl Type { } match (self, other) { - (this, Type::Generic(Generic::Inferred(idx))) => Yes(vec![(*idx, Cow::Borrowed(this))]), + (this, Type::Generic(Generic::Inferred(inferred))) => { + Yes(vec![(inferred.0, Cow::Borrowed(this))]) + } (_, Type::Basic(Basic::Any)) => Yes(vec![]), (_, Type::Basic(Basic::Never)) => No, @@ -747,27 +865,27 @@ impl Type { | (Literal::BigInt(_), Basic::BigInt) )), - (Type::Validated((inner, _)), _) | (_, Type::Validated((inner, _))) => { - inner.extends(state, other).into_static() + (Type::Validated(v), _) | (_, Type::Validated(v)) => { + v.typ.extends(state, other).into_static() } (this, Type::Optional(other)) => { if matches!(this, Type::Basic(Basic::Undefined)) { Yes(vec![]) } else { - this.extends(state, other) + this.extends(state, &other.0) } } (Type::Tuple(this), other) => match other { Type::Tuple(other) => { - if this.len() != other.len() { + if this.types.len() != other.types.len() { return No; } let mut found_unknown = false; let mut inferred = vec![]; - for (this, other) in this.iter().zip(other) { + for (this, other) in this.types.iter().zip(&other.types) { match this.extends(state, other) { Yes(inf) => { inferred.extend(inf); @@ -786,8 +904,8 @@ impl Type { Type::Array(other) => { // Ensure every element in `this` is a subtype of `other`. let mut inferred = vec![]; - for this in this { - match this.extends(state, other) { + for this in &this.types { + match this.extends(state, &other.0) { Yes(infer) => inferred.extend(infer), No => return No, Unknown => return Unknown, @@ -893,7 +1011,7 @@ impl Type { let mut found_unknown = false; let mut inferred = Vec::new(); for t in this.iter_unions() { - for o in other { + for o in &other.types { match t.extends(state, o) { Yes(inf) => { found_yes = true; @@ -950,7 +1068,7 @@ pub fn simplify_union(types: Vec) -> Type { match results.len() { 0 => Type::Basic(Basic::Never), 1 => results.remove(0), - _ => Type::Union(results), + _ => Type::Union(Union { types: results }), } } @@ -1018,9 +1136,9 @@ pub fn intersect<'a: 'b, 'b>( // Intersection distributes into unions. (Type::Union(a), Type::Union(b)) => { - let mut types = Vec::with_capacity(a.len() * b.len()); - for typ in a { - for other in b.iter() { + let mut types = Vec::with_capacity(a.types.len() * b.types.len()); + for typ in &a.types { + for other in &b.types { match intersect(ctx, Cow::Borrowed(typ), Cow::Borrowed(other)).into_owned() { Type::Basic(Basic::Never) => {} other => types.push(other), @@ -1048,67 +1166,64 @@ pub fn intersect<'a: 'b, 'b>( } } - (Type::Array(x), Type::Array(y)) => Cow::Owned(Type::Array(Box::new( - intersect(ctx, Cow::Borrowed(x.as_ref()), Cow::Borrowed(y.as_ref())).into_owned(), - ))), + (Type::Array(x), Type::Array(y)) => Cow::Owned(Type::Array(Array(Box::new( + intersect( + ctx, + Cow::Borrowed(x.0.as_ref()), + Cow::Borrowed(y.0.as_ref()), + ) + .into_owned(), + )))), (Type::Array(x), Type::Tuple(y)) | (Type::Tuple(y), Type::Array(x)) => { - Cow::Owned(Type::Array(Box::new(if y.is_empty() { + Cow::Owned(Type::Array(Array(Box::new(if y.types.is_empty() { Type::Basic(Basic::Never) } else { // Inspect the first element of the tuple for intersection. // It's not completely correct but close enough for now. - intersect(ctx, Cow::Borrowed(x.as_ref()), Cow::Borrowed(&y[0])).into_owned() - }))) + intersect(ctx, Cow::Borrowed(x.0.as_ref()), Cow::Borrowed(&y.types[0])).into_owned() + })))) } (Type::Tuple(x), Type::Tuple(y)) => { - let mut types = Vec::with_capacity(x.len().min(y.len())); - for (a, b) in x.iter().zip(y.iter()) { + let mut types = Vec::with_capacity(x.types.len().min(y.types.len())); + for (a, b) in x.types.iter().zip(y.types.iter()) { types.push(intersect(ctx, Cow::Borrowed(a), Cow::Borrowed(b)).into_owned()); } - Cow::Owned(Type::Tuple(types)) + Cow::Owned(Type::Tuple(Tuple { types })) } - (Type::Optional(x), Type::Optional(y)) => Cow::Owned(Type::Optional(Box::new( - intersect(ctx, Cow::Borrowed(x), Cow::Borrowed(y)).into_owned(), - ))), + (Type::Optional(x), Type::Optional(y)) => Cow::Owned(Type::Optional(Optional(Box::new( + intersect(ctx, Cow::Borrowed(&x.0), Cow::Borrowed(&y.0)).into_owned(), + )))), // Treat optional as "T | undefined". (Type::Optional(x), y) | (y, Type::Optional(x)) => { - union_with(Cow::Borrowed(x), Cow::Borrowed(y)) + union_with(Cow::Borrowed(&x.0), Cow::Borrowed(y)) } - (Type::This, Type::This) => Cow::Owned(Type::This), + (Type::This(This), Type::This(This)) => Cow::Owned(Type::This(This)), // Combine validation expressions into a validated type. - (Type::Validated(_), Type::Validation(_)) => { - let (Type::Validated((typ, a)), Type::Validation(b)) = (a.into_owned(), b.into_owned()) - else { - unreachable!() - }; - Cow::Owned(Type::Validated((typ, a.and(b)))) - } - (Type::Validation(_), Type::Validated(_)) => { - let (Type::Validated((typ, a)), Type::Validation(b)) = (b.into_owned(), a.into_owned()) - else { - unreachable!() - }; - Cow::Owned(Type::Validated((typ, a.and(b)))) - } + (Type::Validated(a), Type::Validation(b)) => Cow::Owned(Type::Validated(Validated { + typ: a.typ.clone(), + expr: a.expr.clone().and(b.clone()), + })), + (Type::Validation(a), Type::Validated(b)) => Cow::Owned(Type::Validated(Validated { + typ: b.typ.clone(), + expr: a.clone().and(b.expr.clone()), + })), // Merge validation expressions together. - (Type::Validation(_), Type::Validation(_)) => Cow::Owned(Type::Validation({ - let (Type::Validation(a), Type::Validation(b)) = (a.into_owned(), b.into_owned()) - else { - unreachable!() - }; - a.and(b) - })), - (_, Type::Validation(expr)) => { - Cow::Owned(Type::Validated((Box::new(a.into_owned()), expr.clone()))) - } - (Type::Validation(expr), _) => { - Cow::Owned(Type::Validated((Box::new(b.into_owned()), expr.clone()))) + (Type::Validation(a), Type::Validation(b)) => { + Cow::Owned(Type::Validation(a.clone().and(b.clone()))) } + (_, Type::Validation(expr)) => Cow::Owned(Type::Validated(Validated { + typ: Box::new(a.into_owned()), + expr: expr.clone(), + })), + (Type::Validation(expr), _) => Cow::Owned(Type::Validated(Validated { + typ: Box::new(b.into_owned()), + expr: expr.clone(), + })), (Type::Generic(_), _) | (_, Type::Generic(_)) => { Cow::Owned(Type::Generic(Generic::Intersection(Intersection { @@ -1212,3 +1327,10 @@ pub fn intersect<'a: 'b, 'b>( (_, _) => Cow::Owned(Type::Basic(Basic::Never)), } } + +pub fn unwrap_validated(typ: &Type) -> (&Type, Option<&validation::Expr>) { + match typ { + Type::Validated(validated) => (&validated.typ, Some(&validated.expr)), + _ => (typ, None), + } +} diff --git a/tsparser/src/parser/types/type_resolve.rs b/tsparser/src/parser/types/type_resolve.rs index 389a117ea8..bb90c07849 100644 --- a/tsparser/src/parser/types/type_resolve.rs +++ b/tsparser/src/parser/types/type_resolve.rs @@ -168,7 +168,7 @@ impl Ctx<'_> { pub fn typ(&self, typ: &ast::TsType) -> Type { match typ { ast::TsType::TsKeywordType(tt) => self.keyword(tt), - ast::TsType::TsThisType(_) => Type::This, + ast::TsType::TsThisType(_) => Type::This(This), ast::TsType::TsArrayType(tt) => self.array(tt), ast::TsType::TsTupleType(tt) => self.tuple(tt), ast::TsType::TsUnionOrIntersectionType(ast::TsUnionOrIntersectionType::TsUnionType(tt)) => self.union(tt), @@ -272,9 +272,12 @@ impl Ctx<'_> { fn type_index(&self, span: Span, obj: &Type, idx: &Type) -> Type { match (obj, idx) { // If either obj or index is a generic type, we need to store it as a Generic::Index. - pair @ ((Type::Generic(_), _) | (_, Type::Generic(_))) => Type::Generic( - Generic::Index((Box::new(pair.0.clone()), Box::new(pair.1.clone()))), - ), + pair @ ((Type::Generic(_), _) | (_, Type::Generic(_))) => { + Type::Generic(Generic::Index(Index { + source: Box::new(pair.0.clone()), + index: Box::new(pair.1.clone()), + })) + } (Type::Named(named), idx) => { let underlying = named.underlying(self.state); @@ -299,7 +302,7 @@ impl Ctx<'_> { let typ = f.typ.clone(); // If the field is optional, wrap the type in Optional. if f.optional { - Type::Optional(Box::new(typ)) + Type::Optional(Optional(Box::new(typ))) } else { typ } @@ -318,9 +321,12 @@ impl Ctx<'_> { } }, - (Type::Validated((inner, expr)), idx) => { - let typ = self.type_index(span, inner, idx); - Type::Validated((Box::new(typ), expr.clone())) + (Type::Validated(v), idx) => { + let typ = self.type_index(span, &v.typ, idx); + Type::Validated(Validated { + typ: Box::new(typ), + expr: v.expr.clone(), + }) } (obj, idx) => { @@ -345,7 +351,7 @@ impl Ctx<'_> { let mut params = params.borrow_mut(); let idx = params.len(); params.push(id); - Type::Generic(Generic::Inferred(idx)) + Type::Generic(Generic::Inferred(Inferred(idx))) } else { tt.span.err("infer type outside of infer context"); Type::Basic(Basic::Never) @@ -359,16 +365,18 @@ impl Ctx<'_> { fn keyof(&self, typ: &Type) -> Type { match typ { Type::Basic(tt) => match tt { - Basic::Any => Type::Union(vec![ - Type::Basic(Basic::String), - Type::Basic(Basic::Number), - Type::Basic(Basic::Symbol), - ]), + Basic::Any => Type::Union(Union { + types: vec![ + Type::Basic(Basic::String), + Type::Basic(Basic::Number), + Type::Basic(Basic::Symbol), + ], + }), // These should technically enumerate the built-in properties // on these types, but we haven't implemented that yet. Basic::String | Basic::Boolean | Basic::Number | Basic::BigInt | Basic::Symbol => { - Type::Union(vec![]) + Type::Union(Union { types: vec![] }) } // keyof these yields never. @@ -381,16 +389,17 @@ impl Ctx<'_> { | Basic::Never => Type::Basic(Basic::Never), }, - Type::Enum(tt) => Type::Union( - tt.members + Type::Enum(tt) => Type::Union(Union { + types: tt + .members .iter() .map(|m| Type::Literal(Literal::String(m.name.clone()))) .collect(), - ), + }), // These should technically enumerate the built-in properties // on these types, but we haven't implemented that yet. - Type::Array(_) | Type::Tuple(_) => Type::Union(vec![]), + Type::Array(_) | Type::Tuple(_) => Type::Union(Union { types: vec![] }), Type::Interface(interface) => { let keys = interface @@ -403,7 +412,7 @@ impl Ctx<'_> { FieldName::Symbol(_) => None, }) .collect(); - Type::Union(keys) + Type::Union(Union { types: keys }) } Type::Named(_) => { @@ -416,25 +425,27 @@ impl Ctx<'_> { Type::Basic(Basic::Never) } - Type::Optional(typ) => self.keyof(typ), - Type::Union(types) => { - let res: Vec<_> = types.iter().map(|t| self.keyof(t)).collect(); - Type::Union(res) + Type::Optional(typ) => self.keyof(&typ.0), + Type::Union(union) => { + let res: Vec<_> = union.types.iter().map(|t| self.keyof(t)).collect(); + Type::Union(Union { types: res }) } // keyof "blah" is the same as keyof string, which should yield all properties. - Type::Literal(_) => Type::Union(vec![]), + Type::Literal(_) => Type::Union(Union { types: vec![] }), - Type::This => Type::Basic(Basic::Never), + Type::This(This) => Type::Basic(Basic::Never), - Type::Generic(generic) => { - Type::Generic(Generic::Keyof(Box::new(Type::Generic(generic.clone())))) - } - Type::Validated((inner, _)) => self.keyof(inner), + Type::Generic(generic) => Type::Generic(Generic::Keyof(Keyof(Box::new( + Type::Generic(generic.clone()), + )))), + Type::Validated(v) => self.keyof(&v.typ), Type::Validation(_) => { HANDLER.with(|handler| handler.err("keyof ValidationExpr unsupported")); Type::Basic(Basic::Never) } + + Type::Custom(Custom::WireSpec(spec)) => self.keyof(&spec.underlying), } } @@ -604,7 +615,7 @@ impl Ctx<'_> { return if let Some(mapped_key_type) = self.mapped_key_type { mapped_key_type.clone() } else { - Type::Generic(Generic::MappedKeyType) + Type::Generic(Generic::MappedKeyType(MappedKeyType)) }; } } @@ -621,7 +632,7 @@ impl Ctx<'_> { return if let Some(type_arg) = self.infer_type_args.get(idx) { type_arg.clone().into_owned() } else { - Type::Generic(Generic::Inferred(idx)) + Type::Generic(Generic::Inferred(Inferred(idx))) }; } } @@ -658,7 +669,19 @@ impl Ctx<'_> { if obj.name.as_ref().is_some_and(|s| s == "Array") && self.state.is_universe(obj.module_id) { let elem = type_arguments.pop().unwrap_or(Type::Basic(Basic::Never)); - return Type::Array(Box::new(elem)); + return Type::Array(Array(Box::new(elem))); + } + + // Is this a reference to the "Header" or "Query" wire spec overrides? + if obj + .name + .as_ref() + .is_some_and(|s| s == "Header" || s == "Query") + && self.state.is_module_path(obj.module_id, "encore.dev/api") + { + if let Some(wire_spec) = self.parse_wire_spec(typ.span, &obj, &type_arguments) { + return Type::Custom(Custom::WireSpec(wire_spec)); + } } match &obj.kind { @@ -762,11 +785,11 @@ impl Ctx<'_> { } fn array(&self, tt: &ast::TsArrayType) -> Type { - Type::Array(Box::new(self.typ(&tt.elem_type))) + Type::Array(Array(Box::new(self.typ(&tt.elem_type)))) } fn optional(&self, tt: &ast::TsOptionalType) -> Type { - Type::Optional(Box::new(self.typ(&tt.type_ann))) + Type::Optional(Optional(Box::new(self.typ(&tt.type_ann)))) } fn tuple(&self, tuple: &ast::TsTupleType) -> Type { @@ -780,7 +803,7 @@ impl Ctx<'_> { Some(t.ty.as_ref()) })); - Type::Tuple(types) + Type::Tuple(Tuple { types }) } fn union(&self, union_type: &ast::TsUnionType) -> Type { @@ -800,7 +823,7 @@ impl Ctx<'_> { // Do we have a union type in `check`, and the AST is a naked type parameter? // If so, we need to treat it as a distributive conditional type. // See: https://www.typescriptlang.org/docs/handbook/advanced-types.html#distributive-conditional-types - if let Type::Union(types) = &check { + if let Type::Union(union) = &check { if let ast::TsType::TsTypeRef(ref check) = tt.check_type.as_ref() { if check.type_params.is_none() { if let Some(ident) = check.type_name.as_ident() { @@ -810,7 +833,8 @@ impl Ctx<'_> { .any(|tp| tp.name.to_id() == ident.to_id()) { // Apply the conditional to each type in the union. - let result = types + let result = union + .types .iter() .map(|t| match t.assignable(self.state, &extends) { Some(true) => self.typ(&tt.true_type), @@ -945,7 +969,7 @@ impl Ctx<'_> { fn expr(&self, expr: &ast::Expr) -> Type { match expr { - ast::Expr::This(_) => Type::This, + ast::Expr::This(_) => Type::This(This), ast::Expr::Array(lit) => self.array_lit(lit), ast::Expr::Object(lit) => self.object_lit(lit), ast::Expr::Fn(_) => { @@ -1124,9 +1148,10 @@ impl Ctx<'_> { ast::Expr::TsNonNull(expr) => { let base = self.expr(&expr.expr); match base { - Type::Optional(typ) => *typ, - Type::Union(types) => { - let non_null = types + Type::Optional(typ) => *typ.0, + Type::Union(union) => { + let non_null = union + .types .into_iter() .filter(|t| { !matches!( @@ -1135,10 +1160,10 @@ impl Ctx<'_> { ) }) .collect::>(); - match &non_null.len() { + match non_null.len() { 0 => Type::Basic(Basic::Never), 1 => non_null[0].clone(), - _ => Type::Union(non_null), + _ => Type::Union(Union { types: non_null }), } } _ => base, @@ -1168,14 +1193,16 @@ impl Ctx<'_> { if elem.spread.is_some() { // The type of [...["a"]] is string[]. if let Type::Array(arr) = base { - base = *arr; + base = *arr.0; } } match &elem_type { Some(Type::Union(_elem_types)) => {} Some(typ) => { - elem_type = Some(Type::Union(vec![typ.clone(), base])); + elem_type = Some(Type::Union(Union { + types: vec![typ.clone(), base], + })); } None => { elem_type = Some(base); @@ -1183,7 +1210,7 @@ impl Ctx<'_> { } } - Type::Union(elem_types) + Type::Union(Union { types: elem_types }) } fn object_lit(&self, lit: &ast::ObjectLit) -> Type { @@ -1280,7 +1307,7 @@ impl Ctx<'_> { | Type::Tuple(_) | Type::Union(_) | Type::Optional(_) - | Type::This + | Type::This(_) | Type::Generic(_) | Type::Class(_) | Type::Validation(_) => { @@ -1338,7 +1365,10 @@ impl Ctx<'_> { let underlying = self.underlying(obj_type); self.resolve_member_prop(&underlying, prop) } - Type::Validated((inner, _)) => self.resolve_member_prop(inner, prop), + Type::Validated(v) => self.resolve_member_prop(&v.typ, prop), + Type::Custom(Custom::WireSpec(spec)) => { + self.resolve_member_prop(&spec.underlying, prop) + } } } @@ -1543,27 +1573,31 @@ impl Ctx<'_> { pub fn concrete<'b>(&'b self, typ: &'b Type) -> Resolved<'b, Type> { match typ { // Basic types that never change. - Type::Basic(_) | Type::Literal(_) | Type::Enum(_) | Type::This => Same(typ), + Type::Basic(_) | Type::Literal(_) | Type::Enum(_) | Type::This(_) => Same(typ), // Nested types that recurse. - Type::Array(elem) => match self.concrete(elem) { - New(t) => New(Type::Array(Box::new(t))), - Changed(t) => New(Type::Array(Box::new(t.clone()))), + Type::Array(elem) => match self.concrete(&elem.0) { + New(t) => New(Type::Array(Array(Box::new(t)))), + Changed(t) => New(Type::Array(Array(Box::new(t.clone())))), Same(_) => Same(typ), }, - Type::Tuple(types) => match self.concrete_list(types) { - New(t) => New(Type::Tuple(t)), - Changed(t) => New(Type::Tuple(t.to_owned())), + Type::Tuple(tuple) => match self.concrete_list(&tuple.types) { + New(t) => New(Type::Tuple(Tuple { types: t })), + Changed(t) => New(Type::Tuple(Tuple { + types: t.to_owned(), + })), Same(_) => Same(typ), }, - Type::Union(types) => match self.concrete_list(types) { - New(t) => New(Type::Union(t)), - Changed(t) => New(Type::Union(t.to_owned())), + Type::Union(union) => match self.concrete_list(&union.types) { + New(t) => New(Type::Union(Union { types: t })), + Changed(t) => New(Type::Union(Union { + types: t.to_owned(), + })), Same(_) => Same(typ), }, - Type::Optional(typ) => match self.concrete(typ) { - New(t) => New(Type::Optional(Box::new(t))), - Changed(t) => New(Type::Optional(Box::new(t.to_owned()))), + Type::Optional(opt) => match self.concrete(&opt.0) { + New(t) => New(Type::Optional(Optional(Box::new(t)))), + Changed(t) => New(Type::Optional(Optional(Box::new(t.to_owned())))), Same(_) => Same(typ), }, @@ -1650,9 +1684,9 @@ impl Ctx<'_> { } } - Generic::Inferred(idx) => { + Generic::Inferred(inferred) => { // If we have a concrete inferred type, return that. - if let Some(arg) = self.infer_type_args.get(*idx) { + if let Some(arg) = self.infer_type_args.get(inferred.0) { Changed(arg) } else { // We don't have a concrete type, so return the original type. @@ -1661,7 +1695,7 @@ impl Ctx<'_> { } Generic::Keyof(source) => { - let concrete_source = self.concrete(source); + let concrete_source = self.concrete(&source.0); let keys = self.keyof(&concrete_source); New(keys) } @@ -1694,6 +1728,7 @@ impl Ctx<'_> { // Construct a modified context that modifies the given type argument // to refer only to the concrete type for this union. let result: Vec<_> = check + .types .into_iter() .filter_map(|c| { // Modify the type args. @@ -1757,9 +1792,9 @@ impl Ctx<'_> { } } - Generic::Index((source, index)) => { - let source = self.concrete(source); - let index = self.concrete(index); + Generic::Index(index) => { + let source = self.concrete(&index.source); + let index = self.concrete(&index.index); let result = self.type_index(Span::default(), &source, &index); New(result) } @@ -1794,6 +1829,9 @@ impl Ctx<'_> { }); } + // An unresolved generic type means we can't resolve this yet. + Type::Generic(_) => return Same(typ), + // Do we have a wildcard type like "string" or "number"? // If so treat it as an index signature. source @ (Type::Basic(Basic::String) @@ -1809,7 +1847,7 @@ impl Ctx<'_> { let (typ, optional) = match value { // Never means the field should be excluded. Type::Basic(Basic::Never) => continue, - Type::Optional(typ) => (*typ, true), + Type::Optional(typ) => (*typ.0, true), typ => (typ, false), }; @@ -1836,9 +1874,9 @@ impl Ctx<'_> { let value = if *optional { match value.as_ref() { Type::Optional(_) => value, - _ => Box::new(Type::Optional(value)), + _ => Box::new(Type::Optional(Optional(value))), } - } else if let Type::Optional(inner) = *value { + } else if let Type::Optional(Optional(inner)) = *value { inner } else { value @@ -1854,19 +1892,36 @@ impl Ctx<'_> { New(Type::Interface(iface)) } - Generic::MappedKeyType => match self.mapped_key_type { + Generic::MappedKeyType(_) => match self.mapped_key_type { Some(key) => Changed(key), None => Same(typ), }, }, - Type::Validated((inner, expr)) => match self.concrete(inner) { - New(inner) => New(Type::Validated((Box::new(inner), expr.clone()))), - Changed(inner) => New(Type::Validated((Box::new(inner.clone()), expr.clone()))), + Type::Validated(v) => match self.concrete(&v.typ) { + New(inner) => New(Type::Validated(Validated { + typ: Box::new(inner), + expr: v.expr.clone(), + })), + Changed(inner) => New(Type::Validated(Validated { + typ: Box::new(inner.clone()), + expr: v.expr.clone(), + })), Same(_) => Same(typ), }, Type::Validation(_) => Same(typ), + Type::Custom(Custom::WireSpec(spec)) => match self.concrete(&spec.underlying) { + New(inner) => New(Type::Custom(Custom::WireSpec(WireSpec { + underlying: Box::new(inner), + ..spec.clone() + }))), + Changed(inner) => New(Type::Custom(Custom::WireSpec(WireSpec { + underlying: Box::new(inner.clone()), + ..spec.clone() + }))), + Same(_) => Same(typ), + }, } } @@ -2033,4 +2088,44 @@ impl Ctx<'_> { _ => None, } } + + fn parse_wire_spec(&self, span: Span, obj: &Object, type_args: &[Type]) -> Option { + let location = match &obj.name.as_deref() { + Some("Header") => WireLocation::Header, + Some("Query") => WireLocation::Query, + _ => return None, + }; + + fn str_lit(sp: Span, typ: &Type) -> Option { + if let Type::Literal(Literal::String(s)) = typ { + return Some(s.clone()); + } + sp.err("expected a string literal as the second type argument"); + None + } + + let (underlying, name_override) = match (type_args.first(), type_args.get(1)) { + (None, None) => (Type::Basic(Basic::String), None), + + (Some(first), None) => { + // If we only have a single argument, check its type. + // If it's a string literal it's the name, otherwise it's the type. + match first { + Type::Literal(Literal::String(lit)) => { + (Type::Basic(Basic::String), Some(lit.to_string())) + } + _ => (first.clone(), None), + } + } + + (Some(typ), Some(name)) => (typ.clone(), str_lit(span, name)), + (None, Some(_)) => unreachable!(), + }; + + Some(WireSpec { + location, + underlying: Box::new(underlying), + name_override, + }) + } } diff --git a/tsparser/src/parser/types/type_string.rs b/tsparser/src/parser/types/type_string.rs new file mode 100644 index 0000000000..11bed6c681 --- /dev/null +++ b/tsparser/src/parser/types/type_string.rs @@ -0,0 +1,212 @@ +use std::fmt::{Display, Write}; + +use super::{validation, Basic, Custom, Generic, Interface, Type, Validated, WireSpec}; + +impl Display for Type { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut renderer = TypeRenderer { buf: f }; + renderer.render_type(self) + } +} + +struct TypeRenderer { + buf: B, +} + +impl TypeRenderer +where + B: Write, +{ + fn render_type(&mut self, typ: &Type) -> std::fmt::Result { + match typ { + Type::Basic(b) => self.render_basic(b), + Type::Array(arr) => { + self.buf.write_str("Array<")?; + self.render_type(&arr.0)?; + self.buf.write_char('>') + } + Type::Interface(iface) => self.render_iface(iface), + Type::Union(union) => { + for (i, typ) in union.types.iter().enumerate() { + if i > 0 { + self.buf.write_str(" | ")?; + } + self.render_type(typ)?; + } + Ok(()) + } + Type::Tuple(tup) => { + self.buf.write_char('[')?; + for (i, typ) in tup.types.iter().enumerate() { + if i > 0 { + self.buf.write_str(", ")?; + } + self.render_type(typ)?; + } + self.buf.write_char(']') + } + Type::Literal(lit) => self.render_literal(lit), + Type::Class(cls) => self.render_class(cls), + Type::Enum(e) => self.render_enum(e), + Type::Named(named) => self.render_named(named), + Type::Optional(opt) => { + self.render_type(&opt.0)?; + self.buf.write_char('?') + } + Type::This(_) => self.buf.write_str("this"), + Type::Generic(g) => self.render_generic(g), + Type::Validation(v) => self.render_validation(v), + Type::Validated(v) => self.render_validated(v), + Type::Custom(c) => self.render_custom(c), + } + } + + fn render_basic(&mut self, b: &Basic) -> std::fmt::Result { + use Basic::*; + let s = match b { + Any => "any", + String => "string", + Boolean => "boolean", + Number => "number", + Object => "object", + BigInt => "bigint", + Date => "Date", + Symbol => "symbol", + Undefined => "undefined", + Null => "null", + Void => "void", + Unknown => "unknown", + Never => "never", + }; + self.buf.write_str(s) + } + + fn render_iface(&mut self, iface: &Interface) -> std::fmt::Result { + self.buf.write_str("interface { ")?; + for (i, field) in iface.fields.iter().enumerate() { + if i > 0 { + self.buf.write_str("; ")?; + } + + use super::FieldName; + self.buf.write_str(match &field.name { + FieldName::String(s) => s, + FieldName::Symbol(_) => "symbol", + })?; + if field.optional { + self.buf.write_char('?')?; + } + self.buf.write_str(": ")?; + self.render_type(&field.typ)?; + } + self.buf.write_str(" }") + } + + fn render_literal(&mut self, lit: &super::Literal) -> std::fmt::Result { + use super::Literal; + match lit { + Literal::String(s) => self.buf.write_fmt(format_args!("{:#?}", s)), + Literal::Boolean(b) => self.buf.write_fmt(format_args!("{}", b)), + Literal::Number(n) => self.buf.write_fmt(format_args!("{}", n)), + Literal::BigInt(n) => self.buf.write_str(n), + } + } + + fn render_class(&mut self, _cls: &super::ClassType) -> std::fmt::Result { + self.buf.write_str("class {}") + } + + fn render_enum(&mut self, e: &super::EnumType) -> std::fmt::Result { + self.buf.write_str("enum { ")?; + for (i, mem) in e.members.iter().enumerate() { + if i > 0 { + self.buf.write_str(", ")?; + } + self.buf.write_str(&mem.name)?; + } + self.buf.write_str(" }") + } + + fn render_named(&mut self, named: &super::Named) -> std::fmt::Result { + let name = named.obj.name.as_deref().unwrap_or("UnknownObject"); + self.buf.write_str(name)?; + + if !named.type_arguments.is_empty() { + self.buf.write_char('<')?; + for (i, arg) in named.type_arguments.iter().enumerate() { + if i > 0 { + self.buf.write_str(", ")?; + } + self.render_type(arg)?; + } + self.buf.write_char('>')?; + } + Ok(()) + } + + fn render_validation(&mut self, v: &validation::Expr) -> std::fmt::Result { + self.buf.write_fmt(format_args!("{}", v)) + } + + fn render_validated(&mut self, v: &Validated) -> std::fmt::Result { + self.render_type(&v.typ)?; + self.buf.write_fmt(format_args!(" & {}", v.expr)) + } + + fn render_custom(&mut self, c: &Custom) -> std::fmt::Result { + match c { + Custom::WireSpec(s) => self.render_wire_spec(s), + } + } + + fn render_wire_spec(&mut self, s: &WireSpec) -> std::fmt::Result { + match &s.location { + super::WireLocation::Query => self.buf.write_str("Query<")?, + super::WireLocation::Header => self.buf.write_str("Header<")?, + } + self.render_type(&s.underlying)?; + if let Some(name) = &s.name_override { + self.buf.write_fmt(format_args!(", {:#?}", name))?; + } + self.buf.write_char('>') + } + + fn render_generic(&mut self, g: &Generic) -> std::fmt::Result { + match g { + Generic::TypeParam(tp) => self.buf.write_fmt(format_args!("TypeParam#{}", tp.idx)), + Generic::Index(idx) => { + self.render_type(&idx.source)?; + self.buf.write_char('[')?; + self.render_type(&idx.index)?; + self.buf.write_char(']') + } + Generic::Mapped(m) => { + self.buf.write_str("{ [P in ")?; + self.render_type(&m.in_type)?; + self.buf.write_str("]: ")?; + self.render_type(&m.value_type)?; + self.buf.write_str(" }") + } + Generic::MappedKeyType(_) => self.buf.write_char('P'), + Generic::Keyof(k) => { + self.buf.write_str("keyof ")?; + self.render_type(&k.0) + } + Generic::Conditional(c) => { + self.render_type(&c.check_type)?; + self.buf.write_str(" extends ")?; + self.render_type(&c.extends_type)?; + self.buf.write_str(" ? ")?; + self.render_type(&c.true_type)?; + self.buf.write_str(" : ")?; + self.render_type(&c.false_type) + } + Generic::Inferred(i) => self.buf.write_fmt(format_args!("Inferred#{}", i.0)), + Generic::Intersection(i) => { + self.render_type(&i.x)?; + self.buf.write_str(" & ")?; + self.render_type(&i.y) + } + } + } +} diff --git a/tsparser/src/parser/types/validation.rs b/tsparser/src/parser/types/validation.rs index 31d991661f..b49726007f 100644 --- a/tsparser/src/parser/types/validation.rs +++ b/tsparser/src/parser/types/validation.rs @@ -1,7 +1,13 @@ use crate::encore::parser::schema::v1 as schema; use core::hash::{Hash, Hasher}; use serde::Serialize; -use std::ops::Deref; +use std::{ + error::Error, + fmt::{Display, Write}, + ops::Deref, +}; + +use super::{Basic, Custom, Type}; #[derive(Debug, Clone, Hash, Serialize, PartialEq, Eq)] pub enum Expr { @@ -10,6 +16,34 @@ pub enum Expr { Or(Vec), } +impl Display for Expr { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Rule(r) => f.write_fmt(format_args!("{}", r)), + Self::And(a) => { + f.write_char('(')?; + for (i, e) in a.iter().enumerate() { + if i > 0 { + f.write_str(" & ")?; + } + f.write_fmt(format_args!("{}", e))?; + } + f.write_char(')') + } + Self::Or(a) => { + f.write_char('(')?; + for (i, e) in a.iter().enumerate() { + if i > 0 { + f.write_str(" | ")?; + } + f.write_fmt(format_args!("{}", e))?; + } + f.write_char(')') + } + } + } +} + #[derive(Debug, Clone, Hash, Serialize, PartialEq, Eq)] pub enum Rule { MinLen(u64), @@ -22,6 +56,22 @@ pub enum Rule { Is(Is), } +impl Display for Rule { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::MinLen(n) => f.write_fmt(format_args!("MinLen<{}>", n)), + Self::MaxLen(n) => f.write_fmt(format_args!("MaxLen<{}>", n)), + Self::MinVal(n) => f.write_fmt(format_args!("Min<{}>", n)), + Self::MaxVal(n) => f.write_fmt(format_args!("Max<{}>", n)), + Self::StartsWith(s) => f.write_fmt(format_args!("StartsWith<{:#?}>", s)), + Self::EndsWith(s) => f.write_fmt(format_args!("EndsWith<{:#?}", s)), + Self::MatchesRegexp(s) => f.write_fmt(format_args!("MatchesRegexp<{:#?}", s)), + Self::Is(Is::Email) => f.write_str("IsEmail"), + Self::Is(Is::Url) => f.write_str("IsURL"), + } + } +} + #[derive(Debug, Clone, Hash, Serialize, PartialEq, Eq)] pub enum Is { Email, @@ -67,6 +117,28 @@ impl Rule { } as i32), } } + + pub fn supports_type(&self, typ: &Type) -> bool { + // If this is a WireSpec, unwrap it as it is intended to be transparent. + let typ = match typ { + Type::Custom(Custom::WireSpec(spec)) => &spec.underlying, + _ => typ, + }; + + match self { + Self::MinLen(_) | Self::MaxLen(_) => { + matches!(typ, Type::Array(_) | Type::Basic(Basic::String)) + } + Self::MinVal(_) | Self::MaxVal(_) => matches!(typ, Type::Basic(Basic::Number)), + Self::StartsWith(_) + | Self::EndsWith(_) + | Self::MatchesRegexp(_) + | Self::Is(Is::Email) + | Self::Is(Is::Url) => { + matches!(typ, Type::Basic(Basic::String)) + } + } + } } impl Expr { @@ -228,11 +300,84 @@ impl Expr { }), } } + + pub fn supports_type<'a>( + &'a self, + typ: &'a Type, + ) -> Result<(), UnsupportedValidationsError<'a>> { + match self { + Self::And(exprs) | Self::Or(exprs) => { + let mut errors = Vec::new(); + for expr in exprs { + if let Err(e) = expr.supports_type(typ) { + errors.extend(e.0); + } + } + if errors.is_empty() { + Ok(()) + } else { + Err(UnsupportedValidationsError(errors)) + } + } + Self::Rule(rule) => { + if !rule.supports_type(typ) { + let v = UnsupportedValidation { typ, rule }; + Err(UnsupportedValidationsError(vec![v])) + } else { + Ok(()) + } + } + } + } +} + +#[derive(Debug, Clone)] +pub struct UnsupportedValidation<'a> { + pub typ: &'a Type, + pub rule: &'a Rule, +} + +#[derive(Debug, Clone)] +pub struct UnsupportedValidationsError<'a>(pub Vec>); + +impl Display for UnsupportedValidation<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_fmt(format_args!( + "{} cannot be applied to {}", + self.rule, self.typ + )) + } } +impl Display for UnsupportedValidationsError<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if self.0.len() == 1 { + write!(f, "unsupported validation: {}", &self.0[0]) + } else { + f.write_str("unsupported validation: ")?; + for (i, rule) in self.0.iter().enumerate() { + if i > 0 { + f.write_str(", ")?; + } + write!(f, "{}", rule)?; + } + Ok(()) + } + } +} + +impl Error for UnsupportedValidationsError<'_> {} +impl Error for UnsupportedValidation<'_> {} + #[derive(Debug, Clone, Copy, Serialize)] pub struct N(pub f64); +impl Display for N { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + impl Deref for N { type Target = f64; diff --git a/tsparser/src/parser/types/visitor.rs b/tsparser/src/parser/types/visitor.rs new file mode 100644 index 0000000000..de0ee5ed0e --- /dev/null +++ b/tsparser/src/parser/types/visitor.rs @@ -0,0 +1,562 @@ +use std::collections::HashSet; + +use litparser::Sp; + +use super::{ + validation, Array, Basic, ClassType, Conditional, Custom, EnumType, Generic, Index, Inferred, + Interface, InterfaceField, Intersection, Keyof, Literal, Mapped, MappedKeyType, Named, + ObjectId, Optional, ResolveState, This, Tuple, Type, TypeParam, Union, Validated, WireSpec, +}; + +pub trait Visit { + fn resolve_state(&self) -> &ResolveState; + fn seen_decls(&mut self) -> &mut HashSet; + + #[inline] + fn visit_basic(&mut self, node: &Basic) { + >::visit_children_with(node, self) + } + + #[inline] + fn visit_array(&mut self, node: &Array) { + >::visit_children_with(node, self) + } + + #[inline] + fn visit_interface(&mut self, node: &Interface) { + >::visit_children_with(node, self) + } + + #[inline] + fn visit_interface_field(&mut self, node: &InterfaceField) { + >::visit_children_with(node, self) + } + + #[inline] + fn visit_union(&mut self, node: &Union) { + >::visit_children_with(node, self) + } + + #[inline] + fn visit_tuple(&mut self, node: &Tuple) { + >::visit_children_with(node, self) + } + + #[inline] + fn visit_literal(&mut self, node: &Literal) { + >::visit_children_with(node, self) + } + + #[inline] + fn visit_class(&mut self, node: &ClassType) { + >::visit_children_with(node, self) + } + + #[inline] + fn visit_enum(&mut self, node: &EnumType) { + >::visit_children_with(node, self) + } + + #[inline] + fn visit_named(&mut self, node: &Named) { + >::visit_children_with(node, self) + } + + #[inline] + fn visit_optional(&mut self, node: &Optional) { + >::visit_children_with(node, self) + } + + #[inline] + fn visit_this(&mut self, node: &This) { + >::visit_children_with(node, self) + } + + #[inline] + fn visit_validation(&mut self, node: &validation::Expr) { + >::visit_children_with(node, self) + } + + #[inline] + fn visit_validated(&mut self, node: &Validated) { + >::visit_children_with(node, self) + } + + #[inline] + fn visit_type(&mut self, node: &Type) { + >::visit_children_with(node, self) + } + + #[inline] + fn visit_types(&mut self, node: &[Type]) { + <[Type] as VisitWith>::visit_children_with(node, self) + } + + #[inline] + fn visit_generic(&mut self, node: &Generic) { + >::visit_children_with(node, self) + } + + #[inline] + fn visit_type_param(&mut self, node: &TypeParam) { + >::visit_children_with(node, self) + } + + #[inline] + fn visit_index(&mut self, node: &Index) { + >::visit_children_with(node, self) + } + + #[inline] + fn visit_mapped(&mut self, node: &Mapped) { + >::visit_children_with(node, self) + } + + #[inline] + fn visit_mapped_key_type(&mut self, node: &MappedKeyType) { + >::visit_children_with(node, self) + } + + #[inline] + fn visit_keyof(&mut self, node: &Keyof) { + >::visit_children_with(node, self) + } + + #[inline] + fn visit_conditional(&mut self, node: &Conditional) { + >::visit_children_with(node, self) + } + + #[inline] + fn visit_intersection(&mut self, node: &Intersection) { + >::visit_children_with(node, self) + } + + #[inline] + fn visit_inferred(&mut self, node: &Inferred) { + >::visit_children_with(node, self) + } + + #[inline] + fn visit_custom(&mut self, node: &Custom) { + >::visit_children_with(node, self) + } + + #[inline] + fn visit_wire_spec(&mut self, node: &WireSpec) { + >::visit_children_with(node, self) + } +} + +pub trait VisitWith { + fn visit_with(&self, visitor: &mut V); + fn visit_children_with(&self, visitor: &mut V); +} + +impl VisitWith for This +where + V: ?Sized + Visit, +{ + #[inline] + fn visit_with(&self, _visitor: &mut V) {} + + #[inline] + fn visit_children_with(&self, _visitor: &mut V) {} +} + +impl VisitWith for Basic { + fn visit_with(&self, visitor: &mut V) { + ::visit_basic(visitor, self) + } + fn visit_children_with(&self, _visitor: &mut V) {} +} + +impl VisitWith for Interface { + fn visit_with(&self, visitor: &mut V) { + ::visit_interface(visitor, self) + } + + fn visit_children_with(&self, visitor: &mut V) { + self.fields + .iter() + .for_each(|item| >::visit_with(item, visitor)) + } +} + +impl VisitWith for InterfaceField { + fn visit_with(&self, visitor: &mut V) { + ::visit_interface_field(visitor, self) + } + + fn visit_children_with(&self, visitor: &mut V) { + >::visit_with(&self.typ, visitor) + } +} + +impl VisitWith for Array { + #[inline] + fn visit_with(&self, visitor: &mut V) { + ::visit_array(visitor, self) + } + + #[inline] + fn visit_children_with(&self, visitor: &mut V) { + >::visit_with(&self.0, visitor) + } +} + +impl VisitWith for Union { + #[inline] + fn visit_with(&self, visitor: &mut V) { + ::visit_union(visitor, self) + } + + #[inline] + fn visit_children_with(&self, visitor: &mut V) { + self.types + .iter() + .for_each(|item| >::visit_with(item, visitor)) + } +} + +impl VisitWith for Tuple { + #[inline] + fn visit_with(&self, visitor: &mut V) { + ::visit_tuple(visitor, self) + } + + #[inline] + fn visit_children_with(&self, visitor: &mut V) { + self.types + .iter() + .for_each(|item| >::visit_with(item, visitor)) + } +} + +impl VisitWith for Optional { + #[inline] + fn visit_with(&self, visitor: &mut V) { + ::visit_optional(visitor, self) + } + + #[inline] + fn visit_children_with(&self, visitor: &mut V) { + >::visit_with(&self.0, visitor) + } +} + +impl VisitWith for validation::Expr { + #[inline] + fn visit_with(&self, visitor: &mut V) { + ::visit_validation(visitor, self) + } + + #[inline] + fn visit_children_with(&self, _visitor: &mut V) {} +} + +impl VisitWith for Validated { + #[inline] + fn visit_with(&self, visitor: &mut V) { + ::visit_validated(visitor, self) + } + + #[inline] + fn visit_children_with(&self, visitor: &mut V) { + >::visit_with(&self.typ, visitor); + >::visit_with(&self.expr, visitor); + } +} + +impl VisitWith for Literal { + #[inline] + fn visit_with(&self, visitor: &mut V) { + ::visit_literal(visitor, self) + } + + #[inline] + fn visit_children_with(&self, _visitor: &mut V) {} +} + +impl VisitWith for ClassType { + #[inline] + fn visit_with(&self, visitor: &mut V) { + ::visit_class(visitor, self) + } + + #[inline] + fn visit_children_with(&self, _visitor: &mut V) {} +} + +impl VisitWith for EnumType { + #[inline] + fn visit_with(&self, visitor: &mut V) { + ::visit_enum(visitor, self) + } + + #[inline] + fn visit_children_with(&self, _visitor: &mut V) {} +} + +impl VisitWith for TypeParam { + #[inline] + fn visit_with(&self, visitor: &mut V) { + ::visit_type_param(visitor, self) + } + + #[inline] + fn visit_children_with(&self, visitor: &mut V) { + if let Some(constraint) = &self.constraint { + >::visit_with(constraint, visitor); + } + } +} + +impl VisitWith for Index { + #[inline] + fn visit_with(&self, visitor: &mut V) { + ::visit_index(visitor, self) + } + + #[inline] + fn visit_children_with(&self, visitor: &mut V) { + >::visit_with(&self.source, visitor); + >::visit_with(&self.index, visitor); + } +} + +impl VisitWith for Mapped { + #[inline] + fn visit_with(&self, visitor: &mut V) { + ::visit_mapped(visitor, self) + } + + #[inline] + fn visit_children_with(&self, visitor: &mut V) { + >::visit_with(&self.in_type, visitor); + >::visit_with(&self.value_type, visitor); + } +} + +impl VisitWith for MappedKeyType { + #[inline] + fn visit_with(&self, visitor: &mut V) { + ::visit_mapped_key_type(visitor, self) + } + + #[inline] + fn visit_children_with(&self, _visitor: &mut V) {} +} + +impl VisitWith for Keyof { + #[inline] + fn visit_with(&self, visitor: &mut V) { + ::visit_keyof(visitor, self) + } + + #[inline] + fn visit_children_with(&self, visitor: &mut V) { + >::visit_with(&self.0, visitor); + } +} + +impl VisitWith for Conditional { + #[inline] + fn visit_with(&self, visitor: &mut V) { + ::visit_conditional(visitor, self) + } + + #[inline] + fn visit_children_with(&self, visitor: &mut V) { + >::visit_with(&self.check_type, visitor); + >::visit_with(&self.extends_type, visitor); + >::visit_with(&self.true_type, visitor); + >::visit_with(&self.false_type, visitor); + } +} + +impl VisitWith for Intersection { + #[inline] + fn visit_with(&self, visitor: &mut V) { + ::visit_intersection(visitor, self) + } + + #[inline] + fn visit_children_with(&self, visitor: &mut V) { + >::visit_with(&self.x, visitor); + >::visit_with(&self.y, visitor); + } +} + +impl VisitWith for Inferred { + #[inline] + fn visit_with(&self, visitor: &mut V) { + ::visit_inferred(visitor, self) + } + + #[inline] + fn visit_children_with(&self, _visitor: &mut V) {} +} + +impl VisitWith for Named { + #[inline] + fn visit_with(&self, visitor: &mut V) { + ::visit_named(visitor, self) + } + + #[inline] + fn visit_children_with(&self, visitor: &mut V) { + // Only recurse if we haven't seen this object before, to avoid infinite recursion + // with recursive types. + if visitor.seen_decls().insert(self.obj.id) { + let underlying = self.underlying(visitor.resolve_state()); + >::visit_with(&underlying, visitor); + } + + self.type_arguments + .iter() + .for_each(|item| >::visit_with(item, visitor)) + } +} + +impl VisitWith for Generic { + #[inline] + fn visit_with(&self, visitor: &mut V) { + ::visit_generic(visitor, self) + } + + #[inline] + fn visit_children_with(&self, visitor: &mut V) { + match self { + Generic::TypeParam(inner) => >::visit_with(inner, visitor), + Generic::Index(inner) => >::visit_with(inner, visitor), + Generic::Mapped(inner) => >::visit_with(inner, visitor), + Generic::MappedKeyType(inner) => { + >::visit_with(inner, visitor) + } + Generic::Keyof(inner) => >::visit_with(inner, visitor), + Generic::Conditional(inner) => { + >::visit_with(inner, visitor) + } + Generic::Intersection(inner) => { + >::visit_with(inner, visitor) + } + Generic::Inferred(inner) => >::visit_with(inner, visitor), + } + } +} + +impl VisitWith for Custom { + #[inline] + fn visit_with(&self, visitor: &mut V) { + ::visit_custom(visitor, self) + } + + #[inline] + fn visit_children_with(&self, visitor: &mut V) { + match self { + Custom::WireSpec(inner) => >::visit_with(inner, visitor), + } + } +} + +impl VisitWith for WireSpec { + #[inline] + fn visit_with(&self, visitor: &mut V) { + ::visit_wire_spec(visitor, self) + } + + #[inline] + fn visit_children_with(&self, visitor: &mut V) { + >::visit_with(&self.underlying, visitor) + } +} + +impl VisitWith for Type { + fn visit_with(&self, visitor: &mut V) { + ::visit_type(visitor, self) + } + + fn visit_children_with(&self, visitor: &mut V) { + match self { + Self::Basic(basic) => >::visit_with(basic, visitor), + Self::Array(array) => >::visit_with(array, visitor), + Type::Interface(interface) => { + >::visit_with(interface, visitor) + } + Type::Union(union) => >::visit_with(union, visitor), + Type::Tuple(tuple) => >::visit_with(tuple, visitor), + Type::Literal(literal) => >::visit_with(literal, visitor), + Type::Class(class_type) => >::visit_with(class_type, visitor), + Type::Enum(enum_type) => >::visit_with(enum_type, visitor), + Type::Named(named) => >::visit_with(named, visitor), + Type::Optional(opt) => >::visit_with(opt, visitor), + Type::This(t) => >::visit_with(t, visitor), + Type::Generic(generic) => >::visit_with(generic, visitor), + Type::Validation(expr) => >::visit_with(expr, visitor), + Type::Validated(expr) => >::visit_with(expr, visitor), + Type::Custom(custom) => >::visit_with(custom, visitor), + } + } +} + +impl VisitWith for [Type] { + #[inline] + fn visit_with(&self, visitor: &mut V) { + ::visit_types(visitor, self) + } + + #[inline] + fn visit_children_with(&self, visitor: &mut V) { + self.iter() + .for_each(|item| >::visit_with(item, visitor)) + } +} + +impl VisitWith for std::boxed::Box +where + V: ?Sized + Visit, + T: VisitWith, +{ + #[inline] + fn visit_with(&self, visitor: &mut V) { + >::visit_with(&**self, visitor) + } + + #[inline] + fn visit_children_with(&self, visitor: &mut V) { + >::visit_children_with(&**self, visitor) + } +} + +impl VisitWith for Sp +where + V: ?Sized + Visit, + T: VisitWith, +{ + #[inline] + fn visit_with(&self, visitor: &mut V) { + >::visit_with(&**self, visitor) + } + + #[inline] + fn visit_children_with(&self, visitor: &mut V) { + >::visit_children_with(&**self, visitor) + } +} + +impl VisitWith for std::vec::Vec +where + V: ?Sized + Visit, + [T]: VisitWith, +{ + #[inline] + fn visit_with(&self, visitor: &mut V) { + <[T] as VisitWith>::visit_with(self, visitor) + } + + #[inline] + fn visit_children_with(&self, visitor: &mut V) { + <[T] as VisitWith>::visit_children_with(self, visitor) + } +}