provider cleanup

This commit is contained in:
Emil Lerch 2025-07-12 18:27:36 -07:00
parent 7490ff3bc5
commit fd8242784d
Signed by: lobo
GPG key ID: A7B62D657EF764F8
6 changed files with 233 additions and 219 deletions

View file

@ -4,10 +4,10 @@ const ArrayList = std.ArrayList;
const atom = @import("atom.zig"); const atom = @import("atom.zig");
const Release = @import("main.zig").Release; const Release = @import("main.zig").Release;
const github = @import("providers/github.zig"); const GitHub = @import("providers/GitHub.zig");
const gitlab = @import("providers/gitlab.zig"); const GitLab = @import("providers/GitLab.zig");
const codeberg = @import("providers/codeberg.zig"); const Codeberg = @import("providers/Codeberg.zig");
const sourcehut = @import("providers/sourcehut.zig"); const SourceHut = @import("providers/SourceHut.zig");
const config = @import("config.zig"); const config = @import("config.zig");
test "Atom feed validates against W3C validator" { test "Atom feed validates against W3C validator" {
@ -60,8 +60,8 @@ test "GitHub provider integration" {
return; return;
} }
var provider = github.GitHubProvider{}; var provider = GitHub.init(app_config.github_token.?);
const releases = provider.fetchReleases(allocator, app_config.github_token.?) catch |err| { const releases = provider.fetchReleases(allocator) catch |err| {
std.debug.print("GitHub provider error: {}\n", .{err}); std.debug.print("GitHub provider error: {}\n", .{err});
return; return;
}; };
@ -98,8 +98,8 @@ test "GitLab provider integration" {
return; return;
} }
var provider = gitlab.GitLabProvider{}; var provider = GitLab.init(app_config.gitlab_token.?);
const releases = provider.fetchReleases(allocator, app_config.gitlab_token.?) catch |err| { const releases = provider.fetchReleases(allocator) catch |err| {
std.debug.print("GitLab provider error: {}\n", .{err}); std.debug.print("GitLab provider error: {}\n", .{err});
return; // Skip test if provider fails return; // Skip test if provider fails
}; };
@ -139,8 +139,8 @@ test "Codeberg provider integration" {
return; return;
} }
var provider = codeberg.CodebergProvider{}; var provider = Codeberg.init(app_config.codeberg_token.?);
const releases = provider.fetchReleases(allocator, app_config.codeberg_token.?) catch |err| { const releases = provider.fetchReleases(allocator) catch |err| {
std.debug.print("Codeberg provider error: {}\n", .{err}); std.debug.print("Codeberg provider error: {}\n", .{err});
return; // Skip test if provider fails return; // Skip test if provider fails
}; };
@ -177,8 +177,8 @@ test "SourceHut provider integration" {
return; return;
} }
var provider = sourcehut.SourceHutProvider{}; var provider = SourceHut.init(app_config.sourcehut.?.token.?, app_config.sourcehut.?.repositories);
const releases = provider.fetchReleasesForRepos(allocator, app_config.sourcehut.?.repositories, app_config.sourcehut.?.token) catch |err| { const releases = provider.fetchReleases(allocator) catch |err| {
std.debug.print("SourceHut provider error: {}\n", .{err}); std.debug.print("SourceHut provider error: {}\n", .{err});
return; // Skip test if provider fails return; // Skip test if provider fails
}; };

View file

