feat: add System.Text.Json source generation for AOT/trim compatibility

- Add PodmanJsonContext (JsonSerializerContext) covering all ~70 DTOs and
  request models used by the library with PropertyNameCaseInsensitive=true
- Replace MaksIT.Core ToJson()/ToObject<T>() with explicit JsonTypeInfo<T>
  parameters threaded through all internal HTTP helper methods
- Update all call sites in PodmanClient partial classes
- Update PodmanProgressSession<T> constructor to accept JsonTypeInfo<T>
- Remove MaksIT.Core package reference (no longer needed)
- Enable IsAotCompatible=true in project file to activate trim/AOT analyzers
- All 13 unit tests pass, 0 build warnings
This commit is contained in:
copilot-swe-agent[bot] 2026-06-30 17:15:10 +00:00 committed by GitHub
parent 1553524cbc
commit bbba014602
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 224 additions and 62 deletions

View File

@ -1,8 +1,8 @@
using System.Net; using System.Net;
using System.Text.Json;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using MaksIT.Core.Extensions;
using MaksIT.PodmanClientDotNet.Dtos.Common; using MaksIT.PodmanClientDotNet.Dtos.Common;
using MaksIT.Results; using MaksIT.Results;
@ -13,7 +13,7 @@ internal static class PodmanHttpResults {
if (string.IsNullOrWhiteSpace(content)) if (string.IsNullOrWhiteSpace(content))
return "Podman API request failed."; return "Podman API request failed.";
var error = content.ToObject<ErrorResponseDto>(); var error = JsonSerializer.Deserialize(content, PodmanJsonContext.Default.ErrorResponseDto);
return string.IsNullOrWhiteSpace(error?.Message) ? content : error.Message; return string.IsNullOrWhiteSpace(error?.Message) ? content : error.Message;
} }

View File

