internal/skip/skip.go (33 lines of code) (raw):

package skip import ( "reflect" "slices" "strings" ) // CanSkipExternalRequest checks if the external request can be skipped based on the plan and state. // Two of the same objects are supplied as parameters, together with the operation that is being performed. // The function uses the `skip_on` struct tag to determine if the field should be skipped. // The value of the `skip_on` tag is a comma-separated list of operations that mean that changes to this field value do not require an external request and are in state only. // The function will return true if the external request can be skipped, false otherwise. func CanSkipExternalRequest[T any](a, b T, operation string) bool { valA := reflect.ValueOf(a) valB := reflect.ValueOf(b) // Since we are using generics, we know that the types of a and b are the same. // Therefore we can check the type of a to determine if it is a struct. if valA.Kind() != reflect.Struct { return false } typeOfA := valA.Type() // iterate over all fields of the struct for i := 0; i < typeOfA.NumField(); i++ { field := typeOfA.Field(i) // Check if the field has the skip_on tag // If it doesn't we need to compare the valued as we cannot determine if the field should be skipped. // If the field has the skip_on tag, we can check if the operation is in the list of operations that should be skipped. tag := field.Tag.Get("skip_on") if tag != "" { // Split the tag values by comma and check if the operation is in the list. // If the operation is in the list, then this field represents a change in state only // and does not require an external request to be made. // Therefore we can skip tp the next field. tagValues := strings.Split(tag, ",") if slices.Contains(tagValues, operation) { continue } } // If we get here then we need to compare the field values. // By now we have determined that the struct fields do not have a valid skip value for this operation. // Therefore if the field values are not equal, then the external request cannot be skipped. fieldA := valA.Field(i) fieldB := valB.Field(i) if !fieldA.IsValid() || !fieldB.IsValid() { return false } if !reflect.DeepEqual(fieldA.Interface(), fieldB.Interface()) { return false } } return true }