Improve documentation comments 📜

This commit is contained in:
Laurenz 2019-10-30 20:13:28 +01:00
parent b4be25e43b
commit ccc4639c7d
6 changed files with 126 additions and 124 deletions

View File

@ -2,21 +2,21 @@
//! //!
//! # Steps //! # Steps
//! - **Parsing:** The parsing step first transforms a plain string into an //! - **Parsing:** The parsing step first transforms a plain string into an
//! [iterator of tokens](crate::parsing::Tokens). Then parser constructs a //! [iterator of tokens](crate::syntax::Tokens). Then, a parser constructs a
//! syntax tree from the token stream. The structures describing the tree can //! syntax tree from the token stream. The structures describing the tree can
//! be found in the [syntax]. Dynamic functions parse their own bodies //! be found in the [syntax](crate::syntax) module.
//! themselves.
//! - **Layouting:** The next step is to transform the syntax tree into a //! - **Layouting:** The next step is to transform the syntax tree into a
//! portable representation of the typesetted document. Types for these can be //! portable representation of the typesetted document. Types for these can be
//! found in the [layout] module. //! found in the [layout] module. A finished layout reading for exporting is a
//! - **Exporting:** The finished document can then be exported into supported //! [multi layout](crate::layout::MultiLayout) consisting of multiple boxes (or
//! formats. Submodules for the supported formats are located in the [export] //! pages).
//! module. Currently the only supported format is _PDF_. //! - **Exporting:** The finished document can finally be exported into a supported
//! format. Submodules for these formats are located in the [export](crate::export)
//! module. Currently, the only supported output format is _PDF_.
pub extern crate toddle; pub extern crate toddle;
use std::cell::RefCell; use std::cell::RefCell;
use toddle::query::{FontLoader, FontProvider, SharedFontLoader}; use toddle::query::{FontLoader, FontProvider, SharedFontLoader};
use crate::func::Scope; use crate::func::Scope;
@ -36,15 +36,15 @@ pub mod size;
pub mod style; pub mod style;
pub mod syntax; pub mod syntax;
/// Transforms source code into typesetted documents. /// Transforms source code into typesetted layouts.
/// ///
/// Can be configured through various methods. /// A typesetter can be configured through various methods.
pub struct Typesetter<'p> { pub struct Typesetter<'p> {
/// The font loader shared by all typesetting processes. /// The font loader shared by all typesetting processes.
loader: SharedFontLoader<'p>, loader: SharedFontLoader<'p>,
/// The default text style. /// The base text style.
text_style: TextStyle, text_style: TextStyle,
/// The default page style. /// The base page style.
page_style: PageStyle, page_style: PageStyle,
} }
@ -59,13 +59,13 @@ impl<'p> Typesetter<'p> {
} }
} }
/// Set the default page style for the document. /// Set the base page style.
#[inline] #[inline]
pub fn set_page_style(&mut self, style: PageStyle) { pub fn set_page_style(&mut self, style: PageStyle) {
self.page_style = style; self.page_style = style;
} }
/// Set the default text style for the document. /// Set the base text style.
#[inline] #[inline]
pub fn set_text_style(&mut self, style: TextStyle) { pub fn set_text_style(&mut self, style: TextStyle) {
self.text_style = style; self.text_style = style;
@ -90,7 +90,7 @@ impl<'p> Typesetter<'p> {
parse(src, ParseContext { scope: &scope }) parse(src, ParseContext { scope: &scope })
} }
/// Layout a syntax tree and return the layout and the referenced font list. /// Layout a syntax tree and return the produced layout.
pub fn layout(&self, tree: &SyntaxTree) -> LayoutResult<MultiLayout> { pub fn layout(&self, tree: &SyntaxTree) -> LayoutResult<MultiLayout> {
let space = LayoutSpace { let space = LayoutSpace {
dimensions: self.page_style.dimensions, dimensions: self.page_style.dimensions,
@ -113,7 +113,7 @@ impl<'p> Typesetter<'p> {
Ok(pages) Ok(pages)
} }
/// Typeset a portable document from source code. /// Process source code directly into a layout.
pub fn typeset(&self, src: &str) -> Result<MultiLayout, TypesetError> { pub fn typeset(&self, src: &str) -> Result<MultiLayout, TypesetError> {
let tree = self.parse(src)?; let tree = self.parse(src)?;
let layout = self.layout(&tree)?; let layout = self.layout(&tree)?;
@ -123,9 +123,9 @@ impl<'p> Typesetter<'p> {
/// The general error type for typesetting. /// The general error type for typesetting.
pub enum TypesetError { pub enum TypesetError {
/// An error that occured while parsing. /// An error that occured in the parsing step.
Parse(ParseError), Parse(ParseError),
/// An error that occured while layouting. /// An error that occured in the layouting step.
Layout(LayoutError), Layout(LayoutError),
} }

View File

@ -1,4 +1,6 @@
/// Create an error type. //! Auxiliary macros.
/// Create trait implementations for an error type.
macro_rules! error_type { macro_rules! error_type {
( (
$var:ident: $err:ident, $var:ident: $err:ident,
@ -38,7 +40,7 @@ macro_rules! error_type {
}; };
} }
/// Create a `Debug` implementation from a display implementation. /// Create a `Debug` implementation from a `Display` implementation.
macro_rules! debug_display { macro_rules! debug_display {
($type:ident) => { ($type:ident) => {
impl std::fmt::Debug for $type { impl std::fmt::Debug for $type {

View File

@ -6,7 +6,7 @@ use std::iter::Sum;
use std::ops::*; use std::ops::*;
use std::str::FromStr; use std::str::FromStr;
/// A general spacing type. /// A general space type.
#[derive(Copy, Clone, PartialEq, Default)] #[derive(Copy, Clone, PartialEq, Default)]
pub struct Size { pub struct Size {
/// The size in typographic points (1/72 inches). /// The size in typographic points (1/72 inches).
@ -98,31 +98,31 @@ impl Size {
} }
impl Size2D { impl Size2D {
/// Create a new vector from two sizes. /// Create a new 2D-size from two sizes.
#[inline] #[inline]
pub fn new(x: Size, y: Size) -> Size2D { pub fn new(x: Size, y: Size) -> Size2D {
Size2D { x, y } Size2D { x, y }
} }
/// Create a vector with all set to zero. /// Create a 2D-size with both sizes set to zero.
#[inline] #[inline]
pub fn zero() -> Size2D { pub fn zero() -> Size2D {
Size2D::default() Size2D::default()
} }
/// Create a new vector with `y` set to zero and `x` to a value. /// Create a new 2D-size with `x` set to a value and `y` zero.
#[inline] #[inline]
pub fn with_x(x: Size) -> Size2D { pub fn with_x(x: Size) -> Size2D {
Size2D { x, y: Size::zero() } Size2D { x, y: Size::zero() }
} }
/// Create a new vector with `x` set to zero and `y` to a value. /// Create a new 2D-size with `y` set to a value and `x` zero.
#[inline] #[inline]
pub fn with_y(y: Size) -> Size2D { pub fn with_y(y: Size) -> Size2D {
Size2D { x: Size::zero(), y } Size2D { x: Size::zero(), y }
} }
/// Return a [`Size2D`] padded by the paddings of the given box. /// Return a 2D-size padded by the paddings of the given box.
#[inline] #[inline]
pub fn padded(&self, padding: SizeBox) -> Size2D { pub fn padded(&self, padding: SizeBox) -> Size2D {
Size2D { Size2D {
@ -131,7 +131,7 @@ impl Size2D {
} }
} }
/// Return a [`Size2D`] reduced by the paddings of the given box. /// Return a 2D-size reduced by the paddings of the given box.
#[inline] #[inline]
pub fn unpadded(&self, padding: SizeBox) -> Size2D { pub fn unpadded(&self, padding: SizeBox) -> Size2D {
Size2D { Size2D {
@ -140,8 +140,8 @@ impl Size2D {
} }
} }
/// Whether the given [`Size2D`] fits into this one, that is, /// Whether the given 2D-size fits into this one, that is,
/// both coordinate values are smaller. /// both coordinate values are smaller or equal.
#[inline] #[inline]
pub fn fits(&self, other: Size2D) -> bool { pub fn fits(&self, other: Size2D) -> bool {
self.x >= other.x && self.y >= other.y self.x >= other.x && self.y >= other.y
@ -160,7 +160,7 @@ impl SizeBox {
} }
} }
/// Create a box with all set to zero. /// Create a box with all values set to zero.
#[inline] #[inline]
pub fn zero() -> SizeBox { pub fn zero() -> SizeBox {
SizeBox::default() SizeBox::default()
@ -319,6 +319,8 @@ impl_num_both!(Mul, mul, MulAssign, mul_assign, i32);
impl_num_back!(Div, div, DivAssign, div_assign, f32); impl_num_back!(Div, div, DivAssign, div_assign, f32);
impl_num_back!(Div, div, DivAssign, div_assign, i32); impl_num_back!(Div, div, DivAssign, div_assign, i32);
//------------------------------------------------------------------------------------------------//
impl Display for Size2D { impl Display for Size2D {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "[{}, {}]", self.x, self.y) write!(f, "[{}, {}]", self.x, self.y)
@ -412,6 +414,8 @@ impl_num_both2d!(Mul, mul, MulAssign, mul_assign, i32);
impl_num_back2d!(Div, div, DivAssign, div_assign, f32); impl_num_back2d!(Div, div, DivAssign, div_assign, f32);
impl_num_back2d!(Div, div, DivAssign, div_assign, i32); impl_num_back2d!(Div, div, DivAssign, div_assign, i32);
//------------------------------------------------------------------------------------------------//
impl Display for SizeBox { impl Display for SizeBox {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!( write!(

View File

@ -1,22 +1,22 @@
//! Layouting styles. //! Styles for text and pages.
use toddle::query::FontClass; use toddle::query::FontClass;
use FontClass::*;
use crate::size::{Size, Size2D, SizeBox}; use crate::size::{Size, Size2D, SizeBox};
/// Default styles for text. /// Defines which fonts to use and how to space text.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct TextStyle { pub struct TextStyle {
/// The classes the font we want has to be part of. /// The classes the font has to be part of.
pub classes: Vec<FontClass>, pub classes: Vec<FontClass>,
/// A sequence of classes. We need the font to be part of at least one of /// The fallback classes from which the font needs to match the
/// these and preferably the leftmost possible. /// leftmost possible one.
pub fallback: Vec<FontClass>, pub fallback: Vec<FontClass>,
/// The font size. /// The font size.
pub font_size: f32, pub font_size: f32,
/// The line spacing (as a multiple of the font size). /// The line spacing (as a multiple of the font size).
pub line_spacing: f32, pub line_spacing: f32,
/// The paragraphs spacing (as a multiple of the line spacing). /// The paragraphs spacing (as a multiple of the font size).
pub paragraph_spacing: f32, pub paragraph_spacing: f32,
} }
@ -24,21 +24,35 @@ impl TextStyle {
/// Toggle a class. /// Toggle a class.
/// ///
/// If the class was one of _italic_ or _bold_, then: /// If the class was one of _italic_ or _bold_, then:
/// - If it was not present, the _regular_ class will be removed. /// - If it was not present before, the _regular_ class will be removed.
/// - If it was present, the _regular_ class will be added in case the other /// - If it was present before, the _regular_ class will be added in case the other
/// style class is not present. /// style class is not present.
pub fn toggle_class(&mut self, class: FontClass) { pub fn toggle_class(&mut self, class: FontClass) {
if self.classes.contains(&class) { if self.classes.contains(&class) {
self.classes.retain(|x| x != &class); // If we retain a Bold or Italic class, we will not add
if (class == FontClass::Italic && !self.classes.contains(&FontClass::Bold)) // the Regular class.
|| (class == FontClass::Bold && !self.classes.contains(&FontClass::Italic)) let mut regular = true;
{ self.classes.retain(|x| {
self.classes.push(FontClass::Regular); if class == *x {
false
} else {
if class == Bold || class == Italic {
regular = false;
}
true
}
});
if regular {
self.classes.push(Regular);
} }
} else { } else {
// If we add an Italic or Bold class, we remove
// the Regular class.
if class == FontClass::Italic || class == FontClass::Bold { if class == FontClass::Italic || class == FontClass::Bold {
self.classes.retain(|x| x != &FontClass::Regular); self.classes.retain(|x| x != &FontClass::Regular);
} }
self.classes.push(class); self.classes.push(class);
} }
} }
@ -46,7 +60,6 @@ impl TextStyle {
impl Default for TextStyle { impl Default for TextStyle {
fn default() -> TextStyle { fn default() -> TextStyle {
use FontClass::*;
TextStyle { TextStyle {
classes: vec![Regular], classes: vec![Regular],
fallback: vec![Serif], fallback: vec![Serif],
@ -57,10 +70,10 @@ impl Default for TextStyle {
} }
} }
/// Default styles for pages. /// Defines the size and margins of a page.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct PageStyle { pub struct PageStyle {
/// Width and height of the page. /// The width and height of the page.
pub dimensions: Size2D, pub dimensions: Size2D,
/// The amount of white space on each side. /// The amount of white space on each side.
pub margins: SizeBox, pub margins: SizeBox,

View File

@ -1,6 +1,5 @@
//! Tokenized and syntax tree representations of source code. //! Tokenization and parsing of source code.
use std::collections::HashMap;
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
use crate::func::Function; use crate::func::Function;
@ -17,8 +16,7 @@ pub use parsing::{parse, ParseContext, ParseError, ParseResult};
pub enum Token<'s> { pub enum Token<'s> {
/// One or more whitespace (non-newline) codepoints. /// One or more whitespace (non-newline) codepoints.
Space, Space,
/// A line feed (`\n`, `\r\n` and some more as defined by the Unicode /// A line feed (`\n`, `\r\n` and some more as defined by the Unicode standard).
/// standard).
Newline, Newline,
/// A left bracket: `[`. /// A left bracket: `[`.
LeftBracket, LeftBracket,
@ -28,37 +26,36 @@ pub enum Token<'s> {
/// header only). /// header only).
/// ///
/// If a colon occurs outside of a function header, it will be tokenized as /// If a colon occurs outside of a function header, it will be tokenized as
/// a [Word](Token::Word). /// [Text](Token::Text), just like the other tokens annotated with
/// _Function header only_.
Colon, Colon,
/// An equals (`=`) sign assigning a function argument a value (Function /// An equals (`=`) sign assigning a function argument a value (Function header only).
/// header only).
Equals, Equals,
/// A comma (`,`) separating two function arguments (Function header only). /// A comma (`,`) separating two function arguments (Function header only).
Comma, Comma,
/// Quoted text as a string value (Function header only). /// Quoted text as a string value (Function header only).
Quoted(&'s str), Quoted(&'s str),
/// An underscore, indicating text in italics. /// An underscore, indicating text in italics (Body only).
Underscore, Underscore,
/// A star, indicating bold text. /// A star, indicating bold text (Body only).
Star, Star,
/// A backtick, indicating monospace text. /// A backtick, indicating monospace text (Body only).
Backtick, Backtick,
/// A line comment. /// A line comment.
LineComment(&'s str), LineComment(&'s str),
/// A block comment. /// A block comment.
BlockComment(&'s str), BlockComment(&'s str),
/// A star followed by a slash unexpectedly ending a block comment (the /// A star followed by a slash unexpectedly ending a block comment
/// comment was not started before, otherwise a /// (the comment was not started before, otherwise a
/// [BlockComment](Token::BlockComment) would be returned). /// [BlockComment](Token::BlockComment would be returned).
StarSlash, StarSlash,
/// Everything else is just text. /// A unit of Plain text.
Text(&'s str), Text(&'s str),
} }
/// A tree representation of the source. /// A tree representation of source code.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct SyntaxTree { pub struct SyntaxTree {
/// The children.
pub nodes: Vec<Node>, pub nodes: Vec<Node>,
} }
@ -70,10 +67,10 @@ impl SyntaxTree {
} }
} }
/// A node in the abstract syntax tree. /// A node in the syntax tree.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum Node { pub enum Node {
/// Whitespace between other nodes. /// Whitespace.
Space, Space,
/// A line feed. /// A line feed.
Newline, Newline,
@ -89,28 +86,22 @@ pub enum Node {
Func(FuncCall), Func(FuncCall),
} }
/// A function invocation consisting of header and body. /// A function invocation, consisting of header and a dynamically parsed body.
#[derive(Debug)] #[derive(Debug)]
pub struct FuncCall { pub struct FuncCall {
pub header: FuncHeader, pub header: FuncHeader,
pub body: Box<dyn Function>, pub body: Box<dyn Function>,
} }
impl PartialEq for FuncCall {
fn eq(&self, other: &FuncCall) -> bool {
(self.header == other.header) && (&self.body == &other.body)
}
}
/// Contains header information of a function invocation. /// Contains header information of a function invocation.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct FuncHeader { pub struct FuncHeader {
pub name: String, pub name: String,
pub args: Vec<Expression>, pub args: Vec<Expression>,
pub kwargs: HashMap<String, Expression>, pub kwargs: Vec<(String, Expression)>,
} }
/// A value expression. /// An argument or return value.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum Expression { pub enum Expression {
Ident(String), Ident(String),
@ -120,6 +111,12 @@ pub enum Expression {
Bool(bool), Bool(bool),
} }
impl PartialEq for FuncCall {
fn eq(&self, other: &FuncCall) -> bool {
(self.header == other.header) && (&self.body == &other.body)
}
}
impl Display for Expression { impl Display for Expression {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
use Expression::*; use Expression::*;

View File

@ -1,6 +1,5 @@
//! Parsing of source code into token streams and syntax trees. //! Parsing of token streams into syntax trees.
use std::collections::HashMap;
use unicode_xid::UnicodeXID; use unicode_xid::UnicodeXID;
use crate::func::{Function, Scope}; use crate::func::{Function, Scope};
@ -20,7 +19,7 @@ pub struct ParseContext<'a> {
pub scope: &'a Scope, pub scope: &'a Scope,
} }
/// Transforms token streams to syntax trees. /// Transforms token streams into syntax trees.
#[derive(Debug)] #[derive(Debug)]
struct Parser<'s> { struct Parser<'s> {
src: &'s str, src: &'s str,
@ -35,14 +34,15 @@ struct Parser<'s> {
enum ParserState { enum ParserState {
/// The base state of the parser. /// The base state of the parser.
Body, Body,
/// We saw one newline already. /// We saw one newline already and are looking for another.
FirstNewline, FirstNewline,
/// We wrote a newline. /// We saw at least two newlines and wrote one, thus not
/// writing another one for more newlines.
WroteNewline, WroteNewline,
} }
impl<'s> Parser<'s> { impl<'s> Parser<'s> {
/// Create a new parser from the source and the context. /// Create a new parser from the source code and the context.
fn new(src: &'s str, ctx: ParseContext<'s>) -> Parser<'s> { fn new(src: &'s str, ctx: ParseContext<'s>) -> Parser<'s> {
Parser { Parser {
src, src,
@ -53,9 +53,8 @@ impl<'s> Parser<'s> {
} }
} }
/// Parse the source into an abstract syntax tree. /// Parse the source into a syntax tree.
fn parse(mut self) -> ParseResult<SyntaxTree> { fn parse(mut self) -> ParseResult<SyntaxTree> {
// Loop through all the tokens.
while self.tokens.peek().is_some() { while self.tokens.peek().is_some() {
self.parse_white()?; self.parse_white()?;
self.parse_body_part()?; self.parse_body_part()?;
@ -66,27 +65,30 @@ impl<'s> Parser<'s> {
/// Parse the next part of the body. /// Parse the next part of the body.
fn parse_body_part(&mut self) -> ParseResult<()> { fn parse_body_part(&mut self) -> ParseResult<()> {
use Token::*;
if let Some(token) = self.tokens.peek() { if let Some(token) = self.tokens.peek() {
match token { match token {
// Functions // Functions.
Token::LeftBracket => self.parse_func()?, LeftBracket => self.parse_func()?,
Token::RightBracket => return Err(ParseError::new("unexpected closing bracket")), RightBracket => return Err(ParseError::new("unexpected closing bracket")),
// Modifiers // Modifiers.
Token::Underscore => self.append_consumed(Node::ToggleItalics), Underscore => self.append_consumed(Node::ToggleItalics),
Token::Star => self.append_consumed(Node::ToggleBold), Star => self.append_consumed(Node::ToggleBold),
Token::Backtick => self.append_consumed(Node::ToggleMonospace), Backtick => self.append_consumed(Node::ToggleMonospace),
// Normal text // Normal text.
Token::Text(word) => self.append_consumed(Node::Text(word.to_owned())), Text(word) => self.append_consumed(Node::Text(word.to_owned())),
Token::Colon | Token::Equals => panic!("bad token for body: {:?}", token),
// The rest is handled elsewhere or should not happen, because `Tokens` does not // The rest is handled elsewhere or should not happen, because `Tokens` does not
// yield colons or equals in the body, but their text equivalents instead. // yield these in a body.
_ => panic!("unexpected token: {:?}", token), Space | Newline | LineComment(_) | BlockComment(_) |
Colon | Equals | Comma | Quoted(_) | StarSlash
=> panic!("parse_body_part: unexpected token: {:?}", token),
} }
} }
Ok(()) Ok(())
} }
@ -122,7 +124,7 @@ impl<'s> Parser<'s> {
let mut header = FuncHeader { let mut header = FuncHeader {
name, name,
args: vec![], args: vec![],
kwargs: HashMap::new(), kwargs: vec![],
}; };
self.skip_white(); self.skip_white();
@ -147,9 +149,9 @@ impl<'s> Parser<'s> {
} }
/// Parse the arguments to a function. /// Parse the arguments to a function.
fn parse_func_args(&mut self) -> ParseResult<(Vec<Expression>, HashMap<String, Expression>)> { fn parse_func_args(&mut self) -> ParseResult<(Vec<Expression>, Vec<(String, Expression)>)> {
let mut args = Vec::new(); let mut args = Vec::new();
let kwargs = HashMap::new(); let kwargs = Vec::new();
let mut comma = false; let mut comma = false;
loop { loop {
@ -448,38 +450,22 @@ mod tests {
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct TreeFn(pub SyntaxTree); pub struct TreeFn(pub SyntaxTree);
impl Function for TreeFn { function! {
fn parse(_: &FuncHeader, body: Option<&str>, ctx: ParseContext) -> ParseResult<Self> data: TreeFn,
where Self: Sized {
if let Some(src) = body {
parse(src, ctx).map(|tree| TreeFn(tree))
} else {
Err(ParseError::new("expected body for tree fn"))
}
}
fn layout(&self, _: LayoutContext) -> LayoutResult<CommandList> { parse(_args, body, ctx) { Ok(TreeFn(parse!(required: body, ctx))) }
Ok(CommandList::new()) layout(_, _) { Ok(commands![]) }
}
} }
/// A testing function without a body. /// A testing function without a body.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct BodylessFn; pub struct BodylessFn;
impl Function for BodylessFn { function! {
fn parse(_: &FuncHeader, body: Option<&str>, _: ParseContext) -> ParseResult<Self> data: BodylessFn,
where Self: Sized {
if body.is_none() {
Ok(BodylessFn)
} else {
Err(ParseError::new("unexpected body for bodyless fn"))
}
}
fn layout(&self, _: LayoutContext) -> LayoutResult<CommandList> { parse(_args, body, _ctx) { parse!(forbidden: body); Ok(BodylessFn) }
Ok(CommandList::new()) layout(_, _) { Ok(commands![]) }
}
} }
} }
@ -539,7 +525,7 @@ mod tests {
header: FuncHeader { header: FuncHeader {
name: $name.to_string(), name: $name.to_string(),
args: vec![], args: vec![],
kwargs: HashMap::new(), kwargs: vec![],
}, },
body: $body, body: $body,
} }
@ -617,7 +603,7 @@ mod tests {
header: FuncHeader { header: FuncHeader {
name: name.to_string(), name: name.to_string(),
args, args,
kwargs: HashMap::new(), kwargs: vec![],
}, },
body: Box::new(BodylessFn) body: Box::new(BodylessFn)
}) })