This is an automated email from the ASF dual-hosted git repository. arosien pushed a commit to branch issue/648/relay-parse-exception-to-console-outputevent in repository https://gitbox.apache.org/repos/asf/daffodil-vscode.git
commit 3b89dbf9658a7315cada1c3960cb2bcb8b45f858 Author: Adam Rosien <[email protected]> AuthorDate: Tue Jul 11 17:25:09 2023 -0700 Relay parse assertion failures to debug console. As part of previously-closed #648. --- .../org.apache.daffodil.debugger.dap/Parse.scala | 64 ++++++++++------------ 1 file changed, 30 insertions(+), 34 deletions(-) diff --git a/server/core/src/main/scala/org.apache.daffodil.debugger.dap/Parse.scala b/server/core/src/main/scala/org.apache.daffodil.debugger.dap/Parse.scala index 8ac32aa..bfdf5bc 100644 --- a/server/core/src/main/scala/org.apache.daffodil.debugger.dap/Parse.scala +++ b/server/core/src/main/scala/org.apache.daffodil.debugger.dap/Parse.scala @@ -32,7 +32,6 @@ import java.io._ import java.net.URI import java.nio.file._ import org.apache.commons.io.FileUtils -import org.apache.daffodil.api.Diagnostic import org.apache.daffodil.debugger.dap.{BuildInfo => DAPBuildInfo} import org.apache.daffodil.debugger.Debugger import org.apache.daffodil.exceptions.SchemaFileLocation @@ -40,7 +39,7 @@ import org.apache.daffodil.infoset._ import org.apache.daffodil.processors.dfa.DFADelimiter import org.apache.daffodil.processors.parsers._ import org.apache.daffodil.processors._ -import org.apache.daffodil.sapi.ValidationMode +import org.apache.daffodil.sapi.{Diagnostic, ValidationMode} import org.apache.daffodil.sapi.infoset.XMLTextInfosetOutputter import org.apache.daffodil.sapi.infoset.JsonInfosetOutputter import org.apache.daffodil.sapi.io.InputSourceDataInputStream @@ -50,9 +49,6 @@ import org.typelevel.log4cats.Logger import org.typelevel.log4cats.slf4j.Slf4jLogger import scala.collection.JavaConverters._ import scala.util.Try -import cats.effect.kernel.Resource.ExitCase.Errored -import cats.effect.kernel.Resource.ExitCase.Canceled -import cats.effect.kernel.Resource.ExitCase.Succeeded trait Parse { @@ -67,6 +63,13 @@ trait Parse { object Parse { implicit val logger: Logger[IO] = Slf4jLogger.getLogger + case class Exception(diagnostics: List[Diagnostic]) + extends RuntimeException( + diagnostics + .map(d => d.toString) + .mkString("\n") + ) + def apply( schema: Path, data: InputStream, @@ -99,25 +102,17 @@ object Parse { } val parse = - IO.interruptibleMany { - val parse_res = dp.parse( + IO.interruptibleMany( + dp.parse( new InputSourceDataInputStream(data), infosetOutputter ) - - parse_res.isError match { - case true => - throw new Error( - parse_res.getDiagnostics - .map(d => d.toString) - .mkString("\n") - ) - case _ => parse_res - } // WARNING: parse doesn't close the OutputStream, so closed below - }.void + ).ensureOr(res => new Parse.Exception(res.getDiagnostics.toList))(res => !res.isError) + .guarantee(IO(os.close) *> done.set(true)) + .void - stopper &> parse.guarantee(IO(os.close) *> done.set(true)) + stopper &> parse } def close(): IO[Unit] = @@ -691,9 +686,9 @@ object Parse { .run() .through(text.utf8.decode) .foldMonoid - .evalTap(_ => Logger[IO].debug("done collecting infoset XML output")) - .map(infosetXML => Some(Events.OutputEvent.createConsoleOutput(infosetXML))) - .enqueueUnterminated(dapEvents) // later handling will terminate dapEvents + .evalTap(xml => + Logger[IO].debug("done collecting infoset XML output") *> + dapEvents.offer(Some(Events.OutputEvent.createConsoleOutput(xml)))) case Debugee.LaunchArgs.InfosetOutput.File(path) => parse .run() @@ -744,20 +739,21 @@ object Parse { .concurrently( Stream( Stream.eval(startup), - parsing.onFinalizeCase(ec => - Logger[IO].debug(s"parsing: ${ec match { - case Errored(_) => "Errored" - case Canceled => "Canceled" - case Succeeded => "Succeeded" - }}") - ), + // ensure dapEvents is terminated when the parse is terminated + parsing + .onFinalizeCase { + case Resource.ExitCase.Errored(e: Parse.Exception) => + // TODO: when Parse.Exception has source coordinates, include it into a more structured OutputEvent + dapEvents.offer(Some(Events.OutputEvent.createConsoleOutput(e.getMessage()))) *> + dapEvents.offer(None) + case _ => dapEvents.offer(None) + } + .onFinalizeCase(ec => Logger[IO].debug(s"parsing: $ec")), deliverParseData.onFinalizeCase { case ec @ Resource.ExitCase.Errored(e) => Logger[IO].warn(e)(s"deliverParseData: $ec") case ec => Logger[IO].debug(s"deliverParseData: $ec") - } ++ Stream.eval( - dapEvents.offer(None) // ensure dapEvents is terminated when the parse is terminated - ), + }, infosetChanges ).parJoinUnbounded ) @@ -989,7 +985,7 @@ object Parse { schemaLocation: SchemaFileLocation, pointsOfUncertainty: List[PointOfUncertainty], delimiterStack: List[Delimiter], - diagnostics: List[Diagnostic] + diagnostics: List[org.apache.daffodil.api.Diagnostic] ) extends Event { // PState is mutable, so we copy all the information we might need downstream. def this(pstate: PState, isStepping: Boolean) = @@ -1244,7 +1240,7 @@ object Parse { events.offer(None) *> // no more events after this state.offer(None) *> // no more states == the parse is terminated infoset.offer(None) *> - Logger[IO].debug("infoset queue completed") + Logger[IO].debug("Debugger fini event: completed parse") } override def startElement(pstate: PState, processor: Parser): Unit =
