Files
AIC-Project/Client/PackageLocal/com.unity.pipeline@0.4.0-exp.1/Editor/Commands/ProjectSettings/ProjectSettingsCommand.cs
T

146 lines
6.1 KiB
C#

using System;
using System.Collections.Generic;
using UnityEngine;
namespace Unity.Pipeline.Editor.Commands.ProjectSettings
{
/// <summary>
/// Shared plumbing the per-group project-settings commands (CLI-202) wrap, so each group's
/// <c>get</c>/<c>set</c> handler stays thin and the policy is applied uniformly:
/// <list type="bullet">
/// <item><description><see cref="Get"/> — read a group's values into a <see cref="ProjectSettingsResponse"/>.</description></item>
/// <item><description><see cref="Apply"/> — gate a write with the <c>confirm</c>/<c>dry_run</c> convention and
/// apply it (project-settings writes are not part of Unity's Undo), then re-read the group so the response
/// reflects the post-change state, and surface domain-reload / reimport signals.</description></item>
/// </list>
///
/// Group handlers supply two callbacks: <c>readValues</c> (snapshot the group's current values)
/// and, for writes, <c>apply</c> (perform the mutation) plus a <c>plan</c> describing the intended
/// effect. All run on the main thread (these commands are <c>MainThreadRequired = true</c>).
/// </summary>
public static class ProjectSettingsCommand
{
/// <summary>
/// Read-only path: build a success response carrying the group's current values.
/// </summary>
public static ProjectSettingsResponse Get(string group, Func<Dictionary<string, object>> readValues)
{
if (readValues == null)
throw new ArgumentNullException(nameof(readValues));
if (!TryReadValues(readValues, out var values, out var readError))
return ProjectSettingsResponse.Fail(group, $"Failed to read {group} settings: {readError}");
return new ProjectSettingsResponse
{
Success = true,
Group = group,
Values = values,
Message = $"Read {group} settings."
};
}
/// <summary>
/// Write path: gate the change with the <c>confirm</c>/<c>dry_run</c> convention and apply it
/// (project-settings writes are not undoable via Ctrl+Z), then re-read the
/// group. <paramref name="requiresDomainReload"/> / <paramref name="requiresReimport"/> declare
/// side effects of this specific change (Unity defers them, so the response returns first);
/// they are reported for an applied change and for a dry-run preview alike, and suppressed when
/// the operation is refused or fails.
/// </summary>
public static ProjectSettingsResponse Apply(
string group,
bool confirm,
bool dryRun,
object auditParams,
Func<string> plan,
Action apply,
Func<Dictionary<string, object>> readValues,
bool requiresDomainReload = false,
bool requiresReimport = false)
{
if (plan == null)
throw new ArgumentNullException(nameof(plan));
if (apply == null)
throw new ArgumentNullException(nameof(apply));
if (readValues == null)
throw new ArgumentNullException(nameof(readValues));
string planText;
try
{
planText = plan();
}
catch (Exception ex)
{
return ProjectSettingsResponse.Fail(group, $"Failed to compute the operation preview: {ex.Message}");
}
// Inline confirm / dry_run gate + Undo grouping (the codebase-wide mutating convention).
var response = new ProjectSettingsResponse { Group = group };
if (dryRun)
{
response.Success = true;
response.DryRun = true;
response.Applied = false;
response.Message = $"Dry run — no changes made. Intended effect: {planText}";
}
else if (!confirm)
{
response.Success = false;
response.Applied = false;
response.Message =
"Refused: this operation mutates the project. Re-run with confirm=true to apply, " +
"or dry_run=true to preview the change.";
}
else
{
try
{
apply();
response.Success = true;
response.Applied = true;
response.Message = planText;
}
catch (Exception ex)
{
response.Success = false;
response.Applied = false;
response.Message = $"Operation failed: {ex.Message}";
}
}
// Side-effect signals are only meaningful when the operation went through (applied) or was
// previewed (dry run); suppress them for a refusal or failure.
response.RequiresDomainReload = response.Success && requiresDomainReload;
response.RequiresReimport = response.Success && requiresReimport;
// Re-read so the caller sees the resulting (or, for a dry run / refusal, the unchanged)
// state. A read failure must not mask the write outcome, so it is non-fatal here.
if (TryReadValues(readValues, out var values, out var readError))
response.Values = values;
else
Debug.LogWarning($"[pipeline] Read-back of {group} settings failed: {readError}");
return response;
}
private static bool TryReadValues(Func<Dictionary<string, object>> readValues,
out Dictionary<string, object> values, out string error)
{
try
{
values = readValues() ?? new Dictionary<string, object>();
error = null;
return true;
}
catch (Exception ex)
{
values = null;
error = ex.Message;
return false;
}
}
}
}