On this page
The CV as data: one declared TOML document rendered both as the page a reader sees and as the Person a crawler reads, so the two can never disagree.
#internal/cv
#internal/cv
Package cv holds the CV as data: one declared document, rendered as a page and as a Person.
A curriculum vitae is a record, not prose that happens to look like one -- every part of it is a field somebody could ask for by name. It is therefore declared in a TOML document and rendered from there, so the page a reader sees and the Person a crawler reads are two renderings of one source rather than two texts that have to be kept in agreement by hand.
The document is validated strictly: an unknown key anywhere, a missing required field, or an empty section is a hard error naming the offending declaration. Every section is required and non-empty, for the reason the curated project listing gives for the same rule -- an absent section would render as a heading over nothing, and there is no sensible default for a fact about a person.
Two renderings, both from [CV]:
- [RenderCVMarkdown] -- the page body, as Markdown, which the build converts like any other page content. - [CVPersonJSONLD] -- a Person carrying what the CV knows on top of the site's declared author: the job title, the summary, the languages, the schools, and every external profile.
#CVSource
const CVSource = "docs/cv.toml"CVSource is where the home project declares its CV, relative to the project root.
#CVFormatVersion
const CVFormatVersion int64 = 1CVFormatVersion is the document format this package reads. A document carrying anything else is a hard error, never a guess about which shape was meant.
#CVPageType
const CVPageType = "cv"CVPageType is the page type a CV page declares in its frontmatter, which the schema-type mapping turns into ProfilePage.
#CVPersonAttr
const CVPersonAttr = "data-cv-person"CVPersonAttr is the attribute the rendered page carries its Person in, for the SEO tag builder to lift into the head.
A directive resolves BEFORE the Markdown converter runs, so anything it emits is text the converter will rewrite: a JSON-LD script in body position comes back with the summary's link turned into an anchor tag inside a JSON string, and an HTML-escaped attribute fares no better, because the inline transforms run across attribute values too. The payload therefore crosses the conversion base64-encoded -- an alphabet with no Markdown meaning -- and is decoded into real structured data in the head, where the page's other JSON-LD is emitted.
#TopLevelKeys
var TopLevelKeys = []string{TopLevelKeys is every key the document itself may carry.
#IdentityKeys
var IdentityKeys = []string{IdentityKeys is every key the [identity] table may carry.
#ProfileKeys
var ProfileKeys = []string{"label", "url"}ProfileKeys is every key an [[identity.profile]] block may carry.
#SkillKeys
var SkillKeys = []string{"category", "items"}SkillKeys is every key a [[skills]] block may carry.
#ProjectKeys
var ProjectKeys = []string{"name", "notes", "technologies"}ProjectKeys is every key a [[projects]] block may carry.
#InterestKeys
var InterestKeys = []string{"title", "body"}InterestKeys is every key an [[interests]] block may carry.
#EducationKeys
var EducationKeys = []string{EducationKeys is every key an [[education]] block may carry.
#ExperienceKeys
var ExperienceKeys = []string{ExperienceKeys is every key an [[experience]] block may carry.
#LanguageKeys
var LanguageKeys = []string{"name", "url", "level"}LanguageKeys is every key a [[languages]] block may carry.
#ContactKeys
var ContactKeys = []string{"body"}ContactKeys is every key the [contact] table may carry.
#Profile
type Profile structProfile is one external address the CV's owner is reachable at.
#Identity
type Identity structIdentity is who the CV is about.
#SkillGroup
type SkillGroup structSkillGroup is one category of skills with its items.
#Project
type Project structProject is one project the CV lists.
#Interest
type Interest structInterest is one hobby or interest with its description.
#Education
type Education structEducation is one qualification.
#Experience
type Experience structExperience is one post held.
#Language
type Language structLanguage is one language spoken, with the level and an optional link.
#CV
type CV structCV is the whole declared document, in declared order.
#ParseCV
func ParseCV(text string, source string) (*CV, error)ParseCV returns the CV the document text declares, naming source in every diagnostic.
It refuses, naming the offending declaration, a syntax error, an unknown key, a missing or empty required field, an empty section, a repeated entry, or a format version this package does not read.
#LoadCV
func LoadCV(path string) (*CV, error)LoadCV returns the CV declared in the TOML document at path.
#RenderCVMarkdown
func RenderCVMarkdown(cv *CV) stringRenderCVMarkdown returns the CV as the Markdown body of a page.
Section headings are fixed, because they are the document's structure rather than one of its facts. The header block and the closing date are HTML, for the reason [renderCVHeader] gives; everything else is Markdown, so the headings enter the table of contents and the prose keeps its links and emphasis.
#CVPersonJSONLD
func CVPersonJSONLD(cv *CV, author map[string]any) (identity.Entity, error)CVPersonJSONLD returns the Person a CV page states, as a JSON-LD document.
The identity itself -- name, url, sameAs -- comes from the site's declared author, so a CV page and the front page name the same person. What the CV adds is what a CV knows: the job title, the summary, the languages, the schools, and the external profiles it lists (folded into sameAs after the declared ones, without repeating any).
It returns [identity.ErrNoDeclaredAuthor] when the build declares no author.
#RenderCVPage
func RenderCVPage(cv *CV, author map[string]any) (string, error)RenderCVPage returns the page body: the CV, carrying the Person it states.
#ExtractCVPerson
func ExtractCVPerson(bodyHTML string) (string, bool, error)ExtractCVPerson returns the Person JSON a rendered CV page carries. The bool reports whether the page carried one at all.
It refuses when the attribute is there but does not decode to a JSON object -- a page that carried a broken entity would publish it as if it were a fact.