// ReSharper disable ClassNeverInstantiated.Global namespace TeamCity.CSharpInteractive; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Scripting; using Microsoft.CodeAnalysis.Scripting; [ExcludeFromCodeCoverage] internal class CSharpScriptRunner : ICSharpScriptRunner { private readonly ILog _log; private readonly IPresenter> _scriptStatePresenter; private readonly IPresenter _diagnosticsPresenter; private readonly IReadOnlyCollection _scriptOptionsFactories; private readonly IExitCodeParser _exitCodeParser; private ScriptState? _scriptState; public CSharpScriptRunner( ILog log, IPresenter> scriptStatePresenter, IPresenter diagnosticsPresenter, IReadOnlyCollection scriptOptionsFactories, IExitCodeParser exitCodeParser) { _log = log; _scriptStatePresenter = scriptStatePresenter; _diagnosticsPresenter = diagnosticsPresenter; _scriptOptionsFactories = scriptOptionsFactories; _exitCodeParser = exitCodeParser; } public CommandResult Run(ICommand sourceCommand, string script) { var success = true; try { var options = _scriptOptionsFactories.Aggregate(ScriptOptions.Default, (current, scriptOptionsFactory) => scriptOptionsFactory.Create(current)); var stopwatch = new Stopwatch(); stopwatch.Start(); _scriptState = (_scriptState ?? CSharpScript.RunAsync(string.Empty, options).Result) .ContinueWithAsync( script, options, exception => { success = false; _log.Trace(() => new[] {new Text($"Exception: {exception}.")}); _log.Error(ErrorId.Exception, new[] {new Text(exception.ToString())}); return true; }) .Result; stopwatch.Stop(); _log.Trace(() => new[] {new Text($"Time Elapsed {stopwatch.Elapsed:g}")}); _diagnosticsPresenter.Show(new CompilationDiagnostics(sourceCommand, _scriptState.Script.GetCompilation().GetDiagnostics().ToList().AsReadOnly())); if (_scriptState.ReturnValue != default) { if (success && _exitCodeParser.TryParse(_scriptState.ReturnValue, out var exitCode)) { return new CommandResult(sourceCommand, success, exitCode); } _log.Trace(() => new[] {new Text("The return value is \""), new Text(_scriptState.ReturnValue.ToString() ?? "empty"), new Text("\".")}); } else { _log.Trace(() => new[] {new Text("The return value is \"null\".")}); } } catch (CompilationErrorException e) { _diagnosticsPresenter.Show(new CompilationDiagnostics(sourceCommand, e.Diagnostics.ToList().AsReadOnly())); success = false; } finally { if (_scriptState != null) { _scriptStatePresenter.Show(_scriptState); } } return new CommandResult(sourceCommand, success); } public void Reset() { _log.Trace(() => new[] {new Text("Reset state.")}); _scriptState = default; } }