@@ -441,6 +441,44 @@ func msgpackUnmarshalUnknown(dec *msgpack.Decoder, typ Type, path *AttributePath
441441 // TODO: If terraform doesn't support an empty string prefix, then neither should we, potentially return an error here.
442442
443443 newRefinements [keyCode ] = refinement .NewStringPrefix (prefix )
444+ case refinement .KeyNumberLowerBound , refinement .KeyNumberUpperBound :
445+ if ! typ .Is (Number ) {
446+ return Value {}, path .NewErrorf ("failed to decode msgpack extension body: numeric bound refinement for non-number type" )
447+ }
448+
449+ // We know these refinements are a tuple of [number, bool] so we can re-use the msgpack decoding logic on this refinement
450+ tfValBound , err := msgpackUnmarshal (rfnDec , Tuple {ElementTypes : []Type {Number , Bool }}, path )
451+ if err != nil || tfValBound .IsNull () || ! tfValBound .IsKnown () {
452+ return Value {}, path .NewErrorf ("failed to decode msgpack extension body: numeric bound refinement must be [number, bool] tuple" )
453+ }
454+
455+ tupleVal := []Value {}
456+ err = tfValBound .As (& tupleVal )
457+ if err != nil {
458+ return Value {}, path .NewErrorf ("failed to decode msgpack extension body: numeric bound refinement tuple value conversion failed: %w" , err )
459+ }
460+
461+ if len (tupleVal ) != 2 {
462+ return Value {}, path .NewErrorf ("failed to decode msgpack extension body: numeric bound refinement tuple value conversion failed: expected 2 elements, got %d elements" , len (tupleVal ))
463+ }
464+
465+ var boundVal * big.Float
466+ err = tupleVal [0 ].As (& boundVal )
467+ if err != nil {
468+ return Value {}, path .NewErrorf ("failed to decode msgpack extension body: numeric bound refinement bound value conversion failed: %w" , err )
469+ }
470+
471+ var inclusiveVal bool
472+ err = tupleVal [1 ].As (& inclusiveVal )
473+ if err != nil {
474+ return Value {}, path .NewErrorf ("failed to decode msgpack extension body: numeric bound refinement inclusive value conversion failed: %w" , err )
475+ }
476+
477+ if keyCode == refinement .KeyNumberLowerBound {
478+ newRefinements [keyCode ] = refinement .NewNumberLowerBound (boundVal , inclusiveVal )
479+ } else {
480+ newRefinements [keyCode ] = refinement .NewNumberUpperBound (boundVal , inclusiveVal )
481+ }
444482 default :
445483 err := rfnDec .Skip ()
446484 if err != nil {
@@ -511,23 +549,95 @@ func marshalUnknownValue(val Value, typ Type, p *AttributePath, enc *msgpack.Enc
511549 refnEnc := msgpack .NewEncoder (& refnBuf )
512550 mapLen := 0
513551
514- // TODO: Should the refinement interface be defining the encoding? Should we export the refinement implementation details?
515- // - If we keep it in the interface, then we can simplify this logic
516- for kind , refn := range val .refinements {
517- switch kind {
518- case refinement .KeyNullness :
519- err := refn .Encode (refnEnc )
552+ for _ , refn := range val .refinements {
553+ switch refnVal := refn .(type ) {
554+ case refinement.Nullness :
555+ err := refnEnc .EncodeInt (int64 (refinement .KeyNullness ))
556+ if err != nil {
557+ return p .NewErrorf ("error encoding Nullness value refinement key: %w" , err )
558+ }
559+
560+ // It shouldn't be possible for an unknown value to be definitely null (i.e. nullness.value = true),
561+ // as that should be represented by a known null value instead. This encoding is in place to be compliant
562+ // with Terraform's encoding which uses a definitely null refinement to collapse into a known null value.
563+ err = refnEnc .EncodeBool (refnVal .Nullness ())
520564 if err != nil {
521565 return p .NewErrorf ("error encoding Nullness value refinement: %w" , err )
522566 }
567+
523568 mapLen ++
524- case refinement .KeyStringPrefix :
525- // TODO: If the prefix is empty, we shouldn't encode a refinement. This should
526- // probably be reflected in the interface.
527- err := refn .Encode (refnEnc )
569+ case refinement.StringPrefix :
570+ if rawPrefix := refnVal .PrefixValue (); rawPrefix != "" {
571+ // Matching go-cty for the max prefix length allowed here
572+ //
573+ // This ensures the total size of the refinements blob does not exceed the limit
574+ // set by the decoder (1024).
575+ maxPrefixLength := 256
576+ prefix := rawPrefix
577+ if len (rawPrefix ) > maxPrefixLength {
578+ prefix = prefix [:maxPrefixLength - 1 ]
579+ }
580+
581+ err := refnEnc .EncodeInt (int64 (refinement .KeyStringPrefix ))
582+ if err != nil {
583+ return p .NewErrorf ("error encoding StringPrefix value refinement key: %w" , err )
584+ }
585+
586+ err = refnEnc .EncodeString (prefix )
587+ if err != nil {
588+ return p .NewErrorf ("error encoding StringPrefix value refinement: %w" , err )
589+ }
590+
591+ mapLen ++
592+ }
593+
594+ case refinement.NumberLowerBound :
595+ // TODO: should check this isn't negative infinity? To match go-cty
596+ boundTfType := Tuple {ElementTypes : []Type {Number , Bool }}
597+
598+ // TODO: Do we need to do this? Kind of nasty
599+ boundTfVal := NewValue (
600+ boundTfType ,
601+ []Value {
602+ NewValue (Number , refnVal .LowerBound ()),
603+ NewValue (Bool , refnVal .IsInclusive ()),
604+ },
605+ )
606+
607+ err := refnEnc .EncodeInt (int64 (refinement .KeyNumberLowerBound ))
608+ if err != nil {
609+ return p .NewErrorf ("error encoding NumberLowerBound value refinement key: %w" , err )
610+ }
611+
612+ err = marshalMsgPack (boundTfVal , boundTfType , p , refnEnc )
613+ if err != nil {
614+ return p .NewErrorf ("error encoding NumberLowerBound value refinement: %w" , err )
615+ }
616+
617+ mapLen ++
618+ case refinement.NumberUpperBound :
619+ // TODO: should check this isn't positive infinity? To match go-cty
620+ boundTfType := Tuple {ElementTypes : []Type {Number , Bool }}
621+
622+ // TODO: Do we need to do this? Kind of nasty
623+ boundTfVal := NewValue (
624+ boundTfType ,
625+ []Value {
626+ NewValue (Number , refnVal .UpperBound ()),
627+ NewValue (Bool , refnVal .IsInclusive ()),
628+ },
629+ )
630+
631+ err := refnEnc .EncodeInt (int64 (refinement .KeyNumberUpperBound ))
528632 if err != nil {
529- return p .NewErrorf ("error encoding StringPrefix value refinement: %w" , err )
633+ return p .NewErrorf ("error encoding NumberUpperBound value refinement key : %w" , err )
530634 }
635+
636+ err = marshalMsgPack (boundTfVal , boundTfType , p , refnEnc )
637+ if err != nil {
638+ return p .NewErrorf ("error encoding NumberUpperBound value refinement: %w" , err )
639+ }
640+
531641 mapLen ++
532642 default :
533643 continue
0 commit comments