summaryrefslogtreecommitdiff
path: root/library/src/meta
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-11-29 13:37:25 +0100
committerLaurenz <laurmaedje@gmail.com>2022-11-29 14:18:13 +0100
commit0efe669278a5e1c3f2985eba2f3360e91159c54a (patch)
tree502712857c48f0decb5e698257c0a96d358a436e /library/src/meta
parent836692e73cff0356e409a9ba5b4887b86809d4ca (diff)
Reorganize library and tests
Diffstat (limited to 'library/src/meta')
-rw-r--r--library/src/meta/document.rs48
-rw-r--r--library/src/meta/link.rs66
-rw-r--r--library/src/meta/mod.rs9
-rw-r--r--library/src/meta/reference.rs26
4 files changed, 149 insertions, 0 deletions
diff --git a/library/src/meta/document.rs b/library/src/meta/document.rs
new file mode 100644
index 00000000..309e1bda
--- /dev/null
+++ b/library/src/meta/document.rs
@@ -0,0 +1,48 @@
+use crate::layout::{LayoutRoot, PageNode};
+use crate::prelude::*;
+
+/// The root node that represents a full document.
+#[derive(Hash)]
+pub struct DocumentNode(pub StyleVec<PageNode>);
+
+#[node(LayoutRoot)]
+impl DocumentNode {
+ /// The document's title.
+ #[property(referenced)]
+ pub const TITLE: Option<EcoString> = None;
+
+ /// The document's author.
+ #[property(referenced)]
+ pub const AUTHOR: Option<EcoString> = None;
+}
+
+impl LayoutRoot for DocumentNode {
+ /// Layout the document into a sequence of frames, one per page.
+ fn layout_root(
+ &self,
+ world: Tracked<dyn World>,
+ styles: StyleChain,
+ ) -> SourceResult<Document> {
+ let mut pages = vec![];
+ for (page, map) in self.0.iter() {
+ let number = 1 + pages.len();
+ let fragment = page.layout(world, number, styles.chain(map))?;
+ pages.extend(fragment);
+ }
+
+ Ok(Document {
+ metadata: Metadata {
+ title: styles.get(Self::TITLE).clone(),
+ author: styles.get(Self::AUTHOR).clone(),
+ },
+ pages,
+ })
+ }
+}
+
+impl Debug for DocumentNode {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ f.write_str("Document ")?;
+ self.0.fmt(f)
+ }
+}
diff --git a/library/src/meta/link.rs b/library/src/meta/link.rs
new file mode 100644
index 00000000..44da9c5d
--- /dev/null
+++ b/library/src/meta/link.rs
@@ -0,0 +1,66 @@
+use crate::prelude::*;
+use crate::text::TextNode;
+
+/// Link text and other elements to a destination.
+#[derive(Debug, Hash)]
+pub struct LinkNode {
+ /// The destination the link points to.
+ pub dest: Destination,
+ /// How the link is represented.
+ pub body: Content,
+}
+
+impl LinkNode {
+ /// Create a link node from a URL with its bare text.
+ pub fn from_url(url: EcoString) -> Self {
+ let mut text = url.as_str();
+ for prefix in ["mailto:", "tel:"] {
+ text = text.trim_start_matches(prefix);
+ }
+ let shorter = text.len() < url.len();
+ let body = TextNode::packed(if shorter { text.into() } else { url.clone() });
+ Self { dest: Destination::Url(url), body }
+ }
+}
+
+#[node(Show, Finalize)]
+impl LinkNode {
+ /// A destination the text should be linked to.
+ #[property(skip, referenced)]
+ pub(crate) const DEST: Option<Destination> = None;
+
+ fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
+ let dest = args.expect::<Destination>("destination")?;
+ Ok(match dest {
+ Destination::Url(url) => match args.eat()? {
+ Some(body) => Self { dest: Destination::Url(url), body },
+ None => Self::from_url(url),
+ },
+ Destination::Internal(_) => Self { dest, body: args.expect("body")? },
+ }
+ .pack())
+ }
+
+ fn field(&self, name: &str) -> Option<Value> {
+ match name {
+ "url" => Some(match &self.dest {
+ Destination::Url(url) => Value::Str(url.clone().into()),
+ Destination::Internal(loc) => Value::Dict(loc.encode()),
+ }),
+ "body" => Some(Value::Content(self.body.clone())),
+ _ => None,
+ }
+ }
+}
+
+impl Show for LinkNode {
+ fn show(&self, _: Tracked<dyn World>, _: StyleChain) -> Content {
+ self.body.clone()
+ }
+}
+
+impl Finalize for LinkNode {
+ fn finalize(&self, realized: Content) -> Content {
+ realized.styled(Self::DEST, Some(self.dest.clone()))
+ }
+}
diff --git a/library/src/meta/mod.rs b/library/src/meta/mod.rs
new file mode 100644
index 00000000..31a69ccc
--- /dev/null
+++ b/library/src/meta/mod.rs
@@ -0,0 +1,9 @@
+//! Interaction between document parts.
+
+mod document;
+mod link;
+mod reference;
+
+pub use self::document::*;
+pub use self::link::*;
+pub use self::reference::*;
diff --git a/library/src/meta/reference.rs b/library/src/meta/reference.rs
new file mode 100644
index 00000000..948aa6f6
--- /dev/null
+++ b/library/src/meta/reference.rs
@@ -0,0 +1,26 @@
+use crate::prelude::*;
+use crate::text::TextNode;
+
+/// A reference to a label.
+#[derive(Debug, Hash)]
+pub struct RefNode(pub EcoString);
+
+#[node(Show)]
+impl RefNode {
+ fn construct(_: &Vm, args: &mut Args) -> SourceResult<Content> {
+ Ok(Self(args.expect("target")?).pack())
+ }
+
+ fn field(&self, name: &str) -> Option<Value> {
+ match name {
+ "target" => Some(Value::Str(self.0.clone().into())),
+ _ => None,
+ }
+ }
+}
+
+impl Show for RefNode {
+ fn show(&self, _: Tracked<dyn World>, _: StyleChain) -> Content {
+ TextNode::packed(format_eco!("@{}", self.0))
+ }
+}