1mod block;
7mod model;
8mod table;
9mod wrap;
10
11pub use model::{
12 CodeLayout, LayoutBlock, LayoutDocument, LayoutLine, LayoutSpan, LinkTarget, RuleLayout,
13 SemanticStyle, TableLayout, TextBlock,
14};
15pub use table::TableLayoutEngine;
16
17use thiserror::Error;
18
19use mdsee_core::Document;
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub struct LayoutOptions {
24 pub terminal_width: u16,
25 pub max_width: u16,
27 pub margin: u16,
29}
30
31impl Default for LayoutOptions {
32 fn default() -> Self {
33 Self {
34 terminal_width: 80,
35 max_width: 100,
36 margin: 2,
37 }
38 }
39}
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq)]
47pub struct LayoutContext {
48 pub terminal_width: u16,
49 pub content_width: u16,
50}
51
52impl LayoutContext {
53 pub fn from_options(options: &LayoutOptions) -> Self {
55 let inner = options
56 .terminal_width
57 .saturating_sub(options.margin.saturating_mul(2));
58 let content_width = inner.min(options.max_width).max(1);
59 Self {
60 terminal_width: options.terminal_width,
61 content_width,
62 }
63 }
64}
65
66#[derive(Debug, Error)]
70pub enum LayoutError {
71 #[error("layout failed")]
72 LayoutFailed,
73}
74
75pub fn layout(document: &Document, options: &LayoutOptions) -> Result<LayoutDocument, LayoutError> {
77 let ctx = LayoutContext::from_options(options);
78 let blocks = document
79 .blocks
80 .iter()
81 .map(|block| block::layout_block(block, &ctx))
82 .collect();
83 Ok(LayoutDocument { blocks })
84}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89
90 #[test]
91 fn content_width_is_min_of_inner_width_and_max_width() {
92 let ctx = LayoutContext::from_options(&LayoutOptions {
94 terminal_width: 80,
95 max_width: 100,
96 margin: 2,
97 });
98 assert_eq!(ctx.content_width, 76);
99
100 let ctx = LayoutContext::from_options(&LayoutOptions {
102 terminal_width: 120,
103 max_width: 100,
104 margin: 2,
105 });
106 assert_eq!(ctx.content_width, 100);
107 }
108
109 #[test]
110 fn content_width_never_drops_below_one() {
111 let ctx = LayoutContext::from_options(&LayoutOptions {
112 terminal_width: 0,
113 max_width: 100,
114 margin: 2,
115 });
116 assert_eq!(ctx.content_width, 1);
117 }
118}