summaryrefslogtreecommitdiff
path: root/src/library/mod.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2020-01-24 12:44:04 +0100
committerLaurenz <laurmaedje@gmail.com>2020-01-24 12:44:04 +0100
commit03fddaf3aea778057aedd74dbcb27bae971ec22f (patch)
tree37e3136e29e0e5d69ec8f56e43d156739d2931ab /src/library/mod.rs
parent78da2bdd5d77d1b8572e5e9da119bfa68127a3fa (diff)
Non-fatal argument parsing 🌋
Diffstat (limited to 'src/library/mod.rs')
-rw-r--r--src/library/mod.rs374
1 files changed, 43 insertions, 331 deletions
diff --git a/src/library/mod.rs b/src/library/mod.rs
index 8d16ccaf..e706642f 100644
--- a/src/library/mod.rs
+++ b/src/library/mod.rs
@@ -1,34 +1,32 @@
//! The standard library.
-use toddle::query::{FontWeight, FontStyle};
-
use crate::func::prelude::*;
-use crate::style::{Paper, PaperClass};
-use self::maps::{ExtentMap, PaddingMap, AxisKey};
-
-pub mod maps;
-pub_use_mod!(align);
-pub_use_mod!(boxed);
-pub_use_mod!(direction);
+pub_use_mod!(font);
+pub_use_mod!(layout);
+pub_use_mod!(page);
+pub_use_mod!(spacing);
/// Create a scope with all standard functions.
pub fn std() -> Scope {
- let mut std = Scope::new();
+ let mut std = Scope::new::<ValFunc>();
+
+ // Basics
+ std.add::<ValFunc>("val");
// Font setup
std.add::<FontFamilyFunc>("font.family");
std.add::<FontStyleFunc>("font.style");
std.add::<FontWeightFunc>("font.weight");
std.add::<FontSizeFunc>("font.size");
+ std.add_with_meta::<ContentSpacingFunc>("word.spacing", ContentKind::Word);
// Layout
+ std.add_with_meta::<ContentSpacingFunc>("line.spacing", ContentKind::Line);
+ std.add_with_meta::<ContentSpacingFunc>("par.spacing", ContentKind::Paragraph);
std.add::<AlignFunc>("align");
std.add::<DirectionFunc>("direction");
- std.add_with_metadata::<ContentSpacingFunc>("par.spacing", ContentKind::Paragraph);
- std.add_with_metadata::<ContentSpacingFunc>("word.spacing", ContentKind::Word);
- std.add_with_metadata::<ContentSpacingFunc>("line.spacing", ContentKind::Line);
std.add::<BoxFunc>("box");
// Spacing
@@ -36,9 +34,9 @@ pub fn std() -> Scope {
std.add::<LineBreakFunc>("line.break");
std.add::<ParBreakFunc>("par.break");
std.add::<PageBreakFunc>("page.break");
- std.add_with_metadata::<SpacingFunc>("spacing", None);
- std.add_with_metadata::<SpacingFunc>("h", Some(Horizontal));
- std.add_with_metadata::<SpacingFunc>("v", Some(Vertical));
+ std.add_with_meta::<SpacingFunc>("spacing", None);
+ std.add_with_meta::<SpacingFunc>("h", Some(Horizontal));
+ std.add_with_meta::<SpacingFunc>("v", Some(Vertical));
// Page setup
std.add::<PageSizeFunc>("page.size");
@@ -47,331 +45,45 @@ pub fn std() -> Scope {
std
}
-// -------------------------------------------------------------------------- //
-// Font setup
-
function! {
- /// `font.family`: Set the font family.
- #[derive(Debug, PartialEq)]
- pub struct FontFamilyFunc {
- body: Option<SyntaxTree>,
- list: Vec<String>,
+ /// `val`: Layouts the body with no special effect.
+ #[derive(Debug, Clone, PartialEq)]
+ pub struct ValFunc {
+ body: Option<SyntaxModel>,
}
- parse(header, body, ctx) {
- FontFamilyFunc {
- body: parse!(optional: body, ctx),
- list: {
- header.args.iter_pos().map(|arg| match arg.v {
- Expr::Str(s) |
- Expr::Ident(Ident(s)) => Ok(s.to_lowercase()),
- _ => error!("expected identifier or string"),
- }).collect::<LayoutResult<Vec<_>>>()?
- }
- }
+ parse(header, body, ctx, errors, decos) {
+ ValFunc { body: body!(opt: body, ctx, errors, decos) }
}
- layout(self, ctx) {
- let mut style = ctx.style.text.clone();
- style.fallback.list = self.list.clone();
- styled(&self.body, &ctx, style)
- }
-}
-
-function! {
- /// `font.style`: Set the font style (normal / italic).
- #[derive(Debug, PartialEq)]
- pub struct FontStyleFunc {
- body: Option<SyntaxTree>,
- style: FontStyle,
- }
-
- parse(header, body, ctx) {
- FontStyleFunc {
- body: parse!(optional: body, ctx),
- style: {
- let s = header.args.get_pos::<String>()?;
- match FontStyle::from_str(&s) {
- Some(style) => style,
- None => error!("invalid font style: `{}`", s),
- }
- }
+ layout(self, ctx, errors) {
+ match &self.body {
+ Some(model) => vec![LayoutSyntaxModel(model)],
+ None => vec![],
}
}
-
- layout(self, ctx) {
- let mut style = ctx.style.text.clone();
- style.variant.style = self.style;
- styled(&self.body, &ctx, style)
- }
}
-function! {
- /// `font.weight`: Set text with a given weight.
- #[derive(Debug, PartialEq)]
- pub struct FontWeightFunc {
- body: Option<SyntaxTree>,
- weight: FontWeight,
- }
-
- parse(header, body, ctx) {
- FontWeightFunc {
- body: parse!(optional: body, ctx),
- weight: match header.args.get_pos::<Expr>()? {
- Expr::Number(weight) => {
- let weight = weight.round() as i16;
- FontWeight(
- if weight < 100 { 100 }
- else if weight <= 900 { weight }
- else { 900 }
- )
- }
- Expr::Ident(Ident(s)) => {
- match FontWeight::from_str(&s) {
- Some(weight) => weight,
- None => error!("invalid font weight: `{}`", s),
- }
- }
- _ => error!("expected identifier or number"),
- },
- }
- }
-
- layout(self, ctx) {
+/// Layout an optional body with a change of the text style.
+fn styled<'a, T, F>(
+ body: &'a Option<SyntaxModel>,
+ ctx: LayoutContext,
+ data: Option<T>,
+ f: F,
+) -> Commands<'a> where F: FnOnce(&mut TextStyle, T) {
+ if let Some(data) = data {
let mut style = ctx.style.text.clone();
- style.variant.weight = self.weight;
- styled(&self.body, &ctx, style)
- }
-}
-
-function! {
- /// `font.size`: Sets the font size.
- #[derive(Debug, PartialEq)]
- pub struct FontSizeFunc {
- body: Option<SyntaxTree>,
- size: ScaleSize,
- }
-
- parse(header, body, ctx) {
- FontSizeFunc {
- body: parse!(optional: body, ctx),
- size: header.args.get_pos::<ScaleSize>()?,
+ f(&mut style, data);
+
+ match body {
+ Some(model) => vec![
+ SetTextStyle(style),
+ LayoutSyntaxModel(model),
+ SetTextStyle(ctx.style.text.clone()),
+ ],
+ None => vec![SetTextStyle(style)],
}
- }
-
- layout(self, ctx) {
- let mut style = ctx.style.text.clone();
- match self.size {
- ScaleSize::Absolute(size) => {
- style.base_font_size = size;
- style.font_scale = 1.0;
- }
- ScaleSize::Scaled(scale) => style.font_scale = scale,
- }
- styled(&self.body, &ctx, style)
- }
-}
-
-// -------------------------------------------------------------------------- //
-// Layout
-
-function! {
- /// `word.spacing`, `line.spacing`, `par.spacing`: The spacing between
- /// words, lines or paragraphs as a multiple of the font size.
- #[derive(Debug, PartialEq)]
- pub struct ContentSpacingFunc {
- body: Option<SyntaxTree>,
- content: ContentKind,
- spacing: f32,
- }
-
- type Meta = ContentKind;
-
- parse(header, body, ctx, meta) {
- ContentSpacingFunc {
- body: parse!(optional: body, ctx),
- content: meta,
- spacing: header.args.get_pos::<f64>()? as f32,
- }
- }
-
- layout(self, ctx) {
- let mut style = ctx.style.text.clone();
- match self.content {
- ContentKind::Word => style.word_spacing_scale = self.spacing,
- ContentKind::Line => style.line_spacing_scale = self.spacing,
- ContentKind::Paragraph => style.paragraph_spacing_scale = self.spacing,
- }
- styled(&self.body, &ctx, style)
- }
-}
-
-/// The different kinds of content that can be spaced.
-#[derive(Debug, Copy, Clone, Eq, PartialEq)]
-pub enum ContentKind {
- Word,
- Line,
- Paragraph,
-}
-
-// -------------------------------------------------------------------------- //
-// Spacing
-
-function! {
- /// `line.break`, `n`: Ends the current line.
- #[derive(Debug, Default, PartialEq)]
- pub struct LineBreakFunc;
-
- parse(default)
- layout() { vec![FinishLine] }
-}
-
-function! {
- /// `par.break`: Ends the current paragraph.
- ///
- /// self has the same effect as two subsequent newlines.
- #[derive(Debug, Default, PartialEq)]
- pub struct ParBreakFunc;
-
- parse(default)
- layout() { vec![BreakParagraph] }
-}
-
-function! {
- /// `page.break`: Ends the current page.
- #[derive(Debug, Default, PartialEq)]
- pub struct PageBreakFunc;
-
- parse(default)
- layout() { vec![BreakPage] }
-}
-
-function! {
- /// `spacing`, `h`, `v`: Adds spacing along an axis.
- #[derive(Debug, PartialEq)]
- pub struct SpacingFunc {
- axis: AxisKey,
- spacing: FSize,
- }
-
- type Meta = Option<SpecificAxis>;
-
- parse(header, body, _, meta) {
- parse!(forbidden: body);
-
- if let Some(axis) = meta {
- SpacingFunc {
- axis: AxisKey::Specific(axis),
- spacing: FSize::from_expr(
- header.args.get_pos::<Spanned<Expr>>()?
- )?,
- }
- } else {
- for arg in header.args.iter_keys() {
- let axis = AxisKey::from_ident(&arg.key)
- .map_err(|_| error!(@unexpected_argument))?;
-
- let spacing = FSize::from_expr(arg.value)?;
- return Ok(SpacingFunc { axis, spacing });
- }
-
- error!("expected axis and spacing")
- }
- }
-
- layout(self, ctx) {
- let axis = self.axis.to_generic(ctx.axes);
- let spacing = self.spacing.scaled(ctx.style.text.font_size());
- vec![SpacingFunc(spacing, SpacingKind::Hard, axis)]
- }
-}
-
-// -------------------------------------------------------------------------- //
-// Page setup
-
-function! {
- /// `page.size`: Set the size of pages.
- #[derive(Debug, PartialEq)]
- pub enum PageSizeFunc {
- Paper(Paper, bool),
- Custom(ExtentMap<PSize>),
- }
-
- parse(header, body) {
- parse!(forbidden: body);
-
- if let Some(name) = header.args.get_pos_opt::<Ident>()? {
- let flip = header.args.get_key_opt::<bool>("flip")?.unwrap_or(false);
- let paper = Paper::from_name(name.as_str())
- .ok_or_else(|| error!(@"invalid paper name: `{}`", name))?;
- PageSizeFunc::Paper(paper, flip)
- } else {
- PageSizeFunc::Custom(ExtentMap::new(&mut header.args, true)?)
- }
- }
-
- layout(self, ctx) {
- let mut style = ctx.style.page;
-
- match self {
- PageSizeFunc::Paper(paper, flip) => {
- style.class = paper.class;
- style.dimensions = paper.dimensions;
- if *flip {
- style.dimensions.swap();
- }
- }
-
- PageSizeFunc::Custom(map) => {
- style.class = PaperClass::Custom;
-
- let map = map.dedup(ctx.axes)?;
- let dims = &mut style.dimensions;
- map.with(Horizontal, |&psize| dims.x = psize.scaled(dims.x));
- map.with(Vertical, |&psize| dims.y = psize.scaled(dims.y));
- }
- }
-
- vec![SetPageStyle(style)]
- }
-}
-
-function! {
- /// `page.margins`: Sets the page margins.
- #[derive(Debug, PartialEq)]
- pub struct PageMarginsFunc {
- map: PaddingMap,
- }
-
- parse(header, body) {
- parse!(forbidden: body);
- PageMarginsFunc {
- map: PaddingMap::new(&mut header.args)?,
- }
- }
-
- layout(self, ctx) {
- let mut style = ctx.style.page;
- self.map.apply(ctx.axes, &mut style.margins)?;
- vec![SetPageStyle(style)]
- }
-}
-
-// -------------------------------------------------------------------------- //
-// Helpers
-
-/// Layout the body with the style or update the style if there is no body.
-fn styled<'a>(
- body: &'a Option<SyntaxTree>,
- ctx: &LayoutContext,
- style: TextStyle
-) -> Commands<'a> {
- match &body {
- Some(body) => vec![
- SetTextStyle(style),
- LayoutTree(body),
- SetTextStyle(ctx.style.text.clone()),
- ],
- None => vec![SetTextStyle(style)]
+ } else {
+ vec![]
}
}