From bc4dfc70ff19afb18f2550396fcc865cd9518b15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Bia=C5=82y?= Date: Fri, 24 May 2024 14:56:07 +0200 Subject: [PATCH] fix issue 488 --- .../main/scala/besom/internal/Resource.scala | 4 + .../scala/besom/internal/ResourceState.scala | 6 +- .../main/scala/besom/internal/resources.scala | 73 +++++++++++-------- 3 files changed, 53 insertions(+), 30 deletions(-) diff --git a/core/src/main/scala/besom/internal/Resource.scala b/core/src/main/scala/besom/internal/Resource.scala index d76cd97f..a35a9ecd 100644 --- a/core/src/main/scala/besom/internal/Resource.scala +++ b/core/src/main/scala/besom/internal/Resource.scala @@ -24,6 +24,8 @@ sealed trait Resource: case _: CustomResource => true case _ => false + // this method can lead to deadlocks as it waits for URN to be resolved, use carefully + // resources can be visually compared by references using toString() if URN is not strictly required private[internal] def asString: Result[Option[String]] = urn.getValue.map(_.map(v => s"${this.getClass.getSimpleName}($v)")) trait CustomResource extends Resource: @@ -45,6 +47,8 @@ trait ComponentResource(using */ override def urn: Output[URN] = base.urn + private[besom] def componentBase: ComponentBase = base + trait ProviderResource extends CustomResource: private[internal] def registrationId: Result[String] = for diff --git a/core/src/main/scala/besom/internal/ResourceState.scala b/core/src/main/scala/besom/internal/ResourceState.scala index 51417398..a67c9bf1 100644 --- a/core/src/main/scala/besom/internal/ResourceState.scala +++ b/core/src/main/scala/besom/internal/ResourceState.scala @@ -36,21 +36,25 @@ case class CommonResourceState( typ: ResourceType, // transformations: List[ResourceTransformation], keepDependency: Boolean -) +): + override def toString(): String = "CommonResourceState" case class CustomResourceState( common: CommonResourceState, id: Output[ResourceId] ) extends ResourceState: export common.* + override def toString(): String = "CustomResourceState" case class ProviderResourceState( custom: CustomResourceState, pkg: String ) extends ResourceState: export custom.* + override def toString(): String = "ProviderResourceState" case class ComponentResourceState( common: CommonResourceState ) extends ResourceState: export common.* + override def toString(): String = "ComponentResourceState" diff --git a/core/src/main/scala/besom/internal/resources.scala b/core/src/main/scala/besom/internal/resources.scala index 4aac591a..b1d2b197 100644 --- a/core/src/main/scala/besom/internal/resources.scala +++ b/core/src/main/scala/besom/internal/resources.scala @@ -1,5 +1,23 @@ package besom.internal +class ResourceStateMismatchException(msg: String) extends Exception(msg) +object ResourceStateMismatchException: + def fail(r: Resource, state: ResourceState, expected: String)(using dbg: Debug): Result[Nothing] = + (for + rstr <- r.asString + msg = s"state for resource $r / ${rstr.getOrElse("???")} is $state, expected $expected, caller: $dbg" + yield new ResourceStateMismatchException(msg)).flatMap(e => Result.fail(e)) + +class ResourceStateMissingException(msg: String) extends Exception(msg) +object ResourceStateMissingException: + inline private def nl = System.lineSeparator + def fail(r: Resource, rs: Map[Resource, ResourceState])(using dbg: Debug): Result[Nothing] = + (for + rstr <- r.asString + msg = s"state for resource $r / ${rstr.getOrElse("???")} not found$nl - caller: $dbg$nl - state available for resources:$nl${rs.keys + .mkString(" * ", nl + " * ", "")}" + yield new ResourceStateMissingException(msg)).flatMap(e => Result.fail(e)) + class Resources private (private val resources: Ref[Map[Resource, ResourceState]]): def add(resource: ProviderResource, state: ProviderResourceState): Result[Unit] = resources.update(_ + (resource -> state)) @@ -25,56 +43,53 @@ class Resources private (private val resources: Ref[Map[Resource, ResourceState] case _ => resource.asString.flatMap(s => Result.fail(Exception(s"resource ${s} and state ${state} don't match"))) - def getStateFor(resource: ProviderResource): Result[ProviderResourceState] = - resources.get.flatMap { - _.get(resource) match + def getStateFor(resource: ProviderResource)(using Debug): Result[ProviderResourceState] = + resources.get.flatMap { rs => + rs.get(resource) match case Some(state) => state match - case _: CustomResourceState => - resource.asString.flatMap(s => Result.fail(Exception(s"state for ProviderResource ${s} is a CustomResourceState!"))) case prs: ProviderResourceState => Result.pure(prs) - case _: ComponentResourceState => - resource.asString.flatMap(s => Result.fail(Exception(s"state for ProviderResource ${s} is a ComponentResourceState!"))) + case _ => ResourceStateMismatchException.fail(resource, state, "ProviderResourceState") case None => - resource.asString.flatMap(s => Result.fail(Exception(s"state for resource ${s} not found"))) + ResourceStateMissingException.fail(resource, rs) } - def getStateFor(resource: CustomResource): Result[CustomResourceState] = - resources.get.flatMap { - _.get(resource) match + def getStateFor(resource: CustomResource)(using Debug): Result[CustomResourceState] = + resources.get.flatMap { rs => + rs.get(resource) match case Some(state) => state match case crs: CustomResourceState => Result.pure(crs) - case _: ProviderResourceState => - resource.asString.flatMap(s => Result.fail(Exception(s"state for CustomResource ${s} is a ProviderResourceState!"))) - case _: ComponentResourceState => - resource.asString.flatMap(s => Result.fail(Exception(s"state for CustomResource ${s} is a ComponentResourceState!"))) + case _ => ResourceStateMismatchException.fail(resource, state, "CustomResourceState") case None => - resource.asString.flatMap(s => Result.fail(Exception(s"state for resource ${s} not found"))) + ResourceStateMissingException.fail(resource, rs) } - def getStateFor(resource: ComponentResource): Result[ComponentResourceState] = - resources.get.flatMap { - _.get(resource) match + def getStateFor(resource: ComponentResource)(using Debug): Result[ComponentResourceState] = + resources.get.flatMap { rs => + rs.get(resource.componentBase) match case Some(state) => state match - case _: CustomResourceState => - resource.asString.flatMap(s => Result.fail(Exception(s"state for ComponentResource ${s} is a CustomResourceState!"))) - case _: ProviderResourceState => - resource.asString.flatMap(s => Result.fail(Exception(s"state for ComponentResource ${s} is a ProviderResourceState!"))) case comprs: ComponentResourceState => Result.pure(comprs) + case _ => ResourceStateMismatchException.fail(resource, state, "ComponentResourceState") case None => - resource.asString.flatMap(s => Result.fail(Exception(s"state for resource ${s} not found"))) + ResourceStateMissingException.fail(resource, rs) } - def getStateFor(resource: Resource): Result[ResourceState] = - resources.get.flatMap { - _.get(resource) match - case Some(state) => Result.pure(state) - case None => resource.asString.flatMap(s => Result.fail(Exception(s"state for resource ${s} not found"))) + def getStateFor(resource: Resource)(using dbg: Debug): Result[ResourceState] = + resources.get.flatMap { rs => + resource match + case compr: ComponentResource => + rs.get(compr.componentBase) match + case Some(state) => Result.pure(state) + case None => ResourceStateMissingException.fail(resource, rs) + case _ => + rs.get(resource) match + case Some(state) => Result.pure(state) + case None => ResourceStateMissingException.fail(resource, rs) } def updateStateFor(resource: Resource)(f: ResourceState => ResourceState): Result[Unit] =