From e5f302647a91cfb44dcfcf0e1592e40db3fe4bc1 Mon Sep 17 00:00:00 2001 From: Vincent Prouillet Date: Tue, 19 Dec 2023 13:22:40 +0100 Subject: [PATCH 01/25] Next version --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d917e4a9b2..44bd027a19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +## 0.19.0 (unreleased) + + ## 0.18.0 (2023-12-18) - Fix LFI in `zola serve` From a586a5af55b0a49cae9dfb05dfba7141a9aa3cdc Mon Sep 17 00:00:00 2001 From: Morgan Creekmore Date: Fri, 5 Jan 2024 05:16:38 -0600 Subject: [PATCH 02/25] Add option to include date in search index (#2401) --- components/config/src/config/search.rs | 3 ++ components/search/src/lib.rs | 40 +++++++++++++++++-- .../getting-started/configuration.md | 2 + 3 files changed, 42 insertions(+), 3 deletions(-) diff --git a/components/config/src/config/search.rs b/components/config/src/config/search.rs index 6f64cc20e3..3ce6878cd2 100644 --- a/components/config/src/config/search.rs +++ b/components/config/src/config/search.rs @@ -22,6 +22,8 @@ pub struct Search { /// Includes the description in the search index. When the site becomes too large, you can switch /// to that instead. `false` by default pub include_description: bool, + /// Include the RFC3339 datetime of the page in the search index. `false` by default. + pub include_date: bool, /// Include the path of the page in the search index. `false` by default. pub include_path: bool, /// Foramt of the search index to be produced. Javascript by default @@ -35,6 +37,7 @@ impl Default for Search { include_content: true, include_description: false, include_path: false, + include_date: false, truncate_content_length: None, index_format: Default::default(), } diff --git a/components/search/src/lib.rs b/components/search/src/lib.rs index 073eb09307..51d25579d2 100644 --- a/components/search/src/lib.rs +++ b/components/search/src/lib.rs @@ -3,6 +3,8 @@ use std::collections::{HashMap, HashSet}; use libs::ammonia; use libs::elasticlunr::{lang, Index, IndexBuilder}; use libs::once_cell::sync::Lazy; +use libs::time::format_description::well_known::Rfc3339; +use libs::time::OffsetDateTime; use config::{Config, Search}; use content::{Library, Section}; @@ -35,6 +37,10 @@ fn build_fields(search_config: &Search, mut index: IndexBuilder) -> IndexBuilder index = index.add_field("description"); } + if search_config.include_date { + index = index.add_field("date") + } + if search_config.include_path { index = index.add_field_with_tokenizer("path", Box::new(path_tokenizer)); } @@ -57,6 +63,7 @@ fn fill_index( search_config: &Search, title: &Option, description: &Option, + datetime: &Option, path: &str, content: &str, ) -> Vec { @@ -70,6 +77,14 @@ fn fill_index( row.push(description.clone().unwrap_or_default()); } + if search_config.include_date { + if let Some(date) = datetime { + if let Ok(d) = date.format(&Rfc3339) { + row.push(d); + } + } + } + if search_config.include_path { row.push(path.to_string()); } @@ -133,6 +148,7 @@ fn add_section_to_index( search_config, §ion.meta.title, §ion.meta.description, + &None, §ion.path, §ion.content, ), @@ -151,6 +167,7 @@ fn add_section_to_index( search_config, &page.meta.title, &page.meta.description, + &page.meta.datetime, &page.path, &page.content, ), @@ -192,7 +209,7 @@ mod tests { let path = "/a/page/".to_string(); let content = "Some content".to_string(); - let res = fill_index(&config.search, &title, &description, &path, &content); + let res = fill_index(&config.search, &title, &description, &None, &path, &content); assert_eq!(res.len(), 2); assert_eq!(res[0], title.unwrap()); assert_eq!(res[1], content); @@ -207,7 +224,7 @@ mod tests { let path = "/a/page/".to_string(); let content = "Some content".to_string(); - let res = fill_index(&config.search, &title, &description, &path, &content); + let res = fill_index(&config.search, &title, &description, &None, &path, &content); assert_eq!(res.len(), 3); assert_eq!(res[0], title.unwrap()); assert_eq!(res[1], description.unwrap()); @@ -223,9 +240,26 @@ mod tests { let path = "/a/page/".to_string(); let content = "Some content".to_string(); - let res = fill_index(&config.search, &title, &description, &path, &content); + let res = fill_index(&config.search, &title, &description, &None, &path, &content); assert_eq!(res.len(), 2); assert_eq!(res[0], title.unwrap()); assert_eq!(res[1], content[..5]); } + + #[test] + fn can_fill_index_date() { + let mut config = Config::default(); + config.search.include_date = true; + let title = Some("A title".to_string()); + let description = Some("A description".to_string()); + let path = "/a/page/".to_string(); + let content = "Some content".to_string(); + let datetime = Some(OffsetDateTime::parse("2023-01-31T00:00:00Z", &Rfc3339).unwrap()); + + let res = fill_index(&config.search, &title, &description, &datetime, &path, &content); + assert_eq!(res.len(), 3); + assert_eq!(res[0], title.unwrap()); + assert_eq!(res[1], "2023-01-31T00:00:00Z"); + assert_eq!(res[2], content); + } } diff --git a/docs/content/documentation/getting-started/configuration.md b/docs/content/documentation/getting-started/configuration.md index fbf4c01f55..d8ec482d5d 100644 --- a/docs/content/documentation/getting-started/configuration.md +++ b/docs/content/documentation/getting-started/configuration.md @@ -169,6 +169,8 @@ paths_keep_dates = false include_title = true # Whether to include the description of the page/section in the index include_description = false +# Whether to include the RFC3339 datetime of the page in the search index +include_date = false # Whether to include the path of the page/section in the index include_path = false # Whether to include the rendered content of the page/section in the index From 682447ee73fb858be3b9fbb73ff0a372b9e4af58 Mon Sep 17 00:00:00 2001 From: Clar Fon <15850505+clarfonthey@users.noreply.github.com> Date: Sun, 7 Jan 2024 14:23:36 -0500 Subject: [PATCH 03/25] Match `` in addition to `` (#2397) * Match without spaces * Add tests for new handling, with a note on pulldown-cmark bug --- components/markdown/src/markdown.rs | 42 +++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/components/markdown/src/markdown.rs b/components/markdown/src/markdown.rs index a6cb3beca6..4d30b2ac92 100644 --- a/components/markdown/src/markdown.rs +++ b/components/markdown/src/markdown.rs @@ -10,7 +10,7 @@ use utils::net::is_external_link; use crate::context::RenderContext; use errors::{Context, Error, Result}; use libs::pulldown_cmark::escape::escape_html; -use libs::regex::Regex; +use libs::regex::{Regex, RegexBuilder}; use utils::site::resolve_internal_link; use utils::slugs::slugify_anchors; use utils::table_of_contents::{make_table_of_contents, Heading}; @@ -24,6 +24,15 @@ const CONTINUE_READING: &str = ""; const ANCHOR_LINK_TEMPLATE: &str = "anchor-link.html"; static EMOJI_REPLACER: Lazy = Lazy::new(EmojiReplacer::new); +/// Set as a regex to help match some extra cases. This way, spaces and case don't matter. +static MORE_DIVIDER_RE: Lazy = Lazy::new(|| { + RegexBuilder::new(r#""#) + .case_insensitive(true) + .dot_matches_new_line(true) + .build() + .unwrap() +}); + /// Although there exists [a list of registered URI schemes][uri-schemes], a link may use arbitrary, /// private schemes. This regex checks if the given string starts with something that just looks /// like a scheme, i.e., a case-insensitive identifier followed by a colon. @@ -485,7 +494,7 @@ pub fn markdown_to_html( }); } Event::Html(text) => { - if text.contains("") { + if !has_summary && MORE_DIVIDER_RE.is_match(&text) { has_summary = true; events.push(Event::Html(CONTINUE_READING.into())); continue; @@ -600,6 +609,8 @@ pub fn markdown_to_html( #[cfg(test)] mod tests { + use config::Config; + use super::*; #[test] @@ -644,4 +655,31 @@ mod tests { assert!(!is_colocated_asset_link(link)); } } + + #[test] + // Tests for summary being split out + fn test_summary_split() { + let top = "Here's a compelling summary."; + let top_rendered = format!("

{top}

"); + let bottom = "Here's the compelling conclusion."; + let bottom_rendered = format!("

{bottom}

