///|
pub(all) struct PosterJob {
slug : String
template : String
preset : String
theme : String
content : @template.TemplateContent
} derive(Debug, Eq, FromJson, ToJson)
///|
pub(all) struct BatchJob {
jobs : Array[PosterJob]
} derive(Debug, Eq, FromJson, ToJson)
///|
pub(all) struct RenderedPoster {
slug : String
svg : String
} derive(Debug, Eq)
///|
pub(all) struct ValidationSummary {
total_jobs : Int
slugs : Array[String]
templates : Array[String]
presets : Array[String]
themes : Array[String]
} derive(Debug, Eq, ToJson)
///|
pub(all) enum ConfigError {
InvalidJson(message~ : String)
UnknownPreset(name~ : String)
UnknownTheme(name~ : String)
UnknownTemplate(name~ : String)
DuplicateSlug(name~ : String)
} derive(Debug, Eq)
///|
fn contains_name(values : Array[String], target : String) -> Bool {
values.any(value => value == target)
}
///|
fn push_unique(values : Array[String], target : String) -> Unit {
if !contains_name(values, target) {
values.push(target)
}
}
///|
fn validate_job(job : PosterJob) -> ConfigError? {
match @registry.resolve_preset(job.preset) {
Some(_) =>
match @registry.resolve_theme(job.theme) {
Some(_) =>
match @registry.resolve_template_name(job.template) {
Some(_) => None
None => Some(ConfigError::UnknownTemplate(name=job.template))
}
None => Some(ConfigError::UnknownTheme(name=job.theme))
}
None => Some(ConfigError::UnknownPreset(name=job.preset))
}
}
///|
pub fn parse_batch(raw : String) -> Result[BatchJob, ConfigError] {
try {
let batch : BatchJob = @json.from_json(@json.parse(raw))
Ok(batch)
} catch {
_ => Err(ConfigError::InvalidJson(message="invalid-json"))
}
}
///|
fn validate_jobs(
jobs : Array[PosterJob],
index : Int,
slugs : Array[String],
templates : Array[String],
presets : Array[String],
themes : Array[String],
) -> Result[ValidationSummary, ConfigError] {
if index >= jobs.length() {
Ok({ total_jobs: jobs.length(), slugs, templates, presets, themes })
} else {
let job = jobs[index]
match validate_job(job) {
Some(error) => Err(error)
None =>
if contains_name(slugs, job.slug) {
Err(ConfigError::DuplicateSlug(name=job.slug))
} else {
slugs.push(job.slug)
push_unique(templates, job.template)
push_unique(presets, job.preset)
push_unique(themes, job.theme)
validate_jobs(jobs, index + 1, slugs, templates, presets, themes)
}
}
}
}
///|
pub fn validate_batch(
batch : BatchJob,
) -> Result[ValidationSummary, ConfigError] {
validate_jobs(batch.jobs, 0, [], [], [], [])
}
///|
/// Build the typed scene document for export backends.
pub fn render_job_document(
job : PosterJob,
) -> Result[@scene.PosterDocument, ConfigError] {
match @registry.resolve_preset(job.preset) {
Some(preset) =>
match @registry.resolve_theme(job.theme) {
Some(theme) =>
match @registry.resolve_template_name(job.template) {
Some(template) => {
let document = @template.render_template(
template~,
preset~,
theme~,
content=job.content,
)
Ok(document)
}
None => Err(ConfigError::UnknownTemplate(name=job.template))
}
None => Err(ConfigError::UnknownTheme(name=job.theme))
}
None => Err(ConfigError::UnknownPreset(name=job.preset))
}
}
///|
/// Render a job to the existing SVG output.
pub fn render_job(job : PosterJob) -> Result[RenderedPoster, ConfigError] {
match render_job_document(job) {
Ok(document) => Ok({ slug: job.slug, svg: @svg.render_svg(document) })
Err(error) => Err(error)
}
}
///|
fn render_jobs(
jobs : Array[PosterJob],
index : Int,
rendered : Array[RenderedPoster],
) -> Result[Array[RenderedPoster], ConfigError] {
if index >= jobs.length() {
Ok(rendered)
} else {
match render_job(jobs[index]) {
Ok(poster) => {
rendered.push(poster)
render_jobs(jobs, index + 1, rendered)
}
Err(error) => Err(error)
}
}
}
///|
pub fn render_batch(
batch : BatchJob,
) -> Result[Array[RenderedPoster], ConfigError] {
match validate_batch(batch) {
Ok(_) => render_jobs(batch.jobs, 0, [])
Err(error) => Err(error)
}
}