Mi sitio web/jardín digital https://nulo.in/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

253 lines
7.7 KiB

const std = @import("std");
const c = @cImport({
@cInclude("cmark.h");
});
const endsWith = std.mem.endsWith;
fn stripExtension(file_name: []const u8) ![]const u8 {
const index = std.mem.lastIndexOfLinear(u8, file_name, ".");
if (index) |i| {
return file_name[0..i];
} else return error.NoExtension;
}
const HeaderOptions = struct {
// Si esto es true, se muestra un botón para volver al index.
ir_al_inicio: bool = true,
// Si esto es true, se muestra un <header>.
header: bool = true,
};
fn header(
writer: std.fs.File.Writer,
title: []const u8,
src_name: []const u8,
options: HeaderOptions,
) !void {
// TODO: deshardcodear base_uri
try writer.print(
\\<!doctype html>
\\<meta charset=utf-8>
\\<meta name=viewport content="width=device-width, initial-scale=1.0">
\\<meta name=author content=Nulo>
\\<meta property=og:title content="{0s}">
\\<meta property=og:type content=website>
\\<meta property=og:url content="https://nulo.in/{1s}.html">
\\<meta property=og:image content=cowboy.svg>
\\<link rel=stylesheet href=drip.css>
\\<link rel=icon href=cowboy.svg>
\\<title>{0s}</title>
\\
, .{ title, src_name });
// if test "$mirror" = true; then
// echo "<p style=color:darkred>Ojo: este sitio es un espejo (mirror). <a href=https://nulo.in>nulo.in</a> es la fuente.</p>"
// fi
if (options.ir_al_inicio) {
try writer.writeAll("<a href=.>☚ Volver al inicio</a>\n");
}
if (options.header) {
try writer.print(
\\<header>
\\<h1>{s}</h1>
\\<a href="https://gitea.nulo.in/Nulo/sitio/commits/branch/ANTIFASCISTA/{s}">Historial</a>
\\</header>
\\
, .{ title, src_name });
}
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var cwd = try std.fs.cwd().openDir(".", .{ .iterate = true });
defer cwd.close();
var cwd_iterator = cwd.iterate();
var build_dir = try cwd.makeOpenPath("build", .{});
defer build_dir.close();
var page_list = std.ArrayList([]const u8).init(allocator);
defer page_list.deinit();
defer for (page_list.items) |item| allocator.free(item);
while (try cwd_iterator.next()) |entry| {
if (entry.kind != .File) continue;
// Autocopiarnos :)
if (endsWith(u8, entry.name, ".zig") or
endsWith(u8, entry.name, ".md") or
endsWith(u8, entry.name, ".css") or
endsWith(u8, entry.name, ".png") or
endsWith(u8, entry.name, ".jpg") or
endsWith(u8, entry.name, ".mp4") or
endsWith(u8, entry.name, ".svg") or
endsWith(u8, entry.name, ".html"))
{
try cwd.copyFile(entry.name, build_dir, entry.name, .{});
}
if (endsWith(u8, entry.name, ".md") or endsWith(u8, entry.name, ".gen")) {
try page_list.append(try allocator.dupe(u8, try stripExtension(entry.name)));
}
if (endsWith(u8, entry.name, ".md"))
try generateMarkdown(allocator, cwd, entry.name, build_dir);
if (endsWith(u8, entry.name, ".gen"))
try generateExecutable(allocator, cwd, entry.name, build_dir);
}
try generatePageList(allocator, build_dir, page_list.items);
}
const Writer = std.io.BufferedWriter(4096, std.fs.File.Writer).Writer;
fn hackilyTransformHtml(input: []const u8, writer: Writer) !void {
var iter = std.mem.split(u8, input, "\n");
while (iter.next()) |line| {
var index: usize = 0;
while (index < line.len) : (index += 1) {
const rest = line[index..];
const link_marker = "<a h";
if (std.ascii.startsWithIgnoreCase(rest, link_marker)) {
index += link_marker.len - 1;
try writer.writeAll("<a rel='noopener noreferrer' h");
continue;
}
if (std.mem.startsWith(u8, rest, "[[")) {
if (std.mem.indexOf(u8, rest, "]]")) |end| {
const name = rest[2..end];
index += 2 + name.len + 2 - 1;
try writer.print(
\\<a href="{0s}.html">{0s}</a>
, .{name});
continue;
}
}
try writer.writeByte(line[index]);
}
try writer.writeByte('\n');
}
}
fn generateMarkdown(
allocator: std.mem.Allocator,
cwd: std.fs.Dir,
src_name: []const u8,
build_dir: std.fs.Dir,
) !void {
var file = try cwd.openFile(src_name, .{});
defer file.close();
const markdown = try file.readToEndAllocOptions(
allocator,
69696969,
null,
@alignOf(u32),
0,
);
defer allocator.free(markdown);
const html = c.cmark_markdown_to_html(
markdown.ptr,
markdown.len,
c.CMARK_OPT_UNSAFE | c.CMARK_OPT_SMART,
);
defer std.c.free(html);
const output_file_name = try std.fmt.allocPrint(
allocator,
"{s}.html",
.{try stripExtension(src_name)},
);
defer allocator.free(output_file_name);
const is_index = std.ascii.eqlIgnoreCase(src_name, "index.md");
const title = if (is_index)
"nulo.in"
else
try stripExtension(src_name);
var output = try build_dir.createFile(output_file_name, .{});
defer output.close();
try header(output.writer(), title, src_name, .{
.ir_al_inicio = !is_index,
.header = !is_index,
});
var buffered_writer = std.io.bufferedWriter(output.writer());
try hackilyTransformHtml(
std.mem.span(html),
buffered_writer.writer(),
);
try buffered_writer.flush();
}
fn generateExecutable(
allocator: std.mem.Allocator,
cwd: std.fs.Dir,
src_name: []const u8,
build_dir: std.fs.Dir,
) !void {
_ = cwd;
const output_file_name = try std.fmt.allocPrint(
allocator,
"{s}.html",
.{try stripExtension(src_name)},
);
defer allocator.free(output_file_name);
const title = try stripExtension(src_name);
var output = try build_dir.createFile(output_file_name, .{});
defer output.close();
try header(output.writer(), title, src_name, .{});
const executable_name = try std.fmt.allocPrint(allocator, "./{s}", .{src_name});
defer allocator.free(executable_name);
// const process = try std.ChildProcess.init(&.{executable_name}, allocator);
// defer process.deinit();
// process.stdout_behavior = .Ignore;
// process.stdout = output;
// const term = try process.spawnAndWait();
const result = try std.ChildProcess.exec(.{
.allocator = allocator,
.argv = &.{executable_name},
});
defer allocator.free(result.stdout);
defer allocator.free(result.stderr);
if (result.stderr.len > 0) {
std.log.err("{s} stderr: {s}", .{ src_name, result.stderr });
}
switch (result.term) {
.Exited => |status| {
if (status != 0) return error.ProcessFailed;
},
else => unreachable,
}
try output.writeAll(result.stdout);
}
fn generatePageList(
allocator: std.mem.Allocator,
build_dir: std.fs.Dir,
page_list: []const []const u8,
) !void {
_ = allocator;
const name = "Lista de páginas";
var output = try build_dir.createFile(name ++ ".html", .{});
defer output.close();
try header(output.writer(), name, "compilar.zig", .{});
try output.writeAll("<ul>");
for (page_list) |page| {
try output.writer().print(
\\<li><a href="{0s}.html">{0s}</a></li>
, .{page});
}
try output.writeAll("</ul>");
}