using System; using System.Collections.Generic; using UnityEngine; namespace Unity.Pipeline.Editor.Commands.ProjectSettings { /// /// Shared plumbing the per-group project-settings commands (CLI-202) wrap, so each group's /// get/set handler stays thin and the policy is applied uniformly: /// /// — read a group's values into a . /// — gate a write with the confirm/dry_run 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. /// /// /// Group handlers supply two callbacks: readValues (snapshot the group's current values) /// and, for writes, apply (perform the mutation) plus a plan describing the intended /// effect. All run on the main thread (these commands are MainThreadRequired = true). /// public static class ProjectSettingsCommand { /// /// Read-only path: build a success response carrying the group's current values. /// public static ProjectSettingsResponse Get(string group, Func> 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." }; } /// /// Write path: gate the change with the confirm/dry_run convention and apply it /// (project-settings writes are not undoable via Ctrl+Z), then re-read the /// group. / 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. /// public static ProjectSettingsResponse Apply( string group, bool confirm, bool dryRun, object auditParams, Func plan, Action apply, Func> 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> readValues, out Dictionary values, out string error) { try { values = readValues() ?? new Dictionary(); error = null; return true; } catch (Exception ex) { values = null; error = ex.Message; return false; } } } }