Initial commit: Nix WSL Wrapper project with DDD, Clean Code, and tests
This commit is contained in:
@@ -0,0 +1,193 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using NixWslWrapper.Core.Domain;
|
||||
using NixWslWrapper.Core.Interfaces;
|
||||
using Spectre.Console;
|
||||
|
||||
namespace NixWslWrapper.Cli
|
||||
{
|
||||
public class CliController
|
||||
{
|
||||
private readonly ICommandExecutor _commandExecutor;
|
||||
|
||||
public CliController(ICommandExecutor commandExecutor)
|
||||
{
|
||||
_commandExecutor = commandExecutor ?? throw new ArgumentNullException(nameof(commandExecutor));
|
||||
}
|
||||
|
||||
public int Run(string[] args)
|
||||
{
|
||||
// Intercept special configuration/status options
|
||||
if (args.Contains("--wsl-status", StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
RenderStatusDashboard();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (args.Contains("--wsl-help", StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
RenderHelp();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Get configuration from environment variables or use defaults
|
||||
string distro = Environment.GetEnvironmentVariable("NIX_WSL_DISTRO") ?? "Debian";
|
||||
string user = Environment.GetEnvironmentVariable("NIX_WSL_USER") ?? "root";
|
||||
string profile = Environment.GetEnvironmentVariable("NIX_WSL_PROFILE") ?? "/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh";
|
||||
|
||||
WslTarget target;
|
||||
try
|
||||
{
|
||||
target = new WslTarget(distro, user, profile);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AnsiConsole.MarkupLine($"[red]Configuration Error:[/] {ex.Message}");
|
||||
return 1;
|
||||
}
|
||||
|
||||
var nixArgs = new NixArguments(args);
|
||||
var result = _commandExecutor.Execute(nixArgs, target);
|
||||
|
||||
if (!result.IsSuccess)
|
||||
{
|
||||
if (result.ErrorMessage != null)
|
||||
{
|
||||
AnsiConsole.MarkupLine($"[bold red]Execution Error:[/] {result.ErrorMessage}");
|
||||
}
|
||||
return result.ExitCode;
|
||||
}
|
||||
|
||||
return result.ExitCode;
|
||||
}
|
||||
|
||||
private static void RenderStatusDashboard()
|
||||
{
|
||||
AnsiConsole.Write(
|
||||
new FigletText("Nix WSL Wrapper")
|
||||
.Color(Color.DeepSkyBlue1));
|
||||
|
||||
var panel = new Panel(
|
||||
new Markup(
|
||||
"[bold]Nix WSL Wrapper Diagnostic Dashboard[/]\n" +
|
||||
"Wraps native Windows MCP / CLI tool inputs and redirects them into Nix running inside WSL.\n\n" +
|
||||
$"[grey]Generated at: {DateTime.Now:yyyy-MM-dd HH:mm:ss}[/]"
|
||||
))
|
||||
{
|
||||
Border = BoxBorder.Rounded,
|
||||
Padding = new Padding(1, 1, 1, 1),
|
||||
Header = new PanelHeader("System Info")
|
||||
};
|
||||
|
||||
AnsiConsole.Write(panel);
|
||||
|
||||
// Fetch Environment details
|
||||
string distro = Environment.GetEnvironmentVariable("NIX_WSL_DISTRO") ?? "Debian (Default)";
|
||||
string user = Environment.GetEnvironmentVariable("NIX_WSL_USER") ?? "root (Default)";
|
||||
string profile = Environment.GetEnvironmentVariable("NIX_WSL_PROFILE") ?? "/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh (Default)";
|
||||
|
||||
var table = new Table()
|
||||
.Border(TableBorder.Rounded)
|
||||
.BorderColor(Color.Grey)
|
||||
.AddColumn("[bold]Setting[/]")
|
||||
.AddColumn("[bold]Value[/]")
|
||||
.AddColumn("[bold]Source[/]");
|
||||
|
||||
table.AddRow("WSL Target Distro", distro, Environment.GetEnvironmentVariable("NIX_WSL_DISTRO") != null ? "[green]Env Var[/]" : "[yellow]Fallback[/]");
|
||||
table.AddRow("WSL Execution User", user, Environment.GetEnvironmentVariable("NIX_WSL_USER") != null ? "[green]Env Var[/]" : "[yellow]Fallback[/]");
|
||||
table.AddRow("Nix Profile Path", profile, Environment.GetEnvironmentVariable("NIX_WSL_PROFILE") != null ? "[green]Env Var[/]" : "[yellow]Fallback[/]");
|
||||
|
||||
AnsiConsole.Write(new Rule("[yellow]Configured Parameters[/]") { Justification = Justify.Left });
|
||||
AnsiConsole.Write(table);
|
||||
|
||||
// Run WSL check
|
||||
AnsiConsole.Write(new Rule("[yellow]WSL Integration Status[/]") { Justification = Justify.Left });
|
||||
|
||||
AnsiConsole.Status()
|
||||
.Spinner(Spinner.Known.Dots)
|
||||
.Start("Verifying WSL and Nix installation...", ctx =>
|
||||
{
|
||||
bool wslOk = false;
|
||||
string wslVersion = "Unknown";
|
||||
try
|
||||
{
|
||||
var psi = new ProcessStartInfo("wsl.exe", "--status")
|
||||
{
|
||||
RedirectStandardOutput = true,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true
|
||||
};
|
||||
using var p = Process.Start(psi);
|
||||
p?.WaitForExit(3000);
|
||||
wslOk = p?.ExitCode == 0;
|
||||
wslVersion = wslOk ? "WSL Active" : "WSL Error";
|
||||
}
|
||||
catch
|
||||
{
|
||||
wslVersion = "wsl.exe not found in PATH";
|
||||
}
|
||||
|
||||
ctx.Status("Checking Nix inside WSL Distro...");
|
||||
bool nixOk = false;
|
||||
string nixVersion = "Not Found";
|
||||
if (wslOk)
|
||||
{
|
||||
try
|
||||
{
|
||||
string targetDistro = Environment.GetEnvironmentVariable("NIX_WSL_DISTRO") ?? "Debian";
|
||||
string targetUser = Environment.GetEnvironmentVariable("NIX_WSL_USER") ?? "root";
|
||||
string targetProfile = Environment.GetEnvironmentVariable("NIX_WSL_PROFILE") ?? "/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh";
|
||||
|
||||
var psi = new ProcessStartInfo("wsl.exe", $"-d {targetDistro} -u {targetUser} -- sh -c \". {targetProfile} && nix --version\"")
|
||||
{
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true
|
||||
};
|
||||
using var p = Process.Start(psi);
|
||||
p?.WaitForExit(5000);
|
||||
if (p?.ExitCode == 0)
|
||||
{
|
||||
nixOk = true;
|
||||
nixVersion = p.StandardOutput.ReadToEnd().Trim();
|
||||
}
|
||||
else
|
||||
{
|
||||
nixVersion = "Nix execution failed inside WSL";
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
nixVersion = $"Error querying: {ex.Message}";
|
||||
}
|
||||
}
|
||||
|
||||
// Render Checks
|
||||
var grid = new Grid().AddColumn().AddColumn();
|
||||
grid.AddRow(new Markup("wsl.exe Executable Status:"), wslOk ? new Markup("[green]✔ OK[/]") : new Markup($"[red]✘ FAILED ({wslVersion})[/]"));
|
||||
grid.AddRow(new Markup("Nix Engine in WSL Distro:"), nixOk ? new Markup($"[green]✔ OK ({nixVersion})[/]") : new Markup($"[red]✘ FAILED ({nixVersion})[/]"));
|
||||
|
||||
AnsiConsole.Write(new Panel(grid) { Border = BoxBorder.None });
|
||||
});
|
||||
}
|
||||
|
||||
private static void RenderHelp()
|
||||
{
|
||||
AnsiConsole.Write(
|
||||
new FigletText("Nix WSL Wrapper")
|
||||
.Color(Color.DeepSkyBlue1));
|
||||
|
||||
AnsiConsole.MarkupLine("[bold]Usage:[/] nix.exe [[nix-arguments-to-forward]]");
|
||||
AnsiConsole.MarkupLine("[bold]Alternative flags (Wrapper specific):[/]");
|
||||
AnsiConsole.MarkupLine(" [yellow]--wsl-status[/] Displays diagnostic status of WSL and Nix integration.");
|
||||
AnsiConsole.MarkupLine(" [yellow]--wsl-help[/] Displays this help menu.");
|
||||
AnsiConsole.WriteLine();
|
||||
AnsiConsole.MarkupLine("[bold]Configuration Environment Variables:[/]");
|
||||
AnsiConsole.MarkupLine(" [green]NIX_WSL_DISTRO[/] The target WSL distribution name (default: [grey]Debian[/]).");
|
||||
AnsiConsole.MarkupLine(" [green]NIX_WSL_USER[/] The user to execute commands under (default: [grey]root[/]).");
|
||||
AnsiConsole.MarkupLine(" [green]NIX_WSL_PROFILE[/] The path to the nix-daemon profile script (default: [grey]/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh[/]).");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\NixWslWrapper.Infrastructure\NixWslWrapper.Infrastructure.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.9" />
|
||||
<PackageReference Include="Spectre.Console" Version="0.57.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NixWslWrapper.Core.Interfaces;
|
||||
using NixWslWrapper.Infrastructure.IO;
|
||||
using NixWslWrapper.Infrastructure.Executors;
|
||||
|
||||
namespace NixWslWrapper.Cli
|
||||
{
|
||||
internal class Program
|
||||
{
|
||||
private static int Main(string[] args)
|
||||
{
|
||||
var serviceProvider = ConfigureServices();
|
||||
var controller = serviceProvider.GetRequiredService<CliController>();
|
||||
return controller.Run(args);
|
||||
}
|
||||
|
||||
private static IServiceProvider ConfigureServices()
|
||||
{
|
||||
return new ServiceCollection()
|
||||
.AddSingleton<IStreamCopier, ThreadedStreamCopier>()
|
||||
.AddSingleton<ICommandExecutor, WslCommandExecutor>()
|
||||
.AddSingleton<CliController>()
|
||||
.BuildServiceProvider();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
|
||||
namespace NixWslWrapper.Core.Domain
|
||||
{
|
||||
public class ExecutionResult
|
||||
{
|
||||
public int ExitCode { get; }
|
||||
public bool IsSuccess => ExitCode == 0;
|
||||
public string? ErrorMessage { get; }
|
||||
|
||||
public ExecutionResult(int exitCode, string? errorMessage = null)
|
||||
{
|
||||
ExitCode = exitCode;
|
||||
ErrorMessage = errorMessage;
|
||||
}
|
||||
|
||||
public static ExecutionResult Success() => new ExecutionResult(0);
|
||||
public static ExecutionResult Failure(int exitCode, string errorMessage) => new ExecutionResult(exitCode, errorMessage);
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if (obj is not ExecutionResult other) return false;
|
||||
return ExitCode == other.ExitCode && string.Equals(ErrorMessage, other.ErrorMessage, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(ExitCode, ErrorMessage);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return IsSuccess ? "Success" : $"Failed with Exit Code {ExitCode}. Error: {ErrorMessage}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace NixWslWrapper.Core.Domain
|
||||
{
|
||||
public class NixArguments
|
||||
{
|
||||
public string[] RawArguments { get; }
|
||||
|
||||
public NixArguments(string[]? rawArguments)
|
||||
{
|
||||
RawArguments = rawArguments ?? Array.Empty<string>();
|
||||
}
|
||||
|
||||
public string ToEscapedString()
|
||||
{
|
||||
if (RawArguments.Length == 0) return string.Empty;
|
||||
|
||||
var escapedList = new List<string>();
|
||||
foreach (var arg in RawArguments)
|
||||
{
|
||||
// Escape existing double quotes
|
||||
string escaped = arg.Replace("\"", "\\\"");
|
||||
// Wrap in double quotes to prevent word splitting in bash
|
||||
escapedList.Add($"\"{escaped}\"");
|
||||
}
|
||||
|
||||
return string.Join(" ", escapedList);
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if (obj is not NixArguments other) return false;
|
||||
if (RawArguments.Length != other.RawArguments.Length) return false;
|
||||
return RawArguments.SequenceEqual(other.RawArguments);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
int hash = 17;
|
||||
foreach (var arg in RawArguments)
|
||||
{
|
||||
hash = hash * 23 + (arg?.GetHashCode() ?? 0);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Join(" ", RawArguments);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
|
||||
namespace NixWslWrapper.Core.Domain
|
||||
{
|
||||
public class WslTarget
|
||||
{
|
||||
public string Distribution { get; }
|
||||
public string User { get; }
|
||||
public string NixProfilePath { get; }
|
||||
|
||||
public WslTarget(string distribution, string user, string nixProfilePath = "/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh")
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(distribution))
|
||||
throw new ArgumentException("WSL Distribution name cannot be null or empty.", nameof(distribution));
|
||||
if (string.IsNullOrWhiteSpace(user))
|
||||
throw new ArgumentException("WSL User cannot be null or empty.", nameof(user));
|
||||
if (string.IsNullOrWhiteSpace(nixProfilePath))
|
||||
throw new ArgumentException("Nix Profile Path cannot be null or empty.", nameof(nixProfilePath));
|
||||
|
||||
Distribution = distribution.Trim();
|
||||
User = user.Trim();
|
||||
NixProfilePath = nixProfilePath.Trim();
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if (obj is not WslTarget other) return false;
|
||||
return string.Equals(Distribution, other.Distribution, StringComparison.OrdinalIgnoreCase) &&
|
||||
string.Equals(User, other.User, StringComparison.OrdinalIgnoreCase) &&
|
||||
string.Equals(NixProfilePath, other.NixProfilePath, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(
|
||||
Distribution.ToLowerOrdinal(),
|
||||
User.ToLowerOrdinal(),
|
||||
NixProfilePath
|
||||
);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"WSL Distro: {Distribution}, User: {User}, Profile: {NixProfilePath}";
|
||||
}
|
||||
}
|
||||
|
||||
internal static class StringExtensions
|
||||
{
|
||||
public static string ToLowerOrdinal(this string str) => str.ToLowerInvariant();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using NixWslWrapper.Core.Domain;
|
||||
|
||||
namespace NixWslWrapper.Core.Interfaces
|
||||
{
|
||||
public interface ICommandExecutor
|
||||
{
|
||||
ExecutionResult Execute(NixArguments arguments, WslTarget target);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using System.IO;
|
||||
|
||||
namespace NixWslWrapper.Core.Interfaces
|
||||
{
|
||||
public interface IStreamCopier
|
||||
{
|
||||
void Copy(Stream input, Stream output);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,61 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace NixWslWrapper.Infrastructure.Executors
|
||||
{
|
||||
public class ProcessBuilder
|
||||
{
|
||||
private readonly ProcessStartInfo _startInfo = new();
|
||||
|
||||
public ProcessBuilder SetFileName(string fileName)
|
||||
{
|
||||
_startInfo.FileName = fileName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ProcessBuilder SetArguments(string arguments)
|
||||
{
|
||||
_startInfo.Arguments = arguments;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ProcessBuilder RedirectInput(bool redirect = true)
|
||||
{
|
||||
_startInfo.RedirectStandardInput = redirect;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ProcessBuilder RedirectOutput(bool redirect = true)
|
||||
{
|
||||
_startInfo.RedirectStandardOutput = redirect;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ProcessBuilder RedirectError(bool redirect = true)
|
||||
{
|
||||
_startInfo.RedirectStandardError = redirect;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ProcessBuilder UseShellExecute(bool useShell = false)
|
||||
{
|
||||
_startInfo.UseShellExecute = useShell;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ProcessBuilder CreateNoWindow(bool noWindow = true)
|
||||
{
|
||||
_startInfo.CreateNoWindow = noWindow;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ProcessStartInfo Build()
|
||||
{
|
||||
return _startInfo;
|
||||
}
|
||||
|
||||
public Process CreateProcess()
|
||||
{
|
||||
return new Process { StartInfo = _startInfo };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using NixWslWrapper.Core.Domain;
|
||||
using NixWslWrapper.Core.Interfaces;
|
||||
|
||||
namespace NixWslWrapper.Infrastructure.Executors
|
||||
{
|
||||
public class WslCommandExecutor : ICommandExecutor
|
||||
{
|
||||
private readonly IStreamCopier _streamCopier;
|
||||
private readonly Stream _hostIn;
|
||||
private readonly Stream _hostOut;
|
||||
private readonly Stream _hostErr;
|
||||
|
||||
public WslCommandExecutor(
|
||||
IStreamCopier streamCopier,
|
||||
Stream? hostIn = null,
|
||||
Stream? hostOut = null,
|
||||
Stream? hostErr = null)
|
||||
{
|
||||
_streamCopier = streamCopier ?? throw new ArgumentNullException(nameof(streamCopier));
|
||||
_hostIn = hostIn ?? Console.OpenStandardInput();
|
||||
_hostOut = hostOut ?? Console.OpenStandardOutput();
|
||||
_hostErr = hostErr ?? Console.OpenStandardError();
|
||||
}
|
||||
|
||||
public ExecutionResult Execute(NixArguments arguments, WslTarget target)
|
||||
{
|
||||
if (arguments == null) throw new ArgumentNullException(nameof(arguments));
|
||||
if (target == null) throw new ArgumentNullException(nameof(target));
|
||||
|
||||
string wslCmd = $". {target.NixProfilePath} && nix {arguments.ToEscapedString()}";
|
||||
string wslArgs = $"-d {target.Distribution} -u {target.User} -- sh -c \"{wslCmd.Replace("\"", "\\\"")}\"";
|
||||
|
||||
var processBuilder = new ProcessBuilder()
|
||||
.SetFileName("wsl.exe")
|
||||
.SetArguments(wslArgs)
|
||||
.RedirectInput(true)
|
||||
.RedirectOutput(true)
|
||||
.RedirectError(true)
|
||||
.UseShellExecute(false)
|
||||
.CreateNoWindow(true);
|
||||
|
||||
using (Process process = processBuilder.CreateProcess())
|
||||
{
|
||||
try
|
||||
{
|
||||
process.Start();
|
||||
|
||||
// Standard streams copy tasks
|
||||
Thread tOut = new(() => _streamCopier.Copy(process.StandardOutput.BaseStream, _hostOut))
|
||||
{
|
||||
IsBackground = true
|
||||
};
|
||||
Thread tErr = new(() => _streamCopier.Copy(process.StandardError.BaseStream, _hostErr))
|
||||
{
|
||||
IsBackground = true
|
||||
};
|
||||
Thread tIn = new(() => _streamCopier.Copy(_hostIn, process.StandardInput.BaseStream))
|
||||
{
|
||||
IsBackground = true
|
||||
};
|
||||
|
||||
tOut.Start();
|
||||
tErr.Start();
|
||||
tIn.Start();
|
||||
|
||||
process.WaitForExit();
|
||||
|
||||
// Wait up to 1 second for stream copier threads to finalize flushing buffer content
|
||||
tOut.Join(1000);
|
||||
tErr.Join(1000);
|
||||
|
||||
return new ExecutionResult(process.ExitCode);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return ExecutionResult.Failure(1, $"Error executing Nix wrapper: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using NixWslWrapper.Core.Interfaces;
|
||||
|
||||
namespace NixWslWrapper.Infrastructure.IO
|
||||
{
|
||||
public class ThreadedStreamCopier : IStreamCopier
|
||||
{
|
||||
private readonly int _bufferSize;
|
||||
|
||||
public ThreadedStreamCopier(int bufferSize = 8192)
|
||||
{
|
||||
if (bufferSize <= 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(bufferSize), "Buffer size must be positive.");
|
||||
_bufferSize = bufferSize;
|
||||
}
|
||||
|
||||
public void Copy(Stream input, Stream output)
|
||||
{
|
||||
byte[] buffer = new byte[_bufferSize];
|
||||
int read;
|
||||
try
|
||||
{
|
||||
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
|
||||
{
|
||||
output.Write(buffer, 0, read);
|
||||
output.Flush();
|
||||
}
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
// Clean exit when stream is closed or process terminates
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
// Clean exit when streams are disposed
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// General catch to ensure thread doesn't crash the host process
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\NixWslWrapper.Core\NixWslWrapper.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
Reference in New Issue
Block a user