Classes/OperationGroup.ps1

#Requires -Version 5.0

Enum OperationGroupType {
    Conjunctive = 1
    Disjunctive = 2
}

<#
    This class bundles operations into a single group.
    Within an operation group at most the first operation can be non failover.
#>

class OperationGroup {
    # Operations included in the group.
    hidden [Operation[]] $Operations
    # Defines if the group is conjunctive or disjunctive
    hidden [OperationGroupType] $Type
    # Defines if the group is failover or not
    hidden [bool] $Failover

    [Operation[]] GetOperations() {
        return $this.Operations
    }

    [OperationGroupType] GetType() {
        return $this.Type
    }

    [boolean] IsFailover() {
        return $this.Failover
    }

    [boolean] Equals($obj) {
        #Check for null and compare run-time types.
        if (($null -eq $obj) -or -not $this.GetType().Equals($obj.GetType())) {
            return $false;
        }
        else {
            $other = $obj -as [OperationGroup]

            $isEqual = ($this.Type -eq $other.Type) -and ($this.Failover -eq $other.Failover) -and ($this.Operations.Length -eq $other.Operations.Length)
            
            for ([int] $i = 0; $i -lt $this.Operations.Length -and $isEqual; $i++) {
                $isEqual = $this.Operations[$i] -eq $other.Operations[$i]
            }

            return $isEqual
        }
    }

    [string] ToString() {
        return "OperationGroup(failover={0},type={1},operations=[{2}])" -f $this.IsFailover(), $this.GetType(), [system.String]::Join(",", $this.Operations)
    }

    # Generates paths from this group.
    # The first item indicates failover characteristic.
    [Tuple[bool, Operation[]][]] GetPaths() {
        # A) Conjunctive => Execute all
        if ($this.GetType() -eq [OperationGroupType]::Conjunctive) {
            if ($this.IsFailover()) {
                $res = [Tuple[bool, Operation[]][]]::new(2)
                # If this operation group is a failover group, there has to be an evaluation without evaluation of any contained operations.
                $res[0] = [Tuple[bool, Operation[]]]::new(
                    $false,
                    @(
                        [NoOp]::new()
                    )
                )
            }
            else {
                $res = [Tuple[bool, Operation[]][]]::new(1)
            }

            # Chain operations
            $res[$res.Length - 1] = [Tuple[bool, Operation[]]]::new(
                $this.IsFailover(),
                $this.GetOperations()
            )
        }
        # B) Disjunctive => Execute one
        elseif ($this.GetType() -eq [OperationGroupType]::Disjunctive) {
            if ($this.IsFailover()) {
                $res = [Tuple[bool, Operation[]][]]::new($this.Operations.Length + 1)
                # If this operation group is a failover group, there has to be an evaluation without evaluation of any contained operations.
                $res[0] = [Tuple[bool, Operation[]]]::new(
                    $false,
                    @(
                        [NoOp]::new()
                    )
                )
            }
            else {
                $res = [Tuple[bool, Operation[]][]]::new($this.Operations.Length)
            }

            # Split operations
            for ([int] $i = 0; $i -lt $this.Operations.Length; $i++) {
                $res[$res.Length - 1 - $i] = [Tuple[bool, Operation[]]]::new(
                    $this.IsFailover(),
                    $this.GetOperations()[$this.Operations.Length - 1 - $i]
                )
            }
        }
        else {
            throw [System.NotImplementedException]::new($this.GetType())
        }
        
        return $res
    }

    OperationGroup([Operation[]] $operations, [boolean] $failover, [OperationGroupType] $type) {
        $this.Operations = $operations
        $this.Failover = $failover
        $this.Type = $type
    }

    OperationGroup([Operation[]] $operations, [boolean] $failover) {
        $this.Operations = $operations
        $this.Failover = $failover
        $this.Type = [OperationGroupType]::Conjunctive
    }

    OperationGroup([Operation[]] $operations) {
        $this.Operations = $operations
        $this.Failover = $false
        $this.Type = [OperationGroupType]::Conjunctive
    }
}