"); + // FIXME: would add a test that includes newlines, but due to the way pulldown-cmark parses HTML nodes, these are passed as separate HTML events. see: https://github.com/raphlinus/pulldown-cmark/issues/803 + let mores = + ["", "", "", "", ""]; + let config = Config::default(); + let context = RenderContext::from_config(&config); + for more in mores { + let content = format!("{top}\n\n{more}\n\n{bottom}"); + let rendered = markdown_to_html(&content, &context, vec![]).unwrap(); + assert!(rendered.summary_len.is_some(), "no summary when splitting on {more}"); + let summary_len = rendered.summary_len.unwrap(); + let summary = &rendered.body[..summary_len].trim(); + let body = &rendered.body[summary_len..].trim(); + let continue_reading = &body[..CONTINUE_READING.len()]; + let body = &body[CONTINUE_READING.len()..].trim(); + assert_eq!(summary, &top_rendered); + assert_eq!(continue_reading, CONTINUE_READING); + assert_eq!(body, &bottom_rendered); + } + } } From 42ea1b49d1e340b083ba2ca12b7ba940bb91793f Mon Sep 17 00:00:00 2001 From: Stan Rozenraukh Date: Sun, 7 Jan 2024 14:37:33 -0500 Subject: [PATCH 04/25] fix(serve): content can be served from output_path (#2398) This fixes a bug introduced in https://github.com/getzola/zola/pull/2258 The issue arose when `output_path` was relative. The request being served would be canonicalized and this would be a string. So, for example, if you were serving content from `public` the code [right after](https://github.com/getzola/zola/blob/38199c125501e9ff0e700e96adaca72cc3f25d2b/src/cmd/serve.rs#L144-L147) the canonicalization checking if `root.starts_with(original_root)` would always return `false` since an absolute path, `/some/path/to/content` would never start with a string like `public`. --- src/cmd/serve.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cmd/serve.rs b/src/cmd/serve.rs index 2f2f718e3e..dce008e1fa 100644 --- a/src/cmd/serve.rs +++ b/src/cmd/serve.rs @@ -461,9 +461,9 @@ pub fn serve( let ws_address = format!("{}:{}", interface, ws_port.unwrap()); let output_path = site.output_path.clone(); - // output path is going to need to be moved later on, so clone it for the - // http closure to avoid contention. - let static_root = output_path.clone(); + // static_root needs to be canonicalized because we do the same for the http server. + let static_root = std::fs::canonicalize(&output_path).unwrap(); + let broadcaster = { thread::spawn(move || { let addr = address.parse().unwrap(); From 8b602b2fde11534643a5aa1cb5c0c474f0f49301 Mon Sep 17 00:00:00 2001 From: Clar Fon <15850505+clarfonthey@users.noreply.github.com> Date: Sun, 7 Jan 2024 15:14:21 -0500 Subject: [PATCH 05/25] Allow IPv6 serve, default `base_url` to listen interface instead of 127.0.0.1 (#2395) * Parse interface as IpAddr, allow IPv6. * Default base_url to socket address, instead of 127.0.0.1 --- src/cli.rs | 7 ++++--- src/cmd/serve.rs | 41 ++++++++++++++++++++++------------------- src/main.rs | 4 ++-- 3 files changed, 28 insertions(+), 24 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index b55dc18a5e..1329edff4d 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,3 +1,4 @@ +use std::net::IpAddr; use std::path::PathBuf; use clap::{Parser, Subcommand}; @@ -54,7 +55,7 @@ pub enum Command { Serve { /// Interface to bind on #[clap(short = 'i', long, default_value = "127.0.0.1")] - interface: String, + interface: IpAddr, /// Which port to use #[clap(short = 'p', long, default_value_t = 1111)] @@ -70,8 +71,8 @@ pub enum Command { force: bool, /// Changes the base_url - #[clap(short = 'u', long, default_value = "127.0.0.1")] - base_url: String, + #[clap(short = 'u', long)] + base_url: Option, /// Include drafts when loading the site #[clap(long)] diff --git a/src/cmd/serve.rs b/src/cmd/serve.rs index dce008e1fa..d13b945942 100644 --- a/src/cmd/serve.rs +++ b/src/cmd/serve.rs @@ -24,7 +24,7 @@ use std::cell::Cell; use std::fs::read_dir; use std::future::IntoFuture; -use std::net::{SocketAddrV4, TcpListener}; +use std::net::{IpAddr, SocketAddr, TcpListener}; use std::path::{Path, PathBuf, MAIN_SEPARATOR}; use std::sync::mpsc::channel; use std::sync::Mutex; @@ -321,20 +321,29 @@ fn rebuild_done_handling(broadcaster: &Sender, res: Result<()>, reload_path: &st #[allow(clippy::too_many_arguments)] fn create_new_site( root_dir: &Path, - interface: &str, + interface: IpAddr, interface_port: u16, output_dir: Option<&Path>, force: bool, - base_url: &str, + base_url: Option<&str>, config_file: &Path, include_drafts: bool, - no_port_append: bool, + mut no_port_append: bool, ws_port: Option, -) -> Result<(Site, String)> { +) -> Result<(Site, SocketAddr)> { SITE_CONTENT.write().unwrap().clear(); let mut site = Site::new(root_dir, config_file)?; - let address = format!("{}:{}", interface, interface_port); + let address = SocketAddr::new(interface, interface_port); + + // if no base URL provided, use socket address + let base_url = base_url.map_or_else( + || { + no_port_append = true; + address.to_string() + }, + |u| u.to_string(), + ); let base_url = if base_url == "/" { String::from("/") @@ -381,11 +390,11 @@ fn create_new_site( #[allow(clippy::too_many_arguments)] pub fn serve( root_dir: &Path, - interface: &str, + interface: IpAddr, interface_port: u16, output_dir: Option<&Path>, force: bool, - base_url: &str, + base_url: Option<&str>, config_file: &Path, open: bool, include_drafts: bool, @@ -394,7 +403,7 @@ pub fn serve( utc_offset: UtcOffset, ) -> Result<()> { let start = Instant::now(); - let (mut site, address) = create_new_site( + let (mut site, bind_address) = create_new_site( root_dir, interface, interface_port, @@ -409,12 +418,8 @@ pub fn serve( messages::report_elapsed_time(start); // Stop right there if we can't bind to the address - let bind_address: SocketAddrV4 = match address.parse() { - Ok(a) => a, - Err(_) => return Err(anyhow!("Invalid address: {}.", address)), - }; if (TcpListener::bind(bind_address)).is_err() { - return Err(anyhow!("Cannot start server on address {}.", address)); + return Err(anyhow!("Cannot start server on address {}.", bind_address)); } let config_path = PathBuf::from(config_file); @@ -466,8 +471,6 @@ pub fn serve( let broadcaster = { thread::spawn(move || { - let addr = address.parse().unwrap(); - let rt = tokio::runtime::Builder::new_current_thread() .enable_all() .build() @@ -484,11 +487,11 @@ pub fn serve( } }); - let server = Server::bind(&addr).serve(make_service); + let server = Server::bind(&bind_address).serve(make_service); - println!("Web server is available at http://{}\n", &address); + println!("Web server is available at http://{}\n", bind_address); if open { - if let Err(err) = open::that(format!("http://{}", &address)) { + if let Err(err) = open::that(format!("http://{}", bind_address)) { eprintln!("Failed to open URL in your browser: {}", err); } } diff --git a/src/main.rs b/src/main.rs index 59bc80df5c..48e95b9693 100644 --- a/src/main.rs +++ b/src/main.rs @@ -102,11 +102,11 @@ fn main() { console::info("Building site..."); if let Err(e) = cmd::serve( &root_dir, - &interface, + interface, port, output_dir.as_deref(), force, - &base_url, + base_url.as_deref(), &config_file, open, drafts, From a6bd2b90131545bcef38c16379d9550296f10dcd Mon Sep 17 00:00:00 2001 From: Clar Fon <15850505+clarfonthey@users.noreply.github.com> Date: Thu, 18 Jan 2024 04:07:59 -0500 Subject: [PATCH 06/25] Refactor: create_directory responsibly (#2407) * Remove ensure_directory_exists since it's identical to create_directory, and misleading * Don't create directories unless needed; rely on create_dir_all instead of manually iterating over components --- components/imageproc/src/processor.rs | 4 +-- components/site/src/lib.rs | 39 +++++++-------------------- components/site/src/sass.rs | 4 +-- components/utils/src/fs.rs | 8 ------ 4 files changed, 14 insertions(+), 41 deletions(-) diff --git a/components/imageproc/src/processor.rs b/components/imageproc/src/processor.rs index 4ef9869698..63346663a6 100644 --- a/components/imageproc/src/processor.rs +++ b/components/imageproc/src/processor.rs @@ -177,7 +177,7 @@ impl Processor { /// Run the enqueued image operations pub fn do_process(&mut self) -> Result<()> { if !self.img_ops.is_empty() { - ufs::ensure_directory_exists(&self.output_dir)?; + ufs::create_directory(&self.output_dir)?; } self.img_ops @@ -197,7 +197,7 @@ impl Processor { return Ok(()); } - ufs::ensure_directory_exists(&self.output_dir)?; + ufs::create_directory(&self.output_dir)?; let output_paths: HashSet<_> = self .img_ops .iter() diff --git a/components/site/src/lib.rs b/components/site/src/lib.rs index 91af546168..ab38ddc8e4 100644 --- a/components/site/src/lib.rs +++ b/components/site/src/lib.rs @@ -23,7 +23,6 @@ use std::time::Instant; use templates::{load_tera, render_redirect_template}; use utils::fs::{ clean_site_output_folder, copy_directory, copy_file_if_needed, create_directory, create_file, - ensure_directory_exists, }; use utils::net::{get_available_port, is_external_link}; use utils::templates::{render_template, ShortcodeDefinition}; @@ -639,7 +638,6 @@ impl Site { create_dirs: bool, ) -> Result { let write_dirs = self.build_mode == BuildMode::Disk || create_dirs; - ensure_directory_exists(&self.output_path)?; let mut site_path = RelativePathBuf::new(); let mut current_path = self.output_path.to_path_buf(); @@ -647,10 +645,6 @@ impl Site { for component in components { current_path.push(component); site_path.push(component); - - if !current_path.exists() && write_dirs { - create_directory(¤t_path)?; - } } if write_dirs { @@ -788,9 +782,13 @@ impl Site { } pub fn render_themes_css(&self) -> Result<()> { - ensure_directory_exists(&self.static_path)?; + let themes = &self.config.markdown.highlight_themes_css; + + if !themes.is_empty() { + create_directory(&self.static_path)?; + } - for t in &self.config.markdown.highlight_themes_css { + for t in themes { let p = self.static_path.join(&t.filename); if !p.exists() { let content = &self.config.markdown.export_theme_css(&t.theme)?; @@ -818,7 +816,7 @@ impl Site { } pub fn build_search_index(&self) -> Result<()> { - ensure_directory_exists(&self.output_path)?; + create_directory(&self.output_path)?; // TODO: add those to the SITE_CONTENT map // index first @@ -857,7 +855,6 @@ impl Site { /// Renders all the aliases for each page/section: a magic HTML template that redirects to /// the canonical one pub fn render_aliases(&self) -> Result<()> { - ensure_directory_exists(&self.output_path)?; let library = self.library.read().unwrap(); for (_, page) in &library.pages { for alias in &page.meta.aliases { @@ -874,7 +871,6 @@ impl Site { /// Renders 404.html pub fn render_404(&self) -> Result<()> { - ensure_directory_exists(&self.output_path)?; let mut context = Context::new(); context.insert("config", &self.config.serialize(&self.config.default_language)); context.insert("lang", &self.config.default_language); @@ -886,7 +882,6 @@ impl Site { /// Renders robots.txt pub fn render_robots(&self) -> Result<()> { - ensure_directory_exists(&self.output_path)?; let mut context = Context::new(); context.insert("config", &self.config.serialize(&self.config.default_language)); let content = render_template("robots.txt", &self.tera, context, &self.config.theme)?; @@ -911,8 +906,6 @@ impl Site { return Ok(()); } - ensure_directory_exists(&self.output_path)?; - let mut components = Vec::new(); if taxonomy.lang != self.config.default_language { components.push(taxonomy.lang.as_ref()); @@ -977,8 +970,6 @@ impl Site { /// What it says on the tin pub fn render_sitemap(&self) -> Result<()> { - ensure_directory_exists(&self.output_path)?; - let library = self.library.read().unwrap(); let all_sitemap_entries = { sitemap::find_entries(&library, &self.taxonomies[..], &self.config) }; @@ -1032,8 +1023,6 @@ impl Site { lang: &str, additional_context_fn: impl Fn(Context) -> Context, ) -> Result<()> { - ensure_directory_exists(&self.output_path)?; - let feed = match feed::render_feed(self, all_pages, lang, base_path, additional_context_fn)? { Some(v) => v, @@ -1060,7 +1049,6 @@ impl Site { /// Renders a single section pub fn render_section(&self, section: &Section, render_pages: bool) -> Result<()> { - ensure_directory_exists(&self.output_path)?; let mut output_path = self.output_path.clone(); let mut components: Vec<&str> = Vec::new(); let create_directories = self.build_mode == BuildMode::Disk || !section.assets.is_empty(); @@ -1068,19 +1056,15 @@ impl Site { if section.lang != self.config.default_language { components.push(§ion.lang); output_path.push(§ion.lang); - - if !output_path.exists() && create_directories { - create_directory(&output_path)?; - } } for component in §ion.file.components { components.push(component); output_path.push(component); + } - if !output_path.exists() && create_directories { - create_directory(&output_path)?; - } + if create_directories { + create_directory(&output_path)?; } if section.meta.generate_feed { @@ -1166,7 +1150,6 @@ impl Site { /// Renders all pages that do not belong to any sections pub fn render_orphan_pages(&self) -> Result<()> { - ensure_directory_exists(&self.output_path)?; let library = self.library.read().unwrap(); for page in library.get_all_orphan_pages() { self.render_page(page)?; @@ -1181,8 +1164,6 @@ impl Site { components: Vec<&'a str>, paginator: &'a Paginator, ) -> Result<()> { - ensure_directory_exists(&self.output_path)?; - let index_components = components.clone(); paginator diff --git a/components/site/src/sass.rs b/components/site/src/sass.rs index 87f5c7dd8c..a5fddedf0b 100644 --- a/components/site/src/sass.rs +++ b/components/site/src/sass.rs @@ -7,10 +7,10 @@ use libs::walkdir::{DirEntry, WalkDir}; use crate::anyhow; use errors::{bail, Result}; -use utils::fs::{create_file, ensure_directory_exists}; +use utils::fs::{create_directory, create_file}; pub fn compile_sass(base_path: &Path, output_path: &Path) -> Result<()> { - ensure_directory_exists(output_path)?; + create_directory(output_path)?; let sass_path = { let mut sass_path = PathBuf::from(base_path); diff --git a/components/utils/src/fs.rs b/components/utils/src/fs.rs index 1d3dbdfa5c..e1a1a5f29a 100644 --- a/components/utils/src/fs.rs +++ b/components/utils/src/fs.rs @@ -27,14 +27,6 @@ pub fn create_file(path: &Path, content: &str) -> Result<()> { Ok(()) } -/// Create a directory at the given path if it doesn't exist already -pub fn ensure_directory_exists(path: &Path) -> Result<()> { - if !path.exists() { - create_directory(path)?; - } - Ok(()) -} - /// Very similar to `create_dir` from the std except it checks if the folder /// exists before creating it pub fn create_directory(path: &Path) -> Result<()> { From 012284516b37512a7d87c834041220df8205c354 Mon Sep 17 00:00:00 2001 From: Clar Fon <15850505+clarfonthey@users.noreply.github.com> Date: Mon, 22 Jan 2024 06:57:42 -0500 Subject: [PATCH 07/25] Refactor: copy_assets instead of copy_asset (#2418) --- components/site/src/lib.rs | 39 +++++++++++++++----------------------- 1 file changed, 15 insertions(+), 24 deletions(-) diff --git a/components/site/src/lib.rs b/components/site/src/lib.rs index ab38ddc8e4..dce82d812f 100644 --- a/components/site/src/lib.rs +++ b/components/site/src/lib.rs @@ -676,8 +676,19 @@ impl Site { Ok(current_path) } - fn copy_asset(&self, src: &Path, dest: &Path) -> Result<()> { - copy_file_if_needed(src, dest, self.config.hard_link_static) + fn copy_assets(&self, parent: &Path, assets: &[impl AsRef], dest: &Path) -> Result<()> { + for asset in assets { + let asset_path = asset.as_ref(); + copy_file_if_needed( + asset_path, + &dest.join( + asset_path.strip_prefix(parent).expect("Couldn't get filename from page asset"), + ), + self.config.hard_link_static, + )?; + } + + Ok(()) } /// Renders a single content page @@ -689,17 +700,7 @@ impl Site { self.write_content(&components, "index.html", content, !page.assets.is_empty())?; // Copy any asset we found previously into the same directory as the index.html - for asset in &page.assets { - let asset_path = asset.as_path(); - self.copy_asset( - asset_path, - ¤t_path.join( - asset_path - .strip_prefix(page.file.path.parent().unwrap()) - .expect("Couldn't get filename from page asset"), - ), - )?; - } + self.copy_assets(page.file.path.parent().unwrap(), &page.assets, ¤t_path)?; Ok(()) } @@ -1082,17 +1083,7 @@ impl Site { } // Copy any asset we found previously into the same directory as the index.html - for asset in §ion.assets { - let asset_path = asset.as_path(); - self.copy_asset( - asset_path, - &output_path.join( - asset_path - .strip_prefix(section.file.path.parent().unwrap()) - .expect("Failed to get asset filename for section"), - ), - )?; - } + self.copy_assets(§ion.file.path.parent().unwrap(), §ion.assets, &output_path)?; if render_pages { section From 0098877056322d5c8cc0672bdf41876dc39c2503 Mon Sep 17 00:00:00 2001 From: Andrew Langmeier Date: Sun, 4 Feb 2024 14:59:30 -0500 Subject: [PATCH 08/25] Allow ignoring files when link checking (#2264) * Allow ignoring files when link checking * cargo fmt * Fix tests * Remove mystery duplicate function..? * Add in some mysterious missing code..? * Simple tests for link checker file globs in config * cargo fmt * Remove comment * convert expect to error propagation * Address comments * cargo fmt --- components/config/src/config/link_checker.rs | 18 ++- components/config/src/config/mod.rs | 110 ++++++++++++------- components/site/src/link_checking.rs | 10 +- components/utils/src/globs.rs | 20 ++++ components/utils/src/lib.rs | 1 + 5 files changed, 117 insertions(+), 42 deletions(-) create mode 100644 components/utils/src/globs.rs diff --git a/components/config/src/config/link_checker.rs b/components/config/src/config/link_checker.rs index 8501287981..7668c71b1f 100644 --- a/components/config/src/config/link_checker.rs +++ b/components/config/src/config/link_checker.rs @@ -1,5 +1,9 @@ +use libs::globset::GlobSet; use serde::{Deserialize, Serialize}; +use errors::Result; +use utils::globs::build_ignore_glob_set; + #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum LinkCheckerLevel { #[serde(rename = "error")] @@ -14,7 +18,7 @@ impl Default for LinkCheckerLevel { } } -#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Clone, Debug, Default, Serialize, Deserialize)] #[serde(default)] pub struct LinkChecker { /// Skip link checking for these URL prefixes @@ -25,4 +29,16 @@ pub struct LinkChecker { pub internal_level: LinkCheckerLevel, /// Emit either "error" or "warn" for broken external links (including anchor links). pub external_level: LinkCheckerLevel, + /// A list of file glob patterns to skip link checking on + pub ignored_files: Vec, + #[serde(skip_serializing, skip_deserializing)] // not a typo, 2 are needed + pub ignored_files_globset: Option, +} + +impl LinkChecker { + pub fn resolve_globset(&mut self) -> Result<()> { + let glob_set = build_ignore_glob_set(&self.ignored_files, "files")?; + self.ignored_files_globset = Some(glob_set); + Ok(()) + } } diff --git a/components/config/src/config/mod.rs b/components/config/src/config/mod.rs index a79542e85e..f2a40675ae 100644 --- a/components/config/src/config/mod.rs +++ b/components/config/src/config/mod.rs @@ -8,13 +8,14 @@ pub mod taxonomies; use std::collections::HashMap; use std::path::{Path, PathBuf}; -use libs::globset::{Glob, GlobSet, GlobSetBuilder}; +use libs::globset::GlobSet; use libs::toml::Value as Toml; use serde::{Deserialize, Serialize}; use crate::theme::Theme; use errors::{anyhow, bail, Result}; use utils::fs::read_file; +use utils::globs::build_ignore_glob_set; use utils::slugs::slugify_paths; // We want a default base url for tests @@ -28,18 +29,6 @@ pub enum Mode { Check, } -fn build_ignore_glob_set(ignore: &Vec, name: &str) -> Result { - let mut glob_set_builder = GlobSetBuilder::new(); - for pat in ignore { - let glob = match Glob::new(pat) { - Ok(g) => g, - Err(e) => bail!("Invalid ignored_{} glob pattern: {}, error = {}", name, pat, e), - }; - glob_set_builder.add(glob); - } - Ok(glob_set_builder.build().unwrap_or_else(|_| panic!("Bad ignored_{} in config file.", name))) -} - #[derive(Clone, Debug, Deserialize)] #[serde(default)] pub struct Config { @@ -150,21 +139,13 @@ impl Config { config.add_default_language()?; config.slugify_taxonomies(); + config.link_checker.resolve_globset()?; - if !config.ignored_content.is_empty() { - // Convert the file glob strings into a compiled glob set matcher. We want to do this once, - // at program initialization, rather than for every page, for example. We arrange for the - // globset matcher to always exist (even though it has to be an inside an Option at the - // moment because of the TOML serializer); if the glob set is empty the `is_match` function - // of the globber always returns false. - let glob_set = build_ignore_glob_set(&config.ignored_content, "content")?; - config.ignored_content_globset = Some(glob_set); - } + let content_glob_set = build_ignore_glob_set(&config.ignored_content, "content")?; + config.ignored_content_globset = Some(content_glob_set); - if !config.ignored_static.is_empty() { - let glob_set = build_ignore_glob_set(&config.ignored_static, "static")?; - config.ignored_static_globset = Some(glob_set); - } + let static_glob_set = build_ignore_glob_set(&config.ignored_static, "static")?; + config.ignored_static_globset = Some(static_glob_set); Ok(config) } @@ -652,45 +633,41 @@ title = "A title" } #[test] - fn missing_ignored_content_results_in_empty_vector_and_empty_globset() { + fn missing_ignored_content_results_in_empty_vector() { let config_str = r#" title = "My site" base_url = "example.com" "#; let config = Config::parse(config_str).unwrap(); - let v = config.ignored_content; - assert_eq!(v.len(), 0); - assert!(config.ignored_content_globset.is_none()); + assert_eq!(config.ignored_content.len(), 0); } #[test] - fn missing_ignored_static_results_in_empty_vector_and_empty_globset() { + fn empty_ignored_content_results_in_empty_vector() { let config_str = r#" title = "My site" base_url = "example.com" +ignored_content = [] "#; + let config = Config::parse(config_str).unwrap(); - let v = config.ignored_static; - assert_eq!(v.len(), 0); - assert!(config.ignored_static_globset.is_none()); + assert_eq!(config.ignored_content.len(), 0); } #[test] - fn empty_ignored_content_results_in_empty_vector_and_empty_globset() { + fn missing_ignored_static_results_in_empty_vector() { let config_str = r#" title = "My site" base_url = "example.com" -ignored_content = [] "#; let config = Config::parse(config_str).unwrap(); - assert_eq!(config.ignored_content.len(), 0); - assert!(config.ignored_content_globset.is_none()); + assert_eq!(config.ignored_static.len(), 0); } #[test] - fn empty_ignored_static_results_in_empty_vector_and_empty_globset() { + fn empty_ignored_static_results_in_empty_vector() { let config_str = r#" title = "My site" base_url = "example.com" @@ -699,7 +676,30 @@ ignored_static = [] let config = Config::parse(config_str).unwrap(); assert_eq!(config.ignored_static.len(), 0); - assert!(config.ignored_static_globset.is_none()); + } + + #[test] + fn missing_link_checker_ignored_files_results_in_empty_vector() { + let config_str = r#" +title = "My site" +base_url = "example.com" + "#; + + let config = Config::parse(config_str).unwrap(); + assert_eq!(config.link_checker.ignored_files.len(), 0); + } + + #[test] + fn empty_link_checker_ignored_files_results_in_empty_vector() { + let config_str = r#" +title = "My site" +base_url = "example.com" +[link_checker] +ignored_files = [] + "#; + + let config = Config::parse(config_str).unwrap(); + assert_eq!(config.link_checker.ignored_files.len(), 0); } #[test] @@ -760,6 +760,36 @@ ignored_static = ["*.{graphml,iso}", "*.py?", "**/{target,temp_folder}"] assert!(g.is_match("content/poetry/zen.py2")); } + #[test] + fn non_empty_link_checker_ignored_pages_results_in_vector_of_patterns_and_configured_globset() { + let config_str = r#" +title = "My site" +base_url = "example.com" +[link_checker] +ignored_files = ["*.{graphml,iso}", "*.py?", "**/{target,temp_folder}"] + "#; + + let config = Config::parse(config_str).unwrap(); + let v = config.link_checker.ignored_files; + assert_eq!(v, vec!["*.{graphml,iso}", "*.py?", "**/{target,temp_folder}"]); + + let g = config.link_checker.ignored_files_globset.unwrap(); + assert_eq!(g.len(), 3); + assert!(g.is_match("foo.graphml")); + assert!(g.is_match("foo/bar/foo.graphml")); + assert!(g.is_match("foo.iso")); + assert!(!g.is_match("foo.png")); + assert!(g.is_match("foo.py2")); + assert!(g.is_match("foo.py3")); + assert!(!g.is_match("foo.py")); + assert!(g.is_match("foo/bar/target")); + assert!(g.is_match("foo/bar/baz/temp_folder")); + assert!(g.is_match("foo/bar/baz/temp_folder/target")); + assert!(g.is_match("temp_folder")); + assert!(g.is_match("my/isos/foo.iso")); + assert!(g.is_match("content/poetry/zen.py2")); + } + #[test] fn link_checker_skip_anchor_prefixes() { let config_str = r#" diff --git a/components/site/src/link_checking.rs b/components/site/src/link_checking.rs index b5c1ed2186..1bb1a66941 100644 --- a/components/site/src/link_checking.rs +++ b/components/site/src/link_checking.rs @@ -3,6 +3,7 @@ use std::path::{Path, PathBuf}; use std::{cmp, collections::HashMap, collections::HashSet, iter::FromIterator, thread}; use config::LinkCheckerLevel; +use libs::globset::GlobSet; use libs::rayon::prelude::*; use crate::Site; @@ -105,6 +106,10 @@ fn should_skip_by_prefix(link: &str, skip_prefixes: &[String]) -> bool { skip_prefixes.iter().any(|prefix| link.starts_with(prefix)) } +fn should_skip_by_file(file_path: &Path, glob_set: &GlobSet) -> bool { + glob_set.is_match(file_path) +} + fn get_link_domain(link: &str) -> Result { return match Url::parse(link) { Ok(url) => match url.host_str().map(String::from) { @@ -150,9 +155,12 @@ pub fn check_external_links(site: &Site) -> Vec { let mut invalid_url_links: u32 = 0; // First we look at all the external links, skip those the user wants to skip and record // the ones that have invalid URLs + let ignored_files_globset = site.config.link_checker.ignored_files_globset.as_ref().unwrap(); for (file_path, links) in external_links { for link in links { - if should_skip_by_prefix(link, &site.config.link_checker.skip_prefixes) { + if should_skip_by_prefix(link, &site.config.link_checker.skip_prefixes) + || should_skip_by_file(file_path, ignored_files_globset) + { skipped_link_count += 1; } else { match get_link_domain(link) { diff --git a/components/utils/src/globs.rs b/components/utils/src/globs.rs new file mode 100644 index 0000000000..2377964b17 --- /dev/null +++ b/components/utils/src/globs.rs @@ -0,0 +1,20 @@ +use libs::globset::{Glob, GlobSet, GlobSetBuilder}; + +use errors::{bail, Result}; + +pub fn build_ignore_glob_set(ignore: &Vec, name: &str) -> Result { + // Convert the file glob strings into a compiled glob set matcher. We want to do this once, + // at program initialization, rather than for every page, for example. We arrange for the + // globset matcher to always exist (even though it has to be inside an Option at the + // moment because of the TOML serializer); if the glob set is empty the `is_match` function + // of the globber always returns false. + let mut glob_set_builder = GlobSetBuilder::new(); + for pat in ignore { + let glob = match Glob::new(pat) { + Ok(g) => g, + Err(e) => bail!("Invalid ignored_{} glob pattern: {}, error = {}", name, pat, e), + }; + glob_set_builder.add(glob); + } + Ok(glob_set_builder.build()?) +} diff --git a/components/utils/src/lib.rs b/components/utils/src/lib.rs index 3b2ab6022d..619636b8f7 100644 --- a/components/utils/src/lib.rs +++ b/components/utils/src/lib.rs @@ -1,6 +1,7 @@ pub mod anchors; pub mod de; pub mod fs; +pub mod globs; pub mod net; pub mod site; pub mod slugs; From 73e06bd4cf0712d2f36675334c29594cb2fb9b34 Mon Sep 17 00:00:00 2001 From: SumDonkuS Date: Sun, 4 Feb 2024 14:39:18 -0700 Subject: [PATCH 09/25] Issue 2359 get section by lang (#2410) * adding optional `lang` arugment to `get_section` global function * Add handling of default language passed in `lang` argument of `get_section` * Remove clones for path. Change "?" to an explicit check for error * lint changes * Clean up error handling for add_lang_to_path call * fix format * Add optional parameter "lang" to get_page template function. Add check for language available in config. * Modify helper function name from calculate_path to get_path_with_lang. Modify documentation for get_section and get_page to include equivalent calls without using lang argument to demostrate how lang argument effects pathing. --- components/site/src/tpls.rs | 17 +- .../templates/src/global_fns/content.rs | 274 ++++++++++++++++-- .../documentation/templates/overview.md | 24 ++ 3 files changed, 290 insertions(+), 25 deletions(-) diff --git a/components/site/src/tpls.rs b/components/site/src/tpls.rs index d1e2eacd5a..c51d0de175 100644 --- a/components/site/src/tpls.rs +++ b/components/site/src/tpls.rs @@ -1,5 +1,6 @@ use crate::Site; use libs::tera::Result as TeraResult; +use std::sync::Arc; use templates::{filters, global_fns}; /// Adds global fns that are to be available to shortcodes while rendering markdown @@ -74,13 +75,25 @@ pub fn register_early_global_fns(site: &mut Site) -> TeraResult<()> { /// Functions filled once we have parsed all the pages/sections only, so not available in shortcodes pub fn register_tera_global_fns(site: &mut Site) { + let language_list: Arc> = + Arc::new(site.config.languages.keys().map(|s| s.to_string()).collect()); site.tera.register_function( "get_page", - global_fns::GetPage::new(site.base_path.clone(), site.library.clone()), + global_fns::GetPage::new( + site.base_path.clone(), + &site.config.default_language, + Arc::clone(&language_list), + site.library.clone(), + ), ); site.tera.register_function( "get_section", - global_fns::GetSection::new(site.base_path.clone(), site.library.clone()), + global_fns::GetSection::new( + site.base_path.clone(), + &site.config.default_language, + Arc::clone(&language_list), + site.library.clone(), + ), ); site.tera.register_function( "get_taxonomy", diff --git a/components/templates/src/global_fns/content.rs b/components/templates/src/global_fns/content.rs index 84c155fd71..17183bb639 100644 --- a/components/templates/src/global_fns/content.rs +++ b/components/templates/src/global_fns/content.rs @@ -1,5 +1,6 @@ use content::{Library, Taxonomy, TaxonomyTerm}; use libs::tera::{from_value, to_value, Function as TeraFn, Result, Value}; +use std::borrow::Cow; use std::collections::HashMap; use std::path::PathBuf; use std::sync::{Arc, RwLock}; @@ -71,14 +72,60 @@ impl TeraFn for GetTaxonomyUrl { } } +fn add_lang_to_path<'a>(path: &str, lang: &str) -> Result> { + match path.rfind('.') { + Some(period_offset) => { + let prefix = path.get(0..period_offset); + let suffix = path.get(period_offset..); + if prefix.is_none() || suffix.is_none() { + Err(format!("Error adding language code to {}", path).into()) + } else { + Ok(Cow::Owned(format!("{}.{}{}", prefix.unwrap(), lang, suffix.unwrap()))) + } + } + None => Ok(Cow::Owned(format!("{}.{}", path, lang))), + } +} + +fn get_path_with_lang<'a>( + path: &'a String, + lang: &Option, + default_lang: &str, + supported_languages: &[String], +) -> Result> { + if supported_languages.contains(&default_lang.to_string()) { + lang.as_ref().map_or_else( + || Ok(Cow::Borrowed(path)), + |lang_code| match default_lang == lang_code { + true => Ok(Cow::Borrowed(path)), + false => add_lang_to_path(path, lang_code), + }, + ) + } else { + Err(format!("Unsupported language {}", default_lang).into()) + } +} + #[derive(Debug)] pub struct GetPage { base_path: PathBuf, + default_lang: String, + supported_languages: Arc>, library: Arc>, } impl GetPage { - pub fn new(base_path: PathBuf, library: Arc>) -> Self { - Self { base_path: base_path.join("content"), library } + pub fn new( + base_path: PathBuf, + default_lang: &str, + supported_languages: Arc>, + library: Arc>, + ) -> Self { + Self { + base_path: base_path.join("content"), + default_lang: default_lang.to_string(), + supported_languages, + library, + } } } impl TeraFn for GetPage { @@ -88,23 +135,50 @@ impl TeraFn for GetPage { args.get("path"), "`get_page` requires a `path` argument with a string value" ); - let full_path = self.base_path.join(&path); - let library = self.library.read().unwrap(); - match library.pages.get(&full_path) { - Some(p) => Ok(to_value(p.serialize(&library)).unwrap()), - None => Err(format!("Page `{}` not found.", path).into()), - } + + let lang = + optional_arg!(String, args.get("lang"), "`get_section`: `lang` must be a string"); + + get_path_with_lang(&path, &lang, &self.default_lang, &self.supported_languages).and_then( + |path_with_lang| { + let full_path = self.base_path.join(path_with_lang.as_ref()); + let library = self.library.read().unwrap(); + + match library.pages.get(&full_path) { + Some(p) => Ok(to_value(p.serialize(&library)).unwrap()), + None => match lang { + Some(lang_code) => { + Err(format!("Page `{}` not found for language `{}`.", path, lang_code) + .into()) + } + None => Err(format!("Page `{}` not found.", path).into()), + }, + } + }, + ) } } #[derive(Debug)] pub struct GetSection { base_path: PathBuf, + default_lang: String, + supported_languages: Arc>, library: Arc>, } impl GetSection { - pub fn new(base_path: PathBuf, library: Arc>) -> Self { - Self { base_path: base_path.join("content"), library } + pub fn new( + base_path: PathBuf, + default_lang: &str, + supported_languages: Arc>, + library: Arc>, + ) -> Self { + Self { + base_path: base_path.join("content"), + default_lang: default_lang.to_string(), + supported_languages, + library, + } } } impl TeraFn for GetSection { @@ -119,19 +193,32 @@ impl TeraFn for GetSection { .get("metadata_only") .map_or(false, |c| from_value::(c.clone()).unwrap_or(false)); - let full_path = self.base_path.join(&path); - let library = self.library.read().unwrap(); - - match library.sections.get(&full_path) { - Some(s) => { - if metadata_only { - Ok(to_value(s.serialize_basic(&library)).unwrap()) - } else { - Ok(to_value(s.serialize(&library)).unwrap()) + let lang = + optional_arg!(String, args.get("lang"), "`get_section`: `lang` must be a string"); + + get_path_with_lang(&path, &lang, self.default_lang.as_str(), &self.supported_languages) + .and_then(|path_with_lang| { + let full_path = self.base_path.join(path_with_lang.as_ref()); + let library = self.library.read().unwrap(); + + match library.sections.get(&full_path) { + Some(s) => { + if metadata_only { + Ok(to_value(s.serialize_basic(&library)).unwrap()) + } else { + Ok(to_value(s.serialize(&library)).unwrap()) + } + } + None => match lang { + Some(lang_code) => Err(format!( + "Section `{}` not found for language `{}`.", + path, lang_code + ) + .into()), + None => Err(format!("Section `{}` not found.", path).into()), + }, } - } - None => Err(format!("Section `{}` not found.", path).into()), - } + }) } } @@ -273,7 +360,148 @@ impl TeraFn for GetTaxonomyTerm { mod tests { use super::*; use config::{Config, TaxonomyConfig}; - use content::TaxonomyTerm; + use content::{FileInfo, Library, Page, Section, SortBy, TaxonomyTerm}; + use std::path::Path; + use std::sync::{Arc, RwLock}; + + fn create_page(title: &str, file_path: &str, lang: &str) -> Page { + let mut page = Page { lang: lang.to_owned(), ..Page::default() }; + page.file = FileInfo::new_page( + Path::new(format!("/test/base/path/{}", file_path).as_str()), + &PathBuf::new(), + ); + page.meta.title = Some(title.to_string()); + page.meta.weight = Some(1); + page.file.find_language("en", &["fr"]).unwrap(); + page + } + + #[test] + fn can_get_page() { + let mut library = Library::default(); + let pages = vec![ + ("Homepage", "content/homepage.md", "en"), + ("Page D'Accueil", "content/homepage.fr.md", "fr"), + ("Blog", "content/blog.md", "en"), + ("Wiki", "content/wiki.md", "en"), + ("Wiki", "content/wiki.fr.md", "fr"), + ("Recipes", "content/wiki/recipes.md", "en"), + ("Recettes", "content/wiki/recipes.fr.md", "fr"), + ("Programming", "content/wiki/programming.md", "en"), + ("La Programmation", "content/wiki/programming.fr.md", "fr"), + ("Novels", "content/novels.md", "en"), + ("Des Romans", "content/novels.fr.md", "fr"), + ]; + for (t, f, l) in pages.clone() { + library.insert_page(create_page(t, f, l)); + } + let base_path = "/test/base/path".into(); + let lang_list = vec!["en".to_string(), "fr".to_string()]; + + let static_fn = + GetPage::new(base_path, "en", Arc::new(lang_list), Arc::new(RwLock::new(library))); + + // Find with lang argument + let mut args = HashMap::new(); + args.insert("path".to_string(), to_value("wiki/recipes.md").unwrap()); + args.insert("lang".to_string(), to_value("fr").unwrap()); + let res = static_fn.call(&args).unwrap(); + let res_obj = res.as_object().unwrap(); + assert_eq!(res_obj["title"], to_value("Recettes").unwrap()); + + // Find with lang in path for legacy support + args = HashMap::new(); + args.insert("path".to_string(), to_value("wiki/recipes.fr.md").unwrap()); + let res = static_fn.call(&args).unwrap(); + let res_obj = res.as_object().unwrap(); + assert_eq!(res_obj["title"], to_value("Recettes").unwrap()); + + // Find with default lang + args = HashMap::new(); + args.insert("path".to_string(), to_value("wiki/recipes.md").unwrap()); + let res = static_fn.call(&args).unwrap(); + let res_obj = res.as_object().unwrap(); + assert_eq!(res_obj["title"], to_value("Recipes").unwrap()); + + // Find with default lang when default lang passed + args = HashMap::new(); + args.insert("path".to_string(), to_value("wiki/recipes.md").unwrap()); + args.insert("lang".to_string(), to_value("en").unwrap()); + let res = static_fn.call(&args).unwrap(); + let res_obj = res.as_object().unwrap(); + assert_eq!(res_obj["title"], to_value("Recipes").unwrap()); + } + + fn create_section(title: &str, file_path: &str, lang: &str) -> Section { + let mut section = Section { lang: lang.to_owned(), ..Section::default() }; + section.file = FileInfo::new_section( + Path::new(format!("/test/base/path/{}", file_path).as_str()), + &PathBuf::new(), + ); + section.meta.title = Some(title.to_string()); + section.meta.weight = 1; + section.meta.transparent = false; + section.meta.sort_by = SortBy::None; + section.meta.page_template = Some("new_page.html".to_owned()); + section.file.find_language("en", &["fr"]).unwrap(); + section + } + + #[test] + fn can_get_section() { + let mut library = Library::default(); + let sections = vec![ + ("Homepage", "content/_index.md", "en"), + ("Page D'Accueil", "content/_index.fr.md", "fr"), + ("Blog", "content/blog/_index.md", "en"), + ("Wiki", "content/wiki/_index.md", "en"), + ("Wiki", "content/wiki/_index.fr.md", "fr"), + ("Recipes", "content/wiki/recipes/_index.md", "en"), + ("Recettes", "content/wiki/recipes/_index.fr.md", "fr"), + ("Programming", "content/wiki/programming/_index.md", "en"), + ("La Programmation", "content/wiki/programming/_index.fr.md", "fr"), + ("Novels", "content/novels/_index.md", "en"), + ("Des Romans", "content/novels/_index.fr.md", "fr"), + ]; + for (t, f, l) in sections.clone() { + library.insert_section(create_section(t, f, l)); + } + let base_path = "/test/base/path".into(); + let lang_list = vec!["en".to_string(), "fr".to_string()]; + + let static_fn = + GetSection::new(base_path, "en", Arc::new(lang_list), Arc::new(RwLock::new(library))); + + // Find with lang argument + let mut args = HashMap::new(); + args.insert("path".to_string(), to_value("wiki/recipes/_index.md").unwrap()); + args.insert("lang".to_string(), to_value("fr").unwrap()); + let res = static_fn.call(&args).unwrap(); + let res_obj = res.as_object().unwrap(); + assert_eq!(res_obj["title"], to_value("Recettes").unwrap()); + + // Find with lang in path for legacy support + args = HashMap::new(); + args.insert("path".to_string(), to_value("wiki/recipes/_index.fr.md").unwrap()); + let res = static_fn.call(&args).unwrap(); + let res_obj = res.as_object().unwrap(); + assert_eq!(res_obj["title"], to_value("Recettes").unwrap()); + + // Find with default lang + args = HashMap::new(); + args.insert("path".to_string(), to_value("wiki/recipes/_index.md").unwrap()); + let res = static_fn.call(&args).unwrap(); + let res_obj = res.as_object().unwrap(); + assert_eq!(res_obj["title"], to_value("Recipes").unwrap()); + + // Find with default lang when default lang passed + args = HashMap::new(); + args.insert("path".to_string(), to_value("wiki/recipes/_index.md").unwrap()); + args.insert("lang".to_string(), to_value("en").unwrap()); + let res = static_fn.call(&args).unwrap(); + let res_obj = res.as_object().unwrap(); + assert_eq!(res_obj["title"], to_value("Recipes").unwrap()); + } #[test] fn can_get_taxonomy() { diff --git a/docs/content/documentation/templates/overview.md b/docs/content/documentation/templates/overview.md index b5f1a50947..c94cf86b93 100644 --- a/docs/content/documentation/templates/overview.md +++ b/docs/content/documentation/templates/overview.md @@ -141,6 +141,18 @@ Takes a path to an `.md` file and returns the associated page. The base path is {% set page = get_page(path="blog/page2.md") %} ``` +If selecting a specific language for the page, you can pass `lang` with the language code to the function: + +```jinja2 +{% set page = get_page(path="blog/page2.md", lang="fr") %} + +{# If "fr" is the default language, this is equivalent to #} +{% set page = get_page(path="blog/page2.md") %} + +{# If "fr" is not the default language, this is equivalent to #} +{% set page = get_page(path="blog/page2.fr.md") %} +``` + ### `get_section` Takes a path to an `_index.md` file and returns the associated section. The base path is the `content` directory. @@ -154,6 +166,18 @@ If you only need the metadata of the section, you can pass `metadata_only=true` {% set section = get_section(path="blog/_index.md", metadata_only=true) %} ``` +If selecting a specific language for the section, you can pass `lang` with the language code to the function: + +```jinja2 +{% set section = get_section(path="blog/_index.md", lang="fr") %} + +{# If "fr" is the default language, this is equivalent to #} +{% set section = get_section(path="blog/_index.md") %} + +{# If "fr" is not the default language, this is equivalent to #} +{% set section = get_section(path="blog/_index.fr.md") %} +``` + ### `get_taxonomy_url` Gets the permalink for the taxonomy item found. From 42fc576388c5cc2d460de54ca6838d927272c5fe Mon Sep 17 00:00:00 2001 From: Timon Van Overveldt Date: Sun, 18 Feb 2024 12:57:17 -0800 Subject: [PATCH 10/25] Update pulldown_cmark dep to v0.10, and add pulldown_cmark_escape dep. (#2432) The pulldown_cmark escaping functionality is now shipped in a separate pulldown_cmark_escape crate (https://crates.io/crates/pulldown-cmark-escape), starting with v0.10.0. The markdown.rs module has to be adapted to a few API changes in pulldown_cmark, and we have to introduce explicit handling of alt text to ensure it continues to be properly escaped. There are also a few other behavior changes that are caught by the tests, but these actually seem to be desired, so I've updated the insta snapshot files for those tests to incorporate those changes. Specifically, one footnote-parsing case seems to be handled better now, and pulldown-cmark's `push_html` now doesn't escape quotes in text nodes anymore (see https://github.com/pulldown-cmark/pulldown-cmark/pull/836). --- CHANGELOG.md | 1 + Cargo.lock | 23 ++++- components/libs/Cargo.toml | 3 +- components/libs/src/lib.rs | 1 + components/markdown/src/markdown.rs | 88 +++++++++++++------ ...wn__all_markdown_features_integration.snap | 6 +- .../markdown__can_handle_heading_ids-2.snap | 8 +- .../markdown__can_handle_heading_ids.snap | 6 +- ...des__doesnt_render_ignored_shortcodes.snap | 6 +- 9 files changed, 94 insertions(+), 48 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 44bd027a19..7edc406a2e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## 0.19.0 (unreleased) +- Updates the pulldown-cmark dependency to v0.10.0. This improves footnote handling, and may also introduce some minor behavior changes such as reducing the amount of unnecessary HTML-escaping of text content. ## 0.18.0 (2023-12-18) diff --git a/Cargo.lock b/Cargo.lock index 26050e06e7..0d90eea7c0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1736,7 +1736,8 @@ dependencies = [ "num-format", "once_cell", "percent-encoding", - "pulldown-cmark", + "pulldown-cmark 0.10.0", + "pulldown-cmark-escape", "quickxml_to_serde", "rayon", "regex", @@ -2775,6 +2776,24 @@ dependencies = [ "unicase", ] +[[package]] +name = "pulldown-cmark" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce76ce678ffc8e5675b22aa1405de0b7037e2fdf8913fea40d1926c6fe1e6e7" +dependencies = [ + "bitflags 2.4.1", + "memchr", + "pulldown-cmark-escape", + "unicase", +] + +[[package]] +name = "pulldown-cmark-escape" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5d8f9aa0e3cbcfaf8bf00300004ee3b72f74770f9cbac93f6928771f613276b" + [[package]] name = "pure-rust-locales" version = "0.7.0" @@ -3341,7 +3360,7 @@ dependencies = [ "cargo_metadata", "error-chain", "glob", - "pulldown-cmark", + "pulldown-cmark 0.9.3", "tempfile", "walkdir", ] diff --git a/components/libs/Cargo.toml b/components/libs/Cargo.toml index 5f551ea663..9f6abf1d58 100644 --- a/components/libs/Cargo.toml +++ b/components/libs/Cargo.toml @@ -21,7 +21,8 @@ nom-bibtex = "0.5" num-format = "0.4" once_cell = "1" percent-encoding = "2" -pulldown-cmark = { version = "0.9", default-features = false, features = ["simd"] } +pulldown-cmark = { version = "0.10", default-features = false, features = ["html", "simd"] } +pulldown-cmark-escape = { version = "0.10", default-features = false } quickxml_to_serde = "0.5" rayon = "1" regex = "1" diff --git a/components/libs/src/lib.rs b/components/libs/src/lib.rs index d0772dc0a4..3992c2ed29 100644 --- a/components/libs/src/lib.rs +++ b/components/libs/src/lib.rs @@ -23,6 +23,7 @@ pub use num_format; pub use once_cell; pub use percent_encoding; pub use pulldown_cmark; +pub use pulldown_cmark_escape; pub use quickxml_to_serde; pub use rayon; pub use regex; diff --git a/components/markdown/src/markdown.rs b/components/markdown/src/markdown.rs index 4d30b2ac92..40c94afac5 100644 --- a/components/markdown/src/markdown.rs +++ b/components/markdown/src/markdown.rs @@ -4,19 +4,20 @@ use errors::bail; use libs::gh_emoji::Replacer as EmojiReplacer; use libs::once_cell::sync::Lazy; use libs::pulldown_cmark as cmark; +use libs::pulldown_cmark_escape as cmark_escape; use libs::tera; use utils::net::is_external_link; use crate::context::RenderContext; use errors::{Context, Error, Result}; -use libs::pulldown_cmark::escape::escape_html; +use libs::pulldown_cmark_escape::escape_html; use libs::regex::{Regex, RegexBuilder}; use utils::site::resolve_internal_link; use utils::slugs::slugify_anchors; use utils::table_of_contents::{make_table_of_contents, Heading}; use utils::types::InsertAnchor; -use self::cmark::{Event, LinkType, Options, Parser, Tag}; +use self::cmark::{Event, LinkType, Options, Parser, Tag, TagEnd}; use crate::codeblock::{CodeBlock, FenceSettings}; use crate::shortcode::{Shortcode, SHORTCODE_PLACEHOLDER}; @@ -220,15 +221,15 @@ fn get_heading_refs(events: &[Event]) -> Vec { for (i, event) in events.iter().enumerate() { match event { - Event::Start(Tag::Heading(level, anchor, classes)) => { + Event::Start(Tag::Heading { level, id, classes, .. }) => { heading_refs.push(HeadingRef::new( i, *level as u32, - anchor.map(|a| a.to_owned()), + id.clone().map(|a| a.to_string()), &classes.iter().map(|x| x.to_string()).collect::>(), )); } - Event::End(Tag::Heading(_, _, _)) => { + Event::End(TagEnd::Heading { .. }) => { heading_refs.last_mut().expect("Heading end before start?").end_idx = i; } _ => (), @@ -254,6 +255,10 @@ pub fn markdown_to_html( let mut error = None; let mut code_block: Option = None; + // Indicates whether we're in the middle of parsing a text node which will be placed in an HTML + // attribute, and which hence has to be escaped using escape_html rather than push_html's + // default HTML body escaping for text nodes. + let mut inside_attribute = false; let mut headings: Vec = vec![]; let mut internal_links = Vec::new(); @@ -294,12 +299,19 @@ pub fn markdown_to_html( // we have some text before the shortcode, push that first if $range.start != sc_span.start { - let content = $text[($range.start - orig_range_start) - ..(sc_span.start - orig_range_start)] - .to_string() - .into(); + let content: cmark::CowStr<'_> = + $text[($range.start - orig_range_start) + ..(sc_span.start - orig_range_start)] + .to_string() + .into(); events.push(if $is_text { - Event::Text(content) + if inside_attribute { + let mut buffer = "".to_string(); + escape_html(&mut buffer, content.as_ref()).unwrap(); + Event::Html(buffer.into()) + } else { + Event::Text(content) + } } else { Event::Html(content) }); @@ -370,7 +382,13 @@ pub fn markdown_to_html( }; if !contains_shortcode(text.as_ref()) { - events.push(Event::Text(text)); + if inside_attribute { + let mut buffer = "".to_string(); + escape_html(&mut buffer, text.as_ref()).unwrap(); + events.push(Event::Html(buffer.into())); + } else { + events.push(Event::Text(text)); + } continue; } @@ -386,7 +404,7 @@ pub fn markdown_to_html( code_block = Some(block); events.push(Event::Html(begin.into())); } - Event::End(Tag::CodeBlock(_)) => { + Event::End(TagEnd::CodeBlock { .. }) => { if let Some(ref mut code_block) = code_block { let html = code_block.highlight(&accumulated_block); events.push(Event::Html(html.into())); @@ -397,44 +415,53 @@ pub fn markdown_to_html( code_block = None; events.push(Event::Html("\n".into())); } - Event::Start(Tag::Image(link_type, src, title)) => { - let link = if is_colocated_asset_link(&src) { - let link = format!("{}{}", context.current_page_permalink, &*src); + Event::Start(Tag::Image { link_type, dest_url, title, id }) => { + let link = if is_colocated_asset_link(&dest_url) { + let link = format!("{}{}", context.current_page_permalink, &*dest_url); link.into() } else { - src + dest_url }; events.push(if lazy_async_image { let mut img_before_alt: String = "\"").expect("Could events.push(if lazy_async_image { + Event::End(TagEnd::Image) => events.push(if lazy_async_image { Event::Html("\" loading=\"lazy\" decoding=\"async\" />".into()) } else { event }), - Event::Start(Tag::Link(link_type, link, title)) if link.is_empty() => { + Event::Start(Tag::Link { link_type, dest_url, title, id }) + if dest_url.is_empty() => + { error = Some(Error::msg("There is a link that is missing a URL")); - events.push(Event::Start(Tag::Link(link_type, "#".into(), title))); + events.push(Event::Start(Tag::Link { + link_type, + dest_url: "#".into(), + title, + id, + })); } - Event::Start(Tag::Link(link_type, link, title)) => { + Event::Start(Tag::Link { link_type, dest_url, title, id }) => { let fixed_link = match fix_link( link_type, - &link, + &dest_url, context, &mut internal_links, &mut external_links, @@ -448,12 +475,12 @@ pub fn markdown_to_html( }; events.push( - if is_external_link(&link) + if is_external_link(&dest_url) && context.config.markdown.has_external_link_tweaks() { let mut escaped = String::new(); // write_str can fail but here there are no reasons it should (afaik?) - cmark::escape::escape_href(&mut escaped, &link) + cmark_escape::escape_href(&mut escaped, &dest_url) .expect("Could not write to buffer"); Event::Html( context @@ -463,7 +490,12 @@ pub fn markdown_to_html( .into(), ) } else { - Event::Start(Tag::Link(link_type, fixed_link.into(), title)) + Event::Start(Tag::Link { + link_type, + dest_url: fixed_link.into(), + title, + id, + }) }, ) } @@ -485,7 +517,7 @@ pub fn markdown_to_html( events.push(event); } - Event::End(Tag::Paragraph) => { + Event::End(TagEnd::Paragraph) => { events.push(if stop_next_end_p { stop_next_end_p = false; Event::Html("".into()) diff --git a/components/markdown/tests/snapshots/markdown__all_markdown_features_integration.snap b/components/markdown/tests/snapshots/markdown__all_markdown_features_integration.snap index 72aedc4c21..9e95d41805 100644 --- a/components/markdown/tests/snapshots/markdown__all_markdown_features_integration.snap +++ b/components/markdown/tests/snapshots/markdown__all_markdown_features_integration.snap @@ -1,8 +1,6 @@ --- -source: components/rendering/tests/markdown.rs -assertion_line: 358 +source: components/markdown/tests/markdown.rs expression: body - ---

