Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: exec and optimze command processing logic #52

Merged
merged 5 commits into from
Jul 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ comptime {
pub fn build(b: *Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});

// Add a global option for versioning
const options = b.addOptions();
options.addOption(std.log.Level, "log_level", b.option(std.log.Level, "log_level", "The Log Level to be used.") orelse .info);
Expand All @@ -46,6 +47,16 @@ pub fn build(b: *Build) void {

b.installArtifact(exe);

// add run step
const run_exe = b.addRunArtifact(exe);
run_exe.step.dependOn(b.getInstallStep());

if (b.args) |args|
run_exe.addArgs(args);

const run_step = b.step("run", "Run the application");
run_step.dependOn(&run_exe.step);

const release = b.step("release", "make an upstream binary release");
const release_targets = [_]std.Target.Query{
.{ .cpu_arch = .x86_64, .os_tag = .linux },
Expand Down
5 changes: 2 additions & 3 deletions src/alias.zig
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ pub fn set_zig_version(version: []const u8) !void {

const user_home = tools.get_home();
const zig_path = try std.fs.path.join(arena_allocator, &[_][]const u8{ user_home, ".zm", "versions", version });
const symlink_path = try std.fs.path.join(arena_allocator, &[_][]const u8{ user_home, ".zm", "current" });
const symlink_path = try tools.get_zvm_path_segment(arena_allocator, "current");

try update_symlink(zig_path, symlink_path);
try verify_zig_version(allocator, version);
Expand Down Expand Up @@ -121,8 +121,7 @@ fn verify_zig_version(allocator: std.mem.Allocator, expected_version: []const u8
}

fn retrieve_zig_version(allocator: std.mem.Allocator) ![]u8 {
const user_home = tools.get_home();
const symlink_path = try std.fs.path.join(allocator, &[_][]const u8{ user_home, ".zm", "current" });
const symlink_path = try tools.get_zvm_path_segment(allocator, "current");
defer allocator.free(symlink_path);

var child_process = std.process.Child.init(&[_][]const u8{ "zig", "version" }, allocator);
Expand Down
121 changes: 96 additions & 25 deletions src/command.zig
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
const std = @import("std");
const builtin = @import("builtin");
const options = @import("options");

const versions = @import("versions.zig");
const install = @import("install.zig");
const alias = @import("alias.zig");
const tools = @import("tools.zig");

const options = @import("options");

// command species
pub const Command = enum {
List,
Install,
Expand All @@ -16,32 +18,100 @@ pub const Command = enum {
Unknown,
};

pub fn handle_commands(cmd: Command, params: ?[]const u8) !void {
switch (cmd) {
.List => {
try handle_list();
},
.Install => {
try install_version(params);
},
.Use => {
try use_version(params);
},
.Default => {
try set_default();
},
.Version => {
try get_version();
},
.Help => {
try display_help();
},
.Unknown => {
try handle_unknown();
},
const CommandData = struct {
cmd: Command = .Unknown,
param: ?[]const u8 = null,
};

const CommandOption = struct {
short_handle: ?[]const u8,
handle: []const u8,
cmd: Command,
};

/// now all available commands
const command_opts = [_]CommandOption{
.{ .short_handle = "ls", .handle = "list", .cmd = Command.List },
.{ .short_handle = "i", .handle = "install", .cmd = Command.Install },
.{ .short_handle = null, .handle = "use", .cmd = Command.Use },
.{ .short_handle = null, .handle = "--version", .cmd = Command.Version },
.{ .short_handle = null, .handle = "--help", .cmd = Command.Help },
.{ .short_handle = null, .handle = "--default", .cmd = Command.Default },
};

/// parse command and handle commands
pub fn handle_command(params: []const []const u8) !void {
if (builtin.os.tag != .windows) {
if (std.mem.eql(u8, std.fs.path.basename(params[0]), "zig"))
try handleAlias(params);
}

// get command data, get the first command and its arg
const command: CommandData = blk: {
// when args len is less than 2, that mean no extra args!
if (params.len < 2) break :blk CommandData{};

const args = params[1..];

for (args, 0..) |arg, index| {
for (command_opts) |opt| {

// whether eql short handle
const is_eql_short_handle =
if (opt.short_handle) |short_handle|
std.mem.eql(u8, arg, short_handle)
else
false;

// whether eql handle
const is_eql_handle = std.mem.eql(u8, arg, opt.handle);

if (!is_eql_short_handle and !is_eql_handle)
continue;

break :blk CommandData{
.cmd = opt.cmd,
.param = if (index + 1 < args.len) args[index + 1] else null,
};
}
}
break :blk CommandData{};
};

switch (command.cmd) {
.List => try handle_list(),
.Install => try install_version(command.param),
.Use => try use_version(command.param),
.Default => try set_default(),
.Version => try get_version(),
.Help => try display_help(),
.Unknown => try handle_unknown(),
}
}

fn handleAlias(params: []const []const u8) !void {
var arena = std.heap.ArenaAllocator.init(tools.get_allocator());
defer arena.deinit();

const allocator = arena.allocator();

const new_params = try allocator.dupe([]const u8, params);

const home = tools.get_home();
const current_zig_path = try std.fs.path.join(allocator, &.{ home, ".zm", "current", "zig" });

std.fs.accessAbsolute(current_zig_path, .{}) catch |err| {
if (err == std.fs.Dir.AccessError.FileNotFound) {
std.debug.print("Zig has not been installed yet, please install zig with zvm!\n", .{});
std.process.exit(1);
}
return err;
};

new_params[0] = current_zig_path;
return std.process.execv(allocator, new_params);
}

fn handle_list() !void {
const allocator = tools.get_allocator();
var version_list = try versions.VersionList.init(allocator);
Expand Down Expand Up @@ -95,6 +165,7 @@ fn display_help() !void {
\\ zvm use 0.8.0 Switch to using Zig version 0.8.0.
\\
\\For additional information and contributions, please visit the GitHub repository.
\\
;

std.debug.print(help_message, .{});
Expand Down
56 changes: 8 additions & 48 deletions src/main.zig
Original file line number Diff line number Diff line change
@@ -1,64 +1,24 @@
const std = @import("std");
const tools = @import("tools.zig");
const Command = @import("command.zig").Command;
const handle_commands = @import("command.zig").handle_commands;

const CommandData = struct {
cmd: Command,
params: ?[]const u8,
};

const CommandOption = struct {
short_handle: ?[]const u8,
handle: []const u8,
cmd: Command,
};
const command = @import("command.zig");

pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
// this will detect the memory whether leak
defer if (gpa.deinit() == .leak) @panic("memory leaked!");

// init some useful data
try tools.data_init(gpa.allocator());
// deinit some data
defer tools.data_deinit();

// get allocator
const allocator = tools.get_allocator();

// get and free args
const args = try std.process.argsAlloc(allocator);
defer std.process.argsFree(allocator, args);

const cmd_data = try parse_args(args);
try handle_commands(cmd_data.cmd, cmd_data.params);
}

fn parse_args(args: []const []const u8) !CommandData {
const options = get_available_commands();
if (args.len < 2) return CommandData{ .cmd = Command.Unknown, .params = null };
return find_command_in_args(args[1..], options) orelse CommandData{ .cmd = Command.Unknown, .params = null };
}

fn get_available_commands() []const CommandOption {
return &[_]CommandOption{
CommandOption{ .short_handle = "ls", .handle = "list", .cmd = Command.List },
CommandOption{ .short_handle = "i", .handle = "install", .cmd = Command.Install },
CommandOption{ .short_handle = null, .handle = "use", .cmd = Command.Use },
CommandOption{ .short_handle = null, .handle = "--version", .cmd = Command.Version },
CommandOption{ .short_handle = null, .handle = "--help", .cmd = Command.Help },
CommandOption{ .short_handle = null, .handle = "--default", .cmd = Command.Default },
};
}

fn find_command_in_args(args: []const []const u8, options: []const CommandOption) ?CommandData {
var i: usize = 0;
for (args) |arg| {
for (options) |option| {
if ((option.short_handle != null and std.mem.eql(u8, arg, option.short_handle.?)) or
std.mem.eql(u8, arg, option.handle))
{
const params = if (i + 1 < args.len) args[i + 1] else null;
return CommandData{ .cmd = option.cmd, .params = params };
}
}
i += 1;
}
return null;
// parse the args and handle command
try command.handle_command(args);
}
1 change: 1 addition & 0 deletions src/tools.zig
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ pub fn get_allocator() std.mem.Allocator {
return allocator;
}

/// get zvm path segment
pub fn get_zvm_path_segment(tmp_allocator: std.mem.Allocator, segment: []const u8) ![]u8 {
return std.fs.path.join(
tmp_allocator,
Expand Down
12 changes: 12 additions & 0 deletions src/versions.zig
Original file line number Diff line number Diff line change
@@ -1,22 +1,31 @@
//! For getting zig version info json from offical website
const std = @import("std");
const config = @import("config.zig");

const uri = std.Uri.parse(config.download_manifest_url) catch unreachable;

pub const VersionList = struct {
// this type will store
const List = std.ArrayList([]const u8);

// store the version message
lists: List,
allocator: std.mem.Allocator,

/// init the VersionList
pub fn init(allocator: std.mem.Allocator) !VersionList {
// create a http client
var client = std.http.Client{ .allocator = allocator };
defer client.deinit();

// we ceate a buffer to store the http response
var buffer: [262144]u8 = undefined; // 256 * 1024 = 262kb

// try open a request
var req = try client.open(.GET, uri, .{ .server_header_buffer = &buffer });
defer req.deinit();

// send request and wait response
try req.send();
try req.wait();

Expand All @@ -26,6 +35,7 @@ pub const VersionList = struct {

const len = try req.readAll(buffer[0..]);

// parse json
const json = try std.json.parseFromSlice(std.json.Value, allocator, buffer[0..len], .{});
defer json.deinit();
const root = json.value;
Expand All @@ -47,10 +57,12 @@ pub const VersionList = struct {
};
}

// get the slice items
pub fn slice(self: *VersionList) [][]const u8 {
return self.lists.items;
}

/// deinit will free memory
pub fn deinit(self: *VersionList) void {
defer self.lists.deinit();
for (self.lists.items) |value| {
Expand Down
Loading