Commit Diff


commit - 41ac9085dc74afb7030d4f0a9c786d14137f8843
commit + 044348ebfee5cdeab03452924bb5db2024d9f9ed
blob - 879b53732d4c4885c1418820b484d8c1e3864f05
blob + 8dc66ae98fe34a3fdf6c8c299e198bf4e3637933
--- content/posts/primeiro-post.md
+++ content/posts/primeiro-post.md
@@ -2,7 +2,7 @@
 title: Primeiro Post
 date: 2026-04-03
 draft: false
-----
+---
 
 # Meu primeiro post
 
blob - /dev/null
blob + b5d009f9cb11e4d0e9c74c4edf7e6535e8546ddf (mode 644)
--- /dev/null
+++ content/posts/os-part1.md
@@ -0,0 +1,10 @@
+---
+title: Construindo um OS em Rust - Parte 1
+date: 2026-04-01
+series: os-em-rust
+part: 1
+---
+
+# O Boot
+
+O que acontece quando vocĂȘ liga o computador...
blob - /dev/null
blob + 1a9bbef4b535816f532fe21028d2db8fac059b7e (mode 644)
--- /dev/null
+++ content/posts/os-part2.md
@@ -0,0 +1,10 @@
+---
+title: Construindo um OS em Rust - Parte 2
+date: 2026-04-02
+series: os-em-rust
+part: 2
+---
+
+# Modo Protegido
+
+Escapando do modo real...
blob - /dev/null
blob + b8dfd1ffaef4e27e6c910f63979c5e7a6464229d (mode 644)
--- /dev/null
+++ content/posts/os-part3.md
@@ -0,0 +1,11 @@
+---
+title: Construindo um OS em Rust - Parte 3
+date: 2026-04-03
+series: os-em-rust
+part: 3
+---
+
+# Longo Mode
+
+Ativando 64-bit
+
blob - 3acb5bcf3d5c13ffd92c2ac67979cf7b27660d31
blob + 88121dca19f0a12c13878a5c58adbb184c833616
--- src/main.rs
+++ src/main.rs
@@ -172,8 +172,15 @@ fn copy_dir(src: &Path, dst: &Path) {
 // Build
 //////////////////////////////////////////////////////////////////////////////
 
+struct Post {
+    meta: Metadata,
+    html_content: String,
+    out_path: PathBuf,
+}
+
 fn build() {
     log!("starting build");
+
     let template = fs::read_to_string("templates/base.html")
         .expect("read templates/base.html");
 
@@ -181,6 +188,8 @@ fn build() {
     let mut files = Vec::new();
     walk(content_dir, &mut files);
 
+    let mut posts: Vec<Post> = Vec::new();
+
     for file in &files {
         if file.extension().and_then(|e| e.to_str()) != Some("md") {
             continue;
@@ -191,6 +200,7 @@ fn build() {
         let meta = parse_frontmatter(&md);
 
         if meta.draft {
+            log!("skip draft: {}", file.display());
             continue;
         }
 
@@ -202,20 +212,83 @@ fn build() {
         let relative = file.strip_prefix(content_dir).expect("strip prefix");
         let out_path = Path::new("public").join(relative).with_extension("html");
 
-        if let Some(parent) = out_path.parent() {
-            fs::create_dir_all(parent).expect("create dir");
+        posts.push(Post { meta, html_content, out_path });
+
+    }
+
+    for i in 0..posts.len() {
+        let nav = series_nav(&posts, i);
+
+        let mut content = posts[i].html_content.clone();
+        if !nav.is_empty() {
+            content.push_str(&nav);
         }
 
         let output = template
-            .replace("{{title}}", &meta.title)
-            .replace("{{content}}", &html_content);
+            .replace("{{title}}", &posts[i].meta.title)
+            .replace("{{content}}", &content);
 
-        fs::write(&out_path, output).expect("write html");
-        println!("build ok: {}", out_path.display());
+        if let Some(parent) = posts[i].out_path.parent() {
+            fs::create_dir_all(parent).expect("create dir");
+        }
+
+        fs::write(&posts[i].out_path, output).expect("write html");
+        log!("wrote {}", posts[i].out_path.display());
     }
 
     let static_dir = Path::new("static");
     if static_dir.exists() {
         copy_dir(static_dir, &Path::new("public/static"));
     }
+
+    println!("build ok");
 }
+
+fn series_nav(posts: &[Post], current: usize) -> String {
+    let series = match &posts[current].meta.series {
+        Some(s) => s,
+        None => return String::new(),
+    };
+
+    let cur_part = posts[current].meta.part.unwrap_or(0);
+
+    let mut prev: Option<&Post> = None;
+    let mut next: Option<&Post> = None;
+
+    for post in posts {
+        if post.meta.series.as_deref() != Some(series) {
+            continue;
+        }
+
+        let p = post.meta.part.unwrap_or(0);
+        if p + 1 == cur_part {
+            prev = Some(post);
+        }
+        if p == cur_part + 1 {
+            next = Some(post);
+        }
+    }
+
+    if prev.is_none() && next.is_none() {
+        return String::new();
+    }
+
+    let mut nav = String::from("<nav class=\"series-nav\">");
+
+    if let Some(p) = prev {
+        let href = format!("/{}", p.out_path.display());
+        nav.push_str(
+            &format!("<a href=\"{}\">\u{2190} {}</a>", href, p.meta.title)
+        );
+    }
+
+    if let Some(p) = next {
+        let href = format!("/{}", p.out_path.display());
+        nav.push_str(
+            &format!("<a href=\"{}\">{} \u{2192}</a>", href, p.meta.title)
+        );
+    }
+
+    nav.push_str("</nav>");
+    nav
+}