resume/generate.scm

143 lines
5.8 KiB
Scheme
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

;;; generate.scm - Walk resume data and produce HTML via SXML
;;; Run with: guile generate.scm > resume.html
(use-modules (sxml simple))
(load "resume.scm")
;; ─── Utility ──────────────────────────────────────────────────────────────────
(define (bullets items)
`(ul ,@(map (lambda (s) `(li ,s)) items)))
(define (date-range start end)
`(span (@ (class "date-range")) ,start " " ,end))
(define (tech-list techs)
`(ul (@ (class "tech-list"))
,@(map (lambda (t) `(li (@ (class "tech-tag")) ,t)) techs)))
;; ─── Individual cards (all become flat children of .columns) ──────────────────
(define (render-project proj)
`(div (@ (class "project-card"))
(div (@ (class "project-header"))
(span (@ (class "project-name")) ,(field proj 'name))
,(date-range (field proj 'date-start) (field proj 'date-end)))
(div (@ (class "project-summary"))
,(bullets (field proj 'summary-bullets)))
(div (@ (class "project-details"))
,(bullets (field proj 'detail-bullets)))
,(tech-list (field proj 'technologies))))
;; Title card: blurb only, no projects nested inside.
;; Projects are emitted as siblings after the title card.
(define (render-title-card tc)
`(div (@ (class "title-card"))
(div (@ (class "title-header"))
(span (@ (class "title-name")) ,(field tc 'title))
,(date-range (field tc 'date-start) (field tc 'date-end)))
(div (@ (class "title-meta"))
,(field tc 'company) " • " ,(field tc 'location))
(div (@ (class "title-blurb"))
,(bullets (field tc 'blurb)))))
;; Emit a title card followed by its project cards as a flat list of siblings
(define (render-title-flat tc)
(append
(list (render-title-card tc))
(map render-project (field tc 'projects))))
(define (render-education edu)
`(div (@ (class "education-card"))
(div (@ (class "edu-header"))
(span (@ (class "edu-degree")) ,(field edu 'degree))
,(date-range (field edu 'date-start) (field edu 'date-end)))
(div (@ (class "edu-meta"))
,(field edu 'institution) " • " ,(field edu 'location))
(div (@ (class "edu-bullets"))
,(bullets (field edu 'bullets)))
(div (@ (class "edu-extras"))
,(bullets (field edu 'extracurriculars)))))
(define (render-interest item)
`(div (@ (class "interest-card"))
(span (@ (class "interest-title")) ,(field item 'title))
(span (@ (class "interest-description")) ,(field item 'description))))
;; ─── Section → flat list of column children ───────────────────────────────────
(define (section->items sec)
(case (car sec)
((job)
(apply append (map render-title-flat (field sec 'titles))))
((education)
(list (render-education sec)))
((hobby-projects)
(map render-project (field sec 'projects)))
((interests)
(map render-interest (field sec 'items)))
(else '())))
(define (section-label sec)
(case (car sec)
((job) "Work Experience")
((education) "Education")
((hobby-projects) "Projects")
((interests) "Interests")
(else "")))
;; Group consecutive sections of same type, emit one heading per group
(define (sections->flat-items sections)
(if (null? sections) '()
(let loop ((rest (cdr sections))
(cur-type (caar sections))
(cur-label (section-label (car sections)))
(cur-items (section->items (car sections)))
(acc '()))
(cond
((null? rest)
(append (reverse acc)
(list `(h2 (@ (class "sec-heading")) ,cur-label))
cur-items))
((eq? (caar rest) cur-type)
;; same type: accumulate items without a new heading
(loop (cdr rest) cur-type cur-label
(append cur-items (section->items (car rest)))
acc))
(else
(loop (cdr rest) (caar rest) (section-label (car rest))
(section->items (car rest))
(append acc
(list `(h2 (@ (class "sec-heading")) ,cur-label))
cur-items)))))))
;; ─── Front matter ─────────────────────────────────────────────────────────────
(define (render-front-matter fm)
`(header (@ (class "front-matter"))
(h1 (@ (class "fm-name")) ,(field fm 'name))
(div (@ (class "fm-contact"))
(span ,(field fm 'location))
(span ,(field fm 'phone))
(span ,(field fm 'email)))))
;; ─── Document ─────────────────────────────────────────────────────────────────
(define (render-resume res)
(let* ((fm (field res 'front-matter))
(items (sections->flat-items (field res 'sections))))
`(*TOP*
(html (@ (lang "en"))
(head
(meta (@ (charset "UTF-8")))
(meta (@ (name "viewport") (content "width=device-width, initial-scale=1")))
(title ,(field fm 'name))
(link (@ (rel "stylesheet") (href "style.css"))))
(body
,(render-front-matter fm)
(main (@ (class "columns"))
,@items))))))
(display "<!DOCTYPE html>\n")
(sxml->xml (render-resume resume))
(newline)