-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
21
-
22
-
23
-
24
-
25
-
26
-
27
-
28
-
29
-
30
-
31
-
32
-
33
-
34
-
35
-
36
-
37
-
38
-
39
-
40
-
41
-
42
-
43
-
44
-
45
-
46
-
47
-
48
-
49
-
50
-
51
-
52
-
53
-
54
-
55
-
56
-
57
-
58
-
59
-
60
-
61
-
62
-
63
-
64
-
65
-
66
-
67
-
68
-
69
-
70
-
71
-
72
-
73
-
74
-
75
-
76
-
77
-
78
-
79
-
80
-
81
-
82
-
83
-
84
-
85
-
86
-
87
-
88
-
89
-
90
-
91
-
92
-
93
-
94
-
95
-
96
-
97
-
98
-
99
-
100
-
101
-
102
-
103
-
104
-
105
-
106
-
107
-
108
-
109
-
110
-
111
-
112
-
113
-
114
-
115
-
116
-
117
-
118
-
119
-
120
-
121
-
122
-
123
-
124
-
125
-
126
-
127
-
128
// Copyright 2026 Shota FUJI
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
//
// SPDX-License-Identifier: MPL-2.0
const std = @import("std");
const config = @import("config");
const Options = @import("Options.zig");
const delete = @import("./commands/delete.zig");
const list = @import("./commands/list.zig");
const rename = @import("./commands/rename.zig");
const start = @import("./commands/start.zig");
const status = @import("./commands/status.zig");
const stop = @import("./commands/stop.zig");
const errors = @import("./errors.zig");
const CommandLineError = errors.CommandLineError;
test {
_ = @import("./events.zig");
_ = @import("./timer.zig");
}
fn printUsage(io: std.Io, file: std.Io.File) !void {
var buf: [512]u8 = undefined;
var writer = file.writer(io, &buf);
try writer.interface.writeAll(
\\timetracker - Timetracking CLI
\\
\\[USAGE]
\\# CLI
\\timetracker [OPTIONS] <COMMAND>
\\
\\[COMMANDS]
\\list List timers.
\\status Show a timer.
\\start Start a new timer.
\\stop Stop a running timer.
\\delete Delete a timer from history.
\\rename Rename timer title.
\\
\\[OPTIONS]
\\-h, --help Print this message to stdout and exits.
\\
);
try writer.flush();
}
fn run(init: *const std.process.Init) !void {
const options = Options.init(init.io, init.environ_map) catch |err| {
std.log.err("Cannot open application directory: {t}", .{err});
return error.CannotOpenDataDirectory;
};
defer options.deinit(init.io);
var args = try init.minimal.args.iterateAllocator(init.gpa);
defer args.deinit();
// Skip the program name.
_ = args.skip();
while (args.next()) |arg| {
if (std.mem.eql(u8, "-h", arg) or std.mem.eql(u8, "--help", arg)) {
printUsage(init.io, std.Io.File.stdout()) catch |err| {
std.log.err("Failed to print usage to stdout: {t}", .{err});
return CommandLineError.WriteToStdoutFailed;
};
return;
}
if (std.mem.eql(u8, "list", arg)) {
try list.run(&args, init.gpa, init.io, options);
return;
}
if (std.mem.eql(u8, "status", arg)) {
try status.run(&args, init.gpa, init.io, options);
return;
}
if (std.mem.eql(u8, "start", arg)) {
try start.run(&args, init.gpa, init.io, options);
return;
}
if (std.mem.eql(u8, "stop", arg)) {
try stop.run(&args, init.gpa, init.io, options);
return;
}
if (std.mem.eql(u8, "delete", arg)) {
try delete.run(&args, init.gpa, init.io, options);
return;
}
if (std.mem.eql(u8, "rename", arg)) {
try rename.run(&args, init.gpa, init.io, options);
return;
}
std.log.err("Unknown option: {s}", .{arg});
printUsage(init.io, std.Io.File.stderr()) catch return CommandLineError.WriteToStderrFailed;
return CommandLineError.InvalidUsage;
}
std.log.err("COMMAND is required.", .{});
printUsage(init.io, std.Io.File.stderr()) catch return CommandLineError.WriteToStderrFailed;
return CommandLineError.InvalidUsage;
}
pub fn main(init: std.process.Init) u8 {
run(&init) catch |err| return switch (err) {
CommandLineError.InvalidUsage => 3,
error.TimerNotFound => 4,
CommandLineError.WriteToStdoutFailed, CommandLineError.WriteToStderrFailed => 5,
error.CannotOpenDataDirectory => 6,
error.OutOfMemory => 9,
else => 1,
};
return 0;
}