Compare commits

...

196 Commits

Author SHA1 Message Date
fluxcdbot
b3929f22f3 CI: autofix rust code.
Some checks failed
clippy Build clippy has failed
format Build format has succeeded
rust-test Build rust-test has failed
build Build build has succeeded
2025-08-31 22:24:16 +00:00
Tom Alexander
bad12160ac Let chains have been stabalized.
Some checks are pending
clippy Build clippy has started
format Build format has succeeded
rust-test Build rust-test has succeeded
build Build build has succeeded
2025-08-31 18:22:06 -04:00
Tom Alexander
c43679fda9 Switch to local-path-provisioner.
Some checks failed
build Build build has started
clippy Build clippy has failed
rust-test Build rust-test has failed
format Build format has failed
2025-08-31 17:53:10 -04:00
Tom Alexander
9cc28f6f0d Merge branch 'unlisted_posts'
All checks were successful
format Build format has succeeded
clippy Build clippy has succeeded
rust-test Build rust-test has succeeded
build Build build has succeeded
2025-02-23 12:12:43 -05:00
Tom Alexander
d2256b8333 Add publish filter to blog posts and pages.
All checks were successful
format Build format has succeeded
clippy Build clippy has succeeded
rust-test Build rust-test has succeeded
2025-02-23 12:08:43 -05:00
Tom Alexander
fa8753077a Add support for unlisted posts.
All checks were successful
format Build format has succeeded
clippy Build clippy has succeeded
rust-test Build rust-test has succeeded
2025-02-23 12:02:14 -05:00
Tom Alexander
0420f58d02 Add a hover effect to the home link in the page header.
All checks were successful
format Build format has succeeded
clippy Build clippy has succeeded
rust-test Build rust-test has succeeded
build Build build has succeeded
2025-02-22 23:16:18 -05:00
Tom Alexander
0250aa106e Merge branch 'about_me'
All checks were successful
format Build format has succeeded
clippy Build clippy has succeeded
rust-test Build rust-test has succeeded
build Build build has succeeded
2025-02-22 22:54:18 -05:00
Tom Alexander
ca1c456571 Pass the nav links in the PageHeader render context.
All checks were successful
format Build format has succeeded
clippy Build clippy has succeeded
rust-test Build rust-test has succeeded
2025-02-22 22:49:59 -05:00
Tom Alexander
4403980e2e Add a me link to the nav bar. 2025-02-22 22:36:42 -05:00
Tom Alexander
dbfbce955d Merge branch 'tracing'
Some checks are pending
clippy Build clippy has started
format Build format has succeeded
rust-test Build rust-test has succeeded
build Build build has succeeded
2025-02-22 21:12:44 -05:00
Tom Alexander
2e08d2e59a Set up tracing. 2025-02-22 21:12:33 -05:00
Tom Alexander
9f14534c10 Merge branch 'source_code_highlighting'
All checks were successful
format Build format has succeeded
clippy Build clippy has succeeded
rust-test Build rust-test has succeeded
build Build build has succeeded
2025-02-22 19:56:47 -05:00
Tom Alexander
4e34ebc29e Reformat css.
All checks were successful
format Build format has succeeded
clippy Build clippy has succeeded
rust-test Build rust-test has succeeded
2025-02-22 19:45:04 -05:00
Tom Alexander
8d85d5ef79 Fix clippy.
All checks were successful
format Build format has succeeded
clippy Build clippy has succeeded
rust-test Build rust-test has succeeded
2025-02-22 19:42:24 -05:00
Tom Alexander
7d73a3c948 Clean up. 2025-02-22 19:24:05 -05:00
Tom Alexander
c501f7cedc Add syntax highlighting for bash.
Some checks failed
format Build format has succeeded
clippy Build clippy has failed
rust-test Build rust-test has succeeded
2025-02-22 19:07:56 -05:00
Tom Alexander
41927764fc Continue highlights across code block lines. 2025-02-22 18:45:20 -05:00
Tom Alexander
75a763569b Disable ligatures in all code areas. 2025-02-22 18:27:45 -05:00
Tom Alexander
c67eb32774 Add more colors to python. 2025-02-22 18:26:33 -05:00
Tom Alexander
04952895cf Add support for highlighting python based on the nix highlighter. 2025-02-22 17:56:56 -05:00
Tom Alexander
749f6d7a55 Dynamically register which CSS files are needed. 2025-02-22 17:28:24 -05:00
Tom Alexander
c4cf814f8d Also add highlighting for paths. 2025-02-22 16:55:53 -05:00
Tom Alexander
3245e830d2 Assign more colors. 2025-02-22 16:46:47 -05:00
Tom Alexander
57eb1b81ec Start assigning colors. 2025-02-22 16:35:05 -05:00
Tom Alexander
c601c8697a Start a language-specific css file for highlight colors. 2025-02-22 16:25:36 -05:00
Tom Alexander
4ea1a46705 Update dust templates to support the new source code block format. 2025-02-22 16:13:23 -05:00
Tom Alexander
4cc04bda46 Update the render context to use the new src block format. 2025-02-22 16:04:47 -05:00
Tom Alexander
7e934cd360 Removed the enum and made plain src blocks just highlighted src blocks with only plain text in them. 2025-02-22 15:55:16 -05:00
Tom Alexander
e34e2ef75f Implement the highlighted src intermediate format.
I am going to remove the enum because I realized plain src blocks can just be highlighted src blocks with only RawText entries.
2025-02-22 15:44:45 -05:00
Tom Alexander
c067ca9cc8 Introduce an enum for a separate highlighted src block type. 2025-02-22 15:22:35 -05:00
Tom Alexander
b06424cb17 Initial highlighting code.
The dust auto-escaping is causing this naive approach to fail so I will have to create a distinction between highlighted code and not-highlighted code.
2025-02-22 15:09:00 -05:00
Tom Alexander
ae6f18d19c Center images when they are the only contents in a paragraph.
Some checks failed
format Build format has succeeded
clippy Build clippy has failed
rust-test Build rust-test has succeeded
build Build build has succeeded
2025-02-22 13:15:58 -05:00
Tom Alexander
c371b999d5 Use the raw timestamp source for rendering timestamps.
Some checks failed
format Build format has succeeded
clippy Build clippy has failed
rust-test Build rust-test has succeeded
build Build build has succeeded
2025-02-22 12:23:35 -05:00
Tom Alexander
073ac0ac25 Merge branch 'style_improvements'
Some checks failed
format Build format has succeeded
rust-test Build rust-test has succeeded
clippy Build clippy has failed
build Build build has succeeded
2025-02-18 07:19:25 -05:00
Tom Alexander
1c356737c1 Only add vertical padding for the top of the first blog post and use the same background color throughout.
Some checks failed
format Build format has succeeded
clippy Build clippy has failed
rust-test Build rust-test has succeeded
2025-02-17 22:32:38 -05:00
Tom Alexander
339bd433f6 Add slight padding the blog posts.
Some checks failed
format Build format has succeeded
clippy Build clippy has failed
rust-test Build rust-test has succeeded
2025-02-17 22:16:43 -05:00
Tom Alexander
bb5fa6a487 Swap site background and blog post background to give better contrast.
Some checks failed
format Build format has succeeded
clippy Build clippy has failed
rust-test Build rust-test has succeeded
2025-02-17 22:12:08 -05:00
Tom Alexander
71b6db14d5 Add a background color for stand-alone blog posts. 2025-02-17 22:08:46 -05:00
Tom Alexander
69fb91db37 Remove padding on blog stream posts.
It was causing an ugly tail on blog stream posts.
2025-02-17 22:00:33 -05:00
Tom Alexander
5fd93fc648 Allow line wrapping even when contiguous words are really long.
The code blocks were causing horizontal scrolling on mobile.
2025-02-17 21:29:43 -05:00
Tom Alexander
033a17e355 Increase home link font size. 2025-02-17 21:29:43 -05:00
Tom Alexander
824f34bd4c Make everything box-sizing: border-box. 2025-02-17 20:57:40 -05:00
Tom Alexander
d93e91c625 Increase the font size for inline source blocks to the normal font size.
Some checks failed
format Build format has succeeded
clippy Build clippy has failed
rust-test Build rust-test has succeeded
2025-02-17 20:54:56 -05:00
Tom Alexander
db21bd8a55 Restrict media to the size of its container.
Some checks failed
format Build format has succeeded
clippy Build clippy has failed
rust-test Build rust-test has succeeded
2025-02-17 20:49:36 -05:00
Tom Alexander
69729bd329 Include viewport meta tag. 2025-02-17 20:49:36 -05:00
Tom Alexander
ddea8fdceb Increase title font-size.
Some checks failed
format Build format has succeeded
clippy Build clippy has failed
rust-test Build rust-test has succeeded
2025-02-17 19:03:48 -05:00
Tom Alexander
71f639e503 Fix size of code blocks in headlines and make the line spacing more pleasant.
Some checks failed
format Build format has succeeded
clippy Build clippy has failed
build Build build has succeeded
rust-test Build rust-test has succeeded
2025-02-16 20:09:17 -05:00
Tom Alexander
8714d3b650 Increase the division between posts.
Some checks failed
format Build format has succeeded
clippy Build clippy has failed
rust-test Build rust-test has succeeded
build Build build has succeeded
2025-02-16 17:39:21 -05:00
Tom Alexander
88064409a6 Add support for PNGs.
Some checks failed
format Build format has succeeded
clippy Build clippy has failed
rust-test Build rust-test has succeeded
build Build build has succeeded
2025-02-16 15:46:26 -05:00
Tom Alexander
7c17087920 Show bold text as bold.
Some checks failed
clippy Build clippy has failed
rust-test Build rust-test has succeeded
build Build build has succeeded
format Build format has succeeded
2025-02-16 11:45:52 -05:00
Tom Alexander
c1837addd0 Remove unused variable.
Some checks failed
format Build format has succeeded
clippy Build clippy has failed
rust-test Build rust-test has succeeded
build Build build has succeeded
2025-02-08 22:05:22 -05:00
Tom Alexander
79c36476bd Merge branch 'render_images'
Some checks failed
rust-test Build rust-test has succeeded
format Build format has succeeded
clippy Build clippy has failed
build Build build has succeeded
2025-02-08 21:48:23 -05:00
Tom Alexander
4a0cbf3ba5 Do not copy a file if it already exists.
Some checks failed
format Build format has started
rust-test Build rust-test has started
clippy Build clippy has failed
2025-02-08 21:28:41 -05:00
Tom Alexander
59ee13345e Copy the images into the output. 2025-02-08 19:55:50 -05:00
Tom Alexander
bf7f37260c Mark the image files for copying. 2025-02-08 19:23:19 -05:00
Tom Alexander
ff478253c3 Generate render link based on path to file. 2025-02-08 19:14:08 -05:00
Tom Alexander
3e952ef0f4 Implement a macro for pushing the directory. 2025-02-08 18:37:32 -05:00
Tom Alexander
4e0f66401d Update the dependency manager file stack when rendering blog post pages. 2025-02-08 18:01:59 -05:00
Tom Alexander
3867f965d2 Add a dependency manager for render-time actions.
This will be used for supporting things like copying static files or rendering code blocks like gnuplot or graphviz.
2025-02-08 17:29:09 -05:00
Tom Alexander
5cac44c625 Store the path to the original source file in the blog post object. 2025-02-07 21:08:06 -05:00
Tom Alexander
463be34302 Async closure is now stable. 2025-02-07 20:51:31 -05:00
Tom Alexander
8fd37cbf22 Remove a previously completed TODO. 2025-02-01 23:28:09 -05:00
Tom Alexander
4fb67c18ae Add support for rendering the images. 2025-02-01 23:14:20 -05:00
Tom Alexander
eb18185131 Add support for Image targets in the intermediate step.
Some checks are pending
clippy Build clippy has started
format Build format has succeeded
rust-test Build rust-test has succeeded
2025-02-01 21:45:52 -05:00
Tom Alexander
7c92b602bc Add automated test for testing the link target code.
Some checks failed
clippy Build clippy has failed
rust-test Build rust-test has succeeded
format Build format has succeeded
2025-02-01 17:59:08 -05:00
Tom Alexander
aeca958cef Add back in docker targets.
All checks were successful
clippy Build clippy has succeeded
rust-test Build rust-test has succeeded
build Build build has succeeded
format Build format has succeeded
2024-10-20 23:11:32 -04:00
Tom Alexander
495a3c0b4e Use linked copy. 2024-10-20 23:04:45 -04:00
Tom Alexander
9402c453da Add dockerfile syntax header. 2024-10-20 22:31:47 -04:00
Tom Alexander
328b2ba520 Merge branch 'buildkit'
All checks were successful
format Build format has succeeded
clippy Build clippy has succeeded
rust-test Build rust-test has succeeded
build Build build has succeeded
2024-10-20 21:56:23 -04:00
Tom Alexander
4a76f61aa7 Update dockerfiles to take advantage of BuildKit.
All checks were successful
clippy Build clippy has succeeded
format Build format has succeeded
rust-test Build rust-test has succeeded
2024-10-20 21:50:16 -04:00
Tom Alexander
2c9396615e Switch to using BuiltKit instead of Kaniko to build docker images. 2024-10-20 21:47:11 -04:00
Tom Alexander
e7b6dc9059 Use the latest kaniko.
All checks were successful
rust-test Build rust-test has succeeded
format Build format has succeeded
clippy Build clippy has succeeded
build Build build has succeeded
2024-10-19 18:53:57 -04:00
Tom Alexander
bcdf441d77 Merge branch 'recursive_blog_post_directory'
All checks were successful
format Build format has succeeded
clippy Build clippy has succeeded
rust-test Build rust-test has succeeded
build Build build has succeeded
2024-10-19 17:34:17 -04:00
Tom Alexander
884a28e63a Remove pointless copying.
All checks were successful
format Build format has succeeded
rust-test Build rust-test has succeeded
clippy Build clippy has succeeded
2024-10-19 17:28:50 -04:00
Tom Alexander
1c3e2ca4d9 Remove the last use of walkdir. 2024-10-19 17:26:37 -04:00
Tom Alexander
2081d25066 Remove a use of WalkDir. 2024-10-19 17:14:05 -04:00
Tom Alexander
7ddc4011b3 Use a type alias for read file results. 2024-10-19 17:03:52 -04:00
Tom Alexander
379850fe3d Use the deep path as the post id. 2024-10-19 16:55:38 -04:00
Tom Alexander
8ab69e480e Case insensitive match for org file extension. 2024-10-19 16:38:29 -04:00
Tom Alexander
c5621212bc Using multi-level deep folders successfully but the output is still shallow. 2024-10-19 16:35:45 -04:00
Tom Alexander
b9f74b7eca Write the filter to find the highest folders containing org documents. 2024-10-19 16:25:54 -04:00
Tom Alexander
493adb4688 Switch to iterative instead of recursive. 2024-10-19 16:15:23 -04:00
Tom Alexander
b6cc7a70b7 Future is not send. 2024-10-18 21:29:15 -04:00
Tom Alexander
8868cfb63f Don't need to hard-code static lifetime. 2024-10-18 21:23:22 -04:00
Tom Alexander
ae3add9c81 Fixed recursion using a BoxFuture. 2024-10-18 21:22:39 -04:00
Tom Alexander
98fa43575d Require boxing?. 2024-10-18 21:19:40 -04:00
Tom Alexander
5d3a6c4174 Also infinite. 2024-10-18 21:15:23 -04:00
Tom Alexander
be467c8074 infinite recursion? 2024-10-18 21:13:50 -04:00
Tom Alexander
0da375c529 Add a function to recursively list all entries in a directory with tokio. 2024-10-18 21:05:29 -04:00
Tom Alexander
1b740b1f2f Update dependencies.
Some checks failed
format Build format has succeeded
clippy Build clippy has failed
rust-test Build rust-test has succeeded
2024-10-18 20:33:41 -04:00
Tom Alexander
d94ecefab9 Merge branch 'webhook_bridge'
All checks were successful
format Build format has succeeded
clippy Build clippy has succeeded
rust-test Build rust-test has succeeded
build Build build has succeeded
2024-09-30 17:39:04 -04:00
Tom Alexander
7501a018fd Switch to using webhook_bridge instead of lighthouse for triggering the CI. 2024-09-30 17:38:10 -04:00
Tom Alexander
ce31397372 Inline the build natter pipeline.
All checks were successful
format Build format has succeeded
rust-clippy Build rust-clippy has succeeded
rust-test Build rust-test has succeeded
build-natter Build build-natter has succeeded
2024-04-12 18:59:34 -04:00
Tom Alexander
7e1e070e23 Update organic.
All checks were successful
format Build format has succeeded
rust-clippy Build rust-clippy has succeeded
rust-test Build rust-test has succeeded
2024-04-12 18:48:25 -04:00
Tom Alexander
fa16a7dd39 Add styling for pagination links.
All checks were successful
rust-test Build rust-test has succeeded
format Build format has succeeded
build-natter Build build-natter has succeeded
rust-clippy Build rust-clippy has succeeded
2023-12-23 23:09:24 -05:00
Tom Alexander
59a91331cc Add support for target links.
All checks were successful
format Build format has succeeded
build-natter Build build-natter has succeeded
rust-clippy Build rust-clippy has succeeded
rust-test Build rust-test has succeeded
2023-12-23 22:54:48 -05:00
Tom Alexander
d2ea6b6a0f Add a light mode to the site.
All checks were successful
format Build format has succeeded
build-natter Build build-natter has succeeded
rust-clippy Build rust-clippy has succeeded
rust-test Build rust-test has succeeded
2023-12-23 21:54:16 -05:00
Tom Alexander
fdff5a667b Merge branch 'post_links'
All checks were successful
format Build format has succeeded
build-natter Build build-natter has succeeded
rust-clippy Build rust-clippy has succeeded
rust-test Build rust-test has succeeded
2023-12-23 21:40:37 -05:00
Tom Alexander
35cf675c87 Fix clippy.
All checks were successful
format Build format has succeeded
rust-clippy Build rust-clippy has succeeded
rust-test Build rust-test has succeeded
2023-12-23 21:36:24 -05:00
Tom Alexander
3b63bbdfde Add support for subpaths. 2023-12-23 21:29:59 -05:00
Tom Alexander
24218f2979 Generate post links, so far only top-level. 2023-12-23 21:22:43 -05:00
Tom Alexander
6a086d57de Generate Post link when the protocol is post. 2023-12-23 20:55:32 -05:00
Tom Alexander
93e0a2fe98 Add URL parsing. 2023-12-23 20:45:20 -05:00
Tom Alexander
86d4e25aa0 Add support for a target variable in regular links.
This will initially be used for supporting "post://" links.
2023-12-23 20:35:27 -05:00
Tom Alexander
cb5d131097 Add support for plain links.
All checks were successful
format Build format has succeeded
rust-test Build rust-test has succeeded
rust-clippy Build rust-clippy has succeeded
build-natter Build build-natter has succeeded
2023-12-23 19:23:39 -05:00
Tom Alexander
eb67327574 Do not error if posts directory does not exist.
All checks were successful
build-natter Build build-natter has succeeded
format Build format has succeeded
rust-clippy Build rust-clippy has succeeded
rust-test Build rust-test has succeeded
2023-12-23 17:14:31 -05:00
Tom Alexander
5228851c0e Merge branch 'pages'
All checks were successful
rust-test Build rust-test has succeeded
rust-clippy Build rust-clippy has succeeded
format Build format has succeeded
build-natter Build build-natter has succeeded
2023-12-23 17:00:24 -05:00
Tom Alexander
4fc08f4375 Add a template for static pages.
All checks were successful
rust-clippy Build rust-clippy has succeeded
rust-test Build rust-test has succeeded
format Build format has succeeded
2023-12-23 16:55:49 -05:00
Tom Alexander
8905c9356b Add a build for regular non-blog-post pages from org source. 2023-12-23 16:55:49 -05:00
Tom Alexander
424a970014 Add make target to run tests, clippy, and the auto-formatter locally.
All checks were successful
format Build format has succeeded
build-natter Build build-natter has succeeded
rust-clippy Build rust-clippy has succeeded
rust-test Build rust-test has succeeded
2023-12-23 16:02:24 -05:00
Tom Alexander
138d694b27 Copy static files over to the output directory. 2023-12-23 15:45:23 -05:00
Tom Alexander
397d4ea0bc Fix clippy issues.
All checks were successful
rust-test Build rust-test has succeeded
rust-clippy Build rust-clippy has succeeded
build-natter Build build-natter has succeeded
format Build format has succeeded
2023-12-23 07:08:06 -05:00
Tom Alexander
818fca87f2 Merge branch 'ci_test'
Some checks failed
rust-test Build rust-test has succeeded
rust-clippy Build rust-clippy has failed
format Build format has succeeded
build-natter Build build-natter has succeeded
2023-12-23 06:14:47 -05:00
fluxcdbot
df339f20fa CI: autofix rust code.
Some checks failed
rust-clippy Build rust-clippy has failed
rust-test Build rust-test has succeeded
format Build format has succeeded
2023-12-23 01:46:40 +00:00
Tom Alexander
d5572c93cd Add prettier step.
Some checks failed
rust-clippy Build rust-clippy has failed
format Build format has succeeded
rust-test Build rust-test has succeeded
2023-12-22 20:43:39 -05:00
Tom Alexander
322dbb8f4f Add CI job to auto-format code. 2023-12-22 20:37:47 -05:00
Tom Alexander
904f834c86 Add CI job to run clippy.
Some checks failed
rust-clippy Build rust-clippy has failed
rust-test Build rust-test has succeeded
2023-12-22 20:08:24 -05:00
Tom Alexander
48af194da0 Use a resolver to remove dependency on installed catalog tasks.
All checks were successful
rust-test Build rust-test has succeeded
2023-12-22 20:02:16 -05:00
Tom Alexander
bcb6b2d75f Inline the cargo cache autoclean step. 2023-12-22 20:02:16 -05:00
Tom Alexander
134444b2c3 Set cargo target dir.
This is to avoid writing to the target dir in the repo folder.
2023-12-22 19:45:46 -05:00
Tom Alexander
4447f1ed4a Inline the test pipeline. 2023-12-22 19:31:12 -05:00
Tom Alexander
457ff9e759 Add dockerfile for running rust tests.
All checks were successful
rust-test Build rust-test has succeeded
2023-12-22 19:09:34 -05:00
Tom Alexander
6f244a0a5f Add pipeline for running the rust tests. 2023-12-22 19:09:34 -05:00
Tom Alexander
cb5838345e Merge branch 'style_tables'
All checks were successful
build-natter Build build-natter has succeeded
2023-12-22 18:07:46 -05:00
Tom Alexander
1107a653cf Add borders to the table groups. 2023-12-22 18:06:55 -05:00
Tom Alexander
95d4ee7080 Render the table groups. 2023-12-22 18:06:55 -05:00
Tom Alexander
fa2dd96f78 Update intermediate phase for table groups. 2023-12-22 18:00:07 -05:00
Tom Alexander
7741e192f5 Group table rows into sections. 2023-12-22 00:08:06 -05:00
Tom Alexander
5dfd46852f Add some basic styling to tables. 2023-12-21 21:59:52 -05:00
Tom Alexander
88e10010d8 Remove extra workspace from tekton pipeline.
Some checks failed
build-natter Build build-natter has failed
2023-12-21 20:36:03 -05:00
Tom Alexander
52c564d4fd Merge branch 'docker' 2023-12-21 20:29:00 -05:00
Tom Alexander
f7874c1843 Add lighthouse job to build the natter images. 2023-12-21 20:25:46 -05:00
Tom Alexander
40120667f7 Add a Dockerfile for building and running natter. 2023-12-21 20:04:04 -05:00
Tom Alexander
b06798f23f Prepare for publishing to crates.io. 2023-12-21 19:40:28 -05:00
Tom Alexander
35dff5cdaf Rename to natter. 2023-12-21 19:28:31 -05:00
Tom Alexander
d641c8d638 Merge branch 'pretty' 2023-12-21 18:36:23 -05:00
Tom Alexander
7e2fd70212 Style the homepage. 2023-12-21 18:34:42 -05:00
Tom Alexander
4fb08bc7d0 Wrap inline footnote definitions in a paragraph tag.
This is to match the behavior of the upstream org html exporter.
2023-12-21 18:09:43 -05:00
Tom Alexander
01b55b7256 Make paragraphs under footnote definitions display inline.
This seems to be the behavior of the upstream org html exporter.
2023-12-21 17:44:40 -05:00
Tom Alexander
bd68681e44 Increase the size of headlines.
This is to make them stand out more from the regular text.
2023-12-21 17:31:19 -05:00
Tom Alexander
27ff13e675 Also special-case plain list items containing only paragraphs and sublists.
This seems to be the behavior of the upstream org html exporter.
2023-12-21 17:27:19 -05:00
Tom Alexander
2914e42ba1 For plain list items with a single child that is a paragraph, do not wrap in paragraph html tags.
This is mimicking the behavior from org-mode's HTML exporter.
2023-12-21 17:18:51 -05:00
Tom Alexander
a8969f141d Style code and verbatim. 2023-12-21 16:49:44 -05:00
Tom Alexander
6f049e00d4 Style inline source blocks. 2023-12-21 16:42:22 -05:00
Tom Alexander
45a1076d18 Style descriptive plain lists. 2023-12-21 16:39:26 -05:00
Tom Alexander
775c88d67a Style footnotes. 2023-12-21 15:44:56 -05:00
Tom Alexander
efbf6cfc0c Style ordered and unordered plain lists. 2023-12-21 15:31:13 -05:00
Tom Alexander
5af4a372ae Make object trailing space dependent on post_blank. 2023-12-21 15:13:07 -05:00
Tom Alexander
72952adb6b Add post_blank to the rust types. 2023-12-21 15:09:13 -05:00
Tom Alexander
8b85c02ef1 Wrap the intermediate Registry in an IntermediateContext.
This is currently just to maintain consistency with the render phase's RenderContext but in the future it should allow us to include immutable data that is not locked behind the ArcMutex.
2023-12-21 13:53:56 -05:00
Tom Alexander
2ae4839ce0 Remove common whitespace prefix from src block lines. 2023-12-21 13:16:05 -05:00
Tom Alexander
5654c40d03 Get the real language for src blocks from the org source. 2023-12-21 12:07:36 -05:00
Tom Alexander
b538750287 Preserve whitespace in src blocks. 2023-12-21 12:04:29 -05:00
Tom Alexander
65ed754bfe Space out some elements. 2023-12-19 21:57:33 -05:00
Tom Alexander
80cdf5166b Style quote blocks, h2, and h3. 2023-12-19 21:41:41 -05:00
Tom Alexander
3968121d54 Style src blocks. 2023-12-19 21:30:59 -05:00
Tom Alexander
a29b625631 Apply a dark background. 2023-12-19 20:33:21 -05:00
Tom Alexander
e193fcc2ba Start applying styles to blog posts. 2023-12-19 19:23:56 -05:00
Tom Alexander
050b426f6f Merge branch 'homepage' 2023-12-19 18:08:50 -05:00
Tom Alexander
da81f93e4a Re-enable the CSS reset sheet. 2023-12-19 18:06:52 -05:00
Tom Alexander
1581e5c401 Rename the output root directory in the render context. 2023-12-19 18:03:59 -05:00
Tom Alexander
2e1c979127 Add a prefix to footnote IDs.
This avoids a conflict with multiple blog posts rendering in the same stream.
2023-12-19 18:01:54 -05:00
Tom Alexander
d4b290ebe6 Remove unnecessary let statements from render macro. 2023-12-19 17:31:50 -05:00
Tom Alexander
4bb1f9983a Standardize the construction of intermediate BlogPostPage. 2023-12-19 17:31:16 -05:00
Tom Alexander
261fe8a1a2 Fix the links to the blog posts. 2023-12-19 16:49:46 -05:00
Tom Alexander
add267d616 Switch over to using the render context in render calls. 2023-12-19 16:20:12 -05:00
Tom Alexander
cb3278aba5 Create a struct that will combine all the context for converting intermediate objects into the dust render context. 2023-12-19 14:54:12 -05:00
Tom Alexander
94d9a95967 Add a basic template for the blog stream page. 2023-12-19 14:13:29 -05:00
Tom Alexander
6511115b95 Implement a flawed version of RenderBlogStreamEntry::new.
There are some shortcomings in this implementation listed in the comments, but this is the general structure.
2023-12-19 10:59:34 -05:00
Tom Alexander
53cd55932b Implement RenderBlogStream::new.
I still need to implement RenderBlogStreamEntry::new.
2023-12-19 10:47:03 -05:00
Tom Alexander
cbe2010407 Invoke dust to render the stream pages.
At this point the render_blog_stream function is done, but RenderBlogStream::new needs to be implemented to actually generate the render context. The body of this function should be similar to convert_blog_post_page_to_render_context.
2023-12-17 17:26:15 -05:00
Tom Alexander
2ba4a5e3d7 Generate newer and older links. 2023-12-17 17:16:26 -05:00
Tom Alexander
c3482cf1e4 Chunking the blog posts for the stream. 2023-12-17 16:57:37 -05:00
Tom Alexander
fdf84e3d0b Finding the index page. 2023-12-17 15:45:50 -05:00
Tom Alexander
0a4376dfb8 Rename intermediate blog post source file. 2023-12-17 15:32:07 -05:00
Tom Alexander
e8ed4a4f4a Initial structure for rendering a blog post stream. 2023-12-17 15:32:07 -05:00
Tom Alexander
60555999db TEMP: Disable the css reset.
This is so I can develop the header without having any CSS written yet since the page is a mess without CSS.
2023-12-17 14:56:23 -05:00
Tom Alexander
6968a5b02c Merge branch 'header' 2023-12-17 14:47:45 -05:00
Tom Alexander
c84cfdc02b Basic template for a page header. 2023-12-17 14:45:42 -05:00
Tom Alexander
c98489cacb Add the page header to the render context. 2023-12-17 14:45:42 -05:00
Tom Alexander
35dbab0ceb Create a page header struct. 2023-12-17 14:45:42 -05:00
Tom Alexander
1ff41940a5 Merge branch 'css' 2023-12-17 14:02:20 -05:00
Tom Alexander
884215a7e1 Writing the stylesheets to the output folder. 2023-12-17 13:46:47 -05:00
Tom Alexander
20c55f0708 Loading stylesheets from the default environment. 2023-12-17 12:43:47 -05:00
Tom Alexander
9e3d72972c Add a CSS reset file. 2023-12-17 11:55:38 -05:00
Tom Alexander
806c45a453 Switch back to the published version of organic. 2023-12-17 11:54:59 -05:00
Tom Alexander
270c42a509 Update to use get_value from the latest organic code. 2023-10-31 22:03:22 -04:00
Tom Alexander
e8963e107b Merge branch 'table' 2023-10-31 20:38:36 -04:00
Tom Alexander
0b64551a23 Add templates for tables. 2023-10-31 20:31:36 -04:00
Tom Alexander
b654ca4859 Add render phase to tables. 2023-10-31 20:29:37 -04:00
Tom Alexander
386af57ce6 Add intermediate stage for tables. 2023-10-31 20:26:34 -04:00
Tom Alexander
ef4d315bf2 Run cargo fix. 2023-10-31 20:02:46 -04:00
Tom Alexander
2142b01967 Merge branch 'text_markup' 2023-10-31 20:02:13 -04:00
Tom Alexander
159d8fb72a Add render stage for text markup. 2023-10-31 20:02:04 -04:00
Tom Alexander
0fae417610 Add intermediate stage for text markup. 2023-10-31 19:57:04 -04:00
209 changed files with 7898 additions and 1958 deletions