h1 Heading

@@ -83,7 +81,7 @@ line 1 of code line 2 of code line 3 of code -

Block code "fences"

+

Block code "fences"

Sample text here...
 

Syntax highlighting

diff --git a/components/markdown/tests/snapshots/markdown__can_handle_heading_ids-2.snap b/components/markdown/tests/snapshots/markdown__can_handle_heading_ids-2.snap index d2847a8e94..e9fabcc4fa 100644 --- a/components/markdown/tests/snapshots/markdown__can_handle_heading_ids-2.snap +++ b/components/markdown/tests/snapshots/markdown__can_handle_heading_ids-2.snap @@ -1,12 +1,10 @@ --- -source: components/rendering/tests/markdown.rs -assertion_line: 84 +source: components/markdown/tests/markdown.rs expression: body - ---

Hello

Hello

-

L'écologie et vous

+

L'écologie et vous

Hello

Hello

Hello

@@ -22,6 +20,6 @@ expression: body

text 1 there

1

footnote

-

Classes

+

Classes

diff --git a/components/markdown/tests/snapshots/markdown__can_handle_heading_ids.snap b/components/markdown/tests/snapshots/markdown__can_handle_heading_ids.snap index 87cce483ae..663be5947f 100644 --- a/components/markdown/tests/snapshots/markdown__can_handle_heading_ids.snap +++ b/components/markdown/tests/snapshots/markdown__can_handle_heading_ids.snap @@ -1,8 +1,6 @@ --- -source: components/rendering/tests/markdown.rs -assertion_line: 79 +source: components/markdown/tests/markdown.rs expression: body - ---

Hello

Hello

@@ -22,6 +20,6 @@ expression: body

text 1 there

1

footnote

-

Classes

+

Classes

diff --git a/components/markdown/tests/snapshots/shortcodes__doesnt_render_ignored_shortcodes.snap b/components/markdown/tests/snapshots/shortcodes__doesnt_render_ignored_shortcodes.snap index 64d9bfbabe..15c94e4678 100644 --- a/components/markdown/tests/snapshots/shortcodes__doesnt_render_ignored_shortcodes.snap +++ b/components/markdown/tests/snapshots/shortcodes__doesnt_render_ignored_shortcodes.snap @@ -1,8 +1,6 @@ --- -source: components/rendering/tests/shortcodes.rs -assertion_line: 104 +source: components/markdown/tests/shortcodes.rs expression: body - --- -

{{ youtube(id="w7Ft2ymGmfc") }}

+

{{ youtube(id="w7Ft2ymGmfc") }}

