Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Try Hypertext #32

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
638 changes: 361 additions & 277 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ edition = "2021"
rouille = { version = "3.6", default-features = false }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
maud = "0.25"
chrono = { version = "0.4", default-features = false, features = ["clock"] }
ureq = { version = "2.7", features = ["json", "tls", "rustls"] }
rustls = { version = "0.21", default-features = false }
rustls-native-certs = "0.6"
pico-args = "0.5.0"
tl = "0.7.7"
hypertext = "0.4"

[build-dependencies]
grass = "0.13"
Expand Down
4 changes: 2 additions & 2 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::process::Command;

fn main() {
let output = Command::new("git")
.args(&["rev-parse", "HEAD"])
.args(["rev-parse", "HEAD"])
.output()
.unwrap();
let git_hash = String::from_utf8(output.stdout).unwrap();
Expand All @@ -30,5 +30,5 @@ fn main() {

let css = grass::from_path(src, &options).unwrap();
let dst = out_dir.join(dst);
fs::write(dst, &css).unwrap();
fs::write(dst, css).unwrap();
}
18 changes: 10 additions & 8 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ mod render;
mod routes;

use api::error::ApiError;
use hypertext::{html_elements, maud, GlobalAttributes, Renderable};
use routes::{
about::render_about,
article::render_article,
Expand All @@ -15,8 +16,8 @@ const CSS: &str = include_str!(concat!(env!("OUT_DIR"), "/main.css"));

macro_rules! document {
($title:expr, $content:expr, $( $head:expr )? ) => {
maud::html! {
(maud::DOCTYPE)
maud! {
!DOCTYPE
html lang="en" {
head {
title { ($title) }
Expand All @@ -35,8 +36,9 @@ macro_rules! document {
}
}
}
};
}
}

pub(crate) use document;

fn main() {
Expand Down Expand Up @@ -145,19 +147,19 @@ fn main() {
}

fn render_error(code: u16, message: &str, path: &str) -> rouille::Response {
let title = format!("{} - {}", code, message);
let title = maud!((code) " - " (message));

let doc = document!(
&title,
maud::html! {
h1 { (&title) }
title,
maud! {
h1 { (title) }
p { "You tried to access \"" (path) "\"" }
p { a href="/" { "Go home" } }
p { a href=(path) { "Try again" } }
},
);

rouille::Response::html(doc.into_string()).with_status_code(code)
rouille::Response::html(doc.render()).with_status_code(code)
}

fn render_api_error(err: &ApiError, path: &str) -> rouille::Response {
Expand Down
51 changes: 25 additions & 26 deletions src/render/byline.rs
Original file line number Diff line number Diff line change
@@ -1,35 +1,34 @@
use crate::api::common::Topic;
use std::fmt::Write;
use hypertext::{maud, Renderable};

pub fn render_byline(authors: &[Topic]) -> String {
match authors.len() {
0 => "".to_string(),
1 => format_author(&authors[0]),
author_count => {
/* Chain author names together */
let mut byline = "By ".to_string();
use crate::api::common::Topic;
use hypertext::html_elements;

for author in authors[..author_count - 2].iter() {
byline.push_str(&format_author(author));
byline.push_str(", ");
pub fn render_byline(authors: &[Topic]) -> impl Renderable + '_ {
maud! {
@match authors.len() {
0 => {},
1 => (format_author(&authors[0])),
author_count => {
"By "
@for author in &authors[..author_count - 2] {
(format_author(author)) ", "
}
(format_author(&authors[author_count - 2]))
" and "
(format_author(&authors[author_count - 1]))
}

let _ = write!(
byline,
"{} and {}",
format_author(&authors[author_count - 2]),
format_author(&authors[author_count - 1])
);

byline
}
}
}

pub fn format_author(author: &Topic) -> String {
if let Some(url) = &author.topic_url {
format!("<a href=\"{}\">{}</a>", url, author.byline)
} else {
author.byline.clone()
pub fn format_author(author: &Topic) -> impl Renderable + '_ {
maud! {
@if let Some(url) = &author.topic_url {
a href=(url) {
(&author.byline)
}
} @else {
(&author.byline)
}
}
}
55 changes: 27 additions & 28 deletions src/render/legacy_article_byline.rs
Original file line number Diff line number Diff line change
@@ -1,38 +1,37 @@
use std::fmt::Write;
use hypertext::{html_elements, maud, Renderable};

use crate::api::legacy_article::LegacyArticleAuthor;

pub fn render_byline(authors: &[LegacyArticleAuthor]) -> String {
match authors.len() {
0 => "".to_string(),
1 => format_author(&authors[0]),
author_count => {
/* Chain author names together */
let mut byline = "By ".to_string();

for author in authors[..author_count - 2].iter() {
byline.push_str(&format_author(author));
byline.push_str(", ");
pub fn render_byline(authors: &[LegacyArticleAuthor]) -> impl Renderable + '_ {
maud! {
@match authors.len() {
0 => {},
1 => (format_author(&authors[0])),
author_count => {
"By "
@for author in &authors[..author_count - 2] {
(format_author(author)) ", "
}
(format_author(&authors[author_count - 2]))
" and "
(format_author(&authors[author_count - 1]))
}

let _ = write!(
byline,
"{} and {}",
format_author(&authors[author_count - 2]),
format_author(&authors[author_count - 1])
);

byline
}
}
}

pub fn format_author(author: &LegacyArticleAuthor) -> String {
match author
.url
.strip_prefix("https://www.reuters.com/journalists/")
{
Some(path) => format!("<a href=\"/authors/{}/\">{}</a>", path, author.name),
None => format!("<a href=\"{}\">{}</a>", author.url, author.name),
pub fn format_author(author: &LegacyArticleAuthor) -> impl Renderable + '_ {
maud! {
a href=@if let Some(path) = author
.url
.strip_prefix("https://www.reuters.com/journalists/")
{
"/journalists/" (path) "/"
} @else {
(&author.url)
}
{
(&author.name)
}
}
}
9 changes: 4 additions & 5 deletions src/routes/about.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
use crate::api::error::ApiResult;
use crate::document;
use maud::html;
use hypertext::{html_elements, maud, GlobalAttributes, Renderable};
use std::env;

const GIT_HASH: &str = env!("GIT_HASH");

pub fn render_about() -> ApiResult<String> {
let doc = document!(
"About",
html! {
maud! {
h1 { "About" }
p { "This is an alternative frontend to " a href="https://www.reuters.com/" { "Reuters" } ". It is intented to be lightweight, fast and was heavily inspired by " a href="https://nitter.net/" { "Nitter" } "." }
ul {
Expand All @@ -33,9 +32,9 @@ pub fn render_about() -> ApiResult<String> {
p { "This project is licensed under the " a href="https://www.gnu.org/licenses/licenses.html#AGPL" { "GNU Affero General Public License" } "." }

h2 { "Build information" }
p { "This version is based off the git commit " a href=(format!("https://github.com/HookedBehemoth/neuters/commit/{}", GIT_HASH)) { (GIT_HASH) }}
p { "This version is based off the git commit " a href={ "https://github.com/HookedBehemoth/neuters/commit/" (GIT_HASH) } { (GIT_HASH) }}
},
);

Ok(doc.into_string())
Ok(doc.render().into_inner())
}
28 changes: 15 additions & 13 deletions src/routes/article.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use crate::{
api::{article::fetch_article_by_url, error::ApiResult},
render::byline,
routes::HtmxAttributes,
};
use chrono::{DateTime, Utc};
use maud::{html, PreEscaped};
use hypertext::{html_elements, maud, maud_move, GlobalAttributes, Raw, Renderable};

pub fn render_article(client: &ureq::Agent, path: &str) -> ApiResult<String> {
let article = fetch_article_by_url(client, path)?;
Expand All @@ -15,32 +16,33 @@ pub fn render_article(client: &ureq::Agent, path: &str) -> ApiResult<String> {

let doc = crate::document!(
&article.title,
html!(
maud!(
h1 { (&article.title) }
p class="byline" {
@if let Some(authors) = &article.authors {
@let byline = byline::render_byline(authors);
@if let Ok(time) = published_time {
@if let Ok(time) = &published_time {
(time) " - "
}
(PreEscaped(byline))
(byline::render_byline(authors))
}
}
(render_items(&article.content_elements.unwrap_or_default()))
@if let Some(articles) = &article.content_elements {
(render_items(articles))
}
),
html! {
maud! {
meta property="og:title" content=(&article.title);
meta property="og:type" content="article";
meta property="og:description" content=(&article.description);
meta property="og:url" content=(path);
}
);

Ok(doc.into_string())
Ok(doc.render().into_inner())
}

fn render_items(items: &[serde_json::Value]) -> maud::Markup {
html! {
fn render_items(items: &[serde_json::Value]) -> impl Renderable + '_ {
maud_move! {
@for content in items {
@match content["type"].as_str() {
Some("header") => {
Expand All @@ -54,7 +56,7 @@ fn render_items(items: &[serde_json::Value]) -> maud::Markup {
}
Some("paragraph") => {
@if let Some(content) = content["content"].as_str() {
p { (PreEscaped(&content)) }
p { (Raw(&content)) }
}
}
Some("image") => {
Expand Down Expand Up @@ -94,7 +96,7 @@ fn render_items(items: &[serde_json::Value]) -> maud::Markup {
tr {
@let cells = match row.as_array() { Some(cells) => cells, None => continue };
@for cell in cells {
td { (PreEscaped(cell.as_str().unwrap_or_default())) }
td { (Raw(cell.as_str().unwrap_or_default())) }
}
}
}
Expand All @@ -113,7 +115,7 @@ fn render_items(items: &[serde_json::Value]) -> maud::Markup {
} else {
markup
};
(maud::PreEscaped(embed))
(Raw(embed))
}
}
Some(unknown) => { p { "Unknown type: " (unknown) } }
Expand Down
16 changes: 8 additions & 8 deletions src/routes/internet_news.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
use chrono::{DateTime, Utc};
use maud::{html, PreEscaped};
use hypertext::{html_elements, maud, GlobalAttributes, Renderable};

use crate::{
api::{
error::{ApiError, ApiResult},
legacy_article::{fetch_legacy_article, parse_legacy_article},
},
render::legacy_article_byline::render_byline,
routes::HtmxAttributes,
};

pub fn render_legacy_article(
Expand Down Expand Up @@ -58,20 +59,19 @@ pub fn render_legacy_article(

let doc = crate::document!(
&article.headline,
html! {
maud! {
h1 { (&article.headline) }
p class="byline" {
@let byline = render_byline(&article.authors);
@if let Ok(time) = published_time {
@if let Ok(time) = &published_time {
(time) " - "
}
(PreEscaped(byline))
(render_byline(&article.authors))
}
@for content in article.body_items.iter() {
@match content.r#type.as_str() {
"paragraph" => {
p {
(content.content)
(&content.content)
}
}
t => {
Expand All @@ -82,13 +82,13 @@ pub fn render_legacy_article(
}
}
},
html! {
maud! {
meta property="og:title" content=(&article.headline);
meta property="og:type" content="article";
meta property="og:description" content=(&article.description);
meta property="og:url" content=(path);
}
);

Ok(Ok(doc.into_string()))
Ok(Ok(doc.render().into_inner()))
}
Loading