Skip to content

Commit 52da47c

Browse files
committed
Watch.Aspire
1 parent eec49ce commit 52da47c

File tree

3 files changed

+59
-16
lines changed

3 files changed

+59
-16
lines changed

src/BuiltInTools/Watch.Aspire/DotNetWatchLauncher.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public static async Task<bool> RunAsync(string workingDirectory, DotNetWatchOpti
2727
var rootProjectOptions = new ProjectOptions()
2828
{
2929
IsRootProject = true,
30-
Representation = new ProjectRepresentation(options.ProjectPath, entryPointFilePath: null),
30+
Representation = options.Project,
3131
WorkingDirectory = workingDirectory,
3232
TargetFramework = null,
3333
BuildArguments = [],

src/BuiltInTools/Watch.Aspire/DotNetWatchOptions.cs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System.Collections.Immutable;
55
using System.CommandLine;
6+
using System.CommandLine.Parsing;
67
using System.Diagnostics.CodeAnalysis;
78
using Microsoft.Extensions.Logging;
89

@@ -16,26 +17,36 @@ internal sealed class DotNetWatchOptions
1617
/// </summary>
1718
public required string SdkDirectory { get; init; }
1819

19-
public required string ProjectPath { get; init; }
20+
public required ProjectRepresentation Project { get; init; }
2021
public required ImmutableArray<string> ApplicationArguments { get; init; }
2122
public LogLevel LogLevel { get; init; }
2223
public bool NoLaunchProfile { get; init; }
2324

2425
public static bool TryParse(string[] args, [NotNullWhen(true)] out DotNetWatchOptions? options)
2526
{
2627
var sdkOption = new Option<string>("--sdk") { Arity = ArgumentArity.ExactlyOne, Required = true, AllowMultipleArgumentsPerToken = false };
27-
var projectOption = new Option<string>("--project") { Arity = ArgumentArity.ExactlyOne, Required = true, AllowMultipleArgumentsPerToken = false };
28+
var projectOption = new Option<string>("--project") { Arity = ArgumentArity.ZeroOrOne, AllowMultipleArgumentsPerToken = false };
29+
var fileOption = new Option<string>("--file") { Arity = ArgumentArity.ZeroOrOne, AllowMultipleArgumentsPerToken = false };
2830
var quietOption = new Option<bool>("--quiet") { Arity = ArgumentArity.Zero };
2931
var verboseOption = new Option<bool>("--verbose") { Arity = ArgumentArity.Zero };
3032
var noLaunchProfileOption = new Option<bool>("--no-launch-profile") { Arity = ArgumentArity.Zero };
3133
var applicationArguments = new Argument<string[]>("arguments") { Arity = ArgumentArity.ZeroOrMore };
3234

3335
verboseOption.Validators.Add(v =>
3436
{
35-
if (v.GetValue(quietOption) && v.GetValue(verboseOption))
37+
if (HasOption(v, quietOption) && HasOption(v, verboseOption))
3638
{
3739
v.AddError("Cannot specify both '--quiet' and '--verbose' options.");
3840
}
41+
42+
if (HasOption(v, projectOption) && HasOption(v, fileOption))
43+
{
44+
v.AddError("Cannot specify both '--file' and '--project' options.");
45+
}
46+
else if (!HasOption(v, projectOption) && !HasOption(v, fileOption))
47+
{
48+
v.AddError("Must specify either '--file' or '--project' option.");
49+
}
3950
});
4051

4152
var rootCommand = new RootCommand()
@@ -45,6 +56,7 @@ public static bool TryParse(string[] args, [NotNullWhen(true)] out DotNetWatchOp
4556
{
4657
sdkOption,
4758
projectOption,
59+
fileOption,
4860
quietOption,
4961
verboseOption,
5062
noLaunchProfileOption
@@ -70,12 +82,15 @@ public static bool TryParse(string[] args, [NotNullWhen(true)] out DotNetWatchOp
7082
options = new DotNetWatchOptions()
7183
{
7284
SdkDirectory = parseResult.GetRequiredValue(sdkOption),
73-
ProjectPath = parseResult.GetRequiredValue(projectOption),
85+
Project = new ProjectRepresentation(projectPath: parseResult.GetValue(projectOption), entryPointFilePath: parseResult.GetValue(fileOption)),
7486
LogLevel = parseResult.GetValue(quietOption) ? LogLevel.Warning : parseResult.GetValue(verboseOption) ? LogLevel.Debug : LogLevel.Information,
7587
ApplicationArguments = [.. parseResult.GetValue(applicationArguments) ?? []],
7688
NoLaunchProfile = parseResult.GetValue(noLaunchProfileOption),
7789
};
7890

7991
return true;
8092
}
93+
94+
private static bool HasOption(SymbolResult symbolResult, Option option)
95+
=> symbolResult.GetResult(option) is OptionResult or && !or.Implicit;
8196
}

test/Microsoft.DotNet.HotReload.Watch.Aspire.Tests/DotNetWatchOptionsTests.cs

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,19 @@ namespace Microsoft.DotNet.Watch.UnitTests;
88
public class DotNetWatchOptionsTests
99
{
1010
[Fact]
11-
public void TryParse_RequiredProjectOption()
11+
public void TryParse_RequiredSdkOption()
1212
{
13-
// Project option is missing
14-
var args = new[] { "--verbose", "a", "b" };
13+
// --sdk option is missing
14+
var args = new[] { "--project", "proj", "a", "b" };
1515
Assert.False(DotNetWatchOptions.TryParse(args, out var options));
1616
Assert.Null(options);
1717
}
1818

1919
[Fact]
20-
public void TryParse_RequiredSdkOption()
20+
public void TryParse_RequiredProjectOrFileOption()
2121
{
22-
// Project option is missing
23-
var args = new[] { "--project", "proj", "a", "b" };
22+
// --project and --file options are missing
23+
var args = new[] { "--verbose", "a", "b" };
2424
Assert.False(DotNetWatchOptions.TryParse(args, out var options));
2525
Assert.Null(options);
2626
}
@@ -31,10 +31,28 @@ public void TryParse_ProjectAndSdkPaths()
3131
var args = new[] { "--sdk", "sdk", "--project", "myproject.csproj" };
3232
Assert.True(DotNetWatchOptions.TryParse(args, out var options));
3333
Assert.Equal("sdk", options.SdkDirectory);
34-
Assert.Equal("myproject.csproj", options.ProjectPath);
34+
Assert.Equal("myproject.csproj", options.Project.PhysicalPath);
35+
Assert.Empty(options.ApplicationArguments);
36+
}
37+
38+
[Fact]
39+
public void TryParse_FilePath()
40+
{
41+
var args = new[] { "--sdk", "sdk", "--file", "file.cs" };
42+
Assert.True(DotNetWatchOptions.TryParse(args, out var options));
43+
Assert.Equal("sdk", options.SdkDirectory);
44+
Assert.Equal("file.cs", options.Project.EntryPointFilePath);
3545
Assert.Empty(options.ApplicationArguments);
3646
}
3747

48+
[Fact]
49+
public void TryParse_ProjectAndFilePaths()
50+
{
51+
var args = new[] { "--sdk", "sdk", "--project", "myproject.csproj", "--file", "file.cs" };
52+
Assert.False(DotNetWatchOptions.TryParse(args, out var options));
53+
Assert.Null(options);
54+
}
55+
3856
[Fact]
3957
public void TryParse_ApplicationArguments()
4058
{
@@ -95,22 +113,32 @@ public void TryParse_ConflictingOptions()
95113
}
96114

97115
[Fact]
98-
public void TryParse_MultipleOptionValues()
116+
public void TryParse_Project_MultipleValues()
99117
{
100118
// Project option should only accept one value
101119
var args = new[] { "--sdk", "sdk", "--project", "proj1", "proj2" };
102120
Assert.True(DotNetWatchOptions.TryParse(args, out var options));
103-
Assert.Equal("proj1", options.ProjectPath);
121+
Assert.Equal("proj1", options.Project.PhysicalPath);
104122
AssertEx.SequenceEqual(["proj2"], options.ApplicationArguments);
105123
}
106-
124+
125+
[Fact]
126+
public void TryParse_File_MultipleValues()
127+
{
128+
// Project option should only accept one value
129+
var args = new[] { "--sdk", "sdk", "--file", "file1.cs", "file2.cs" };
130+
Assert.True(DotNetWatchOptions.TryParse(args, out var options));
131+
Assert.Equal("file1.cs", options.Project.EntryPointFilePath);
132+
AssertEx.SequenceEqual(["file2.cs"], options.ApplicationArguments);
133+
}
134+
107135
[Fact]
108136
public void TryParse_AllOptionsSet()
109137
{
110138
var args = new[] { "--sdk", "sdk", "--project", "myapp.csproj", "--verbose", "--no-launch-profile", "arg1", "arg2", "arg3" };
111139
Assert.True(DotNetWatchOptions.TryParse(args, out var options));
112140

113-
Assert.Equal("myapp.csproj", options.ProjectPath);
141+
Assert.Equal("myapp.csproj", options.Project.PhysicalPath);
114142
Assert.Equal(LogLevel.Debug, options.LogLevel);
115143
Assert.True(options.NoLaunchProfile);
116144
AssertEx.SequenceEqual(["arg1", "arg2", "arg3"], options.ApplicationArguments);

0 commit comments

Comments
 (0)