From 472b1f574c662ae3d9a890f5ece0b53e30380cdb Mon Sep 17 00:00:00 2001 From: Clar Fon <15850505+clarfonthey@users.noreply.github.com> Date: Thu, 29 Feb 2024 07:46:29 -0500 Subject: [PATCH 11/25] Add MIME type to get_image_metadata (#2409) --- components/imageproc/src/meta.rs | 4 +++- components/imageproc/tests/resize_image.rs | 23 +++++++++++++++---- components/templates/src/global_fns/images.rs | 8 +++++++ .../documentation/templates/overview.md | 2 +- 4 files changed, 31 insertions(+), 6 deletions(-) diff --git a/components/imageproc/src/meta.rs b/components/imageproc/src/meta.rs index a619e97d28..8ee83c335f 100644 --- a/components/imageproc/src/meta.rs +++ b/components/imageproc/src/meta.rs @@ -37,11 +37,12 @@ pub struct ImageMetaResponse { pub width: u32, pub height: u32, pub format: Option<&'static str>, + pub mime: Option<&'static str>, } impl ImageMetaResponse { pub fn new_svg(width: u32, height: u32) -> Self { - Self { width, height, format: Some("svg") } + Self { width, height, format: Some("svg"), mime: Some("text/svg+xml") } } } @@ -51,6 +52,7 @@ impl From for ImageMetaResponse { width: im.size.0, height: im.size.1, format: im.format.and_then(|f| f.extensions_str().first()).copied(), + mime: im.format.map(|f| f.to_mime_type()), } } } diff --git a/components/imageproc/tests/resize_image.rs b/components/imageproc/tests/resize_image.rs index 7715687f8e..c52ff9321e 100644 --- a/components/imageproc/tests/resize_image.rs +++ b/components/imageproc/tests/resize_image.rs @@ -136,7 +136,12 @@ fn resize_image_webp_jpg() { fn read_image_metadata_jpg() { assert_eq!( image_meta_test("jpg.jpg"), - ImageMetaResponse { width: 300, height: 380, format: Some("jpg") } + ImageMetaResponse { + width: 300, + height: 380, + format: Some("jpg"), + mime: Some("image/jpeg") + } ); } @@ -144,7 +149,7 @@ fn read_image_metadata_jpg() { fn read_image_metadata_png() { assert_eq!( image_meta_test("png.png"), - ImageMetaResponse { width: 300, height: 380, format: Some("png") } + ImageMetaResponse { width: 300, height: 380, format: Some("png"), mime: Some("image/png") } ); } @@ -152,7 +157,12 @@ fn read_image_metadata_png() { fn read_image_metadata_svg() { assert_eq!( image_meta_test("svg.svg"), - ImageMetaResponse { width: 300, height: 300, format: Some("svg") } + ImageMetaResponse { + width: 300, + height: 300, + format: Some("svg"), + mime: Some("text/svg+xml") + } ); } @@ -160,7 +170,12 @@ fn read_image_metadata_svg() { fn read_image_metadata_webp() { assert_eq!( image_meta_test("webp.webp"), - ImageMetaResponse { width: 300, height: 380, format: Some("webp") } + ImageMetaResponse { + width: 300, + height: 380, + format: Some("webp"), + mime: Some("image/webp") + } ); } diff --git a/components/templates/src/global_fns/images.rs b/components/templates/src/global_fns/images.rs index 641e26391c..2ac74b0ebb 100644 --- a/components/templates/src/global_fns/images.rs +++ b/components/templates/src/global_fns/images.rs @@ -269,6 +269,8 @@ mod tests { let data = static_fn.call(&args).unwrap().as_object().unwrap().clone(); assert_eq!(data["height"], to_value(380).unwrap()); assert_eq!(data["width"], to_value(300).unwrap()); + assert_eq!(data["format"], to_value("jpg").unwrap()); + assert_eq!(data["mime"], to_value("image/jpeg").unwrap()); // 2. a call to something in `static` with an absolute path is handled currently the same as the above let mut args = HashMap::new(); @@ -276,6 +278,8 @@ mod tests { let data = static_fn.call(&args).unwrap().as_object().unwrap().clone(); assert_eq!(data["height"], to_value(380).unwrap()); assert_eq!(data["width"], to_value(300).unwrap()); + assert_eq!(data["format"], to_value("jpg").unwrap()); + assert_eq!(data["mime"], to_value("image/jpeg").unwrap()); // 3. a call to something in `content` with a relative path let mut args = HashMap::new(); @@ -283,6 +287,8 @@ mod tests { let data = static_fn.call(&args).unwrap().as_object().unwrap().clone(); assert_eq!(data["height"], to_value(380).unwrap()); assert_eq!(data["width"], to_value(300).unwrap()); + assert_eq!(data["format"], to_value("jpg").unwrap()); + assert_eq!(data["mime"], to_value("image/jpeg").unwrap()); // 4. a call to something in `content` with a @/ path corresponds to let mut args = HashMap::new(); @@ -290,5 +296,7 @@ mod tests { let data = static_fn.call(&args).unwrap().as_object().unwrap().clone(); assert_eq!(data["height"], to_value(380).unwrap()); assert_eq!(data["width"], to_value(300).unwrap()); + assert_eq!(data["format"], to_value("jpg").unwrap()); + assert_eq!(data["mime"], to_value("image/jpeg").unwrap()); } } diff --git a/docs/content/documentation/templates/overview.md b/docs/content/documentation/templates/overview.md index c94cf86b93..49a7d1f886 100644 --- a/docs/content/documentation/templates/overview.md +++ b/docs/content/documentation/templates/overview.md @@ -305,7 +305,7 @@ It can take the following arguments: - `path`: mandatory, see [File Searching Logic](@/documentation/templates/overview.md#file-searching-logic) for details - `allow_missing`: optional, `true` or `false`, defaults to `false`. Whether a missing file should raise an error or not. -The method returns a map containing `width`, `height` and `format` (the lowercased value as string). +The method returns a map containing `width`, `height`, `format`, and `mime`. The `format` returned is the most common file extension for the file format, which may not match the one used for the image. ```jinja2 {% set meta = get_image_metadata(path="...") %} From 4bf67afd875b99d2bad6fe252d6fb6e44f52b313 Mon Sep 17 00:00:00 2001 From: James Williams Date: Fri, 1 Mar 2024 02:17:45 -0800 Subject: [PATCH 12/25] Fix --base-url improper path and protocol handling using `zola serve` (#2311) * Fix --base-url improper path and protocol handling. * Fix formatting. --- src/cmd/serve.rs | 140 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 113 insertions(+), 27 deletions(-) diff --git a/src/cmd/serve.rs b/src/cmd/serve.rs index d13b945942..27c9b057bb 100644 --- a/src/cmd/serve.rs +++ b/src/cmd/serve.rs @@ -92,16 +92,34 @@ fn set_serve_error(msg: &'static str, e: errors::Error) { } } -async fn handle_request(req: Request, mut root: PathBuf) -> Result> { +async fn handle_request( + req: Request, + mut root: PathBuf, + base_path: String, +) -> Result> { + let path_str = req.uri().path(); + if !path_str.starts_with(&base_path) { + return Ok(not_found()); + } + + let trimmed_path = &path_str[base_path.len() - 1..]; + let original_root = root.clone(); let mut path = RelativePathBuf::new(); // https://zola.discourse.group/t/percent-encoding-for-slugs/736 - let decoded = match percent_encoding::percent_decode_str(req.uri().path()).decode_utf8() { + let decoded = match percent_encoding::percent_decode_str(trimmed_path).decode_utf8() { Ok(d) => d, Err(_) => return Ok(not_found()), }; - for c in decoded.split('/') { + let decoded_path = if base_path != "/" && decoded.starts_with(&base_path) { + // Remove the base_path from the request path before processing + decoded[base_path.len()..].to_string() + } else { + decoded.to_string() + }; + + for c in decoded_path.split('/') { path.push(c); } @@ -318,6 +336,39 @@ fn rebuild_done_handling(broadcaster: &Sender, res: Result<()>, reload_path: &st } } +fn construct_url(base_url: &str, no_port_append: bool, interface_port: u16) -> String { + if base_url == "/" { + return String::from("/"); + } + + let (protocol, stripped_url) = match base_url { + url if url.starts_with("http://") => ("http://", &url[7..]), + url if url.starts_with("https://") => ("https://", &url[8..]), + url => ("http://", url), + }; + + let (domain, path) = { + let parts: Vec<&str> = stripped_url.splitn(2, '/').collect(); + if parts.len() > 1 { + (parts[0], format!("/{}", parts[1])) + } else { + (parts[0], String::new()) + } + }; + + let full_address = if no_port_append { + format!("{}{}{}", protocol, domain, path) + } else { + format!("{}{}:{}{}", protocol, domain, interface_port, path) + }; + + if full_address.ends_with('/') { + full_address + } else { + format!("{}/", full_address) + } +} + #[allow(clippy::too_many_arguments)] fn create_new_site( root_dir: &Path, @@ -330,7 +381,7 @@ fn create_new_site( include_drafts: bool, mut no_port_append: bool, ws_port: Option, -) -> Result<(Site, SocketAddr)> { +) -> Result<(Site, SocketAddr, String)> { SITE_CONTENT.write().unwrap().clear(); let mut site = Site::new(root_dir, config_file)?; @@ -345,24 +396,10 @@ fn create_new_site( |u| u.to_string(), ); - let base_url = if base_url == "/" { - String::from("/") - } else { - let base_address = if no_port_append { - base_url.to_string() - } else { - format!("{}:{}", base_url, interface_port) - }; - - if site.config.base_url.ends_with('/') { - format!("http://{}/", base_address) - } else { - format!("http://{}", base_address) - } - }; + let constructed_base_url = construct_url(&base_url, no_port_append, interface_port); site.enable_serve_mode(); - site.set_base_url(base_url); + site.set_base_url(constructed_base_url.clone()); if let Some(output_dir) = output_dir { if !force && output_dir.exists() { return Err(Error::msg(format!( @@ -384,7 +421,7 @@ fn create_new_site( messages::notify_site_size(&site); messages::warn_about_ignored_pages(&site); site.build()?; - Ok((site, address)) + Ok((site, address, constructed_base_url)) } #[allow(clippy::too_many_arguments)] @@ -403,7 +440,7 @@ pub fn serve( utc_offset: UtcOffset, ) -> Result<()> { let start = Instant::now(); - let (mut site, bind_address) = create_new_site( + let (mut site, bind_address, constructed_base_url) = create_new_site( root_dir, interface, interface_port, @@ -415,6 +452,11 @@ pub fn serve( no_port_append, None, )?; + let base_path = match constructed_base_url.splitn(4, '/').nth(3) { + Some(path) => format!("/{}", path), + None => "/".to_string(), + }; + messages::report_elapsed_time(start); // Stop right there if we can't bind to the address @@ -479,19 +521,27 @@ pub fn serve( rt.block_on(async { let make_service = make_service_fn(move |_| { let static_root = static_root.clone(); + let base_path = base_path.clone(); async { Ok::<_, hyper::Error>(service_fn(move |req| { - response_error_injector(handle_request(req, static_root.clone())) + response_error_injector(handle_request( + req, + static_root.clone(), + base_path.clone(), + )) })) } }); let server = Server::bind(&bind_address).serve(make_service); - println!("Web server is available at http://{}\n", bind_address); + println!( + "Web server is available at {} (bound to {})\n", + &constructed_base_url, &bind_address + ); if open { - if let Err(err) = open::that(format!("http://{}", bind_address)) { + if let Err(err) = open::that(format!("{}", &constructed_base_url)) { eprintln!("Failed to open URL in your browser: {}", err); } } @@ -618,7 +668,7 @@ pub fn serve( no_port_append, ws_port, ) { - Ok((s, _)) => { + Ok((s, _, _)) => { clear_serve_error(); rebuild_done_handling(&broadcaster, Ok(()), "/x.js"); @@ -801,7 +851,7 @@ fn is_folder_empty(dir: &Path) -> bool { mod tests { use std::path::{Path, PathBuf}; - use super::{detect_change_kind, is_temp_file, ChangeKind}; + use super::{construct_url, detect_change_kind, is_temp_file, ChangeKind}; #[test] fn can_recognize_temp_files() { @@ -893,4 +943,40 @@ mod tests { let config_filename = Path::new("config.toml"); assert_eq!(expected, detect_change_kind(pwd, path, config_filename)); } + + #[test] + fn test_construct_url_base_url_is_slash() { + let result = construct_url("/", false, 8080); + assert_eq!(result, "/"); + } + + #[test] + fn test_construct_url_http_protocol() { + let result = construct_url("http://example.com", false, 8080); + assert_eq!(result, "http://example.com:8080/"); + } + + #[test] + fn test_construct_url_https_protocol() { + let result = construct_url("https://example.com", false, 8080); + assert_eq!(result, "https://example.com:8080/"); + } + + #[test] + fn test_construct_url_no_protocol() { + let result = construct_url("example.com", false, 8080); + assert_eq!(result, "http://example.com:8080/"); + } + + #[test] + fn test_construct_url_no_port_append() { + let result = construct_url("https://example.com", true, 8080); + assert_eq!(result, "https://example.com/"); + } + + #[test] + fn test_construct_url_trailing_slash() { + let result = construct_url("http://example.com/", false, 8080); + assert_eq!(result, "http://example.com:8080/"); + } } From 7a12e622bec6f021d33c70ca55b2f42e7105429a Mon Sep 17 00:00:00 2001 From: William Ouwehand Date: Thu, 21 Mar 2024 23:23:22 +0100 Subject: [PATCH 13/25] Fix section transparency serialization (#2462) Fixes passing section.transparency for https://github.com/getzola/zola/issues/1840 --- components/content/src/ser.rs | 2 ++ docs/content/documentation/templates/pages-sections.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/components/content/src/ser.rs b/components/content/src/ser.rs index 18e175f11d..607d361dee 100644 --- a/components/content/src/ser.rs +++ b/components/content/src/ser.rs @@ -161,6 +161,7 @@ pub struct SerializingSection<'a> { translations: Vec>, backlinks: Vec>, generate_feed: bool, + transparent: bool, } #[derive(Debug)] @@ -220,6 +221,7 @@ impl<'a> SerializingSection<'a> { assets: §ion.serialized_assets, lang: §ion.lang, generate_feed: section.meta.generate_feed, + transparent: section.meta.transparent, pages, subsections, translations, diff --git a/docs/content/documentation/templates/pages-sections.md b/docs/content/documentation/templates/pages-sections.md index 1b1b15ad8b..5d0d466d3e 100644 --- a/docs/content/documentation/templates/pages-sections.md +++ b/docs/content/documentation/templates/pages-sections.md @@ -109,6 +109,8 @@ translations: Array; backlinks: Array<{permalink: String, title: String?}>; // Whether this section generates a feed or not. Taken from the front-matter if set generate_feed: bool; +// Whether this section is transparent. Taken from the front-matter if set +transparent: bool; ``` ## Table of contents From 866eb0c40541cca4eea0edf033f7a8a1f224ff00 Mon Sep 17 00:00:00 2001 From: Tanishq Date: Wed, 3 Apr 2024 15:55:49 +0530 Subject: [PATCH 14/25] Fix resizing for images with EXIF orientation (#2468) * Fix resizing for images with EXIF orientation * Added test for asymmetric resize for exif images --------- Co-authored-by: Tanishq --- components/imageproc/src/processor.rs | 5 ++--- components/imageproc/tests/resize_image.rs | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/components/imageproc/src/processor.rs b/components/imageproc/src/processor.rs index 63346663a6..2c6a17b725 100644 --- a/components/imageproc/src/processor.rs +++ b/components/imageproc/src/processor.rs @@ -38,7 +38,8 @@ impl ImageOp { return Ok(()); } - let mut img = image::open(&self.input_path)?; + let img = image::open(&self.input_path)?; + let mut img = fix_orientation(&img, &self.input_path).unwrap_or(img); let img = match self.instr.crop_instruction { Some((x, y, w, h)) => img.crop(x, y, w, h), @@ -49,8 +50,6 @@ impl ImageOp { None => img, }; - let img = fix_orientation(&img, &self.input_path).unwrap_or(img); - let f = File::create(&self.output_path)?; let mut buffered_f = BufWriter::new(f); diff --git a/components/imageproc/tests/resize_image.rs b/components/imageproc/tests/resize_image.rs index c52ff9321e..41114abf50 100644 --- a/components/imageproc/tests/resize_image.rs +++ b/components/imageproc/tests/resize_image.rs @@ -248,3 +248,25 @@ fn check_img(img: DynamicImage) -> bool { // bottom right is white && img.get_pixel(15, 15).channels() == [255, 255, 255, 255] } + +#[test] +fn asymmetric_resize_with_exif_orientations() { + // No exif metadata + image_op_test("exif_0.jpg", "scale", Some(16), Some(32), "auto", "jpg", 16, 32, 16, 16); + // 1: Horizontal (normal) + image_op_test("exif_1.jpg", "scale", Some(16), Some(32), "auto", "jpg", 16, 32, 16, 16); + // 2: Mirror horizontal + image_op_test("exif_2.jpg", "scale", Some(16), Some(32), "auto", "jpg", 16, 32, 16, 16); + // 3: Rotate 180 + image_op_test("exif_3.jpg", "scale", Some(16), Some(32), "auto", "jpg", 16, 32, 16, 16); + // 4: Mirror vertical + image_op_test("exif_4.jpg", "scale", Some(16), Some(32), "auto", "jpg", 16, 32, 16, 16); + // 5: Mirror horizontal and rotate 270 CW + image_op_test("exif_5.jpg", "scale", Some(16), Some(32), "auto", "jpg", 16, 32, 16, 16); + // 6: Rotate 90 CW + image_op_test("exif_6.jpg", "scale", Some(16), Some(32), "auto", "jpg", 16, 32, 16, 16); + // 7: Mirror horizontal and rotate 90 CW + image_op_test("exif_7.jpg", "scale", Some(16), Some(32), "auto", "jpg", 16, 32, 16, 16); + // 8: Rotate 270 CW + image_op_test("exif_8.jpg", "scale", Some(16), Some(32), "auto", "jpg", 16, 32, 16, 16); +} From 7bf429b400266a75eb78025b211aa50a3def7f7a Mon Sep 17 00:00:00 2001 From: Tanishq Date: Fri, 5 Apr 2024 20:48:30 +0530 Subject: [PATCH 15/25] Fix link check report inconsistency (#2472) * Fix link check report inconsistency * Fix formatting issue --------- Co-authored-by: Tanishq --- components/site/src/link_checking.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/components/site/src/link_checking.rs b/components/site/src/link_checking.rs index 1bb1a66941..c7e4678195 100644 --- a/components/site/src/link_checking.rs +++ b/components/site/src/link_checking.rs @@ -178,13 +178,15 @@ pub fn check_external_links(site: &Site) -> Vec { } } + // Get unique links count from Vec by creating a temporary HashSet. + let unique_links_count = HashSet::<&str>::from_iter( + checked_links.iter().map(|link_def| link_def.external_link.as_str()), + ) + .len(); + println!( "Checking {} external link(s). Skipping {} external link(s).{}", - // Get unique links count from Vec by creating a temporary HashSet. - HashSet::<&str>::from_iter( - checked_links.iter().map(|link_def| link_def.external_link.as_str()) - ) - .len(), + unique_links_count, skipped_link_count, if invalid_url_links == 0 { "".to_string() @@ -272,7 +274,7 @@ pub fn check_external_links(site: &Site) -> Vec { println!( "> Checked {} external link(s): {} error(s) found.", - checked_links.len(), + unique_links_count, errors.len() ); From 20f00bad300b9d19e2ca596632ca20bd46067e2f Mon Sep 17 00:00:00 2001 From: Clar Fon <15850505+clarfonthey@users.noreply.github.com> Date: Tue, 23 Apr 2024 15:57:30 -0400 Subject: [PATCH 16/25] Refactor: remove even more unnecessary create_directory (#2428) --- components/site/src/lib.rs | 42 ++++++++++++-------------------------- components/utils/src/fs.rs | 22 ++++++++++---------- 2 files changed, 24 insertions(+), 40 deletions(-) diff --git a/components/site/src/lib.rs b/components/site/src/lib.rs index dce82d812f..b906885131 100644 --- a/components/site/src/lib.rs +++ b/components/site/src/lib.rs @@ -635,10 +635,7 @@ impl Site { components: &[&str], filename: &str, content: String, - create_dirs: bool, ) -> Result { - let write_dirs = self.build_mode == BuildMode::Disk || create_dirs; - let mut site_path = RelativePathBuf::new(); let mut current_path = self.output_path.to_path_buf(); @@ -647,10 +644,6 @@ impl Site { site_path.push(component); } - if write_dirs { - create_directory(¤t_path)?; - } - let final_content = if !filename.ends_with("html") || !self.config.minify_html { content } else { @@ -696,8 +689,7 @@ impl Site { let output = page.render_html(&self.tera, &self.config, &self.library.read().unwrap())?; let content = self.inject_livereload(output); let components: Vec<&str> = page.path.split('/').collect(); - let current_path = - self.write_content(&components, "index.html", content, !page.assets.is_empty())?; + let current_path = self.write_content(&components, "index.html", content)?; // Copy any asset we found previously into the same directory as the index.html self.copy_assets(page.file.path.parent().unwrap(), &page.assets, ¤t_path)?; @@ -849,7 +841,7 @@ impl Site { None => "index.html", }; let content = render_redirect_template(permalink, &self.tera)?; - self.write_content(&split, page_name, content, false)?; + self.write_content(&split, page_name, content)?; Ok(()) } @@ -877,7 +869,7 @@ impl Site { context.insert("lang", &self.config.default_language); let output = render_template("404.html", &self.tera, context, &self.config.theme)?; let content = self.inject_livereload(output); - self.write_content(&[], "404.html", content, false)?; + self.write_content(&[], "404.html", content)?; Ok(()) } @@ -886,7 +878,7 @@ impl Site { let mut context = Context::new(); context.insert("config", &self.config.serialize(&self.config.default_language)); let content = render_template("robots.txt", &self.tera, context, &self.config.theme)?; - self.write_content(&[], "robots.txt", content, false)?; + self.write_content(&[], "robots.txt", content)?; Ok(()) } @@ -917,7 +909,7 @@ impl Site { let list_output = taxonomy.render_all_terms(&self.tera, &self.config, &self.library.read().unwrap())?; let content = self.inject_livereload(list_output); - self.write_content(&components, "index.html", content, false)?; + self.write_content(&components, "index.html", content)?; let library = self.library.read().unwrap(); taxonomy @@ -942,7 +934,7 @@ impl Site { let single_output = taxonomy.render_term(item, &self.tera, &self.config, &library)?; let content = self.inject_livereload(single_output); - self.write_content(&comp, "index.html", content, false)?; + self.write_content(&comp, "index.html", content)?; } if taxonomy.kind.feed { @@ -981,7 +973,7 @@ impl Site { let mut context = Context::new(); context.insert("entries", &all_sitemap_entries); let sitemap = render_template("sitemap.xml", &self.tera, context, &self.config.theme)?; - self.write_content(&[], "sitemap.xml", sitemap, false)?; + self.write_content(&[], "sitemap.xml", sitemap)?; return Ok(()); } @@ -994,7 +986,7 @@ impl Site { context.insert("entries", &chunk); let sitemap = render_template("sitemap.xml", &self.tera, context, &self.config.theme)?; let file_name = format!("sitemap{}.xml", i + 1); - self.write_content(&[], &file_name, sitemap, false)?; + self.write_content(&[], &file_name, sitemap)?; let mut sitemap_url = self.config.make_permalink(&file_name); sitemap_url.pop(); // Remove trailing slash sitemap_index.push(sitemap_url); @@ -1009,7 +1001,7 @@ impl Site { main_context, &self.config.theme, )?; - self.write_content(&[], "sitemap.xml", sitemap, false)?; + self.write_content(&[], "sitemap.xml", sitemap)?; Ok(()) } @@ -1040,10 +1032,9 @@ impl Site { &components.iter().map(|x| x.as_ref()).collect::>(), feed_filename, feed, - false, )?; } else { - self.write_content(&[], feed_filename, feed, false)?; + self.write_content(&[], feed_filename, feed)?; } Ok(()) } @@ -1052,7 +1043,6 @@ impl Site { pub fn render_section(&self, section: &Section, render_pages: bool) -> Result<()> { let mut output_path = self.output_path.clone(); let mut components: Vec<&str> = Vec::new(); - let create_directories = self.build_mode == BuildMode::Disk || !section.assets.is_empty(); if section.lang != self.config.default_language { components.push(§ion.lang); @@ -1064,10 +1054,6 @@ impl Site { output_path.push(component); } - if create_directories { - create_directory(&output_path)?; - } - if section.meta.generate_feed { let library = &self.library.read().unwrap(); let pages = section.pages.iter().map(|k| library.pages.get(k).unwrap()).collect(); @@ -1107,7 +1093,6 @@ impl Site { &components, "index.html", render_redirect_template(&permalink, &self.tera)?, - create_directories, )?; return Ok(()); @@ -1122,7 +1107,7 @@ impl Site { let output = section.render_html(&self.tera, &self.config, &self.library.read().unwrap())?; let content = self.inject_livereload(output); - self.write_content(&components, "index.html", content, false)?; + self.write_content(&components, "index.html", content)?; } Ok(()) @@ -1174,14 +1159,13 @@ impl Site { let content = self.inject_livereload(output); if pager.index > 1 { - self.write_content(&pager_components, "index.html", content, false)?; + self.write_content(&pager_components, "index.html", content)?; } else { - self.write_content(&index_components, "index.html", content, false)?; + self.write_content(&index_components, "index.html", content)?; self.write_content( &pager_components, "index.html", render_redirect_template(&paginator.permalink, &self.tera)?, - false, )?; } diff --git a/components/utils/src/fs.rs b/components/utils/src/fs.rs index e1a1a5f29a..2b1557068a 100644 --- a/components/utils/src/fs.rs +++ b/components/utils/src/fs.rs @@ -19,8 +19,17 @@ pub fn is_path_in_directory(parent: &Path, path: &Path) -> Result { Ok(canonical_path.starts_with(canonical_parent)) } +/// Creates the parent of a directory, if needed. +fn create_parent(path: &Path) -> Result<()> { + if let Some(parent) = path.parent() { + create_directory(parent)?; + } + Ok(()) +} + /// Create a file with the content given pub fn create_file(path: &Path, content: &str) -> Result<()> { + create_parent(path)?; let mut file = File::create(path).with_context(|| format!("Failed to create file {}", path.display()))?; file.write_all(content.as_bytes())?; @@ -58,12 +67,7 @@ pub fn copy_file(src: &Path, dest: &Path, base_path: &Path, hard_link: bool) -> let relative_path = src.strip_prefix(base_path).unwrap(); let target_path = dest.join(relative_path); - if let Some(parent_directory) = target_path.parent() { - create_dir_all(parent_directory).with_context(|| { - format!("Failed to create directory {}", parent_directory.display()) - })?; - } - + create_parent(&target_path)?; copy_file_if_needed(src, &target_path, hard_link) } @@ -72,11 +76,7 @@ pub fn copy_file(src: &Path, dest: &Path, base_path: &Path, hard_link: bool) -> /// 2. Its modification timestamp is identical to that of the src file. /// 3. Its filesize is identical to that of the src file. pub fn copy_file_if_needed(src: &Path, dest: &Path, hard_link: bool) -> Result<()> { - if let Some(parent_directory) = dest.parent() { - create_dir_all(parent_directory).with_context(|| { - format!("Failed to create directory {}", parent_directory.display()) - })?; - } + create_parent(&dest)?; if hard_link { if dest.exists() { From 3423b8a6bd02cce1cdeaf623b9016a2d0877a509 Mon Sep 17 00:00:00 2001 From: James Williams Date: Fri, 3 May 2024 11:09:37 -0700 Subject: [PATCH 17/25] Restore trailing slash behaviour in serve command (#2482) * Restore trailing slash behaviour in serve command. * Restore guard in case where base_url is just a slash. --- src/cmd/serve.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/cmd/serve.rs b/src/cmd/serve.rs index 27c9b057bb..d3aa01f660 100644 --- a/src/cmd/serve.rs +++ b/src/cmd/serve.rs @@ -396,7 +396,11 @@ fn create_new_site( |u| u.to_string(), ); - let constructed_base_url = construct_url(&base_url, no_port_append, interface_port); + let mut constructed_base_url = construct_url(&base_url, no_port_append, interface_port); + + if !site.config.base_url.ends_with("/") && constructed_base_url != "/" { + constructed_base_url.truncate(constructed_base_url.len() - 1); + } site.enable_serve_mode(); site.set_base_url(constructed_base_url.clone()); From b76d37cc1b2f28572903b849d6dd312d15d18cfa Mon Sep 17 00:00:00 2001 From: Eugene Lomov Date: Thu, 9 May 2024 16:45:47 +0300 Subject: [PATCH 18/25] Implemented bottom footnotes with backreferences (#2480) * Implemented bottom footnotes with backreferences Fixes #1285 * Added bottom_footnotes option to configuration.md * Renamed fix_github_style_footnotes() * Added tests for convert_footnotes_to_github_style() * Changed test to plain html instead of Vec * Added integration test for footnotes * Applied suggested changes --- components/config/src/config/markup.rs | 3 + components/markdown/src/markdown.rs | 264 +++++++++++++++++- ...down__markdown__tests__def_before_use.snap | 10 + ...down__tests__footnote_inside_footnote.snap | 13 + ...kdown__markdown__tests__multiple_refs.snap | 10 + ...rkdown__markdown__tests__no_footnotes.snap | 6 + ..._markdown__tests__reordered_footnotes.snap | 13 + ...own__markdown__tests__single_footnote.snap | 10 + components/markdown/tests/markdown.rs | 37 +++ .../markdown__github_style_footnotes.snap | 36 +++ .../getting-started/configuration.md | 3 + 11 files changed, 403 insertions(+), 2 deletions(-) create mode 100644 components/markdown/src/snapshots/markdown__markdown__tests__def_before_use.snap create mode 100644 components/markdown/src/snapshots/markdown__markdown__tests__footnote_inside_footnote.snap create mode 100644 components/markdown/src/snapshots/markdown__markdown__tests__multiple_refs.snap create mode 100644 components/markdown/src/snapshots/markdown__markdown__tests__no_footnotes.snap create mode 100644 components/markdown/src/snapshots/markdown__markdown__tests__reordered_footnotes.snap create mode 100644 components/markdown/src/snapshots/markdown__markdown__tests__single_footnote.snap create mode 100644 components/markdown/tests/snapshots/markdown__github_style_footnotes.snap diff --git a/components/config/src/config/markup.rs b/components/config/src/config/markup.rs index b1ac7fa22f..580a665ae6 100644 --- a/components/config/src/config/markup.rs +++ b/components/config/src/config/markup.rs @@ -43,6 +43,8 @@ pub struct Markdown { pub external_links_no_referrer: bool, /// Whether smart punctuation is enabled (changing quotes, dashes, dots etc in their typographic form) pub smart_punctuation: bool, + /// Whether footnotes are rendered at the bottom in the style of GitHub. + pub bottom_footnotes: bool, /// A list of directories to search for additional `.sublime-syntax` and `.tmTheme` files in. pub extra_syntaxes_and_themes: Vec, /// The compiled extra syntaxes into a syntax set @@ -203,6 +205,7 @@ impl Default for Markdown { external_links_no_follow: false, external_links_no_referrer: false, smart_punctuation: false, + bottom_footnotes: false, extra_syntaxes_and_themes: vec![], extra_syntax_set: None, extra_theme_set: Arc::new(None), diff --git a/components/markdown/src/markdown.rs b/components/markdown/src/markdown.rs index 40c94afac5..a8565176c8 100644 --- a/components/markdown/src/markdown.rs +++ b/components/markdown/src/markdown.rs @@ -1,5 +1,7 @@ +use std::collections::HashMap; use std::fmt::Write; +use crate::markdown::cmark::CowStr; use errors::bail; use libs::gh_emoji::Replacer as EmojiReplacer; use libs::once_cell::sync::Lazy; @@ -239,6 +241,158 @@ fn get_heading_refs(events: &[Event]) -> Vec { heading_refs } +fn convert_footnotes_to_github_style(old_events: &mut Vec) { + let events = std::mem::take(old_events); + // step 1: We need to extract footnotes from the event stream and tweak footnote references + + // footnotes bodies are stored in a stack of vectors, because it is possible to have footnotes + // inside footnotes + let mut footnote_bodies_stack = Vec::new(); + let mut footnotes = Vec::new(); + // this will allow to create a multiple back references + let mut footnote_numbers = HashMap::new(); + let filtered_events = events.into_iter().filter_map(|event| { + match event { + // New footnote definition is pushed to the stack + Event::Start(Tag::FootnoteDefinition(_)) => { + footnote_bodies_stack.push(vec![event]); + None + } + // The topmost footnote definition is popped from the stack + Event::End(TagEnd::FootnoteDefinition) => { + // unwrap will never fail, because Tag::FootnoteDefinition always comes before + // TagEnd::FootnoteDefinition + let mut footnote_body = footnote_bodies_stack.pop().unwrap(); + footnote_body.push(event); + footnotes.push(footnote_body); + None + } + Event::FootnoteReference(name) => { + // n will be a unique index of the footnote + let n = footnote_numbers.len() + 1; + // nr is a number of references to this footnote + let (n, nr) = footnote_numbers.entry(name.clone()).or_insert((n, 0usize)); + *nr += 1; + let reference = Event::Html(format!(r##"[{n}]"##).into()); + + if footnote_bodies_stack.is_empty() { + // we are in the main text, just output the reference + Some(reference) + } else { + // we are inside other footnote, we have to push that reference into that + // footnote + footnote_bodies_stack.last_mut().unwrap().push(reference); + None + } + } + _ if !footnote_bodies_stack.is_empty() => { + footnote_bodies_stack.last_mut().unwrap().push(event); + None + } + _ => Some(event), + } + } + ); + + old_events.extend(filtered_events); + + if footnotes.is_empty() { + return; + } + + old_events.push(Event::Html("
    \n".into())); + + // Step 2: retain only footnotes which was actually referenced + footnotes.retain(|f| match f.first() { + Some(Event::Start(Tag::FootnoteDefinition(name))) => { + footnote_numbers.get(name).unwrap_or(&(0, 0)).1 != 0 + } + _ => false, + }); + + // Step 3: Sort footnotes in the order of their appearance + footnotes.sort_by_cached_key(|f| match f.first() { + Some(Event::Start(Tag::FootnoteDefinition(name))) => { + footnote_numbers.get(name).unwrap_or(&(0, 0)).0 + } + _ => unreachable!(), + }); + + // Step 4: Add backreferences to footnotes + let footnotes = footnotes.into_iter().flat_map(|fl| { + // To write backrefs, the name needs kept until the end of the footnote definition. + let mut name = CowStr::from(""); + // Backrefs are included in the final paragraph of the footnote, if it's normal text. + // For example, this DOM can be produced: + // + // Markdown: + // + // five [^feet]. + // + // [^feet]: + // A foot is defined, in this case, as 0.3048 m. + // + // Historically, the foot has not been defined this way, corresponding to many + // subtly different units depending on the location. + // + // HTML: + // + //

    five [1].

    + // + //
      + //
    1. + //

      A foot is defined, in this case, as 0.3048 m.

      + //

      Historically, the foot has not been defined this way, corresponding to many + // subtly different units depending on the location. ↩

      + //
    2. + //
    + // + // This is mostly a visual hack, so that footnotes use less vertical space. + // + // If there is no final paragraph, such as a tabular, list, or image footnote, it gets + // pushed after the last tag instead. + let mut has_written_backrefs = false; + let fl_len = fl.len(); + let footnote_numbers = &footnote_numbers; + fl.into_iter().enumerate().map(move |(i, f)| match f { + Event::Start(Tag::FootnoteDefinition(current_name)) => { + name = current_name; + has_written_backrefs = false; + Event::Html(format!(r##"
  1. "##).into()) + } + Event::End(TagEnd::FootnoteDefinition) | Event::End(TagEnd::Paragraph) + if !has_written_backrefs && i >= fl_len - 2 => + { + let usage_count = footnote_numbers.get(&name).unwrap().1; + let mut end = String::with_capacity( + name.len() + (r##" ↩
  2. "##.len() * usage_count), + ); + for usage in 1..=usage_count { + if usage == 1 { + write!(&mut end, r##" ↩"##).unwrap(); + } else { + write!(&mut end, r##" ↩{usage}"##) + .unwrap(); + } + } + has_written_backrefs = true; + if f == Event::End(TagEnd::FootnoteDefinition) { + end.push_str("\n"); + } else { + end.push_str("

    \n"); + } + Event::Html(end.into()) + } + Event::End(TagEnd::FootnoteDefinition) => Event::Html("\n".into()), + Event::FootnoteReference(_) => unreachable!("converted to HTML earlier"), + f => f, + }) + }); + + old_events.extend(footnotes); + old_events.push(Event::Html("
\n".into())); +} + pub fn markdown_to_html( content: &str, context: &RenderContext, @@ -623,6 +777,10 @@ pub fn markdown_to_html( insert_many(&mut events, anchors_to_insert); } + if context.config.markdown.bottom_footnotes { + convert_footnotes_to_github_style(&mut events); + } + cmark::html::push_html(&mut html, events.into_iter()); } @@ -641,11 +799,11 @@ pub fn markdown_to_html( #[cfg(test)] mod tests { + use super::*; use config::Config; + use insta::assert_snapshot; - use super::*; #[test] - fn insert_many_works() { let mut v = vec![1, 2, 3, 4, 5]; insert_many(&mut v, vec![(0, 0), (2, -1), (5, 6)]); @@ -714,4 +872,106 @@ mod tests { assert_eq!(body, &bottom_rendered); } } + + #[test] + fn no_footnotes() { + let mut opts = Options::empty(); + opts.insert(Options::ENABLE_TABLES); + opts.insert(Options::ENABLE_FOOTNOTES); + opts.insert(Options::ENABLE_STRIKETHROUGH); + opts.insert(Options::ENABLE_TASKLISTS); + opts.insert(Options::ENABLE_HEADING_ATTRIBUTES); + + let content = "Some text *without* footnotes.\n\nOnly ~~fancy~~ formatting."; + let mut events: Vec<_> = Parser::new_ext(&content, opts).collect(); + convert_footnotes_to_github_style(&mut events); + let mut html = String::new(); + cmark::html::push_html(&mut html, events.into_iter()); + assert_snapshot!(html); + } + + #[test] + fn single_footnote() { + let mut opts = Options::empty(); + opts.insert(Options::ENABLE_TABLES); + opts.insert(Options::ENABLE_FOOTNOTES); + opts.insert(Options::ENABLE_STRIKETHROUGH); + opts.insert(Options::ENABLE_TASKLISTS); + opts.insert(Options::ENABLE_HEADING_ATTRIBUTES); + + let content = "This text has a footnote[^1]\n [^1]:But it is meaningless."; + let mut events: Vec<_> = Parser::new_ext(&content, opts).collect(); + convert_footnotes_to_github_style(&mut events); + let mut html = String::new(); + cmark::html::push_html(&mut html, events.into_iter()); + assert_snapshot!(html); + } + + #[test] + fn reordered_footnotes() { + let mut opts = Options::empty(); + opts.insert(Options::ENABLE_TABLES); + opts.insert(Options::ENABLE_FOOTNOTES); + opts.insert(Options::ENABLE_STRIKETHROUGH); + opts.insert(Options::ENABLE_TASKLISTS); + opts.insert(Options::ENABLE_HEADING_ATTRIBUTES); + + let content = "This text has two[^2] footnotes[^1]\n[^1]: not sorted.\n[^2]: But they are"; + let mut events: Vec<_> = Parser::new_ext(&content, opts).collect(); + convert_footnotes_to_github_style(&mut events); + let mut html = String::new(); + cmark::html::push_html(&mut html, events.into_iter()); + assert_snapshot!(html); + } + + #[test] + fn def_before_use() { + let mut opts = Options::empty(); + opts.insert(Options::ENABLE_TABLES); + opts.insert(Options::ENABLE_FOOTNOTES); + opts.insert(Options::ENABLE_STRIKETHROUGH); + opts.insert(Options::ENABLE_TASKLISTS); + opts.insert(Options::ENABLE_HEADING_ATTRIBUTES); + + let content = "[^1]:It's before the reference.\n\n There is footnote definition?[^1]"; + let mut events: Vec<_> = Parser::new_ext(&content, opts).collect(); + convert_footnotes_to_github_style(&mut events); + let mut html = String::new(); + cmark::html::push_html(&mut html, events.into_iter()); + assert_snapshot!(html); + } + + #[test] + fn multiple_refs() { + let mut opts = Options::empty(); + opts.insert(Options::ENABLE_TABLES); + opts.insert(Options::ENABLE_FOOTNOTES); + opts.insert(Options::ENABLE_STRIKETHROUGH); + opts.insert(Options::ENABLE_TASKLISTS); + opts.insert(Options::ENABLE_HEADING_ATTRIBUTES); + + let content = "This text has two[^1] identical footnotes[^1]\n[^1]: So one is present.\n[^2]: But another in not."; + let mut events: Vec<_> = Parser::new_ext(&content, opts).collect(); + convert_footnotes_to_github_style(&mut events); + let mut html = String::new(); + cmark::html::push_html(&mut html, events.into_iter()); + assert_snapshot!(html); + } + + #[test] + fn footnote_inside_footnote() { + let mut opts = Options::empty(); + opts.insert(Options::ENABLE_TABLES); + opts.insert(Options::ENABLE_FOOTNOTES); + opts.insert(Options::ENABLE_STRIKETHROUGH); + opts.insert(Options::ENABLE_TASKLISTS); + opts.insert(Options::ENABLE_HEADING_ATTRIBUTES); + + let content = "This text has a footnote[^1]\n[^1]: But the footnote has another footnote[^2].\n[^2]: That's it."; + let mut events: Vec<_> = Parser::new_ext(&content, opts).collect(); + convert_footnotes_to_github_style(&mut events); + let mut html = String::new(); + cmark::html::push_html(&mut html, events.into_iter()); + assert_snapshot!(html); + } } diff --git a/components/markdown/src/snapshots/markdown__markdown__tests__def_before_use.snap b/components/markdown/src/snapshots/markdown__markdown__tests__def_before_use.snap new file mode 100644 index 0000000000..57e3a922ad --- /dev/null +++ b/components/markdown/src/snapshots/markdown__markdown__tests__def_before_use.snap @@ -0,0 +1,10 @@ +--- +source: components/markdown/src/markdown.rs +expression: html +--- +

There is footnote definition?[1]

+
    +
  1. +

    It's before the reference. ↩

    +
  2. +
diff --git a/components/markdown/src/snapshots/markdown__markdown__tests__footnote_inside_footnote.snap b/components/markdown/src/snapshots/markdown__markdown__tests__footnote_inside_footnote.snap new file mode 100644 index 0000000000..6b5e28d476 --- /dev/null +++ b/components/markdown/src/snapshots/markdown__markdown__tests__footnote_inside_footnote.snap @@ -0,0 +1,13 @@ +--- +source: components/markdown/src/markdown.rs +expression: html +--- +

This text has a footnote[1]

+
    +
  1. +

    But the footnote has another footnote[2]. ↩

    +
  2. +
  3. +

    That's it. ↩

    +
  4. +
diff --git a/components/markdown/src/snapshots/markdown__markdown__tests__multiple_refs.snap b/components/markdown/src/snapshots/markdown__markdown__tests__multiple_refs.snap new file mode 100644 index 0000000000..1f7eaff186 --- /dev/null +++ b/components/markdown/src/snapshots/markdown__markdown__tests__multiple_refs.snap @@ -0,0 +1,10 @@ +--- +source: components/markdown/src/markdown.rs +expression: html +--- +

This text has two[1] identical footnotes[1]

+
    +
  1. +

    So one is present. ↩ ↩2

    +
  2. +
diff --git a/components/markdown/src/snapshots/markdown__markdown__tests__no_footnotes.snap b/components/markdown/src/snapshots/markdown__markdown__tests__no_footnotes.snap new file mode 100644 index 0000000000..f9cc7730d8 --- /dev/null +++ b/components/markdown/src/snapshots/markdown__markdown__tests__no_footnotes.snap @@ -0,0 +1,6 @@ +--- +source: components/markdown/src/markdown.rs +expression: html +--- +

Some text without footnotes.

+

Only fancy formatting.

diff --git a/components/markdown/src/snapshots/markdown__markdown__tests__reordered_footnotes.snap b/components/markdown/src/snapshots/markdown__markdown__tests__reordered_footnotes.snap new file mode 100644 index 0000000000..865a344e5c --- /dev/null +++ b/components/markdown/src/snapshots/markdown__markdown__tests__reordered_footnotes.snap @@ -0,0 +1,13 @@ +--- +source: components/markdown/src/markdown.rs +expression: html +--- +

This text has two[1] footnotes[2]

+
    +
  1. +

    But they are ↩

    +
  2. +
  3. +

    not sorted. ↩

    +
  4. +
diff --git a/components/markdown/src/snapshots/markdown__markdown__tests__single_footnote.snap b/components/markdown/src/snapshots/markdown__markdown__tests__single_footnote.snap new file mode 100644 index 0000000000..34a7f2d3e6 --- /dev/null +++ b/components/markdown/src/snapshots/markdown__markdown__tests__single_footnote.snap @@ -0,0 +1,10 @@ +--- +source: components/markdown/src/markdown.rs +expression: html +--- +

This text has a footnote[1]

+
    +
  1. +

    But it is meaningless. ↩

    +
  2. +
diff --git a/components/markdown/tests/markdown.rs b/components/markdown/tests/markdown.rs index a12246837b..e8cdcd42c2 100644 --- a/components/markdown/tests/markdown.rs +++ b/components/markdown/tests/markdown.rs @@ -355,3 +355,40 @@ and multiple paragraphs. .body; insta::assert_snapshot!(body); } + +#[test] +fn github_style_footnotes() { + let mut config = Config::default_for_test(); + config.markdown.bottom_footnotes = true; + + let markdown = r#"This text has a footnote[^1] + +[^1]:But it is meaningless. + +This text has two[^3] footnotes[^2]. + +[^2]: not sorted. +[^3]: But they are + +[^4]:It's before the reference. + +There is footnote definition?[^4] + +This text has two[^5] identical footnotes[^5] +[^5]: So one is present. +[^6]: But another in not. + +This text has a footnote[^7] + +[^7]: But the footnote has another footnote[^8]. + +[^8]: That's it. + +Footnotes can also be referenced with identifiers[^first]. + +[^first]: Like this: `[^first]`. +"#; + + let body = common::render_with_config(&markdown, config).unwrap().body; + insta::assert_snapshot!(body); +} diff --git a/components/markdown/tests/snapshots/markdown__github_style_footnotes.snap b/components/markdown/tests/snapshots/markdown__github_style_footnotes.snap new file mode 100644 index 0000000000..de55e63fb7 --- /dev/null +++ b/components/markdown/tests/snapshots/markdown__github_style_footnotes.snap @@ -0,0 +1,36 @@ +--- +source: components/markdown/tests/markdown.rs +expression: body +--- +

