summaryrefslogtreecommitdiff
path: root/src/func/mod.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2019-10-23 00:14:43 +0200
committerLaurenz <laurmaedje@gmail.com>2019-10-23 00:21:40 +0200
commitecf0ff4d054f11c79ec0ddbbdf45f3dfcf9fc8d7 (patch)
treeefdc76915e5c7f43629aa3aa5d5aa3998d7f5367 /src/func/mod.rs
parentcff325b520727ac0eec46a3757831afaa0916cc1 (diff)
Introduce a set of macros for writing functions more concisely 🎁
Diffstat (limited to 'src/func/mod.rs')
-rw-r--r--src/func/mod.rs187
1 files changed, 187 insertions, 0 deletions
diff --git a/src/func/mod.rs b/src/func/mod.rs
new file mode 100644
index 00000000..9a6fcbd1
--- /dev/null
+++ b/src/func/mod.rs
@@ -0,0 +1,187 @@
+//! Dynamic typesetting functions.
+
+use std::any::Any;
+use std::collections::HashMap;
+use std::fmt::{self, Debug, Formatter};
+
+use crate::layout::{Layout, LayoutContext, Alignment, LayoutResult, MultiLayout};
+use crate::parsing::{ParseContext, ParseResult};
+use crate::style::TextStyle;
+use crate::syntax::{FuncHeader, SyntaxTree};
+
+#[macro_use]
+mod helpers;
+pub use helpers::Arguments;
+
+/// Useful imports for creating your own functions.
+pub mod prelude {
+ pub use crate::func::{Command, CommandList, Function};
+ pub use crate::layout::{layout_tree, Layout, LayoutContext, MultiLayout};
+ pub use crate::layout::{Flow, Alignment, LayoutError, LayoutResult};
+ pub use crate::parsing::{parse, ParseContext, ParseError, ParseResult};
+ pub use crate::syntax::{Expression, FuncHeader, SyntaxTree};
+ pub use crate::size::{Size, Size2D, SizeBox};
+ pub use super::helpers::*;
+}
+
+/// Typesetting function types.
+///
+/// These types have to be able to parse tokens into themselves and store the
+/// relevant information from the parsing to do their role in typesetting later.
+///
+/// The trait `FunctionBounds` is automatically implemented for types which can
+/// be used as functions, that is they fulfill the bounds `Debug + PartialEq +
+/// 'static`.
+pub trait Function: FunctionBounds {
+ /// Parse the header and body into this function given a context.
+ fn parse(header: &FuncHeader, body: Option<&str>, ctx: ParseContext) -> ParseResult<Self>
+ where Self: Sized;
+
+ /// Layout this function given a context.
+ ///
+ /// Returns optionally the resulting layout and a new context if changes to
+ /// the context should be made.
+ fn layout(&self, ctx: LayoutContext) -> LayoutResult<CommandList>;
+}
+
+impl PartialEq for dyn Function {
+ fn eq(&self, other: &dyn Function) -> bool {
+ self.help_eq(other)
+ }
+}
+
+/// A helper trait that describes requirements for types that can implement
+/// [`Function`].
+///
+/// Automatically implemented for all types which fulfill to the bounds `Debug +
+/// PartialEq + 'static`. There should be no need to implement this manually.
+pub trait FunctionBounds: Debug {
+ /// Cast self into `Any`.
+ fn help_cast_as_any(&self) -> &dyn Any;
+
+ /// Compare self with another function.
+ fn help_eq(&self, other: &dyn Function) -> bool;
+}
+
+impl<T> FunctionBounds for T
+where T: Debug + PartialEq + 'static
+{
+ fn help_cast_as_any(&self) -> &dyn Any {
+ self
+ }
+
+ fn help_eq(&self, other: &dyn Function) -> bool {
+ if let Some(other) = other.help_cast_as_any().downcast_ref::<Self>() {
+ self == other
+ } else {
+ false
+ }
+ }
+}
+
+/// A sequence of commands requested for execution by a function.
+#[derive(Debug)]
+pub struct CommandList<'a> {
+ pub commands: Vec<Command<'a>>,
+}
+
+impl<'a> CommandList<'a> {
+ /// Create an empty command list.
+ pub fn new() -> CommandList<'a> {
+ CommandList { commands: vec![] }
+ }
+
+ /// Create a command list with commands from a vector.
+ pub fn from_vec(commands: Vec<Command<'a>>) -> CommandList<'a> {
+ CommandList { commands }
+ }
+
+ /// Add a command to the sequence.
+ pub fn add(&mut self, command: Command<'a>) {
+ self.commands.push(command);
+ }
+
+ /// Whether there are any commands in this sequence.
+ pub fn is_empty(&self) -> bool {
+ self.commands.is_empty()
+ }
+}
+
+impl<'a> IntoIterator for CommandList<'a> {
+ type Item = Command<'a>;
+ type IntoIter = std::vec::IntoIter<Command<'a>>;
+
+ fn into_iter(self) -> Self::IntoIter {
+ self.commands.into_iter()
+ }
+}
+
+impl<'a> IntoIterator for &'a CommandList<'a> {
+ type Item = &'a Command<'a>;
+ type IntoIter = std::slice::Iter<'a, Command<'a>>;
+
+ fn into_iter(self) -> Self::IntoIter {
+ self.commands.iter()
+ }
+}
+
+/// Commands requested for execution by functions.
+#[derive(Debug)]
+pub enum Command<'a> {
+ Layout(&'a SyntaxTree),
+ Add(Layout),
+ AddMany(MultiLayout),
+ AddFlex(Layout),
+ SetAlignment(Alignment),
+ SetStyle(TextStyle),
+ FinishLayout,
+ FinishFlexRun,
+}
+
+macro_rules! commands {
+ ($($x:expr),*$(,)*) => ({
+ $crate::func::CommandList::from_vec(vec![$($x,)*])
+ });
+}
+
+/// A map from identifiers to functions.
+pub struct Scope {
+ parsers: HashMap<String, Box<ParseFunc>>,
+}
+
+/// A function which parses a function invocation into a function type.
+type ParseFunc = dyn Fn(&FuncHeader, Option<&str>, ParseContext) -> ParseResult<Box<dyn Function>>;
+
+impl Scope {
+ /// Create a new empty scope.
+ pub fn new() -> Scope {
+ Scope {
+ parsers: HashMap::new(),
+ }
+ }
+
+ /// Create a new scope with the standard functions contained.
+ pub fn with_std() -> Scope {
+ crate::library::std()
+ }
+
+ /// Add a function type to the scope giving it a name.
+ pub fn add<F: Function + 'static>(&mut self, name: &str) {
+ self.parsers.insert(
+ name.to_owned(),
+ Box::new(|h, b, c| F::parse(h, b, c).map(|func| Box::new(func) as Box<dyn Function>)),
+ );
+ }
+
+ /// Return the parser with the given name if there is one.
+ pub(crate) fn get_parser(&self, name: &str) -> Option<&ParseFunc> {
+ self.parsers.get(name).map(|x| &**x)
+ }
+}
+
+impl Debug for Scope {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ write!(f, "Scope ")?;
+ write!(f, "{:?}", self.parsers.keys())
+ }
+}