From 8543d2d61fbafde6222e9b95f9604b41570db477 Mon Sep 17 00:00:00 2001 From: cyqsimon <28627918+cyqsimon@users.noreply.github.com> Date: Wed, 20 Jul 2022 16:26:08 +0800 Subject: Switch to `qrcode` lib --- src/consts.rs | 4 ++++ src/listing.rs | 41 +++++------------------------------------ src/main.rs | 52 +++++++++++++++++++++++++--------------------------- src/renderer.rs | 40 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 74 insertions(+), 63 deletions(-) create mode 100644 src/consts.rs (limited to 'src') diff --git a/src/consts.rs b/src/consts.rs new file mode 100644 index 0000000..07f47b0 --- /dev/null +++ b/src/consts.rs @@ -0,0 +1,4 @@ +use qrcode::EcLevel; + +/// The error correction level to use for all QR code generation. +pub const QR_EC_LEVEL: EcLevel = EcLevel::M; diff --git a/src/listing.rs b/src/listing.rs index 82b4cdb..f90c40c 100644 --- a/src/listing.rs +++ b/src/listing.rs @@ -9,14 +9,14 @@ use actix_web::{HttpMessage, HttpRequest, HttpResponse}; use bytesize::ByteSize; use comrak::{markdown_to_html, ComrakOptions}; use percent_encoding::{percent_decode_str, utf8_percent_encode}; -use qrcodegen::{QrCode, QrCodeEcc}; +use qrcode::QrCode; use serde::Deserialize; use strum_macros::{Display, EnumString}; use crate::archive::ArchiveMethod; use crate::auth::CurrentUser; use crate::errors::{self, ContextualError}; -use crate::renderer; +use crate::{consts, renderer}; use self::percent_encode_sets::PATH_SEGMENT; @@ -220,10 +220,10 @@ pub fn directory_listing( // If the `qrcode` parameter is included in the url, then should respond to the QR code if let Some(url) = query_params.qrcode { - let res = match QrCode::encode_text(&url, QrCodeEcc::Medium) { + let res = match QrCode::with_error_correction_level(url, consts::QR_EC_LEVEL) { Ok(qr) => HttpResponse::Ok() - .append_header(("Content-Type", "image/svg+xml")) - .body(qr_to_svg_string(&qr, 2)), + .content_type("text/html; charset=utf-8") + .body(renderer::qr_code_page(&qr).into_string()), Err(err) => { log::error!("URL is invalid (too long?): {:?}", err); HttpResponse::UriTooLong().finish() @@ -407,34 +407,3 @@ pub fn extract_query_parameters(req: &HttpRequest) -> QueryParameters { } } } - -// Returns a string of SVG code for an image depicting -// the given QR Code, with the given number of border modules. -// The string always uses Unix newlines (\n), regardless of the platform. -fn qr_to_svg_string(qr: &QrCode, border: i32) -> String { - assert!(border >= 0, "Border must be non-negative"); - let mut result = String::new(); - result += "\n"; - result += "\n"; - let dimension = qr - .size() - .checked_add(border.checked_mul(2).unwrap()) - .unwrap(); - result += &format!( - "\n", dimension); - result += "\t\n"; - result += "\t\n"; - result += "\n"; - result -} diff --git a/src/main.rs b/src/main.rs index b46262d..558dabb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,13 +13,14 @@ use anyhow::Result; use clap::{crate_version, IntoApp, Parser}; use clap_complete::generate; use log::{error, warn}; -use qrcodegen::{QrCode, QrCodeEcc}; +use qrcode::QrCode; use yansi::{Color, Paint}; mod archive; mod args; mod auth; mod config; +mod consts; mod errors; mod file_upload; mod listing; @@ -239,7 +240,7 @@ async fn run(miniserve_config: MiniserveConfig) -> Result<(), ContextualError> { .iter() .filter(|url| !url.contains("//127.0.0.1:") && !url.contains("//[::1]:")) { - match QrCode::encode_text(url, QrCodeEcc::Low) { + match QrCode::with_error_correction_level(url, consts::QR_EC_LEVEL) { Ok(qr) => { println!("QR code for {}:", Color::Green.paint(url).bold()); print_qr(&qr); @@ -352,30 +353,27 @@ async fn css() -> impl Responder { .body(css) } -// Prints to the console two inverted QrCodes side by side. +// Prints to the console a normal and an inverted QrCode side by side. fn print_qr(qr: &QrCode) { - let border = 4; - let size = qr.size() + 2 * border; - - for y in (0..size).step_by(2) { - for x in 0..2 * size { - let inverted = x >= size; - let (x, y) = (x % size - border, y - border); - - //each char represents two vertical modules - let (mod1, mod2) = match inverted { - false => (qr.get_module(x, y), qr.get_module(x, y + 1)), - true => (!qr.get_module(x, y), !qr.get_module(x, y + 1)), - }; - let c = match (mod1, mod2) { - (false, false) => ' ', - (true, false) => '▀', - (false, true) => '▄', - (true, true) => '█', - }; - print!("{0}", c); - } - println!(); - } - println!(); + use qrcode::render::unicode::Dense1x2; + + let normal = qr + .render() + .quiet_zone(true) + .dark_color(Dense1x2::Dark) + .light_color(Dense1x2::Light) + .build(); + let inverted = qr + .render() + .quiet_zone(true) + .dark_color(Dense1x2::Light) + .light_color(Dense1x2::Dark) + .build(); + let codes = normal + .lines() + .zip(inverted.lines()) + .map(|(l, r)| format!("{} {}", l, r)) + .collect::>() + .join("\n"); + println!("{}", codes); } diff --git a/src/renderer.rs b/src/renderer.rs index 7ec48b0..48b74b6 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -3,6 +3,7 @@ use chrono::{DateTime, Utc}; use chrono_humanize::Humanize; use clap::{crate_name, crate_version}; use maud::{html, Markup, PreEscaped, DOCTYPE}; +use qrcode::QrCode; use std::time::SystemTime; use strum::IntoEnumIterator; @@ -224,6 +225,45 @@ pub fn raw(entries: Vec, is_root: bool) -> Markup { } } +/// Renders the QR code page +pub fn qr_code_page(qr: &QrCode) -> Markup { + use qrcode::render::svg; + + html! { + (DOCTYPE) + html { + body { + // make QR code expand and fill page + style { + (PreEscaped("\ + html {\ + width: 100vw;\ + height: 100vh;\ + }\ + body {\ + width: 100%;\ + height: 100%;\ + margin: 0;\ + display: grid;\ + align-items: center;\ + justify-items: center;\ + }\ + svg {\ + width: 80%;\ + height: 80%;\ + }\ + ")) + } + (PreEscaped(qr.render() + .quiet_zone(false) + .dark_color(svg::Color("#000000")) + .light_color(svg::Color("#ffffff")) + .build())) + } + } + } +} + // Partial: version footer fn version_footer() -> Markup { html! { -- cgit v1.2.3 From c2ece55040d4f1f03e189636895645435d762322 Mon Sep 17 00:00:00 2001 From: cyqsimon <28627918+cyqsimon@users.noreply.github.com> Date: Wed, 20 Jul 2022 20:09:55 +0800 Subject: Fix tests --- src/renderer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/renderer.rs b/src/renderer.rs index 48b74b6..95aeb5f 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -232,7 +232,7 @@ pub fn qr_code_page(qr: &QrCode) -> Markup { html! { (DOCTYPE) html { - body { + body.qr_code_page { // make QR code expand and fill page style { (PreEscaped("\ -- cgit v1.2.3 From 3d86dcdb7143dc5675eab82edcec69fd3cf69805 Mon Sep 17 00:00:00 2001 From: cyqsimon <28627918+cyqsimon@users.noreply.github.com> Date: Thu, 21 Jul 2022 14:33:37 +0800 Subject: Use `mime` for content type --- src/listing.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/listing.rs b/src/listing.rs index f90c40c..b201ebb 100644 --- a/src/listing.rs +++ b/src/listing.rs @@ -222,7 +222,7 @@ pub fn directory_listing( if let Some(url) = query_params.qrcode { let res = match QrCode::with_error_correction_level(url, consts::QR_EC_LEVEL) { Ok(qr) => HttpResponse::Ok() - .content_type("text/html; charset=utf-8") + .content_type(mime::TEXT_HTML_UTF_8) .body(renderer::qr_code_page(&qr).into_string()), Err(err) => { log::error!("URL is invalid (too long?): {:?}", err); -- cgit v1.2.3 From a99d8bdcadf4f822bb86a2710e5d73783ac7097a Mon Sep 17 00:00:00 2001 From: cyqsimon <28627918+cyqsimon@users.noreply.github.com> Date: Tue, 26 Jul 2022 12:27:14 +0800 Subject: Move QR code page style to `style.scss` --- src/listing.rs | 23 ++++++++--------------- src/renderer.rs | 47 +++++++++++++++++------------------------------ 2 files changed, 25 insertions(+), 45 deletions(-) (limited to 'src') diff --git a/src/listing.rs b/src/listing.rs index b201ebb..66e541c 100644 --- a/src/listing.rs +++ b/src/listing.rs @@ -154,10 +154,7 @@ pub async fn file_handler(req: HttpRequest) -> actix_web::Result io::Result { +pub fn directory_listing(dir: &actix_files::Directory, req: &HttpRequest) -> io::Result { let extensions = req.extensions(); let current_user: Option<&CurrentUser> = extensions.get::(); @@ -198,8 +195,7 @@ pub fn directory_listing( } Component::Normal(s) => { name = s.to_string_lossy().to_string(); - link_accumulator - .push_str(&(utf8_percent_encode(&name, PATH_SEGMENT).to_string() + "/")); + link_accumulator.push_str(&(utf8_percent_encode(&name, PATH_SEGMENT).to_string() + "/")); } _ => name = "".to_string(), }; @@ -223,7 +219,7 @@ pub fn directory_listing( let res = match QrCode::with_error_correction_level(url, consts::QR_EC_LEVEL) { Ok(qr) => HttpResponse::Ok() .content_type(mime::TEXT_HTML_UTF_8) - .body(renderer::qr_code_page(&qr).into_string()), + .body(renderer::qr_code_page(&breadcrumbs, &qr, conf).into_string()), Err(err) => { log::error!("URL is invalid (too long?): {:?}", err); HttpResponse::UriTooLong().finish() @@ -301,9 +297,9 @@ pub fn directory_listing( } match query_params.sort.unwrap_or(SortingMethod::Name) { - SortingMethod::Name => entries.sort_by(|e1, e2| { - alphanumeric_sort::compare_str(e1.name.to_lowercase(), e2.name.to_lowercase()) - }), + SortingMethod::Name => { + entries.sort_by(|e1, e2| alphanumeric_sort::compare_str(e1.name.to_lowercase(), e2.name.to_lowercase())) + } SortingMethod::Size => entries.sort_by(|e1, e2| { // If we can't get the size of the entry (directory for instance) // let's consider it's 0b @@ -371,10 +367,7 @@ pub fn directory_listing( .content_type(archive_method.content_type()) .append_header(archive_method.content_encoding()) .append_header(("Content-Transfer-Encoding", "binary")) - .append_header(( - "Content-Disposition", - format!("attachment; filename={:?}", file_name), - )) + .append_header(("Content-Disposition", format!("attachment; filename={:?}", file_name))) .body(actix_web::body::BodyStream::new(rx)), )) } else { @@ -386,7 +379,7 @@ pub fn directory_listing( readme, is_root, query_params, - breadcrumbs, + &breadcrumbs, &encoded_dir, conf, current_user, diff --git a/src/renderer.rs b/src/renderer.rs index 95aeb5f..0154ce0 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -18,7 +18,7 @@ pub fn page( readme: Option<(String, String)>, is_root: bool, query_params: QueryParameters, - breadcrumbs: Vec, + breadcrumbs: &[Breadcrumb], encoded_dir: &str, conf: &MiniserveConfig, current_user: Option<&CurrentUser>, @@ -34,11 +34,7 @@ pub fn page( let upload_action = build_upload_action(&upload_route, encoded_dir, sort_method, sort_order); let mkdir_action = build_mkdir_action(&upload_route, encoded_dir); - let title_path = breadcrumbs - .iter() - .map(|el| el.name.clone()) - .collect::>() - .join("/"); + let title_path = breadcrumbs_to_path_string(breadcrumbs); html! { (DOCTYPE) @@ -226,34 +222,16 @@ pub fn raw(entries: Vec, is_root: bool) -> Markup { } /// Renders the QR code page -pub fn qr_code_page(qr: &QrCode) -> Markup { +pub fn qr_code_page(breadcrumbs: &[Breadcrumb], qr: &QrCode, conf: &MiniserveConfig) -> Markup { use qrcode::render::svg; + let title = breadcrumbs_to_path_string(breadcrumbs); + html! { (DOCTYPE) - html { - body.qr_code_page { - // make QR code expand and fill page - style { - (PreEscaped("\ - html {\ - width: 100vw;\ - height: 100vh;\ - }\ - body {\ - width: 100%;\ - height: 100%;\ - margin: 0;\ - display: grid;\ - align-items: center;\ - justify-items: center;\ - }\ - svg {\ - width: 80%;\ - height: 80%;\ - }\ - ")) - } + html.qr_code_page { + (page_header(&title, false, &conf.favicon_route, &conf.css_route)) + body { (PreEscaped(qr.render() .quiet_zone(false) .dark_color(svg::Color("#000000")) @@ -264,6 +242,15 @@ pub fn qr_code_page(qr: &QrCode) -> Markup { } } +/// Build a path string from a list of breadcrumbs. +fn breadcrumbs_to_path_string(breadcrumbs: &[Breadcrumb]) -> String { + breadcrumbs + .iter() + .map(|el| el.name.clone()) + .collect::>() + .join("/") +} + // Partial: version footer fn version_footer() -> Markup { html! { -- cgit v1.2.3 From ef3ec4039e220d3dc4a83ac267101c4dc45dace8 Mon Sep 17 00:00:00 2001 From: cyqsimon <28627918+cyqsimon@users.noreply.github.com> Date: Tue, 26 Jul 2022 13:10:21 +0800 Subject: Use low EC level for QR code --- src/consts.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/consts.rs b/src/consts.rs index 07f47b0..f10570a 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -1,4 +1,4 @@ use qrcode::EcLevel; /// The error correction level to use for all QR code generation. -pub const QR_EC_LEVEL: EcLevel = EcLevel::M; +pub const QR_EC_LEVEL: EcLevel = EcLevel::L; -- cgit v1.2.3 From 4fca60cc5b06fff1aa7f9e52b774d868dc7bfe1a Mon Sep 17 00:00:00 2001 From: cyqsimon <28627918+cyqsimon@users.noreply.github.com> Date: Mon, 22 Aug 2022 19:07:24 +0800 Subject: Fix rustfmt complaints --- src/listing.rs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/listing.rs b/src/listing.rs index 66e541c..bc8dcfb 100644 --- a/src/listing.rs +++ b/src/listing.rs @@ -154,7 +154,10 @@ pub async fn file_handler(req: HttpRequest) -> actix_web::Result io::Result { +pub fn directory_listing( + dir: &actix_files::Directory, + req: &HttpRequest, +) -> io::Result { let extensions = req.extensions(); let current_user: Option<&CurrentUser> = extensions.get::(); @@ -195,7 +198,8 @@ pub fn directory_listing(dir: &actix_files::Directory, req: &HttpRequest) -> io: } Component::Normal(s) => { name = s.to_string_lossy().to_string(); - link_accumulator.push_str(&(utf8_percent_encode(&name, PATH_SEGMENT).to_string() + "/")); + link_accumulator + .push_str(&(utf8_percent_encode(&name, PATH_SEGMENT).to_string() + "/")); } _ => name = "".to_string(), }; @@ -297,9 +301,9 @@ pub fn directory_listing(dir: &actix_files::Directory, req: &HttpRequest) -> io: } match query_params.sort.unwrap_or(SortingMethod::Name) { - SortingMethod::Name => { - entries.sort_by(|e1, e2| alphanumeric_sort::compare_str(e1.name.to_lowercase(), e2.name.to_lowercase())) - } + SortingMethod::Name => entries.sort_by(|e1, e2| { + alphanumeric_sort::compare_str(e1.name.to_lowercase(), e2.name.to_lowercase()) + }), SortingMethod::Size => entries.sort_by(|e1, e2| { // If we can't get the size of the entry (directory for instance) // let's consider it's 0b @@ -367,7 +371,10 @@ pub fn directory_listing(dir: &actix_files::Directory, req: &HttpRequest) -> io: .content_type(archive_method.content_type()) .append_header(archive_method.content_encoding()) .append_header(("Content-Transfer-Encoding", "binary")) - .append_header(("Content-Disposition", format!("attachment; filename={:?}", file_name))) + .append_header(( + "Content-Disposition", + format!("attachment; filename={:?}", file_name), + )) .body(actix_web::body::BodyStream::new(rx)), )) } else { -- cgit v1.2.3 From 9f7d5a8aeaf252fb51d88b965b3a324e8e815f37 Mon Sep 17 00:00:00 2001 From: cyqsimon <28627918+cyqsimon@users.noreply.github.com> Date: Thu, 1 Sep 2022 19:37:39 +0800 Subject: Fix drop-down QR code --- src/listing.rs | 26 ++++++---------- src/renderer.rs | 95 ++++++++++++++++++++++++++++++++++----------------------- 2 files changed, 66 insertions(+), 55 deletions(-) (limited to 'src') diff --git a/src/listing.rs b/src/listing.rs index bc8dcfb..b000649 100644 --- a/src/listing.rs +++ b/src/listing.rs @@ -9,14 +9,13 @@ use actix_web::{HttpMessage, HttpRequest, HttpResponse}; use bytesize::ByteSize; use comrak::{markdown_to_html, ComrakOptions}; use percent_encoding::{percent_decode_str, utf8_percent_encode}; -use qrcode::QrCode; use serde::Deserialize; use strum_macros::{Display, EnumString}; use crate::archive::ArchiveMethod; use crate::auth::CurrentUser; use crate::errors::{self, ContextualError}; -use crate::{consts, renderer}; +use crate::renderer; use self::percent_encode_sets::PATH_SEGMENT; @@ -38,7 +37,6 @@ pub struct QueryParameters { pub order: Option, pub raw: Option, pub mkdir_name: Option, - qrcode: Option, download: Option, } @@ -166,6 +164,13 @@ pub fn directory_listing( let base = Path::new(serve_path); let random_route_abs = format!("/{}", conf.route_prefix); + let abs_url = format!( + "{}://{}{}", + req.connection_info().scheme(), + req.connection_info().host(), + req.uri() + ); + dbg!(&abs_url); let is_root = base.parent().is_none() || Path::new(&req.path()) == Path::new(&random_route_abs); let encoded_dir = match base.strip_prefix(random_route_abs) { @@ -218,20 +223,6 @@ pub fn directory_listing( let query_params = extract_query_parameters(req); - // If the `qrcode` parameter is included in the url, then should respond to the QR code - if let Some(url) = query_params.qrcode { - let res = match QrCode::with_error_correction_level(url, consts::QR_EC_LEVEL) { - Ok(qr) => HttpResponse::Ok() - .content_type(mime::TEXT_HTML_UTF_8) - .body(renderer::qr_code_page(&breadcrumbs, &qr, conf).into_string()), - Err(err) => { - log::error!("URL is invalid (too long?): {:?}", err); - HttpResponse::UriTooLong().finish() - } - }; - return Ok(ServiceResponse::new(req.clone(), res)); - } - let mut entries: Vec = Vec::new(); let mut readme: Option<(String, String)> = None; @@ -384,6 +375,7 @@ pub fn directory_listing( renderer::page( entries, readme, + abs_url, is_root, query_params, &breadcrumbs, diff --git a/src/renderer.rs b/src/renderer.rs index 0154ce0..614672f 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -2,12 +2,15 @@ use actix_web::http::StatusCode; use chrono::{DateTime, Utc}; use chrono_humanize::Humanize; use clap::{crate_name, crate_version}; +use lazy_static::lazy_static; use maud::{html, Markup, PreEscaped, DOCTYPE}; -use qrcode::QrCode; +use qrcode::{types::QrError, QrCode}; +use regex::Regex; use std::time::SystemTime; use strum::IntoEnumIterator; use crate::auth::CurrentUser; +use crate::consts; use crate::listing::{Breadcrumb, Entry, QueryParameters, SortingMethod, SortingOrder}; use crate::{archive::ArchiveMethod, MiniserveConfig}; @@ -16,6 +19,7 @@ use crate::{archive::ArchiveMethod, MiniserveConfig}; pub fn page( entries: Vec, readme: Option<(String, String)>, + abs_url: impl AsRef, is_root: bool, query_params: QueryParameters, breadcrumbs: &[Breadcrumb], @@ -86,7 +90,10 @@ pub fn page( } } } - (color_scheme_selector(conf.show_qrcode, conf.hide_theme_selector)) + nav { + (qr_spoiler(conf.show_qrcode, abs_url)) + (color_scheme_selector(conf.hide_theme_selector)) + } div.container { span #top { } h1.title dir="ltr" { @@ -221,25 +228,30 @@ pub fn raw(entries: Vec, is_root: bool) -> Markup { } } -/// Renders the QR code page -pub fn qr_code_page(breadcrumbs: &[Breadcrumb], qr: &QrCode, conf: &MiniserveConfig) -> Markup { +/// Renders the QR code SVG +fn qr_code_svg(url: impl AsRef, no_width_height_attr: bool) -> Result { use qrcode::render::svg; - - let title = breadcrumbs_to_path_string(breadcrumbs); - - html! { - (DOCTYPE) - html.qr_code_page { - (page_header(&title, false, &conf.favicon_route, &conf.css_route)) - body { - (PreEscaped(qr.render() - .quiet_zone(false) - .dark_color(svg::Color("#000000")) - .light_color(svg::Color("#ffffff")) - .build())) - } + let qr = QrCode::with_error_correction_level(url.as_ref(), consts::QR_EC_LEVEL)?; + let mut svg = qr + .render() + .quiet_zone(false) + .dark_color(svg::Color("#000000")) + .light_color(svg::Color("#ffffff")) + .build(); + + if no_width_height_attr { + // HACK: qrcode crate hard-codes height and width into SVG's attributes. + // This behaviour may be undesirable because we want it to fit its HTML container. + // The proper way to remove them is to use a XML parser, but regex is good enough for a + // simple case like this. + lazy_static! { + static ref RE: Regex = + Regex::new(r#"(?P.+?>)"#).unwrap(); } + svg = RE.replace(&svg, "$front$aft").to_string(); } + + Ok(svg) } /// Build a path string from a list of breadcrumbs. @@ -317,30 +329,37 @@ const THEME_PICKER_CHOICES: &[(&str, &str)] = &[ pub const THEME_SLUGS: &[&str] = &["squirrel", "archlinux", "zenburn", "monokai"]; -/// Partial: color scheme selector -fn color_scheme_selector(show_qrcode: bool, hide_theme_selector: bool) -> Markup { +/// Partial: qr code spoiler +fn qr_spoiler(show_qrcode: bool, content: impl AsRef) -> Markup { html! { - nav { - @if show_qrcode { - div { - p onmouseover="document.querySelector('#qrcode').src = `?qrcode=${encodeURIComponent(window.location.href)}`" { - "QR code" - } - div.qrcode { - img #qrcode alt="QR code" title="QR code of this page"; + @if show_qrcode { + div { + p { + "QR code" + } + div.qrcode #qrcode { + @match qr_code_svg(content, true) { + Ok(svg) => (PreEscaped(svg)), + Err(err) => (format!("QR generation error: {}", err)), } } } - @if !hide_theme_selector { - div { - p { - "Change theme..." - } - ul.theme { - @for color_scheme in THEME_PICKER_CHOICES { - li.(format!("theme_{}", color_scheme.1)) { - (color_scheme_link(color_scheme)) - } + } + } +} + +/// Partial: color scheme selector +fn color_scheme_selector(hide_theme_selector: bool) -> Markup { + html! { + @if !hide_theme_selector { + div { + p { + "Change theme..." + } + ul.theme { + @for color_scheme in THEME_PICKER_CHOICES { + li.(format!("theme_{}", color_scheme.1)) { + (color_scheme_link(color_scheme)) } } } -- cgit v1.2.3 From f762672b098ec7963748530bccd78f16f42ab9d3 Mon Sep 17 00:00:00 2001 From: cyqsimon <28627918+cyqsimon@users.noreply.github.com> Date: Thu, 1 Sep 2022 20:13:37 +0800 Subject: Remove leftover `dbg!` --- src/listing.rs | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/listing.rs b/src/listing.rs index b000649..c0104b1 100644 --- a/src/listing.rs +++ b/src/listing.rs @@ -170,7 +170,6 @@ pub fn directory_listing( req.connection_info().host(), req.uri() ); - dbg!(&abs_url); let is_root = base.parent().is_none() || Path::new(&req.path()) == Path::new(&random_route_abs); let encoded_dir = match base.strip_prefix(random_route_abs) { -- cgit v1.2.3 From aa2dda7885a83354d1519eef62397b90189a9802 Mon Sep 17 00:00:00 2001 From: cyqsimon <28627918+cyqsimon@users.noreply.github.com> Date: Thu, 1 Sep 2022 20:20:27 +0800 Subject: Switch to `fast_qr` crate --- src/consts.rs | 4 ++-- src/main.rs | 33 ++++----------------------------- src/renderer.rs | 36 ++++++++++-------------------------- 3 files changed, 16 insertions(+), 57 deletions(-) (limited to 'src') diff --git a/src/consts.rs b/src/consts.rs index f10570a..1a105f7 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -1,4 +1,4 @@ -use qrcode::EcLevel; +use fast_qr::ECL; /// The error correction level to use for all QR code generation. -pub const QR_EC_LEVEL: EcLevel = EcLevel::L; +pub const QR_EC_LEVEL: ECL = ECL::L; diff --git a/src/main.rs b/src/main.rs index 558dabb..11193cc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,8 +12,8 @@ use actix_web_httpauth::middleware::HttpAuthentication; use anyhow::Result; use clap::{crate_version, IntoApp, Parser}; use clap_complete::generate; +use fast_qr::QRBuilder; use log::{error, warn}; -use qrcode::QrCode; use yansi::{Color, Paint}; mod archive; @@ -240,13 +240,13 @@ async fn run(miniserve_config: MiniserveConfig) -> Result<(), ContextualError> { .iter() .filter(|url| !url.contains("//127.0.0.1:") && !url.contains("//[::1]:")) { - match QrCode::with_error_correction_level(url, consts::QR_EC_LEVEL) { + match QRBuilder::new(url.clone()).ecl(consts::QR_EC_LEVEL).build() { Ok(qr) => { println!("QR code for {}:", Color::Green.paint(url).bold()); - print_qr(&qr); + qr.print(); } Err(e) => { - error!("Failed to render QR to terminal: {}", e); + error!("Failed to render QR to terminal: {:?}", e); } }; } @@ -352,28 +352,3 @@ async fn css() -> impl Responder { .insert_header(ContentType(mime::TEXT_CSS)) .body(css) } - -// Prints to the console a normal and an inverted QrCode side by side. -fn print_qr(qr: &QrCode) { - use qrcode::render::unicode::Dense1x2; - - let normal = qr - .render() - .quiet_zone(true) - .dark_color(Dense1x2::Dark) - .light_color(Dense1x2::Light) - .build(); - let inverted = qr - .render() - .quiet_zone(true) - .dark_color(Dense1x2::Light) - .light_color(Dense1x2::Dark) - .build(); - let codes = normal - .lines() - .zip(inverted.lines()) - .map(|(l, r)| format!("{} {}", l, r)) - .collect::>() - .join("\n"); - println!("{}", codes); -} diff --git a/src/renderer.rs b/src/renderer.rs index 614672f..77b9eed 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -2,10 +2,10 @@ use actix_web::http::StatusCode; use chrono::{DateTime, Utc}; use chrono_humanize::Humanize; use clap::{crate_name, crate_version}; -use lazy_static::lazy_static; +use fast_qr::convert::svg::SvgBuilder; +use fast_qr::qr::QRCodeError; +use fast_qr::QRBuilder; use maud::{html, Markup, PreEscaped, DOCTYPE}; -use qrcode::{types::QrError, QrCode}; -use regex::Regex; use std::time::SystemTime; use strum::IntoEnumIterator; @@ -229,27 +229,11 @@ pub fn raw(entries: Vec, is_root: bool) -> Markup { } /// Renders the QR code SVG -fn qr_code_svg(url: impl AsRef, no_width_height_attr: bool) -> Result { - use qrcode::render::svg; - let qr = QrCode::with_error_correction_level(url.as_ref(), consts::QR_EC_LEVEL)?; - let mut svg = qr - .render() - .quiet_zone(false) - .dark_color(svg::Color("#000000")) - .light_color(svg::Color("#ffffff")) - .build(); - - if no_width_height_attr { - // HACK: qrcode crate hard-codes height and width into SVG's attributes. - // This behaviour may be undesirable because we want it to fit its HTML container. - // The proper way to remove them is to use a XML parser, but regex is good enough for a - // simple case like this. - lazy_static! { - static ref RE: Regex = - Regex::new(r#"(?P.+?>)"#).unwrap(); - } - svg = RE.replace(&svg, "$front$aft").to_string(); - } +fn qr_code_svg(url: impl AsRef, margin: usize) -> Result { + let qr = QRBuilder::new(url.as_ref().into()) + .ecl(consts::QR_EC_LEVEL) + .build()?; + let svg = SvgBuilder::new().margin(margin).build_qr(qr); Ok(svg) } @@ -338,9 +322,9 @@ fn qr_spoiler(show_qrcode: bool, content: impl AsRef) -> Markup { "QR code" } div.qrcode #qrcode { - @match qr_code_svg(content, true) { + @match qr_code_svg(content, 1) { Ok(svg) => (PreEscaped(svg)), - Err(err) => (format!("QR generation error: {}", err)), + Err(err) => (format!("QR generation error: {:?}", err)), } } } -- cgit v1.2.3 From 044f30c550888429cf8a9d39ef1a282ff3385e09 Mon Sep 17 00:00:00 2001 From: cyqsimon <28627918+cyqsimon@users.noreply.github.com> Date: Thu, 1 Sep 2022 21:10:30 +0800 Subject: Move QR margin size into `consts` --- src/consts.rs | 3 +++ src/renderer.rs | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/consts.rs b/src/consts.rs index 1a105f7..d864683 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -2,3 +2,6 @@ use fast_qr::ECL; /// The error correction level to use for all QR code generation. pub const QR_EC_LEVEL: ECL = ECL::L; + +/// The margin size for the SVG QR code on the webpage. +pub const SVG_QR_MARGIN: usize = 1; diff --git a/src/renderer.rs b/src/renderer.rs index 77b9eed..562f215 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -322,7 +322,7 @@ fn qr_spoiler(show_qrcode: bool, content: impl AsRef) -> Markup { "QR code" } div.qrcode #qrcode { - @match qr_code_svg(content, 1) { + @match qr_code_svg(content, consts::SVG_QR_MARGIN) { Ok(svg) => (PreEscaped(svg)), Err(err) => (format!("QR generation error: {:?}", err)), } -- cgit v1.2.3 From 2d63d3d7a25483293789546a347f6235ef0aea67 Mon Sep 17 00:00:00 2001 From: cyqsimon <28627918+cyqsimon@users.noreply.github.com> Date: Thu, 15 Sep 2022 19:54:50 +0800 Subject: Add tooltip on QR code --- src/renderer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/renderer.rs b/src/renderer.rs index 562f215..ebb9f6f 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -321,7 +321,7 @@ fn qr_spoiler(show_qrcode: bool, content: impl AsRef) -> Markup { p { "QR code" } - div.qrcode #qrcode { + div.qrcode #qrcode title=(PreEscaped(content.as_ref())) { @match qr_code_svg(content, consts::SVG_QR_MARGIN) { Ok(svg) => (PreEscaped(svg)), Err(err) => (format!("QR generation error: {:?}", err)), -- cgit v1.2.3