From 6559c0a6a4e21a7f4e01435510aa5ecd7638c911 Mon Sep 17 00:00:00 2001 From: LordMZTE Date: Fri, 10 Nov 2023 21:41:39 +0100 Subject: [PATCH] add pacmanxfer script --- scripts/pacmanxfer/.gitignore | 2 + scripts/pacmanxfer/build.zig | 36 ++++++++++ scripts/pacmanxfer/build.zig.zon | 12 ++++ scripts/pacmanxfer/src/main.zig | 107 +++++++++++++++++++++++++++++ setup/commands/install-scripts.rkt | 1 + 5 files changed, 158 insertions(+) create mode 100644 scripts/pacmanxfer/.gitignore create mode 100644 scripts/pacmanxfer/build.zig create mode 100644 scripts/pacmanxfer/build.zig.zon create mode 100644 scripts/pacmanxfer/src/main.zig diff --git a/scripts/pacmanxfer/.gitignore b/scripts/pacmanxfer/.gitignore new file mode 100644 index 0000000..e73c965 --- /dev/null +++ b/scripts/pacmanxfer/.gitignore @@ -0,0 +1,2 @@ +zig-cache/ +zig-out/ diff --git a/scripts/pacmanxfer/build.zig b/scripts/pacmanxfer/build.zig new file mode 100644 index 0000000..f80cf52 --- /dev/null +++ b/scripts/pacmanxfer/build.zig @@ -0,0 +1,36 @@ +//! This is a tool which wraps rsync and curl and is intended to be used by pacman for downloading +//! packages. +//! +//! pacman.conf: +//! [common] +//! XferCommand = /path/to/pacmanxfer %u %o +const std = @import("std"); + +pub fn build(b: *std.Build) void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + const ansiterm_dep = b.dependency("ansi_term", .{ .target = target, .optimize = optimize }); + + const exe = b.addExecutable(.{ + .name = "pacmanxfer", + .root_source_file = .{ .path = "src/main.zig" }, + .target = target, + .optimize = optimize, + }); + + exe.addModule("ansi-term", ansiterm_dep.module("ansi-term")); + + b.installArtifact(exe); + + const run_cmd = b.addRunArtifact(exe); + + run_cmd.step.dependOn(b.getInstallStep()); + + if (b.args) |args| { + run_cmd.addArgs(args); + } + + const run_step = b.step("run", "Run the app"); + run_step.dependOn(&run_cmd.step); +} diff --git a/scripts/pacmanxfer/build.zig.zon b/scripts/pacmanxfer/build.zig.zon new file mode 100644 index 0000000..11c614d --- /dev/null +++ b/scripts/pacmanxfer/build.zig.zon @@ -0,0 +1,12 @@ +.{ + .name = "pacmanxfer", + .version = "0.0.0", + + .dependencies = .{ + .ansi_term = .{ + .url = "https://github.com/ziglibs/ansi-term/archive/1614b61486d567b59abe11a097d11aa6ce679819.tar.gz", + .hash = "1220647eea49d2c48d5e59354291e975f813be3cc5a9d9920a50bbfaa40a891a06ee", + }, + }, + .paths = .{""}, +} diff --git a/scripts/pacmanxfer/src/main.zig b/scripts/pacmanxfer/src/main.zig new file mode 100644 index 0000000..d1b2a38 --- /dev/null +++ b/scripts/pacmanxfer/src/main.zig @@ -0,0 +1,107 @@ +const std = @import("std"); +const at = @import("ansi-term"); + +const Downloader = enum { + rsync, + curl, + + fn forProtocol(proto: []const u8) ?Downloader { + return if (std.mem.eql(u8, proto, "rsync")) + .rsync + else if (std.mem.eql(u8, proto, "https") or std.mem.eql(u8, proto, "http")) + .curl + else + null; + } + + fn getOutputLineCount(self: Downloader) usize { + return switch (self) { + .rsync => 2, + .curl => 3, + }; + } +}; + +pub fn main() !u8 { + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + defer _ = gpa.deinit(); + const alloc = gpa.allocator(); + + if (std.os.argv.len != 3) + return error.InvalidArguments; + + const url_arg = std.mem.span(std.os.argv[1]); + const dest = std.mem.span(std.os.argv[2]); + + const url = try std.Uri.parse(url_arg); + + const downloader = Downloader.forProtocol(url.scheme) orelse return error.UnknownProtocol; + + const argv = switch (downloader) { + .rsync => try alloc.dupe([]const u8, &.{ + "rsync", + "--copy-links", // needed to correctly download pacman databases + "--partial", + "--info=progress2", + url_arg, + dest, + }), + // zig fmt: off + .curl => try alloc.dupe([]const u8, &.{ + "curl", + "--location", // handle redirects + "--continue-at", "-", // resume partial downloads + "--fail", // fail fast + "--output", dest, + url_arg, + }), + // zig fmt: on + }; + defer alloc.free(argv); + + var stdout = std.io.bufferedWriter(std.io.getStdOut().writer()); + + var style: ?at.style.Style = null; + try updateStyle(stdout.writer(), &style, .{ + .foreground = switch (downloader) { + .curl => .Red, + .rsync => .Blue, + }, + .font_style = .{ .bold = true }, + }); + try stdout.writer().writeAll("==> "); + try updateStyle(stdout.writer(), &style, .{}); + try stdout.writer().writeAll(std.fs.path.basename(url.path)); + try stdout.writer().writeByte('\n'); + try updateStyle(stdout.writer(), &style, .{ + .foreground = .Green, + .font_style = .{ .bold = true }, + }); + try stdout.writer().writeAll(">> "); + try updateStyle(stdout.writer(), &style, .{}); + try stdout.writer().writeAll(url_arg); + try stdout.writer().writeByte('\n'); + + try stdout.flush(); + + var child = std.process.Child.init(argv, alloc); + const term = try child.spawnAndWait(); + + const retcode = switch (term) { + .Exited => |t| t, + .Signal, .Stopped, .Unknown => 255, + }; + + if (retcode == 0) { + try at.cursor.cursorUp(stdout.writer(), downloader.getOutputLineCount() + 1); + try at.clear.clearFromCursorToScreenEnd(stdout.writer()); + try stdout.flush(); + } + + return retcode; +} + +fn updateStyle(writer: anytype, old: *?at.style.Style, new: at.style.Style) !void { + try at.format.updateStyle(writer, new, old.*); + old.* = new; +} diff --git a/setup/commands/install-scripts.rkt b/setup/commands/install-scripts.rkt index 7c34581..b739f1a 100644 --- a/setup/commands/install-scripts.rkt +++ b/setup/commands/install-scripts.rkt @@ -21,6 +21,7 @@ (install-rust "scripts/i3status") (install-zig "scripts/mzteinit") (install-zig "scripts/openbrowser") + (install-zig "scripts/pacmanxfer") (install-zig "scripts/playtwitch") (install-zig "scripts/prompt") (install-zig "scripts/randomwallpaper")