Give packs an identity so create can refuse missing deps and load in a stable order.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Leonid Pershin
2026-08-20 00:20:36 +03:00
co-authored by Cursor
parent e5a0ae5b9d
commit 263c94c55d
28 changed files with 851 additions and 56 deletions
+27 -3
View File
@@ -80,6 +80,14 @@ internal static class SchoolEndpoints
Problem(StatusCodes.Status400BadRequest, "unknown-name-set", "The selected name set is not in the catalog."),
SchoolCreationError.UnknownNativeLanguage =>
Problem(StatusCodes.Status400BadRequest, "unknown-native-language", "The selected native language is not in that name set."),
SchoolCreationError.MissingMod =>
Problem(
StatusCodes.Status400BadRequest,
"missing-mod",
$"Mod '{outcome.MissingPackId}' is required but was not selected.",
missing: outcome.MissingPackId),
SchoolCreationError.ModCycle =>
Problem(StatusCodes.Status400BadRequest, "mod-cycle", "Selected mods have a cyclic dependency."),
_ => Results.Problem("Unknown error."),
};
})
@@ -468,7 +476,12 @@ internal static class SchoolEndpoints
return true;
}
private static IResult Problem(int statusCode, string code, string detail, StaffingOutcome? staffing = null)
private static IResult Problem(
int statusCode,
string code,
string detail,
StaffingOutcome? staffing = null,
string? missing = null)
{
var extensions = new Dictionary<string, object?> { ["code"] = code };
if (staffing is not null)
@@ -479,6 +492,11 @@ internal static class SchoolEndpoints
extensions["attempted"] = staffing.Attempted;
}
if (missing is not null)
{
extensions["missing"] = missing;
}
return Results.Problem(detail: detail, statusCode: statusCode, title: code, extensions: extensions);
}
}
@@ -492,10 +510,16 @@ internal sealed record CreateSchoolRequest(
string? NameSetId,
string? NativeLanguage);
internal sealed record SchoolResponse(int Id, string Name, DateTime GameTime, bool Running, byte SpeedIndex)
internal sealed record SchoolResponse(
int Id,
string Name,
DateTime GameTime,
bool Running,
byte SpeedIndex,
IReadOnlyList<string> ModIds)
{
public static SchoolResponse From(SchoolState school) =>
new(school.Id, school.Name, school.GameTime, school.Running, school.SpeedIndex);
new(school.Id, school.Name, school.GameTime, school.Running, school.SpeedIndex, school.ModIds);
}
/// <summary>Everything the main menu needs in one request.</summary>