fix internal link in file with a specific path
This commit is contained in:
parent
758dfcea5b
commit
48a1518d7f
9 changed files with 169 additions and 43 deletions
|
@ -6,7 +6,7 @@ use crate::{
|
||||||
config::Config,
|
config::Config,
|
||||||
template::{InfosPage, NavBar},
|
template::{InfosPage, NavBar},
|
||||||
utils::{
|
utils::{
|
||||||
markdown::File,
|
markdown::{File, FilePath},
|
||||||
metadata::MType,
|
metadata::MType,
|
||||||
misc::{make_kw, read_file, Html},
|
misc::{make_kw, read_file, Html},
|
||||||
routes::blog::{build_rss, get_post, get_posts, Post, BLOG_DIR, MIME_TYPE_RSS, POST_DIR},
|
routes::blog::{build_rss, get_post, get_posts, Post, BLOG_DIR, MIME_TYPE_RSS, POST_DIR},
|
||||||
|
@ -32,7 +32,13 @@ fn build_index(config: Config) -> String {
|
||||||
let mut posts = get_posts(&format!("{blog_dir}/{POST_DIR}"));
|
let mut posts = get_posts(&format!("{blog_dir}/{POST_DIR}"));
|
||||||
|
|
||||||
// Get about
|
// Get about
|
||||||
let about: Option<File> = read_file(format!("{blog_dir}/about.md"), MType::Generic);
|
let about: Option<File> = read_file(
|
||||||
|
FilePath {
|
||||||
|
base: blog_dir,
|
||||||
|
path: "about.md".to_owned(),
|
||||||
|
},
|
||||||
|
MType::Generic,
|
||||||
|
);
|
||||||
|
|
||||||
// Sort from newest to oldest
|
// Sort from newest to oldest
|
||||||
posts.sort_by_cached_key(|p| (p.date.year, p.date.month, p.date.day));
|
posts.sort_by_cached_key(|p| (p.date.year, p.date.month, p.date.day));
|
||||||
|
|
|
@ -6,7 +6,7 @@ use crate::{
|
||||||
config::Config,
|
config::Config,
|
||||||
template::{InfosPage, NavBar},
|
template::{InfosPage, NavBar},
|
||||||
utils::{
|
utils::{
|
||||||
markdown::File,
|
markdown::{File, FilePath},
|
||||||
metadata::MType,
|
metadata::MType,
|
||||||
misc::{make_kw, read_file, Html},
|
misc::{make_kw, read_file, Html},
|
||||||
routes::contact::{find_links, read, remove_paragraphs},
|
routes::contact::{find_links, read, remove_paragraphs},
|
||||||
|
@ -84,11 +84,26 @@ fn build_page(config: Config) -> String {
|
||||||
let ext = ".md";
|
let ext = ".md";
|
||||||
|
|
||||||
// Get about
|
// Get about
|
||||||
let about = read_file(format!("{contacts_dir}/about.md"), MType::Generic);
|
let about = read_file(
|
||||||
|
FilePath {
|
||||||
|
base: contacts_dir.clone(),
|
||||||
|
path: "about.md".to_owned(),
|
||||||
|
},
|
||||||
|
MType::Generic,
|
||||||
|
);
|
||||||
|
|
||||||
let mut socials = read(&format!("{contacts_dir}/socials/*{ext}"));
|
let mut socials = read(FilePath {
|
||||||
let mut forges = read(&format!("{contacts_dir}/forges/*{ext}"));
|
base: contacts_dir.clone(),
|
||||||
let mut others = read(&format!("{contacts_dir}/others/*{ext}"));
|
path: format!("socials/*{ext}"),
|
||||||
|
});
|
||||||
|
let mut forges = read(FilePath {
|
||||||
|
base: contacts_dir.clone(),
|
||||||
|
path: format!("forges/*{ext}"),
|
||||||
|
});
|
||||||
|
let mut others = read(FilePath {
|
||||||
|
base: contacts_dir,
|
||||||
|
path: format!("others/*{ext}"),
|
||||||
|
});
|
||||||
|
|
||||||
// Remove paragraphs in custom statements
|
// Remove paragraphs in custom statements
|
||||||
[&mut socials, &mut forges, &mut others]
|
[&mut socials, &mut forges, &mut others]
|
||||||
|
|
|
@ -8,7 +8,7 @@ use crate::{
|
||||||
config::Config,
|
config::Config,
|
||||||
template::{InfosPage, NavBar},
|
template::{InfosPage, NavBar},
|
||||||
utils::{
|
utils::{
|
||||||
markdown::File,
|
markdown::{File, FilePath},
|
||||||
metadata::MType,
|
metadata::MType,
|
||||||
misc::{make_kw, read_file, Html},
|
misc::{make_kw, read_file, Html},
|
||||||
routes::cours::{excluded, get_filetree},
|
routes::cours::{excluded, get_filetree},
|
||||||
|
@ -54,7 +54,13 @@ fn get_content(
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
read_file(format!("{cours_dir}/{filename}"), MType::Cours)
|
read_file(
|
||||||
|
FilePath {
|
||||||
|
base: cours_dir.to_owned(),
|
||||||
|
path: filename.to_owned(),
|
||||||
|
},
|
||||||
|
MType::Cours,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_page(info: &web::Query<PathRequest>, config: Config) -> String {
|
fn build_page(info: &web::Query<PathRequest>, config: Config) -> String {
|
||||||
|
|
|
@ -6,7 +6,7 @@ use crate::{
|
||||||
config::Config,
|
config::Config,
|
||||||
template::{InfosPage, NavBar},
|
template::{InfosPage, NavBar},
|
||||||
utils::{
|
utils::{
|
||||||
markdown::File,
|
markdown::{File, FilePath},
|
||||||
metadata::MType,
|
metadata::MType,
|
||||||
misc::{make_kw, read_file, Html},
|
misc::{make_kw, read_file, Html},
|
||||||
},
|
},
|
||||||
|
@ -37,7 +37,10 @@ struct StyleAvatar {
|
||||||
#[once(time = 60)]
|
#[once(time = 60)]
|
||||||
fn build_page(config: Config) -> String {
|
fn build_page(config: Config) -> String {
|
||||||
let mut file = read_file(
|
let mut file = read_file(
|
||||||
format!("{}/index.md", config.locations.data_dir),
|
FilePath {
|
||||||
|
base: config.locations.data_dir,
|
||||||
|
path: "index.md".to_owned(),
|
||||||
|
},
|
||||||
MType::Index,
|
MType::Index,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -68,7 +71,13 @@ fn build_page(config: Config) -> String {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
file = read_file("README.md".to_string(), MType::Generic);
|
file = read_file(
|
||||||
|
FilePath {
|
||||||
|
base: String::new(),
|
||||||
|
path: "README.md".to_owned(),
|
||||||
|
},
|
||||||
|
MType::Generic,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
config.tmpl.render(
|
config.tmpl.render(
|
||||||
|
|
|
@ -7,7 +7,7 @@ use crate::{
|
||||||
config::Config,
|
config::Config,
|
||||||
template::{InfosPage, NavBar},
|
template::{InfosPage, NavBar},
|
||||||
utils::{
|
utils::{
|
||||||
markdown::File,
|
markdown::{File, FilePath},
|
||||||
metadata::MType,
|
metadata::MType,
|
||||||
misc::{make_kw, read_file, Html},
|
misc::{make_kw, read_file, Html},
|
||||||
},
|
},
|
||||||
|
@ -32,20 +32,35 @@ struct PortfolioTemplate<'a> {
|
||||||
#[once(time = 60)]
|
#[once(time = 60)]
|
||||||
fn build_page(config: Config) -> String {
|
fn build_page(config: Config) -> String {
|
||||||
let projects_dir = format!("{}/projects", config.locations.data_dir);
|
let projects_dir = format!("{}/projects", config.locations.data_dir);
|
||||||
let apps_dir = format!("{projects_dir}/apps");
|
let apps_dir = FilePath {
|
||||||
|
base: format!("{projects_dir}/apps"),
|
||||||
|
path: String::new(),
|
||||||
|
};
|
||||||
let ext = ".md";
|
let ext = ".md";
|
||||||
|
|
||||||
// Get about
|
// Get about
|
||||||
let about = read_file(format!("{projects_dir}/about.md"), MType::Generic);
|
let about = read_file(
|
||||||
|
FilePath {
|
||||||
|
base: projects_dir,
|
||||||
|
path: "about.md".to_owned(),
|
||||||
|
},
|
||||||
|
MType::Generic,
|
||||||
|
);
|
||||||
|
|
||||||
// Get apps
|
// Get apps
|
||||||
let apps = glob(&format!("{apps_dir}/*{ext}"))
|
let apps = glob(&format!("{apps_dir}/*{ext}"))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.map(|e| read_file(e.unwrap().to_string_lossy().to_string(), MType::Portfolio).unwrap())
|
.map(|e| {
|
||||||
|
read_file(
|
||||||
|
apps_dir.from(&e.unwrap().to_string_lossy()),
|
||||||
|
MType::Portfolio,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
})
|
||||||
.collect::<Vec<File>>();
|
.collect::<Vec<File>>();
|
||||||
|
|
||||||
let appdata = if apps.is_empty() {
|
let appdata = if apps.is_empty() {
|
||||||
(None, Some(apps_dir.as_str()))
|
(None, Some(apps_dir.to_string()))
|
||||||
} else {
|
} else {
|
||||||
(Some(apps), None)
|
(Some(apps), None)
|
||||||
};
|
};
|
||||||
|
@ -53,7 +68,13 @@ fn build_page(config: Config) -> String {
|
||||||
// Get archived apps
|
// Get archived apps
|
||||||
let archived_apps = glob(&format!("{apps_dir}/archive/*{ext}"))
|
let archived_apps = glob(&format!("{apps_dir}/archive/*{ext}"))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.map(|e| read_file(e.unwrap().to_string_lossy().to_string(), MType::Portfolio).unwrap())
|
.map(|e| {
|
||||||
|
read_file(
|
||||||
|
apps_dir.from(&e.unwrap().to_string_lossy()),
|
||||||
|
MType::Portfolio,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
})
|
||||||
.collect::<Vec<File>>();
|
.collect::<Vec<File>>();
|
||||||
|
|
||||||
let archived_appdata = if archived_apps.is_empty() {
|
let archived_appdata = if archived_apps.is_empty() {
|
||||||
|
@ -71,7 +92,7 @@ fn build_page(config: Config) -> String {
|
||||||
},
|
},
|
||||||
about,
|
about,
|
||||||
apps: appdata.0,
|
apps: appdata.0,
|
||||||
location_apps: appdata.1,
|
location_apps: appdata.1.as_deref(),
|
||||||
archived_apps: archived_appdata.0,
|
archived_apps: archived_appdata.0,
|
||||||
archived_apps_exists: archived_appdata.1,
|
archived_apps_exists: archived_appdata.1,
|
||||||
err_msg: "is empty",
|
err_msg: "is empty",
|
||||||
|
|
|
@ -21,8 +21,36 @@ pub struct File {
|
||||||
pub content: String,
|
pub content: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Hash, PartialEq, Eq, Clone)]
|
||||||
|
pub struct FilePath {
|
||||||
|
pub base: String,
|
||||||
|
pub path: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for FilePath {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
if self.path.is_empty() {
|
||||||
|
return write!(f, "{}", self.base);
|
||||||
|
}
|
||||||
|
if self.base.is_empty() {
|
||||||
|
return write!(f, "{}", self.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(f, "{}/{}", self.base, self.path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FilePath {
|
||||||
|
pub fn from(&self, fullpath: &str) -> Self {
|
||||||
|
Self {
|
||||||
|
base: self.base.clone(),
|
||||||
|
path: fullpath.to_owned().split_off(self.base.len()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Options used for parser and compiler MD --> HTML
|
/// Options used for parser and compiler MD --> HTML
|
||||||
pub fn get_options(metadata_type: MType) -> ComrakOptions {
|
pub fn get_options(path: Option<FilePath>, metadata_type: MType) -> ComrakOptions {
|
||||||
comrak::Options {
|
comrak::Options {
|
||||||
extension: comrak::ExtensionOptions::builder()
|
extension: comrak::ExtensionOptions::builder()
|
||||||
.strikethrough(true)
|
.strikethrough(true)
|
||||||
|
@ -39,12 +67,27 @@ pub fn get_options(metadata_type: MType) -> ComrakOptions {
|
||||||
.math_dollars(true)
|
.math_dollars(true)
|
||||||
.underline(true)
|
.underline(true)
|
||||||
.maybe_link_url_rewriter(match metadata_type {
|
.maybe_link_url_rewriter(match metadata_type {
|
||||||
MType::Cours => Some(Arc::new(|url: &str| {
|
MType::Cours => Some(Arc::new(move |url: &str| {
|
||||||
if url.contains("://") {
|
if url.contains("://") {
|
||||||
return String::from(url);
|
return String::from(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
format!("/cours?q={url}")
|
let file = if url.starts_with("./") {
|
||||||
|
url.to_owned().split_off(2)
|
||||||
|
} else {
|
||||||
|
url.to_owned()
|
||||||
|
};
|
||||||
|
|
||||||
|
match &path {
|
||||||
|
Some(p) => {
|
||||||
|
let mut parts = p.path.split('/').collect::<Vec<_>>();
|
||||||
|
parts.pop();
|
||||||
|
parts.push(&file);
|
||||||
|
|
||||||
|
format!("/cours?q={}", parts.join("/"))
|
||||||
|
}
|
||||||
|
None => format!("/cours?q={file}"),
|
||||||
|
}
|
||||||
})),
|
})),
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
|
@ -116,7 +159,7 @@ fn custom_img_size(html: &str) -> String {
|
||||||
|
|
||||||
/// Fix local images to base64 and integration of markdown files
|
/// Fix local images to base64 and integration of markdown files
|
||||||
fn fix_images_and_integration(
|
fn fix_images_and_integration(
|
||||||
path: &str,
|
path: FilePath,
|
||||||
html: &str,
|
html: &str,
|
||||||
metadata_type: MType,
|
metadata_type: MType,
|
||||||
recursive: bool,
|
recursive: bool,
|
||||||
|
@ -137,7 +180,8 @@ fn fix_images_and_integration(
|
||||||
RewriteStrSettings {
|
RewriteStrSettings {
|
||||||
element_content_handlers: vec![element!("img", |el| {
|
element_content_handlers: vec![element!("img", |el| {
|
||||||
if let Some(src) = el.get_attribute("src") {
|
if let Some(src) = el.get_attribute("src") {
|
||||||
let img_src = Path::new(path).parent().unwrap();
|
let path_str = path.to_string();
|
||||||
|
let img_src = Path::new(&path_str).parent().unwrap();
|
||||||
let img_path = urlencoding::decode(img_src.join(src).to_str().unwrap())
|
let img_path = urlencoding::decode(img_src.join(src).to_str().unwrap())
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_string();
|
.to_string();
|
||||||
|
@ -145,10 +189,15 @@ fn fix_images_and_integration(
|
||||||
if let Ok(file) = fs::read_to_string(&img_path) {
|
if let Ok(file) = fs::read_to_string(&img_path) {
|
||||||
let mime = mime_guess::from_path(&img_path).first_or_octet_stream();
|
let mime = mime_guess::from_path(&img_path).first_or_octet_stream();
|
||||||
if recursive && mime == "text/markdown" {
|
if recursive && mime == "text/markdown" {
|
||||||
let mut options = get_options(metadata_type);
|
let mut options = get_options(Some(path.clone()), metadata_type);
|
||||||
options.extension.footnotes = false;
|
options.extension.footnotes = false;
|
||||||
let data =
|
let data = read_md(
|
||||||
read_md(&img_path, &file, metadata_type, Some(options), false);
|
path.from(&img_path),
|
||||||
|
&file,
|
||||||
|
metadata_type,
|
||||||
|
Some(options),
|
||||||
|
false,
|
||||||
|
);
|
||||||
el.replace(&data.content, ContentType::Html);
|
el.replace(&data.content, ContentType::Html);
|
||||||
|
|
||||||
// Store the metadata for later merging
|
// Store the metadata for later merging
|
||||||
|
@ -177,7 +226,7 @@ fn fix_images_and_integration(
|
||||||
|
|
||||||
/// Transform markdown string to File structure
|
/// Transform markdown string to File structure
|
||||||
pub fn read_md(
|
pub fn read_md(
|
||||||
path: &str,
|
path: FilePath,
|
||||||
raw_text: &str,
|
raw_text: &str,
|
||||||
metadata_type: MType,
|
metadata_type: MType,
|
||||||
options: Option<Options>,
|
options: Option<Options>,
|
||||||
|
@ -185,7 +234,10 @@ pub fn read_md(
|
||||||
) -> File {
|
) -> File {
|
||||||
let arena = Arena::new();
|
let arena = Arena::new();
|
||||||
|
|
||||||
let mut opt = options.map_or_else(|| get_options(metadata_type), |specific_opt| specific_opt);
|
let mut opt = options.map_or_else(
|
||||||
|
|| get_options(Some(path.clone()), metadata_type),
|
||||||
|
|specific_opt| specific_opt,
|
||||||
|
);
|
||||||
let root = parse_document(&arena, raw_text, &opt);
|
let root = parse_document(&arena, raw_text, &opt);
|
||||||
|
|
||||||
// Find metadata
|
// Find metadata
|
||||||
|
|
|
@ -12,7 +12,7 @@ use reqwest::Client;
|
||||||
use crate::config::FileConfiguration;
|
use crate::config::FileConfiguration;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
markdown::{read_md, File},
|
markdown::{read_md, File, FilePath},
|
||||||
metadata::{MType, Metadata},
|
metadata::{MType, Metadata},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -57,13 +57,14 @@ impl Responder for Html {
|
||||||
|
|
||||||
/// Read a file
|
/// Read a file
|
||||||
#[cached]
|
#[cached]
|
||||||
pub fn read_file(filename: String, expected_file: MType) -> Option<File> {
|
pub fn read_file(filename: FilePath, expected_file: MType) -> Option<File> {
|
||||||
Path::new(&filename.clone())
|
let as_str = filename.to_string();
|
||||||
|
Path::new(&as_str)
|
||||||
.extension()
|
.extension()
|
||||||
.and_then(|ext| match ext.to_str().unwrap() {
|
.and_then(|ext| match ext.to_str().unwrap() {
|
||||||
"pdf" => fs::read(filename).map_or(None, |bytes| Some(read_pdf(bytes))),
|
"pdf" => fs::read(&as_str).map_or(None, |bytes| Some(read_pdf(bytes))),
|
||||||
_ => fs::read_to_string(&filename).map_or(None, |text| {
|
_ => fs::read_to_string(&as_str).map_or(None, |text| {
|
||||||
Some(read_md(&filename, &text, expected_file, None, true))
|
Some(read_md(filename, &text, expected_file, None, true))
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ use crate::{
|
||||||
template::InfosPage,
|
template::InfosPage,
|
||||||
utils::{
|
utils::{
|
||||||
date::Date,
|
date::Date,
|
||||||
markdown::{get_options, File},
|
markdown::{get_options, File, FilePath},
|
||||||
metadata::{get, FileMetadataBlog, MType},
|
metadata::{get, FileMetadataBlog, MType},
|
||||||
misc::{get_url, make_kw, read_file},
|
misc::{get_url, make_kw, read_file},
|
||||||
},
|
},
|
||||||
|
@ -45,7 +45,13 @@ impl Post {
|
||||||
let blog_dir = format!("{data_dir}/{BLOG_DIR}/{POST_DIR}");
|
let blog_dir = format!("{data_dir}/{BLOG_DIR}/{POST_DIR}");
|
||||||
let ext = ".md";
|
let ext = ".md";
|
||||||
|
|
||||||
if let Some(file) = read_file(format!("{blog_dir}/{}{ext}", self.url), MType::Blog) {
|
if let Some(file) = read_file(
|
||||||
|
FilePath {
|
||||||
|
base: blog_dir,
|
||||||
|
path: format!("{}{ext}", self.url),
|
||||||
|
},
|
||||||
|
MType::Blog,
|
||||||
|
) {
|
||||||
self.content = Some(file.content);
|
self.content = Some(file.content);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,7 +85,7 @@ pub fn get_posts(location: &str) -> Vec<Post> {
|
||||||
|text| {
|
|text| {
|
||||||
let arena = Arena::new();
|
let arena = Arena::new();
|
||||||
|
|
||||||
let options = get_options(MType::Generic);
|
let options = get_options(None, MType::Generic);
|
||||||
let root = parse_document(&arena, &text, &options);
|
let root = parse_document(&arena, &text, &options);
|
||||||
let mut metadata = get(root, MType::Blog).blog.unwrap();
|
let mut metadata = get(root, MType::Blog).blog.unwrap();
|
||||||
|
|
||||||
|
@ -137,7 +143,13 @@ pub fn get_post(
|
||||||
let blog_dir = format!("{data_dir}/{BLOG_DIR}/{POST_DIR}");
|
let blog_dir = format!("{data_dir}/{BLOG_DIR}/{POST_DIR}");
|
||||||
let ext = ".md";
|
let ext = ".md";
|
||||||
|
|
||||||
*post = read_file(format!("{blog_dir}/{filename}{ext}"), MType::Blog);
|
*post = read_file(
|
||||||
|
FilePath {
|
||||||
|
base: blog_dir,
|
||||||
|
path: format!("{filename}{ext}"),
|
||||||
|
},
|
||||||
|
MType::Blog,
|
||||||
|
);
|
||||||
|
|
||||||
let default = (
|
let default = (
|
||||||
filename,
|
filename,
|
||||||
|
|
|
@ -2,7 +2,11 @@ use cached::proc_macro::once;
|
||||||
use glob::glob;
|
use glob::glob;
|
||||||
use std::fs::read_to_string;
|
use std::fs::read_to_string;
|
||||||
|
|
||||||
use crate::utils::{markdown::File, metadata::MType, misc::read_file};
|
use crate::utils::{
|
||||||
|
markdown::{File, FilePath},
|
||||||
|
metadata::MType,
|
||||||
|
misc::read_file,
|
||||||
|
};
|
||||||
|
|
||||||
/// Contact node
|
/// Contact node
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -50,10 +54,10 @@ pub fn remove_paragraphs(list: &mut [File]) {
|
||||||
.for_each(|file| file.content = file.content.replace("<p>", "").replace("</p>", ""));
|
.for_each(|file| file.content = file.content.replace("<p>", "").replace("</p>", ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read(path: &str) -> Vec<File> {
|
pub fn read(path: FilePath) -> Vec<File> {
|
||||||
glob(path)
|
glob(&path.to_string())
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.map(|e| read_file(e.unwrap().to_string_lossy().to_string(), MType::Contact).unwrap())
|
.map(|e| read_file(path.from(&e.unwrap().to_string_lossy()), MType::Contact).unwrap())
|
||||||
.filter(|f| {
|
.filter(|f| {
|
||||||
!f.metadata
|
!f.metadata
|
||||||
.info
|
.info
|
||||||
|
|
Loading…
Reference in a new issue