@ -1,6 +1,7 @@
using System.Text.Json;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using MaksIT.Core.Extensions;
using MaksIT.PodmanClientDotNet.Dtos.Build; using MaksIT.PodmanClientDotNet.Dtos.Build;
using MaksIT.PodmanClientDotNet.Dtos.Image; using MaksIT.PodmanClientDotNet.Dtos.Image;
using MaksIT.Results; using MaksIT.Results;
@ -24,7 +25,7 @@ internal static class PodmanNdjsonStreams {
if (!line.Contains("\"error\"", StringComparison.Ordinal)) if (!line.Contains("\"error\"", StringComparison.Ordinal))
continue; continue;
var errorDetails = line.ToObject<PullImageResponseDto>(); var errorDetails = JsonSerializer.Deserialize(line, PodmanJsonContext.Default.PullImageResponseDto);
var message = errorDetails?.Error ?? $"{operation} failed."; var message = errorDetails?.Error ?? $"{operation} failed.";
logger.LogError("{Operation} failed: {Message}", operation, message); logger.LogError("{Operation} failed: {Message}", operation, message);
return Result.BadRequest(message); return Result.BadRequest(message);
@ -46,7 +47,7 @@ internal static class PodmanNdjsonStreams {
if (string.IsNullOrWhiteSpace(line)) if (string.IsNullOrWhiteSpace(line))
continue; continue;
var progress = line.ToObject<BuildProgressLineDto>(); var progress = JsonSerializer.Deserialize(line, PodmanJsonContext.Default.BuildProgressLineDto);
if (progress is null) if (progress is null)
continue; continue;

View File

@ -1,8 +1,9 @@
using MaksIT.PodmanClientDotNet;
using System.Net; using System.Net;
using System.Text.Json;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using MaksIT.Core.Extensions;
using MaksIT.PodmanClientDotNet.Internal; using MaksIT.PodmanClientDotNet.Internal;
using MaksIT.PodmanClientDotNet.Models; using MaksIT.PodmanClientDotNet.Models;
using MaksIT.PodmanClientDotNet.Dtos.Container; using MaksIT.PodmanClientDotNet.Dtos.Container;
@ -251,7 +252,9 @@ public partial class PodmanClient {
return await PostJsonAsync<CreateContainerRequest, CreateContainerResponseDto>( return await PostJsonAsync<CreateContainerRequest, CreateContainerResponseDto>(
"/libpod/containers/create", "/libpod/containers/create",
"Create container", "Create container",
createContainerParameters createContainerParameters,
PodmanJsonContext.Default.CreateContainerRequest,
PodmanJsonContext.Default.CreateContainerResponseDto
).ConfigureAwait(false); ).ConfigureAwait(false);
} }
@ -314,7 +317,7 @@ public partial class PodmanClient {
if (response.IsSuccessStatusCode) { if (response.IsSuccessStatusCode) {
var value = !string.IsNullOrWhiteSpace(jsonResponse) var value = !string.IsNullOrWhiteSpace(jsonResponse)
? jsonResponse.ToObject<DeleteContainerResponseDto[]>() ? JsonSerializer.Deserialize(jsonResponse, PodmanJsonContext.Default.DeleteContainerResponseDtoArray)
: null; : null;
return PodmanHttpResults.Success(response.StatusCode, value); return PodmanHttpResults.Success(response.StatusCode, value);
} }
@ -336,7 +339,7 @@ public partial class PodmanClient {
if (response.IsSuccessStatusCode) { if (response.IsSuccessStatusCode) {
var value = !string.IsNullOrWhiteSpace(jsonResponse) var value = !string.IsNullOrWhiteSpace(jsonResponse)
? jsonResponse.ToObject<DeleteContainerResponseDto[]>() ? JsonSerializer.Deserialize(jsonResponse, PodmanJsonContext.Default.DeleteContainerResponseDtoArray)
: null; : null;
return PodmanHttpResults.Success(response.StatusCode, value); return PodmanHttpResults.Success(response.StatusCode, value);
} }

View File

@ -1,3 +1,4 @@
using MaksIT.PodmanClientDotNet;
using MaksIT.PodmanClientDotNet.Dtos.Common; using MaksIT.PodmanClientDotNet.Dtos.Common;
using MaksIT.PodmanClientDotNet.Dtos.Container; using MaksIT.PodmanClientDotNet.Dtos.Container;
using MaksIT.Results; using MaksIT.Results;
@ -16,6 +17,7 @@ public partial class PodmanClient {
GetJsonAsync<List<ContainerListEntryDto>>( GetJsonAsync<List<ContainerListEntryDto>>(
"/libpod/containers/json", "/libpod/containers/json",
"List containers", "List containers",
PodmanJsonContext.Default.ListContainerListEntryDto,
[ [
("all", all.ToString().ToLowerInvariant()), ("all", all.ToString().ToLowerInvariant()),
("limit", limit?.ToString()), ("limit", limit?.ToString()),
@ -27,7 +29,7 @@ public partial class PodmanClient {
); );
public Task<Result<ContainerInspectDto?>> InspectContainerAsync(string name, CancellationToken cancellationToken = default) => public Task<Result<ContainerInspectDto?>> InspectContainerAsync(string name, CancellationToken cancellationToken = default) =>
GetJsonAsync<ContainerInspectDto>($"{ContainerPath(name)}/json", "Inspect container", cancellationToken: cancellationToken); GetJsonAsync<ContainerInspectDto>($"{ContainerPath(name)}/json", "Inspect container", PodmanJsonContext.Default.ContainerInspectDto, cancellationToken: cancellationToken);
public Task<Result> ContainerExistsAsync(string name, CancellationToken cancellationToken = default) => public Task<Result> ContainerExistsAsync(string name, CancellationToken cancellationToken = default) =>
GetWithoutBodyAsync($"{ContainerPath(name)}/exists", "Container exists", cancellationToken: cancellationToken); GetWithoutBodyAsync($"{ContainerPath(name)}/exists", "Container exists", cancellationToken: cancellationToken);
@ -58,6 +60,7 @@ public partial class PodmanClient {
PostLibpodAsync<ContainerWaitDto>( PostLibpodAsync<ContainerWaitDto>(
$"{ContainerPath(name)}/wait", $"{ContainerPath(name)}/wait",
"Wait container", "Wait container",
PodmanJsonContext.Default.ContainerWaitDto,
query: condition is null ? null : [("condition", condition)], query: condition is null ? null : [("condition", condition)],
cancellationToken: cancellationToken cancellationToken: cancellationToken
); );
@ -92,6 +95,7 @@ public partial class PodmanClient {
GetJsonAsync<ContainerStatsDto>( GetJsonAsync<ContainerStatsDto>(
$"{ContainerPath(name)}/stats", $"{ContainerPath(name)}/stats",
"Get container stats", "Get container stats",
PodmanJsonContext.Default.ContainerStatsDto,
[("stream", stream.ToString().ToLowerInvariant())], [("stream", stream.ToString().ToLowerInvariant())],
cancellationToken cancellationToken
); );
@ -107,13 +111,14 @@ public partial class PodmanClient {
query.Add(("containers", c)); query.Add(("containers", c));
} }
return GetJsonAsync<Dictionary<string, ContainerStatsDto>>("/libpod/containers/stats", "Get containers stats", query, cancellationToken); return GetJsonAsync<Dictionary<string, ContainerStatsDto>>("/libpod/containers/stats", "Get containers stats", PodmanJsonContext.Default.DictionaryStringContainerStatsDto, query, cancellationToken);
} }
public Task<Result<PruneReportDto?>> PruneContainersAsync(string? filters = null, CancellationToken cancellationToken = default) => public Task<Result<PruneReportDto?>> PruneContainersAsync(string? filters = null, CancellationToken cancellationToken = default) =>
PostLibpodAsync<PruneReportDto>( PostLibpodAsync<PruneReportDto>(
"/libpod/containers/prune", "/libpod/containers/prune",
"Prune containers", "Prune containers",
PodmanJsonContext.Default.PruneReportDto,
query: filters is null ? null : [("filters", filters)], query: filters is null ? null : [("filters", filters)],
cancellationToken: cancellationToken cancellationToken: cancellationToken
); );
@ -178,7 +183,7 @@ public partial class PodmanClient {
); );
public Task<Result<ContainerMountDto?>> MountContainerAsync(string name, CancellationToken cancellationToken = default) => public Task<Result<ContainerMountDto?>> MountContainerAsync(string name, CancellationToken cancellationToken = default) =>
PostLibpodAsync<ContainerMountDto>($"{ContainerPath(name)}/mount", "Mount container", cancellationToken: cancellationToken); PostLibpodAsync<ContainerMountDto>($"{ContainerPath(name)}/mount", "Mount container", PodmanJsonContext.Default.ContainerMountDto, cancellationToken: cancellationToken);
public Task<Result> UnmountContainerAsync(string name, CancellationToken cancellationToken = default) => public Task<Result> UnmountContainerAsync(string name, CancellationToken cancellationToken = default) =>
PostWithoutBodyAsync($"{ContainerPath(name)}/unmount", "Unmount container", cancellationToken: cancellationToken); PostWithoutBodyAsync($"{ContainerPath(name)}/unmount", "Unmount container", cancellationToken: cancellationToken);
@ -228,7 +233,7 @@ public partial class PodmanClient {
); );
public Task<Result<ContainerChangesDto?>> GetContainerChangesAsync(string name, CancellationToken cancellationToken = default) => public Task<Result<ContainerChangesDto?>> GetContainerChangesAsync(string name, CancellationToken cancellationToken = default) =>
GetJsonAsync<ContainerChangesDto>($"{ContainerPath(name)}/changes", "Get container changes", cancellationToken: cancellationToken); GetJsonAsync<ContainerChangesDto>($"{ContainerPath(name)}/changes", "Get container changes", PodmanJsonContext.Default.ContainerChangesDto, cancellationToken: cancellationToken);
public Task<Result<ContainerCommitDto?>> CommitContainerAsync( public Task<Result<ContainerCommitDto?>> CommitContainerAsync(
string container, string container,
@ -255,14 +260,14 @@ public partial class PodmanClient {
query.Add(("changes", change)); query.Add(("changes", change));
} }
return PostLibpodAsync<ContainerCommitDto>("/libpod/commit", "Commit container", query: query, cancellationToken: cancellationToken); return PostLibpodAsync<ContainerCommitDto>("/libpod/commit", "Commit container", PodmanJsonContext.Default.ContainerCommitDto, query: query, cancellationToken: cancellationToken);
} }
public Task<Result<ContainerHealthCheckDto?>> HealthCheckContainerAsync(string name, CancellationToken cancellationToken = default) => public Task<Result<ContainerHealthCheckDto?>> HealthCheckContainerAsync(string name, CancellationToken cancellationToken = default) =>
GetJsonAsync<ContainerHealthCheckDto>($"{ContainerPath(name)}/healthcheck", "Health check container", cancellationToken: cancellationToken); GetJsonAsync<ContainerHealthCheckDto>($"{ContainerPath(name)}/healthcheck", "Health check container", PodmanJsonContext.Default.ContainerHealthCheckDto, cancellationToken: cancellationToken);
public Task<Result<MountedContainersResponseDto?>> GetMountedContainersAsync(CancellationToken cancellationToken = default) => public Task<Result<MountedContainersResponseDto?>> GetMountedContainersAsync(CancellationToken cancellationToken = default) =>
GetJsonAsync<MountedContainersResponseDto>("/libpod/containers/showmounted", "Get mounted containers", cancellationToken: cancellationToken); GetJsonAsync<MountedContainersResponseDto>("/libpod/containers/showmounted", "Get mounted containers", PodmanJsonContext.Default.MountedContainersResponseDto, cancellationToken: cancellationToken);
public Task<Result<ContainerTopDto?>> TopContainerAsync( public Task<Result<ContainerTopDto?>> TopContainerAsync(
string name, string name,
@ -273,6 +278,7 @@ public partial class PodmanClient {
GetJsonAsync<ContainerTopDto>( GetJsonAsync<ContainerTopDto>(
$"{ContainerPath(name)}/top", $"{ContainerPath(name)}/top",
"Top container", "Top container",
PodmanJsonContext.Default.ContainerTopDto,
[ [
("ps_args", psArgs), ("ps_args", psArgs),
("stream", stream.ToString().ToLowerInvariant()), ("stream", stream.ToString().ToLowerInvariant()),

View File

@ -1,8 +1,8 @@
using System.Text; using MaksIT.PodmanClientDotNet;
using System.Text.Json;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using MaksIT.Core.Extensions;
using MaksIT.PodmanClientDotNet.Internal; using MaksIT.PodmanClientDotNet.Internal;
using MaksIT.PodmanClientDotNet.Models; using MaksIT.PodmanClientDotNet.Models;
using MaksIT.PodmanClientDotNet.Dtos.Exec; using MaksIT.PodmanClientDotNet.Dtos.Exec;
@ -39,7 +39,9 @@ public partial class PodmanClient {
return await PostJsonAsync<CreateExecRequest, CreateExecResponseDto>( return await PostJsonAsync<CreateExecRequest, CreateExecResponseDto>(
$"/libpod/containers/{Uri.EscapeDataString(containerName)}/exec", $"/libpod/containers/{Uri.EscapeDataString(containerName)}/exec",
"Create exec", "Create exec",
execRequest execRequest,
PodmanJsonContext.Default.CreateExecRequest,
PodmanJsonContext.Default.CreateExecResponseDto
).ConfigureAwait(false); ).ConfigureAwait(false);
} }
@ -60,7 +62,8 @@ public partial class PodmanClient {
var result = await PostJsonWithoutBodyAsync<StartExecRequest>( var result = await PostJsonWithoutBodyAsync<StartExecRequest>(
$"/libpod/exec/{Uri.EscapeDataString(execId)}/start", $"/libpod/exec/{Uri.EscapeDataString(execId)}/start",
"Start exec", "Start exec",
startExecRequest startExecRequest,
PodmanJsonContext.Default.StartExecRequest
).ConfigureAwait(false); ).ConfigureAwait(false);
if (result.IsSuccess) if (result.IsSuccess)
@ -72,7 +75,8 @@ public partial class PodmanClient {
public Task<Result<InspectExecResponseDto?>> InspectExecAsync(string execId) => public Task<Result<InspectExecResponseDto?>> InspectExecAsync(string execId) =>
GetJsonAsync<InspectExecResponseDto>( GetJsonAsync<InspectExecResponseDto>(
$"/libpod/exec/{Uri.EscapeDataString(execId)}/json", $"/libpod/exec/{Uri.EscapeDataString(execId)}/json",
"Inspect exec" "Inspect exec",
PodmanJsonContext.Default.InspectExecResponseDto
); );
public Task<Result> ResizeExecAsync(string execId, int height, int width, CancellationToken cancellationToken = default) => public Task<Result> ResizeExecAsync(string execId, int height, int width, CancellationToken cancellationToken = default) =>

View File

@ -1,3 +1,4 @@
using MaksIT.PodmanClientDotNet;
using System.Net.Http.Headers; using System.Net.Http.Headers;
using MaksIT.PodmanClientDotNet.Dtos.Generate; using MaksIT.PodmanClientDotNet.Dtos.Generate;
@ -18,6 +19,7 @@ public partial class PodmanClient {
GetJsonAsync<GenerateSystemdDto>( GetJsonAsync<GenerateSystemdDto>(
$"/libpod/generate/{Uri.EscapeDataString(name)}/systemd", $"/libpod/generate/{Uri.EscapeDataString(name)}/systemd",
"Generate systemd", "Generate systemd",
PodmanJsonContext.Default.GenerateSystemdDto,
[ [
("useName", useName.ToString().ToLowerInvariant()), ("useName", useName.ToString().ToLowerInvariant()),
("new", createNew.ToString().ToLowerInvariant()), ("new", createNew.ToString().ToLowerInvariant()),
@ -62,6 +64,7 @@ public partial class PodmanClient {
return PostLibpodAsync<PlayKubeReportDto>( return PostLibpodAsync<PlayKubeReportDto>(
"/libpod/play/kube", "/libpod/play/kube",
"Play kube", "Play kube",
PodmanJsonContext.Default.PlayKubeReportDto,
content, content,
[ [
("network", network), ("network", network),

View File

@ -1,7 +1,8 @@
using System.Net; using System.Net;
using System.Text; using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization.Metadata;
using MaksIT.Core.Extensions;
using MaksIT.PodmanClientDotNet.Internal; using MaksIT.PodmanClientDotNet.Internal;
using MaksIT.Results; using MaksIT.Results;
@ -55,13 +56,14 @@ public partial class PodmanClient {
internal Task<Result<T?>> GetJsonAsync<T>( internal Task<Result<T?>> GetJsonAsync<T>(
string libpodPath, string libpodPath,
string operation, string operation,
JsonTypeInfo<T> typeInfo,
IEnumerable<(string Key, string? Value)>? query = null, IEnumerable<(string Key, string? Value)>? query = null,
CancellationToken cancellationToken = default CancellationToken cancellationToken = default
) => ) =>
SendAsync<T>( SendAsync<T>(
() => _httpClient.GetAsync(LibpodPath(libpodPath) + BuildQuery(query ?? []), cancellationToken), () => _httpClient.GetAsync(LibpodPath(libpodPath) + BuildQuery(query ?? []), cancellationToken),
operation, operation,
body => body.ToObject<T>(), body => JsonSerializer.Deserialize(body, typeInfo),
cancellationToken cancellationToken
); );
@ -81,17 +83,19 @@ public partial class PodmanClient {
string libpodPath, string libpodPath,
string operation, string operation,
TRequest? requestBody, TRequest? requestBody,
JsonTypeInfo<TRequest> requestTypeInfo,
JsonTypeInfo<TResponse> responseTypeInfo,
IEnumerable<(string Key, string? Value)>? query = null, IEnumerable<(string Key, string? Value)>? query = null,
CancellationToken cancellationToken = default CancellationToken cancellationToken = default
) { ) {
var content = requestBody is null var content = requestBody is null
? null ? null
: new StringContent(requestBody.ToJson(), Encoding.UTF8, "application/json"); : new StringContent(JsonSerializer.Serialize(requestBody, requestTypeInfo), Encoding.UTF8, "application/json");
return SendAsync<TResponse>( return SendAsync<TResponse>(
() => _httpClient.PostAsync(LibpodPath(libpodPath) + BuildQuery(query ?? []), content, cancellationToken), () => _httpClient.PostAsync(LibpodPath(libpodPath) + BuildQuery(query ?? []), content, cancellationToken),
operation, operation,
body => string.IsNullOrWhiteSpace(body) ? default : body.ToObject<TResponse>(), body => string.IsNullOrWhiteSpace(body) ? default : JsonSerializer.Deserialize(body, responseTypeInfo),
cancellationToken cancellationToken
); );
} }
@ -100,12 +104,13 @@ public partial class PodmanClient {
string libpodPath, string libpodPath,
string operation, string operation,
TRequest? requestBody, TRequest? requestBody,
JsonTypeInfo<TRequest> requestTypeInfo,
IEnumerable<(string Key, string? Value)>? query = null, IEnumerable<(string Key, string? Value)>? query = null,
CancellationToken cancellationToken = default CancellationToken cancellationToken = default
) { ) {
var content = requestBody is null var content = requestBody is null
? null ? null
: new StringContent(requestBody.ToJson(), Encoding.UTF8, "application/json"); : new StringContent(JsonSerializer.Serialize(requestBody, requestTypeInfo), Encoding.UTF8, "application/json");
return SendWithoutBodyAsync( return SendWithoutBodyAsync(
() => _httpClient.PostAsync(LibpodPath(libpodPath) + BuildQuery(query ?? []), content, cancellationToken), () => _httpClient.PostAsync(LibpodPath(libpodPath) + BuildQuery(query ?? []), content, cancellationToken),
@ -155,6 +160,7 @@ public partial class PodmanClient {
internal Task<Result<TResponse?>> PostLibpodAsync<TResponse>( internal Task<Result<TResponse?>> PostLibpodAsync<TResponse>(
string libpodPath, string libpodPath,
string operation, string operation,
JsonTypeInfo<TResponse> responseTypeInfo,
HttpContent? content = null, HttpContent? content = null,
IEnumerable<(string Key, string? Value)>? query = null, IEnumerable<(string Key, string? Value)>? query = null,
CancellationToken cancellationToken = default CancellationToken cancellationToken = default
@ -162,20 +168,21 @@ public partial class PodmanClient {
SendAsync<TResponse>( SendAsync<TResponse>(
() => _httpClient.PostAsync(LibpodPath(libpodPath) + BuildQuery(query ?? []), content, cancellationToken), () => _httpClient.PostAsync(LibpodPath(libpodPath) + BuildQuery(query ?? []), content, cancellationToken),
operation, operation,
body => string.IsNullOrWhiteSpace(body) ? default : body.ToObject<TResponse>(), body => string.IsNullOrWhiteSpace(body) ? default : JsonSerializer.Deserialize(body, responseTypeInfo),
cancellationToken cancellationToken
); );
internal Task<Result<T?>> DeleteJsonAsync<T>( internal Task<Result<T?>> DeleteJsonAsync<T>(
string libpodPath, string libpodPath,
string operation, string operation,
JsonTypeInfo<T> typeInfo,
IEnumerable<(string Key, string? Value)>? query = null, IEnumerable<(string Key, string? Value)>? query = null,
CancellationToken cancellationToken = default CancellationToken cancellationToken = default
) => ) =>
SendAsync<T>( SendAsync<T>(
() => _httpClient.DeleteAsync(LibpodPath(libpodPath) + BuildQuery(query ?? []), cancellationToken), () => _httpClient.DeleteAsync(LibpodPath(libpodPath) + BuildQuery(query ?? []), cancellationToken),
operation, operation,
body => string.IsNullOrWhiteSpace(body) ? default : body.ToObject<T>(), body => string.IsNullOrWhiteSpace(body) ? default : JsonSerializer.Deserialize(body, typeInfo),
cancellationToken cancellationToken
); );

View File

@ -1,3 +1,4 @@
using MaksIT.PodmanClientDotNet;
using System.Net.Http.Headers; using System.Net.Http.Headers;
using MaksIT.PodmanClientDotNet.Dtos.Common; using MaksIT.PodmanClientDotNet.Dtos.Common;
@ -16,6 +17,7 @@ public partial class PodmanClient {
GetJsonAsync<List<ImageListEntryDto>>( GetJsonAsync<List<ImageListEntryDto>>(
"/libpod/images/json", "/libpod/images/json",
"List images", "List images",
PodmanJsonContext.Default.ListImageListEntryDto,
[ [
("all", all.ToString().ToLowerInvariant()), ("all", all.ToString().ToLowerInvariant()),
("filters", filters), ("filters", filters),
@ -24,7 +26,7 @@ public partial class PodmanClient {
); );
public Task<Result<ImageInspectDto?>> InspectImageAsync(string name, CancellationToken cancellationToken = default) => public Task<Result<ImageInspectDto?>> InspectImageAsync(string name, CancellationToken cancellationToken = default) =>
GetJsonAsync<ImageInspectDto>($"{ImagePath(name)}/json", "Inspect image", cancellationToken: cancellationToken); GetJsonAsync<ImageInspectDto>($"{ImagePath(name)}/json", "Inspect image", PodmanJsonContext.Default.ImageInspectDto, cancellationToken: cancellationToken);
public Task<Result> ImageExistsAsync(string name, CancellationToken cancellationToken = default) => public Task<Result> ImageExistsAsync(string name, CancellationToken cancellationToken = default) =>
GetWithoutBodyAsync($"{ImagePath(name)}/exists", "Image exists", cancellationToken: cancellationToken); GetWithoutBodyAsync($"{ImagePath(name)}/exists", "Image exists", cancellationToken: cancellationToken);
@ -33,6 +35,7 @@ public partial class PodmanClient {
DeleteJsonAsync<ImageDeleteDto[]>( DeleteJsonAsync<ImageDeleteDto[]>(
ImagePath(name), ImagePath(name),
"Delete image", "Delete image",
PodmanJsonContext.Default.ImageDeleteDtoArray,
[("force", force.ToString().ToLowerInvariant())], [("force", force.ToString().ToLowerInvariant())],
cancellationToken cancellationToken
); );
@ -50,11 +53,11 @@ public partial class PodmanClient {
foreach (var image in images) foreach (var image in images)
query.Add(("images", image)); query.Add(("images", image));
return DeleteJsonAsync<ImageDeleteDto[]>("/libpod/images/remove", "Remove images", query, cancellationToken); return DeleteJsonAsync<ImageDeleteDto[]>("/libpod/images/remove", "Remove images", PodmanJsonContext.Default.ImageDeleteDtoArray, query, cancellationToken);
} }
public Task<Result<PruneReportDto?>> PruneImagesAsync(CancellationToken cancellationToken = default) => public Task<Result<PruneReportDto?>> PruneImagesAsync(CancellationToken cancellationToken = default) =>
PostLibpodAsync<PruneReportDto>("/libpod/images/prune", "Prune images", cancellationToken: cancellationToken); PostLibpodAsync<PruneReportDto>("/libpod/images/prune", "Prune images", PodmanJsonContext.Default.PruneReportDto, cancellationToken: cancellationToken);
public Task<Result<List<ImageSearchResultDto>?>> SearchImagesAsync( public Task<Result<List<ImageSearchResultDto>?>> SearchImagesAsync(
string term, string term,
@ -64,6 +67,7 @@ public partial class PodmanClient {
GetJsonAsync<List<ImageSearchResultDto>>( GetJsonAsync<List<ImageSearchResultDto>>(
"/libpod/images/search", "/libpod/images/search",
"Search images", "Search images",
PodmanJsonContext.Default.ListImageSearchResultDto,
[ [
("term", term), ("term", term),
("limit", limit?.ToString()), ("limit", limit?.ToString()),
@ -115,13 +119,13 @@ public partial class PodmanClient {
); );
public Task<Result<List<ImageHistoryEntryDto>?>> GetImageHistoryAsync(string name, CancellationToken cancellationToken = default) => public Task<Result<List<ImageHistoryEntryDto>?>> GetImageHistoryAsync(string name, CancellationToken cancellationToken = default) =>
GetJsonAsync<List<ImageHistoryEntryDto>>($"{ImagePath(name)}/history", "Get image history", cancellationToken: cancellationToken); GetJsonAsync<List<ImageHistoryEntryDto>>($"{ImagePath(name)}/history", "Get image history", PodmanJsonContext.Default.ListImageHistoryEntryDto, cancellationToken: cancellationToken);
public Task<Result<ImageTreeDto?>> GetImageTreeAsync(string name, CancellationToken cancellationToken = default) => public Task<Result<ImageTreeDto?>> GetImageTreeAsync(string name, CancellationToken cancellationToken = default) =>
GetJsonAsync<ImageTreeDto>($"{ImagePath(name)}/tree", "Get image tree", cancellationToken: cancellationToken); GetJsonAsync<ImageTreeDto>($"{ImagePath(name)}/tree", "Get image tree", PodmanJsonContext.Default.ImageTreeDto, cancellationToken: cancellationToken);
public Task<Result<ImageChangesDto?>> GetImageChangesAsync(string name, CancellationToken cancellationToken = default) => public Task<Result<ImageChangesDto?>> GetImageChangesAsync(string name, CancellationToken cancellationToken = default) =>
GetJsonAsync<ImageChangesDto>($"{ImagePath(name)}/changes", "Get image changes", cancellationToken: cancellationToken); GetJsonAsync<ImageChangesDto>($"{ImagePath(name)}/changes", "Get image changes", PodmanJsonContext.Default.ImageChangesDto, cancellationToken: cancellationToken);
public Task<Result<ImageImportDto?>> ImportImageAsync( public Task<Result<ImageImportDto?>> ImportImageAsync(
Stream? tarball = null, Stream? tarball = null,
@ -140,6 +144,7 @@ public partial class PodmanClient {
return PostLibpodAsync<ImageImportDto>( return PostLibpodAsync<ImageImportDto>(
"/libpod/images/import", "/libpod/images/import",
"Import image", "Import image",
PodmanJsonContext.Default.ImageImportDto,
content, content,
[ [
("changes", changes), ("changes", changes),
@ -154,7 +159,7 @@ public partial class PodmanClient {
public Task<Result<ImageLoadDto?>> LoadImageAsync(Stream tarball, CancellationToken cancellationToken = default) { public Task<Result<ImageLoadDto?>> LoadImageAsync(Stream tarball, CancellationToken cancellationToken = default) {
var content = new StreamContent(tarball); var content = new StreamContent(tarball);
content.Headers.ContentType = new MediaTypeHeaderValue("application/x-tar"); content.Headers.ContentType = new MediaTypeHeaderValue("application/x-tar");
return PostLibpodAsync<ImageLoadDto>("/libpod/images/load", "Load image", content, cancellationToken: cancellationToken); return PostLibpodAsync<ImageLoadDto>("/libpod/images/load", "Load image", PodmanJsonContext.Default.ImageLoadDto, content, cancellationToken: cancellationToken);
} }
public Task<Result<Stream?>> ExportImagesAsync( public Task<Result<Stream?>> ExportImagesAsync(

View File

@ -1,3 +1,4 @@
using MaksIT.PodmanClientDotNet;
using MaksIT.PodmanClientDotNet.Dtos.Manifest; using MaksIT.PodmanClientDotNet.Dtos.Manifest;
using MaksIT.Results; using MaksIT.Results;
@ -13,6 +14,7 @@ public partial class PodmanClient {
PostLibpodAsync<ManifestCreateDto>( PostLibpodAsync<ManifestCreateDto>(
"/libpod/manifests/create", "/libpod/manifests/create",
"Create manifest", "Create manifest",
PodmanJsonContext.Default.ManifestCreateDto,
query: [ query: [
("name", name), ("name", name),
("image", image), ("image", image),
@ -30,10 +32,10 @@ public partial class PodmanClient {
); );
public Task<Result<ManifestInspectDto?>> InspectManifestAsync(string name, CancellationToken cancellationToken = default) => public Task<Result<ManifestInspectDto?>> InspectManifestAsync(string name, CancellationToken cancellationToken = default) =>
GetJsonAsync<ManifestInspectDto>($"{ManifestPath(name)}/json", "Inspect manifest", cancellationToken: cancellationToken); GetJsonAsync<ManifestInspectDto>($"{ManifestPath(name)}/json", "Inspect manifest", PodmanJsonContext.Default.ManifestInspectDto, cancellationToken: cancellationToken);
public Task<Result> AddToManifestAsync(string name, ManifestAddRequestDto request, CancellationToken cancellationToken = default) => public Task<Result> AddToManifestAsync(string name, ManifestAddRequestDto request, CancellationToken cancellationToken = default) =>
PostJsonWithoutBodyAsync($"{ManifestPath(name)}/add", "Add to manifest", request, cancellationToken: cancellationToken); PostJsonWithoutBodyAsync($"{ManifestPath(name)}/add", "Add to manifest", request, PodmanJsonContext.Default.ManifestAddRequestDto, cancellationToken: cancellationToken);
public Task<Result> PushManifestAsync( public Task<Result> PushManifestAsync(
string name, string name,

View File

@ -1,3 +1,4 @@
using MaksIT.PodmanClientDotNet;
using MaksIT.PodmanClientDotNet.Dtos.Network; using MaksIT.PodmanClientDotNet.Dtos.Network;
using MaksIT.PodmanClientDotNet.Models.Network; using MaksIT.PodmanClientDotNet.Models.Network;
using MaksIT.Results; using MaksIT.Results;
@ -11,14 +12,16 @@ public partial class PodmanClient {
"/libpod/networks/create", "/libpod/networks/create",
"Create network", "Create network",
request, request,
PodmanJsonContext.Default.NetworkCreateRequest,
PodmanJsonContext.Default.NetworkListEntryDto,
cancellationToken: cancellationToken cancellationToken: cancellationToken
); );
public Task<Result<List<NetworkListEntryDto>?>> ListNetworksAsync(CancellationToken cancellationToken = default) => public Task<Result<List<NetworkListEntryDto>?>> ListNetworksAsync(CancellationToken cancellationToken = default) =>
GetJsonAsync<List<NetworkListEntryDto>>("/libpod/networks/json", "List networks", cancellationToken: cancellationToken); GetJsonAsync<List<NetworkListEntryDto>>("/libpod/networks/json", "List networks", PodmanJsonContext.Default.ListNetworkListEntryDto, cancellationToken: cancellationToken);
public Task<Result<NetworkInspectDto?>> InspectNetworkAsync(string name, CancellationToken cancellationToken = default) => public Task<Result<NetworkInspectDto?>> InspectNetworkAsync(string name, CancellationToken cancellationToken = default) =>
GetJsonAsync<NetworkInspectDto>($"/libpod/networks/{Uri.EscapeDataString(name)}/json", "Inspect network", cancellationToken: cancellationToken); GetJsonAsync<NetworkInspectDto>($"/libpod/networks/{Uri.EscapeDataString(name)}/json", "Inspect network", PodmanJsonContext.Default.NetworkInspectDto, cancellationToken: cancellationToken);
public Task<Result> DeleteNetworkAsync(string name, CancellationToken cancellationToken = default) => public Task<Result> DeleteNetworkAsync(string name, CancellationToken cancellationToken = default) =>
DeleteWithoutBodyAsync($"/libpod/networks/{Uri.EscapeDataString(name)}", "Delete network", cancellationToken: cancellationToken); DeleteWithoutBodyAsync($"/libpod/networks/{Uri.EscapeDataString(name)}", "Delete network", cancellationToken: cancellationToken);
@ -32,6 +35,7 @@ public partial class PodmanClient {
$"/libpod/networks/{Uri.EscapeDataString(name)}/connect", $"/libpod/networks/{Uri.EscapeDataString(name)}/connect",
"Connect network", "Connect network",
request, request,
PodmanJsonContext.Default.NetworkConnectRequest,
cancellationToken: cancellationToken cancellationToken: cancellationToken
); );
@ -44,6 +48,7 @@ public partial class PodmanClient {
$"/libpod/networks/{Uri.EscapeDataString(name)}/disconnect", $"/libpod/networks/{Uri.EscapeDataString(name)}/disconnect",
"Disconnect network", "Disconnect network",
request, request,
PodmanJsonContext.Default.NetworkDisconnectRequest,
cancellationToken: cancellationToken cancellationToken: cancellationToken
); );
} }

View File

@ -1,3 +1,4 @@
using MaksIT.PodmanClientDotNet;
using MaksIT.PodmanClientDotNet.Dtos.Common; using MaksIT.PodmanClientDotNet.Dtos.Common;
using MaksIT.PodmanClientDotNet.Dtos.Pod; using MaksIT.PodmanClientDotNet.Dtos.Pod;
using MaksIT.PodmanClientDotNet.Models.Pod; using MaksIT.PodmanClientDotNet.Models.Pod;
@ -5,18 +6,19 @@ using MaksIT.Results;
public partial class PodmanClient { public partial class PodmanClient {
public Task<Result<PodListEntryDto?>> CreatePodAsync(PodCreateRequest request, CancellationToken cancellationToken = default) => public Task<Result<PodListEntryDto?>> CreatePodAsync(PodCreateRequest request, CancellationToken cancellationToken = default) =>
PostJsonAsync<PodCreateRequest, PodListEntryDto>("/libpod/pods/create", "Create pod", request, cancellationToken: cancellationToken); PostJsonAsync<PodCreateRequest, PodListEntryDto>("/libpod/pods/create", "Create pod", request, PodmanJsonContext.Default.PodCreateRequest, PodmanJsonContext.Default.PodListEntryDto, cancellationToken: cancellationToken);
public Task<Result<List<PodListEntryDto>?>> ListPodsAsync(bool all = false, CancellationToken cancellationToken = default) => public Task<Result<List<PodListEntryDto>?>> ListPodsAsync(bool all = false, CancellationToken cancellationToken = default) =>
GetJsonAsync<List<PodListEntryDto>>( GetJsonAsync<List<PodListEntryDto>>(
"/libpod/pods/json", "/libpod/pods/json",
"List pods", "List pods",
PodmanJsonContext.Default.ListPodListEntryDto,
[("all", all.ToString().ToLowerInvariant())], [("all", all.ToString().ToLowerInvariant())],
cancellationToken cancellationToken
); );
public Task<Result<PodInspectDto?>> InspectPodAsync(string name, CancellationToken cancellationToken = default) => public Task<Result<PodInspectDto?>> InspectPodAsync(string name, CancellationToken cancellationToken = default) =>
GetJsonAsync<PodInspectDto>($"/libpod/pods/{Uri.EscapeDataString(name)}/json", "Inspect pod", cancellationToken: cancellationToken); GetJsonAsync<PodInspectDto>($"/libpod/pods/{Uri.EscapeDataString(name)}/json", "Inspect pod", PodmanJsonContext.Default.PodInspectDto, cancellationToken: cancellationToken);
public Task<Result> PodExistsAsync(string name, CancellationToken cancellationToken = default) => public Task<Result> PodExistsAsync(string name, CancellationToken cancellationToken = default) =>
GetWithoutBodyAsync($"/libpod/pods/{Uri.EscapeDataString(name)}/exists", "Pod exists", cancellationToken: cancellationToken); GetWithoutBodyAsync($"/libpod/pods/{Uri.EscapeDataString(name)}/exists", "Pod exists", cancellationToken: cancellationToken);
@ -63,11 +65,11 @@ public partial class PodmanClient {
PostWithoutBodyAsync($"/libpod/pods/{Uri.EscapeDataString(name)}/unpause", "Unpause pod", cancellationToken: cancellationToken); PostWithoutBodyAsync($"/libpod/pods/{Uri.EscapeDataString(name)}/unpause", "Unpause pod", cancellationToken: cancellationToken);
public Task<Result<PruneReportDto?>> PrunePodsAsync(CancellationToken cancellationToken = default) => public Task<Result<PruneReportDto?>> PrunePodsAsync(CancellationToken cancellationToken = default) =>
PostLibpodAsync<PruneReportDto>("/libpod/pods/prune", "Prune pods", cancellationToken: cancellationToken); PostLibpodAsync<PruneReportDto>("/libpod/pods/prune", "Prune pods", PodmanJsonContext.Default.PruneReportDto, cancellationToken: cancellationToken);
public Task<Result<PodTopDto?>> TopPodAsync(string name, CancellationToken cancellationToken = default) => public Task<Result<PodTopDto?>> TopPodAsync(string name, CancellationToken cancellationToken = default) =>
GetJsonAsync<PodTopDto>($"/libpod/pods/{Uri.EscapeDataString(name)}/top", "Top pod", cancellationToken: cancellationToken); GetJsonAsync<PodTopDto>($"/libpod/pods/{Uri.EscapeDataString(name)}/top", "Top pod", PodmanJsonContext.Default.PodTopDto, cancellationToken: cancellationToken);
public Task<Result<PodStatsResponseDto?>> GetPodsStatsAsync(CancellationToken cancellationToken = default) => public Task<Result<PodStatsResponseDto?>> GetPodsStatsAsync(CancellationToken cancellationToken = default) =>
GetJsonAsync<PodStatsResponseDto>("/libpod/pods/stats", "Get pods stats", cancellationToken: cancellationToken); GetJsonAsync<PodStatsResponseDto>("/libpod/pods/stats", "Get pods stats", PodmanJsonContext.Default.PodStatsResponseDto, cancellationToken: cancellationToken);
} }

View File

@ -1,8 +1,9 @@
using MaksIT.PodmanClientDotNet;
using System.Text; using System.Text;
using System.Text.Json;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using MaksIT.Core.Extensions;
using MaksIT.PodmanClientDotNet.Dtos.Build; using MaksIT.PodmanClientDotNet.Dtos.Build;
using MaksIT.PodmanClientDotNet.Dtos.Image; using MaksIT.PodmanClientDotNet.Dtos.Image;
using MaksIT.PodmanClientDotNet.Internal; using MaksIT.PodmanClientDotNet.Internal;
@ -65,7 +66,7 @@ public partial class PodmanClient {
Height = height, Height = height,
Width = width, Width = width,
}; };
var body = Encoding.UTF8.GetBytes(startExecRequest.ToJson()); var body = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(startExecRequest, PodmanJsonContext.Default.StartExecRequest));
var query = BuildQuery([]); var query = BuildQuery([]);
var hijack = await PodmanHijackConnection.ConnectAsync( var hijack = await PodmanHijackConnection.ConnectAsync(
@ -122,7 +123,7 @@ public partial class PodmanClient {
return streamResult.ToResultOfType<IPodmanProgressSession<PullImageResponseDto>>(null!); return streamResult.ToResultOfType<IPodmanProgressSession<PullImageResponseDto>>(null!);
return Result<IPodmanProgressSession<PullImageResponseDto>?>.Ok( return Result<IPodmanProgressSession<PullImageResponseDto>?>.Ok(
new PodmanProgressSession<PullImageResponseDto>(streamResult.Value!) new PodmanProgressSession<PullImageResponseDto>(streamResult.Value!, PodmanJsonContext.Default.PullImageResponseDto)
); );
} }
@ -169,7 +170,7 @@ public partial class PodmanClient {
return streamResult.ToResultOfType<IPodmanProgressSession<BuildProgressLineDto>>(null!); return streamResult.ToResultOfType<IPodmanProgressSession<BuildProgressLineDto>>(null!);
return Result<IPodmanProgressSession<BuildProgressLineDto>?>.Ok( return Result<IPodmanProgressSession<BuildProgressLineDto>?>.Ok(
new PodmanProgressSession<BuildProgressLineDto>(streamResult.Value!) new PodmanProgressSession<BuildProgressLineDto>(streamResult.Value!, PodmanJsonContext.Default.BuildProgressLineDto)
); );
} }

View File

@ -1,22 +1,23 @@
using MaksIT.PodmanClientDotNet;
using MaksIT.PodmanClientDotNet.Dtos.Common; using MaksIT.PodmanClientDotNet.Dtos.Common;
using MaksIT.PodmanClientDotNet.Dtos.System; using MaksIT.PodmanClientDotNet.Dtos.System;
using MaksIT.Results; using MaksIT.Results;
public partial class PodmanClient { public partial class PodmanClient {
public Task<Result<LibpodPingDto?>> PingAsync(CancellationToken cancellationToken = default) => public Task<Result<LibpodPingDto?>> PingAsync(CancellationToken cancellationToken = default) =>
GetJsonAsync<LibpodPingDto>("/libpod/_ping", "Ping", cancellationToken: cancellationToken); GetJsonAsync<LibpodPingDto>("/libpod/_ping", "Ping", PodmanJsonContext.Default.LibpodPingDto, cancellationToken: cancellationToken);
public Task<Result<LibpodVersionDto?>> GetVersionAsync(CancellationToken cancellationToken = default) => public Task<Result<LibpodVersionDto?>> GetVersionAsync(CancellationToken cancellationToken = default) =>
GetJsonAsync<LibpodVersionDto>("/libpod/version", "Get version", cancellationToken: cancellationToken); GetJsonAsync<LibpodVersionDto>("/libpod/version", "Get version", PodmanJsonContext.Default.LibpodVersionDto, cancellationToken: cancellationToken);
public Task<Result<InfoDto?>> GetInfoAsync(CancellationToken cancellationToken = default) => public Task<Result<InfoDto?>> GetInfoAsync(CancellationToken cancellationToken = default) =>
GetJsonAsync<InfoDto>("/libpod/info", "Get info", cancellationToken: cancellationToken); GetJsonAsync<InfoDto>("/libpod/info", "Get info", PodmanJsonContext.Default.InfoDto, cancellationToken: cancellationToken);
public Task<Result<SystemDfDto?>> GetSystemDiskUsageAsync(CancellationToken cancellationToken = default) => public Task<Result<SystemDfDto?>> GetSystemDiskUsageAsync(CancellationToken cancellationToken = default) =>
GetJsonAsync<SystemDfDto>("/libpod/system/df", "Get system disk usage", cancellationToken: cancellationToken); GetJsonAsync<SystemDfDto>("/libpod/system/df", "Get system disk usage", PodmanJsonContext.Default.SystemDfDto, cancellationToken: cancellationToken);
public Task<Result<PruneReportDto?>> PruneSystemAsync(CancellationToken cancellationToken = default) => public Task<Result<PruneReportDto?>> PruneSystemAsync(CancellationToken cancellationToken = default) =>
PostLibpodAsync<PruneReportDto>("/libpod/system/prune", "Prune system", cancellationToken: cancellationToken); PostLibpodAsync<PruneReportDto>("/libpod/system/prune", "Prune system", PodmanJsonContext.Default.PruneReportDto, cancellationToken: cancellationToken);
public Task<Result<Stream?>> GetEventsAsync(CancellationToken cancellationToken = default) => public Task<Result<Stream?>> GetEventsAsync(CancellationToken cancellationToken = default) =>
GetStreamAsync("/libpod/events", "Get events", cancellationToken: cancellationToken); GetStreamAsync("/libpod/events", "Get events", cancellationToken: cancellationToken);

View File

@ -1,3 +1,4 @@
using MaksIT.PodmanClientDotNet;
using MaksIT.PodmanClientDotNet.Dtos.Common; using MaksIT.PodmanClientDotNet.Dtos.Common;
using MaksIT.PodmanClientDotNet.Dtos.Volume; using MaksIT.PodmanClientDotNet.Dtos.Volume;
using MaksIT.PodmanClientDotNet.Models.Volume; using MaksIT.PodmanClientDotNet.Models.Volume;
@ -12,14 +13,16 @@ public partial class PodmanClient {
"/libpod/volumes/create", "/libpod/volumes/create",
"Create volume", "Create volume",
request, request,
PodmanJsonContext.Default.CreateVolumeRequest,
PodmanJsonContext.Default.VolumeInspectResponseDto,
cancellationToken: cancellationToken cancellationToken: cancellationToken
); );
public Task<Result<List<VolumeListEntryDto>?>> ListVolumesAsync(CancellationToken cancellationToken = default) => public Task<Result<List<VolumeListEntryDto>?>> ListVolumesAsync(CancellationToken cancellationToken = default) =>
GetJsonAsync<List<VolumeListEntryDto>>("/libpod/volumes/json", "List volumes", cancellationToken: cancellationToken); GetJsonAsync<List<VolumeListEntryDto>>("/libpod/volumes/json", "List volumes", PodmanJsonContext.Default.ListVolumeListEntryDto, cancellationToken: cancellationToken);
public Task<Result<VolumeInspectResponseDto?>> InspectVolumeAsync(string name, CancellationToken cancellationToken = default) => public Task<Result<VolumeInspectResponseDto?>> InspectVolumeAsync(string name, CancellationToken cancellationToken = default) =>
GetJsonAsync<VolumeInspectResponseDto>($"/libpod/volumes/{Uri.EscapeDataString(name)}/json", "Inspect volume", cancellationToken: cancellationToken); GetJsonAsync<VolumeInspectResponseDto>($"/libpod/volumes/{Uri.EscapeDataString(name)}/json", "Inspect volume", PodmanJsonContext.Default.VolumeInspectResponseDto, cancellationToken: cancellationToken);
public Task<Result> DeleteVolumeAsync(string name, bool force = false, CancellationToken cancellationToken = default) => public Task<Result> DeleteVolumeAsync(string name, bool force = false, CancellationToken cancellationToken = default) =>
DeleteWithoutBodyAsync( DeleteWithoutBodyAsync(
@ -30,5 +33,5 @@ public partial class PodmanClient {
); );
public Task<Result<PruneReportDto?>> PruneVolumesAsync(CancellationToken cancellationToken = default) => public Task<Result<PruneReportDto?>> PruneVolumesAsync(CancellationToken cancellationToken = default) =>
PostLibpodAsync<PruneReportDto>("/libpod/volumes/prune", "Prune volumes", cancellationToken: cancellationToken); PostLibpodAsync<PruneReportDto>("/libpod/volumes/prune", "Prune volumes", PodmanJsonContext.Default.PruneReportDto, cancellationToken: cancellationToken);
} }

View File

@ -39,6 +39,9 @@
<!-- Deterministic builds for reproducibility --> <!-- Deterministic builds for reproducibility -->
<Deterministic>true</Deterministic> <Deterministic>true</Deterministic>
<ContinuousIntegrationBuild Condition="'$(CI)' == 'true'">true</ContinuousIntegrationBuild> <ContinuousIntegrationBuild Condition="'$(CI)' == 'true'">true</ContinuousIntegrationBuild>
<!-- AOT / trimming compatibility -->
<IsAotCompatible>true</IsAotCompatible>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@ -46,7 +49,6 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="MaksIT.Core" Version="1.6.8" />
<PackageReference Include="MaksIT.Results" Version="2.0.3" /> <PackageReference Include="MaksIT.Results" Version="2.0.3" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.9" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.9" />
<PackageReference Include="Microsoft.Extensions.Http" Version="10.0.9" /> <PackageReference Include="Microsoft.Extensions.Http" Version="10.0.9" />

View File

@ -0,0 +1,116 @@
using System.Text.Json.Serialization;
using MaksIT.PodmanClientDotNet.Dtos.Build;
using MaksIT.PodmanClientDotNet.Dtos.Common;
using MaksIT.PodmanClientDotNet.Dtos.Container;
using MaksIT.PodmanClientDotNet.Dtos.Exec;
using MaksIT.PodmanClientDotNet.Dtos.Generate;
using MaksIT.PodmanClientDotNet.Dtos.Image;
using MaksIT.PodmanClientDotNet.Dtos.Manifest;
using MaksIT.PodmanClientDotNet.Dtos.Network;
using MaksIT.PodmanClientDotNet.Dtos.Pod;
using MaksIT.PodmanClientDotNet.Dtos.System;
using MaksIT.PodmanClientDotNet.Dtos.Volume;
using MaksIT.PodmanClientDotNet.Models.Container;
using MaksIT.PodmanClientDotNet.Models.Exec;
using MaksIT.PodmanClientDotNet.Models.Network;
using MaksIT.PodmanClientDotNet.Models.Pod;
using MaksIT.PodmanClientDotNet.Models.Volume;
namespace MaksIT.PodmanClientDotNet;
// ----- Response / DTO types -----
// Common
[JsonSerializable(typeof(ErrorResponseDto))]
[JsonSerializable(typeof(IdResponseDto))]
[JsonSerializable(typeof(PruneReportDto))]
[JsonSerializable(typeof(ReportDto))]
// Build
[JsonSerializable(typeof(BuildProgressLineDto))]
[JsonSerializable(typeof(BuildReportDto))]
// Container
[JsonSerializable(typeof(ContainerChangesDto))]
[JsonSerializable(typeof(ContainerCommitDto))]
[JsonSerializable(typeof(ContainerHealthCheckDto))]
[JsonSerializable(typeof(ContainerInspectDto))]
[JsonSerializable(typeof(ContainerListEntryDto))]
[JsonSerializable(typeof(List<ContainerListEntryDto>))]
[JsonSerializable(typeof(ContainerMountDto))]
[JsonSerializable(typeof(ContainerStatsDto))]
[JsonSerializable(typeof(Dictionary<string, ContainerStatsDto>))]
[JsonSerializable(typeof(ContainerTopDto))]
[JsonSerializable(typeof(ContainerWaitDto))]
[JsonSerializable(typeof(CreateContainerResponseDto))]
[JsonSerializable(typeof(DeleteContainerResponseDto))]
[JsonSerializable(typeof(DeleteContainerResponseDto[]))]
[JsonSerializable(typeof(MountedContainersResponseDto))]
// Exec
[JsonSerializable(typeof(CreateExecResponseDto))]
[JsonSerializable(typeof(InspectExecResponseDto))]
// Generate
[JsonSerializable(typeof(GenerateSystemdDto))]
[JsonSerializable(typeof(PlayKubeReportDto))]
// Image
[JsonSerializable(typeof(ImageChangesDto))]
[JsonSerializable(typeof(ImageDeleteDto))]
[JsonSerializable(typeof(ImageDeleteDto[]))]
[JsonSerializable(typeof(ImageHistoryEntryDto))]
[JsonSerializable(typeof(List<ImageHistoryEntryDto>))]
[JsonSerializable(typeof(ImageImportDto))]
[JsonSerializable(typeof(ImageInspectDto))]
[JsonSerializable(typeof(ImageListEntryDto))]
[JsonSerializable(typeof(List<ImageListEntryDto>))]
[JsonSerializable(typeof(ImageLoadDto))]
[JsonSerializable(typeof(ImageRemoveResponseDto))]
[JsonSerializable(typeof(ImageSearchResultDto))]
[JsonSerializable(typeof(List<ImageSearchResultDto>))]
[JsonSerializable(typeof(ImageTreeDto))]
[JsonSerializable(typeof(PullImageResponseDto))]
// Manifest
[JsonSerializable(typeof(ManifestCreateDto))]
[JsonSerializable(typeof(ManifestInspectDto))]
// Network
[JsonSerializable(typeof(NetworkInspectDto))]
[JsonSerializable(typeof(NetworkListEntryDto))]
[JsonSerializable(typeof(List<NetworkListEntryDto>))]
// Pod
[JsonSerializable(typeof(PodInspectDto))]
[JsonSerializable(typeof(PodListEntryDto))]
[JsonSerializable(typeof(List<PodListEntryDto>))]
[JsonSerializable(typeof(PodTopDto))]
[JsonSerializable(typeof(PodStatsResponseDto))]
// System
[JsonSerializable(typeof(InfoDto))]
[JsonSerializable(typeof(LibpodPingDto))]
[JsonSerializable(typeof(LibpodVersionDto))]
[JsonSerializable(typeof(SystemDfDto))]
// Volume
[JsonSerializable(typeof(VolumeInspectResponseDto))]
[JsonSerializable(typeof(VolumeListEntryDto))]
[JsonSerializable(typeof(List<VolumeListEntryDto>))]
// ----- Request / model types -----
[JsonSerializable(typeof(CreateContainerRequest))]
[JsonSerializable(typeof(CreateExecRequest))]
[JsonSerializable(typeof(StartExecRequest))]
[JsonSerializable(typeof(NetworkCreateRequest))]
[JsonSerializable(typeof(NetworkConnectRequest))]
[JsonSerializable(typeof(NetworkDisconnectRequest))]
[JsonSerializable(typeof(PodCreateRequest))]
[JsonSerializable(typeof(CreateVolumeRequest))]
[JsonSerializable(typeof(ManifestAddRequestDto))]
[JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.Unspecified, PropertyNameCaseInsensitive = true)]
internal sealed partial class PodmanJsonContext : JsonSerializerContext {
}

View File

@ -1,15 +1,16 @@
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization.Metadata;
using MaksIT.Core.Extensions;
namespace MaksIT.PodmanClientDotNet.Streaming; namespace MaksIT.PodmanClientDotNet.Streaming;
internal sealed class PodmanProgressSession<T> : IPodmanProgressSession<T> { internal sealed class PodmanProgressSession<T> : IPodmanProgressSession<T> {
private readonly Stream _stream; private readonly Stream _stream;
private readonly bool _ownsStream; private readonly bool _ownsStream;
private readonly JsonTypeInfo<T> _typeInfo;
internal PodmanProgressSession(Stream stream, bool ownsStream = true) { internal PodmanProgressSession(Stream stream, JsonTypeInfo<T> typeInfo, bool ownsStream = true) {
_stream = stream ?? throw new ArgumentNullException(nameof(stream)); _stream = stream ?? throw new ArgumentNullException(nameof(stream));
_typeInfo = typeInfo ?? throw new ArgumentNullException(nameof(typeInfo));
_ownsStream = ownsStream; _ownsStream = ownsStream;
} }
@ -24,7 +25,7 @@ internal sealed class PodmanProgressSession<T> : IPodmanProgressSession<T> {
T? item; T? item;
try { try {
item = line.ToObject<T>(); item = JsonSerializer.Deserialize(line, _typeInfo);
} }
catch (JsonException) { catch (JsonException) {
continue; continue;

View File

@ -10,7 +10,7 @@ public class PodmanProgressSessionTests {
public async Task ReadProgressAsync_ParsesNdjsonLines() { public async Task ReadProgressAsync_ParsesNdjsonLines() {
var json = "{\"status\":\"Pulling fs layer\"}\n{\"id\":\"abc\"}\n"; var json = "{\"status\":\"Pulling fs layer\"}\n{\"id\":\"abc\"}\n";
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(json)); using var stream = new MemoryStream(Encoding.UTF8.GetBytes(json));
await using var session = new PodmanProgressSession<PullImageResponseDto>(stream, ownsStream: false); await using var session = new PodmanProgressSession<PullImageResponseDto>(stream, PodmanJsonContext.Default.PullImageResponseDto, ownsStream: false);
var items = new List<PullImageResponseDto>(); var items = new List<PullImageResponseDto>();
await foreach (var item in session.ReadProgressAsync(TestContext.Current.CancellationToken)) await foreach (var item in session.ReadProgressAsync(TestContext.Current.CancellationToken))