This text has a footnote[1]

+

This text has two[2] footnotes[3].

+

There is footnote definition?[4]

+

This text has two[5] identical footnotes[5]

+

This text has a footnote[6]

+

Footnotes can also be referenced with identifiers[8].

+
    +
  1. +

    But it is meaningless. ↩

    +
  2. +
  3. +

    But they are ↩

    +
  4. +
  5. +

    not sorted. ↩

    +
  6. +
  7. +

    It's before the reference. ↩

    +
  8. +
  9. +

    So one is present. ↩ ↩2

    +
  10. +
  11. +

    But the footnote has another footnote[7]. ↩

    +
  12. +
  13. +

    That's it. ↩

    +
  14. +
  15. +

    Like this: [^first]. ↩

    +
  16. +
diff --git a/docs/content/documentation/getting-started/configuration.md b/docs/content/documentation/getting-started/configuration.md index d8ec482d5d..dce920ce8d 100644 --- a/docs/content/documentation/getting-started/configuration.md +++ b/docs/content/documentation/getting-started/configuration.md @@ -135,6 +135,9 @@ smart_punctuation = false # For example, `![xx](...)` is ok but `![*x*x](...)` isn’t ok lazy_async_image = false +# Whether footnotes are rendered in the GitHub-style (at the bottom, with back references) or plain (in the place, where they are defined) +bottom_footnotes = false + # Configuration of the link checker. [link_checker] # Skip link checking for external URLs that start with these prefixes From b3d5349d3b82063efe60fd752b26d61553611ec1 Mon Sep 17 00:00:00 2001 From: Vincent Prouillet Date: Thu, 9 May 2024 23:24:56 +0200 Subject: [PATCH 19/25] Update deps --- Cargo.lock | 1818 ++++++++++++++++--------- components/imageproc/src/processor.rs | 8 +- components/libs/Cargo.toml | 12 +- 3 files changed, 1200 insertions(+), 638 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0d90eea7c0..293d58133a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,23 +19,23 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "ahash" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ - "getrandom 0.2.11", + "getrandom 0.2.15", "once_cell", "version_check", ] [[package]] name = "ahash" -version = "0.8.6" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if 1.0.0", - "getrandom 0.2.11", + "getrandom 0.2.15", "once_cell", "version_check", "zerocopy", @@ -52,18 +52,24 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] +[[package]] +name = "aligned-vec" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1" + [[package]] name = "ammonia" -version = "3.3.0" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e6d1c7838db705c9b756557ee27c384ce695a1c51a6fe528784cb1c6840170" +checksum = "1ab99eae5ee58501ab236beb6f20f6ca39be615267b014899c89b2f0bc18a459" dependencies = [ "html5ever", "maplit", @@ -89,47 +95,48 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.5" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", + "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.4" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" [[package]] name = "anstyle-parse" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" dependencies = [ "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.2" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" dependencies = [ "anstyle", "windows-sys 0.52.0", @@ -143,9 +150,26 @@ checksum = "70033777eb8b5124a81a1889416543dddef2de240019b674c81285a2635a7e1e" [[package]] name = "anyhow" -version = "1.0.75" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3" + +[[package]] +name = "arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" + +[[package]] +name = "arg_enum_proc_macro" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.61", +] [[package]] name = "arrayvec" @@ -169,22 +193,45 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi 0.1.19", + "hermit-abi", "libc", "winapi 0.3.9", ] [[package]] name = "autocfg" -version = "1.1.0" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "av1-grain" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "6678909d8c5d46a42abcf571271e15fdbc0a225e3646cf23762cd415046c78bf" +dependencies = [ + "anyhow", + "arrayvec", + "log", + "nom", + "num-rational", + "v_frame", +] + +[[package]] +name = "avif-serialize" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876c75a42f6364451a033496a14c44bffe41f5f4a8236f697391f11024e596d2" +dependencies = [ + "arrayvec", +] [[package]] name = "backtrace" -version = "0.3.69" +version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" dependencies = [ "addr2line", "cc", @@ -197,9 +244,24 @@ dependencies = [ [[package]] name = "base64" -version = "0.21.5" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64-simd" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "781dd20c3aff0bd194fe7d2a977dd92f21c173891f3a03b677359e5fa457e5d5" +dependencies = [ + "simd-abstraction", +] [[package]] name = "bincode" @@ -224,9 +286,27 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.1" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[package]] +name = "bitstream-io" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +checksum = "7c12d1856e42f0d817a835fe55853957c85c8c8a470114029143d3f12671446e" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] [[package]] name = "block-buffer" @@ -260,9 +340,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.8.0" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "542f33a8835a0884b006a0c3df3dadd99c0c3f296ed26c2fdc8028e01ad6230c" +checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" dependencies = [ "memchr", "serde", @@ -274,11 +354,17 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ae4235e6dac0694637c763029ecea1a2ec9e4e06ec2729bd21ba4d9c863eb7" +[[package]] +name = "built" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41bfbdb21256b87a8b5e80fab81a8eed158178e812fd7ba451907518b2742f16" + [[package]] name = "bumpalo" -version = "3.14.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "byte-tools" @@ -286,17 +372,39 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" +[[package]] +name = "bytecheck" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "bytecount" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1e5f035d16fc623ae5f74981db80a439803888314e3a555fd6f04acd51a3205" +checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" [[package]] name = "bytemuck" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" +checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" [[package]] name = "byteorder" @@ -304,6 +412,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + [[package]] name = "bytes" version = "0.4.12" @@ -316,9 +430,9 @@ dependencies = [ [[package]] name = "bytes" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" [[package]] name = "camino" @@ -331,9 +445,9 @@ dependencies = [ [[package]] name = "cargo-platform" -version = "0.1.5" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e34637b3140142bdf929fb439e8aa4ebad7651ebf7b1080b3930aa16ac1459ff" +checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" dependencies = [ "serde", ] @@ -353,12 +467,13 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.83" +version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4" dependencies = [ "jobserver", "libc", + "once_cell", ] [[package]] @@ -370,6 +485,16 @@ dependencies = [ "smallvec", ] +[[package]] +name = "cfg-expr" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec", + "target-lexicon", +] + [[package]] name = "cfg-if" version = "0.1.10" @@ -382,24 +507,30 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + [[package]] name = "chrono" -version = "0.4.31" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ "android-tzdata", "iana-time-zone", "num-traits", "pure-rust-locales", - "windows-targets 0.48.5", + "windows-targets 0.52.5", ] [[package]] name = "chrono-tz" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e23185c0e21df6ed832a12e2bda87c7d1def6842881fb634a8511ced741b0d76" +checksum = "d59ae0466b83e838b81a54256c39d5d7c20b9d7daa10510a242d9b75abd5936e" dependencies = [ "chrono", "chrono-tz-build", @@ -429,40 +560,40 @@ dependencies = [ "clap_lex 0.2.4", "indexmap 1.9.3", "once_cell", - "strsim", + "strsim 0.10.0", "termcolor", "textwrap", ] [[package]] name = "clap" -version = "4.4.11" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2" +checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" dependencies = [ "clap_builder", - "clap_derive 4.4.7", + "clap_derive 4.5.4", ] [[package]] name = "clap_builder" -version = "4.4.11" +version = "4.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" dependencies = [ "anstream", "anstyle", - "clap_lex 0.6.0", - "strsim", + "clap_lex 0.7.0", + "strsim 0.11.1", ] [[package]] name = "clap_complete" -version = "4.4.4" +version = "4.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bffe91f06a11b4b9420f62103854e90867812cd5d01557f853c5ee8e791b12ae" +checksum = "dd79504325bf38b10165b02e89b4347300f855f273c4cb30c4a3209e6583275e" dependencies = [ - "clap 4.4.11", + "clap 4.5.4", ] [[package]] @@ -471,7 +602,7 @@ version = "3.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro-error", "proc-macro2", "quote", @@ -480,14 +611,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.4.7" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.61", ] [[package]] @@ -501,9 +632,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "codemap" @@ -519,9 +650,9 @@ checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" [[package]] name = "colorchoice" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" [[package]] name = "colored" @@ -553,14 +684,34 @@ dependencies = [ [[package]] name = "console" -version = "0.15.7" +version = "0.15.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8" +checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" dependencies = [ "encode_unicode", "lazy_static", "libc", - "windows-sys 0.45.0", + "windows-sys 0.52.0", +] + +[[package]] +name = "const-str" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21077772762a1002bb421c3af42ac1725fa56066bfc53d9a55bb79905df2aaf3" +dependencies = [ + "const-str-proc-macro", +] + +[[package]] +name = "const-str-proc-macro" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e1e0fdd2e5d3041e530e1b21158aeeef8b5d0e306bc5c1e3d6cf0930d10e25a" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] @@ -578,12 +729,6 @@ dependencies = [ "utils", ] -[[package]] -name = "convert_case" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" - [[package]] name = "core-foundation" version = "0.9.4" @@ -602,9 +747,9 @@ checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "cpufeatures" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] @@ -620,44 +765,37 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.3.2" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" dependencies = [ "cfg-if 1.0.0", ] [[package]] name = "crossbeam-deque" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fca89a0e215bab21874660c67903c5f143333cab1da83d041c7ded6053774751" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" dependencies = [ - "cfg-if 1.0.0", "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" -version = "0.9.16" +version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d2fe95351b870527a5d09bf563ed3c97c0cffb87cf1c78a591bf48bb218d9aa" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ - "autocfg", - "cfg-if 1.0.0", "crossbeam-utils", - "memoffset", ] [[package]] name = "crossbeam-utils" -version = "0.8.17" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d96137f14f244c37f989d9fff8f95e6c18b918e71f36638f8c49112e4c78f" -dependencies = [ - "cfg-if 1.0.0", -] +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" [[package]] name = "crunchy" @@ -676,14 +814,35 @@ dependencies = [ ] [[package]] -name = "css-minify" -version = "0.3.1" +name = "cssparser" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "874c6e2d19f8d4a285083b11a3241bfbe01ac3ed85f26e1e6b34888d960552bd" +checksum = "9be934d936a0fbed5bcdc01042b770de1398bf79d0e192f49fa7faea0e99281e" dependencies = [ - "derive_more", - "indexmap 1.9.3", - "nom", + "cssparser-macros", + "dtoa-short", + "itoa", + "phf 0.11.2", + "smallvec", +] + +[[package]] +name = "cssparser-color" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556c099a61d85989d7af52b692e35a8d68a57e7df8c6d07563dc0778b3960c9f" +dependencies = [ + "cssparser", +] + +[[package]] +name = "cssparser-macros" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" +dependencies = [ + "quote", + "syn 2.0.61", ] [[package]] @@ -709,41 +868,56 @@ dependencies = [ [[package]] name = "ctrlc" -version = "3.4.1" +version = "3.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e95fbd621905b854affdc67943b043a0fbb6ed7385fd5a25650d19a8a6cfdf" +checksum = "672465ae37dc1bc6380a6547a8883d5dd397b0f1faaad4f265726cc7042a5345" dependencies = [ "nix", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] -name = "deranged" -version = "0.3.10" +name = "dashmap" +version = "5.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eb30d70a07a3b04884d2677f06bec33509dc67ca60d92949e5535352d3191dc" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ - "powerfmt", + "cfg-if 1.0.0", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", ] [[package]] -name = "derive_more" -version = "0.99.17" +name = "data-encoding" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" + +[[package]] +name = "data-url" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a30bfce702bcfa94e906ef82421f2c0e61c076ad76030c16ee5d2e9a32fe193" dependencies = [ - "convert_case", - "proc-macro2", - "quote", - "rustc_version", - "syn 1.0.109", + "matches", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", ] [[package]] name = "deunicode" -version = "1.4.2" +version = "1.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae2a35373c5c74340b79ae6780b498b2b183915ec5dacf263aac5a099bf485a" +checksum = "322ef0094744e63628e6f0eb2295517f79276a5b342a4c2ff3042566ca181d4e" [[package]] name = "digest" @@ -772,7 +946,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.61", ] [[package]] @@ -781,11 +955,26 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" +[[package]] +name = "dtoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" + +[[package]] +name = "dtoa-short" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbaceec3c6e4211c79e7b1800fb9680527106beb2f9c51904a3210c03a448c74" +dependencies = [ + "dtoa", +] + [[package]] name = "either" -version = "1.9.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" [[package]] name = "elasticlunr-rs" @@ -875,9 +1064,9 @@ checksum = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569" [[package]] name = "encoding_rs" -version = "0.8.33" +version = "0.8.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" dependencies = [ "cfg-if 1.0.0", ] @@ -903,9 +1092,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", "windows-sys 0.52.0", @@ -929,9 +1118,9 @@ dependencies = [ [[package]] name = "exr" -version = "1.71.0" +version = "1.72.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "832a761f35ab3e6664babfbdc6cef35a4860e816ec3916dcfd0882954e98a8a8" +checksum = "887d93f60543e9a9362ef8a21beedd0a833c5d9610e18c67abe15a5963dcb1a4" dependencies = [ "bit_field", "flume", @@ -951,15 +1140,15 @@ checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" [[package]] name = "fastrand" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] name = "fdeflate" -version = "0.3.1" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64d6dafc854908ff5da46ff3f8f473c6984119a2876a383a860246dd7841a868" +checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" dependencies = [ "simd-adler32", ] @@ -972,15 +1161,15 @@ checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall", + "redox_syscall 0.4.1", "windows-sys 0.52.0", ] [[package]] name = "flate2" -version = "1.0.28" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" dependencies = [ "crc32fast", "miniz_oxide", @@ -1060,6 +1249,12 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "futf" version = "0.1.5" @@ -1072,42 +1267,42 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", ] [[package]] name = "futures-core" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-io" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-sink" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-core", "futures-io", @@ -1159,9 +1354,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.11" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if 1.0.0", "libc", @@ -1180,9 +1375,9 @@ dependencies = [ [[package]] name = "gif" -version = "0.12.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045" +checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" dependencies = [ "color_quant", "weezl", @@ -1206,11 +1401,11 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" dependencies = [ - "aho-corasick 1.1.2", + "aho-corasick 1.1.3", "bstr", "log", "regex-automata", - "regex-syntax 0.8.2", + "regex-syntax", ] [[package]] @@ -1226,40 +1421,40 @@ dependencies = [ [[package]] name = "grass" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7746cd9bf09f9bb7d98638774a70642000356f543898d9a352cd043f82744528" +checksum = "b89786a806d5b192cf4e573f9831c847a455a142d000c922bdfc1e5edad14303" dependencies = [ "grass_compiler", ] [[package]] name = "grass_compiler" -version = "0.13.0" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "187adfc0b34289c7f8f3819453ce9da3177c3d73f40ac74bb17faba578813d45" +checksum = "7cf7d155dd7cef20195016d01005033a5521aad307033f0f8e8bf0a02f5f7554" dependencies = [ "codemap", - "indexmap 1.9.3", + "indexmap 2.2.6", "lasso", "once_cell", - "phf 0.10.1", + "phf 0.11.2", "rand 0.8.5", ] [[package]] name = "h2" -version = "0.3.22" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" dependencies = [ - "bytes 1.5.0", + "bytes 1.6.0", "fnv", "futures-core", "futures-sink", "futures-util", "http", - "indexmap 2.1.0", + "indexmap 2.2.6", "slab", "tokio", "tokio-util", @@ -1268,33 +1463,38 @@ dependencies = [ [[package]] name = "half" -version = "2.2.1" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b4af3693f1b705df946e9fe5631932443781d0aabb423b62fcd4d73f6d2fd0" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" dependencies = [ + "cfg-if 1.0.0", "crunchy", ] [[package]] name = "hashbrown" -version = "0.11.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ - "ahash 0.7.7", + "ahash 0.7.8", ] [[package]] name = "hashbrown" -version = "0.12.3" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash 0.8.11", + "bumpalo", +] [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "heck" @@ -1302,6 +1502,12 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -1311,33 +1517,27 @@ dependencies = [ "libc", ] -[[package]] -name = "hermit-abi" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" - [[package]] name = "html5ever" -version = "0.26.0" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" +checksum = "c13771afe0e6e846f1e67d038d4cb29998a6779f93c809212e4e9c32efd244d4" dependencies = [ "log", "mac", "markup5ever", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.61", ] [[package]] name = "http" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ - "bytes 1.5.0", + "bytes 1.6.0", "fnv", "itoa", ] @@ -1348,7 +1548,7 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ - "bytes 1.5.0", + "bytes 1.6.0", "http", "pin-project-lite", ] @@ -1382,11 +1582,11 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.27" +version = "0.14.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" dependencies = [ - "bytes 1.5.0", + "bytes 1.6.0", "futures-channel", "futures-core", "futures-util", @@ -1397,7 +1597,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.4.10", + "socket2", "tokio", "tower-service", "tracing", @@ -1424,7 +1624,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ - "bytes 1.5.0", + "bytes 1.6.0", "hyper", "native-tls", "tokio", @@ -1433,9 +1633,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.58" +version = "0.1.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -1466,9 +1666,9 @@ dependencies = [ [[package]] name = "ignore" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "747ad1b4ae841a78e8aba0d63adbfbeaea26b517b63705d47856b73015d27060" +checksum = "b46810df39e66e925525d6e38ce1e7f6e1d208f72dc39757880fcb66e2c58af1" dependencies = [ "crossbeam-deque", "globset", @@ -1482,21 +1682,35 @@ dependencies = [ [[package]] name = "image" -version = "0.24.7" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f3dfdbdd72063086ff443e297b61695500514b1e41095b6fb9a5ab48a70a711" +checksum = "fd54d660e773627692c524beaad361aca785a4f9f5730ce91f42aabe5bce3d11" dependencies = [ "bytemuck", "byteorder", "color_quant", "exr", "gif", - "jpeg-decoder", - "num-rational", + "image-webp", "num-traits", "png", "qoi", + "ravif", + "rayon", + "rgb", "tiff", + "zune-core", + "zune-jpeg", +] + +[[package]] +name = "image-webp" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d730b085583c4d789dfd07fdcf185be59501666a90c97c40162b37e4fdad272d" +dependencies = [ + "byteorder-lite", + "thiserror", ] [[package]] @@ -1512,6 +1726,12 @@ dependencies = [ "utils", ] +[[package]] +name = "imgref" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44feda355f4159a7c757171a77de25daf6411e217b4cabd03bd6650690468126" + [[package]] name = "indexmap" version = "1.9.3" @@ -1524,12 +1744,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.1.0" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", - "hashbrown 0.14.3", + "hashbrown 0.14.5", ] [[package]] @@ -1554,15 +1774,25 @@ dependencies = [ [[package]] name = "insta" -version = "1.34.0" +version = "1.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d64600be34b2fcfc267740a243fa7744441bb4947a619ac4e5bb6507f35fbfc" +checksum = "3eab73f58e59ca6526037208f0e98851159ec1633cf17b6cd2e1f2c3fd5d53cc" dependencies = [ - "console 0.15.7", + "console 0.15.8", "lazy_static", "linked-hash-map", "similar", - "yaml-rust", +] + +[[package]] +name = "interpolate_name" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.61", ] [[package]] @@ -1599,11 +1829,35 @@ dependencies = [ "once_cell", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + [[package]] name = "itoa" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jieba-rs" @@ -1613,7 +1867,7 @@ checksum = "93f0c1347cd3ac8d7c6e3a2dc33ac496d365cf09fc0831aa61111e1a6738983e" dependencies = [ "cedarwood", "fxhash", - "hashbrown 0.14.3", + "hashbrown 0.14.5", "lazy_static", "phf 0.11.2", "phf_codegen 0.11.2", @@ -1622,27 +1876,24 @@ dependencies = [ [[package]] name = "jobserver" -version = "0.1.27" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" +checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" dependencies = [ "libc", ] [[package]] name = "jpeg-decoder" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e" -dependencies = [ - "rayon", -] +checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" [[package]] name = "js-sys" -version = "0.3.66" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] @@ -1668,11 +1919,11 @@ dependencies = [ [[package]] name = "lasso" -version = "0.6.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aeb7b21a526375c5ca55f1a6dfd4e1fad9fa4edd750f530252a718a44b2608f0" +checksum = "4644821e1c3d7a560fe13d842d13f587c07348a1a05d3a797152d41c90c56df2" dependencies = [ - "hashbrown 0.11.2", + "hashbrown 0.13.2", ] [[package]] @@ -1704,9 +1955,20 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.151" +version = "0.2.154" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" +checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" + +[[package]] +name = "libfuzzer-sys" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7" +dependencies = [ + "arbitrary", + "cc", + "once_cell", +] [[package]] name = "libm" @@ -1718,10 +1980,10 @@ checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" name = "libs" version = "0.1.0" dependencies = [ - "ahash 0.8.6", + "ahash 0.8.11", "ammonia", "atty", - "base64", + "base64 0.22.1", "csv", "elasticlunr-rs", "filetime", @@ -1736,7 +1998,7 @@ dependencies = [ "num-format", "once_cell", "percent-encoding", - "pulldown-cmark 0.10.0", + "pulldown-cmark 0.10.3", "pulldown-cmark-escape", "quickxml_to_serde", "rayon", @@ -1752,7 +2014,7 @@ dependencies = [ "tera", "termcolor", "time", - "toml 0.8.8", + "toml 0.8.12", "unic-langid", "unicode-segmentation", "url", @@ -1762,14 +2024,39 @@ dependencies = [ [[package]] name = "libwebp-sys" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e0df0a0f9444d52aee6335cd724d21a2ee3285f646291799a72be518ec8ee3c" +checksum = "829b6b604f31ed6d2bccbac841fe0788de93dbd87e4eb1ba2c4adfe8c012a838" dependencies = [ "cc", "glob", ] +[[package]] +name = "lightningcss" +version = "1.0.0-alpha.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bd5bed3814fb631bfc1e24c2be6f7e86a9837c660909acab79a38374dcb8798" +dependencies = [ + "ahash 0.8.11", + "bitflags 2.5.0", + "const-str", + "cssparser", + "cssparser-color", + "dashmap", + "data-encoding", + "getrandom 0.2.15", + "itertools 0.10.5", + "lazy_static", + "parcel_selectors", + "parcel_sourcemap", + "paste", + "pathdiff", + "rayon", + "serde", + "smallvec", +] + [[package]] name = "lindera" version = "0.14.0" @@ -1929,12 +2216,9 @@ dependencies = [ [[package]] name = "line-wrap" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30344350a2a51da54c1d53be93fade8a237e545dbcc4bdbe635413f2117cab9" -dependencies = [ - "safemem", -] +checksum = "dd1bc4d24ad230d21fb898d1116b1801d7adfc449d42026475862ab48b11e70e" [[package]] name = "link_checker" @@ -1955,15 +2239,15 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -1971,9 +2255,18 @@ dependencies = [ [[package]] name = "log" -version = "0.4.20" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "loop9" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" +dependencies = [ + "imgref", +] [[package]] name = "lzma-rs" @@ -2014,33 +2307,40 @@ dependencies = [ [[package]] name = "markup5ever" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" +checksum = "16ce3abbeba692c8b8441d036ef91aea6df8da2c6b6e21c7e14d3c18e526be45" dependencies = [ "log", - "phf 0.10.1", - "phf_codegen 0.10.0", + "phf 0.11.2", + "phf_codegen 0.11.2", "string_cache", "string_cache_codegen", "tendril", ] [[package]] -name = "memchr" -version = "2.6.4" +name = "matches" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" [[package]] -name = "memoffset" -version = "0.9.0" +name = "maybe-rayon" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" dependencies = [ - "autocfg", + "cfg-if 1.0.0", + "rayon", ] +[[package]] +name = "memchr" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + [[package]] name = "mime" version = "0.3.17" @@ -2068,23 +2368,40 @@ dependencies = [ [[package]] name = "minify-html" -version = "0.11.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc4d9147754a49e80557df835eb59e743eab1bf75410a134f55dc4b9dbb692ad" +checksum = "1cd4517942a8e7425c990b14977f86a63e4996eed7b15cfcca1540126ac5ff25" dependencies = [ "aho-corasick 0.7.20", - "css-minify", "lazy_static", + "lightningcss", "memchr", + "minify-html-common", "minify-js", + "once_cell", + "rustc-hash", +] + +[[package]] +name = "minify-html-common" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "697a6b40dffdc5de10c0cbd709dc2bc2039cea9dab8aaa636eb9a49d6b411780" +dependencies = [ + "aho-corasick 0.7.20", + "itertools 0.12.1", + "lazy_static", + "memchr", "rustc-hash", + "serde", + "serde_json", ] [[package]] name = "minify-js" -version = "0.4.3" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c300f90ba1138b5c5daf5d9441dc9bdc67b808aac22cf638362a2647bc213be4" +checksum = "22d6c512a82abddbbc13b70609cb2beff01be2c7afff534d6e5e1c85e438fc8b" dependencies = [ "lazy_static", "parse-js", @@ -2098,9 +2415,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" dependencies = [ "adler", "simd-adler32", @@ -2127,9 +2444,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "wasi 0.11.0+wasi-snapshot-preview1", @@ -2215,18 +2532,19 @@ dependencies = [ [[package]] name = "new_debug_unreachable" -version = "1.0.4" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" [[package]] name = "nix" -version = "0.27.1" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.5.0", "cfg-if 1.0.0", + "cfg_aliases", "libc", ] @@ -2254,9 +2572,9 @@ dependencies = [ [[package]] name = "nom-tracable" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "160767ce1eed2cdadc2256015a6dc51d9632226ea02e0f0ce4590b904e1d80e2" +checksum = "6a39d3ec4e5bc9816ca540bd6b1e4885c0275536eb3293d317d984bb17f9a294" dependencies = [ "nom", "nom-tracable-macros", @@ -2265,9 +2583,9 @@ dependencies = [ [[package]] name = "nom-tracable-macros" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7ac681ea0c3d468b003bdebe3a65d1632e340302452f95c3ffadf515704c48d" +checksum = "c9c68f5316254dae193b3ce083f6caf19ae1a58471e6585e89f0796b9e5bdf4a" dependencies = [ "quote", "syn 1.0.109", @@ -2284,6 +2602,12 @@ dependencies = [ "nom", ] +[[package]] +name = "noop_proc_macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" + [[package]] name = "notify" version = "4.0.17" @@ -2302,6 +2626,33 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "num-bigint" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.61", +] + [[package]] name = "num-format" version = "0.4.4" @@ -2314,58 +2665,47 @@ dependencies = [ [[package]] name = "num-integer" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "autocfg", "num-traits", ] [[package]] name = "num-rational" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" dependencies = [ - "autocfg", + "num-bigint", "num-integer", "num-traits", ] [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi 0.3.3", - "libc", -] - [[package]] name = "num_threads" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" dependencies = [ "libc", ] [[package]] name = "object" -version = "0.32.1" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ "memchr", ] @@ -2406,9 +2746,9 @@ checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" [[package]] name = "open" -version = "5.0.1" +version = "5.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90878fb664448b54c4e592455ad02831e23a3f7e157374a8b95654731aac7349" +checksum = "449f0ff855d85ddbf1edd5b646d65249ead3f5e422aaa86b7d2d0b049b103e32" dependencies = [ "is-wsl", "libc", @@ -2417,11 +2757,11 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.61" +version = "0.10.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b8419dc8cc6d866deb801274bba2e6f8f6108c1bb7fcc10ee5ab864931dbb45" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.5.0", "cfg-if 1.0.0", "foreign-types", "libc", @@ -2438,7 +2778,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.61", ] [[package]] @@ -2449,9 +2789,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.97" +version = "0.9.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3eaad34cdd97d81de97964fc7f29e2d104f483840d906ef56daa1912338460b" +checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" dependencies = [ "cc", "libc", @@ -2465,11 +2805,47 @@ version = "6.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" +[[package]] +name = "outref" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f222829ae9293e33a9f5e9f440c6760a3d450a64affe1846486b140db81c1f4" + +[[package]] +name = "parcel_selectors" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05d74befe2d076330d9a58bf9ca2da424568724ab278adf15fb5718253133887" +dependencies = [ + "bitflags 2.5.0", + "cssparser", + "fxhash", + "log", + "phf 0.10.1", + "phf_codegen 0.10.0", + "precomputed-hash", + "smallvec", +] + +[[package]] +name = "parcel_sourcemap" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "485b74d7218068b2b7c0e3ff12fbc61ae11d57cb5d8224f525bd304c6be05bbb" +dependencies = [ + "base64-simd", + "data-url", + "rkyv", + "serde", + "serde_json", + "vlq", +] + [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" dependencies = [ "lock_api", "parking_lot_core", @@ -2477,37 +2853,45 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall", + "redox_syscall 0.5.1", "smallvec", - "windows-targets 0.48.5", + "windows-targets 0.52.5", ] [[package]] name = "parse-js" -version = "0.10.3" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30534759e6ad87aa144c396544747e1c25b1020bd133356fd758c8facec764e5" +checksum = "9ec3b11d443640ec35165ee8f6f0559f1c6f41878d70330fe9187012b5935f02" dependencies = [ "aho-corasick 0.7.20", + "bumpalo", + "hashbrown 0.13.2", "lazy_static", "memchr", ] [[package]] name = "parse-zoneinfo" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c705f256449c60da65e11ff6626e0c16a0a0b96aaa348de61376b249bc340f41" +checksum = "1f2a05b18d44e2957b88f96ba460715e295bc1d7510468a2f3d3b44535d26c24" dependencies = [ "regex", ] +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + [[package]] name = "path-slash" version = "0.2.1" @@ -2528,9 +2912,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.5" +version = "2.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae9cee2a55a544be8b89dc6848072af97a20f2422603c10865be2a42b580fff5" +checksum = "560131c633294438da9f7c4b08189194b20946c8274c6b9e38881a7874dc8ee8" dependencies = [ "memchr", "thiserror", @@ -2539,9 +2923,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.5" +version = "2.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81d78524685f5ef2a3b3bd1cafbc9fcabb036253d9b1463e726a91cd16e2dfc2" +checksum = "26293c9193fbca7b1a3bf9b79dc1e388e927e6cacaa78b4a3ab705a1d3d41459" dependencies = [ "pest", "pest_generator", @@ -2549,22 +2933,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.5" +version = "2.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68bd1206e71118b5356dae5ddc61c8b11e28b09ef6a31acbd15ea48a28e0c227" +checksum = "3ec22af7d3fb470a85dd2ca96b7c577a1eb4ef6f1683a9fe9a8c16e136c04687" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.61", ] [[package]] name = "pest_meta" -version = "2.7.5" +version = "2.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c747191d4ad9e4a4ab9c8798f1e82a39affe7ef9648390b7e5548d18e099de6" +checksum = "d7a240022f37c361ec1878d646fc5b7d7c4d28d5946e1a80ad5a7a4f4ca0bdcd" dependencies = [ "once_cell", "pest", @@ -2577,9 +2961,7 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" dependencies = [ - "phf_macros", "phf_shared 0.10.0", - "proc-macro-hack", ] [[package]] @@ -2588,6 +2970,7 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" dependencies = [ + "phf_macros", "phf_shared 0.11.2", ] @@ -2633,16 +3016,15 @@ dependencies = [ [[package]] name = "phf_macros" -version = "0.10.0" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0" +checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" dependencies = [ - "phf_generator 0.10.0", - "phf_shared 0.10.0", - "proc-macro-hack", + "phf_generator 0.11.2", + "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.61", ] [[package]] @@ -2665,9 +3047,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" @@ -2677,18 +3059,18 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.27" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "plist" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5699cc8a63d1aa2b1ee8e12b9ad70ac790d65788cd36101fa37f87ea46c4cef" +checksum = "d9d34169e64b3c7a80c8621a48adaf44e0cf62c78a9b25dd9dd35f1881a17cf9" dependencies = [ - "base64", - "indexmap 2.1.0", + "base64 0.21.7", + "indexmap 2.2.6", "line-wrap", "quick-xml 0.31.0", "serde", @@ -2697,9 +3079,9 @@ dependencies = [ [[package]] name = "png" -version = "0.17.10" +version = "0.17.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd75bf2d8dd3702b9707cdbc56a5b9ef42cec752eb8b3bafc01234558442aa64" +checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" dependencies = [ "bitflags 1.3.2", "crc32fast", @@ -2751,38 +3133,71 @@ dependencies = [ ] [[package]] -name = "proc-macro-hack" -version = "0.5.20+deprecated" +name = "proc-macro2" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" +checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" +dependencies = [ + "unicode-ident", +] [[package]] -name = "proc-macro2" -version = "1.0.70" +name = "profiling" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +checksum = "43d84d1d7a6ac92673717f9f6d1518374ef257669c24ebc5ac25d5033828be58" dependencies = [ - "unicode-ident", + "profiling-procmacros", +] + +[[package]] +name = "profiling-procmacros" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8021cf59c8ec9c432cfc2526ac6b8aa508ecaf29cd415f271b8406c1b851c3fd" +dependencies = [ + "quote", + "syn 2.0.61", +] + +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] name = "pulldown-cmark" -version = "0.9.3" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a1a2f1f0a7ecff9c31abbe177637be0e97a0aef46cf8738ece09327985d998" +checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.5.0", "memchr", "unicase", ] [[package]] name = "pulldown-cmark" -version = "0.10.0" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce76ce678ffc8e5675b22aa1405de0b7037e2fdf8913fea40d1926c6fe1e6e7" +checksum = "76979bea66e7875e7509c4ec5300112b316af87fa7a252ca91c448b32dfe3993" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.5.0", "memchr", "pulldown-cmark-escape", "unicase", @@ -2790,15 +3205,15 @@ dependencies = [ [[package]] name = "pulldown-cmark-escape" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5d8f9aa0e3cbcfaf8bf00300004ee3b72f74770f9cbac93f6928771f613276b" +checksum = "bd348ff538bc9caeda7ee8cad2d1d48236a1f443c1fa3913c6a02fe0043b1dd3" [[package]] name = "pure-rust-locales" -version = "0.7.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed02a829e62dc2715ceb8afb4f80e298148e1345749ceb369540fe0eb3368432" +checksum = "1190fd18ae6ce9e137184f207593877e70f39b015040156b1e05081cdfe3733a" [[package]] name = "qoi" @@ -2835,11 +3250,12 @@ dependencies = [ [[package]] name = "quickxml_to_serde" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26f35112b35480fd72f63444289083eeedbd61d13907c82c4309f0ccda35e244" +checksum = "286b05c7a00b356ff6ac5218e10d628e0a3be02e777d067ca7286d353c3c407e" dependencies = [ "minidom", + "regex", "serde", "serde_derive", "serde_json", @@ -2847,13 +3263,19 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.33" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + [[package]] name = "rand" version = "0.7.3" @@ -2913,7 +3335,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.11", + "getrandom 0.2.15", ] [[package]] @@ -2925,11 +3347,61 @@ dependencies = [ "rand_core 0.5.1", ] +[[package]] +name = "rav1e" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9" +dependencies = [ + "arbitrary", + "arg_enum_proc_macro", + "arrayvec", + "av1-grain", + "bitstream-io", + "built", + "cfg-if 1.0.0", + "interpolate_name", + "itertools 0.12.1", + "libc", + "libfuzzer-sys", + "log", + "maybe-rayon", + "new_debug_unreachable", + "noop_proc_macro", + "num-derive", + "num-traits", + "once_cell", + "paste", + "profiling", + "rand 0.8.5", + "rand_chacha 0.3.1", + "simd_helpers", + "system-deps", + "thiserror", + "v_frame", + "wasm-bindgen", +] + +[[package]] +name = "ravif" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc13288f5ab39e6d7c9d501759712e6969fcc9734220846fc9ed26cae2cc4234" +dependencies = [ + "avif-serialize", + "imgref", + "loop9", + "quick-error", + "rav1e", + "rayon", + "rgb", +] + [[package]] name = "rayon" -version = "1.8.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", @@ -2937,9 +3409,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.12.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" dependencies = [ "crossbeam-deque", "crossbeam-utils", @@ -2954,55 +3426,67 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_syscall" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +dependencies = [ + "bitflags 2.5.0", +] + [[package]] name = "regex" -version = "1.10.2" +version = "1.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" dependencies = [ - "aho-corasick 1.1.2", + "aho-corasick 1.1.3", "memchr", "regex-automata", - "regex-syntax 0.8.2", + "regex-syntax", ] [[package]] name = "regex-automata" -version = "0.4.3" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ - "aho-corasick 1.1.2", + "aho-corasick 1.1.3", "memchr", - "regex-syntax 0.8.2", + "regex-syntax", ] [[package]] name = "regex-syntax" -version = "0.7.5" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] -name = "regex-syntax" -version = "0.8.2" +name = "relative-path" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" [[package]] -name = "relative-path" -version = "1.9.0" +name = "rend" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c707298afce11da2efef2f600116fa93ffa7a032b5d7b628aa17711ec81383ca" +checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" +dependencies = [ + "bytecheck", +] [[package]] name = "reqwest" -version = "0.11.22" +version = "0.11.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" dependencies = [ - "base64", - "bytes 1.5.0", + "base64 0.21.7", + "bytes 1.6.0", "encoding_rs", "futures-core", "futures-util", @@ -3025,6 +3509,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", + "sync_wrapper", "system-configuration", "tokio", "tokio-native-tls", @@ -3038,18 +3523,57 @@ dependencies = [ "winreg", ] +[[package]] +name = "rgb" +version = "0.8.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05aaa8004b64fd573fc9d002f4e632d51ad4f026c2b5ba95fcb6c2f32c2c47d8" +dependencies = [ + "bytemuck", +] + [[package]] name = "ring" -version = "0.17.7" +version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", - "getrandom 0.2.11", + "cfg-if 1.0.0", + "getrandom 0.2.15", "libc", "spin", "untrusted", - "windows-sys 0.48.0", + "windows-sys 0.52.0", +] + +[[package]] +name = "rkyv" +version = "0.7.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cba464629b3394fc4dbc6f940ff8f5b4ff5c7aef40f29166fd4ad12acbc99c0" +dependencies = [ + "bitvec", + "bytecheck", + "bytes 1.6.0", + "hashbrown 0.12.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", + "tinyvec", + "uuid", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7dddfff8de25e6f62b9d64e6e432bf1c6736c57d20323e15ee10435fbda7c65" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] @@ -3073,9 +3597,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" @@ -3083,22 +3607,13 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" -[[package]] -name = "rustc_version" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" -dependencies = [ - "semver", -] - [[package]] name = "rustix" -version = "0.38.28" +version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.5.0", "errno", "libc", "linux-raw-sys", @@ -3107,9 +3622,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.10" +version = "0.21.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", "ring", @@ -3123,7 +3638,7 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ - "base64", + "base64 0.21.7", ] [[package]] @@ -3138,15 +3653,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" - -[[package]] -name = "safemem" -version = "0.3.3" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "same-file" @@ -3159,11 +3668,11 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -3182,6 +3691,12 @@ dependencies = [ "untrusted", ] +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + [[package]] name = "search" version = "0.1.0" @@ -3194,11 +3709,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.9.2" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.5.0", "core-foundation", "core-foundation-sys", "libc", @@ -3207,9 +3722,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.9.1" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" dependencies = [ "core-foundation-sys", "libc", @@ -3217,40 +3732,40 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.20" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" dependencies = [ "serde", ] [[package]] name = "serde" -version = "1.0.193" +version = "1.0.201" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.193" +version = "1.0.201" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.61", ] [[package]] name = "serde_json" -version = "1.0.108" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" dependencies = [ - "indexmap 2.1.0", + "indexmap 2.2.6", "itoa", "ryu", "serde", @@ -3258,9 +3773,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" dependencies = [ "serde", ] @@ -3279,11 +3794,11 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.9.27" +version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cc7a1570e38322cfe4154732e5110f887ea57e22b76f4bfd32b5bdd3368666c" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.1.0", + "indexmap 2.2.6", "itoa", "ryu", "serde", @@ -3313,17 +3828,41 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "simd-abstraction" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cadb29c57caadc51ff8346233b5cec1d240b68ce55cf1afc764818791876987" +dependencies = [ + "outref", +] + [[package]] name = "simd-adler32" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +[[package]] +name = "simd_helpers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" +dependencies = [ + "quote", +] + +[[package]] +name = "simdutf8" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" + [[package]] name = "similar" -version = "2.3.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aeaf503862c419d66959f5d7ca015337d864e9c49485d771b732e2a20453597" +checksum = "fa42c91313f1d05da9b26f267f931cf178d4aba455b4c4622dd7355eb80c6640" [[package]] name = "siphasher" @@ -3360,7 +3899,7 @@ dependencies = [ "cargo_metadata", "error-chain", "glob", - "pulldown-cmark 0.9.3", + "pulldown-cmark 0.9.6", "tempfile", "walkdir", ] @@ -3386,28 +3925,18 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" - -[[package]] -name = "socket2" -version = "0.4.10" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" -dependencies = [ - "libc", - "winapi 0.3.9", -] +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" -version = "0.5.5" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -3451,6 +3980,12 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "svg_metadata" version = "0.5.0" @@ -3477,20 +4012,26 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.41" +version = "2.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c8b28c477cc3bf0e7966561e3460130e1255f7a1cf71931075f1c5e7a7e269" +checksum = "c993ed8ccba56ae856363b1845da7266a7cb78e1d146c8a32d54b45a8b831fc9" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + [[package]] name = "syntect" -version = "5.1.0" +version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e02b4b303bf8d08bfeb0445cba5068a3d306b6baece1d5582171a9bf49188f91" +checksum = "874dcfa363995604333cf947ae9f751ca3af4522c60886774c4963943b4746b1" dependencies = [ "bincode", "bitflags 1.3.2", @@ -3499,8 +4040,9 @@ dependencies = [ "once_cell", "onig", "plist", - "regex-syntax 0.7.5", + "regex-syntax", "serde", + "serde_derive", "serde_json", "thiserror", "walkdir", @@ -3528,6 +4070,25 @@ dependencies = [ "libc", ] +[[package]] +name = "system-deps" +version = "6.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +dependencies = [ + "cfg-expr", + "heck 0.5.0", + "pkg-config", + "toml 0.8.12", + "version-compare", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "tar" version = "0.4.40" @@ -3539,17 +4100,22 @@ dependencies = [ "xattr", ] +[[package]] +name = "target-lexicon" +version = "0.12.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" + [[package]] name = "tempfile" -version = "3.8.1" +version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if 1.0.0", "fastrand", - "redox_syscall", "rustix", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -3602,9 +4168,9 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" dependencies = [ "winapi-util", ] @@ -3627,7 +4193,7 @@ dependencies = [ "cfg-if 1.0.0", "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.61", ] [[package]] @@ -3638,41 +4204,41 @@ checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.61", "test-case-core", ] [[package]] name = "textwrap" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" +checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" [[package]] name = "thiserror" -version = "1.0.51" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7" +checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.51" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df" +checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.61", ] [[package]] name = "tiff" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d172b0f4d3fba17ba89811858b9d3d97f928aece846475bbda076ca46736211" +checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" dependencies = [ "flate2", "jpeg-decoder", @@ -3681,13 +4247,14 @@ dependencies = [ [[package]] name = "time" -version = "0.3.30" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", "libc", + "num-conv", "num_threads", "powerfmt", "serde", @@ -3703,10 +4270,11 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.15" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ + "num-conv", "time-core", ] @@ -3736,17 +4304,16 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.35.0" +version = "1.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d45b238a16291a4e1584e61820b8ae57d696cc5015c459c229ccc6990cc1c" +checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" dependencies = [ "backtrace", - "bytes 1.5.0", + "bytes 1.6.0", "libc", - "mio 0.8.10", - "num_cpus", + "mio 0.8.11", "pin-project-lite", - "socket2 0.5.5", + "socket2", "windows-sys 0.48.0", ] @@ -3772,16 +4339,15 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" dependencies = [ - "bytes 1.5.0", + "bytes 1.6.0", "futures-core", "futures-sink", "pin-project-lite", "tokio", - "tracing", ] [[package]] @@ -3795,9 +4361,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.8" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" +checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" dependencies = [ "serde", "serde_spanned", @@ -3816,11 +4382,11 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.21.0" +version = "0.22.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" +checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" dependencies = [ - "indexmap 2.1.0", + "indexmap 2.2.6", "serde", "serde_spanned", "toml_datetime", @@ -3949,9 +4515,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" @@ -3961,24 +4527,24 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] [[package]] name = "unicode-segmentation" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "unsafe-libyaml" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" [[package]] name = "untrusted" @@ -4019,23 +4585,52 @@ dependencies = [ "tempfile", ] +[[package]] +name = "uuid" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" + +[[package]] +name = "v_frame" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f32aaa24bacd11e488aa9ba66369c7cd514885742c9fe08cfe85884db3e92b" +dependencies = [ + "aligned-vec", + "num-traits", + "wasm-bindgen", +] + [[package]] name = "vcpkg" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "version-compare" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" + [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "vlq" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65dd7eed29412da847b0f78bcec0ac98588165988a8cfe41d4ea1d429f8ccfff" + [[package]] name = "walkdir" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", @@ -4064,9 +4659,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen-macro", @@ -4074,24 +4669,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.61", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.39" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -4101,9 +4696,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4111,28 +4706,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.61", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "web-sys" -version = "0.3.66" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" dependencies = [ "js-sys", "wasm-bindgen", @@ -4140,9 +4735,9 @@ dependencies = [ [[package]] name = "webp" -version = "0.2.6" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bb5d8e7814e92297b0e1c773ce43d290bef6c17452dafd9fc49e5edb5beba71" +checksum = "8f53152f51fb5af0c08484c33d16cca96175881d1f3dec068c23b31a158c2d99" dependencies = [ "image", "libwebp-sys", @@ -4150,15 +4745,15 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.25.3" +version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" [[package]] name = "weezl" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" +checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" [[package]] name = "winapi" @@ -4190,11 +4785,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ - "winapi 0.3.9", + "windows-sys 0.52.0", ] [[package]] @@ -4205,20 +4800,11 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-core" -version = "0.51.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" -dependencies = [ - "windows-targets 0.48.5", -] - -[[package]] -name = "windows-sys" -version = "0.45.0" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.42.2", + "windows-targets 0.52.5", ] [[package]] @@ -4236,22 +4822,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", -] - -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows-targets 0.52.5", ] [[package]] @@ -4271,25 +4842,20 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -4298,15 +4864,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" @@ -4316,15 +4876,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" @@ -4334,15 +4888,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" [[package]] -name = "windows_i686_msvc" -version = "0.42.2" +name = "windows_i686_gnullvm" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" @@ -4352,15 +4906,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" @@ -4370,15 +4918,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" @@ -4388,15 +4930,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" @@ -4406,15 +4942,15 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" -version = "0.5.28" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c830786f7720c2fd27a1a0e27a709dbd3c4d009b56d098fc742d4f4eab91fe2" +checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d" dependencies = [ "memchr", ] @@ -4466,11 +5002,20 @@ dependencies = [ "winapi-build", ] +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + [[package]] name = "xattr" -version = "1.1.3" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7dae5072fe1f8db8f8d29059189ac175196e410e40ba42d5d4684ae2f750995" +checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f" dependencies = [ "libc", "linux-raw-sys", @@ -4485,9 +5030,9 @@ checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" [[package]] name = "yada" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6d12cb7a57bbf2ab670ed9545bae3648048547f9039279a89ce000208e585c1" +checksum = "aed111bd9e48a802518765906cbdadf0b45afb72b9c81ab049a3b86252adffdd" [[package]] name = "yaml-rust" @@ -4500,29 +5045,29 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.31" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c4061bedbb353041c12f413700357bec76df2c7e2ca8e4df8bac24c6bf68e3d" +checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.31" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3c129550b3e6de3fd0ba67ba5c81818f9805e58b8d7fee80a3a59d2c9fc601a" +checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.61", ] [[package]] name = "zola" version = "0.18.0" dependencies = [ - "clap 4.4.11", + "clap 4.5.4", "clap_complete", "console 0.1.0", "ctrlc", @@ -4543,6 +5088,12 @@ dependencies = [ "ws", ] +[[package]] +name = "zune-core" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" + [[package]] name = "zune-inflate" version = "0.2.54" @@ -4551,3 +5102,12 @@ checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" dependencies = [ "simd-adler32", ] + +[[package]] +name = "zune-jpeg" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec866b44a2a1fd6133d363f073ca1b179f438f99e7e5bfb1e33f7181facfe448" +dependencies = [ + "zune-core", +] diff --git a/components/imageproc/src/processor.rs b/components/imageproc/src/processor.rs index 2c6a17b725..678e84a1f5 100644 --- a/components/imageproc/src/processor.rs +++ b/components/imageproc/src/processor.rs @@ -7,10 +7,11 @@ use config::Config; use errors::{anyhow, Context, Result}; use libs::ahash::{HashMap, HashSet}; use libs::image::imageops::FilterType; -use libs::image::{EncodableLayout, ImageOutputFormat}; +use libs::image::{EncodableLayout, ImageFormat}; use libs::rayon::prelude::*; use libs::{image, webp}; use serde::{Deserialize, Serialize}; +use libs::image::codecs::jpeg::JpegEncoder; use utils::fs as ufs; use crate::format::Format; @@ -55,10 +56,11 @@ impl ImageOp { match self.format { Format::Png => { - img.write_to(&mut buffered_f, ImageOutputFormat::Png)?; + img.write_to(&mut buffered_f, ImageFormat::Png)?; } Format::Jpeg(q) => { - img.write_to(&mut buffered_f, ImageOutputFormat::Jpeg(q))?; + let mut encoder = JpegEncoder::new_with_quality(&mut buffered_f, q); + encoder.encode_image(&img)?; } Format::WebP(q) => { let encoder = webp::Encoder::from_image(&img) diff --git a/components/libs/Cargo.toml b/components/libs/Cargo.toml index 9f6abf1d58..7c23ff3127 100644 --- a/components/libs/Cargo.toml +++ b/components/libs/Cargo.toml @@ -5,25 +5,25 @@ edition = "2021" [dependencies] ahash = "0.8" -ammonia = "3" +ammonia = "4" atty = "0.2.11" -base64 = "0.21" +base64 = "0.22" csv = "1" elasticlunr-rs = { version = "3.0.2", features = ["da", "no", "de", "du", "es", "fi", "fr", "hu", "it", "pt", "ro", "ru", "sv", "tr"] } filetime = "0.2" gh-emoji = "1" glob = "0.3" globset = "0.4" -image = "0.24" +image = "0.25" lexical-sort = "0.3" -minify-html = "0.11" +minify-html = "0.15" nom-bibtex = "0.5" num-format = "0.4" once_cell = "1" percent-encoding = "2" pulldown-cmark = { version = "0.10", default-features = false, features = ["html", "simd"] } pulldown-cmark-escape = { version = "0.10", default-features = false } -quickxml_to_serde = "0.5" +quickxml_to_serde = "0.6" rayon = "1" regex = "1" relative-path = "1" @@ -43,7 +43,7 @@ unic-langid = "0.9" unicode-segmentation = "1.2" url = "2" walkdir = "2" -webp = "0.2" +webp = "0.3" [features] From eccc642a0001f07002a6cd93a8a2ab75d544e9ed Mon Sep 17 00:00:00 2001 From: Vincent Prouillet Date: Thu, 9 May 2024 23:26:18 +0200 Subject: [PATCH 20/25] Update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7edc406a2e..b53c298784 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,10 @@ ## 0.19.0 (unreleased) - Updates the pulldown-cmark dependency to v0.10.0. This improves footnote handling, and may also introduce some minor behavior changes such as reducing the amount of unnecessary HTML-escaping of text content. +- Add bottom footnotes with backreference option +- Fix link check report inconsistency +- Fix resizing for images with EXIF orientation +- Add MIME type to get_image_metadata ## 0.18.0 (2023-12-18) From 731e6c05ad6a0e97b90cfd83dcd8a802df1a1932 Mon Sep 17 00:00:00 2001 From: Vincent Prouillet Date: Sun, 12 May 2024 21:19:35 +0200 Subject: [PATCH 21/25] Update msrv --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 0221c7169f..2ac2cae6d2 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -21,7 +21,7 @@ stages: rustup_toolchain: stable linux-pinned: imageName: 'ubuntu-20.04' - rustup_toolchain: 1.71.1 + rustup_toolchain: 1.74.0 pool: vmImage: $(imageName) steps: From 83d144d1414df961339005e243c529e498bdd626 Mon Sep 17 00:00:00 2001 From: Vincent Prouillet Date: Sun, 12 May 2024 22:11:13 +0200 Subject: [PATCH 22/25] update tests --- components/site/src/minify.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/site/src/minify.rs b/components/site/src/minify.rs index 8f3862b902..7c613299e9 100644 --- a/components/site/src/minify.rs +++ b/components/site/src/minify.rs @@ -108,7 +108,7 @@ mod tests { "#; - let expected = r#"