@ -5,10 +5,10 @@ const ArrayList = std.ArrayList;
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
const Thread = std.Thread; const Thread = std.Thread;
const github = @import("providers/github.zig"); const GitHub = @import("providers/GitHub.zig");
const gitlab = @import("providers/gitlab.zig"); const GitLab = @import("providers/GitLab.zig");
const codeberg = @import("providers/codeberg.zig"); const Codeberg = @import("providers/Codeberg.zig");
const sourcehut = @import("providers/sourcehut.zig"); const SourceHut = @import("providers/SourceHut.zig");
const atom = @import("atom.zig"); const atom = @import("atom.zig");
const config = @import("config.zig"); const config = @import("config.zig");
const zeit = @import("zeit"); const zeit = @import("zeit");
@ -102,33 +102,27 @@ pub fn main() !u8 {
defer providers.deinit(); defer providers.deinit();
// Initialize providers with their tokens (need to persist for the lifetime of the program) // Initialize providers with their tokens (need to persist for the lifetime of the program)
var github_provider: ?github.GitHubProvider = null; var github_provider: ?GitHub = null;
var gitlab_provider: ?gitlab.GitLabProvider = null; var gitlab_provider: ?GitLab = null;
var codeberg_provider: ?codeberg.CodebergProvider = null; var codeberg_provider: ?Codeberg = null;
var sourcehut_provider: ?sourcehut.SourceHutProvider = null; var sourcehut_provider: ?SourceHut = null;
if (app_config.github_token) |token| { if (app_config.github_token) |token| {
github_provider = github.GitHubProvider.init(token); github_provider = GitHub.init(token);
try providers.append(Provider.init(&github_provider.?)); try providers.append(github_provider.?.provider());
} }
if (app_config.gitlab_token) |token| { if (app_config.gitlab_token) |token| {
gitlab_provider = gitlab.GitLabProvider.init(token); gitlab_provider = GitLab.init(token);
try providers.append(Provider.init(&gitlab_provider.?)); try providers.append(gitlab_provider.?.provider());
} }
if (app_config.codeberg_token) |token| { if (app_config.codeberg_token) |token| {
codeberg_provider = codeberg.CodebergProvider.init(token); codeberg_provider = Codeberg.init(token);
try providers.append(Provider.init(&codeberg_provider.?)); try providers.append(codeberg_provider.?.provider());
}
// Configure SourceHut provider with repositories if available
if (app_config.sourcehut) |sh_config| {
if (sh_config.repositories.len > 0 and sh_config.token != null) {
sourcehut_provider = sourcehut.SourceHutProvider.init(sh_config.token.?, sh_config.repositories);
try providers.append(Provider.init(&sourcehut_provider.?));
}
} }
if (app_config.sourcehut) |sh_config| if (sh_config.repositories.len > 0 and sh_config.token != null) {
sourcehut_provider = SourceHut.init(sh_config.token.?, sh_config.repositories);
try providers.append(sourcehut_provider.?.provider());
};
// Fetch releases from all providers concurrently using thread pool // Fetch releases from all providers concurrently using thread pool
const provider_results = try fetchReleasesFromAllProviders(allocator, providers.items, existing_releases.items); const provider_results = try fetchReleasesFromAllProviders(allocator, providers.items, existing_releases.items);

View file

@ -6,15 +6,21 @@ const ArrayList = std.ArrayList;
const zeit = @import("zeit"); const zeit = @import("zeit");
const Release = @import("../main.zig").Release; const Release = @import("../main.zig").Release;
const Provider = @import("../Provider.zig");
pub const CodebergProvider = struct {
token: []const u8, token: []const u8,
pub fn init(token: []const u8) CodebergProvider { const Self = @This();
return CodebergProvider{ .token = token };
pub fn init(token: []const u8) Self {
return Self{ .token = token };
} }
pub fn fetchReleases(self: *@This(), allocator: Allocator) !ArrayList(Release) { pub fn provider(self: *Self) Provider {
return Provider.init(self);
}
pub fn fetchReleases(self: *Self, allocator: Allocator) !ArrayList(Release) {
var client = http.Client{ .allocator = allocator }; var client = http.Client{ .allocator = allocator };
defer client.deinit(); defer client.deinit();
@ -46,11 +52,10 @@ pub const CodebergProvider = struct {
return releases; return releases;
} }
pub fn getName(self: *@This()) []const u8 { pub fn getName(self: *Self) []const u8 {
_ = self; _ = self;
return "codeberg"; return "codeberg";
} }
};
fn getStarredRepos(allocator: Allocator, client: *http.Client, token: []const u8) !ArrayList([]const u8) { fn getStarredRepos(allocator: Allocator, client: *http.Client, token: []const u8) !ArrayList([]const u8) {
var repos = ArrayList([]const u8).init(allocator); var repos = ArrayList([]const u8).init(allocator);
@ -262,10 +267,10 @@ fn parseTimestamp(date_str: []const u8) !i64 {
test "codeberg provider" { test "codeberg provider" {
const allocator = std.testing.allocator; const allocator = std.testing.allocator;
var provider = CodebergProvider.init(""); var codeberg_provider = init("");
// Test with empty token (should fail gracefully) // Test with empty token (should fail gracefully)
const releases = provider.fetchReleases(allocator) catch |err| { const releases = codeberg_provider.fetchReleases(allocator) catch |err| {
try std.testing.expect(err == error.Unauthorized or err == error.HttpRequestFailed); try std.testing.expect(err == error.Unauthorized or err == error.HttpRequestFailed);
return; return;
}; };
@ -276,7 +281,7 @@ test "codeberg provider" {
releases.deinit(); releases.deinit();
} }
try std.testing.expectEqualStrings("codeberg", provider.getName()); try std.testing.expectEqualStrings("codeberg", codeberg_provider.getName());
} }
test "codeberg release parsing with live data snapshot" { test "codeberg release parsing with live data snapshot" {

View file

@ -6,15 +6,21 @@ const ArrayList = std.ArrayList;
const zeit = @import("zeit"); const zeit = @import("zeit");
const Release = @import("../main.zig").Release; const Release = @import("../main.zig").Release;
const Provider = @import("../Provider.zig");
pub const GitHubProvider = struct {
token: []const u8, token: []const u8,
pub fn init(token: []const u8) GitHubProvider { const Self = @This();
return GitHubProvider{ .token = token };
pub fn init(token: []const u8) Self {
return Self{ .token = token };
} }
pub fn fetchReleases(self: *@This(), allocator: Allocator) !ArrayList(Release) { pub fn provider(self: *Self) Provider {
return Provider.init(self);
}
pub fn fetchReleases(self: *Self, allocator: Allocator) !ArrayList(Release) {
var client = http.Client{ .allocator = allocator }; var client = http.Client{ .allocator = allocator };
defer client.deinit(); defer client.deinit();
@ -43,11 +49,10 @@ pub const GitHubProvider = struct {
return releases; return releases;
} }
pub fn getName(self: *@This()) []const u8 { pub fn getName(self: *Self) []const u8 {
_ = self; _ = self;
return "github"; return "github";
} }
};
fn getStarredRepos(allocator: Allocator, client: *http.Client, token: []const u8) !ArrayList([]const u8) { fn getStarredRepos(allocator: Allocator, client: *http.Client, token: []const u8) !ArrayList([]const u8) {
var repos = ArrayList([]const u8).init(allocator); var repos = ArrayList([]const u8).init(allocator);
@ -174,10 +179,10 @@ fn parseTimestamp(date_str: []const u8) !i64 {
test "github provider" { test "github provider" {
const allocator = std.testing.allocator; const allocator = std.testing.allocator;
var provider = GitHubProvider.init(""); var github_provider = init("");
// Test with empty token (should fail gracefully) // Test with empty token (should fail gracefully)
const releases = provider.fetchReleases(allocator) catch |err| { const releases = github_provider.fetchReleases(allocator) catch |err| {
try std.testing.expect(err == error.HttpRequestFailed); try std.testing.expect(err == error.HttpRequestFailed);
return; return;
}; };
@ -188,7 +193,7 @@ test "github provider" {
releases.deinit(); releases.deinit();
} }
try std.testing.expectEqualStrings("github", provider.getName()); try std.testing.expectEqualStrings("github", github_provider.getName());
} }
test "github release parsing with live data snapshot" { test "github release parsing with live data snapshot" {

View file

@ -6,15 +6,21 @@ const ArrayList = std.ArrayList;
const zeit = @import("zeit"); const zeit = @import("zeit");
const Release = @import("../main.zig").Release; const Release = @import("../main.zig").Release;
const Provider = @import("../Provider.zig");
pub const GitLabProvider = struct {
token: []const u8, token: []const u8,
pub fn init(token: []const u8) GitLabProvider { const Self = @This();
return GitLabProvider{ .token = token };
pub fn init(token: []const u8) Self {
return Self{ .token = token };
} }
pub fn fetchReleases(self: *@This(), allocator: Allocator) !ArrayList(Release) { pub fn provider(self: *Self) Provider {
return Provider.init(self);
}
pub fn fetchReleases(self: *Self, allocator: Allocator) !ArrayList(Release) {
var client = http.Client{ .allocator = allocator }; var client = http.Client{ .allocator = allocator };
defer client.deinit(); defer client.deinit();
@ -46,11 +52,10 @@ pub const GitLabProvider = struct {
return releases; return releases;
} }
pub fn getName(self: *@This()) []const u8 { pub fn getName(self: *Self) []const u8 {
_ = self; _ = self;
return "gitlab"; return "gitlab";
} }
};
fn getStarredProjects(allocator: Allocator, client: *http.Client, token: []const u8) !ArrayList([]const u8) { fn getStarredProjects(allocator: Allocator, client: *http.Client, token: []const u8) !ArrayList([]const u8) {
var projects = ArrayList([]const u8).init(allocator); var projects = ArrayList([]const u8).init(allocator);
@ -258,10 +263,10 @@ fn parseTimestamp(date_str: []const u8) !i64 {
test "gitlab provider" { test "gitlab provider" {
const allocator = std.testing.allocator; const allocator = std.testing.allocator;
var provider = GitLabProvider.init(""); var gitlab_provider = init("");
// Test with empty token (should fail gracefully) // Test with empty token (should fail gracefully)
const releases = provider.fetchReleases(allocator) catch |err| { const releases = gitlab_provider.fetchReleases(allocator) catch |err| {
try std.testing.expect(err == error.HttpRequestFailed); try std.testing.expect(err == error.HttpRequestFailed);
return; return;
}; };
@ -272,7 +277,7 @@ test "gitlab provider" {
releases.deinit(); releases.deinit();
} }
try std.testing.expectEqualStrings("gitlab", provider.getName()); try std.testing.expectEqualStrings("gitlab", gitlab_provider.getName());
} }
test "gitlab release parsing with live data snapshot" { test "gitlab release parsing with live data snapshot" {

View file

@ -6,20 +6,26 @@ const ArrayList = std.ArrayList;
const zeit = @import("zeit"); const zeit = @import("zeit");
const Release = @import("../main.zig").Release; const Release = @import("../main.zig").Release;
const Provider = @import("../Provider.zig");
pub const SourceHutProvider = struct {
repositories: [][]const u8, repositories: [][]const u8,
token: []const u8, token: []const u8,
pub fn init(token: []const u8, repositories: [][]const u8) SourceHutProvider { const Self = @This();
return SourceHutProvider{ .token = token, .repositories = repositories };
pub fn init(token: []const u8, repositories: [][]const u8) Self {
return Self{ .token = token, .repositories = repositories };
} }
pub fn fetchReleases(self: *@This(), allocator: Allocator) !ArrayList(Release) { pub fn provider(self: *Self) Provider {
return Provider.init(self);
}
pub fn fetchReleases(self: *Self, allocator: Allocator) !ArrayList(Release) {
return self.fetchReleasesForRepos(allocator, self.repositories, self.token); return self.fetchReleasesForRepos(allocator, self.repositories, self.token);
} }
pub fn fetchReleasesForRepos(self: *@This(), allocator: Allocator, repositories: [][]const u8, token: ?[]const u8) !ArrayList(Release) { pub fn fetchReleasesForRepos(self: *Self, allocator: Allocator, repositories: [][]const u8, token: ?[]const u8) !ArrayList(Release) {
_ = self; _ = self;
var client = http.Client{ .allocator = allocator }; var client = http.Client{ .allocator = allocator };
defer client.deinit(); defer client.deinit();
@ -63,7 +69,7 @@ pub const SourceHutProvider = struct {
return releases; return releases;
} }
pub fn fetchReleasesForReposFiltered(self: *@This(), allocator: Allocator, repositories: [][]const u8, token: ?[]const u8, existing_releases: []const Release) !ArrayList(Release) { pub fn fetchReleasesForReposFiltered(self: *Self, allocator: Allocator, repositories: [][]const u8, token: ?[]const u8, existing_releases: []const Release) !ArrayList(Release) {
var latest_date: i64 = 0; var latest_date: i64 = 0;
for (existing_releases) |release| { for (existing_releases) |release| {
if (std.mem.eql(u8, release.provider, "sourcehut")) { if (std.mem.eql(u8, release.provider, "sourcehut")) {
@ -85,11 +91,10 @@ pub const SourceHutProvider = struct {
return filterNewReleases(allocator, all_releases.items, latest_date); return filterNewReleases(allocator, all_releases.items, latest_date);
} }
pub fn getName(self: *@This()) []const u8 { pub fn getName(self: *Self) []const u8 {
_ = self; _ = self;
return "sourcehut"; return "sourcehut";
} }
};
fn getRepoTags(allocator: Allocator, client: *http.Client, token: ?[]const u8, repo: []const u8) !ArrayList(Release) { fn getRepoTags(allocator: Allocator, client: *http.Client, token: ?[]const u8, repo: []const u8) !ArrayList(Release) {
var releases = ArrayList(Release).init(allocator); var releases = ArrayList(Release).init(allocator);
@ -343,10 +348,10 @@ test "sourcehut provider" {
const allocator = std.testing.allocator; const allocator = std.testing.allocator;
const repos = [_][]const u8{}; const repos = [_][]const u8{};
var provider = SourceHutProvider.init("", &repos); var sourcehut_provider = init("", &repos);
// Test with empty token (should fail gracefully) // Test with empty token (should fail gracefully)
const releases = provider.fetchReleases(allocator) catch |err| { const releases = sourcehut_provider.fetchReleases(allocator) catch |err| {
try std.testing.expect(err == error.HttpRequestFailed); try std.testing.expect(err == error.HttpRequestFailed);
return; return;
}; };
@ -357,7 +362,7 @@ test "sourcehut provider" {
releases.deinit(); releases.deinit();
} }
try std.testing.expectEqualStrings("sourcehut", provider.getName()); try std.testing.expectEqualStrings("sourcehut", sourcehut_provider.getName());
} }
test "sourcehut release parsing with live data snapshot" { test "sourcehut release parsing with live data snapshot" {