From 886c01b1a59c2cc683473c257d844ca6e3652a01 Mon Sep 17 00:00:00 2001 From: EternaleapFromPit Date: Tue, 18 Nov 2025 08:17:25 +0300 Subject: [PATCH 1/3] 2 windows terminal issues fixed plus search functionality 2 windows terminal issues fixed plus search functionality --- src/libs/helper_functions.zig | 24 +++- src/main.zig | 11 +- src/packages/search.zig | 212 ++++++++++++++++++++++++++++++++++ 3 files changed, 243 insertions(+), 4 deletions(-) create mode 100644 src/packages/search.zig diff --git a/src/libs/helper_functions.zig b/src/libs/helper_functions.zig index 553ed86..ece1755 100644 --- a/src/libs/helper_functions.zig +++ b/src/libs/helper_functions.zig @@ -8,7 +8,11 @@ const releases_url = "https://api.github.com/repos/{s}/releases"; const branches_url = "https://api.github.com/repos/{s}/branches"; const tar_file_url = "https://github.com/{s}/archive/refs/tags/{s}.tar.gz"; -const in = std.fs.File.stdin(); +var in: std.fs.File = undefined; + +pub fn set_file_stdin() void { + in = std.fs.File.stdin(); +} pub fn semver_parse_range_based(input: []const u8) !struct { min: types.semver, @@ -192,8 +196,22 @@ pub fn read_string(allocator: std.mem.Allocator) !u32 { pub fn read_integer() !usize { var buf: [25]u8 = undefined; var reader = in.reader(&buf); - const num_str = reader.interface.takeDelimiterExclusive('\n') catch return error.UnexpectedEos; - return try std.fmt.parseInt(usize, num_str, 10); + const num_str_cr = reader.interface.takeDelimiterExclusive('\n') catch |err| { + std.debug.print("ERROR in takeDelimiterExclusive: {s}\n", .{@errorName(err)}); + return error.UnexpectedEos; + }; + + // Windows terminal compatibility issues + // Trim the carriage return + const num_str = std.mem.trim(u8, num_str_cr, " \r\n\t"); + + std.debug.print("\n", .{}); + const parsed_int = std.fmt.parseInt(usize, num_str, 10) catch |err| { + std.debug.print("Failed to parse '{s}': {s}\n", .{ num_str, @errorName(err) }); + return error.InvalidNumber; + }; + + return parsed_int; } pub fn fetch_versions(repo: types.repository, allocator: std.mem.Allocator) ![][]const u8 { diff --git a/src/main.zig b/src/main.zig index 2e55dcf..a752270 100644 --- a/src/main.zig +++ b/src/main.zig @@ -8,6 +8,7 @@ const program_manager = @import("./programs/install.zig"); const types = @import("types.zig"); const hfs = @import("./libs/helper_functions.zig"); const init = @import("./libs/init.zig"); +const package_search = @import("./packages/search.zig"); inline fn eql(x: []const u8, y: []const u8) bool { return std.mem.eql(u8, x, y); @@ -35,6 +36,7 @@ pub fn main() !void { // if (deinit_status == .leak) @panic("Memory got leaked."); // } + hfs.set_file_stdin(); const allocator = std.heap.c_allocator; // arguments @@ -91,6 +93,10 @@ pub fn main() !void { } // ==================================================== try program_manager.install_app(repo, allocator); + } else if (eql(args[1], "search")) { + const query = if (args.len > 2) args[2] else null; + const filter = if (args.len > 3) args[3] else null; + try package_search.search_packages(allocator, query, filter); } else if (eql(args[1], "remove")) { try remove_package.remove_dependency(allocator, args[2]); } else if (eql(args[1], "update")) { @@ -142,8 +148,11 @@ pub fn main() !void { return; } try info_package.info(repo, allocator); + } else if (eql(args[1], "search")) { + const query = if (args.len > 2) args[2] else null; + const filter = if (args.len > 3) args[3] else null; + try package_search.search_packages(allocator, query, filter); } else display.err.unknown_argument(args[2]), - // args[0] args[1] args[2] args[3] // zigp something something_else yet_something else => display.err.unknown_argument(args[2]), diff --git a/src/packages/search.zig b/src/packages/search.zig new file mode 100644 index 0000000..a4bc98a --- /dev/null +++ b/src/packages/search.zig @@ -0,0 +1,212 @@ +const std = @import("std"); +const http = std.http; +const json = std.json; + +// package response from zigistry +const Package = struct { + name: []const u8, + full_name: []const u8, + description: []const u8, + stargazers_count: u32, + forks_count: u32, + license: ?[]const u8, + topics: []const []const u8, + repo_from: []const u8, + zig_minimum_version: []const u8, + has_build_zig: bool, + has_build_zig_zon: bool, + updated_at: []const u8, +}; + +pub fn search_packages(allocator: std.mem.Allocator, query: ?[]const u8, filter: ?[]const u8) !void { + const actual_query = if (query != null and query.?.len > 0 and !std.mem.eql(u8, query.?, "*")) query else null; + const actual_filter = if (filter != null and filter.?.len > 0) filter else null; + + if (actual_query == null and actual_filter == null) { + std.debug.print("Error: No valid search parameters provided.\n", .{}); + return error.InvalidSearchParameters; + } + + var url_buffer: [512]u8 = undefined; + const url = try build_search_url(&url_buffer, actual_query, actual_filter); + + std.debug.print("Searching: {s}\n\n", .{url}); + + // Create HTTP client + var client = http.Client{ .allocator = allocator }; + defer client.deinit(); + + var body = std.Io.Writer.Allocating.init(allocator); + const bodywriter: *std.Io.Writer = &body.writer; + defer body.deinit(); + const response = try client.fetch(.{ + .location = .{ .url = url }, + .method = .GET, + .response_writer = bodywriter, + }); + + if (response.status != .ok) { + std.debug.print("API request failed with status: {s}\n", .{@tagName(response.status)}); + return; + } + + const packages = try parse_packages(allocator, body.written()); + defer free_packages(allocator, packages); + + print_packages(packages); +} + +fn free_packages(allocator: std.mem.Allocator, packages: []Package) void { + for (packages) |pkg| { + allocator.free(pkg.name); + allocator.free(pkg.full_name); + allocator.free(pkg.description); + if (pkg.license) |license| allocator.free(license); + for (pkg.topics) |topic| allocator.free(topic); + allocator.free(pkg.topics); + allocator.free(pkg.repo_from); + allocator.free(pkg.zig_minimum_version); + allocator.free(pkg.updated_at); + } + allocator.free(packages); +} + +fn build_search_url(buffer: []u8, query: ?[]const u8, filter: ?[]const u8) ![]const u8 { + const base_url = "https://zigistry-api.hf.space/api/searchPackages"; + + if (query != null and filter != null) { + // Both provided: ?q=query&filter=filter + return std.fmt.bufPrint(buffer, "{s}?q={s}&filter={s}", .{ base_url, query.?, filter.? }); + } else if (query != null) { + // Only query provided: ?q=query + return std.fmt.bufPrint(buffer, "{s}?q={s}", .{ base_url, query.? }); + } else if (filter != null) { + // Only filter provided: ?filter=filter + return std.fmt.bufPrint(buffer, "{s}?filter={s}", .{ base_url, filter.? }); + } else { + // Neither + return error.NoSearchParameters; + } +} + +// Parse JSON response into Package array +fn parse_packages(allocator: std.mem.Allocator, body: []const u8) ![]Package { + var parsed = try json.parseFromSlice(json.Value, allocator, body, .{}); + defer parsed.deinit(); + + if (parsed.value != .array) { + return error.InvalidJSON; + } + + const packages_array = parsed.value.array; + var packages = try allocator.alloc(Package, packages_array.items.len); + + for (packages_array.items, 0..) |item, i| { + if (item != .object) continue; + + const obj = item.object; + packages[i] = Package{ + .name = try allocator.dupe(u8, get_string(obj, "name") orelse "unknown"), + .full_name = try allocator.dupe(u8, get_string(obj, "full_name") orelse "unknown"), + .description = try allocator.dupe(u8, get_string(obj, "description") orelse ""), + .stargazers_count = get_u32(obj, "stargazers_count") orelse 0, + .forks_count = get_u32(obj, "forks_count") orelse 0, + .license = if (get_string(obj, "license")) |license| + try allocator.dupe(u8, license) + else + null, + .topics = try parse_topics(allocator, obj), + .repo_from = try allocator.dupe(u8, get_string(obj, "repo_from") orelse "unknown"), + .zig_minimum_version = try allocator.dupe(u8, get_string(obj, "zig_minimum_version") orelse "unknown"), + .has_build_zig = get_bool(obj, "has_build_zig") orelse false, + .has_build_zig_zon = get_bool(obj, "has_build_zig_zon") orelse false, + .updated_at = try allocator.dupe(u8, get_string(obj, "updated_at") orelse "unknown"), + }; + } + + return packages; +} + +// JSON object to sting +fn get_string(obj: json.ObjectMap, key: []const u8) ?[]const u8 { + if (obj.get(key)) |value| { + if (value == .string) { + return value.string; + } + } + return null; +} + +// JSON object to u32 +fn get_u32(obj: json.ObjectMap, key: []const u8) ?u32 { + if (obj.get(key)) |value| { + if (value == .integer) { + return @intCast(value.integer); + } + } + return null; +} + +// JSON object to bool +fn get_bool(obj: json.ObjectMap, key: []const u8) ?bool { + if (obj.get(key)) |value| { + if (value == .bool) { + return value.bool; + } + } + return null; +} + +// Parse topics array +fn parse_topics(allocator: std.mem.Allocator, obj: json.ObjectMap) ![]const []const u8 { + if (obj.get("topics")) |value| { + if (value == .array) { + const topics_array = value.array; + var topics = try allocator.alloc([]const u8, topics_array.items.len); + + for (topics_array.items, 0..) |topic, i| { + if (topic == .string) { + topics[i] = try allocator.dupe(u8, topic.string); + } else { + topics[i] = try allocator.dupe(u8, ""); + } + } + return topics; + } + } + return &[0][]const u8{}; +} + +// Print packages in a nice format +fn print_packages(packages: []const Package) void { + if (packages.len == 0) { + std.debug.print("No packages found.\n", .{}); + return; + } + + std.debug.print("Found {d} package(s):\n\n", .{packages.len}); + + for (packages, 0..) |pkg, i| { + std.debug.print("--- Package {d} ---\n", .{i + 1}); + std.debug.print("Name: {s}\n", .{pkg.name}); + std.debug.print("Full Name: {s}\n", .{pkg.full_name}); + std.debug.print("Description: {s}\n", .{pkg.description}); + std.debug.print("Stars: {d}, Forks: {d}\n", .{ pkg.stargazers_count, pkg.forks_count }); + std.debug.print("License: {s}\n", .{pkg.license orelse "None"}); + std.debug.print("Source: {s}\n", .{pkg.repo_from}); + std.debug.print("Zig Version: {s}\n", .{pkg.zig_minimum_version}); + std.debug.print("Build.zig: {}, Build.zig.zon: {}\n", .{ pkg.has_build_zig, pkg.has_build_zig_zon }); + + if (pkg.topics.len > 0) { + std.debug.print("Topics: ", .{}); + for (pkg.topics, 0..) |topic, j| { + if (j > 0) std.debug.print(", ", .{}); + std.debug.print("{s}", .{topic}); + } + std.debug.print("\n", .{}); + } + + std.debug.print("Updated: {s}\n", .{pkg.updated_at}); + std.debug.print("\n", .{}); + } +} From 00b099630962ee2434c83c5943ed030faecb41f5 Mon Sep 17 00:00:00 2001 From: Rohan Vashisht Date: Wed, 19 Nov 2025 01:02:45 +0530 Subject: [PATCH 2/3] added syntax highlighting and improved date printing. --- src/packages/search.zig | 68 ++++++++++++++++++++++++++++++++--------- 1 file changed, 54 insertions(+), 14 deletions(-) diff --git a/src/packages/search.zig b/src/packages/search.zig index a4bc98a..9178268 100644 --- a/src/packages/search.zig +++ b/src/packages/search.zig @@ -1,6 +1,44 @@ const std = @import("std"); const http = std.http; const json = std.json; +const ansi = @import("../libs/ansi_codes.zig"); + +fn month_num_to_month_name(month: u8) []const u8 { + return switch (month) { + 1 => "Jan", + 2 => "Feb", + 3 => "Mar", + 4 => "Apr", + 5 => "May", + 6 => "Jun", + 7 => "Jul", + 8 => "Aug", + 9 => "Sep", + 10 => "Oct", + 11 => "Nov", + 12 => "Dec", + else => "unknown month", + }; +} + +fn print_special_date_time(date: []const u8) struct { + year: []const u8, + month: []const u8, + day: []const u8, + hour: []const u8, + minutes: []const u8, + seconds: []const u8, +} { + var my_iter = std.mem.splitAny(u8, date, "-:ZT"); + return .{ + .year = my_iter.next().?, + .month = my_iter.next().?, + .day = if (my_iter.next()) |day| if (day[0] == '0') day[1..] else day else unreachable, + .hour = my_iter.next().?, + .minutes = my_iter.next().?, + .seconds = my_iter.next().?, + }; +} // package response from zigistry const Package = struct { @@ -30,7 +68,7 @@ pub fn search_packages(allocator: std.mem.Allocator, query: ?[]const u8, filter: var url_buffer: [512]u8 = undefined; const url = try build_search_url(&url_buffer, actual_query, actual_filter); - std.debug.print("Searching: {s}\n\n", .{url}); + // std.debug.print("Searching: {s}\n\n", .{url}); // Create HTTP client var client = http.Client{ .allocator = allocator }; @@ -184,29 +222,31 @@ fn print_packages(packages: []const Package) void { return; } - std.debug.print("Found {d} package(s):\n\n", .{packages.len}); + std.debug.print(ansi.BRIGHT_GREEN ++ "Found {s}{d}{s} package(s):{s}\n\n", .{ ansi.UNDERLINE, packages.len, ansi.RESET ++ ansi.BRIGHT_GREEN, ansi.RESET }); for (packages, 0..) |pkg, i| { - std.debug.print("--- Package {d} ---\n", .{i + 1}); - std.debug.print("Name: {s}\n", .{pkg.name}); - std.debug.print("Full Name: {s}\n", .{pkg.full_name}); - std.debug.print("Description: {s}\n", .{pkg.description}); - std.debug.print("Stars: {d}, Forks: {d}\n", .{ pkg.stargazers_count, pkg.forks_count }); - std.debug.print("License: {s}\n", .{pkg.license orelse "None"}); - std.debug.print("Source: {s}\n", .{pkg.repo_from}); - std.debug.print("Zig Version: {s}\n", .{pkg.zig_minimum_version}); - std.debug.print("Build.zig: {}, Build.zig.zon: {}\n", .{ pkg.has_build_zig, pkg.has_build_zig_zon }); + std.debug.print(ansi.BRIGHT_YELLOW ++ "---{s} Package {d}{s} ---{s}\n", .{ ansi.RESET ++ ansi.BRIGHT_CYAN, i + 1, ansi.BRIGHT_YELLOW, ansi.RESET }); + std.debug.print(ansi.BRIGHT_GREEN ++ ansi.BOLD ++ "Name{s}: {s}\n", .{ ansi.RESET, pkg.name }); + std.debug.print(ansi.BRIGHT_GREEN ++ ansi.BOLD ++ "Full Name{s}: {s}\n", .{ ansi.RESET, pkg.full_name }); + std.debug.print(ansi.BRIGHT_GREEN ++ ansi.BOLD ++ "Description{s}: {s}\n", .{ ansi.RESET, pkg.description }); + std.debug.print(ansi.BRIGHT_GREEN ++ ansi.BOLD ++ "Stars{s}: {d}, {s}Forks{s}: {d}\n", .{ ansi.RESET, pkg.stargazers_count, ansi.BRIGHT_GREEN ++ ansi.BOLD, ansi.RESET, pkg.forks_count }); + std.debug.print(ansi.BRIGHT_GREEN ++ ansi.BOLD ++ "License{s}: {s}\n", .{ ansi.RESET, pkg.license orelse "None" }); + std.debug.print(ansi.BRIGHT_GREEN ++ ansi.BOLD ++ "Source{s}: {s}\n", .{ ansi.RESET, pkg.repo_from }); + std.debug.print(ansi.BRIGHT_GREEN ++ ansi.BOLD ++ "Zig Version{s}: {s}\n", .{ ansi.RESET, pkg.zig_minimum_version }); + std.debug.print(ansi.BRIGHT_GREEN ++ ansi.BOLD ++ "Build.zig{s}: {}, Build.zig.zon: {}\n", .{ ansi.RESET, pkg.has_build_zig, pkg.has_build_zig_zon }); if (pkg.topics.len > 0) { - std.debug.print("Topics: ", .{}); + std.debug.print("{s}Topics{s}: ", .{ ansi.BRIGHT_GREEN ++ ansi.BOLD, ansi.RESET }); for (pkg.topics, 0..) |topic, j| { if (j > 0) std.debug.print(", ", .{}); - std.debug.print("{s}", .{topic}); + std.debug.print(ansi.BRIGHT_CYAN ++ ansi.BOLD ++ ansi.UNDERLINE ++ "#{s}" ++ ansi.RESET, .{topic}); } std.debug.print("\n", .{}); } - std.debug.print("Updated: {s}\n", .{pkg.updated_at}); + const data = print_special_date_time(pkg.updated_at); + + std.debug.print(ansi.BRIGHT_GREEN ++ ansi.BOLD ++ "Updated{s}: {s}th of {s} {s} at {s}:{s}:{s}\n", .{ ansi.RESET, data.day, month_num_to_month_name(std.fmt.parseInt(u8, data.month, 10) catch 13), data.year, data.hour, data.minutes, data.seconds }); std.debug.print("\n", .{}); } } From e599030d1c011884645d556adc59bf76fc7863a1 Mon Sep 17 00:00:00 2001 From: Rohan Vashisht Date: Wed, 19 Nov 2025 01:04:38 +0530 Subject: [PATCH 3/3] fixed highlighting --- src/packages/search.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/packages/search.zig b/src/packages/search.zig index 9178268..7d0b691 100644 --- a/src/packages/search.zig +++ b/src/packages/search.zig @@ -233,7 +233,7 @@ fn print_packages(packages: []const Package) void { std.debug.print(ansi.BRIGHT_GREEN ++ ansi.BOLD ++ "License{s}: {s}\n", .{ ansi.RESET, pkg.license orelse "None" }); std.debug.print(ansi.BRIGHT_GREEN ++ ansi.BOLD ++ "Source{s}: {s}\n", .{ ansi.RESET, pkg.repo_from }); std.debug.print(ansi.BRIGHT_GREEN ++ ansi.BOLD ++ "Zig Version{s}: {s}\n", .{ ansi.RESET, pkg.zig_minimum_version }); - std.debug.print(ansi.BRIGHT_GREEN ++ ansi.BOLD ++ "Build.zig{s}: {}, Build.zig.zon: {}\n", .{ ansi.RESET, pkg.has_build_zig, pkg.has_build_zig_zon }); + std.debug.print(ansi.BRIGHT_GREEN ++ ansi.BOLD ++ "Build.zig{s}: {}, {s}Build.zig.zon{s}: {}\n", .{ ansi.RESET, pkg.has_build_zig, ansi.BRIGHT_GREEN ++ ansi.BOLD, ansi.RESET, pkg.has_build_zig_zon }); if (pkg.topics.len > 0) { std.debug.print("{s}Topics{s}: ", .{ ansi.BRIGHT_GREEN ++ ansi.BOLD, ansi.RESET });