tray: --import-profile merges a profile document into the user config
RioJoy.Tray --import-profile <profile.json> merges a single-profile document (a repo's profiles/*.json) into %APPDATA%\RIOJoy\config.json via ConfigStore.ImportProfile: same-name profiles are replaced in place (FindProfile's case-insensitive convention), everything else appended, all other config content preserved. Refuses (exit 3) while a tray instance is running - a live tray rewrites the config from memory and would silently discard the import. Documents that a Name must be stated in the file itself: RioProfile defaults Name to 'Unnamed', so the import checks the raw JSON, not the deserialized object. This is the profile install story for bench machines and pods (previously: hand-paste into the Profiles array). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -52,4 +52,48 @@ public static class ConfigStore
|
||||
if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(path));
|
||||
return File.Exists(path) ? Deserialize(File.ReadAllText(path)) : new AppConfig();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserialize a single-profile document (the shape shipped in a repo's
|
||||
/// <c>profiles/*.json</c>) with the same serializer settings as the config.
|
||||
/// </summary>
|
||||
public static RioProfile DeserializeProfile(string json)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(json)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(json));
|
||||
return JsonConvert.DeserializeObject<RioProfile>(json, Options)
|
||||
?? throw new JsonSerializationException("Profile JSON deserialized to null.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Merge the single-profile file at <paramref name="profilePath"/> into the
|
||||
/// config at <paramref name="configPath"/> (created with defaults if absent).
|
||||
/// A profile with the same name (case-insensitive, the
|
||||
/// <see cref="AppConfig.FindProfile"/> convention) is replaced in place;
|
||||
/// otherwise the profile is appended. All other config content is preserved.
|
||||
/// </summary>
|
||||
public static ProfileImportResult ImportProfile(string configPath, string profilePath)
|
||||
{
|
||||
string profileJson = File.ReadAllText(profilePath);
|
||||
RioProfile profile = DeserializeProfile(profileJson);
|
||||
// Name keys the merge, and RioProfile defaults it to "Unnamed" — so a
|
||||
// document that never states one must be rejected, not merged under the
|
||||
// class default.
|
||||
if (Newtonsoft.Json.Linq.JObject.Parse(profileJson).Value<string>("Name") is not { Length: > 0 })
|
||||
throw new JsonSerializationException($"Profile file '{profilePath}' has no Name.");
|
||||
|
||||
AppConfig config = Load(configPath);
|
||||
int existing = config.Profiles.FindIndex(
|
||||
p => string.Equals(p.Name, profile.Name, StringComparison.OrdinalIgnoreCase));
|
||||
bool replaced = existing >= 0;
|
||||
if (replaced)
|
||||
config.Profiles[existing] = profile;
|
||||
else
|
||||
config.Profiles.Add(profile);
|
||||
|
||||
Save(config, configPath);
|
||||
return new ProfileImportResult(profile.Name!, replaced);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Outcome of <see cref="ConfigStore.ImportProfile"/>.</summary>
|
||||
public sealed record ProfileImportResult(string Name, bool Replaced);
|
||||
|
||||
Reference in New Issue
Block a user