mirror of
https://github.com/jetzig-framework/jetzig.git
synced 2025-05-14 14:06:08 +00:00
Implement init command, get rid of old init script/artifacts
Remove old bash script for setting up a new project, do everything in Zig to make it platform agnostic and give us an easy place to add scaffolding commands in future.
This commit is contained in:
parent
01ac93ce81
commit
d3a3582136
5
.gitignore
vendored
5
.gitignore
vendored
@ -1,9 +1,4 @@
|
|||||||
TODO.md
|
|
||||||
main
|
|
||||||
get/
|
|
||||||
zig-out/
|
zig-out/
|
||||||
zig-cache/
|
zig-cache/
|
||||||
*.core
|
*.core
|
||||||
src/app/views/**/*.compiled.zig
|
|
||||||
archive.tar.gz
|
|
||||||
static/
|
static/
|
||||||
|
BIN
archive.tar.gz
Normal file
BIN
archive.tar.gz
Normal file
Binary file not shown.
@ -22,7 +22,7 @@ pub fn build(b: *std.Build) !void {
|
|||||||
|
|
||||||
const mime_module = try GenerateMimeTypes.generateMimeModule(b);
|
const mime_module = try GenerateMimeTypes.generateMimeModule(b);
|
||||||
|
|
||||||
b.installArtifact(lib);
|
const zig_args_dep = b.dependency("args", .{ .target = target, .optimize = optimize });
|
||||||
|
|
||||||
const jetzig_module = b.addModule("jetzig", .{ .root_source_file = .{ .path = "src/jetzig.zig" } });
|
const jetzig_module = b.addModule("jetzig", .{ .root_source_file = .{ .path = "src/jetzig.zig" } });
|
||||||
jetzig_module.addImport("mime_types", mime_module);
|
jetzig_module.addImport("mime_types", mime_module);
|
||||||
@ -39,6 +39,7 @@ pub fn build(b: *std.Build) !void {
|
|||||||
|
|
||||||
lib.root_module.addImport("zmpl", zmpl_dep.module("zmpl"));
|
lib.root_module.addImport("zmpl", zmpl_dep.module("zmpl"));
|
||||||
jetzig_module.addImport("zmpl", zmpl_dep.module("zmpl"));
|
jetzig_module.addImport("zmpl", zmpl_dep.module("zmpl"));
|
||||||
|
lib.root_module.addImport("args", zig_args_dep.module("args"));
|
||||||
|
|
||||||
// This is the way to make it look nice in the zig build script
|
// This is the way to make it look nice in the zig build script
|
||||||
// If we would do it the other way around, we would have to do
|
// If we would do it the other way around, we would have to do
|
||||||
|
@ -3,8 +3,12 @@
|
|||||||
.version = "0.0.0",
|
.version = "0.0.0",
|
||||||
.dependencies = .{
|
.dependencies = .{
|
||||||
.zmpl = .{
|
.zmpl = .{
|
||||||
.url = "https://github.com/jetzig-framework/zmpl/archive/aa7147f8a52d927dce6cdd4f5b3bb2de5080f28c.tar.gz",
|
.url = "https://github.com/jetzig-framework/zmpl/archive/ed99a1604b37fb05b0f5843a3288588f3dfe2e63.tar.gz",
|
||||||
.hash = "12203d262b39b2328adb981e41c5127507f3d47e977c1a4e69a96688a4213b986d04",
|
.hash = "1220771fe742fc620872051b92082d370549ed857e5a93ae43f92c5767ca2aaf42b1",
|
||||||
|
},
|
||||||
|
.args = .{
|
||||||
|
.url = "https://github.com/MasterQ32/zig-args/archive/89f18a104d9c13763b90e97d6b4ce133da8a3e2b.tar.gz",
|
||||||
|
.hash = "12203ded54c85878eea7f12744066dcb4397177395ac49a7b2aa365bf6047b623829",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
33
cli/build.zig
Normal file
33
cli/build.zig
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const compile = @import("compile.zig");
|
||||||
|
|
||||||
|
pub fn build(b: *std.Build) !void {
|
||||||
|
const target = b.standardTargetOptions(.{});
|
||||||
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
|
|
||||||
|
const exe = b.addExecutable(.{
|
||||||
|
.name = "jetzig",
|
||||||
|
.root_source_file = .{ .path = "cli.zig" },
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
|
||||||
|
const zig_args_dep = b.dependency("args", .{ .target = target, .optimize = optimize });
|
||||||
|
|
||||||
|
exe.root_module.addImport("args", zig_args_dep.module("args"));
|
||||||
|
exe.root_module.addImport(
|
||||||
|
"init_data",
|
||||||
|
try compile.initDataModule(b),
|
||||||
|
);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
15
cli/build.zig.zon
Normal file
15
cli/build.zig.zon
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
.{
|
||||||
|
.name = "jetzig-cli",
|
||||||
|
.version = "0.0.0",
|
||||||
|
.minimum_zig_version = "0.12.0",
|
||||||
|
|
||||||
|
.dependencies = .{
|
||||||
|
.args = .{
|
||||||
|
.url = "https://github.com/MasterQ32/zig-args/archive/89f18a104d9c13763b90e97d6b4ce133da8a3e2b.tar.gz",
|
||||||
|
.hash = "12203ded54c85878eea7f12744066dcb4397177395ac49a7b2aa365bf6047b623829",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.paths = .{
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
}
|
60
cli/cli.zig
Normal file
60
cli/cli.zig
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const args = @import("args");
|
||||||
|
const init = @import("init.zig");
|
||||||
|
|
||||||
|
const Options = struct {
|
||||||
|
help: bool = false,
|
||||||
|
|
||||||
|
pub const shorthands = .{
|
||||||
|
.h = "help",
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const meta = .{
|
||||||
|
.option_docs = .{
|
||||||
|
.init = "Initialize a new project",
|
||||||
|
.help = "Print help and exit",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const Verb = union(enum) {
|
||||||
|
init: init.Options,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Main entrypoint for `jetzig` executable. Parses command line args and generates a new
|
||||||
|
/// project, scaffolding, etc.
|
||||||
|
pub fn main() !void {
|
||||||
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
|
const allocator = gpa.allocator();
|
||||||
|
defer std.debug.assert(gpa.deinit() == .ok);
|
||||||
|
|
||||||
|
const options = try args.parseWithVerbForCurrentProcess(Options, Verb, allocator, .print);
|
||||||
|
defer options.deinit();
|
||||||
|
|
||||||
|
const writer = std.io.getStdErr().writer();
|
||||||
|
|
||||||
|
if (options.verb) |verb| {
|
||||||
|
switch (verb) {
|
||||||
|
.init => |opts| return init.run(
|
||||||
|
allocator,
|
||||||
|
opts,
|
||||||
|
writer,
|
||||||
|
options.positionals,
|
||||||
|
.{ .help = options.options.help },
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.options.help) {
|
||||||
|
try args.printHelp(Options, "jetzig", writer);
|
||||||
|
try writer.writeAll(
|
||||||
|
\\
|
||||||
|
\\Commands:
|
||||||
|
\\
|
||||||
|
\\ init Initialize a new project.
|
||||||
|
\\
|
||||||
|
\\ Pass --help to any command for more information, e.g. `jetzig init --help`
|
||||||
|
\\
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
71
cli/compile.zig
Normal file
71
cli/compile.zig
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
fn base64Encode(allocator: std.mem.Allocator, input: []const u8) []const u8 {
|
||||||
|
const encoder = std.base64.Base64Encoder.init(
|
||||||
|
std.base64.url_safe_no_pad.alphabet_chars,
|
||||||
|
std.base64.url_safe_no_pad.pad_char,
|
||||||
|
);
|
||||||
|
const size = encoder.calcSize(input.len);
|
||||||
|
const ptr = allocator.alloc(u8, size) catch @panic("OOM");
|
||||||
|
_ = encoder.encode(ptr, input);
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn initDataModule(build: *std.Build) !*std.Build.Module {
|
||||||
|
const root_path = build.pathFromRoot("..");
|
||||||
|
|
||||||
|
var buf = std.ArrayList(u8).init(build.allocator);
|
||||||
|
defer buf.deinit();
|
||||||
|
|
||||||
|
const writer = buf.writer();
|
||||||
|
|
||||||
|
const paths = .{
|
||||||
|
"demo/build.zig",
|
||||||
|
"demo/src/main.zig",
|
||||||
|
"demo/src/app/middleware/DemoMiddleware.zig",
|
||||||
|
"demo/src/app/views/init.zig",
|
||||||
|
"demo/src/app/views/init/index.zmpl",
|
||||||
|
"demo/src/app/views/init/_content.zmpl",
|
||||||
|
"demo/public/jetzig.png",
|
||||||
|
"demo/public/zmpl.png",
|
||||||
|
"demo/public/favicon.ico",
|
||||||
|
"demo/public/styles.css",
|
||||||
|
".gitignore",
|
||||||
|
};
|
||||||
|
|
||||||
|
try writer.writeAll(
|
||||||
|
\\pub const init_data = .{
|
||||||
|
\\
|
||||||
|
);
|
||||||
|
|
||||||
|
var dir = try std.fs.openDirAbsolute(root_path, .{});
|
||||||
|
defer dir.close();
|
||||||
|
|
||||||
|
inline for (paths) |path| {
|
||||||
|
const stat = try dir.statFile(path);
|
||||||
|
const encoded = base64Encode(
|
||||||
|
build.allocator,
|
||||||
|
try dir.readFileAlloc(build.allocator, path, stat.size),
|
||||||
|
);
|
||||||
|
defer build.allocator.free(encoded);
|
||||||
|
|
||||||
|
const output = try std.fmt.allocPrint(
|
||||||
|
build.allocator,
|
||||||
|
\\.{{ .path = "{s}", .data = "{s}" }},
|
||||||
|
,
|
||||||
|
.{ path, encoded },
|
||||||
|
);
|
||||||
|
defer build.allocator.free(output);
|
||||||
|
|
||||||
|
try writer.writeAll(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
try writer.writeAll(
|
||||||
|
\\};
|
||||||
|
\\
|
||||||
|
);
|
||||||
|
|
||||||
|
const write_files = build.addWriteFiles();
|
||||||
|
const init_data_source = write_files.add("init_data.zig", buf.items);
|
||||||
|
return build.createModule(.{ .root_source_file = init_data_source });
|
||||||
|
}
|
427
cli/init.zig
Normal file
427
cli/init.zig
Normal file
@ -0,0 +1,427 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const args = @import("args");
|
||||||
|
const init_data = @import("init_data").init_data;
|
||||||
|
|
||||||
|
fn base64Decode(allocator: std.mem.Allocator, input: []const u8) ![]const u8 {
|
||||||
|
const decoder = std.base64.Base64Decoder.init(
|
||||||
|
std.base64.url_safe_no_pad.alphabet_chars,
|
||||||
|
std.base64.url_safe_no_pad.pad_char,
|
||||||
|
);
|
||||||
|
const size = try decoder.calcSizeForSlice(input);
|
||||||
|
const ptr = try allocator.alloc(u8, size);
|
||||||
|
try decoder.decode(ptr, input);
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const Options = struct {
|
||||||
|
path: ?[]const u8 = null,
|
||||||
|
|
||||||
|
pub const shorthands = .{
|
||||||
|
.p = "path",
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const meta = .{
|
||||||
|
.usage_summary = "[--path PATH]",
|
||||||
|
.full_text =
|
||||||
|
\\Initializes a new Jetzig project in the current directory or attempts to
|
||||||
|
\\create a new directory specified by PATH
|
||||||
|
\\
|
||||||
|
\\Creates build.zig, build.zig.zon, src/main.zig, and an example view with a template.
|
||||||
|
\\
|
||||||
|
\\Run `zig build run` to launch a development server when complete.
|
||||||
|
,
|
||||||
|
.option_docs = .{
|
||||||
|
.path = "Set the output path relative to the current directory (default: current directory)",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Run the `jetzig init` command.
|
||||||
|
pub fn run(
|
||||||
|
allocator: std.mem.Allocator,
|
||||||
|
options: Options,
|
||||||
|
writer: anytype,
|
||||||
|
positionals: [][]const u8,
|
||||||
|
other_options: struct { help: bool },
|
||||||
|
) !void {
|
||||||
|
_ = options;
|
||||||
|
var install_path: ?[]const u8 = null;
|
||||||
|
|
||||||
|
for (positionals) |arg| {
|
||||||
|
if (install_path != null) {
|
||||||
|
std.debug.print("Unexpected positional argument: {s}\n", .{arg});
|
||||||
|
return error.JetzigUnexpectedPositionalArgumentsError;
|
||||||
|
}
|
||||||
|
install_path = arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
const github_url = try githubUrl(allocator);
|
||||||
|
defer allocator.free(github_url);
|
||||||
|
|
||||||
|
if (other_options.help) {
|
||||||
|
try args.printHelp(Options, "jetzig init", writer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var install_dir: std.fs.Dir = undefined;
|
||||||
|
defer install_dir.close();
|
||||||
|
|
||||||
|
var project_name: []const u8 = undefined;
|
||||||
|
defer allocator.free(project_name);
|
||||||
|
|
||||||
|
if (install_path) |path| {
|
||||||
|
install_dir = try std.fs.cwd().makeOpenPath(path, .{});
|
||||||
|
project_name = try allocator.dupe(u8, std.fs.path.basename(path));
|
||||||
|
} else {
|
||||||
|
const cwd_realpath = try std.fs.cwd().realpathAlloc(allocator, ".");
|
||||||
|
defer allocator.free(cwd_realpath);
|
||||||
|
|
||||||
|
const default_project_name = std.fs.path.basename(cwd_realpath);
|
||||||
|
project_name = try promptInput(allocator, "Project name", .{ .default = default_project_name });
|
||||||
|
const sub_path = if (std.mem.eql(u8, project_name, default_project_name)) "" else project_name;
|
||||||
|
|
||||||
|
const default_install_path = try std.fs.path.join(
|
||||||
|
allocator,
|
||||||
|
&[_][]const u8{ cwd_realpath, sub_path },
|
||||||
|
);
|
||||||
|
defer allocator.free(default_install_path);
|
||||||
|
|
||||||
|
const input_install_path = try promptInput(
|
||||||
|
allocator,
|
||||||
|
"Install path",
|
||||||
|
.{ .default = default_install_path },
|
||||||
|
);
|
||||||
|
defer allocator.free(input_install_path);
|
||||||
|
install_dir = try std.fs.cwd().makeOpenPath(input_install_path, .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
const real_path = try install_dir.realpathAlloc(allocator, ".");
|
||||||
|
defer allocator.free(real_path);
|
||||||
|
|
||||||
|
const output = try std.fmt.allocPrint(allocator, "Creating new project in {s}\n\n", .{real_path});
|
||||||
|
defer allocator.free(output);
|
||||||
|
try writer.writeAll(output);
|
||||||
|
|
||||||
|
try copySourceFile(
|
||||||
|
allocator,
|
||||||
|
install_dir,
|
||||||
|
"demo/build.zig",
|
||||||
|
"build.zig",
|
||||||
|
&[_]Replace{.{ .from = "jetzig-demo", .to = project_name }},
|
||||||
|
);
|
||||||
|
|
||||||
|
try copySourceFile(
|
||||||
|
allocator,
|
||||||
|
install_dir,
|
||||||
|
"demo/src/main.zig",
|
||||||
|
"src/main.zig",
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
|
||||||
|
try copySourceFile(
|
||||||
|
allocator,
|
||||||
|
install_dir,
|
||||||
|
"demo/src/app/middleware/DemoMiddleware.zig",
|
||||||
|
"src/app/middleware/DemoMiddleware.zig",
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
|
||||||
|
try copySourceFile(
|
||||||
|
allocator,
|
||||||
|
install_dir,
|
||||||
|
"demo/src/app/views/init.zig",
|
||||||
|
"src/app/views/root.zig",
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
|
||||||
|
try copySourceFile(
|
||||||
|
allocator,
|
||||||
|
install_dir,
|
||||||
|
"demo/src/app/views/init/index.zmpl",
|
||||||
|
"src/app/views/root/index.zmpl",
|
||||||
|
&[_]Replace{
|
||||||
|
.{ .from = "init/", .to = "root/" },
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
try copySourceFile(
|
||||||
|
allocator,
|
||||||
|
install_dir,
|
||||||
|
"demo/src/app/views/init/_content.zmpl",
|
||||||
|
"src/app/views/root/_content.zmpl",
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
|
||||||
|
try copySourceFile(
|
||||||
|
allocator,
|
||||||
|
install_dir,
|
||||||
|
"demo/public/jetzig.png",
|
||||||
|
"public/jetzig.png",
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
|
||||||
|
try copySourceFile(
|
||||||
|
allocator,
|
||||||
|
install_dir,
|
||||||
|
"demo/public/zmpl.png",
|
||||||
|
"public/zmpl.png",
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
|
||||||
|
try copySourceFile(
|
||||||
|
allocator,
|
||||||
|
install_dir,
|
||||||
|
"demo/public/favicon.ico",
|
||||||
|
"public/favicon.ico",
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
|
||||||
|
try copySourceFile(
|
||||||
|
allocator,
|
||||||
|
install_dir,
|
||||||
|
"demo/public/styles.css",
|
||||||
|
"public/styles.css",
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
|
||||||
|
try copySourceFile(
|
||||||
|
allocator,
|
||||||
|
install_dir,
|
||||||
|
".gitignore",
|
||||||
|
".gitignore",
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
|
||||||
|
try runCommand(allocator, install_dir, &[_][]const u8{
|
||||||
|
"zig",
|
||||||
|
"fetch",
|
||||||
|
"--save",
|
||||||
|
github_url,
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: Use arg or interactive prompt to do Git setup in net project, default to no.
|
||||||
|
// const git_setup = false;
|
||||||
|
// if (git_setup) try gitSetup(allocator, install_dir);
|
||||||
|
|
||||||
|
std.debug.print(
|
||||||
|
\\
|
||||||
|
\\Setup complete! ✈️ 🦎
|
||||||
|
\\
|
||||||
|
\\Launch your new application:
|
||||||
|
\\
|
||||||
|
\\ $ cd {s}
|
||||||
|
\\
|
||||||
|
\\ $ zig build run
|
||||||
|
\\
|
||||||
|
\\And then browse to http://localhost:8080/
|
||||||
|
\\
|
||||||
|
\\
|
||||||
|
, .{real_path});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn runCommand(allocator: std.mem.Allocator, install_dir: std.fs.Dir, argv: []const []const u8) !void {
|
||||||
|
const result = try std.process.Child.run(.{ .allocator = allocator, .argv = argv, .cwd_dir = install_dir });
|
||||||
|
defer allocator.free(result.stdout);
|
||||||
|
defer allocator.free(result.stderr);
|
||||||
|
|
||||||
|
const command = try std.mem.join(allocator, " ", argv);
|
||||||
|
defer allocator.free(command);
|
||||||
|
|
||||||
|
std.debug.print("[exec] {s}", .{command});
|
||||||
|
|
||||||
|
if (result.term.Exited != 0) {
|
||||||
|
printFailure();
|
||||||
|
std.debug.print(
|
||||||
|
\\
|
||||||
|
\\Error running command: {s}
|
||||||
|
\\
|
||||||
|
\\[stdout]:
|
||||||
|
\\
|
||||||
|
\\{s}
|
||||||
|
\\
|
||||||
|
\\[stderr]:
|
||||||
|
\\
|
||||||
|
\\{s}
|
||||||
|
\\
|
||||||
|
, .{ command, result.stdout, result.stderr });
|
||||||
|
return error.JetzigRunCommandError;
|
||||||
|
} else {
|
||||||
|
printSuccess();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Replace = struct {
|
||||||
|
from: []const u8,
|
||||||
|
to: []const u8,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn copySourceFile(
|
||||||
|
allocator: std.mem.Allocator,
|
||||||
|
install_dir: std.fs.Dir,
|
||||||
|
src: []const u8,
|
||||||
|
dest: []const u8,
|
||||||
|
replace: ?[]const Replace,
|
||||||
|
) !void {
|
||||||
|
std.debug.print("[create] {s}", .{dest});
|
||||||
|
|
||||||
|
var content: []const u8 = undefined;
|
||||||
|
if (replace) |capture| {
|
||||||
|
const initial = readSourceFile(allocator, src) catch |err| {
|
||||||
|
printFailure();
|
||||||
|
return err;
|
||||||
|
};
|
||||||
|
defer allocator.free(initial);
|
||||||
|
for (capture) |item| {
|
||||||
|
content = try std.mem.replaceOwned(u8, allocator, initial, item.from, item.to);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
content = readSourceFile(allocator, src) catch |err| {
|
||||||
|
printFailure();
|
||||||
|
return err;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
defer allocator.free(content);
|
||||||
|
|
||||||
|
writeSourceFile(install_dir, dest, content) catch |err| {
|
||||||
|
printFailure();
|
||||||
|
return err;
|
||||||
|
};
|
||||||
|
printSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read a file from Jetzig source code.
|
||||||
|
fn readSourceFile(allocator: std.mem.Allocator, path: []const u8) ![]const u8 {
|
||||||
|
inline for (init_data) |file| {
|
||||||
|
if (std.mem.eql(u8, path, file.path)) return try base64Decode(allocator, file.data);
|
||||||
|
}
|
||||||
|
return error.SourceFileNotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write a file to the new project's directory.
|
||||||
|
fn writeSourceFile(install_dir: std.fs.Dir, path: []const u8, content: []const u8) !void {
|
||||||
|
// TODO: Detect presence and ask for confirmation if necessary.
|
||||||
|
if (std.fs.path.dirname(path)) |dirname| {
|
||||||
|
var dir = try install_dir.makeOpenPath(dirname, .{});
|
||||||
|
defer dir.close();
|
||||||
|
|
||||||
|
const file = try dir.createFile(std.fs.path.basename(path), .{ .truncate = true });
|
||||||
|
defer file.close();
|
||||||
|
|
||||||
|
try file.writeAll(content);
|
||||||
|
} else {
|
||||||
|
const file = try install_dir.createFile(path, .{ .truncate = true });
|
||||||
|
defer file.close();
|
||||||
|
|
||||||
|
try file.writeAll(content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a full GitHub URL for passing to `zig fetch`.
|
||||||
|
fn githubUrl(allocator: std.mem.Allocator) ![]const u8 {
|
||||||
|
var client = std.http.Client{ .allocator = allocator };
|
||||||
|
defer client.deinit();
|
||||||
|
|
||||||
|
const url = "https://api.github.com/repos/jetzig-framework/jetzig/branches/main";
|
||||||
|
const extra_headers = &[_]std.http.Header{.{ .name = "X-GitHub-Api-Version", .value = "2022-11-28" }};
|
||||||
|
|
||||||
|
var response_storage = std.ArrayList(u8).init(allocator);
|
||||||
|
defer response_storage.deinit();
|
||||||
|
|
||||||
|
const fetch_result = try client.fetch(.{
|
||||||
|
.location = .{ .url = url },
|
||||||
|
.extra_headers = extra_headers,
|
||||||
|
.response_storage = .{ .dynamic = &response_storage },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (fetch_result.status != .ok) {
|
||||||
|
std.debug.print("Error fetching from GitHub: {s}\n", .{url});
|
||||||
|
return error.JetzigGitHubFetchError;
|
||||||
|
}
|
||||||
|
|
||||||
|
const parsed_response = try std.json.parseFromSlice(
|
||||||
|
struct { commit: struct { sha: []const u8 } },
|
||||||
|
allocator,
|
||||||
|
response_storage.items,
|
||||||
|
.{ .ignore_unknown_fields = true },
|
||||||
|
);
|
||||||
|
defer parsed_response.deinit();
|
||||||
|
|
||||||
|
return try std.mem.concat(
|
||||||
|
allocator,
|
||||||
|
u8,
|
||||||
|
&[_][]const u8{
|
||||||
|
"https://github.com/jetzig-framework/jetzig/archive/",
|
||||||
|
parsed_response.value.commit.sha,
|
||||||
|
".tar.gz",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prompt a user for input and return the result. Accepts an optional default value.
|
||||||
|
fn promptInput(
|
||||||
|
allocator: std.mem.Allocator,
|
||||||
|
prompt: []const u8,
|
||||||
|
options: struct { default: ?[]const u8 },
|
||||||
|
) ![]const u8 {
|
||||||
|
const stdin = std.io.getStdIn();
|
||||||
|
const reader = stdin.reader();
|
||||||
|
|
||||||
|
const max_read_bytes = 1024;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (options.default) |default| {
|
||||||
|
std.debug.print(
|
||||||
|
\\{s} [default: "{s}"]:
|
||||||
|
, .{ prompt, default });
|
||||||
|
} else {
|
||||||
|
std.debug.print(
|
||||||
|
\\{s}:
|
||||||
|
, .{prompt});
|
||||||
|
}
|
||||||
|
const input = try reader.readUntilDelimiterOrEofAlloc(allocator, '\n', max_read_bytes);
|
||||||
|
if (input) |capture| {
|
||||||
|
defer allocator.free(capture);
|
||||||
|
|
||||||
|
if (std.mem.eql(u8, capture, "")) {
|
||||||
|
if (options.default) |default| return try allocator.dupe(u8, strip(default));
|
||||||
|
} else return try allocator.dupe(u8, strip(capture));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strip leading and trailing whitespace from a u8 slice.
|
||||||
|
fn strip(input: []const u8) []const u8 {
|
||||||
|
return std.mem.trim(u8, input, &std.ascii.whitespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize a new Git repository when setting up a new project (optional).
|
||||||
|
fn gitSetup(allocator: std.mem.Allocator, install_dir: *std.fs.Dir) !void {
|
||||||
|
try runCommand(allocator, install_dir, &[_][]const u8{
|
||||||
|
"git",
|
||||||
|
"init",
|
||||||
|
".",
|
||||||
|
});
|
||||||
|
|
||||||
|
try runCommand(allocator, install_dir, &[_][]const u8{
|
||||||
|
"git",
|
||||||
|
"add",
|
||||||
|
".",
|
||||||
|
});
|
||||||
|
|
||||||
|
try runCommand(allocator, install_dir, &[_][]const u8{
|
||||||
|
"git",
|
||||||
|
"commit",
|
||||||
|
"-m",
|
||||||
|
"Initialize Jetzig project",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Print a success confirmation.
|
||||||
|
fn printSuccess() void {
|
||||||
|
std.debug.print(" ✅\n", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Print a failure confirmation.
|
||||||
|
fn printFailure() void {
|
||||||
|
std.debug.print(" ❌\n", .{});
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
.{
|
.{
|
||||||
.name = "sandbox-jetzig",
|
.name = "jetzig-demo",
|
||||||
.version = "0.0.0",
|
.version = "0.0.0",
|
||||||
.minimum_zig_version = "0.12.0",
|
.minimum_zig_version = "0.12.0",
|
||||||
.dependencies = .{
|
.dependencies = .{
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
/* Root stylesheet. Load into your Zmpl template with:
|
||||||
|
*
|
||||||
|
* <link rel="stylesheet" href="/styles.css" />
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
.message {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 3rem;
|
||||||
|
}
|
@ -1,26 +0,0 @@
|
|||||||
const std = @import("std");
|
|
||||||
const jetzig = @import("jetzig");
|
|
||||||
|
|
||||||
my_data: u8,
|
|
||||||
|
|
||||||
const Self = @This();
|
|
||||||
|
|
||||||
pub fn init(request: *jetzig.http.Request) !*Self {
|
|
||||||
var middleware = try request.allocator.create(Self);
|
|
||||||
middleware.my_data = 42;
|
|
||||||
return middleware;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn beforeRequest(self: *Self, request: *jetzig.http.Request) !void {
|
|
||||||
request.server.logger.debug("[DemoMiddleware] Before request, custom data: {d}", .{self.my_data});
|
|
||||||
self.my_data = 43;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn afterRequest(self: *Self, request: *jetzig.http.Request, response: *jetzig.http.Response) !void {
|
|
||||||
request.server.logger.debug("[DemoMiddleware] After request, custom data: {d}", .{self.my_data});
|
|
||||||
request.server.logger.debug("[DemoMiddleware] content-type: {s}", .{response.content_type});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(self: *Self, request: *jetzig.http.Request) void {
|
|
||||||
request.allocator.destroy(self);
|
|
||||||
}
|
|
53
demo/src/app/middleware/DemoMiddleware.zig
Normal file
53
demo/src/app/middleware/DemoMiddleware.zig
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/// Demo middleware. Assign middleware by declaring `pub const middleware` in the
|
||||||
|
/// `jetzig_options` defined in your application's `src/main.zig`.
|
||||||
|
///
|
||||||
|
/// Middleware is called before and after the request, providing full access to the active
|
||||||
|
/// request, allowing you to execute any custom code for logging, tracking, inserting response
|
||||||
|
/// headers, etc.
|
||||||
|
///
|
||||||
|
/// This middleware is configured in the demo app's `src/main.zig`:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// pub const jetzig_options = struct {
|
||||||
|
/// pub const middleware: []const type = &.{@import("app/middleware/DemoMiddleware.zig")};
|
||||||
|
/// };
|
||||||
|
/// ```
|
||||||
|
const std = @import("std");
|
||||||
|
const jetzig = @import("jetzig");
|
||||||
|
|
||||||
|
/// Define any custom data fields you want to store here. Assigning to these fields in the `init`
|
||||||
|
/// function allows you to access them in the `beforeRequest` and `afterRequest` functions, where
|
||||||
|
/// they can also be modified.
|
||||||
|
my_custom_value: []const u8,
|
||||||
|
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
/// Initialize middleware.
|
||||||
|
pub fn init(request: *jetzig.http.Request) !*Self {
|
||||||
|
var middleware = try request.allocator.create(Self);
|
||||||
|
middleware.my_custom_value = "initial value";
|
||||||
|
return middleware;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Invoked immediately after the request head has been processed, before relevant view function
|
||||||
|
/// is processed. This gives you access to request headers but not the request body.
|
||||||
|
pub fn beforeRequest(self: *Self, request: *jetzig.http.Request) !void {
|
||||||
|
request.server.logger.debug("[DemoMiddleware] my_custom_value: {s}", .{self.my_custom_value});
|
||||||
|
self.my_custom_value = @tagName(request.method);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Invoked immediately after the request has finished responding. Provides full access to the
|
||||||
|
/// response as well as the request.
|
||||||
|
pub fn afterRequest(self: *Self, request: *jetzig.http.Request, response: *jetzig.http.Response) !void {
|
||||||
|
request.server.logger.debug(
|
||||||
|
"[DemoMiddleware] my_custom_value: {s}, response status: {s}",
|
||||||
|
.{ self.my_custom_value, @tagName(response.status_code) },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Invoked after `afterRequest` is called, use this function to do any clean-up.
|
||||||
|
/// Note that `request.allocator` is an arena allocator, so any allocations are automatically
|
||||||
|
/// done before the next request starts processing.
|
||||||
|
pub fn deinit(self: *Self, request: *jetzig.http.Request) void {
|
||||||
|
request.allocator.destroy(self);
|
||||||
|
}
|
43
demo/src/app/views/init.zig
Normal file
43
demo/src/app/views/init.zig
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
const jetzig = @import("jetzig");
|
||||||
|
|
||||||
|
/// `src/app/views/root.zig` represents the root URL `/`
|
||||||
|
/// The `index` view function is invoked when when the HTTP verb is `GET`.
|
||||||
|
/// Other view types are invoked either by passing a resource ID value (e.g. `/1234`) or by using
|
||||||
|
/// a different HTTP verb:
|
||||||
|
///
|
||||||
|
/// GET / => index(request, data)
|
||||||
|
/// GET /1234 => get(id, request, data)
|
||||||
|
/// POST / => post(request, data)
|
||||||
|
/// PUT /1234 => put(id, request, data)
|
||||||
|
/// PATCH /1234 => patch(id, request, data)
|
||||||
|
/// DELETE /1234 => delete(id, request, data)
|
||||||
|
pub fn index(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
|
||||||
|
// The first call to `data.object()` or `data.array()` sets the root response data value.
|
||||||
|
// JSON requests return a JSON string representation of the root data value.
|
||||||
|
// Zmpl templates can access all values in the root data value.
|
||||||
|
var root = try data.object();
|
||||||
|
|
||||||
|
// Add a string to the root object.
|
||||||
|
try root.put("message", data.string("Welcome to Jetzig!"));
|
||||||
|
|
||||||
|
// Request params have the same type as a `data.object()` so they can be inserted them
|
||||||
|
// directly into the response data. Fetch `http://localhost:8080/?message=hello` to set the
|
||||||
|
// param. JSON data is also accepted when the `content-type: application/json` header is
|
||||||
|
// present.
|
||||||
|
const params = try request.params();
|
||||||
|
|
||||||
|
if (params.get("message")) |value| {
|
||||||
|
try root.put("message_param", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set arbitrary response headers as required. `content-type` is automatically assigned for
|
||||||
|
// HTML, JSON responses.
|
||||||
|
//
|
||||||
|
// Static files located in `public/` in the root of your project directory are accessible
|
||||||
|
// from the root path (e.g. `public/jetzig.png`) is available at `/jetzig.png` and the
|
||||||
|
// content type is inferred from the extension using MIME types.
|
||||||
|
try request.response.headers.append("x-example-header", "example header value");
|
||||||
|
|
||||||
|
// Render the response and set the response code.
|
||||||
|
return request.render(.ok);
|
||||||
|
}
|
18
demo/src/app/views/init/_content.zmpl
Normal file
18
demo/src/app/views/init/_content.zmpl
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// Renders the `message` response data value.
|
||||||
|
<h3 class="message text-[#39b54a]">{.message}</h3>
|
||||||
|
|
||||||
|
<div><img class="p-3 mx-auto" src="/jetzig.png" /></div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<a href="https://github.com/jetzig-framework/zmpl">
|
||||||
|
<img class="p-3 m-3 mx-auto" src="/zmpl.png" />
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>Visit <a class="font-bold text-[#39b54a]" href="https://jetzig.dev/">jetzig.dev</a> to get started.</div>
|
||||||
|
<div>Join our Discord server and introduce yourself:</div>
|
||||||
|
<div>
|
||||||
|
<a class="font-bold text-[#39b54a]" href="https://discord.gg/eufqssz7X6">https://discord.gg/eufqssz7X6</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
20
demo/src/app/views/init/index.zmpl
Normal file
20
demo/src/app/views/init/index.zmpl
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<script src="https://unpkg.com/htmx.org@1.9.10"></script>
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
|
<link rel="stylesheet" href="/styles.css" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="text-center pt-10 m-auto">
|
||||||
|
// If present, renders the `message_param` response data value, add `?message=hello` to the
|
||||||
|
// URL to see the output:
|
||||||
|
<h2 class="param text-3xl text-[#f7931e]">{.message_param}</h2>
|
||||||
|
|
||||||
|
// Renders `src/app/views/init/_content.zmpl` with the same template data available:
|
||||||
|
<div>{^init/content}</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -33,7 +33,9 @@ const Quote = struct {
|
|||||||
|
|
||||||
// Quotes taken from: https://gist.github.com/natebass/b0a548425a73bdf8ea5c618149fe1fce
|
// Quotes taken from: https://gist.github.com/natebass/b0a548425a73bdf8ea5c618149fe1fce
|
||||||
fn randomQuote(allocator: std.mem.Allocator) !Quote {
|
fn randomQuote(allocator: std.mem.Allocator) !Quote {
|
||||||
const json = try std.fs.cwd().readFileAlloc(allocator, "src/app/config/quotes.json", 12684);
|
const path = "src/app/config/quotes.json";
|
||||||
|
const stat = try std.fs.cwd().statFile(path);
|
||||||
|
const json = try std.fs.cwd().readFileAlloc(allocator, path, stat.size);
|
||||||
const quotes = try std.json.parseFromSlice([]Quote, allocator, json, .{});
|
const quotes = try std.json.parseFromSlice([]Quote, allocator, json, .{});
|
||||||
return quotes.value[std.crypto.random.intRangeLessThan(usize, 0, quotes.value.len)];
|
return quotes.value[std.crypto.random.intRangeLessThan(usize, 0, quotes.value.len)];
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ pub const jetzig = @import("jetzig");
|
|||||||
pub const routes = @import("routes").routes;
|
pub const routes = @import("routes").routes;
|
||||||
|
|
||||||
pub const jetzig_options = struct {
|
pub const jetzig_options = struct {
|
||||||
pub const middleware: []const type = &.{@import("DemoMiddleware.zig")};
|
pub const middleware: []const type = &.{@import("app/middleware/DemoMiddleware.zig")};
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
|
79
init.bash
79
init.bash
@ -1,79 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
printf 'Enter project name (default: "jetzig-demo"): '
|
|
||||||
read -a project
|
|
||||||
if [ -z "${project}" ]
|
|
||||||
then
|
|
||||||
project="jetzig-demo"
|
|
||||||
fi
|
|
||||||
|
|
||||||
pwd="$(pwd)"
|
|
||||||
printf "Enter project parent directory (default: \"${pwd}\"): "
|
|
||||||
read -a dir
|
|
||||||
if [ -z "${dir}" ]
|
|
||||||
then
|
|
||||||
dir="${pwd}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
set -eu
|
|
||||||
|
|
||||||
project_path="${dir}/${project}"
|
|
||||||
echo
|
|
||||||
echo "Initializing new project in: ${project_path}"
|
|
||||||
|
|
||||||
mkdir -p "${project_path}"
|
|
||||||
|
|
||||||
do_exit () {
|
|
||||||
echo "Error fetching '$1':"
|
|
||||||
echo "$2"
|
|
||||||
echo "Exiting."
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
remote_base=https://raw.githubusercontent.com/jetzig-framework/jetzig/main/src/init
|
|
||||||
|
|
||||||
objects=(
|
|
||||||
'build.zig'
|
|
||||||
'build.zig.zon'
|
|
||||||
'src/main.zig'
|
|
||||||
'src/app/views/index.zig'
|
|
||||||
'src/app/views/index.zmpl'
|
|
||||||
'src/app/views/quotes.zig'
|
|
||||||
'src/app/views/quotes/get.zmpl'
|
|
||||||
'src/app/config/quotes.json'
|
|
||||||
'public/jetzig.png'
|
|
||||||
'.gitignore'
|
|
||||||
)
|
|
||||||
|
|
||||||
for object in "${objects[@]}"
|
|
||||||
do
|
|
||||||
printf "Creating output: ${object} "
|
|
||||||
url="${remote_base}/${object}"
|
|
||||||
mkdir -p "$(dirname "${project_path}/${object}")"
|
|
||||||
set +e
|
|
||||||
output=$(curl -s --fail --output "${project_path}/${object}" "${url}" 2>&1)
|
|
||||||
set -e
|
|
||||||
if (($?))
|
|
||||||
then
|
|
||||||
do_exit "${url}" "${output}"
|
|
||||||
else
|
|
||||||
echo "✅"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
sed -i.bak -e "s,%%project_name%%,${project},g" "${project_path}/build.zig"
|
|
||||||
rm "${project_path}/build.zig.bak"
|
|
||||||
sed -i.bak -e "s,%%project_name%%,${project},g" "${project_path}/build.zig.zon"
|
|
||||||
rm "${project_path}/build.zig.zon.bak"
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo "Finished creating new project in: ${project_path}"
|
|
||||||
echo
|
|
||||||
echo "Run your new project:"
|
|
||||||
echo
|
|
||||||
echo " cd '${project_path}'"
|
|
||||||
echo ' zig build run'
|
|
||||||
echo
|
|
||||||
echo "Welcome to Jetzig. ✈️🦎 "
|
|
||||||
echo
|
|
2
src/init/.gitignore
vendored
2
src/init/.gitignore
vendored
@ -1,2 +0,0 @@
|
|||||||
# Compiled Zmpl views:
|
|
||||||
src/app/views/**/*.compiled.zig
|
|
@ -1,86 +0,0 @@
|
|||||||
const std = @import("std");
|
|
||||||
const jetzig_build = @import("jetzig");
|
|
||||||
// Although this function looks imperative, note that its job is to
|
|
||||||
// declaratively construct a build graph that will be executed by an external
|
|
||||||
// runner.
|
|
||||||
pub fn build(b: *std.Build) void {
|
|
||||||
// Standard target options allows the person running `zig build` to choose
|
|
||||||
// what target to build for. Here we do not override the defaults, which
|
|
||||||
// means any target is allowed, and the default is native. Other options
|
|
||||||
// for restricting supported target set are available.
|
|
||||||
const target = b.standardTargetOptions(.{});
|
|
||||||
|
|
||||||
// Standard optimization options allow the person running `zig build` to select
|
|
||||||
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
|
|
||||||
// set a preferred release mode, allowing the user to decide how to optimize.
|
|
||||||
const optimize = b.standardOptimizeOption(.{});
|
|
||||||
const jetzig_dep = b.dependency("jetzig", .{ .optimize = optimize, .target = target });
|
|
||||||
const compile_view_step = jetzig_build.CompileViewsStep.create(b, .{ .template_path = "src/app/views/" });
|
|
||||||
|
|
||||||
// This declares intent for the library to be installed into the standard
|
|
||||||
// location when the user invokes the "install" step (the default step when
|
|
||||||
// running `zig build`).
|
|
||||||
|
|
||||||
const exe = b.addExecutable(.{
|
|
||||||
.name = "%%project_name%%",
|
|
||||||
.root_source_file = .{ .path = "src/main.zig" },
|
|
||||||
.target = target,
|
|
||||||
.optimize = optimize,
|
|
||||||
});
|
|
||||||
exe.root_module.addImport("jetzig", jetzig_dep.module("jetzig"));
|
|
||||||
|
|
||||||
exe.root_module.addImport("zmpl", jetzig_dep.module("zmpl"));
|
|
||||||
// This declares intent for the executable to be installed into the
|
|
||||||
// standard location when the user invokes the "install" step (the default
|
|
||||||
// step when running `zig build`).
|
|
||||||
b.installArtifact(exe);
|
|
||||||
exe.step.dependOn(&compile_view_step.step);
|
|
||||||
|
|
||||||
// This *creates* a Run step in the build graph, to be executed when another
|
|
||||||
// step is evaluated that depends on it. The next line below will establish
|
|
||||||
// such a dependency.
|
|
||||||
const run_cmd = b.addRunArtifact(exe);
|
|
||||||
|
|
||||||
// By making the run step depend on the install step, it will be run from the
|
|
||||||
// installation directory rather than directly from within the cache directory.
|
|
||||||
// This is not necessary, however, if the application depends on other installed
|
|
||||||
// files, this ensures they will be present and in the expected location.
|
|
||||||
run_cmd.step.dependOn(b.getInstallStep());
|
|
||||||
|
|
||||||
// This allows the user to pass arguments to the application in the build
|
|
||||||
// command itself, like this: `zig build run -- arg1 arg2 etc`
|
|
||||||
if (b.args) |args| {
|
|
||||||
run_cmd.addArgs(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This creates a build step. It will be visible in the `zig build --help` menu,
|
|
||||||
// and can be selected like this: `zig build run`
|
|
||||||
// This will evaluate the `run` step rather than the default, which is "install".
|
|
||||||
const run_step = b.step("run", "Run the app");
|
|
||||||
run_step.dependOn(&run_cmd.step);
|
|
||||||
|
|
||||||
// Creates a step for unit testing. This only builds the test executable
|
|
||||||
// but does not run it.
|
|
||||||
const lib_unit_tests = b.addTest(.{
|
|
||||||
.root_source_file = .{ .path = "src/root.zig" },
|
|
||||||
.target = target,
|
|
||||||
.optimize = optimize,
|
|
||||||
});
|
|
||||||
|
|
||||||
const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests);
|
|
||||||
|
|
||||||
const exe_unit_tests = b.addTest(.{
|
|
||||||
.root_source_file = .{ .path = "src/main.zig" },
|
|
||||||
.target = target,
|
|
||||||
.optimize = optimize,
|
|
||||||
});
|
|
||||||
|
|
||||||
const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests);
|
|
||||||
|
|
||||||
// Similar to creating the run step earlier, this exposes a `test` step to
|
|
||||||
// the `zig build --help` menu, providing a way for the user to request
|
|
||||||
// running the unit tests.
|
|
||||||
const test_step = b.step("test", "Run unit tests");
|
|
||||||
test_step.dependOn(&run_lib_unit_tests.step);
|
|
||||||
test_step.dependOn(&run_exe_unit_tests.step);
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
.{
|
|
||||||
.name = "%%project_name%%",
|
|
||||||
.version = "0.0.0",
|
|
||||||
.dependencies = .{
|
|
||||||
.jetzig = .{
|
|
||||||
.url = "https://github.com/jetzig-framework/jetzig/archive/0395d00b4b0f9e87a4bfab62c73ac047abb2e5da.tar.gz",
|
|
||||||
.hash = "122016141c39005c2149b3c6029b702332c346e3305f52376e1edbc5ee0295318e4c",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
.paths = .{
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
}
|
|
Binary file not shown.
Before Width: | Height: | Size: 22 KiB |
@ -1,11 +0,0 @@
|
|||||||
const std = @import("std");
|
|
||||||
const jetzig = @import("jetzig");
|
|
||||||
const Request = jetzig.http.Request;
|
|
||||||
const Data = jetzig.data.Data;
|
|
||||||
const View = jetzig.views.View;
|
|
||||||
|
|
||||||
pub fn index(request: *jetzig.http.Request, data: *jetzig.data.Data) anyerror!jetzig.views.View {
|
|
||||||
var object = try data.object();
|
|
||||||
try object.put("message", data.string("Welcome to Jetzig!"));
|
|
||||||
return request.render(.ok);
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
const std = @import("std");
|
|
||||||
const jetzig = @import("jetzig");
|
|
||||||
|
|
||||||
const Request = jetzig.http.Request;
|
|
||||||
const Data = jetzig.data.Data;
|
|
||||||
const View = jetzig.views.View;
|
|
||||||
|
|
||||||
pub fn get(id: []const u8, request: *Request, data: *Data) anyerror!View {
|
|
||||||
var body = try data.object();
|
|
||||||
|
|
||||||
const random_quote = try randomQuote(request.allocator);
|
|
||||||
|
|
||||||
if (std.mem.eql(u8, id, "random")) {
|
|
||||||
try body.put("quote", data.string(random_quote.quote));
|
|
||||||
try body.put("author", data.string(random_quote.author));
|
|
||||||
} else {
|
|
||||||
try body.put("quote", data.string("If you can dream it, you can achieve it."));
|
|
||||||
try body.put("author", data.string("Zig Ziglar"));
|
|
||||||
}
|
|
||||||
|
|
||||||
return request.render(.ok);
|
|
||||||
}
|
|
||||||
|
|
||||||
const Quote = struct {
|
|
||||||
quote: []const u8,
|
|
||||||
author: []const u8,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Quotes taken from: https://gist.github.com/natebass/b0a548425a73bdf8ea5c618149fe1fce
|
|
||||||
fn randomQuote(allocator: std.mem.Allocator) !Quote {
|
|
||||||
const json = try std.fs.cwd().readFileAlloc(allocator, "src/app/config/quotes.json", 12684);
|
|
||||||
const quotes = try std.json.parseFromSlice([]Quote, allocator, json, .{});
|
|
||||||
return quotes.value[std.crypto.random.intRangeLessThan(usize, 0, quotes.value.len)];
|
|
||||||
}
|
|
@ -1,2 +0,0 @@
|
|||||||
<div>"{.quote}"</div>
|
|
||||||
<div><b>--{.author}</b></div>
|
|
@ -1,15 +0,0 @@
|
|||||||
const std = @import("std");
|
|
||||||
|
|
||||||
pub const jetzig = @import("jetzig");
|
|
||||||
pub const routes = @import("routes").routes;
|
|
||||||
|
|
||||||
pub fn main() !void {
|
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
||||||
defer std.debug.assert(gpa.deinit() == .ok);
|
|
||||||
const allocator = gpa.allocator();
|
|
||||||
|
|
||||||
const app = try jetzig.init(allocator);
|
|
||||||
defer app.deinit();
|
|
||||||
|
|
||||||
try app.start(comptime jetzig.route(routes));
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user