package net.cijber.pubgrub.version import net.cijber.pubgrub.SetRelation import net.cijber.pubgrub.stubs.Version open class VersionRange>( val bottom: VersionBorder?, val top: VersionBorder? ) : VersionConstraint { override val isEmpty: Boolean = false override val isAny: Boolean = bottom == null && top == null infix fun relation(rhs: VersionRange): SetRelation { val topDiff = when { top != null && rhs.top != null -> top.compareTo(rhs.top) top != null -> -1 rhs.top != null -> 1 else -> 0 } val bottomDiff = when { bottom != null && rhs.bottom != null -> bottom.compareTo(rhs.bottom) bottom != null -> 1 rhs.bottom != null -> -1 else -> 0 } if (topDiff == 0 && bottomDiff == 0) { return SetRelation.Equal } if (topDiff == 1 && bottomDiff == -1) { return SetRelation.Superset } if (topDiff <= 0 && bottomDiff >= 0) { return SetRelation.Subset } val topBottomDiff = when { top != null && rhs.bottom != null -> top.compareTo(rhs.bottom) else -> 1 } val bottomTopDiff = when { bottom != null && rhs.top != null -> bottom.compareTo(rhs.top) else -> -1 } if (bottomTopDiff == 1 || topBottomDiff == -1) { return SetRelation.Disjoint } return SetRelation.Overlaps } override fun allowsAll(rhs: VersionConstraint): Boolean { if (rhs.isEmpty) return true if (rhs.isAny) return false if (rhs is VersionRange) { return relation(rhs) in setOf(SetRelation.Equal, SetRelation.Superset) } if (rhs is VersionUnion) { return rhs.constraints.all(this::allowsAll) } error("Unknown VersionConstraint: $rhs") } override fun allowsAny(rhs: VersionConstraint): Boolean { if (rhs.isEmpty || rhs.isAny) return true if (rhs is VersionRange) { return relation(rhs) != SetRelation.Disjoint } if (rhs is VersionUnion) { return rhs.constraints.any(this::allowsAny) } error("Unknown VersionConstraint: $rhs") } override fun allows(version: V): Boolean { return (bottom?.beneath(version, true) ?: true) && (top?.above(version, true) ?: true) } override fun intersect(rhs: VersionConstraint): VersionConstraint { if (rhs.isEmpty) { return rhs } if (rhs.isAny) { return this } if (rhs is VersionUnion) { return rhs.intersect(this) } if (rhs is VersionRange) { if (relation(rhs) != SetRelation.Disjoint) { return VersionRange( when { rhs.bottom != null && bottom != null -> maxOf(rhs.bottom, bottom) else -> bottom ?: rhs.bottom }, when { rhs.top != null && top != null -> minOf(rhs.top, top) else -> top ?: rhs.top } ) } return VersionEmpty() } error("Unkown VersionConstraint: $rhs") } override fun union(rhs: VersionConstraint): VersionConstraint { if (rhs is VersionRange) { return when (relation(rhs)) { SetRelation.Superset, SetRelation.Equal -> this SetRelation.Subset -> rhs SetRelation.Overlaps -> VersionRange( when { rhs.bottom != null && bottom != null -> minOf(rhs.bottom, bottom) else -> null }, when { rhs.top != null && top != null -> maxOf(rhs.top, top) else -> null } ) SetRelation.Disjoint -> VersionUnion(listOf(this, rhs)) } } if (rhs is VersionEmpty) { return this } if (rhs is VersionUnion) { return VersionUnion(rhs.constraints + listOf(this)) } error("Unknown VersionConstraint given: $rhs") } override fun difference(rhs: VersionConstraint): VersionConstraint { if (rhs.isEmpty) { return this } if (rhs.isAny) { return VersionEmpty() } if (rhs is VersionUnion) { val ranges = mutableListOf(this) for (constraint in rhs.constraints) { val rel = constraint relation ranges.last() if (rel == SetRelation.Disjoint) { continue } val last = ranges.dropLast(1).first() val res = last.difference(constraint) if (res.isEmpty) { break } if (res is VersionUnion) { ranges.addAll(res.constraints) } if (res is VersionRange) { ranges.add(res) } } return when (ranges.count()) { 0 -> VersionEmpty() 1 -> ranges.first() else -> VersionUnion(ranges) } } if (rhs is VersionRange) { return when (rhs relation this) { SetRelation.Disjoint -> this SetRelation.Equal, SetRelation.Superset -> VersionEmpty() SetRelation.Overlaps -> VersionRange( when { bottom != null && rhs.bottom != null && rhs.bottom < bottom -> rhs.top else -> bottom }, when { top != null && rhs.top != null && rhs.top > top -> rhs.bottom else -> top } ) SetRelation.Subset -> { if (top == rhs.top) { return VersionRange(bottom, rhs.bottom?.reversed()) } if (bottom == rhs.bottom) { return VersionRange(rhs.top?.reversed(), top) } return VersionUnion( listOf( VersionRange(bottom, rhs.bottom?.reversed()), VersionRange(rhs.top?.reversed(), top) ) ) } } } error("Unknown VersionConstraint given: $rhs") } companion object { fun > single(version: V) = VersionRange(VersionBorder.bottom(version), VersionBorder.top(version)) } }