Example blog post

FOO BAR"#; + let expected = r#"

Example blog post

FOO BAR"#; let res = html(input.to_owned()).unwrap(); assert_eq!(res, expected); } @@ -135,7 +135,7 @@ mod tests { "#; - let expected = r#"

Example blog post

FOO BAR"#; + let expected = r#"

Example blog post

FOO BAR"#; let res = html(input.to_owned()).unwrap(); assert_eq!(res, expected); } From cebeb3a90e5cc2f9560075471fc69ad89c9af3e7 Mon Sep 17 00:00:00 2001 From: Vincent Prouillet Date: Mon, 13 May 2024 10:20:22 +0200 Subject: [PATCH 23/25] cargo fmt --- components/imageproc/src/processor.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/imageproc/src/processor.rs b/components/imageproc/src/processor.rs index 678e84a1f5..577aeb1f3e 100644 --- a/components/imageproc/src/processor.rs +++ b/components/imageproc/src/processor.rs @@ -6,12 +6,12 @@ use std::path::{Path, PathBuf}; use config::Config; use errors::{anyhow, Context, Result}; use libs::ahash::{HashMap, HashSet}; +use libs::image::codecs::jpeg::JpegEncoder; use libs::image::imageops::FilterType; use libs::image::{EncodableLayout, ImageFormat}; use libs::rayon::prelude::*; use libs::{image, webp}; use serde::{Deserialize, Serialize}; -use libs::image::codecs::jpeg::JpegEncoder; use utils::fs as ufs; use crate::format::Format; From 8842da7523ba45dce32f6dd2bfc2d066c2ecb641 Mon Sep 17 00:00:00 2001 From: Luca Versari Date: Wed, 1 May 2024 10:37:58 +0200 Subject: [PATCH 24/25] Add JPEG XL support to image processing. As discussed in #2421. --- Cargo.lock | 115 ++++++++++++++++++ components/imageproc/src/format.rs | 10 +- components/imageproc/src/meta.rs | 31 ++++- components/imageproc/src/processor.rs | 53 +++++++- components/imageproc/tests/resize_image.rs | 10 ++ components/imageproc/tests/test_imgs/jxl.jxl | Bin 0 -> 21972 bytes components/libs/Cargo.toml | 1 + components/libs/src/lib.rs | 1 + .../content/image-processing/index.md | 1 + 9 files changed, 210 insertions(+), 12 deletions(-) create mode 100644 components/imageproc/tests/test_imgs/jxl.jxl diff --git a/Cargo.lock b/Cargo.lock index 293d58133a..4c5e73796a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -636,6 +636,15 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +[[package]] +name = "cmake" +version = "0.1.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130" +dependencies = [ + "cc", +] + [[package]] name = "codemap" version = "0.1.3" @@ -876,6 +885,41 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "darling" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.10.0", + "syn 2.0.61", +] + +[[package]] +name = "darling_macro" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.61", +] + [[package]] name = "dashmap" version = "5.5.3" @@ -913,6 +957,37 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "derive_builder" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0350b5cb0331628a5916d6c5c0b72e97393b8b6b03b47a9284f4e7f5a405ffd7" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d48cda787f839151732d396ac69e3473923d54312c070ee21e9effcaa8ca0b1d" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.61", +] + +[[package]] +name = "derive_builder_macro" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b" +dependencies = [ + "derive_builder_core", + "syn 2.0.61", +] + [[package]] name = "deunicode" version = "1.4.4" @@ -1654,6 +1729,12 @@ dependencies = [ "cc", ] +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "idna" version = "0.5.0" @@ -1889,6 +1970,39 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" +[[package]] +name = "jpegxl-rs" +version = "0.10.3+libjxl-0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e697c7532d4a244d91306a19c63763769366fbc61e75d5a08cc602923e478a7a" +dependencies = [ + "byteorder", + "derive_builder", + "half", + "image", + "jpegxl-sys", + "thiserror", +] + +[[package]] +name = "jpegxl-src" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7808dc25b79f6ff27137f91b1b7659eb27f570d401ff464d03fd336041fc5c68" +dependencies = [ + "cmake", +] + +[[package]] +name = "jpegxl-sys" +version = "0.10.3+libjxl-0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8262d19ed55829441dc2a2b1315c8f15e040139443fc1dbc48551890020a5c2" +dependencies = [ + "jpegxl-src", + "pkg-config", +] + [[package]] name = "js-sys" version = "0.3.69" @@ -1992,6 +2106,7 @@ dependencies = [ "globset", "grass", "image", + "jpegxl-rs", "lexical-sort", "minify-html", "nom-bibtex", diff --git a/components/imageproc/src/format.rs b/components/imageproc/src/format.rs index d7c992eab8..ea49d73756 100644 --- a/components/imageproc/src/format.rs +++ b/components/imageproc/src/format.rs @@ -6,12 +6,14 @@ const DEFAULT_Q_JPG: u8 = 75; /// Thumbnail image format #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Format { - /// JPEG, The `u8` argument is JPEG quality (in percent). + /// JPEG, The `u8` argument is JPEG quality (1..100). Jpeg(u8), /// PNG Png, - /// WebP, The `u8` argument is WebP quality (in percent), None meaning lossless. + /// WebP, The `u8` argument is WebP quality (1..100), None meaning lossless. WebP(Option), + /// JPEG XL, The `u8` argument is quality (1..100), None meaning lossless. + JXL(Option), } impl Format { @@ -32,6 +34,7 @@ impl Format { "jpeg" | "jpg" => Ok(Jpeg(jpg_quality)), "png" => Ok(Png), "webp" => Ok(WebP(quality)), + "jxl" => Ok(JXL(quality)), _ => Err(anyhow!("Invalid image format: {}", format)), } } @@ -44,6 +47,7 @@ impl Format { Png => "png", Jpeg(_) => "jpg", WebP(_) => "webp", + JXL(_) => "jxl", } } } @@ -58,6 +62,8 @@ impl Hash for Format { Jpeg(q) => 1001 + q as u16, WebP(None) => 2000, WebP(Some(q)) => 2001 + q as u16, + JXL(None) => 3000, + JXL(Some(q)) => 3001 + q as u16, }; hasher.write_u16(q); diff --git a/components/imageproc/src/meta.rs b/components/imageproc/src/meta.rs index 8ee83c335f..1ba38897c1 100644 --- a/components/imageproc/src/meta.rs +++ b/components/imageproc/src/meta.rs @@ -1,6 +1,7 @@ use errors::{anyhow, Context, Result}; use libs::image::io::Reader as ImgReader; -use libs::image::{ImageFormat, ImageResult}; +use libs::image::ImageFormat; +use libs::jpegxl_rs::decoder_builder; use libs::svg_metadata::Metadata as SvgMetadata; use serde::Serialize; use std::ffi::OsStr; @@ -15,12 +16,23 @@ pub struct ImageMeta { } impl ImageMeta { - pub fn read(path: &Path) -> ImageResult { - let reader = ImgReader::open(path).and_then(ImgReader::with_guessed_format)?; - let format = reader.format(); - let size = reader.into_dimensions()?; + pub fn read(path: &Path) -> Result { + if path.extension().is_some_and(|ext| ext == "jxl") { + Self::read_jxl(path) + } else { + let reader = ImgReader::open(path).and_then(ImgReader::with_guessed_format)?; + let format = reader.format(); + let size = reader.into_dimensions()?; - Ok(Self { size, format }) + Ok(Self { size, format }) + } + } + + fn read_jxl(path: &Path) -> Result { + let input = std::fs::read(path)?; + let decoder = decoder_builder().build()?; + let (meta, _) = decoder.decode(&input)?; + Ok(ImageMeta { size: (meta.width, meta.height), format: None }) } pub fn is_lossy(&self) -> bool { @@ -44,6 +56,9 @@ impl ImageMetaResponse { pub fn new_svg(width: u32, height: u32) -> Self { Self { width, height, format: Some("svg"), mime: Some("text/svg+xml") } } + pub fn new_jxl(width: u32, height: u32) -> Self { + Self { width, height, format: Some("jxl"), mime: Some("image/jxl") } + } } impl From for ImageMetaResponse { @@ -75,6 +90,10 @@ pub fn read_image_metadata>(path: P) -> Result // this is not a typo, this returns the correct values for width and height. .map(|(h, w)| ImageMetaResponse::new_svg(w as u32, h as u32)) } + "jxl" => { + let meta = ImageMeta::read(path)?; + Ok(ImageMetaResponse::new_jxl(meta.size.0, meta.size.1)) + } _ => ImageMeta::read(path).map(ImageMetaResponse::from).with_context(err_context), } } diff --git a/components/imageproc/src/processor.rs b/components/imageproc/src/processor.rs index 577aeb1f3e..72f8a42615 100644 --- a/components/imageproc/src/processor.rs +++ b/components/imageproc/src/processor.rs @@ -4,13 +4,16 @@ use std::io::{BufWriter, Write}; use std::path::{Path, PathBuf}; use config::Config; -use errors::{anyhow, Context, Result}; +use errors::{anyhow, bail, Context, Result}; use libs::ahash::{HashMap, HashSet}; use libs::image::codecs::jpeg::JpegEncoder; use libs::image::imageops::FilterType; -use libs::image::{EncodableLayout, ImageFormat}; +use libs::image::{ColorType, EncodableLayout, ImageFormat}; +use libs::jpegxl_rs::decoder_builder; +use libs::jpegxl_rs::encode::EncoderFrame; +use libs::jpegxl_rs::image::ToDynamic; use libs::rayon::prelude::*; -use libs::{image, webp}; +use libs::{image, jpegxl_rs, webp}; use serde::{Deserialize, Serialize}; use utils::fs as ufs; @@ -39,7 +42,15 @@ impl ImageOp { return Ok(()); } - let img = image::open(&self.input_path)?; + let img = if self.input_path.extension().is_some_and(|ext| ext == "jxl") { + let input = std::fs::read(&self.input_path)?; + let decoder = decoder_builder().build()?; + decoder + .decode_to_image(&input)? + .context("jxl image could not be represented in an Image")? + } else { + image::open(&self.input_path)? + }; let mut img = fix_orientation(&img, &self.input_path).unwrap_or(img); let img = match self.instr.crop_instruction { @@ -71,6 +82,40 @@ impl ImageOp { }; buffered_f.write_all(memory.as_bytes())?; } + Format::JXL(q) => { + let mut encoder = jpegxl_rs::encoder_builder(); + if let Some(q) = q { + if q == 100 { + encoder.uses_original_profile(true); + encoder.lossless(true); + } else { + encoder.set_jpeg_quality(q as f32); + } + } else { + encoder.uses_original_profile(true); + encoder.lossless(true); + } + let frame = EncoderFrame::new(img.as_bytes()); + let frame = match img.color() { + ColorType::L8 => frame.num_channels(1), + ColorType::La8 => { + encoder.has_alpha(true); + frame.num_channels(2) + } + ColorType::Rgb8 => frame.num_channels(3), + ColorType::Rgba8 => { + encoder.has_alpha(true); + frame.num_channels(4) + } + _ => { + bail!("Unsupported pixel type {:?}", img.color()); + } + }; + let mut encoder = encoder.build()?; + buffered_f.write_all( + &encoder.encode_frame::(&frame, img.width(), img.height())?.data, + )?; + } } Ok(()) diff --git a/components/imageproc/tests/resize_image.rs b/components/imageproc/tests/resize_image.rs index 41114abf50..40e32732e1 100644 --- a/components/imageproc/tests/resize_image.rs +++ b/components/imageproc/tests/resize_image.rs @@ -132,6 +132,16 @@ fn resize_image_webp_jpg() { image_op_test("webp.webp", "scale", Some(150), Some(150), "auto", "jpg", 150, 150, 300, 380); } +#[test] +fn resize_image_png_jxl() { + image_op_test("png.png", "scale", Some(150), Some(150), "jxl", "jxl", 150, 150, 300, 380); +} + +#[test] +fn resize_image_jxl_png() { + image_op_test("jxl.jxl", "scale", Some(150), Some(150), "png", "png", 150, 150, 300, 380); +} + #[test] fn read_image_metadata_jpg() { assert_eq!( diff --git a/components/imageproc/tests/test_imgs/jxl.jxl b/components/imageproc/tests/test_imgs/jxl.jxl new file mode 100644 index 0000000000000000000000000000000000000000..a5e3db1de35224a54f7f1cd3ffe582a217bb6e03 GIT binary patch literal 21972 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 Date: Thu, 16 May 2024 14:41:07 +0200 Subject: [PATCH 25/25] Rename JXL -> JpegXl --- components/imageproc/src/format.rs | 10 +++++----- components/imageproc/src/processor.rs | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/components/imageproc/src/format.rs b/components/imageproc/src/format.rs index ea49d73756..f0f6bb69a0 100644 --- a/components/imageproc/src/format.rs +++ b/components/imageproc/src/format.rs @@ -13,7 +13,7 @@ pub enum Format { /// WebP, The `u8` argument is WebP quality (1..100), None meaning lossless. WebP(Option), /// JPEG XL, The `u8` argument is quality (1..100), None meaning lossless. - JXL(Option), + JpegXL(Option), } impl Format { @@ -34,7 +34,7 @@ impl Format { "jpeg" | "jpg" => Ok(Jpeg(jpg_quality)), "png" => Ok(Png), "webp" => Ok(WebP(quality)), - "jxl" => Ok(JXL(quality)), + "jxl" => Ok(JpegXL(quality)), _ => Err(anyhow!("Invalid image format: {}", format)), } } @@ -47,7 +47,7 @@ impl Format { Png => "png", Jpeg(_) => "jpg", WebP(_) => "webp", - JXL(_) => "jxl", + JpegXL(_) => "jxl", } } } @@ -62,8 +62,8 @@ impl Hash for Format { Jpeg(q) => 1001 + q as u16, WebP(None) => 2000, WebP(Some(q)) => 2001 + q as u16, - JXL(None) => 3000, - JXL(Some(q)) => 3001 + q as u16, + JpegXL(None) => 3000, + JpegXL(Some(q)) => 3001 + q as u16, }; hasher.write_u16(q); diff --git a/components/imageproc/src/processor.rs b/components/imageproc/src/processor.rs index 72f8a42615..48e661127a 100644 --- a/components/imageproc/src/processor.rs +++ b/components/imageproc/src/processor.rs @@ -82,7 +82,7 @@ impl ImageOp { }; buffered_f.write_all(memory.as_bytes())?; } - Format::JXL(q) => { + Format::JpegXL(q) => { let mut encoder = jpegxl_rs::encoder_builder(); if let Some(q) = q { if q == 100 {