3
.dockerignore Normal file
View File

@@ -0,0 +1,3 @@
**/.git
target/
org_test_documents/

View File

@@ -0,0 +1,224 @@
apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
name: build
spec:
timeouts:
pipeline: "2h0m0s"
tasks: "1h0m0s"
finally: "0h30m0s"
taskRunTemplate:
serviceAccountName: build-bot
pipelineSpec:
params:
- name: image-name
description: The name for the built image
type: string
- name: target-name
description: The dockerfile target to build
type: string
- name: path-to-image-context
description: The path to the build context
type: string
- name: path-to-dockerfile
description: The path to the Dockerfile
type: string
tasks:
- name: report-pending
taskRef:
resolver: git
params:
- name: url
value: https://code.fizz.buzz/mirror/catalog.git # mirror of https://github.com/tektoncd/catalog.git
- name: revision
value: df36b3853a5657fd883015cdbf07ad6466918acf
- name: pathInRepo
value: task/gitea-set-status/0.1/gitea-set-status.yaml
params:
- name: CONTEXT
value: "$(params.JOB_NAME)"
- name: REPO_FULL_NAME
value: "$(params.REPO_OWNER)/$(params.REPO_NAME)"
- name: GITEA_HOST_URL
value: code.fizz.buzz
- name: SHA
value: "$(tasks.fetch-repository.results.commit)"
- name: DESCRIPTION
value: "Build $(params.JOB_NAME) has started"
- name: STATE
value: pending
- name: TARGET_URL
value: "https://tekton.fizz.buzz/#/namespaces/$(context.pipelineRun.namespace)/pipelineruns/$(context.pipelineRun.name)"
- name: fetch-repository
taskRef:
resolver: git
params:
- name: url
value: https://code.fizz.buzz/mirror/catalog.git # mirror of https://github.com/tektoncd/catalog.git
- name: revision
value: df36b3853a5657fd883015cdbf07ad6466918acf
- name: pathInRepo
value: task/git-clone/0.9/git-clone.yaml
workspaces:
- name: output
workspace: git-source
params:
- name: url
value: $(params.REPO_URL)
- name: revision
value: $(params.PULL_BASE_SHA)
- name: deleteExisting
value: "true"
- name: get-git-commit-time
taskSpec:
metadata: {}
stepTemplate:
image: alpine:3.20
computeResources:
requests:
cpu: 10m
memory: 600Mi
workingDir: "$(workspaces.repo.path)"
results:
- name: unix-time
description: The time of the git commit in unix timestamp format.
steps:
- image: alpine/git:v2.34.2
name: detect-tag-step
script: |
#!/usr/bin/env sh
set -euo pipefail
echo -n "$(git log -1 --pretty=%ct)" | tee $(results.unix-time.path)
workspaces:
- name: repo
workspace: git-source
runAfter:
- fetch-repository
- name: build-image
taskRef:
resolver: git
params:
- name: url
value: https://code.fizz.buzz/talexander/personal_tekton_catalog.git
- name: revision
value: 7ee31a185243ee6da13dcd26a592c585b64c80e5
- name: pathInRepo
value: task/buildkit-rootless-daemonless/0.1/buildkit-rootless-daemonless.yaml
params:
- name: OUTPUT
value: >-
type=image,"name=$(params.image-name):latest,$(params.image-name):$(tasks.fetch-repository.results.commit)",push=true,compression=zstd,compression-level=22,oci-mediatypes=true
- name: CONTEXT
value: $(params.path-to-image-context)
- name: DOCKERFILE
value: $(params.path-to-dockerfile)
- name: EXTRA_ARGS
value:
- "--opt"
- "target=$(params.target-name)"
- --import-cache
- "type=registry,ref=$(params.image-name):buildcache"
- --export-cache
- "type=registry,ref=$(params.image-name):buildcache,mode=max,compression=zstd,compression-level=22,rewrite-timestamp=true,image-manifest=true,oci-mediatypes=true"
- --opt
- build-arg:SOURCE_DATE_EPOCH=$(tasks.get-git-commit-time.results.unix-time)
- name: BUILDKITD_TOML
value: |
debug = true
[registry."docker.io"]
mirrors = ["dockerhub.dockerhub.svc.cluster.local"]
[registry."dockerhub.dockerhub.svc.cluster.local"]
http = true
insecure = true
workspaces:
- name: source
workspace: git-source
- name: dockerconfig
workspace: docker-credentials
runAfter:
- fetch-repository
finally:
- name: report-success
when:
- input: "$(tasks.status)"
operator: in
values: ["Succeeded", "Completed"]
taskRef:
resolver: git
params:
- name: url
value: https://code.fizz.buzz/mirror/catalog.git # mirror of https://github.com/tektoncd/catalog.git
- name: revision
value: df36b3853a5657fd883015cdbf07ad6466918acf
- name: pathInRepo
value: task/gitea-set-status/0.1/gitea-set-status.yaml
params:
- name: CONTEXT
value: "$(params.JOB_NAME)"
- name: REPO_FULL_NAME
value: "$(params.REPO_OWNER)/$(params.REPO_NAME)"
- name: GITEA_HOST_URL
value: code.fizz.buzz
- name: SHA
value: "$(tasks.fetch-repository.results.commit)"
- name: DESCRIPTION
value: "Build $(params.JOB_NAME) has succeeded"
- name: STATE
value: success
- name: TARGET_URL
value: "https://tekton.fizz.buzz/#/namespaces/$(context.pipelineRun.namespace)/pipelineruns/$(context.pipelineRun.name)"
- name: report-failure
when:
- input: "$(tasks.status)"
operator: in
values: ["Failed"]
taskRef:
resolver: git
params:
- name: url
value: https://code.fizz.buzz/mirror/catalog.git # mirror of https://github.com/tektoncd/catalog.git
- name: revision
value: df36b3853a5657fd883015cdbf07ad6466918acf
- name: pathInRepo
value: task/gitea-set-status/0.1/gitea-set-status.yaml
params:
- name: CONTEXT
value: "$(params.JOB_NAME)"
- name: REPO_FULL_NAME
value: "$(params.REPO_OWNER)/$(params.REPO_NAME)"
- name: GITEA_HOST_URL
value: code.fizz.buzz
- name: SHA
value: "$(tasks.fetch-repository.results.commit)"
- name: DESCRIPTION
value: "Build $(params.JOB_NAME) has failed"
- name: STATE
value: failure
- name: TARGET_URL
value: "https://tekton.fizz.buzz/#/namespaces/$(context.pipelineRun.namespace)/pipelineruns/$(context.pipelineRun.name)"
workspaces:
- name: git-source
- name: docker-credentials
workspaces:
- name: git-source
volumeClaimTemplate:
spec:
storageClassName: "local-path"
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
subPath: rust-source
- name: docker-credentials
secret:
secretName: harbor-plain
params:
- name: image-name
value: "harbor.fizz.buzz/private/natter"
- name: target-name
value: ""
- name: path-to-image-context
value: .
- name: path-to-dockerfile
value: docker/natter/

View File

@@ -0,0 +1,369 @@
apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
name: rust-format
spec:
timeouts:
pipeline: "2h0m0s"
tasks: "1h0m0s"
finally: "0h30m0s"
taskRunTemplate:
serviceAccountName: build-bot
pipelineSpec:
params:
- name: image-name
description: The name for the built image
type: string
- name: target-name
description: The dockerfile target to build
type: string
- name: path-to-image-context
description: The path to the build context
type: string
- name: path-to-dockerfile
description: The path to the Dockerfile
type: string
tasks:
- name: report-pending
taskRef:
resolver: git
params:
- name: url
value: https://code.fizz.buzz/mirror/catalog.git # mirror of https://github.com/tektoncd/catalog.git
- name: revision
value: df36b3853a5657fd883015cdbf07ad6466918acf
- name: pathInRepo
value: task/gitea-set-status/0.1/gitea-set-status.yaml
params:
- name: CONTEXT
value: "$(params.JOB_NAME)"
- name: REPO_FULL_NAME
value: "$(params.REPO_OWNER)/$(params.REPO_NAME)"
- name: GITEA_HOST_URL
value: code.fizz.buzz
- name: SHA
value: "$(tasks.fetch-repository.results.commit)"
- name: DESCRIPTION
value: "Build $(params.JOB_NAME) has started"
- name: STATE
value: pending
- name: TARGET_URL
value: "https://tekton.fizz.buzz/#/namespaces/$(context.pipelineRun.namespace)/pipelineruns/$(context.pipelineRun.name)"
- name: fetch-repository
taskRef:
resolver: git
params:
- name: url
value: https://code.fizz.buzz/mirror/catalog.git # mirror of https://github.com/tektoncd/catalog.git
- name: revision
value: df36b3853a5657fd883015cdbf07ad6466918acf
- name: pathInRepo
value: task/git-clone/0.9/git-clone.yaml
workspaces:
- name: output
workspace: git-source
params:
- name: url
value: $(params.REPO_URL)
- name: revision
value: $(params.PULL_BASE_SHA)
- name: deleteExisting
value: "true"
- name: get-git-commit-time
taskSpec:
metadata: {}
stepTemplate:
image: alpine:3.20
computeResources:
requests:
cpu: 10m
memory: 600Mi
workingDir: "$(workspaces.repo.path)"
results:
- name: unix-time
description: The time of the git commit in unix timestamp format.
steps:
- image: alpine/git:v2.34.2
name: detect-tag-step
script: |
#!/usr/bin/env sh
set -euo pipefail
echo -n "$(git log -1 --pretty=%ct)" | tee $(results.unix-time.path)
workspaces:
- name: repo
workspace: git-source
runAfter:
- fetch-repository
- name: build-image
taskRef:
resolver: git
params:
- name: url
value: https://code.fizz.buzz/talexander/personal_tekton_catalog.git
- name: revision
value: 7ee31a185243ee6da13dcd26a592c585b64c80e5
- name: pathInRepo
value: task/buildkit-rootless-daemonless/0.1/buildkit-rootless-daemonless.yaml
params:
- name: OUTPUT
value: >-
type=image,"name=$(params.image-name):latest,$(params.image-name):$(tasks.fetch-repository.results.commit)",push=true,compression=zstd,compression-level=22,oci-mediatypes=true
- name: CONTEXT
value: $(params.path-to-image-context)
- name: DOCKERFILE
value: $(params.path-to-dockerfile)
- name: EXTRA_ARGS
value:
- "--opt"
- "target=$(params.target-name)"
- --import-cache
- "type=registry,ref=$(params.image-name):buildcache"
- --export-cache
- "type=registry,ref=$(params.image-name):buildcache,mode=max,compression=zstd,compression-level=22,rewrite-timestamp=true,image-manifest=true,oci-mediatypes=true"
- --opt
- build-arg:SOURCE_DATE_EPOCH=$(tasks.get-git-commit-time.results.unix-time)
- name: BUILDKITD_TOML
value: |
debug = true
[registry."docker.io"]
mirrors = ["dockerhub.dockerhub.svc.cluster.local"]
[registry."dockerhub.dockerhub.svc.cluster.local"]
http = true
insecure = true
workspaces:
- name: source
workspace: git-source
- name: dockerconfig
workspace: docker-credentials
runAfter:
- fetch-repository
- name: run-cargo-fmt
taskSpec:
metadata: {}
params:
- name: docker-image
type: string
description: Docker image to run.
default: alpine:3.20
stepTemplate:
image: alpine:3.20
computeResources:
requests:
cpu: 10m
memory: 600Mi
workingDir: /workspace/source
workspaces:
- name: source
mountPath: /source
- name: cargo-cache
mountPath: /usr/local/cargo/registry
optional: true
steps:
- name: run
image: $(params.docker-image)
workingDir: "$(workspaces.source.path)"
command: ["cargo", "fmt"]
args: []
env:
- name: CARGO_TARGET_DIR
value: /target
workspaces:
- name: source
workspace: git-source
- name: cargo-cache
workspace: cargo-cache
runAfter:
- build-image
params:
- name: docker-image
value: "$(tasks.build-image.results.IMAGE_URL[1])"
- name: run-prettier
taskSpec:
metadata: {}
params:
- name: docker-image
type: string
description: Docker image to run.
default: alpine:3.20
stepTemplate:
image: alpine:3.20
computeResources:
requests:
cpu: 10m
memory: 600Mi
workingDir: /workspace/source
workspaces:
- name: source
mountPath: /source
steps:
- name: run
image: $(params.docker-image)
workingDir: "$(workspaces.source.path)"
command: ["sh", "-c"]
args:
- |
prettier --write --no-error-on-unmatched-pattern "default_environment/**/*.js" "default_environment/**/*.css"
workspaces:
- name: source
workspace: git-source
runAfter:
- build-image
params:
- name: docker-image
value: "$(tasks.build-image.results.IMAGE_URL[1])"
- name: commit-changes
taskRef:
resolver: git
params:
- name: url
value: https://code.fizz.buzz/mirror/catalog.git # mirror of https://github.com/tektoncd/catalog.git
- name: revision
value: df36b3853a5657fd883015cdbf07ad6466918acf
- name: pathInRepo
value: task/git-cli/0.4/git-cli.yaml
params:
- name: GIT_USER_NAME
value: fluxcdbot
- name: GIT_USER_EMAIL
value: "fluxcdbot@users.noreply.github.com"
- name: GIT_SCRIPT
value: |
pwd
git config --global --add safe.directory /workspace/source
git_status=$(git status --porcelain)
if [ -n "$git_status" ]; then
git commit -a -m "CI: autofix rust code."
git push origin HEAD:$(params.PULL_BASE_REF)
else
echo "No changes to commit."
fi
workspaces:
- name: source
workspace: git-source
runAfter:
- run-cargo-fmt
- run-prettier
finally:
- name: report-success
when:
- input: "$(tasks.status)"
operator: in
values: ["Succeeded", "Completed"]
taskRef:
resolver: git
params:
- name: url
value: https://code.fizz.buzz/mirror/catalog.git # mirror of https://github.com/tektoncd/catalog.git
- name: revision
value: df36b3853a5657fd883015cdbf07ad6466918acf
- name: pathInRepo
value: task/gitea-set-status/0.1/gitea-set-status.yaml
params:
- name: CONTEXT
value: "$(params.JOB_NAME)"
- name: REPO_FULL_NAME
value: "$(params.REPO_OWNER)/$(params.REPO_NAME)"
- name: GITEA_HOST_URL
value: code.fizz.buzz
- name: SHA
value: "$(tasks.fetch-repository.results.commit)"
- name: DESCRIPTION
value: "Build $(params.JOB_NAME) has succeeded"
- name: STATE
value: success
- name: TARGET_URL
value: "https://tekton.fizz.buzz/#/namespaces/$(context.pipelineRun.namespace)/pipelineruns/$(context.pipelineRun.name)"
- name: report-failure
when:
- input: "$(tasks.status)"
operator: in
values: ["Failed"]
taskRef:
resolver: git
params:
- name: url
value: https://code.fizz.buzz/mirror/catalog.git # mirror of https://github.com/tektoncd/catalog.git
- name: revision
value: df36b3853a5657fd883015cdbf07ad6466918acf
- name: pathInRepo
value: task/gitea-set-status/0.1/gitea-set-status.yaml
params:
- name: CONTEXT
value: "$(params.JOB_NAME)"
- name: REPO_FULL_NAME
value: "$(params.REPO_OWNER)/$(params.REPO_NAME)"
- name: GITEA_HOST_URL
value: code.fizz.buzz
- name: SHA
value: "$(tasks.fetch-repository.results.commit)"
- name: DESCRIPTION
value: "Build $(params.JOB_NAME) has failed"
- name: STATE
value: failure
- name: TARGET_URL
value: "https://tekton.fizz.buzz/#/namespaces/$(context.pipelineRun.namespace)/pipelineruns/$(context.pipelineRun.name)"
- name: cargo-cache-autoclean
taskSpec:
metadata: {}
params:
- name: docker-image
type: string
description: Docker image to run.
default: alpine:3.20
stepTemplate:
image: alpine:3.20
computeResources:
requests:
cpu: 10m
memory: 600Mi
workingDir: /workspace/source
workspaces:
- name: source
mountPath: /source
- name: cargo-cache
mountPath: /usr/local/cargo/registry
optional: true
steps:
- name: run
image: $(params.docker-image)
workingDir: "$(workspaces.source.path)"
command: [cargo, cache, --autoclean]
args: []
workspaces:
- name: source
workspace: git-source
- name: cargo-cache
workspace: cargo-cache
params:
- name: docker-image
value: "$(tasks.build-image.results.IMAGE_URL[1])"
workspaces:
- name: git-source
- name: docker-credentials
- name: cargo-cache
workspaces:
- name: git-source
volumeClaimTemplate:
spec:
storageClassName: "local-path"
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
subPath: rust-source
- name: cargo-cache
persistentVolumeClaim:
claimName: natter-cargo-cache-fmt
- name: docker-credentials
secret:
secretName: harbor-plain
params:
- name: image-name
value: "harbor.fizz.buzz/private/natter-development-format"
- name: target-name
value: ""
- name: path-to-image-context
value: docker/natter_development/
- name: path-to-dockerfile
value: docker/natter_development/

View File

