Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SlnFileFactory.CreateFromFilteredSolutionFile: Refactor #46404

Merged
merged 4 commits into from
Feb 3, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 23 additions & 28 deletions src/Cli/dotnet/SlnFileFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Text.Json;
using System.Text.Json.Nodes;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.VisualStudio.SolutionPersistence;
using Microsoft.VisualStudio.SolutionPersistence.Model;
Expand Down Expand Up @@ -71,38 +72,29 @@ public static SolutionModel CreateFromFileOrDirectory(string fileOrDirectory, bo

public static SolutionModel CreateFromFilteredSolutionFile(string filteredSolutionPath)
{
JsonDocument jsonDocument;
JsonElement jsonElement;
JsonElement filteredSolutionJsonElement;
string originalSolutionPath;
string originalSolutionPathAbsolute;
string[] filteredSolutionProjectPaths;

IEnumerable<string> filteredSolutionProjectPaths;
try
{
jsonDocument = JsonDocument.Parse(File.ReadAllText(filteredSolutionPath));
jsonElement = jsonDocument.RootElement;
filteredSolutionJsonElement = jsonElement.GetProperty("solution");
originalSolutionPath = filteredSolutionJsonElement.GetProperty("path").GetString();
JsonElement root = JsonDocument.Parse(File.ReadAllText(filteredSolutionPath)).RootElement;
originalSolutionPath = root.GetProperty("solution").GetProperty("path").GetString();
filteredSolutionProjectPaths = root.GetProperty("solution").GetProperty("projects").EnumerateArray().Select(p => p.GetString()).ToArray();
originalSolutionPathAbsolute = Path.GetFullPath(originalSolutionPath, Path.GetDirectoryName(filteredSolutionPath));
if (!File.Exists(originalSolutionPathAbsolute))
{
throw new Exception();
}
filteredSolutionProjectPaths = filteredSolutionJsonElement.GetProperty("projects")
.EnumerateArray()
.Select(project => project.GetString())
.ToArray();
}
catch (Exception ex) {
catch (Exception ex)
{
throw new GracefulException(
CommonLocalizableStrings.InvalidSolutionFormatString,
filteredSolutionPath, ex.Message);
}

SolutionModel filteredSolution = new SolutionModel();
SolutionModel filteredSolution = new();
SolutionModel originalSolution = CreateFromFileOrDirectory(originalSolutionPathAbsolute);

// Store the original solution path in the description field of the filtered solution
filteredSolution.Description = originalSolutionPathAbsolute;

Comment on lines +95 to +97
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In some areas it might be useful to keep a reference to the original solution file path. See #46392
The Description field is used to store manually entered user descriptions for solution files. This feature does not exist in .slnf schema, so it seems reasonable to describe a solution filter by its original solution file path.
Doing this also avoids having to parse the file twice to find this data.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If Description doesn't exist in the .slnf schema, does that mean this value only exists in memory? If it isn't in the schema, it would never get written to disk. So, what is the purpose of setting it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, at the moment this does not write to slnf files. However, it is useful having a field in the SolutionModel that stores metadata such as Description. For example, on

string solution = SlnFileFactory.GetSolutionPathFromFilteredSolutionFile(solutionFilterFilePath);

the file is parsed twice (once to get the projects, and a second time to get the original solution path). By storing this information in memory, this could be avoided.

We also wouldn't have to worry about overriding user-entered information as it is not part of the slnf file, but does describe it

foreach (var platform in originalSolution.Platforms)
{
filteredSolution.AddPlatform(platform);
Expand All @@ -112,16 +104,19 @@ public static SolutionModel CreateFromFilteredSolutionFile(string filteredSoluti
filteredSolution.AddBuildType(buildType);
}

foreach (string path in filteredSolutionProjectPaths)
{
// Normalize path to use correct directory separator
string normalizedPath = path.Replace('\\', Path.DirectorySeparatorChar);

SolutionProjectModel project = originalSolution.FindProject(normalizedPath) ?? throw new GracefulException(
IEnumerable<SolutionProjectModel> projects = filteredSolutionProjectPaths
.Select(path => path.Replace('\\', Path.DirectorySeparatorChar))
.Select(path => originalSolution.FindProject(path) ?? throw new GracefulException(
CommonLocalizableStrings.ProjectNotFoundInTheSolution,
normalizedPath,
originalSolutionPath);
filteredSolution.AddProject(project.FilePath, project.Type, project.Parent is null ? null : filteredSolution.AddFolder(project.Parent.Path));
path,
originalSolutionPath));

foreach (var project in projects)
{
_ = filteredSolution.AddProject(
project.FilePath,
project.Type,
project.Parent is null ? null : filteredSolution.AddFolder(project.Parent.Path));
}

return filteredSolution;
Expand Down