using System.Diagnostics.CodeAnalysis; using ErsatzTV.Core; using ErsatzTV.Core.Errors; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; namespace ErsatzTV.Extensions; /// /// REST status-code mapping helpers for the JSON write/read API (slice #2a). /// These are additive and must not change the behavior of the existing /// / helpers, /// which IptvController and other read endpoints depend on. /// [SuppressMessage("ReSharper", "VSTHRD003")] public static class ApiResults { /// /// Maps a failure to 404 when it is a , 412 when it is a /// (optimistic-concurrency mismatch, issue #253), /// otherwise 422. /// public static IActionResult ToErrorResult(this BaseError error) => error switch { NotFoundError => new NotFoundObjectResult(CreateProblemDetails(404, "Resource not found", error.Value)), PreconditionFailedError => new ObjectResult(CreateProblemDetails(412, "Precondition Failed", error.Value)) { StatusCode = StatusCodes.Status412PreconditionFailed }, _ => new UnprocessableEntityObjectResult(CreateProblemDetails(422, "Validation failed", error.Value)) }; /// Right: 201 Created with a Location header and body; Left: 404 (NotFound) or 422. public static IActionResult ToCreatedResult( this Either either, Func location, Func body) => either.Match( Left: error => error.ToErrorResult(), Right: value => new CreatedResult(location(value), body(value))); /// Right: 200 with body; Left: 404 (NotFound) or 422. public static IActionResult ToUpdatedResult(this Either either) => either.Match( Left: error => error.ToErrorResult(), Right: value => (IActionResult)new OkObjectResult(value)); /// Right(Unit): 204 No Content; Left: 404 (NotFound) or 422. public static IActionResult ToDeletedResult(this Either either) => either.Match( Left: error => error.ToErrorResult(), Right: _ => (IActionResult)new NoContentResult()); /// Some: 200 with body; None: 404. public static IActionResult ToGetResult(this Option option) => option.Match( Some: value => (IActionResult)new OkObjectResult(value), None: () => NotFoundProblem()); public static IActionResult NotFoundProblem(string detail = "Resource not found") => new NotFoundObjectResult(CreateProblemDetails(404, "Resource not found", detail)); /// /// 409 directly — for a mutation that races a background operation /// holding a lock (e.g. a playout build in flight). Mirrors . /// public static IActionResult ConflictProblem(string title, string detail) => new ConflictObjectResult(CreateProblemDetails(409, title, detail)); private static ProblemDetails CreateProblemDetails(int status, string title, string detail) => new() { Status = status, Title = title, Detail = detail }; }