@@ -0,0 +1,313 @@
apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
name: rust-clippy
spec:
taskRunTemplate:
serviceAccountName: build-bot
timeouts:
pipeline: "2h0m0s"
tasks: "1h0m40s"
finally: "0h30m0s"
pipelineSpec:
params:
- name: image-name
description: The name for the built image
type: string
- name: target-name
description: The dockerfile target to build
type: string
- name: path-to-image-context
description: The path to the build context
type: string
- name: path-to-dockerfile
description: The path to the Dockerfile
type: string
tasks:
- name: report-pending
taskRef:
resolver: git
params:
- name: url
value: https://code.fizz.buzz/mirror/catalog.git # mirror of https://github.com/tektoncd/catalog.git
- name: revision
value: df36b3853a5657fd883015cdbf07ad6466918acf
- name: pathInRepo
value: task/gitea-set-status/0.1/gitea-set-status.yaml
params:
- name: CONTEXT
value: "$(params.JOB_NAME)"
- name: REPO_FULL_NAME
value: "$(params.REPO_OWNER)/$(params.REPO_NAME)"
- name: GITEA_HOST_URL
value: code.fizz.buzz
- name: SHA
value: "$(tasks.fetch-repository.results.commit)"
- name: DESCRIPTION
value: "Build $(params.JOB_NAME) has started"
- name: STATE
value: pending
- name: TARGET_URL
value: "https://tekton.fizz.buzz/#/namespaces/$(context.pipelineRun.namespace)/pipelineruns/$(context.pipelineRun.name)"
- name: fetch-repository
taskRef:
resolver: git
params:
- name: url
value: https://code.fizz.buzz/mirror/catalog.git # mirror of https://github.com/tektoncd/catalog.git
- name: revision
value: df36b3853a5657fd883015cdbf07ad6466918acf
- name: pathInRepo
value: task/git-clone/0.9/git-clone.yaml
workspaces:
- name: output
workspace: git-source
params:
- name: url
value: $(params.REPO_URL)
- name: revision
value: $(params.PULL_BASE_SHA)
- name: deleteExisting
value: "true"
- name: get-git-commit-time
taskSpec:
metadata: {}
stepTemplate:
image: alpine:3.20
computeResources:
requests:
cpu: 10m
memory: 600Mi
workingDir: "$(workspaces.repo.path)"
results:
- name: unix-time
description: The time of the git commit in unix timestamp format.
steps:
- image: alpine/git:v2.34.2
name: detect-tag-step
script: |
#!/usr/bin/env sh
set -euo pipefail
echo -n "$(git log -1 --pretty=%ct)" | tee $(results.unix-time.path)
workspaces:
- name: repo
workspace: git-source
runAfter:
- fetch-repository
- name: build-image
taskRef:
resolver: git
params:
- name: url
value: https://code.fizz.buzz/talexander/personal_tekton_catalog.git
- name: revision
value: 7ee31a185243ee6da13dcd26a592c585b64c80e5
- name: pathInRepo
value: task/buildkit-rootless-daemonless/0.1/buildkit-rootless-daemonless.yaml
params:
- name: OUTPUT
value: >-
type=image,"name=$(params.image-name):latest,$(params.image-name):$(tasks.fetch-repository.results.commit)",push=true,compression=zstd,compression-level=22,oci-mediatypes=true
- name: CONTEXT
value: $(params.path-to-image-context)
- name: DOCKERFILE
value: $(params.path-to-dockerfile)
- name: EXTRA_ARGS
value:
- "--opt"
- "target=$(params.target-name)"
- --import-cache
- "type=registry,ref=$(params.image-name):buildcache"
- --export-cache
- "type=registry,ref=$(params.image-name):buildcache,mode=max,compression=zstd,compression-level=22,rewrite-timestamp=true,image-manifest=true,oci-mediatypes=true"
- --opt
- build-arg:SOURCE_DATE_EPOCH=$(tasks.get-git-commit-time.results.unix-time)
- name: BUILDKITD_TOML
value: |
debug = true
[registry."docker.io"]
mirrors = ["dockerhub.dockerhub.svc.cluster.local"]
[registry."dockerhub.dockerhub.svc.cluster.local"]
http = true
insecure = true
workspaces:
- name: source
workspace: git-source
- name: dockerconfig
workspace: docker-credentials
runAfter:
- fetch-repository
- name: run-cargo-clippy
taskSpec:
metadata: {}
params:
- name: docker-image
type: string
description: Docker image to run.
default: alpine:3.20
stepTemplate:
image: alpine:3.20
computeResources:
requests:
cpu: 10m
memory: 600Mi
workingDir: /workspace/source
workspaces:
- name: source
mountPath: /source
- name: cargo-cache
mountPath: /usr/local/cargo/registry
optional: true
steps:
- name: run
image: $(params.docker-image)
workingDir: "$(workspaces.source.path)"
command:
[
"cargo",
"clippy",
"--no-deps",
"--all-targets",
"--all-features",
"--",
"-D",
"warnings",
]
args: []
env:
- name: CARGO_TARGET_DIR
value: /target
workspaces:
- name: source
workspace: git-source
- name: cargo-cache
workspace: cargo-cache
runAfter:
- build-image
params:
- name: docker-image
value: "$(tasks.build-image.results.IMAGE_URL[1])"
finally:
- name: report-success
when:
- input: "$(tasks.status)"
operator: in
values: ["Succeeded", "Completed"]
taskRef:
resolver: git
params:
- name: url
value: https://code.fizz.buzz/mirror/catalog.git # mirror of https://github.com/tektoncd/catalog.git
- name: revision
value: df36b3853a5657fd883015cdbf07ad6466918acf
- name: pathInRepo
value: task/gitea-set-status/0.1/gitea-set-status.yaml
params:
- name: CONTEXT
value: "$(params.JOB_NAME)"
- name: REPO_FULL_NAME
value: "$(params.REPO_OWNER)/$(params.REPO_NAME)"
- name: GITEA_HOST_URL
value: code.fizz.buzz
- name: SHA
value: "$(tasks.fetch-repository.results.commit)"
- name: DESCRIPTION
value: "Build $(params.JOB_NAME) has succeeded"
- name: STATE
value: success
- name: TARGET_URL
value: "https://tekton.fizz.buzz/#/namespaces/$(context.pipelineRun.namespace)/pipelineruns/$(context.pipelineRun.name)"
- name: report-failure
when:
- input: "$(tasks.status)"
operator: in
values: ["Failed"]
taskRef:
resolver: git
params:
- name: url
value: https://code.fizz.buzz/mirror/catalog.git # mirror of https://github.com/tektoncd/catalog.git
- name: revision
value: df36b3853a5657fd883015cdbf07ad6466918acf
- name: pathInRepo
value: task/gitea-set-status/0.1/gitea-set-status.yaml
params:
- name: CONTEXT
value: "$(params.JOB_NAME)"
- name: REPO_FULL_NAME
value: "$(params.REPO_OWNER)/$(params.REPO_NAME)"
- name: GITEA_HOST_URL
value: code.fizz.buzz
- name: SHA
value: "$(tasks.fetch-repository.results.commit)"
- name: DESCRIPTION
value: "Build $(params.JOB_NAME) has failed"
- name: STATE
value: failure
- name: TARGET_URL
value: "https://tekton.fizz.buzz/#/namespaces/$(context.pipelineRun.namespace)/pipelineruns/$(context.pipelineRun.name)"
- name: cargo-cache-autoclean
taskSpec:
metadata: {}
params:
- name: docker-image
type: string
description: Docker image to run.
default: alpine:3.20
stepTemplate:
image: alpine:3.20
computeResources:
requests:
cpu: 10m
memory: 600Mi
workingDir: /workspace/source
workspaces:
- name: source
mountPath: /source
- name: cargo-cache
mountPath: /usr/local/cargo/registry
optional: true
steps:
- name: run
image: $(params.docker-image)
workingDir: "$(workspaces.source.path)"
command: [cargo, cache, --autoclean]
args: []
workspaces:
- name: source
workspace: git-source
- name: cargo-cache
workspace: cargo-cache
params:
- name: docker-image
value: "$(tasks.build-image.results.IMAGE_URL[1])"
workspaces:
- name: git-source
- name: docker-credentials
- name: cargo-cache
workspaces:
- name: git-source
volumeClaimTemplate:
spec:
storageClassName: "local-path"
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
subPath: rust-source
- name: cargo-cache
persistentVolumeClaim:
claimName: natter-cargo-cache-clippy
- name: docker-credentials
secret:
secretName: harbor-plain
params:
- name: image-name
value: "harbor.fizz.buzz/private/natter-development-clippy"
- name: target-name
value: ""
- name: path-to-image-context
value: docker/natter_development/
- name: path-to-dockerfile
value: docker/natter_development/

View File

@@ -0,0 +1,303 @@
apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
name: rust-test
spec:
timeouts:
pipeline: "2h0m0s"
tasks: "1h0m40s"
finally: "0h30m0s"
taskRunTemplate:
serviceAccountName: build-bot
pipelineSpec:
params:
- name: image-name
description: The name for the built image
type: string
- name: target-name
description: The dockerfile target to build
type: string
- name: path-to-image-context
description: The path to the build context
type: string
- name: path-to-dockerfile
description: The path to the Dockerfile
type: string
tasks:
- name: report-pending
taskRef:
resolver: git
params:
- name: url
value: https://code.fizz.buzz/mirror/catalog.git # mirror of https://github.com/tektoncd/catalog.git
- name: revision
value: df36b3853a5657fd883015cdbf07ad6466918acf
- name: pathInRepo
value: task/gitea-set-status/0.1/gitea-set-status.yaml
params:
- name: CONTEXT
value: "$(params.JOB_NAME)"
- name: REPO_FULL_NAME
value: "$(params.REPO_OWNER)/$(params.REPO_NAME)"
- name: GITEA_HOST_URL
value: code.fizz.buzz
- name: SHA
value: "$(tasks.fetch-repository.results.commit)"
- name: DESCRIPTION
value: "Build $(params.JOB_NAME) has started"
- name: STATE
value: pending
- name: TARGET_URL
value: "https://tekton.fizz.buzz/#/namespaces/$(context.pipelineRun.namespace)/pipelineruns/$(context.pipelineRun.name)"
- name: fetch-repository
taskRef:
resolver: git
params:
- name: url
value: https://code.fizz.buzz/mirror/catalog.git # mirror of https://github.com/tektoncd/catalog.git
- name: revision
value: df36b3853a5657fd883015cdbf07ad6466918acf
- name: pathInRepo
value: task/git-clone/0.9/git-clone.yaml
workspaces:
- name: output
workspace: git-source
params:
- name: url
value: $(params.REPO_URL)
- name: revision
value: $(params.PULL_BASE_SHA)
- name: deleteExisting
value: "true"
- name: get-git-commit-time
taskSpec:
metadata: {}
stepTemplate:
image: alpine:3.20
computeResources:
requests:
cpu: 10m
memory: 600Mi
workingDir: "$(workspaces.repo.path)"
results:
- name: unix-time
description: The time of the git commit in unix timestamp format.
steps:
- image: alpine/git:v2.34.2
name: detect-tag-step
script: |
#!/usr/bin/env sh
set -euo pipefail
echo -n "$(git log -1 --pretty=%ct)" | tee $(results.unix-time.path)
workspaces:
- name: repo
workspace: git-source
runAfter:
- fetch-repository
- name: build-image
taskRef:
resolver: git
params:
- name: url
value: https://code.fizz.buzz/talexander/personal_tekton_catalog.git
- name: revision
value: 7ee31a185243ee6da13dcd26a592c585b64c80e5
- name: pathInRepo
value: task/buildkit-rootless-daemonless/0.1/buildkit-rootless-daemonless.yaml
params:
- name: OUTPUT
value: >-
type=image,"name=$(params.image-name):latest,$(params.image-name):$(tasks.fetch-repository.results.commit)",push=true,compression=zstd,compression-level=22,oci-mediatypes=true
- name: CONTEXT
value: $(params.path-to-image-context)
- name: DOCKERFILE
value: $(params.path-to-dockerfile)
- name: EXTRA_ARGS
value:
- "--opt"
- "target=$(params.target-name)"
- --import-cache
- "type=registry,ref=$(params.image-name):buildcache"
- --export-cache
- "type=registry,ref=$(params.image-name):buildcache,mode=max,compression=zstd,compression-level=22,rewrite-timestamp=true,image-manifest=true,oci-mediatypes=true"
- --opt
- build-arg:SOURCE_DATE_EPOCH=$(tasks.get-git-commit-time.results.unix-time)
- name: BUILDKITD_TOML
value: |
debug = true
[registry."docker.io"]
mirrors = ["dockerhub.dockerhub.svc.cluster.local"]
[registry."dockerhub.dockerhub.svc.cluster.local"]
http = true
insecure = true
workspaces:
- name: source
workspace: git-source
- name: dockerconfig
workspace: docker-credentials
runAfter:
- fetch-repository
- name: run-cargo-test
taskSpec:
metadata: {}
params:
- name: docker-image
type: string
description: Docker image to run.
default: alpine:3.20
stepTemplate:
image: alpine:3.20
computeResources:
requests:
cpu: 10m
memory: 600Mi
workingDir: /workspace/source
workspaces:
- name: source
mountPath: /source
- name: cargo-cache
mountPath: /usr/local/cargo/registry
optional: true
steps:
- name: run
image: $(params.docker-image)
workingDir: "$(workspaces.source.path)"
command: [cargo, test, --no-fail-fast]
args: []
env:
- name: CARGO_TARGET_DIR
value: /target
workspaces:
- name: source
workspace: git-source
- name: cargo-cache
workspace: cargo-cache
runAfter:
- build-image
params:
- name: docker-image
value: "$(tasks.build-image.results.IMAGE_URL[1])"
finally:
- name: report-success
when:
- input: "$(tasks.status)"
operator: in
values: ["Succeeded", "Completed"]
taskRef:
resolver: git
params:
- name: url
value: https://code.fizz.buzz/mirror/catalog.git # mirror of https://github.com/tektoncd/catalog.git
- name: revision
value: df36b3853a5657fd883015cdbf07ad6466918acf
- name: pathInRepo
value: task/gitea-set-status/0.1/gitea-set-status.yaml
params:
- name: CONTEXT
value: "$(params.JOB_NAME)"
- name: REPO_FULL_NAME
value: "$(params.REPO_OWNER)/$(params.REPO_NAME)"
- name: GITEA_HOST_URL
value: code.fizz.buzz
- name: SHA
value: "$(tasks.fetch-repository.results.commit)"
- name: DESCRIPTION
value: "Build $(params.JOB_NAME) has succeeded"
- name: STATE
value: success
- name: TARGET_URL
value: "https://tekton.fizz.buzz/#/namespaces/$(context.pipelineRun.namespace)/pipelineruns/$(context.pipelineRun.name)"
- name: report-failure
when:
- input: "$(tasks.status)"
operator: in
values: ["Failed"]
taskRef:
resolver: git
params:
- name: url
value: https://code.fizz.buzz/mirror/catalog.git # mirror of https://github.com/tektoncd/catalog.git
- name: revision
value: df36b3853a5657fd883015cdbf07ad6466918acf
- name: pathInRepo
value: task/gitea-set-status/0.1/gitea-set-status.yaml
params:
- name: CONTEXT
value: "$(params.JOB_NAME)"
- name: REPO_FULL_NAME
value: "$(params.REPO_OWNER)/$(params.REPO_NAME)"
- name: GITEA_HOST_URL
value: code.fizz.buzz
- name: SHA
value: "$(tasks.fetch-repository.results.commit)"
- name: DESCRIPTION
value: "Build $(params.JOB_NAME) has failed"
- name: STATE
value: failure
- name: TARGET_URL
value: "https://tekton.fizz.buzz/#/namespaces/$(context.pipelineRun.namespace)/pipelineruns/$(context.pipelineRun.name)"
- name: cargo-cache-autoclean
taskSpec:
metadata: {}
params:
- name: docker-image
type: string
description: Docker image to run.
default: alpine:3.20
stepTemplate:
image: alpine:3.20
computeResources:
requests:
cpu: 10m
memory: 600Mi
workingDir: /workspace/source
workspaces:
- name: source
mountPath: /source
- name: cargo-cache
mountPath: /usr/local/cargo/registry
optional: true
steps:
- name: run
image: $(params.docker-image)
workingDir: "$(workspaces.source.path)"
command: [cargo, cache, --autoclean]
args: []
workspaces:
- name: source
workspace: git-source
- name: cargo-cache
workspace: cargo-cache
params:
- name: docker-image
value: "$(tasks.build-image.results.IMAGE_URL[1])"
workspaces:
- name: git-source
- name: docker-credentials
- name: cargo-cache
workspaces:
- name: git-source
volumeClaimTemplate:
spec:
storageClassName: "local-path"
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
subPath: rust-source
- name: cargo-cache
persistentVolumeClaim:
claimName: natter-cargo-cache-test
- name: docker-credentials
secret:
secretName: harbor-plain
params:
- name: image-name
value: "harbor.fizz.buzz/private/natter-development-test"
- name: target-name
value: ""
- name: path-to-image-context
value: docker/natter_development/
- name: path-to-dockerfile
value: docker/natter_development/

View File

@@ -0,0 +1,25 @@
version = "0.0.1"
[[push]]
name = "rust-test"
source = "pipeline-rust-test.yaml"
clone_uri = "git@code.fizz.buzz:talexander/natter.git"
skip_branches = [ "^v[0-9]+\\.[0-9]+\\.[0-9]+$" ]
[[push]]
name = "clippy"
source = "pipeline-rust-clippy.yaml"
clone_uri = "git@code.fizz.buzz:talexander/natter.git"
skip_branches = [ "^v[0-9]+\\.[0-9]+\\.[0-9]+$" ]
[[push]]
name = "format"
source = "pipeline-format.yaml"
clone_uri = "git@code.fizz.buzz:talexander/natter.git"
skip_branches = [ "^v[0-9]+\\.[0-9]+\\.[0-9]+$" ]
[[push]]
name = "build"
source = "pipeline-build-hash.yaml"
clone_uri = "git@code.fizz.buzz:talexander/natter.git"
branches = [ "^main$", "^master$" ]

1464
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,21 +1,54 @@
[package] [package]
name = "writer" name = "natter"
version = "0.0.1" version = "0.0.1"
edition = "2021" edition = "2024"
authors = ["Tom Alexander <tom@fizz.buzz>"]
description = "A static site generator using org source files."
license = "0BSD"
repository = "https://code.fizz.buzz/talexander/natter"
readme = "README.md"
keywords = ["static", "site", "generator"]
categories = ["command-line-utilities"]
resolver = "2"
include = [
"LICENSE",
"**/*.rs",
"Cargo.toml",
"Cargo.lock",
"default_environment/"
]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
clap = { version = "4.4.6", default-features = false, features = ["std", "color", "help", "derive"] } clap = { version = "4.4.6", default-features = false, features = ["std", "color", "help", "derive"] }
duster = { git = "https://code.fizz.buzz/talexander/duster.git", branch = "master" } duster = "0.1.1"
tree-sitter-highlight = { git = "https://github.com/tree-sitter/tree-sitter.git", rev = "52f7eaff3182a726eb064a91d4e49dfbaecd4ee3" }
futures = "0.3.29" futures = "0.3.29"
include_dir = "0.7.3" include_dir = "0.7.3"
# TODO: This is temporary to work on the latest organic code. Eventually switch back to using the published crate. # TODO: This is temporary to work on the latest organic code. Eventually switch back to using the published crate.
organic = { path = "../organic" } # organic = { path = "../organic" }
# organic = "0.1.12" organic = "0.1.16"
serde = { version = "1.0.189", default-features = false, features = ["std", "derive"] } serde = { version = "1.0.189", default-features = false, features = ["std", "derive"] }
serde_json = "1.0.107" serde_json = "1.0.107"
tokio = { version = "1.30.0", default-features = false, features = ["rt", "rt-multi-thread", "fs", "io-util"] } tokio = { version = "1.30.0", default-features = false, features = ["rt", "rt-multi-thread", "fs", "io-util"] }
toml = "0.8.2" toml = "0.8.2"
walkdir = "2.4.0" tree-sitter-bash = "0.23.3"
tree-sitter-highlight = "0.25.2"
tree-sitter-nix = "0.0.2"
tree-sitter-python = "0.23.6"
url = "2.5.0"
tracing = { version = "0.1.37", optional = true }
tracing-opentelemetry = { version = "0.20.0", optional = true }
tracing-subscriber = { version = "0.3.17", optional = true, features = ["env-filter"] }
opentelemetry = { version = "0.20.0", optional = true, default-features = false, features = ["trace", "rt-tokio"] }
opentelemetry-otlp = { version = "0.13.0", optional = true }
opentelemetry-semantic-conventions = { version = "0.12.0", optional = true }
[features]
default = ["tracing"]
tracing = ["dep:opentelemetry", "dep:opentelemetry-otlp", "dep:opentelemetry-semantic-conventions", "dep:tracing", "dep:tracing-opentelemetry", "dep:tracing-subscriber"]
# Optimized build for any sort of release.
[profile.release-lto]
inherits = "release"
lto = true
strip = "symbols"

39
Makefile Normal file
View File

@@ -0,0 +1,39 @@
SHELL := bash
.ONESHELL:
.SHELLFLAGS := -eu -o pipefail -c
.DELETE_ON_ERROR:
MAKEFLAGS += --warn-undefined-variables
MAKEFLAGS += --no-builtin-rules
OS:=$(shell uname -s)
ifeq ($(origin .RECIPEPREFIX), undefined)
$(error This Make does not support .RECIPEPREFIX. Please use GNU Make 4.0 or later)
endif
.RECIPEPREFIX = >
IMAGE_NAME:=natter
TARGET :=
.PHONY: help
help:
> @grep -h "##" $(MAKEFILE_LIST) | grep -v grep | sed -E 's/^([^:]*): *## */\1: /'
.PHONY: docker_test
docker_test: ## Run the rust tests
> $(MAKE) -C docker/natter_development build
> docker run --rm -i -t --mount type=tmpfs,destination=/tmp -v "$(shell readlink -f .):/source" --workdir=/source --env CARGO_TARGET_DIR=/target -v "natter-cargo-registry:/usr/local/cargo/registry" natter-development cargo test
.PHONY: docker_clippy
docker_clippy: ## Run static analysis of the code.
> $(MAKE) -C docker/natter_development build
> docker run --rm -i -t --mount type=tmpfs,destination=/tmp -v "$(shell readlink -f .):/source" --workdir=/source --env CARGO_TARGET_DIR=/target -v "natter-cargo-registry:/usr/local/cargo/registry" natter-development cargo clippy --no-deps --all-targets --all-features -- -D warnings
.PHONY: docker_format
docker_format: ## Auto-format source files.
> $(MAKE) -C docker/natter_development build
> docker run --rm -i -t --mount type=tmpfs,destination=/tmp -v "$(shell readlink -f .):/source" --workdir=/source --env CARGO_TARGET_DIR=/target -v "natter-cargo-registry:/usr/local/cargo/registry" natter-development cargo fmt
> docker run --rm -i -t --mount type=tmpfs,destination=/tmp -v "$(shell readlink -f .):/source" --workdir=/source --env CARGO_TARGET_DIR=/target -v "natter-cargo-registry:/usr/local/cargo/registry" natter-development prettier --write --no-error-on-unmatched-pattern "default_environment/**/*.js" "default_environment/**/*.css"
.PHONY: clean
clean:
> $(MAKE) -C docker/natter_development clean

3
README.md Normal file
View File

@@ -0,0 +1,3 @@
# Natter
Natter is a static site generator for blogs using org as an input format. It is at a very early stage, and will have deep sweeping changes.

21
TODO.org Normal file
View File

@@ -0,0 +1,21 @@
* Things to do [6/17]
** DONE If the paragraph only contains an image, text-align center
** DONE Syntax highlighting for code blocks
** TODO Render gnuplot
** TODO Pretty-print the timestamps
** TODO Support Table of Contents
** TODO Support line numbers in code blocks
** TODO Support references to code block lines
** TODO Only include text up to first heading on homepage and include a "read more" link
** DONE Make loading language-specific CSS files conditional on the presence of src blocks using those languages
** DONE Set up tracing so I can use warning and such
** TODO Make copying of language-specific CSS files conditional on the presence of src blocks using those languages
** TODO Switch to an entirely lazily-evaluated output tree
** TODO Add highlighting for languages [1/2]
*** DONE bash
*** TODO gnuplot
https://github.com/dpezto/tree-sitter-gnuplot is not on crates.io so I'd have to add a git dependency to use it. This would prevent publishing this crate to crates.io.
** DONE Bug: carry over highlight starts when breaking lines
** TODO Add dates to posts
** DONE Add support for unlisted posts (posts that do not show up on the homepage).
** TODO Add support for showing file name where we currently show language

View File

@@ -0,0 +1,39 @@
:root {
--srclg-bash-srchl-comment-color: #048a81;
--srclg-bash-srchl-function-color: #e95a62;
--srclg-bash-srchl-keyword-color: #1a936f;
--srclg-bash-srchl-property-color: inherit;
--srclg-bash-srchl-string-color: #ecc30b;
}
@media (prefers-color-scheme: light) {
:root {
--srclg-bash-srchl-comment-color: #fb757e;
--srclg-bash-srchl-function-color: #16a59d;
--srclg-bash-srchl-keyword-color: #e56c90;
--srclg-bash-srchl-property-color: inherit;
--srclg-bash-srchl-string-color: #133cf4;
}
}
.main_content {
.src_block {
&.srclg_bash {
.srchl_comment {
color: var(--srclg-bash-srchl-comment-color);
}
.srchl_function {
color: var(--srclg-bash-srchl-function-color);
}
.srchl_keyword {
color: var(--srclg-bash-srchl-keyword-color);
}
.srchl_property {
color: var(--srclg-bash-srchl-property-color);
}
.srchl_string {
color: var(--srclg-bash-srchl-string-color);
}
}
}
}

View File

@@ -0,0 +1,39 @@
:root {
--srclg-nix-srchl-keyword-color: #1a936f;
--srclg-nix-srchl-comment-color: #048a81;
--srclg-nix-srchl-property-color: #bfbccb;
--srclg-nix-srchl-string-color: #ecc30b;
--srclg-nix-srchl-string-special-path-color: #067bc2;
}
@media (prefers-color-scheme: light) {
:root {
--srclg-nix-srchl-keyword-color: #e56c90;
--srclg-nix-srchl-comment-color: #fb757e;
--srclg-nix-srchl-property-color: #404334;
--srclg-nix-srchl-string-color: #133cf4;
--srclg-nix-srchl-string-special-path-color: #f9843d;
}
}
.main_content {
.src_block {
&.srclg_nix {
.srchl_keyword {
color: var(--srclg-nix-srchl-keyword-color);
}
.srchl_comment {
color: var(--srclg-nix-srchl-comment-color);
}
.srchl_property {
color: var(--srclg-nix-srchl-property-color);
}
.srchl_string {
color: var(--srclg-nix-srchl-string-color);
}
.srchl_string_special_path {
color: var(--srclg-nix-srchl-string-special-path-color);
}
}
}
}

View File

@@ -0,0 +1,51 @@
/* ea912c */
/* e95a62 */
:root {
--srclg-python-srchl-comment-color: #048a81;
--srclg-python-srchl-function-builtin-color: #e95a62;
--srclg-python-srchl-keyword-color: #1a936f;
--srclg-python-srchl-property-color: inherit;
--srclg-python-srchl-string-color: #ecc30b;
--srclg-python-srchl-type-color: #067bc2;
--srclg-python-srchl-variable-color: #ea912c;
}
@media (prefers-color-scheme: light) {
:root {
--srclg-python-srchl-comment-color: #fb757e;
--srclg-python-srchl-function-builtin-color: #16a59d;
--srclg-python-srchl-keyword-color: #e56c90;
--srclg-python-srchl-property-color: inherit;
--srclg-python-srchl-string-color: #133cf4;
--srclg-python-srchl-type-color: #f9843d;
--srclg-python-srchl-variable-color: #156ed3;
}
}
.main_content {
.src_block {
&.srclg_python {
.srchl_comment {
color: var(--srclg-python-srchl-comment-color);
}
.srchl_function_builtin {
color: var(--srclg-python-srchl-function-builtin-color);
}
.srchl_keyword {
color: var(--srclg-python-srchl-keyword-color);
}
.srchl_property {
color: var(--srclg-python-srchl-property-color);
}
.srchl_string {
color: var(--srclg-python-srchl-string-color);
}
.srchl_type {
color: var(--srclg-python-srchl-type-color);
}
.srchl_variable {
color: var(--srclg-python-srchl-variable-color);
}
}
}
}

View File

@@ -0,0 +1,358 @@
:root {
--main-max-width: 800px;
--site-background-color: #0a0a0a;
--site-text-color: #fffffc;
--header-divider-color: #6a687a;
--stream-divider-color: #6ccff6;
--stream-post-background-color: #0a0a0a;
--blog-post-background-color: #0a0a0a;
--src-font-family: ui-monospace, "Cascadia Code", "Source Code Pro", Menlo,
Consolas, "DejaVu Sans Mono", monospace;
--src-block-background-color: #141414;
--src-block-border-color: #84828f;
--src-block-language-color: #0a0a0a;
--src-block-language-background: #84828f;
--quote-block-border-color: #84828f;
--table-border-color: #6a687a;
--table-odd-background-color: #0a0a0a;
--table-even-background-color: #141414;
--header-nav-regular-font-color: var(--site-text-color);
--header-nav-regular-background-color: var(--site-background-color);
--header-nav-hover-font-color: var(--site-background-color);
--header-nav-hover-background-color: var(--site-text-color);
--header-home-regular-font-color: var(--site-text-color);
--header-home-hover-font-color: #6ccff6;
}
@media (prefers-color-scheme: light) {
:root {
--site-background-color: #f5f5f5;
--site-text-color: #000003;
--header-divider-color: #959785;
--stream-divider-color: #933009;
--stream-post-background-color: #f5f5f5;
--blog-post-background-color: #f5f5f5;
--src-block-background-color: #ebebeb;
--src-block-border-color: #7b7d70;
--src-block-language-color: #f5f5f5;
--src-block-language-background: #7b7d70;
--quote-block-border-color: #7b7d70;
--table-border-color: #959785;
--table-odd-background-color: #f5f5f5;
--table-even-background-color: #ebebeb;
--header-nav-regular-font-color: var(--site-text-color);
--header-nav-regular-background-color: var(--site-background-color);
--header-nav-hover-font-color: var(--site-background-color);
--header-nav-hover-background-color: var(--site-text-color);
--header-home-regular-font-color: var(--site-text-color);
--header-home-hover-font-color: #933009;
}
}
* {
box-sizing: border-box;
}
body {
color: var(--site-text-color);
background-color: var(--site-background-color);
font-family: source-sans-pro, Seravek, "Gill Sans Nova", Ubuntu, Calibri,
"DejaVu Sans", sans-serif;
a:link,
a:visited {
/* TODO: Should I use a different color for links? */
color: var(--site-text-color);
}
}
.page_centering {
display: flex;
flex-direction: column;
align-items: center;
}
.page_header {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: stretch;
width: 100%;
max-width: var(--main-max-width);
border-bottom: 0.1rem solid var(--header-divider-color);
.home_link {
display: block;
font-size: 2rem;
font-weight: 600;
text-decoration: none;
color: var(--header-home-regular-font-color);
transition-property: color;
transition-duration: 0.1s;
transition-timing-function: ease-out;
&:hover {
color: var(--header-home-hover-font-color) !important;
}
&:link,
&:visited {
color: inherit;
}
}
.header_nav_bar {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: end;
align-items: stretch;
column-gap: 1rem;
.nav_link {
display: flex;
flex-direction: column;
justify-content: space-around;
color: var(--header-nav-regular-font-color);
background: var(--header-nav-regular-background-color);
padding: 0 0.5rem;
transition-property: background, color;
transition-duration: 0.1s;
transition-timing-function: ease-out;
&:hover {
color: var(--header-nav-hover-font-color);
background: var(--header-nav-hover-background-color);
}
}
}
}
.main_content {
width: 100%;
max-width: var(--main-max-width);
font-size: 1.2rem;
line-height: 1.4;
padding-bottom: 8rem;
/* A stand-alone blog post (not in a blog stream). */
.blog_post {
padding: 1rem 0.2rem 0 0.2rem;
background: var(--blog-post-background-color);
}
.blog_stream {
.stream_divider {
color: var(--stream-divider-color);
margin: 40px 0;
}
.stream_nav {
display: flex;
flex-direction: row;
align-items: center;
> a {
display: inline-block;
padding: 0.2rem 0.5rem;
font-weight: 700;
font-size: 1.5rem;
}
> .spacer {
display: inline-block;
flex: 1 1;
}
}
}
/* A blog post in a blog stream (for example, the homepage). */
.blog_stream_post {
background: var(--stream-post-background-color);
padding: 0 0.2rem;
}
.blog_stream_post {
background: var(--stream-post-background-color);
}
.blog_stream_post:nth-child(1) {
padding-top: 1rem;
}
.blog_post_title {
font-size: 2.9rem;
font-weight: 700;
padding-bottom: 1rem;
}
p {
margin: 1rem 0;
&.image {
text-align: center;
}
}
.src_block {
background: var(--src-block-background-color);
border-radius: 3px;
border: 1px solid var(--src-block-border-color);
font-size: 1rem;
font-family: var(--src-font-family);
margin: 1rem 0;
font-variant-ligatures: none;
.src_language {
display: inline-block;
color: var(--src-block-language-color);
background: var(--src-block-language-background);
border-radius: 0 0 3px 0;
padding: 0.1rem 0.5rem;
font-size: 0.8rem;
vertical-align: top;
}
.src_body {
margin: 0.5rem;
.src_line {
white-space: pre-wrap;
overflow-wrap: anywhere;
}
}
}
.inline_source_block {
font-family: var(--src-font-family);
font-size: 1.2rem;
font-variant-ligatures: none;
}
.code,
.verbatim {
font-family: var(--src-font-family);
font-variant-ligatures: none;
}
.quote_block {
border-left: 1px solid var(--quote-block-border-color);
padding: 0 0 0 1rem;
margin: 1rem 0 1rem 2rem;
}
h2,
h3 {
margin: 1rem 0;
padding-bottom: 0.5rem;
}
h2 {
font-size: 2.3rem;
font-weight: 600;
}
h3 {
font-size: 2.1rem;
font-weight: 600;
}
.plain_list {
&.unordered {
list-style-type: disc;
padding-left: 2.5rem;
}
&.ordered {
list-style-type: decimal;
padding-left: 2.5rem;
}
&.descriptive {
font-size: 1rem;
> dt {
font-weight: 600;
}
> dd {
padding-left: 2.5rem;
}
}
}
.footnote_reference {
vertical-align: super;
font-size: 80%;
> a {
text-decoration: none;
}
}
.footnote_definition {
.label {
text-decoration: none;
}
.definition {
display: inline;
> p {
display: inline;
}
}
}
.org_table {
table-layout: fixed;
border-collapse: collapse;
border: 1px solid var(--table-border-color);
> tbody {
border-width: 1px 0;
border-style: solid;
border-color: var(--table-border-color);
> tr {
> td {
padding: 0.2rem;
}
}
> tr:nth-child(odd) {
background-color: var(--table-odd-background-color);
}
> tr:nth-child(even) {
background-color: var(--table-even-background-color);
}
}
> thead {
border-width: 1px 0;
border-style: solid;
border-color: var(--table-border-color);
> tr {
> th {
padding: 0.2rem;
font-weight: 600;
}
}
}
}
b {
font-weight: 700;
}
/* Never have media larger than its container */
img,
picture,
video {
max-width: 100%;
}
}

View File

@@ -0,0 +1,129 @@
/* http://meyerweb.com/eric/tools/css/reset/
v2.0 | 20110126
License: none (public domain)
*/
html,
body,
div,
span,
applet,
object,
iframe,
h1,
h2,
h3,
h4,
h5,
h6,
p,
blockquote,
pre,
a,
abbr,
acronym,
address,
big,
cite,
code,
del,
dfn,
em,
img,
ins,
kbd,
q,
s,
samp,
small,
strike,
strong,
sub,
sup,
tt,
var,
b,
u,
i,
center,
dl,
dt,
dd,
ol,
ul,
li,
fieldset,
form,
label,
legend,
table,
caption,
tbody,
tfoot,
thead,
tr,
th,
td,
article,
aside,
canvas,
details,
embed,
figure,
figcaption,
footer,
header,
hgroup,
menu,
nav,
output,
ruby,
section,
summary,
time,
mark,
audio,
video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
menu,
nav,
section {
display: block;
}
body {
line-height: 1;
}
ol,
ul {
list-style: none;
}
blockquote,
q {
quotes: none;
}
blockquote:before,
blockquote:after,
q:before,
q:after {
content: "";
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}

View File

@@ -32,7 +32,8 @@
{@eq value="code"}{>code/}{/eq} {@eq value="code"}{>code/}{/eq}
{@eq value="verbatim"}{>verbatim/}{/eq} {@eq value="verbatim"}{>verbatim/}{/eq}
{@eq value="plain_text"}{>plain_text/}{/eq} {@eq value="plain_text"}{>plain_text/}{/eq}
{@eq value="regular_link"}{>regular_link/}{/eq} {@eq value="regular_link_anchor"}{>regular_link_anchor/}{/eq}
{@eq value="regular_link_image"}{>regular_link_image/}{/eq}
{@eq value="radio_link"}{>radio_link/}{/eq} {@eq value="radio_link"}{>radio_link/}{/eq}
{@eq value="radio_target"}{>radio_target/}{/eq} {@eq value="radio_target"}{>radio_target/}{/eq}
{@eq value="plain_link"}{>plain_link/}{/eq} {@eq value="plain_link"}{>plain_link/}{/eq}

View File

@@ -1,8 +1,6 @@
<div class="blog_post"> <article class="blog_post">
<div class="blog_post_intro"> {?.title}<h1 class="blog_post_title"><span>{.title}</span></h1>{/.title}
{?.title}{?.self_link}<a class="blog_post_title" href="{.link}">{.title}</a>{:else}<div class="blog_post_title">{.title}</div>{/.self_link}{/.title}
{! TODO: date? !} {! TODO: date? !}
</div>
{! TODO: Table of contents? !} {! TODO: Table of contents? !}
@@ -18,4 +16,4 @@
{/.footnotes} {/.footnotes}
{/.footnotes} {/.footnotes}
</div> </div>
</div> </article>

View File

@@ -0,0 +1,34 @@
<div class="blog_stream">
{#.children}
{@gt key=$idx value=0}<hr class="stream_divider" />{/gt}
<div class="blog_stream_post">
<div class="blog_post_intro">
{?.title}{?.self_link}<a class="blog_post_title" href="{.self_link}">{.title}</a>{:else}<div class="blog_post_title">{.title}</div>{/.self_link}{/.title}
{! TODO: date? !}
</div>
{! TODO: Table of contents? !}
<div class="blog_post_body">
{#.children}
{>document_element/}
{/.children}
{?.footnotes}
<h2>Footnotes:</h2>
{#.footnotes}
{>real_footnote_definition/}
{/.footnotes}
{/.footnotes}
</div>
</div>
{/.children}
{#.stream_pagination}
<hr class="stream_divider" />
<div class="stream_nav">
{?.older_link}<a href="{.older_link}">Older</a>{/.older_link}
<div class="spacer"></div>
{?.newer_link}<a href="{.newer_link}">Newer</a>{/.newer_link}
</div>
{/.stream_pagination}
</div>

View File

@@ -1 +1 @@
<code>{.source}</code> <code class="code">{.contents}</code>

View File

@@ -1,6 +1,7 @@
{@select key=.type} {@select key=.type}
{@eq value="paragraph"}{>paragraph/}{/eq} {@eq value="paragraph"}{>paragraph/}{/eq}
{@eq value="plain_list"}{>plain_list/}{/eq} {@eq value="plain_list"}{>plain_list/}{/eq}
{@eq value="plain_list_simple_item"}{>plain_list_simple_item/}{/eq}
{@eq value="center_block"}{>center_block/}{/eq} {@eq value="center_block"}{>center_block/}{/eq}
{@eq value="quote_block"}{>quote_block/}{/eq} {@eq value="quote_block"}{>quote_block/}{/eq}
{@eq value="special_block"}{>special_block/}{/eq} {@eq value="special_block"}{>special_block/}{/eq}

View File

@@ -1 +1 @@
<sup><a id="{.reference_id}" href="{.definition_link}">{.label}</a></sup> <sup class="footnote_reference"><a id="{.reference_id}" href="{.definition_link}">{.label}</a></sup>

View File

@@ -1 +1 @@
<code>{.value}</code> <code class="inline_source_block">{.value}</code>

View File

@@ -1,18 +1,21 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
{#global_settings.css_files}<link rel="stylesheet" href="{.}">{/global_settings.css_files} {#global_settings.css_files}<link rel="stylesheet" href="{.}">{/global_settings.css_files}
{#global_settings.js_files}<script type="text/javascript" src="{.}"></script>{/global_settings.js_files} {#global_settings.js_files}<script type="text/javascript" src="{.}"></script>{/global_settings.js_files}
{?global_settings.page_title}<title>{global_settings.page_title}</title>{/global_settings.page_title} {?global_settings.page_title}<title>{global_settings.page_title}</title>{/global_settings.page_title}
</head> </head>
<body> <body class="page_centering">
{! TODO: Header bar with links? !} {#.page_header}{>page_header/}{/.page_header}
<div class="main_content"> <main class="main_content">
{@select key=.type} {@select key=.type}
{@eq value="page"}{>page/}{/eq}
{@eq value="blog_post_page"}{>blog_post_page/}{/eq} {@eq value="blog_post_page"}{>blog_post_page/}{/eq}
{@eq value="blog_stream"}{>blog_stream/}{/eq}
{@none}{!TODO: make this panic!}ERROR: Unrecognized page content type{/none} {@none}{!TODO: make this panic!}ERROR: Unrecognized page content type{/none}
{/select} {/select}
</div> </main>
</body> </body>
</html> </html>

View File

@@ -6,7 +6,8 @@
{@eq value="code"}{>code/}{/eq} {@eq value="code"}{>code/}{/eq}
{@eq value="verbatim"}{>verbatim/}{/eq} {@eq value="verbatim"}{>verbatim/}{/eq}
{@eq value="plain_text"}{>plain_text/}{/eq} {@eq value="plain_text"}{>plain_text/}{/eq}
{@eq value="regular_link"}{>regular_link/}{/eq} {@eq value="regular_link_anchor"}{>regular_link_anchor/}{/eq}
{@eq value="regular_link_image"}{>regular_link_image/}{/eq}
{@eq value="radio_link"}{>radio_link/}{/eq} {@eq value="radio_link"}{>radio_link/}{/eq}
{@eq value="radio_target"}{>radio_target/}{/eq} {@eq value="radio_target"}{>radio_target/}{/eq}
{@eq value="plain_link"}{>plain_link/}{/eq} {@eq value="plain_link"}{>plain_link/}{/eq}
@@ -27,5 +28,4 @@
{@eq value="superscript"}{>superscript/}{/eq} {@eq value="superscript"}{>superscript/}{/eq}
{@eq value="timestamp"}{>timestamp/}{/eq} {@eq value="timestamp"}{>timestamp/}{/eq}
{@none}{!TODO: make this panic!}ERROR: Unrecognized type {.type}.{/none} {@none}{!TODO: make this panic!}ERROR: Unrecognized type {.type}.{/none}
{/select}{~s} {/select}{@gt key=.post_blank value=0}{~s}{/gt}
{! TODO: Maybe the final space should be conditional on end blank in the org source !}

View File

@@ -0,0 +1,19 @@
<article class="page">
{?.title}<h1 class="blog_post_title"><span>{.title}</span></h1>{/.title}
{! TODO: date? !}
{! TODO: Table of contents? !}
<div class="blog_post_body">
{#.children}
{>document_element/}
{/.children}
{?.footnotes}
<h2>Footnotes:</h2>
{#.footnotes}
{>real_footnote_definition/}
{/.footnotes}
{/.footnotes}
</div>
</article>

View File

@@ -0,0 +1,9 @@
<header class="page_header">
<a class="home_link" href="{.home_link}">{.website_title}</a>
{! TODO: Additional links? Probably using the nav semantic element. !}
<nav class="header_nav_bar">
{#.nav_links}
<a class="nav_link" href="{.url}"><div>{.text}</div></a>
{/.nav_links}
</nav>
</header>

View File

@@ -1,3 +1,3 @@
<p>{#.children} <p class="{?.is_single_image}image{/.is_single_image}">{#.children}
{>object/} {>object/}
{/.children}</p> {/.children}</p>

View File

@@ -1 +1 @@
!!!!!!!! plain_link <a href="{.raw_link}">{.raw_link}</a>

View File

@@ -1,6 +1,6 @@
{@select key=.list_type} {@select key=.list_type}
{@eq value="unordered"}<ul>{#.children}{>plain_list_item/}{/.children}</ul>{/eq} {@eq value="unordered"}<ul class="plain_list unordered">{#.children}{>plain_list_item/}{/.children}</ul>{/eq}
{@eq value="ordered"}<ol>{#.children}{>plain_list_item/}{/.children}</ol>{/eq} {@eq value="ordered"}<ol class="plain_list ordered">{#.children}{>plain_list_item/}{/.children}</ol>{/eq}
{@eq value="descriptive"}<dl>{#.children}{>plain_list_item/}{/.children}</dl>{/eq} {@eq value="descriptive"}<dl class="plain_list descriptive">{#.children}{>plain_list_item/}{/.children}</dl>{/eq}
{@none}{!TODO: make this panic!}ERROR: Unrecognized list type {.list_type}.{/none} {@none}{!TODO: make this panic!}ERROR: Unrecognized list type {.list_type}.{/none}
{/select} {/select}

View File

@@ -0,0 +1,3 @@
{#.children}
{>object/}
{/.children}

View File

@@ -1,3 +1,3 @@
<blockquote>{#.children} <blockquote class="quote_block">{#.children}
{>element/} {>element/}
{/.children}</blockquote> {/.children}</blockquote>

View File

@@ -1 +1 @@
<div><sup><a id="{.definition_id}" href="{.reference_link}">{.label}</a></sup><div>{#.contents}{>ast_node/}{/.contents}</div></div> <div class="footnote_definition"><a id="{.definition_id}" href="{.reference_link}" class="label">{.label}.</a> <div class="definition">{#.contents}{>ast_node/}{/.contents}</div></div>

View File

@@ -1 +0,0 @@
<a href="{.raw_link}">{#.children}{>object/}{/.children}</a>

View File

@@ -0,0 +1 @@
<a href="{.target}">{#.children}{>object/}{/.children}</a>

View File

@@ -0,0 +1 @@
<img src="{.src}" alt="{.alt}" />

View File

@@ -1,9 +1,17 @@
<table> <div class="src_block{?.language} srclg_{.language}{/.language}">
{?.language}<div class="src_language">{.language}</div>{/.language}
<table class="src_body">
<tbody> <tbody>
{#.lines} {#.lines}
<tr> <tr>
<td><code>{.}</code></td> <td>{#.children}{@select key=.type}
{@eq value="raw_text"}<code class="src_line">{.content}</code>{/eq}
{@eq value="highlight_start"}<span class="srchl_{.name}">{/eq}
{@eq value="highlight_end"}</span>{/eq}
{@none}{!TODO: make this panic!}ERROR: Unrecognized type {.type}.{/none}
{/select}{/.children}</td>
</tr> </tr>
{/.lines} {/.lines}
</tbody> </tbody>
</table> </table>
</div>

View File

@@ -1 +1,5 @@
!!!!!!!! table <table class="org_table">{#.children}{@select key=.type}
{@eq value="head"}{>table_head/}{/eq}
{@eq value="body"}{>table_body/}{/eq}
{@none}{!TODO: make this panic!}ERROR: Unrecognized type {.type}.{/none}
{/select}{/.children}</table>

View File

@@ -0,0 +1 @@
<tbody>{#.children}{>table_row/}{/.children}</tbody>

View File

@@ -0,0 +1 @@
<td>{#.children}{>object/}{/.children}</td>

View File

@@ -0,0 +1 @@
<thead>{#.children}{>table_head_row/}{/.children}</thead>

View File

@@ -0,0 +1 @@
<th scope="col">{#.children}{>object/}{/.children}</th>

View File

@@ -0,0 +1 @@
<tr>{#.children}{>table_head_cell/}{/.children}</tr>

View File

@@ -0,0 +1 @@
<tr>{#.children}{>table_cell/}{/.children}</tr>

View File

@@ -1 +1 @@
!!!!!!!! timestamp <span class="timestamp">{.source}</span>

View File

@@ -1 +1 @@
<code>{.source}</code> <code class="verbatim">{.contents}</code>

16
docker/natter/Dockerfile Normal file
View File

@@ -0,0 +1,16 @@
# syntax=docker/dockerfile:1
ARG ALPINE_VERSION="3.20"
FROM rustlang/rust:nightly-alpine$ALPINE_VERSION AS builder
RUN apk add --no-cache musl-dev
RUN mkdir /root/natter
WORKDIR /root/natter
COPY --link . .
# TODO: Add static build, which currently errors due to proc_macro. RUSTFLAGS="-C target-feature=+crt-static"
RUN --mount=type=tmpfs,target=/tmp --mount=type=cache,target=/usr/local/cargo/registry,sharing=locked CARGO_TARGET_DIR=/target cargo build --profile release-lto
FROM alpine:$ALPINE_VERSION AS runner
COPY --link --from=builder /target/release-lto/natter /usr/bin/

32
docker/natter/Makefile Normal file
View File

@@ -0,0 +1,32 @@
SHELL := bash
.ONESHELL:
.SHELLFLAGS := -eu -o pipefail -c
.DELETE_ON_ERROR:
MAKEFLAGS += --warn-undefined-variables
MAKEFLAGS += --no-builtin-rules
OS:=$(shell uname -s)
ifeq ($(origin .RECIPEPREFIX), undefined)
$(error This Make does not support .RECIPEPREFIX. Please use GNU Make 4.0 or later)
endif
.RECIPEPREFIX = >
IMAGE_NAME:=natter
TARGET :=
.PHONY: help
help:
> @grep -h "##" $(MAKEFILE_LIST) | grep -v grep | sed -E 's/^([^:]*): *## */\1: /'
.PHONY: build
build: ## Build the docker image.
> docker build --tag $(IMAGE_NAME) --target=$(TARGET) --file Dockerfile ../../
.PHONY: shell
shell: ## Launch an interactive shell inside the docker image.
shell: build
> docker run --rm -i -t --entrypoint /bin/sh --mount type=tmpfs,destination=/tmp $(IMAGE_NAME)
.PHONY: clean
clean:
> docker rmi $(IMAGE_NAME)

View File

@@ -0,0 +1,14 @@
# syntax=docker/dockerfile:1
ARG ALPINE_VERSION="3.20"
FROM rustlang/rust:nightly-alpine$ALPINE_VERSION AS builder
RUN apk add --no-cache musl-dev
RUN --mount=type=tmpfs,target=/tmp --mount=type=cache,target=/usr/local/cargo/registry,sharing=locked cargo install --locked --no-default-features --features ci-autoclean cargo-cache
RUN rustup component add rustfmt
RUN rustup component add clippy
FROM builder AS javascript
RUN apk add --no-cache npm
RUN --mount=type=tmpfs,target=/tmp --mount=type=cache,target=/npmcache,sharing=locked npm set cache /npmcache && npm install --global prettier@3.1.0

View File

@@ -0,0 +1,33 @@
SHELL := bash
.ONESHELL:
.SHELLFLAGS := -eu -o pipefail -c
.DELETE_ON_ERROR:
MAKEFLAGS += --warn-undefined-variables
MAKEFLAGS += --no-builtin-rules
ifeq ($(origin .RECIPEPREFIX), undefined)
$(error This Make does not support .RECIPEPREFIX. Please use GNU Make 4.0 or later)
endif
.RECIPEPREFIX = >
IMAGE_NAME:=natter-development
TARGET :=
.PHONY: help
help:
> @grep -h "##" $(MAKEFILE_LIST) | grep -v grep | sed -E 's/^([^:]*): *## */\1: /'
.PHONY: build
build: ## Build the docker image.
> docker build --tag $(IMAGE_NAME) --target=$(TARGET) --file Dockerfile .
> docker volume create natter-cargo-registry
.PHONY: shell
shell: ## Launch an interactive shell inside the docker image with the source repository mounted at /source.
shell: build
> docker run --rm -i -t --entrypoint /bin/sh --mount type=tmpfs,destination=/tmp -v "$$(readlink -f ../../):/source" --workdir=/source --env CARGO_TARGET_DIR=/target -v "natter-cargo-registry:/usr/local/cargo/registry" $(IMAGE_NAME)
.PHONY: clean
clean:
> docker rmi $(IMAGE_NAME)
> docker volume rm natter-cargo-registry

View File

@@ -0,0 +1,22 @@
[[file:image.svg]]
[[file:/image.svg]]
[[file:./image.svg]]
[[/image.svg]]
[[./image.svg]]
# Check capitalization of extension
[[./image.SVG]]
# Check spaces in path
[[./image and stuff.SVG]]
[[/ssh:admin@test.example:important/file.svg]]
[[file:/ssh:admin@test.example:important/file.svg]]
# Check multiple parts in the path
[[file:/foo/bar/baz/image.svg]]

4
rust-toolchain.toml Normal file
View File

@@ -0,0 +1,4 @@
[toolchain]
channel = "nightly"
profile = "default"
components = ["clippy", "rustfmt"]

View File

@@ -4,7 +4,7 @@ use clap::Subcommand;
use std::path::PathBuf; use std::path::PathBuf;
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
#[command(name = "Writer")] #[command(name = "Natter")]
#[command(version = env!("CARGO_PKG_VERSION"))] #[command(version = env!("CARGO_PKG_VERSION"))]
#[command(about = "Generate a static site.", long_about = None)] #[command(about = "Generate a static site.", long_about = None)]
#[command(propagate_version = true)] #[command(propagate_version = true)]
@@ -24,14 +24,14 @@ pub(crate) enum Commands {
#[derive(Args, Debug)] #[derive(Args, Debug)]
pub(crate) struct InitArgs { pub(crate) struct InitArgs {
/// Path where you want the initial writer structure to be located. /// Path where you want the initial natter structure to be located.
#[arg(short, long)] #[arg(short, long)]
pub(crate) path: PathBuf, pub(crate) path: PathBuf,
} }
#[derive(Args, Debug)] #[derive(Args, Debug)]
pub(crate) struct BuildArgs { pub(crate) struct BuildArgs {
/// Path to the writer config file. /// Path to the natter config file.
#[arg(short, long)] #[arg(short, long)]
pub(crate) config: PathBuf, pub(crate) config: PathBuf,
} }

View File

@@ -0,0 +1 @@

View File

@@ -1,4 +1,5 @@
mod render; mod render;
mod runner; mod runner;
mod stylesheet;
pub(crate) use runner::build_site; pub(crate) use runner::build_site;

View File

@@ -1,34 +1,57 @@
use std::ffi::OsStr; use std::ffi::OsStr;
use std::path::PathBuf; use std::path::PathBuf;
use include_dir::include_dir;
use include_dir::Dir; use include_dir::Dir;
use include_dir::include_dir;
use tokio::fs::DirEntry;
use tokio::task::JoinHandle;
use crate::config::Config; use crate::config::Config;
use crate::context::DependencyManager;
use crate::context::RenderBlogPostPage;
use crate::context::RenderBlogPostPageInput;
use crate::context::RenderBlogStream;
use crate::context::RenderBlogStreamInput;
use crate::context::RenderContext;
use crate::context::RenderPage;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::convert_blog_post_page_to_render_context;
use crate::intermediate::BlogPost; use crate::intermediate::BlogPost;
use crate::intermediate::IPage;
use crate::intermediate::PublishStatus;
use crate::intermediate::get_web_path;
use crate::render::DusterRenderer; use crate::render::DusterRenderer;
use crate::render::RendererIntegration; use crate::render::RendererIntegration;
use crate::walk_fs::WalkAction;
use crate::walk_fs::WalkFsFilterResult;
use crate::walk_fs::walk_fs;
use super::stylesheet::Stylesheet;
static MAIN_TEMPLATES: Dir = include_dir!("$CARGO_MANIFEST_DIR/default_environment/templates/html"); static MAIN_TEMPLATES: Dir = include_dir!("$CARGO_MANIFEST_DIR/default_environment/templates/html");
pub(crate) struct SiteRenderer { pub(crate) struct SiteRenderer {
output_directory: PathBuf, output_directory: PathBuf,
blog_posts: Vec<BlogPost>, blog_posts: Vec<BlogPost>,
stylesheets: Vec<Stylesheet>,
pages: Vec<IPage>,
} }
impl SiteRenderer { impl SiteRenderer {
pub(crate) fn new<P: Into<PathBuf>>( pub(crate) fn new<P: Into<PathBuf>>(
output_directory: P, output_directory: P,
blog_posts: Vec<BlogPost>, blog_posts: Vec<BlogPost>,
stylesheets: Vec<Stylesheet>,
pages: Vec<IPage>,
) -> SiteRenderer { ) -> SiteRenderer {
SiteRenderer { SiteRenderer {
output_directory: output_directory.into(), output_directory: output_directory.into(),
blog_posts, blog_posts,
stylesheets,
pages,
} }
} }
pub(crate) async fn render_blog_posts(&self, config: &Config) -> Result<(), CustomError> {
fn init_renderer_integration(&self) -> Result<DusterRenderer<'_>, CustomError> {
let mut renderer_integration = DusterRenderer::new(); let mut renderer_integration = DusterRenderer::new();
let sources: Vec<_> = MAIN_TEMPLATES let sources: Vec<_> = MAIN_TEMPLATES
@@ -56,32 +79,231 @@ impl SiteRenderer {
renderer_integration.load_template(name, contents)?; renderer_integration.load_template(name, contents)?;
} }
for blog_post in &self.blog_posts { Ok(renderer_integration)
for blog_post_page in &blog_post.pages { }
let output_path = self
.output_directory
.join("posts")
.join(&blog_post.id)
.join(blog_post_page.get_output_path());
let render_context = convert_blog_post_page_to_render_context( pub(crate) async fn render_pages(&self, config: &Config) -> Result<(), CustomError> {
let renderer_integration = self.init_renderer_integration()?;
for page in self.pages.iter().filter(|page| match page.natter_publish {
PublishStatus::Full => true,
PublishStatus::Unlisted => true,
PublishStatus::Unpublished => false,
}) {
let output_path = self.output_directory.join(page.get_output_path());
let dependency_manager =
std::sync::Arc::new(std::sync::Mutex::new(DependencyManager::new()));
let render_context = RenderContext::new(
config, config,
&self.output_directory, self.output_directory.as_path(),
&output_path, output_path.as_path(),
blog_post, None,
blog_post_page, dependency_manager.clone(),
)?; )?;
let rendered_output = renderer_integration.render(render_context)?; let dust_context = RenderPage::new(render_context.clone(), page)?;
let rendered_output = renderer_integration.render(dust_context)?;
let parent_directory = output_path let parent_directory = output_path
.parent() .parent()
.ok_or("Output file should have a containing directory.")?; .ok_or("Output file should have a containing directory.")?;
tokio::fs::create_dir_all(parent_directory).await?; tokio::fs::create_dir_all(parent_directory).await?;
tokio::fs::write(output_path, rendered_output).await?; tokio::fs::write(&output_path, rendered_output).await?;
let dependencies = dependency_manager.lock().unwrap().take_dependencies();
for dependency in dependencies {
dependency.perform(render_context.clone()).await?;
} }
} }
Ok(()) Ok(())
} }
pub(crate) async fn render_blog_posts(&self, config: &Config) -> Result<(), CustomError> {
let renderer_integration = self.init_renderer_integration()?;
for blog_post in self.blog_posts.iter().filter(|blog_post| {
match blog_post
.get_index_page()
.expect("Blog posts should have an index page.")
.natter_publish
{
PublishStatus::Full => true,
PublishStatus::Unlisted => true,
PublishStatus::Unpublished => false,
}
}) {
for blog_post_page in &blog_post.pages {
let output_path = self
.output_directory
.join(config.get_relative_path_to_post(&blog_post.id))
.join(blog_post_page.get_output_path());
let dependency_manager =
std::sync::Arc::new(std::sync::Mutex::new(DependencyManager::new()));
let convert_input = RenderBlogPostPageInput::new(blog_post, blog_post_page);
let render_context = RenderContext::new(
config,
self.output_directory.as_path(),
output_path.as_path(),
None,
dependency_manager.clone(),
)?;
let dust_context = RenderBlogPostPage::new(render_context.clone(), &convert_input)?;
let rendered_output = renderer_integration.render(dust_context)?;
let parent_directory = output_path
.parent()
.ok_or("Output file should have a containing directory.")?;
tokio::fs::create_dir_all(parent_directory).await?;
tokio::fs::write(&output_path, rendered_output).await?;
let dependencies = dependency_manager.lock().unwrap().take_dependencies();
for dependency in dependencies {
dependency.perform(render_context.clone()).await?;
}
}
}
Ok(())
}
pub(crate) async fn render_blog_stream(&self, config: &Config) -> Result<(), CustomError> {
let renderer_integration = self.init_renderer_integration()?;
// Sort blog posts by date, newest first.
let sorted_blog_posts = {
let mut sorted_blog_posts: Vec<_> = self
.blog_posts
.iter()
.filter(|blog_post| {
match blog_post
.get_index_page()
.expect("Blog posts should have an index page.")
.natter_publish
{
PublishStatus::Full => true,
PublishStatus::Unlisted => false,
PublishStatus::Unpublished => false,
}
})
.collect();
sorted_blog_posts
.sort_by_key(|blog_post| (blog_post.get_date(), blog_post.id.as_str()));
sorted_blog_posts.reverse();
sorted_blog_posts
};
for blog_post in &sorted_blog_posts {
if blog_post.get_date().is_none() {
return Err(format!("Blog post {} does not have a date.", blog_post.id).into());
}
}
// Group blog posts based on # of posts per page.
let stream_chunks: Vec<_> = sorted_blog_posts
.chunks(config.get_stream_entries_per_page())
.collect();
// For each group, create a RenderBlogStream.
let num_stream_pages = stream_chunks.len();
for (page_num, chunk) in stream_chunks.into_iter().enumerate() {
let output_file = if page_num == 0 {
self.output_directory.join("index.html")
} else {
self.output_directory
.join("stream")
.join(format!("{}.html", page_num))
};
let newer_link = if page_num == 0 {
None
} else if page_num == 1 {
Some(get_web_path(
config,
&self.output_directory,
&output_file,
"index.html",
)?)
} else {
Some(get_web_path(
config,
&self.output_directory,
&output_file,
format!("stream/{}.html", page_num - 1),
)?)
};
let older_link = if page_num == (num_stream_pages - 1) {
None
} else {
Some(get_web_path(
config,
&self.output_directory,
&output_file,
format!("stream/{}.html", page_num + 1),
)?)
};
let dependency_manager =
std::sync::Arc::new(std::sync::Mutex::new(DependencyManager::new()));
let convert_input = RenderBlogStreamInput::new(chunk, older_link, newer_link);
let render_context = RenderContext::new(
config,
self.output_directory.as_path(),
output_file.as_path(),
None,
dependency_manager.clone(),
)?;
let blog_stream = RenderBlogStream::new(render_context.clone(), &convert_input)?;
// Pass each RenderBlogStream to dust as the context to render index.html and any additional stream pages.
let rendered_output = renderer_integration.render(blog_stream)?;
let parent_directory = output_file
.parent()
.ok_or("Output file should have a containing directory.")?;
tokio::fs::create_dir_all(parent_directory).await?;
tokio::fs::write(&output_file, rendered_output).await?;
let dependencies = dependency_manager.lock().unwrap().take_dependencies();
for dependency in dependencies {
dependency.perform(render_context.clone()).await?;
}
}
Ok(())
}
pub(crate) async fn render_stylesheets(&self) -> Result<(), CustomError> {
let stylesheet_output_directory = self.output_directory.join("stylesheet");
if !stylesheet_output_directory.exists() {
tokio::fs::create_dir(&stylesheet_output_directory).await?;
}
for stylesheet in &self.stylesheets {
let file_output_path = stylesheet_output_directory.join(&stylesheet.path);
let parent_directory = file_output_path
.parent()
.ok_or("Output file should have a containing directory.")?;
tokio::fs::create_dir_all(parent_directory).await?;
tokio::fs::write(file_output_path, stylesheet.contents.as_bytes()).await?;
}
Ok(())
}
pub(crate) async fn copy_static_files(&self, config: &Config) -> Result<(), CustomError> {
let static_files_directory = config
.get_root_directory()
.join(config.get_relative_path_to_static_files());
if !static_files_directory.exists() {
return Ok(());
}
let static_files = get_all_files(&static_files_directory).await?;
for entry in static_files {
let (path, contents) = entry.await??;
let relative_path = path.strip_prefix(&static_files_directory)?;
let output_path = self.output_directory.join(relative_path);
let parent_directory = output_path
.parent()
.ok_or("Output file should have a containing directory.")?;
tokio::fs::create_dir_all(parent_directory).await?;
tokio::fs::write(output_path, contents).await?;
}
Ok(())
}
} }
fn build_name_contents_pairs<'a>( fn build_name_contents_pairs<'a>(
@@ -96,3 +318,32 @@ fn build_name_contents_pairs<'a>(
let contents = std::str::from_utf8(entry.contents())?; let contents = std::str::from_utf8(entry.contents())?;
Ok((name, contents)) Ok((name, contents))
} }
type ReadFileResult = std::io::Result<(PathBuf, Vec<u8>)>;
async fn filter_to_files(entry: &DirEntry) -> WalkFsFilterResult {
let file_type = entry.file_type().await?;
if file_type.is_dir() {
return Ok(WalkAction::Recurse);
}
if file_type.is_file() {
return Ok(WalkAction::HaltAndCapture);
}
unreachable!("Unhandled file type.");
}
async fn get_all_files<P: Into<PathBuf>>(
root_dir: P,
) -> Result<impl Iterator<Item = JoinHandle<ReadFileResult>>, CustomError> {
let files = walk_fs(root_dir, filter_to_files).await?;
let files_and_content = files
.into_iter()
.map(|entry| tokio::spawn(read_file(entry.path())));
Ok(files_and_content)
}
async fn read_file(path: PathBuf) -> ReadFileResult {
let contents = tokio::fs::read(&path).await?;
Ok((path, contents))
}

View File

@@ -1,16 +1,45 @@
use std::ffi::OsStr;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::Arc;
use std::sync::Mutex;
use super::stylesheet::Stylesheet;
use crate::cli::parameters::BuildArgs; use crate::cli::parameters::BuildArgs;
use crate::command::build::render::SiteRenderer; use crate::command::build::render::SiteRenderer;
use crate::config::Config; use crate::config::Config;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::BlogPost; use crate::intermediate::BlogPost;
use crate::intermediate::IPage;
use crate::intermediate::IntermediateContext;
use crate::intermediate::PageInput;
use crate::intermediate::Registry;
use crate::intermediate::get_org_files;
use crate::walk_fs::WalkAction;
use crate::walk_fs::WalkFsFilterResult;
use crate::walk_fs::walk_fs;
use include_dir::Dir;
use include_dir::include_dir;
use tokio::fs::DirEntry;
static DEFAULT_STYLESHEETS: Dir =
include_dir!("$CARGO_MANIFEST_DIR/default_environment/stylesheet");
pub(crate) async fn build_site(args: BuildArgs) -> Result<(), CustomError> { pub(crate) async fn build_site(args: BuildArgs) -> Result<(), CustomError> {
let config = Config::load_from_file(args.config).await?; let config = Config::load_from_file(args.config).await?;
let blog_posts = load_blog_posts(&config).await?; let blog_posts = load_blog_posts(&config).await?;
let renderer = SiteRenderer::new(get_output_directory(&config).await?, blog_posts); let stylesheets = load_stylesheets().await?;
let pages = load_pages(&config).await?;
let renderer = SiteRenderer::new(
get_output_directory(&config).await?,
blog_posts,
stylesheets,
pages,
);
renderer.render_blog_posts(&config).await?; renderer.render_blog_posts(&config).await?;
renderer.render_blog_stream(&config).await?;
renderer.render_pages(&config).await?;
renderer.render_stylesheets().await?;
renderer.copy_static_files(&config).await?;
Ok(()) Ok(())
} }
@@ -34,27 +63,128 @@ async fn get_output_directory(config: &Config) -> Result<PathBuf, CustomError> {
Ok(output_directory) Ok(output_directory)
} }
async fn get_post_directories(config: &Config) -> Result<Vec<PathBuf>, CustomError> { async fn filter_to_highest_folders_containing_org_files(entry: &DirEntry) -> WalkFsFilterResult {
let mut ret = Vec::new();
let mut entries = tokio::fs::read_dir(config.get_posts_directory()).await?;
while let Some(entry) = entries.next_entry().await? {
let file_type = entry.file_type().await?; let file_type = entry.file_type().await?;
if file_type.is_dir() { if !file_type.is_dir() {
ret.push(entry.path()); return Ok(WalkAction::Halt);
}
let mut entries = tokio::fs::read_dir(entry.path()).await?;
while let Some(entry) = entries.next_entry().await? {
let entry_type = entry.file_type().await?;
if !entry_type.is_file() {
continue;
}
match entry.path().extension().and_then(OsStr::to_str) {
Some(ext) if ext.eq_ignore_ascii_case("org") => {
return Ok(WalkAction::HaltAndCapture);
}
_ => {}
} }
} }
Ok(ret) Ok(WalkAction::Recurse)
}
async fn get_post_directories(config: &Config) -> Result<Vec<PathBuf>, CustomError> {
if !config.get_posts_directory().exists() {
return Ok(Vec::new());
}
let top_level_org_folders = walk_fs(
config.get_posts_directory(),
filter_to_highest_folders_containing_org_files,
)
.await?;
Ok(top_level_org_folders
.into_iter()
.map(|entry| entry.path())
.collect())
} }
async fn load_blog_posts(config: &Config) -> Result<Vec<BlogPost>, CustomError> { async fn load_blog_posts(config: &Config) -> Result<Vec<BlogPost>, CustomError> {
let root_directory = config.get_root_directory().to_owned(); let root_directory = config.get_root_directory().to_owned();
let post_directories = get_post_directories(&config).await?; let posts_directory = config.get_posts_directory();
let load_jobs = post_directories let post_directories = get_post_directories(config).await?;
.into_iter() let load_jobs = post_directories.into_iter().map(|path| {
.map(|path| tokio::spawn(BlogPost::load_blog_post(root_directory.clone(), path))); tokio::spawn(BlogPost::load_blog_post(
root_directory.clone(),
posts_directory.clone(),
path,
))
});
let mut blog_posts = Vec::new(); let mut blog_posts = Vec::new();
for job in load_jobs { for job in load_jobs {
blog_posts.push(job.await??); blog_posts.push(job.await??);
} }
Ok(blog_posts) Ok(blog_posts)
} }
async fn load_pages(config: &Config) -> Result<Vec<IPage>, CustomError> {
let pages_source = config
.get_root_directory()
.join(config.get_relative_path_to_pages());
if !pages_source.exists() {
return Ok(Vec::new());
}
let page_files = get_org_files(&pages_source).await?;
let org_files = {
let mut ret = Vec::new();
for page in page_files {
ret.push(page.await??);
}
ret
};
let parsed_org_files = {
let mut ret = Vec::new();
for (path, contents) in org_files.iter() {
let parsed = organic::parser::parse_file(contents.as_str(), Some(path))
.map_err(|_| CustomError::Static("Failed to parse org-mode document."))?;
ret.push((path, contents, parsed));
}
ret
};
let pages = {
let mut ret = Vec::new();
for (real_path, _contents, parsed_document) in parsed_org_files.iter() {
let mut registry = Registry::new();
// Assign IDs to the targets
organic::types::AstNode::from(parsed_document)
.iter_all_ast_nodes()
.for_each(|node| {
if let organic::types::AstNode::Target(target) = node {
registry.get_target(target.value);
}
});
let registry = Arc::new(Mutex::new(registry));
let intermediate_context = IntermediateContext::new(registry)?;
let relative_to_pages_dir_path = real_path.strip_prefix(&pages_source)?;
ret.push(
IPage::new(
intermediate_context,
PageInput::new(relative_to_pages_dir_path, real_path, parsed_document),
)
.await?,
);
}
ret
};
Ok(pages)
}
async fn load_stylesheets() -> Result<Vec<Stylesheet>, CustomError> {
let sources: Vec<_> = DEFAULT_STYLESHEETS
.files()
.filter(|f| f.path().extension() == Some(OsStr::new("css")))
.collect();
let mut ret = Vec::with_capacity(sources.len());
for entry in sources {
let path = entry.path().to_path_buf();
let contents = String::from_utf8(entry.contents().to_vec())?;
let stylesheet = Stylesheet::new(path, contents).await?;
ret.push(stylesheet);
}
Ok(ret)
}

View File

@@ -0,0 +1,15 @@
use std::path::PathBuf;
use crate::error::CustomError;
#[derive(Debug)]
pub(crate) struct Stylesheet {
pub(crate) path: PathBuf,
pub(crate) contents: String,
}
impl Stylesheet {
pub(crate) async fn new(path: PathBuf, contents: String) -> Result<Stylesheet, CustomError> {
Ok(Stylesheet { path, contents })
}
}

View File

@@ -1,3 +1,3 @@
mod runner; mod runner;
pub(crate) use runner::init_writer_folder; pub(crate) use runner::init_natter_folder;

View File

@@ -2,7 +2,7 @@ use crate::cli::parameters::InitArgs;
use crate::config::Config; use crate::config::Config;
use crate::error::CustomError; use crate::error::CustomError;
pub(crate) async fn init_writer_folder(args: InitArgs) -> Result<(), CustomError> { pub(crate) async fn init_natter_folder(args: InitArgs) -> Result<(), CustomError> {
if args.path.exists() && !args.path.is_dir() { if args.path.exists() && !args.path.is_dir() {
return Err("The supplied path exists but is not a directory. Aborting.".into()); return Err("The supplied path exists but is not a directory. Aborting.".into());
} }
@@ -13,7 +13,7 @@ pub(crate) async fn init_writer_folder(args: InitArgs) -> Result<(), CustomError
let mut existing_entries = tokio::fs::read_dir(&args.path).await?; let mut existing_entries = tokio::fs::read_dir(&args.path).await?;
let first_entry = existing_entries.next_entry().await?; let first_entry = existing_entries.next_entry().await?;
if let Some(_) = first_entry { if first_entry.is_some() {
return Err("The directory is not empty. Aborting.".into()); return Err("The directory is not empty. Aborting.".into());
} }

View File

@@ -8,6 +8,7 @@ use crate::error::CustomError;
use super::raw::RawConfig; use super::raw::RawConfig;
/// This is the config struct used by most of the code, which is an interpreted version of the RawConfig struct which is the raw disk-representation of the config. /// This is the config struct used by most of the code, which is an interpreted version of the RawConfig struct which is the raw disk-representation of the config.
#[derive(Debug)]
pub(crate) struct Config { pub(crate) struct Config {
raw: RawConfig, raw: RawConfig,
config_path: PathBuf, config_path: PathBuf,
@@ -16,7 +17,7 @@ pub(crate) struct Config {
impl Config { impl Config {
pub(crate) fn new<P: AsRef<Path>>(root_dir: P) -> Result<Config, CustomError> { pub(crate) fn new<P: AsRef<Path>>(root_dir: P) -> Result<Config, CustomError> {
fn inner(root_dir: &Path) -> Result<Config, CustomError> { fn inner(root_dir: &Path) -> Result<Config, CustomError> {
let file_path = root_dir.join("writer.toml"); let file_path = root_dir.join("natter.toml");
Ok(Config { Ok(Config {
raw: RawConfig::default(), raw: RawConfig::default(),
config_path: file_path, config_path: file_path,
@@ -46,8 +47,7 @@ impl Config {
} }
pub(crate) fn get_root_directory(&self) -> &Path { pub(crate) fn get_root_directory(&self) -> &Path {
&self self.config_path
.config_path
.parent() .parent()
.expect("Config file must exist inside a directory.") .expect("Config file must exist inside a directory.")
} }
@@ -56,6 +56,15 @@ impl Config {
self.get_root_directory().join("posts") self.get_root_directory().join("posts")
} }
/// Get the relative path to the folder containing a blog post.
///
/// This could be appended to the output root directory to get the
/// blog post output folder or it could be used to generate a link
/// to the blog post.
pub(crate) fn get_relative_path_to_post<P: AsRef<Path>>(&self, post_id: P) -> PathBuf {
Path::new("posts").join(post_id)
}
pub(crate) fn get_output_directory(&self) -> PathBuf { pub(crate) fn get_output_directory(&self) -> PathBuf {
self.get_root_directory().join("output") self.get_root_directory().join("output")
} }
@@ -67,4 +76,24 @@ impl Config {
pub(crate) fn get_web_root(&self) -> Option<&str> { pub(crate) fn get_web_root(&self) -> Option<&str> {
self.raw.web_root.as_deref() self.raw.web_root.as_deref()
} }
pub(crate) fn get_site_title(&self) -> Option<&str> {
self.raw.site_title.as_deref()
}
pub(crate) fn get_stream_entries_per_page(&self) -> usize {
self.raw
.stream
.as_ref()
.and_then(|stream| stream.entries_per_page)
.unwrap_or(5)
}
pub(crate) fn get_relative_path_to_static_files(&self) -> PathBuf {
Path::new("static").into()
}
pub(crate) fn get_relative_path_to_pages(&self) -> PathBuf {
Path::new("pages").into()
}
} }

View File

@@ -1,24 +1,18 @@
use serde::Deserialize; use serde::Deserialize;
use serde::Serialize; use serde::Serialize;
/// This is the struct for the writer.toml config file that ends up in each site's root directory. /// This is the struct for the natter.toml config file that ends up in each site's root directory.
#[derive(Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize, Default)]
pub(crate) struct RawConfig { pub(crate) struct RawConfig {
site_title: String, pub(super) site_title: Option<String>,
author: Option<String>, author: Option<String>,
email: Option<String>, email: Option<String>,
pub(super) use_relative_paths: Option<bool>, pub(super) use_relative_paths: Option<bool>,
pub(super) web_root: Option<String>, pub(super) web_root: Option<String>,
pub(super) stream: Option<RawConfigStream>,
} }
impl Default for RawConfig { #[derive(Debug, Deserialize, Serialize, Default)]
fn default() -> Self { pub(crate) struct RawConfigStream {
RawConfig { pub(super) entries_per_page: Option<usize>,
site_title: "My super awesome website".to_owned(),
author: None,
email: None,
use_relative_paths: None,
web_root: None,
}
}
} }

View File

@@ -1,8 +1,6 @@
use std::path::Path;
use serde::Serialize; use serde::Serialize;
use crate::config::Config; use super::render_context::RenderContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::IAngleLink; use crate::intermediate::IAngleLink;
@@ -11,6 +9,8 @@ use super::macros::rnoop;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(tag = "type")] #[serde(tag = "type")]
#[serde(rename = "angle_link")] #[serde(rename = "angle_link")]
pub(crate) struct RenderAngleLink {} pub(crate) struct RenderAngleLink {
post_blank: organic::types::PostBlank,
}
rnoop!(RenderAngleLink, IAngleLink); rnoop!(RenderAngleLink, IAngleLink);

View File

@@ -1,11 +1,10 @@
use std::path::Path;
use serde::Serialize; use serde::Serialize;
use crate::config::Config;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::IAstNode; use crate::intermediate::IAstNode;
use super::RenderHeading;
use super::RenderSection;
use super::angle_link::RenderAngleLink; use super::angle_link::RenderAngleLink;
use super::babel_call::RenderBabelCall; use super::babel_call::RenderBabelCall;
use super::bold::RenderBold; use super::bold::RenderBold;
@@ -45,6 +44,7 @@ use super::quote_block::RenderQuoteBlock;
use super::radio_link::RenderRadioLink; use super::radio_link::RenderRadioLink;
use super::radio_target::RenderRadioTarget; use super::radio_target::RenderRadioTarget;
use super::regular_link::RenderRegularLink; use super::regular_link::RenderRegularLink;
use super::render_context::RenderContext;
use super::special_block::RenderSpecialBlock; use super::special_block::RenderSpecialBlock;
use super::src_block::RenderSrcBlock; use super::src_block::RenderSrcBlock;
use super::statistics_cookie::RenderStatisticsCookie; use super::statistics_cookie::RenderStatisticsCookie;
@@ -57,8 +57,6 @@ use super::timestamp::RenderTimestamp;
use super::underline::RenderUnderline; use super::underline::RenderUnderline;
use super::verbatim::RenderVerbatim; use super::verbatim::RenderVerbatim;
use super::verse_block::RenderVerseBlock; use super::verse_block::RenderVerseBlock;
use super::RenderHeading;
use super::RenderSection;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(untagged)] #[serde(untagged)]
@@ -119,287 +117,209 @@ pub(crate) enum RenderAstNode {
} }
pub(crate) trait IntoRenderAstNode { pub(crate) trait IntoRenderAstNode {
fn into_render_ast_node( fn as_render_ast_node(
&self, &self,
config: &Config, render_context: RenderContext<'_>,
output_directory: &Path,
output_file: &Path,
) -> Result<RenderAstNode, CustomError>; ) -> Result<RenderAstNode, CustomError>;
} }
impl IntoRenderAstNode for IAstNode { impl IntoRenderAstNode for IAstNode {
fn into_render_ast_node( fn as_render_ast_node(
&self, &self,
config: &Config, render_context: RenderContext<'_>,
output_directory: &Path,
output_file: &Path,
) -> Result<RenderAstNode, CustomError> { ) -> Result<RenderAstNode, CustomError> {
match self { match self {
IAstNode::Heading(inner) => Ok(RenderAstNode::Heading(RenderHeading::new( IAstNode::Heading(inner) => Ok(RenderAstNode::Heading(RenderHeading::new(
config, render_context,
output_directory,
output_file,
inner, inner,
)?)), )?)),
IAstNode::Section(inner) => Ok(RenderAstNode::Section(RenderSection::new( IAstNode::Section(inner) => Ok(RenderAstNode::Section(RenderSection::new(
config, render_context,
output_directory,
output_file,
inner, inner,
)?)), )?)),
IAstNode::Paragraph(inner) => Ok(RenderAstNode::Paragraph(RenderParagraph::new( IAstNode::Paragraph(inner) => Ok(RenderAstNode::Paragraph(RenderParagraph::new(
config, render_context,
output_directory,
output_file,
inner, inner,
)?)), )?)),
IAstNode::PlainList(inner) => Ok(RenderAstNode::PlainList(RenderPlainList::new( IAstNode::PlainList(inner) => Ok(RenderAstNode::PlainList(RenderPlainList::new(
config, render_context,
output_directory,
output_file,
inner, inner,
)?)), )?)),
IAstNode::CenterBlock(inner) => Ok(RenderAstNode::CenterBlock(RenderCenterBlock::new( IAstNode::CenterBlock(inner) => Ok(RenderAstNode::CenterBlock(RenderCenterBlock::new(
config, render_context,
output_directory,
output_file,
inner, inner,
)?)), )?)),
IAstNode::QuoteBlock(inner) => Ok(RenderAstNode::QuoteBlock(RenderQuoteBlock::new( IAstNode::QuoteBlock(inner) => Ok(RenderAstNode::QuoteBlock(RenderQuoteBlock::new(
config, render_context,
output_directory,
output_file,
inner, inner,
)?)), )?)),
IAstNode::SpecialBlock(inner) => Ok(RenderAstNode::SpecialBlock( IAstNode::SpecialBlock(inner) => Ok(RenderAstNode::SpecialBlock(
RenderSpecialBlock::new(config, output_directory, output_file, inner)?, RenderSpecialBlock::new(render_context, inner)?,
)), )),
IAstNode::DynamicBlock(inner) => Ok(RenderAstNode::DynamicBlock( IAstNode::DynamicBlock(inner) => Ok(RenderAstNode::DynamicBlock(
RenderDynamicBlock::new(config, output_directory, output_file, inner)?, RenderDynamicBlock::new(render_context, inner)?,
)), )),
IAstNode::FootnoteDefinition(inner) => Ok(RenderAstNode::FootnoteDefinition( IAstNode::FootnoteDefinition(inner) => Ok(RenderAstNode::FootnoteDefinition(
RenderFootnoteDefinition::new(config, output_directory, output_file, inner)?, RenderFootnoteDefinition::new(render_context, inner)?,
)), )),
IAstNode::Comment(inner) => Ok(RenderAstNode::Comment(RenderComment::new( IAstNode::Comment(inner) => Ok(RenderAstNode::Comment(RenderComment::new(
config, render_context,
output_directory,
output_file,
inner, inner,
)?)), )?)),
IAstNode::Drawer(inner) => Ok(RenderAstNode::Drawer(RenderDrawer::new( IAstNode::Drawer(inner) => Ok(RenderAstNode::Drawer(RenderDrawer::new(
config, render_context,
output_directory,
output_file,
inner, inner,
)?)), )?)),
IAstNode::PropertyDrawer(inner) => Ok(RenderAstNode::PropertyDrawer( IAstNode::PropertyDrawer(inner) => Ok(RenderAstNode::PropertyDrawer(
RenderPropertyDrawer::new(config, output_directory, output_file, inner)?, RenderPropertyDrawer::new(render_context, inner)?,
)), )),
IAstNode::Table(inner) => Ok(RenderAstNode::Table(RenderTable::new( IAstNode::Table(inner) => Ok(RenderAstNode::Table(RenderTable::new(
config, render_context,
output_directory,
output_file,
inner, inner,
)?)), )?)),
IAstNode::VerseBlock(inner) => Ok(RenderAstNode::VerseBlock(RenderVerseBlock::new( IAstNode::VerseBlock(inner) => Ok(RenderAstNode::VerseBlock(RenderVerseBlock::new(
config, render_context,
output_directory,
output_file,
inner, inner,
)?)), )?)),
IAstNode::CommentBlock(inner) => Ok(RenderAstNode::CommentBlock( IAstNode::CommentBlock(inner) => Ok(RenderAstNode::CommentBlock(
RenderCommentBlock::new(config, output_directory, output_file, inner)?, RenderCommentBlock::new(render_context, inner)?,
)), )),
IAstNode::ExampleBlock(inner) => Ok(RenderAstNode::ExampleBlock( IAstNode::ExampleBlock(inner) => Ok(RenderAstNode::ExampleBlock(
RenderExampleBlock::new(config, output_directory, output_file, inner)?, RenderExampleBlock::new(render_context, inner)?,
)), )),
IAstNode::ExportBlock(inner) => Ok(RenderAstNode::ExportBlock(RenderExportBlock::new( IAstNode::ExportBlock(inner) => Ok(RenderAstNode::ExportBlock(RenderExportBlock::new(
config, render_context,
output_directory,
output_file,
inner, inner,
)?)), )?)),
IAstNode::SrcBlock(inner) => Ok(RenderAstNode::SrcBlock(RenderSrcBlock::new( IAstNode::SrcBlock(inner) => Ok(RenderAstNode::SrcBlock(RenderSrcBlock::new(
config, render_context,
output_directory,
output_file,
inner, inner,
)?)), )?)),
IAstNode::Clock(inner) => Ok(RenderAstNode::Clock(RenderClock::new( IAstNode::Clock(inner) => Ok(RenderAstNode::Clock(RenderClock::new(
config, render_context,
output_directory,
output_file,
inner, inner,
)?)), )?)),
IAstNode::DiarySexp(inner) => Ok(RenderAstNode::DiarySexp(RenderDiarySexp::new( IAstNode::DiarySexp(inner) => Ok(RenderAstNode::DiarySexp(RenderDiarySexp::new(
config, render_context,
output_directory,
output_file,
inner, inner,
)?)), )?)),
IAstNode::Planning(inner) => Ok(RenderAstNode::Planning(RenderPlanning::new( IAstNode::Planning(inner) => Ok(RenderAstNode::Planning(RenderPlanning::new(
config, render_context,
output_directory,
output_file,
inner, inner,
)?)), )?)),
IAstNode::FixedWidthArea(inner) => Ok(RenderAstNode::FixedWidthArea( IAstNode::FixedWidthArea(inner) => Ok(RenderAstNode::FixedWidthArea(
RenderFixedWidthArea::new(config, output_directory, output_file, inner)?, RenderFixedWidthArea::new(render_context, inner)?,
)), )),
IAstNode::HorizontalRule(inner) => Ok(RenderAstNode::HorizontalRule( IAstNode::HorizontalRule(inner) => Ok(RenderAstNode::HorizontalRule(
RenderHorizontalRule::new(config, output_directory, output_file, inner)?, RenderHorizontalRule::new(render_context, inner)?,
)), )),
IAstNode::Keyword(inner) => Ok(RenderAstNode::Keyword(RenderKeyword::new( IAstNode::Keyword(inner) => Ok(RenderAstNode::Keyword(RenderKeyword::new(
config, render_context,
output_directory,
output_file,
inner, inner,
)?)), )?)),
IAstNode::BabelCall(inner) => Ok(RenderAstNode::BabelCall(RenderBabelCall::new( IAstNode::BabelCall(inner) => Ok(RenderAstNode::BabelCall(RenderBabelCall::new(
config, render_context,
output_directory,
output_file,
inner, inner,
)?)), )?)),
IAstNode::LatexEnvironment(inner) => Ok(RenderAstNode::LatexEnvironment( IAstNode::LatexEnvironment(inner) => Ok(RenderAstNode::LatexEnvironment(
RenderLatexEnvironment::new(config, output_directory, output_file, inner)?, RenderLatexEnvironment::new(render_context, inner)?,
)), )),
IAstNode::Bold(inner) => Ok(RenderAstNode::Bold(RenderBold::new( IAstNode::Bold(inner) => {
config, Ok(RenderAstNode::Bold(RenderBold::new(render_context, inner)?))
output_directory, }
output_file,
inner,
)?)),
IAstNode::Italic(inner) => Ok(RenderAstNode::Italic(RenderItalic::new( IAstNode::Italic(inner) => Ok(RenderAstNode::Italic(RenderItalic::new(
config, render_context,
output_directory,
output_file,
inner, inner,
)?)), )?)),
IAstNode::Underline(inner) => Ok(RenderAstNode::Underline(RenderUnderline::new( IAstNode::Underline(inner) => Ok(RenderAstNode::Underline(RenderUnderline::new(
config, render_context,
output_directory,
output_file,
inner, inner,
)?)), )?)),
IAstNode::StrikeThrough(inner) => Ok(RenderAstNode::StrikeThrough( IAstNode::StrikeThrough(inner) => Ok(RenderAstNode::StrikeThrough(
RenderStrikeThrough::new(config, output_directory, output_file, inner)?, RenderStrikeThrough::new(render_context, inner)?,
)), )),
IAstNode::Code(inner) => Ok(RenderAstNode::Code(RenderCode::new( IAstNode::Code(inner) => {
config, Ok(RenderAstNode::Code(RenderCode::new(render_context, inner)?))
output_directory, }
output_file,
inner,
)?)),
IAstNode::Verbatim(inner) => Ok(RenderAstNode::Verbatim(RenderVerbatim::new( IAstNode::Verbatim(inner) => Ok(RenderAstNode::Verbatim(RenderVerbatim::new(
config, render_context,
output_directory,
output_file,
inner, inner,
)?)), )?)),
IAstNode::PlainText(inner) => Ok(RenderAstNode::PlainText(RenderPlainText::new( IAstNode::PlainText(inner) => Ok(RenderAstNode::PlainText(RenderPlainText::new(
config, render_context,
output_directory,
output_file,
inner, inner,
)?)), )?)),
IAstNode::RegularLink(inner) => Ok(RenderAstNode::RegularLink(RenderRegularLink::new( IAstNode::RegularLink(inner) => Ok(RenderAstNode::RegularLink(RenderRegularLink::new(
config, render_context,
output_directory,
output_file,
inner, inner,
)?)), )?)),
IAstNode::RadioLink(inner) => Ok(RenderAstNode::RadioLink(RenderRadioLink::new( IAstNode::RadioLink(inner) => Ok(RenderAstNode::RadioLink(RenderRadioLink::new(
config, render_context,
output_directory,
output_file,
inner, inner,
)?)), )?)),
IAstNode::RadioTarget(inner) => Ok(RenderAstNode::RadioTarget(RenderRadioTarget::new( IAstNode::RadioTarget(inner) => Ok(RenderAstNode::RadioTarget(RenderRadioTarget::new(
config, render_context,
output_directory,
output_file,
inner, inner,
)?)), )?)),
IAstNode::PlainLink(inner) => Ok(RenderAstNode::PlainLink(RenderPlainLink::new( IAstNode::PlainLink(inner) => Ok(RenderAstNode::PlainLink(RenderPlainLink::new(
config, render_context,
output_directory,
output_file,
inner, inner,
)?)), )?)),
IAstNode::AngleLink(inner) => Ok(RenderAstNode::AngleLink(RenderAngleLink::new( IAstNode::AngleLink(inner) => Ok(RenderAstNode::AngleLink(RenderAngleLink::new(
config, render_context,
output_directory,
output_file,
inner, inner,
)?)), )?)),
IAstNode::OrgMacro(inner) => Ok(RenderAstNode::OrgMacro(RenderOrgMacro::new( IAstNode::OrgMacro(inner) => Ok(RenderAstNode::OrgMacro(RenderOrgMacro::new(
config, render_context,
output_directory,
output_file,
inner, inner,
)?)), )?)),
IAstNode::Entity(inner) => Ok(RenderAstNode::Entity(RenderEntity::new( IAstNode::Entity(inner) => Ok(RenderAstNode::Entity(RenderEntity::new(
config, render_context,
output_directory,
output_file,
inner, inner,
)?)), )?)),
IAstNode::LatexFragment(inner) => Ok(RenderAstNode::LatexFragment( IAstNode::LatexFragment(inner) => Ok(RenderAstNode::LatexFragment(
RenderLatexFragment::new(config, output_directory, output_file, inner)?, RenderLatexFragment::new(render_context, inner)?,
)), )),
IAstNode::ExportSnippet(inner) => Ok(RenderAstNode::ExportSnippet( IAstNode::ExportSnippet(inner) => Ok(RenderAstNode::ExportSnippet(
RenderExportSnippet::new(config, output_directory, output_file, inner)?, RenderExportSnippet::new(render_context, inner)?,
)), )),
IAstNode::FootnoteReference(inner) => Ok(RenderAstNode::FootnoteReference( IAstNode::FootnoteReference(inner) => Ok(RenderAstNode::FootnoteReference(
RenderFootnoteReference::new(config, output_directory, output_file, inner)?, RenderFootnoteReference::new(render_context, inner)?,
)), )),
IAstNode::Citation(inner) => Ok(RenderAstNode::Citation(RenderCitation::new( IAstNode::Citation(inner) => Ok(RenderAstNode::Citation(RenderCitation::new(
config, render_context,
output_directory,
output_file,
inner, inner,
)?)), )?)),
IAstNode::CitationReference(inner) => Ok(RenderAstNode::CitationReference( IAstNode::CitationReference(inner) => Ok(RenderAstNode::CitationReference(
RenderCitationReference::new(config, output_directory, output_file, inner)?, RenderCitationReference::new(render_context, inner)?,
)), )),
IAstNode::InlineBabelCall(inner) => Ok(RenderAstNode::InlineBabelCall( IAstNode::InlineBabelCall(inner) => Ok(RenderAstNode::InlineBabelCall(
RenderInlineBabelCall::new(config, output_directory, output_file, inner)?, RenderInlineBabelCall::new(render_context, inner)?,
)), )),
IAstNode::InlineSourceBlock(inner) => Ok(RenderAstNode::InlineSourceBlock( IAstNode::InlineSourceBlock(inner) => Ok(RenderAstNode::InlineSourceBlock(
RenderInlineSourceBlock::new(config, output_directory, output_file, inner)?, RenderInlineSourceBlock::new(render_context, inner)?,
)), )),
IAstNode::LineBreak(inner) => Ok(RenderAstNode::LineBreak(RenderLineBreak::new( IAstNode::LineBreak(inner) => Ok(RenderAstNode::LineBreak(RenderLineBreak::new(
config, render_context,
output_directory,
output_file,
inner, inner,
)?)), )?)),
IAstNode::Target(inner) => Ok(RenderAstNode::Target(RenderTarget::new( IAstNode::Target(inner) => Ok(RenderAstNode::Target(RenderTarget::new(
config, render_context,
output_directory,
output_file,
inner, inner,
)?)), )?)),
IAstNode::StatisticsCookie(inner) => Ok(RenderAstNode::StatisticsCookie( IAstNode::StatisticsCookie(inner) => Ok(RenderAstNode::StatisticsCookie(
RenderStatisticsCookie::new(config, output_directory, output_file, inner)?, RenderStatisticsCookie::new(render_context, inner)?,
)), )),
IAstNode::Subscript(inner) => Ok(RenderAstNode::Subscript(RenderSubscript::new( IAstNode::Subscript(inner) => Ok(RenderAstNode::Subscript(RenderSubscript::new(
config, render_context,
output_directory,
output_file,
inner, inner,
)?)), )?)),
IAstNode::Superscript(inner) => Ok(RenderAstNode::Superscript(RenderSuperscript::new( IAstNode::Superscript(inner) => Ok(RenderAstNode::Superscript(RenderSuperscript::new(
config, render_context,
output_directory,
output_file,
inner, inner,
)?)), )?)),
IAstNode::Timestamp(inner) => Ok(RenderAstNode::Timestamp(RenderTimestamp::new( IAstNode::Timestamp(inner) => Ok(RenderAstNode::Timestamp(RenderTimestamp::new(
config, render_context,
output_directory,
output_file,
inner, inner,
)?)), )?)),
} }

View File

@@ -1,8 +1,6 @@
use std::path::Path;
use serde::Serialize; use serde::Serialize;
use crate::config::Config; use super::render_context::RenderContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::IBabelCall; use crate::intermediate::IBabelCall;
@@ -11,6 +9,8 @@ use super::macros::rnoop;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(tag = "type")] #[serde(tag = "type")]
#[serde(rename = "babel_call")] #[serde(rename = "babel_call")]
pub(crate) struct RenderBabelCall {} pub(crate) struct RenderBabelCall {
post_blank: organic::types::PostBlank,
}
rnoop!(RenderBabelCall, IBabelCall); rnoop!(RenderBabelCall, IBabelCall);

View File

@@ -1,8 +1,32 @@
use std::collections::HashSet;
use serde::Serialize; use serde::Serialize;
use super::footnote_definition::RenderRealFootnoteDefinition; use super::render_context::RenderContext;
use crate::context::macros::push_file;
use crate::error::CustomError;
use crate::intermediate::BlogPost;
use crate::intermediate::BlogPostPage;
use crate::intermediate::get_web_path;
use super::GlobalSettings; use super::GlobalSettings;
use super::PageHeader;
use super::RenderDocumentElement; use super::RenderDocumentElement;
use super::footnote_definition::RenderRealFootnoteDefinition;
use super::macros::render;
#[derive(Debug)]
pub(crate) struct RenderBlogPostPageInput<'a> {
#[allow(dead_code)]
post: &'a BlogPost,
page: &'a BlogPostPage,
}
impl<'a> RenderBlogPostPageInput<'a> {
pub(crate) fn new(post: &'a BlogPost, page: &'a BlogPostPage) -> RenderBlogPostPageInput<'a> {
RenderBlogPostPageInput { post, page }
}
}
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(tag = "type")] #[serde(tag = "type")]
@@ -10,6 +34,8 @@ use super::RenderDocumentElement;
pub(crate) struct RenderBlogPostPage { pub(crate) struct RenderBlogPostPage {
global_settings: GlobalSettings, global_settings: GlobalSettings,
page_header: Option<PageHeader>,
/// The title that will be shown visibly on the page. /// The title that will be shown visibly on the page.
title: Option<String>, title: Option<String>,
@@ -20,21 +46,109 @@ pub(crate) struct RenderBlogPostPage {
footnotes: Vec<RenderRealFootnoteDefinition>, footnotes: Vec<RenderRealFootnoteDefinition>,
} }
impl RenderBlogPostPage { render!(
// TODO: Maybe these settings should be moved into a common struct so this can have the same type signature as the others. RenderBlogPostPage,
pub(crate) fn new( RenderBlogPostPageInput,
global_settings: GlobalSettings, original,
title: Option<String>, render_context,
self_link: Option<String>, {
children: Vec<RenderDocumentElement>, push_file!(render_context, &original.page.src, {
footnotes: Vec<RenderRealFootnoteDefinition>, let page_header = PageHeader::new(
) -> RenderBlogPostPage { render_context.config.get_site_title().map(str::to_string),
RenderBlogPostPage { Some(get_web_path(
render_context.config,
render_context.output_root_directory,
render_context.output_file,
"",
)?),
Some(get_web_path(
render_context.config,
render_context.output_root_directory,
render_context.output_file,
"about_me",
)?),
);
let link_to_blog_post = get_web_path(
render_context.config,
render_context.output_root_directory,
render_context.output_file,
render_context
.output_file
.strip_prefix(render_context.output_root_directory)?,
)?;
let children = {
let mut children = Vec::new();
for child in original.page.children.iter() {
children.push(RenderDocumentElement::new(render_context.clone(), child)?);
}
children
};
let footnotes = {
let mut ret = Vec::new();
for footnote in original.page.footnotes.iter() {
ret.push(RenderRealFootnoteDefinition::new(
render_context.clone(),
footnote,
)?);
}
ret
};
let mut css_files = vec![
get_web_path(
render_context.config,
render_context.output_root_directory,
render_context.output_file,
"stylesheet/reset.css",
)?,
get_web_path(
render_context.config,
render_context.output_root_directory,
render_context.output_file,
"stylesheet/main.css",
)?,
];
let additional_css_files = render_context
.dependency_manager
.lock()
.unwrap()
.list_css()?
.map(|css_name| {
get_web_path(
render_context.config,
render_context.output_root_directory,
render_context.output_file,
format!("stylesheet/{}", css_name),
)
})
.collect::<Result<HashSet<_>, _>>()?;
css_files.extend(additional_css_files.into_iter());
let js_files = vec![get_web_path(
render_context.config,
render_context.output_root_directory,
render_context.output_file,
"blog_post.js",
)?];
let global_settings =
GlobalSettings::new(original.page.title.clone(), css_files, js_files);
let ret = RenderBlogPostPage {
global_settings, global_settings,
title, page_header: Some(page_header),
self_link, title: original.page.title.clone(),
self_link: Some(link_to_blog_post),
children, children,
footnotes, footnotes,
};
Ok(ret)
})
} }
} );
}

242
src/context/blog_stream.rs Normal file
View File

@@ -0,0 +1,242 @@
use std::collections::HashSet;
use serde::Serialize;
use super::macros::render;
use super::render_context::RenderContext;
use crate::context::RenderDocumentElement;
use crate::context::RenderRealFootnoteDefinition;
use crate::context::macros::push_file;
use crate::error::CustomError;
use crate::intermediate::BlogPost;
use crate::intermediate::get_web_path;
use super::GlobalSettings;
use super::PageHeader;
#[derive(Debug)]
pub(crate) struct RenderBlogStreamInput<'a, 'b> {
original: &'a [&'b BlogPost],
older_link: Option<String>,
newer_link: Option<String>,
}
impl<'a, 'b> RenderBlogStreamInput<'a, 'b> {
pub(crate) fn new(
original: &'a [&'b BlogPost],
older_link: Option<String>,
newer_link: Option<String>,
) -> RenderBlogStreamInput<'a, 'b> {
RenderBlogStreamInput {
original,
older_link,
newer_link,
}
}
}
#[derive(Debug, Serialize)]
#[serde(tag = "type")]
#[serde(rename = "blog_stream")]
pub(crate) struct RenderBlogStream {
global_settings: GlobalSettings,
page_header: Option<PageHeader>,
children: Vec<RenderBlogStreamEntry>,
stream_pagination: Option<RenderBlogStreamPagination>,
}
render!(
RenderBlogStream,
RenderBlogStreamInput,
original,
render_context,
{
let page_header = PageHeader::new(
render_context.config.get_site_title().map(str::to_string),
Some(get_web_path(
render_context.config,
render_context.output_root_directory,
render_context.output_file,
"",
)?),
Some(get_web_path(
render_context.config,
render_context.output_root_directory,
render_context.output_file,
"about_me",
)?),
);
let children = original
.original
.iter()
.enumerate()
.map(|(i, blog_post)| {
RenderBlogStreamEntry::new(
render_context.clone(),
&RenderBlogStreamEntryInput::new(blog_post, i),
)
})
.collect::<Result<Vec<_>, _>>()?;
let stream_pagination = if original.older_link.is_some() || original.newer_link.is_some() {
Some(RenderBlogStreamPagination::new(
original.older_link.clone(),
original.newer_link.clone(),
)?)
} else {
None
};
let mut css_files = vec![
get_web_path(
render_context.config,
render_context.output_root_directory,
render_context.output_file,
"stylesheet/reset.css",
)?,
get_web_path(
render_context.config,
render_context.output_root_directory,
render_context.output_file,
"stylesheet/main.css",
)?,
];
let additional_css_files = render_context
.dependency_manager
.lock()
.unwrap()
.list_css()?
.map(|css_name| {
get_web_path(
render_context.config,
render_context.output_root_directory,
render_context.output_file,
format!("stylesheet/{}", css_name),
)
})
.collect::<Result<HashSet<_>, _>>()?;
css_files.extend(additional_css_files.into_iter());
let js_files = vec![get_web_path(
render_context.config,
render_context.output_root_directory,
render_context.output_file,
"blog_post.js",
)?];
let global_settings = GlobalSettings::new(
render_context.config.get_site_title().map(str::to_string),
css_files,
js_files,
);
Ok(RenderBlogStream {
global_settings,
page_header: Some(page_header),
children,
stream_pagination,
})
}
);
#[derive(Debug)]
pub(crate) struct RenderBlogStreamEntryInput<'a> {
original: &'a BlogPost,
offset: usize,
}
impl<'a> RenderBlogStreamEntryInput<'a> {
fn new(original: &'a BlogPost, offset: usize) -> RenderBlogStreamEntryInput<'a> {
RenderBlogStreamEntryInput { original, offset }
}
}
#[derive(Debug, Serialize)]
pub(crate) struct RenderBlogStreamEntry {
/// The title that will be shown visibly on the page.
title: Option<String>,
self_link: Option<String>,
children: Vec<RenderDocumentElement>,
footnotes: Vec<RenderRealFootnoteDefinition>,
}
render!(
RenderBlogStreamEntry,
RenderBlogStreamEntryInput,
original,
render_context,
{
let offset_string = original.offset.to_string();
let render_context = {
let mut render_context = render_context.clone();
render_context.id_addition = Some(offset_string.as_str());
render_context
};
let link_to_blog_post = get_web_path(
render_context.config,
render_context.output_root_directory,
render_context.output_file,
render_context
.config
.get_relative_path_to_post(&original.original.id),
)?;
// TODO: Should I guess an index page instead of erroring out?
let index_page = original
.original
.get_index_page()
.ok_or_else(|| format!("Blog post {} needs an index page.", original.original.id))?;
push_file!(render_context, &index_page.src, {
let title = index_page.title.clone();
let children = index_page
.children
.iter()
.map(|child| RenderDocumentElement::new(render_context.clone(), child))
.collect::<Result<Vec<_>, _>>()?;
let footnotes = {
let mut ret = Vec::new();
for footnote in index_page.footnotes.iter() {
ret.push(RenderRealFootnoteDefinition::new(
render_context.clone(),
footnote,
)?);
}
ret
};
Ok(RenderBlogStreamEntry {
title,
self_link: Some(link_to_blog_post),
children,
footnotes,
})
})
}
);
#[derive(Debug, Serialize)]
pub(crate) struct RenderBlogStreamPagination {
older_link: Option<String>,
newer_link: Option<String>,
}
impl RenderBlogStreamPagination {
fn new(
older_link: Option<String>,
newer_link: Option<String>,
) -> Result<RenderBlogStreamPagination, CustomError> {
Ok(RenderBlogStreamPagination {
older_link,
newer_link,
})
}
}

View File

@@ -1,16 +1,31 @@
use std::path::Path;
use serde::Serialize; use serde::Serialize;
use crate::config::Config;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::IBold; use crate::intermediate::IBold;
use super::macros::rnoop; use super::RenderObject;
use super::macros::render;
use super::render_context::RenderContext;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(tag = "type")] #[serde(tag = "type")]
#[serde(rename = "bold")] #[serde(rename = "bold")]
pub(crate) struct RenderBold {} pub(crate) struct RenderBold {
children: Vec<RenderObject>,
post_blank: organic::types::PostBlank,
}
rnoop!(RenderBold, IBold); render!(RenderBold, IBold, original, render_context, {
let children = {
let mut ret = Vec::new();
for obj in original.children.iter() {
ret.push(RenderObject::new(render_context.clone(), obj)?);
}
ret
};
Ok(RenderBold {
children,
post_blank: original.post_blank,
})
});

View File

@@ -1,8 +1,6 @@
use std::path::Path;
use serde::Serialize; use serde::Serialize;
use crate::config::Config; use super::render_context::RenderContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::ICenterBlock; use crate::intermediate::ICenterBlock;
@@ -11,6 +9,8 @@ use super::macros::rnoop;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(tag = "type")] #[serde(tag = "type")]
#[serde(rename = "center_block")] #[serde(rename = "center_block")]
pub(crate) struct RenderCenterBlock {} pub(crate) struct RenderCenterBlock {
post_blank: organic::types::PostBlank,
}
rnoop!(RenderCenterBlock, ICenterBlock); rnoop!(RenderCenterBlock, ICenterBlock);

View File

@@ -1,8 +1,6 @@
use std::path::Path;
use serde::Serialize; use serde::Serialize;
use crate::config::Config; use super::render_context::RenderContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::ICitation; use crate::intermediate::ICitation;
@@ -11,6 +9,8 @@ use super::macros::rnoop;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(tag = "type")] #[serde(tag = "type")]
#[serde(rename = "citation")] #[serde(rename = "citation")]
pub(crate) struct RenderCitation {} pub(crate) struct RenderCitation {
post_blank: organic::types::PostBlank,
}
rnoop!(RenderCitation, ICitation); rnoop!(RenderCitation, ICitation);

View File

@@ -1,8 +1,6 @@
use std::path::Path;
use serde::Serialize; use serde::Serialize;
use crate::config::Config; use super::render_context::RenderContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::ICitationReference; use crate::intermediate::ICitationReference;
@@ -11,6 +9,8 @@ use super::macros::rnoop;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(tag = "type")] #[serde(tag = "type")]
#[serde(rename = "citation_reference")] #[serde(rename = "citation_reference")]
pub(crate) struct RenderCitationReference {} pub(crate) struct RenderCitationReference {
post_blank: organic::types::PostBlank,
}
rnoop!(RenderCitationReference, ICitationReference); rnoop!(RenderCitationReference, ICitationReference);

View File

@@ -1,8 +1,6 @@
use std::path::Path;
use serde::Serialize; use serde::Serialize;
use crate::config::Config; use super::render_context::RenderContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::IClock; use crate::intermediate::IClock;
@@ -11,6 +9,8 @@ use super::macros::rnoop;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(tag = "type")] #[serde(tag = "type")]
#[serde(rename = "clock")] #[serde(rename = "clock")]
pub(crate) struct RenderClock {} pub(crate) struct RenderClock {
post_blank: organic::types::PostBlank,
}
rnoop!(RenderClock, IClock); rnoop!(RenderClock, IClock);

View File

@@ -1,16 +1,22 @@
use std::path::Path;
use serde::Serialize; use serde::Serialize;
use crate::config::Config; use super::render_context::RenderContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::ICode; use crate::intermediate::ICode;
use super::macros::rnoop; use super::macros::render;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(tag = "type")] #[serde(tag = "type")]
#[serde(rename = "code")] #[serde(rename = "code")]
pub(crate) struct RenderCode {} pub(crate) struct RenderCode {
contents: String,
post_blank: organic::types::PostBlank,
}
rnoop!(RenderCode, ICode); render!(RenderCode, ICode, original, _render_context, {
Ok(RenderCode {
contents: original.contents.clone(),
post_blank: original.post_blank,
})
});

View File

@@ -1,8 +1,6 @@
use std::path::Path;
use serde::Serialize; use serde::Serialize;
use crate::config::Config; use super::render_context::RenderContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::IComment; use crate::intermediate::IComment;
@@ -11,6 +9,8 @@ use super::macros::rnoop;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(tag = "type")] #[serde(tag = "type")]
#[serde(rename = "comment")] #[serde(rename = "comment")]
pub(crate) struct RenderComment {} pub(crate) struct RenderComment {
post_blank: organic::types::PostBlank,
}
rnoop!(RenderComment, IComment); rnoop!(RenderComment, IComment);

View File

@@ -1,8 +1,6 @@
use std::path::Path;
use serde::Serialize; use serde::Serialize;
use crate::config::Config; use super::render_context::RenderContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::ICommentBlock; use crate::intermediate::ICommentBlock;
@@ -11,6 +9,8 @@ use super::macros::rnoop;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(tag = "type")] #[serde(tag = "type")]
#[serde(rename = "comment_block")] #[serde(rename = "comment_block")]
pub(crate) struct RenderCommentBlock {} pub(crate) struct RenderCommentBlock {
post_blank: organic::types::PostBlank,
}
rnoop!(RenderCommentBlock, ICommentBlock); rnoop!(RenderCommentBlock, ICommentBlock);

51
src/context/dependency.rs Normal file
View File

@@ -0,0 +1,51 @@
use std::path::PathBuf;
use crate::error::CustomError;
use super::RenderContext;
#[derive(Debug)]
pub(crate) enum Dependency {
StaticFile { absolute_path: PathBuf },
CssFile { name: String },
}
impl Dependency {
pub(crate) async fn perform(
&self,
render_context: RenderContext<'_>,
) -> Result<(), CustomError> {
match self {
Dependency::StaticFile { absolute_path } => {
let input_root_directory = render_context.config.get_root_directory();
let relative_path_to_file = absolute_path.strip_prefix(input_root_directory)?;
let path_to_output = render_context
.output_root_directory
.join(relative_path_to_file);
tokio::fs::create_dir_all(
path_to_output
.parent()
.ok_or("Output file should have a containing directory.")?,
)
.await?;
if tokio::fs::metadata(&path_to_output).await.is_ok() {
// TODO: compare hash and error out if they do not match.
println!(
"Not copying {} to {} because the output file already exists.",
absolute_path.display(),
path_to_output.display()
);
} else {
tokio::fs::copy(absolute_path, path_to_output).await?;
}
Ok(())
}
Dependency::CssFile { name: _ } => {
// We don't do anything because the CSS files are already copied into the output from natter's default environment.
// TODO: When we add support for CSS outside the default environment, we should add support for dynamically picking which ones to copy here.
Ok(())
}
}
}
}

View File

@@ -0,0 +1,84 @@
use std::path::Path;
use std::path::PathBuf;
use crate::error::CustomError;
use super::dependency::Dependency;
pub(crate) type RefDependencyManager = std::sync::Arc<std::sync::Mutex<DependencyManager>>;
#[derive(Debug)]
pub(crate) struct DependencyManager {
/// A stack of paths for the files being visited.
///
/// The last entry is the current file being processed. This can be used for handling relative-path links.
file_stack: Vec<PathBuf>,
dependencies: Vec<Dependency>,
}
impl DependencyManager {
pub(crate) fn new() -> Self {
DependencyManager {
file_stack: Vec::new(),
dependencies: Vec::new(),
}
}
pub(crate) fn push_file<P>(&mut self, path: P) -> Result<(), CustomError>
where
P: Into<PathBuf>,
{
self.file_stack.push(path.into());
Ok(())
}
pub(crate) fn pop_file(&mut self) -> Result<(), CustomError> {
self.file_stack
.pop()
.expect("Popped more files off the dependency manager file stack than exist.");
Ok(())
}
pub(crate) fn get_current_folder(&self) -> Result<&Path, CustomError> {
Ok(self
.file_stack
.last()
.ok_or("No current file")?
.parent()
.ok_or("Current file was not in a directory")?)
}
pub(crate) fn mark_file_for_copying<P>(&mut self, path: P) -> Result<(), CustomError>
where
P: Into<PathBuf>,
{
self.dependencies.push(Dependency::StaticFile {
absolute_path: path.into(),
});
Ok(())
}
/// Return the dependencies and forget about them.
pub(crate) fn take_dependencies(&mut self) -> Vec<Dependency> {
let mut dependencies = Vec::new();
std::mem::swap(&mut self.dependencies, &mut dependencies);
dependencies
}
pub(crate) fn include_css<N>(&mut self, name: N) -> Result<(), CustomError>
where
std::string::String: From<N>,
{
self.dependencies
.push(Dependency::CssFile { name: name.into() });
Ok(())
}
pub(crate) fn list_css(&self) -> Result<impl Iterator<Item = &String>, CustomError> {
Ok(self.dependencies.iter().filter_map(|dep| match dep {
Dependency::CssFile { name } => Some(name),
_ => None,
}))
}
}

View File

@@ -1,8 +1,6 @@
use std::path::Path;
use serde::Serialize; use serde::Serialize;
use crate::config::Config; use super::render_context::RenderContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::IDiarySexp; use crate::intermediate::IDiarySexp;
@@ -11,6 +9,8 @@ use super::macros::rnoop;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(tag = "type")] #[serde(tag = "type")]
#[serde(rename = "diary_sexp")] #[serde(rename = "diary_sexp")]
pub(crate) struct RenderDiarySexp {} pub(crate) struct RenderDiarySexp {
post_blank: organic::types::PostBlank,
}
rnoop!(RenderDiarySexp, IDiarySexp); rnoop!(RenderDiarySexp, IDiarySexp);

View File

@@ -1,14 +1,12 @@
use std::path::Path;
use serde::Serialize; use serde::Serialize;
use crate::config::Config; use super::render_context::RenderContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::IDocumentElement; use crate::intermediate::IDocumentElement;
use super::macros::render;
use super::RenderHeading; use super::RenderHeading;
use super::RenderSection; use super::RenderSection;
use super::macros::render;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(untagged)] #[serde(untagged)]
@@ -21,16 +19,14 @@ render!(
RenderDocumentElement, RenderDocumentElement,
IDocumentElement, IDocumentElement,
original, original,
config, render_context,
output_directory,
output_file,
{ {
match original { match original {
IDocumentElement::Heading(inner) => Ok(RenderDocumentElement::Heading( IDocumentElement::Heading(inner) => Ok(RenderDocumentElement::Heading(
RenderHeading::new(config, output_directory, output_file, inner)?, RenderHeading::new(render_context.clone(), inner)?,
)), )),
IDocumentElement::Section(inner) => Ok(RenderDocumentElement::Section( IDocumentElement::Section(inner) => Ok(RenderDocumentElement::Section(
RenderSection::new(config, output_directory, output_file, inner)?, RenderSection::new(render_context.clone(), inner)?,
)), )),
} }
} }

View File

@@ -1,8 +1,6 @@
use std::path::Path;
use serde::Serialize; use serde::Serialize;
use crate::config::Config; use super::render_context::RenderContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::IDrawer; use crate::intermediate::IDrawer;
@@ -11,6 +9,8 @@ use super::macros::rnoop;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(tag = "type")] #[serde(tag = "type")]
#[serde(rename = "drawer")] #[serde(rename = "drawer")]
pub(crate) struct RenderDrawer {} pub(crate) struct RenderDrawer {
post_blank: organic::types::PostBlank,
}
rnoop!(RenderDrawer, IDrawer); rnoop!(RenderDrawer, IDrawer);

View File

@@ -1,8 +1,6 @@
use std::path::Path;
use serde::Serialize; use serde::Serialize;
use crate::config::Config; use super::render_context::RenderContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::IDynamicBlock; use crate::intermediate::IDynamicBlock;
@@ -11,6 +9,8 @@ use super::macros::rnoop;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(tag = "type")] #[serde(tag = "type")]
#[serde(rename = "dynamic_block")] #[serde(rename = "dynamic_block")]
pub(crate) struct RenderDynamicBlock {} pub(crate) struct RenderDynamicBlock {
post_blank: organic::types::PostBlank,
}
rnoop!(RenderDynamicBlock, IDynamicBlock); rnoop!(RenderDynamicBlock, IDynamicBlock);

View File

@@ -1,8 +1,7 @@
use std::path::Path;
use serde::Serialize; use serde::Serialize;
use crate::config::Config; use super::plain_list_simple_item::RenderPlainListSimpleItem;
use super::render_context::RenderContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::IElement; use crate::intermediate::IElement;
@@ -37,6 +36,7 @@ use super::verse_block::RenderVerseBlock;
pub(crate) enum RenderElement { pub(crate) enum RenderElement {
Paragraph(RenderParagraph), Paragraph(RenderParagraph),
PlainList(RenderPlainList), PlainList(RenderPlainList),
PlainListSimpleItem(RenderPlainListSimpleItem),
CenterBlock(RenderCenterBlock), CenterBlock(RenderCenterBlock),
QuoteBlock(RenderQuoteBlock), QuoteBlock(RenderQuoteBlock),
SpecialBlock(RenderSpecialBlock), SpecialBlock(RenderSpecialBlock),
@@ -61,132 +61,101 @@ pub(crate) enum RenderElement {
LatexEnvironment(RenderLatexEnvironment), LatexEnvironment(RenderLatexEnvironment),
} }
render!( render!(RenderElement, IElement, original, render_context, {
RenderElement,
IElement,
original,
config,
output_directory,
output_file,
{
match original { match original {
IElement::Paragraph(inner) => Ok(RenderElement::Paragraph(RenderParagraph::new( IElement::Paragraph(inner) => Ok(RenderElement::Paragraph(RenderParagraph::new(
config, render_context.clone(),
output_directory,
output_file,
inner, inner,
)?)), )?)),
IElement::PlainList(inner) => Ok(RenderElement::PlainList(RenderPlainList::new( IElement::PlainList(inner) => Ok(RenderElement::PlainList(RenderPlainList::new(
config, render_context.clone(),
output_directory,
output_file,
inner, inner,
)?)), )?)),
IElement::PlainListSimpleItem(inner) => Ok(RenderElement::PlainListSimpleItem(
RenderPlainListSimpleItem::new(render_context.clone(), inner)?,
)),
IElement::CenterBlock(inner) => Ok(RenderElement::CenterBlock(RenderCenterBlock::new( IElement::CenterBlock(inner) => Ok(RenderElement::CenterBlock(RenderCenterBlock::new(
config, render_context.clone(),
output_directory,
output_file,
inner, inner,
)?)), )?)),
IElement::QuoteBlock(inner) => Ok(RenderElement::QuoteBlock(RenderQuoteBlock::new( IElement::QuoteBlock(inner) => Ok(RenderElement::QuoteBlock(RenderQuoteBlock::new(
config, render_context.clone(),
output_directory, inner,
output_file, )?)),
IElement::SpecialBlock(inner) => Ok(RenderElement::SpecialBlock(RenderSpecialBlock::new(
render_context.clone(),
inner,
)?)),
IElement::DynamicBlock(inner) => Ok(RenderElement::DynamicBlock(RenderDynamicBlock::new(
render_context.clone(),
inner, inner,
)?)), )?)),
IElement::SpecialBlock(inner) => Ok(RenderElement::SpecialBlock(
RenderSpecialBlock::new(config, output_directory, output_file, inner)?,
)),
IElement::DynamicBlock(inner) => Ok(RenderElement::DynamicBlock(
RenderDynamicBlock::new(config, output_directory, output_file, inner)?,
)),
IElement::FootnoteDefinition(inner) => Ok(RenderElement::FootnoteDefinition( IElement::FootnoteDefinition(inner) => Ok(RenderElement::FootnoteDefinition(
RenderFootnoteDefinition::new(config, output_directory, output_file, inner)?, RenderFootnoteDefinition::new(render_context.clone(), inner)?,
)), )),
IElement::Comment(inner) => Ok(RenderElement::Comment(RenderComment::new( IElement::Comment(inner) => Ok(RenderElement::Comment(RenderComment::new(
config, render_context.clone(),
output_directory,
output_file,
inner, inner,
)?)), )?)),
IElement::Drawer(inner) => Ok(RenderElement::Drawer(RenderDrawer::new( IElement::Drawer(inner) => Ok(RenderElement::Drawer(RenderDrawer::new(
config, render_context.clone(),
output_directory,
output_file,
inner, inner,
)?)), )?)),
IElement::PropertyDrawer(inner) => Ok(RenderElement::PropertyDrawer( IElement::PropertyDrawer(inner) => Ok(RenderElement::PropertyDrawer(
RenderPropertyDrawer::new(config, output_directory, output_file, inner)?, RenderPropertyDrawer::new(render_context.clone(), inner)?,
)), )),
IElement::Table(inner) => Ok(RenderElement::Table(RenderTable::new( IElement::Table(inner) => Ok(RenderElement::Table(RenderTable::new(
config, render_context.clone(),
output_directory,
output_file,
inner, inner,
)?)), )?)),
IElement::VerseBlock(inner) => Ok(RenderElement::VerseBlock(RenderVerseBlock::new( IElement::VerseBlock(inner) => Ok(RenderElement::VerseBlock(RenderVerseBlock::new(
config, render_context.clone(),
output_directory, inner,
output_file, )?)),
IElement::CommentBlock(inner) => Ok(RenderElement::CommentBlock(RenderCommentBlock::new(
render_context.clone(),
inner,
)?)),
IElement::ExampleBlock(inner) => Ok(RenderElement::ExampleBlock(RenderExampleBlock::new(
render_context.clone(),
inner, inner,
)?)), )?)),
IElement::CommentBlock(inner) => Ok(RenderElement::CommentBlock(
RenderCommentBlock::new(config, output_directory, output_file, inner)?,
)),
IElement::ExampleBlock(inner) => Ok(RenderElement::ExampleBlock(
RenderExampleBlock::new(config, output_directory, output_file, inner)?,
)),
IElement::ExportBlock(inner) => Ok(RenderElement::ExportBlock(RenderExportBlock::new( IElement::ExportBlock(inner) => Ok(RenderElement::ExportBlock(RenderExportBlock::new(
config, render_context.clone(),
output_directory,
output_file,
inner, inner,
)?)), )?)),
IElement::SrcBlock(inner) => Ok(RenderElement::SrcBlock(RenderSrcBlock::new( IElement::SrcBlock(inner) => Ok(RenderElement::SrcBlock(RenderSrcBlock::new(
config, render_context.clone(),
output_directory,
output_file,
inner, inner,
)?)), )?)),
IElement::Clock(inner) => Ok(RenderElement::Clock(RenderClock::new( IElement::Clock(inner) => Ok(RenderElement::Clock(RenderClock::new(
config, render_context.clone(),
output_directory,
output_file,
inner, inner,
)?)), )?)),
IElement::DiarySexp(inner) => Ok(RenderElement::DiarySexp(RenderDiarySexp::new( IElement::DiarySexp(inner) => Ok(RenderElement::DiarySexp(RenderDiarySexp::new(
config, render_context.clone(),
output_directory,
output_file,
inner, inner,
)?)), )?)),
IElement::Planning(inner) => Ok(RenderElement::Planning(RenderPlanning::new( IElement::Planning(inner) => Ok(RenderElement::Planning(RenderPlanning::new(
config, render_context.clone(),
output_directory,
output_file,
inner, inner,
)?)), )?)),
IElement::FixedWidthArea(inner) => Ok(RenderElement::FixedWidthArea( IElement::FixedWidthArea(inner) => Ok(RenderElement::FixedWidthArea(
RenderFixedWidthArea::new(config, output_directory, output_file, inner)?, RenderFixedWidthArea::new(render_context.clone(), inner)?,
)), )),
IElement::HorizontalRule(inner) => Ok(RenderElement::HorizontalRule( IElement::HorizontalRule(inner) => Ok(RenderElement::HorizontalRule(
RenderHorizontalRule::new(config, output_directory, output_file, inner)?, RenderHorizontalRule::new(render_context.clone(), inner)?,
)), )),
IElement::Keyword(inner) => Ok(RenderElement::Keyword(RenderKeyword::new( IElement::Keyword(inner) => Ok(RenderElement::Keyword(RenderKeyword::new(
config, render_context.clone(),
output_directory,
output_file,
inner, inner,
)?)), )?)),
IElement::BabelCall(inner) => Ok(RenderElement::BabelCall(RenderBabelCall::new( IElement::BabelCall(inner) => Ok(RenderElement::BabelCall(RenderBabelCall::new(
config, render_context.clone(),
output_directory,
output_file,
inner, inner,
)?)), )?)),
IElement::LatexEnvironment(inner) => Ok(RenderElement::LatexEnvironment( IElement::LatexEnvironment(inner) => Ok(RenderElement::LatexEnvironment(
RenderLatexEnvironment::new(config, output_directory, output_file, inner)?, RenderLatexEnvironment::new(render_context.clone(), inner)?,
)), )),
} }
} });
);

View File

@@ -1,8 +1,6 @@
use std::path::Path;
use serde::Serialize; use serde::Serialize;
use crate::config::Config; use super::render_context::RenderContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::IEntity; use crate::intermediate::IEntity;
@@ -13,18 +11,12 @@ use super::macros::render;
#[serde(rename = "entity")] #[serde(rename = "entity")]
pub(crate) struct RenderEntity { pub(crate) struct RenderEntity {
html: String, html: String,
post_blank: organic::types::PostBlank,
} }
render!( render!(RenderEntity, IEntity, original, _render_context, {
RenderEntity,
IEntity,
original,
config,
output_directory,
output_file,
{
Ok(RenderEntity { Ok(RenderEntity {
html: original.html.clone(), html: original.html.clone(),
post_blank: original.post_blank,
}) })
} });
);

View File

@@ -1,8 +1,6 @@
use std::path::Path;
use serde::Serialize; use serde::Serialize;
use crate::config::Config; use super::render_context::RenderContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::IExampleBlock; use crate::intermediate::IExampleBlock;
@@ -11,6 +9,8 @@ use super::macros::rnoop;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(tag = "type")] #[serde(tag = "type")]
#[serde(rename = "example_block")] #[serde(rename = "example_block")]
pub(crate) struct RenderExampleBlock {} pub(crate) struct RenderExampleBlock {
post_blank: organic::types::PostBlank,
}
rnoop!(RenderExampleBlock, IExampleBlock); rnoop!(RenderExampleBlock, IExampleBlock);

View File

@@ -1,8 +1,6 @@
use std::path::Path;
use serde::Serialize; use serde::Serialize;
use crate::config::Config; use super::render_context::RenderContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::IExportBlock; use crate::intermediate::IExportBlock;
@@ -11,6 +9,8 @@ use super::macros::rnoop;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(tag = "type")] #[serde(tag = "type")]
#[serde(rename = "export_block")] #[serde(rename = "export_block")]
pub(crate) struct RenderExportBlock {} pub(crate) struct RenderExportBlock {
post_blank: organic::types::PostBlank,
}
rnoop!(RenderExportBlock, IExportBlock); rnoop!(RenderExportBlock, IExportBlock);

View File

@@ -1,8 +1,6 @@
use std::path::Path;
use serde::Serialize; use serde::Serialize;
use crate::config::Config; use super::render_context::RenderContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::IExportSnippet; use crate::intermediate::IExportSnippet;
@@ -11,6 +9,8 @@ use super::macros::rnoop;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(tag = "type")] #[serde(tag = "type")]
#[serde(rename = "export_snippet")] #[serde(rename = "export_snippet")]
pub(crate) struct RenderExportSnippet {} pub(crate) struct RenderExportSnippet {
post_blank: organic::types::PostBlank,
}
rnoop!(RenderExportSnippet, IExportSnippet); rnoop!(RenderExportSnippet, IExportSnippet);

View File

@@ -1,8 +1,6 @@
use std::path::Path;
use serde::Serialize; use serde::Serialize;
use crate::config::Config; use super::render_context::RenderContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::IFixedWidthArea; use crate::intermediate::IFixedWidthArea;
@@ -11,6 +9,8 @@ use super::macros::rnoop;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(tag = "type")] #[serde(tag = "type")]
#[serde(rename = "fixed_width_area")] #[serde(rename = "fixed_width_area")]
pub(crate) struct RenderFixedWidthArea {} pub(crate) struct RenderFixedWidthArea {
post_blank: organic::types::PostBlank,
}
rnoop!(RenderFixedWidthArea, IFixedWidthArea); rnoop!(RenderFixedWidthArea, IFixedWidthArea);

View File

@@ -1,8 +1,6 @@
use std::path::Path;
use serde::Serialize; use serde::Serialize;
use crate::config::Config; use super::render_context::RenderContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::IFootnoteDefinition; use crate::intermediate::IFootnoteDefinition;
use crate::intermediate::IRealFootnoteDefinition; use crate::intermediate::IRealFootnoteDefinition;
@@ -15,39 +13,40 @@ use super::macros::rnoop;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(tag = "type")] #[serde(tag = "type")]
#[serde(rename = "footnote_definition")] #[serde(rename = "footnote_definition")]
pub(crate) struct RenderFootnoteDefinition {} pub(crate) struct RenderFootnoteDefinition {
post_blank: organic::types::PostBlank,
}
rnoop!(RenderFootnoteDefinition, IFootnoteDefinition); rnoop!(RenderFootnoteDefinition, IFootnoteDefinition);
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(tag = "type")] #[serde(tag = "type")]
#[serde(rename = "footnote_reference")] #[serde(rename = "real_footnote_definition")]
pub(crate) struct RenderRealFootnoteDefinition { pub(crate) struct RenderRealFootnoteDefinition {
definition_id: String, definition_id: String,
reference_link: String, reference_link: String,
label: String, label: String,
contents: Vec<RenderAstNode>, contents: Vec<RenderAstNode>,
// TODO: Do I need post_blank for real footnote definitions?
} }
render!( render!(
RenderRealFootnoteDefinition, RenderRealFootnoteDefinition,
IRealFootnoteDefinition, IRealFootnoteDefinition,
original, original,
config, render_context,
output_directory,
output_file,
{ {
let contents = { let contents = {
let mut ret = Vec::new(); let mut ret = Vec::new();
for obj in original.contents.iter() { for obj in original.contents.iter() {
ret.push(obj.into_render_ast_node(config, output_directory, output_file)?); ret.push(obj.as_render_ast_node(render_context.clone())?);
} }
ret ret
}; };
Ok(RenderRealFootnoteDefinition { Ok(RenderRealFootnoteDefinition {
definition_id: original.get_definition_id(), definition_id: original.get_definition_id(render_context.id_addition),
reference_link: format!("#{}", original.get_reference_id()), reference_link: format!("#{}", original.get_reference_id(render_context.id_addition)),
label: original.get_display_label(), label: original.get_display_label(),
contents, contents,
}) })

View File

@@ -1,8 +1,6 @@
use std::path::Path;
use serde::Serialize; use serde::Serialize;
use crate::config::Config; use super::render_context::RenderContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::IFootnoteReference; use crate::intermediate::IFootnoteReference;
@@ -15,20 +13,23 @@ pub(crate) struct RenderFootnoteReference {
reference_id: String, reference_id: String,
definition_link: String, definition_link: String,
label: String, label: String,
post_blank: organic::types::PostBlank,
} }
render!( render!(
RenderFootnoteReference, RenderFootnoteReference,
IFootnoteReference, IFootnoteReference,
original, original,
config, render_context,
output_directory,
output_file,
{ {
Ok(RenderFootnoteReference { Ok(RenderFootnoteReference {
reference_id: original.get_reference_id(), reference_id: original.get_reference_id(render_context.id_addition),
definition_link: format!("#{}", original.get_definition_id()), definition_link: format!(
"#{}",
original.get_definition_id(render_context.id_addition)
),
label: original.get_display_label(), label: original.get_display_label(),
post_blank: original.post_blank,
}) })
} }
); );

View File

@@ -1,14 +1,12 @@
use std::path::Path;
use serde::Serialize; use serde::Serialize;
use crate::config::Config; use super::render_context::RenderContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::IHeading; use crate::intermediate::IHeading;
use super::macros::render;
use super::RenderDocumentElement; use super::RenderDocumentElement;
use super::RenderObject; use super::RenderObject;
use super::macros::render;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(tag = "type")] #[serde(tag = "type")]
@@ -17,25 +15,14 @@ pub(crate) struct RenderHeading {
level: organic::types::HeadlineLevel, level: organic::types::HeadlineLevel,
title: Vec<RenderObject>, title: Vec<RenderObject>,
children: Vec<RenderDocumentElement>, children: Vec<RenderDocumentElement>,
post_blank: organic::types::PostBlank,
} }
render!( render!(RenderHeading, IHeading, original, render_context, {
RenderHeading,
IHeading,
original,
config,
output_directory,
output_file,
{
let title = { let title = {
let mut ret = Vec::new(); let mut ret = Vec::new();
for obj in original.title.iter() { for obj in original.title.iter() {
ret.push(RenderObject::new( ret.push(RenderObject::new(render_context.clone(), obj)?);
config,
output_directory,
output_file,
obj,
)?);
} }
ret ret
}; };
@@ -43,12 +30,7 @@ render!(
let children = { let children = {
let mut ret = Vec::new(); let mut ret = Vec::new();
for obj in original.children.iter() { for obj in original.children.iter() {
ret.push(RenderDocumentElement::new( ret.push(RenderDocumentElement::new(render_context.clone(), obj)?);
config,
output_directory,
output_file,
obj,
)?);
} }
ret ret
}; };
@@ -57,6 +39,6 @@ render!(
level: original.level + 1, // Adding 1 because the page title is going to be h1. level: original.level + 1, // Adding 1 because the page title is going to be h1.
title, title,
children, children,
post_blank: original.post_blank,
}) })
} });
);

View File

@@ -1,8 +1,6 @@
use std::path::Path;
use serde::Serialize; use serde::Serialize;
use crate::config::Config; use super::render_context::RenderContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::IHorizontalRule; use crate::intermediate::IHorizontalRule;
@@ -11,6 +9,8 @@ use super::macros::rnoop;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(tag = "type")] #[serde(tag = "type")]
#[serde(rename = "horizontal_rule")] #[serde(rename = "horizontal_rule")]
pub(crate) struct RenderHorizontalRule {} pub(crate) struct RenderHorizontalRule {
post_blank: organic::types::PostBlank,
}
rnoop!(RenderHorizontalRule, IHorizontalRule); rnoop!(RenderHorizontalRule, IHorizontalRule);

View File

@@ -1,8 +1,6 @@
use std::path::Path;
use serde::Serialize; use serde::Serialize;
use crate::config::Config; use super::render_context::RenderContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::IInlineBabelCall; use crate::intermediate::IInlineBabelCall;
@@ -11,6 +9,8 @@ use super::macros::rnoop;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(tag = "type")] #[serde(tag = "type")]
#[serde(rename = "inline_babel_call")] #[serde(rename = "inline_babel_call")]
pub(crate) struct RenderInlineBabelCall {} pub(crate) struct RenderInlineBabelCall {
post_blank: organic::types::PostBlank,
}
rnoop!(RenderInlineBabelCall, IInlineBabelCall); rnoop!(RenderInlineBabelCall, IInlineBabelCall);

View File

@@ -1,8 +1,6 @@
use std::path::Path;
use serde::Serialize; use serde::Serialize;
use crate::config::Config; use super::render_context::RenderContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::IInlineSourceBlock; use crate::intermediate::IInlineSourceBlock;
@@ -13,18 +11,18 @@ use super::macros::render;
#[serde(rename = "inline_source_block")] #[serde(rename = "inline_source_block")]
pub(crate) struct RenderInlineSourceBlock { pub(crate) struct RenderInlineSourceBlock {
value: String, value: String,
post_blank: organic::types::PostBlank,
} }
render!( render!(
RenderInlineSourceBlock, RenderInlineSourceBlock,
IInlineSourceBlock, IInlineSourceBlock,
original, original,
config, _render_context,
output_directory,
output_file,
{ {
Ok(RenderInlineSourceBlock { Ok(RenderInlineSourceBlock {
value: original.value.clone(), value: original.value.clone(),
post_blank: original.post_blank,
}) })
} }
); );

View File

@@ -1,16 +1,31 @@
use std::path::Path;
use serde::Serialize; use serde::Serialize;
use crate::config::Config; use super::render_context::RenderContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::IItalic; use crate::intermediate::IItalic;
use super::macros::rnoop; use super::RenderObject;
use super::macros::render;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(tag = "type")] #[serde(tag = "type")]
#[serde(rename = "italic")] #[serde(rename = "italic")]
pub(crate) struct RenderItalic {} pub(crate) struct RenderItalic {
children: Vec<RenderObject>,
post_blank: organic::types::PostBlank,
}
rnoop!(RenderItalic, IItalic); render!(RenderItalic, IItalic, original, render_context, {
let children = {
let mut ret = Vec::new();
for obj in original.children.iter() {
ret.push(RenderObject::new(render_context.clone(), obj)?);
}
ret
};
Ok(RenderItalic {
children,
post_blank: original.post_blank,
})
});

View File

@@ -1,8 +1,6 @@
use std::path::Path;
use serde::Serialize; use serde::Serialize;
use crate::config::Config; use super::render_context::RenderContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::IKeyword; use crate::intermediate::IKeyword;
@@ -11,6 +9,8 @@ use super::macros::rnoop;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(tag = "type")] #[serde(tag = "type")]
#[serde(rename = "keyword")] #[serde(rename = "keyword")]
pub(crate) struct RenderKeyword {} pub(crate) struct RenderKeyword {
post_blank: organic::types::PostBlank,
}
rnoop!(RenderKeyword, IKeyword); rnoop!(RenderKeyword, IKeyword);

View File

@@ -1,8 +1,6 @@
use std::path::Path;
use serde::Serialize; use serde::Serialize;
use crate::config::Config; use super::render_context::RenderContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::ILatexEnvironment; use crate::intermediate::ILatexEnvironment;
@@ -11,6 +9,8 @@ use super::macros::rnoop;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(tag = "type")] #[serde(tag = "type")]
#[serde(rename = "latex_environment")] #[serde(rename = "latex_environment")]
pub(crate) struct RenderLatexEnvironment {} pub(crate) struct RenderLatexEnvironment {
post_blank: organic::types::PostBlank,
}
rnoop!(RenderLatexEnvironment, ILatexEnvironment); rnoop!(RenderLatexEnvironment, ILatexEnvironment);

View File

@@ -1,8 +1,6 @@
use std::path::Path;
use serde::Serialize; use serde::Serialize;
use crate::config::Config; use super::render_context::RenderContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::ILatexFragment; use crate::intermediate::ILatexFragment;
@@ -13,18 +11,18 @@ use super::macros::render;
#[serde(rename = "latex_fragment")] #[serde(rename = "latex_fragment")]
pub(crate) struct RenderLatexFragment { pub(crate) struct RenderLatexFragment {
value: String, value: String,
post_blank: organic::types::PostBlank,
} }
render!( render!(
RenderLatexFragment, RenderLatexFragment,
ILatexFragment, ILatexFragment,
original, original,
_config, _render_context,
_output_directory,
_output_file,
{ {
Ok(RenderLatexFragment { Ok(RenderLatexFragment {
value: original.value.clone(), value: original.value.clone(),
post_blank: original.post_blank,
}) })
} }
); );

View File

@@ -1,8 +1,6 @@
use std::path::Path;
use serde::Serialize; use serde::Serialize;
use crate::config::Config; use super::render_context::RenderContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::intermediate::ILineBreak; use crate::intermediate::ILineBreak;
@@ -11,6 +9,8 @@ use super::macros::rnoop;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(tag = "type")] #[serde(tag = "type")]
#[serde(rename = "line_break")] #[serde(rename = "line_break")]
pub(crate) struct RenderLineBreak {} pub(crate) struct RenderLineBreak {
post_blank: organic::types::PostBlank,
}
rnoop!(RenderLineBreak, ILineBreak); rnoop!(RenderLineBreak, ILineBreak);

View File

@@ -2,18 +2,12 @@
/// ///
/// This exists to make changing the type signature easier. /// This exists to make changing the type signature easier.
macro_rules! render { macro_rules! render {
($rstruct:ident, $istruct:ident, $original:ident, $config:ident, $output_directory:ident, $output_file:ident, $fnbody:tt) => { ($rstruct:ident, $istruct:ident, $original:ident, $render_context:ident, $fnbody:tt) => {
impl $rstruct { impl $rstruct {
pub(crate) fn new( pub(crate) fn new(
config: &Config, $render_context: RenderContext<'_>,
output_directory: &Path, $original: &$istruct,
output_file: &Path,
original: &$istruct,
) -> Result<$rstruct, CustomError> { ) -> Result<$rstruct, CustomError> {
let $original = original;
let $config = config;
let $output_directory = output_directory;
let $output_file = output_file;
$fnbody $fnbody
} }
} }
@@ -29,15 +23,35 @@ macro_rules! rnoop {
($rstruct:ident, $istruct:ident) => { ($rstruct:ident, $istruct:ident) => {
impl $rstruct { impl $rstruct {
pub(crate) fn new( pub(crate) fn new(
_config: &Config, _render_context: RenderContext<'_>,
_output_directory: &Path, original: &$istruct,
_output_file: &Path,
_original: &$istruct,
) -> Result<$rstruct, CustomError> { ) -> Result<$rstruct, CustomError> {
Ok($rstruct {}) Ok($rstruct {
post_blank: original.post_blank,
})
} }
} }
}; };
} }
pub(crate) use rnoop; pub(crate) use rnoop;
/// Push a file onto the render DependencyManager's file stack while inside the code block.
macro_rules! push_file {
($render_context:ident, $path:expr, $body:tt) => {{
$render_context
.dependency_manager
.lock()
.unwrap()
.push_file($path)?;
let ret = (|| $body)();
$render_context
.dependency_manager
.lock()
.unwrap()
.pop_file()?;
ret
}};
}
pub(crate) use push_file;

View File

@@ -2,6 +2,7 @@ mod angle_link;
mod ast_node; mod ast_node;
mod babel_call; mod babel_call;
mod blog_post_page; mod blog_post_page;
mod blog_stream;
mod bold; mod bold;
mod center_block; mod center_block;
mod citation; mod citation;
@@ -10,6 +11,8 @@ mod clock;
mod code; mod code;
mod comment; mod comment;
mod comment_block; mod comment_block;
mod dependency;
mod dependency_manager;
mod diary_sexp; mod diary_sexp;
mod document_element; mod document_element;
mod drawer; mod drawer;
@@ -35,10 +38,13 @@ mod line_break;
mod macros; mod macros;
mod object; mod object;
mod org_macro; mod org_macro;
mod page;
mod page_header;
mod paragraph; mod paragraph;
mod plain_link; mod plain_link;
mod plain_list; mod plain_list;
mod plain_list_item; mod plain_list_item;
mod plain_list_simple_item;
mod plain_text; mod plain_text;
mod planning; mod planning;
mod property_drawer; mod property_drawer;
@@ -46,6 +52,7 @@ mod quote_block;
mod radio_link; mod radio_link;
mod radio_target; mod radio_target;
mod regular_link; mod regular_link;
mod render_context;
mod section; mod section;
mod special_block; mod special_block;
mod src_block; mod src_block;
@@ -54,6 +61,9 @@ mod strike_through;
mod subscript; mod subscript;
mod superscript; mod superscript;
mod table; mod table;
mod table_cell;
mod table_group;
mod table_row;
mod target; mod target;
mod timestamp; mod timestamp;
mod underline; mod underline;
@@ -61,10 +71,17 @@ mod verbatim;
mod verse_block; mod verse_block;
pub(crate) use blog_post_page::RenderBlogPostPage; pub(crate) use blog_post_page::RenderBlogPostPage;
pub(crate) use blog_post_page::RenderBlogPostPageInput;
pub(crate) use blog_stream::RenderBlogStream;
pub(crate) use blog_stream::RenderBlogStreamInput;
pub(crate) use dependency_manager::DependencyManager;
pub(crate) use document_element::RenderDocumentElement; pub(crate) use document_element::RenderDocumentElement;
pub(crate) use element::RenderElement; pub(crate) use element::RenderElement;
pub(crate) use footnote_definition::RenderRealFootnoteDefinition; pub(crate) use footnote_definition::RenderRealFootnoteDefinition;
pub(crate) use global_settings::GlobalSettings; pub(crate) use global_settings::GlobalSettings;
pub(crate) use heading::RenderHeading; pub(crate) use heading::RenderHeading;
pub(crate) use object::RenderObject; pub(crate) use object::RenderObject;
pub(crate) use page::RenderPage;
pub(crate) use page_header::PageHeader;
pub(crate) use render_context::RenderContext;
pub(crate) use section::RenderSection; pub(crate) use section::RenderSection;

Some files were not shown because too many files have changed in this diff Show More