mirror of https://github.com/dpc/tagwiki
A lot of stuff working
This commit is contained in:
parent
1f899adee4
commit
9833122928
|
@ -1,14 +1,15 @@
|
||||||
* {
|
body {
|
||||||
-webkit-box-sizing: border-box;
|
max-width: 40em;
|
||||||
-moz-box-sizing: border-box;
|
margin: 0 auto;
|
||||||
box-sizing: border-box;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
.button-warning {
|
||||||
background-color: #baa4a4;
|
color: white;
|
||||||
height: 100%;
|
background: rgb(223, 117, 20);
|
||||||
|
}
|
||||||
|
.button-green {
|
||||||
|
color: white;
|
||||||
|
background: rgb(28, 184, 65);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TOOD: make the whole form exactly fit the screen (including the buttons) */
|
/* TOOD: make the whole form exactly fit the screen (including the buttons) */
|
||||||
|
|
|
@ -8,7 +8,6 @@ use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Index<T> {
|
pub struct Index<T> {
|
||||||
// tag -> page_ids
|
|
||||||
page_ids_by_tag: HashMap<String, HashSet<Id>>,
|
page_ids_by_tag: HashMap<String, HashSet<Id>>,
|
||||||
tags_by_page_id: HashMap<Id, Vec<Tag>>,
|
tags_by_page_id: HashMap<Id, Vec<Tag>>,
|
||||||
title_by_page_id: HashMap<Id, String>,
|
title_by_page_id: HashMap<Id, String>,
|
||||||
|
@ -135,11 +134,11 @@ impl<T> Index<T> {
|
||||||
.entry(tag.clone())
|
.entry(tag.clone())
|
||||||
.or_default()
|
.or_default()
|
||||||
.insert(page.id().to_owned());
|
.insert(page.id().to_owned());
|
||||||
self.tags_by_page_id
|
|
||||||
.insert(page.id().to_owned(), page.tags.clone());
|
|
||||||
self.title_by_page_id
|
|
||||||
.insert(page.id().to_owned(), page.title.clone());
|
|
||||||
}
|
}
|
||||||
|
self.tags_by_page_id
|
||||||
|
.insert(page.id().to_owned(), page.tags.clone());
|
||||||
|
self.title_by_page_id
|
||||||
|
.insert(page.id().to_owned(), page.title.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clean_data_for_page(&mut self, id: Id) {
|
fn clean_data_for_page(&mut self, id: Id) {
|
||||||
|
|
225
src/main.rs
225
src/main.rs
|
@ -1,6 +1,6 @@
|
||||||
//! tagwiki
|
//! tagwiki
|
||||||
|
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, format_err, Result};
|
||||||
use log::info;
|
use log::info;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
@ -21,8 +21,8 @@ mod index;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
use horrorshow::helper::doctype;
|
use horrorshow::helper::doctype;
|
||||||
use horrorshow::owned_html;
|
|
||||||
use horrorshow::prelude::*;
|
use horrorshow::prelude::*;
|
||||||
|
use horrorshow::{box_html, owned_html};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct RejectAnyhow(anyhow::Error);
|
struct RejectAnyhow(anyhow::Error);
|
||||||
|
@ -68,44 +68,89 @@ struct GetParams {
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
struct PostForm {
|
struct PostForm {
|
||||||
body: String,
|
body: Option<String>,
|
||||||
id: Option<String>,
|
id: Option<String>,
|
||||||
|
_method: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_html_page(page: impl RenderOnce) -> impl RenderOnce {
|
impl PostForm {
|
||||||
owned_html! {
|
fn get_body(&self) -> Result<&str> {
|
||||||
: doctype::HTML;
|
self.body
|
||||||
head {
|
.as_deref()
|
||||||
link(rel="stylesheet", media="all", href="/_style.css");
|
.ok_or_else(|| format_err!("Missing body"))
|
||||||
}
|
|
||||||
body : page;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_page_editing_view(page: &page::Parsed) -> impl RenderOnce {
|
fn render_html_page(body: impl RenderOnce) -> impl RenderOnce {
|
||||||
let body = page.source_body.clone();
|
|
||||||
let id = page.id().to_owned();
|
|
||||||
owned_html! {
|
owned_html! {
|
||||||
form(action=".", method="post") {
|
: doctype::HTML;
|
||||||
input(type="submit", value="Save");
|
head {
|
||||||
input(type="hidden", name="id", value=id);
|
link(rel="stylesheet",href="https://unpkg.com/purecss@2.0.1/build/pure-min.css",crossorigin="anonymous");
|
||||||
textarea(name="body") {
|
link(rel="stylesheet",href="https://unpkg.com/purecss@2.0.1/build/grids-responsive-min.css");
|
||||||
: body
|
meta(name="viewport",content="width=device-width, initial-scale=1");
|
||||||
|
link(rel="stylesheet", media="all", href="/_style.css");
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
: body
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_page_editing_view(page: Option<&page::Parsed>) -> impl RenderOnce {
|
||||||
|
if let Some(page) = page.as_ref() {
|
||||||
|
let body = page.source_body.clone();
|
||||||
|
let id = page.id().to_owned();
|
||||||
|
(box_html! {
|
||||||
|
form(action=".", method="post") {
|
||||||
|
input(type="submit", value="Save", class="pure-button pure-button-primary");
|
||||||
|
input(type="hidden", name="id", value=id);
|
||||||
|
textarea(name="body") {
|
||||||
|
: body
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}) as Box<dyn RenderBox>
|
||||||
|
} else {
|
||||||
|
box_html! {
|
||||||
|
form(action=".", method="post") {
|
||||||
|
input(type="submit", value="Save", class="pure-button pure-button-primary");
|
||||||
|
input(type="hidden", name="_method", value="put");
|
||||||
|
textarea(name="body");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_page_view(page: &page::Parsed) -> impl RenderOnce {
|
fn render_page_view(page: &page::Parsed) -> impl RenderOnce {
|
||||||
let page_html = page.html.clone();
|
let page_html = page.html.clone();
|
||||||
let id = page.id().to_owned();
|
let id = page.id().to_owned();
|
||||||
|
let id_copy = id.clone();
|
||||||
owned_html! {
|
owned_html! {
|
||||||
form(action=".", method="get") {
|
div(class="pure-menu pure-menu-horizontal") {
|
||||||
input(type="hidden", name="edit", value="true");
|
form(action="..", method="get", class="pure-menu-item") {
|
||||||
input(type="hidden", name="id", value=id);
|
button(type="submit", class="pure-button"){
|
||||||
button(type="submit"){
|
: "Up"
|
||||||
: "Edit"
|
}
|
||||||
|
}
|
||||||
|
form(action="/", method="get", class="pure-menu-item") {
|
||||||
|
input(type="hidden", name="edit", value="true");
|
||||||
|
button(type="submit", class="pure-button button-green"){
|
||||||
|
: "New"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
form(action=".", method="get", class="pure-menu-item") {
|
||||||
|
input(type="hidden", name="edit", value="true");
|
||||||
|
input(type="hidden", name="id", value=id);
|
||||||
|
button(type="submit", class="pure-button pure-button-primary"){
|
||||||
|
: "Edit"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
form(action=".", method="post", class="pure-menu-item") {
|
||||||
|
input(type="hidden", name="edit", value="true");
|
||||||
|
input(type="hidden", name="id", value=id_copy);
|
||||||
|
input(type="hidden", name="_method", value="delete");
|
||||||
|
button(type="submit", class="pure-button button-warning",onclick="return confirm('Are you sure?');"){
|
||||||
|
: "Delete"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
: Raw(page_html)
|
: Raw(page_html)
|
||||||
|
@ -114,6 +159,19 @@ fn render_page_view(page: &page::Parsed) -> impl RenderOnce {
|
||||||
|
|
||||||
fn render_post_list(posts: impl Iterator<Item = index::PageInfo> + 'static) -> impl RenderOnce {
|
fn render_post_list(posts: impl Iterator<Item = index::PageInfo> + 'static) -> impl RenderOnce {
|
||||||
owned_html! {
|
owned_html! {
|
||||||
|
div(class="pure-menu pure-menu-horizontal") {
|
||||||
|
form(action="..", method="get", class="pure-menu-item") {
|
||||||
|
button(type="submit", class="pure-button"){
|
||||||
|
: "Up"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
form(action="/", method="get", class="pure-menu-item") {
|
||||||
|
input(type="hidden", name="edit", value="true");
|
||||||
|
button(type="submit", class="pure-button button-green"){
|
||||||
|
: "New"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
ul {
|
ul {
|
||||||
@ for post in posts {
|
@ for post in posts {
|
||||||
li {
|
li {
|
||||||
|
@ -143,8 +201,8 @@ async fn handle_style_css() -> std::result::Result<warp::http::Response<String>,
|
||||||
.status(200)
|
.status(200)
|
||||||
.header(warp::http::header::CONTENT_TYPE, "text/css")
|
.header(warp::http::header::CONTENT_TYPE, "text/css")
|
||||||
.body(
|
.body(
|
||||||
include_str!("../resources/reset.css").to_string()
|
// include_str!("../resources/reset.css").to_string()
|
||||||
+ include_str!("../resources/style.css"),
|
include_str!("../resources/style.css").to_string(),
|
||||||
)
|
)
|
||||||
.expect("correct redirect"))
|
.expect("correct redirect"))
|
||||||
}
|
}
|
||||||
|
@ -154,9 +212,20 @@ async fn handle_post_wrapped(
|
||||||
path: FullPath,
|
path: FullPath,
|
||||||
form: PostForm,
|
form: PostForm,
|
||||||
) -> Result<Box<dyn warp::Reply>, warp::Rejection> {
|
) -> Result<Box<dyn warp::Reply>, warp::Rejection> {
|
||||||
handle_post(state, path, form)
|
if let Some("put") = form._method.as_deref() {
|
||||||
.await
|
// workaround for not being able to use `method="put"` in html forms
|
||||||
.map_err(|e| warp::reject::custom(RejectAnyhow(e)))
|
handle_put(state, path, form)
|
||||||
|
.await
|
||||||
|
.map_err(|e| warp::reject::custom(RejectAnyhow(e)))
|
||||||
|
} else if let Some("delete") = form._method.as_deref() {
|
||||||
|
handle_delete(state, path, form)
|
||||||
|
.await
|
||||||
|
.map_err(|e| warp::reject::custom(RejectAnyhow(e)))
|
||||||
|
} else {
|
||||||
|
handle_post(state, path, form)
|
||||||
|
.await
|
||||||
|
.map_err(|e| warp::reject::custom(RejectAnyhow(e)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_post(
|
async fn handle_post(
|
||||||
|
@ -167,8 +236,8 @@ async fn handle_post(
|
||||||
let tags = path_to_tags(&path);
|
let tags = path_to_tags(&path);
|
||||||
let mut write = state.page_store.write().await;
|
let mut write = state.page_store.write().await;
|
||||||
|
|
||||||
let post_id = if let Some(id) = form.id {
|
let post_id = if let Some(id) = form.id.as_deref() {
|
||||||
id
|
id.to_owned()
|
||||||
} else {
|
} else {
|
||||||
let results = write.find(tags.as_slice());
|
let results = write.find(tags.as_slice());
|
||||||
match results.matching_pages.len() {
|
match results.matching_pages.len() {
|
||||||
|
@ -177,9 +246,9 @@ async fn handle_post(
|
||||||
_ => return Ok(Box::new(warp_temporary_redirect_to_get_method(".".into()))),
|
_ => return Ok(Box::new(warp_temporary_redirect_to_get_method(".".into()))),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let page = write.get(post_id.clone()).await?;
|
let page = write.get(post_id.to_owned()).await?;
|
||||||
|
|
||||||
let page = page.with_new_source_body(&get_rid_of_windows_newlines(form.body));
|
let page = page.with_new_source_body(&get_rid_of_windows_newlines(form.get_body()?.to_owned()));
|
||||||
|
|
||||||
write.put(&page).await?;
|
write.put(&page).await?;
|
||||||
|
|
||||||
|
@ -187,29 +256,55 @@ async fn handle_post(
|
||||||
"?id={}",
|
"?id={}",
|
||||||
post_id
|
post_id
|
||||||
))))
|
))))
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
async fn handle_put_wrapped(
|
||||||
match results.matching_pages.len() {
|
state: Arc<State>,
|
||||||
1 => {
|
path: FullPath,
|
||||||
let page = write
|
form: PostForm,
|
||||||
.get(results.matching_pages[0].id.clone())
|
) -> Result<Box<dyn warp::Reply>, warp::Rejection> {
|
||||||
.await
|
handle_put(state, path, form)
|
||||||
.map_err(|e| warp::reject::custom(RejectAnyhow(e)))?;
|
.await
|
||||||
|
.map_err(|e| warp::reject::custom(RejectAnyhow(e)))
|
||||||
|
}
|
||||||
|
async fn handle_put(
|
||||||
|
state: Arc<State>,
|
||||||
|
_path: FullPath,
|
||||||
|
form: PostForm,
|
||||||
|
) -> Result<Box<dyn warp::Reply>> {
|
||||||
|
let page = page::Parsed::new(&get_rid_of_windows_newlines(form.get_body()?.to_owned()));
|
||||||
|
let mut write = state.page_store.write().await;
|
||||||
|
write.put(&page).await?;
|
||||||
|
|
||||||
let page = page.with_new_source_body(&get_rid_of_windows_newlines(form.body));
|
Ok(Box::new(warp_temporary_redirect_to_get_method(&format!(
|
||||||
|
"?id={}",
|
||||||
|
page.id()
|
||||||
|
))))
|
||||||
|
}
|
||||||
|
|
||||||
write
|
async fn handle_delete_wrapped(
|
||||||
.put(&page)
|
state: Arc<State>,
|
||||||
.await
|
path: FullPath,
|
||||||
.map_err(|e| warp::reject::custom(RejectAnyhow(e)))?;
|
form: PostForm,
|
||||||
|
) -> Result<Box<dyn warp::Reply>, warp::Rejection> {
|
||||||
|
handle_delete(state, path, form)
|
||||||
|
.await
|
||||||
|
.map_err(|e| warp::reject::custom(RejectAnyhow(e)))
|
||||||
|
}
|
||||||
|
async fn handle_delete(
|
||||||
|
state: Arc<State>,
|
||||||
|
_path: FullPath,
|
||||||
|
query: PostForm,
|
||||||
|
) -> Result<Box<dyn warp::Reply>> {
|
||||||
|
let mut write = state.page_store.write().await;
|
||||||
|
let page = write
|
||||||
|
.get(query.id.ok_or_else(|| format_err!("Missing ID"))?)
|
||||||
|
.await?;
|
||||||
|
write.delete(page.id().to_owned()).await?;
|
||||||
|
|
||||||
Ok(Box::new(warp_temporary_redirect_after_post(".".into())))
|
Ok(Box::new(warp_temporary_redirect_to_get_method(&format!(
|
||||||
}
|
".",
|
||||||
_ => {
|
))))
|
||||||
// TODO: ERROR
|
|
||||||
Ok(Box::new(format!("Results: {:?}", results)))
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// I wish this could be generic
|
// I wish this could be generic
|
||||||
|
@ -223,11 +318,11 @@ async fn handle_get_wrapped(
|
||||||
.map_err(|e| warp::reject::custom(RejectAnyhow(e)))
|
.map_err(|e| warp::reject::custom(RejectAnyhow(e)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_page(page: &page::Parsed, edit: bool) -> Box<dyn RenderBox> {
|
fn render_page(page: Option<&page::Parsed>, edit: bool) -> Box<dyn RenderBox> {
|
||||||
if edit {
|
if edit {
|
||||||
Box::new(render_page_editing_view(page)) as Box<dyn RenderBox>
|
Box::new(render_page_editing_view(page)) as Box<dyn RenderBox>
|
||||||
} else {
|
} else {
|
||||||
Box::new(render_page_view(page)) as Box<dyn RenderBox>
|
Box::new(render_page_view(page.expect("always some"))) as Box<dyn RenderBox>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,9 +337,13 @@ async fn handle_get(
|
||||||
if let Some(q_id) = query.id {
|
if let Some(q_id) = query.id {
|
||||||
let page = read.get(q_id).await?;
|
let page = read.get(q_id).await?;
|
||||||
return Ok(warp_reply_from_render(render_html_page(render_page(
|
return Ok(warp_reply_from_render(render_html_page(render_page(
|
||||||
&page,
|
Some(&page),
|
||||||
query.edit.is_some(),
|
query.edit.is_some(),
|
||||||
))));
|
))));
|
||||||
|
} else if query.edit.is_some() {
|
||||||
|
return Ok(warp_reply_from_render(render_html_page(render_page(
|
||||||
|
None, true,
|
||||||
|
))));
|
||||||
}
|
}
|
||||||
let results = read.find(tags.as_slice());
|
let results = read.find(tags.as_slice());
|
||||||
if results.matching_tags != tags {
|
if results.matching_tags != tags {
|
||||||
|
@ -255,7 +354,7 @@ async fn handle_get(
|
||||||
if results.matching_pages.len() == 1 {
|
if results.matching_pages.len() == 1 {
|
||||||
let page = read.get(results.matching_pages[0].id.clone()).await?;
|
let page = read.get(results.matching_pages[0].id.clone()).await?;
|
||||||
Ok(warp_reply_from_render(render_html_page(render_page(
|
Ok(warp_reply_from_render(render_html_page(render_page(
|
||||||
&page,
|
Some(&page),
|
||||||
query.edit.is_some(),
|
query.edit.is_some(),
|
||||||
))))
|
))))
|
||||||
} else {
|
} else {
|
||||||
|
@ -280,11 +379,21 @@ async fn start(opts: &cli::Opts) -> Result<()> {
|
||||||
.and(warp::query::<GetParams>())
|
.and(warp::query::<GetParams>())
|
||||||
.and(warp::get())
|
.and(warp::get())
|
||||||
.and_then(handle_get_wrapped))
|
.and_then(handle_get_wrapped))
|
||||||
.or(with_state(state)
|
.or(with_state(state.clone())
|
||||||
.and(warp::path::full())
|
.and(warp::path::full())
|
||||||
.and(warp::post())
|
.and(warp::post())
|
||||||
.and(warp::filters::body::form())
|
.and(warp::filters::body::form())
|
||||||
.and_then(handle_post_wrapped));
|
.and_then(handle_post_wrapped))
|
||||||
|
.or(with_state(state.clone())
|
||||||
|
.and(warp::path::full())
|
||||||
|
.and(warp::delete())
|
||||||
|
.and(warp::filters::body::form())
|
||||||
|
.and_then(handle_delete_wrapped))
|
||||||
|
.or(with_state(state)
|
||||||
|
.and(warp::path::full())
|
||||||
|
.and(warp::put())
|
||||||
|
.and(warp::filters::body::form())
|
||||||
|
.and_then(handle_put_wrapped));
|
||||||
info!("Listening on port {}", opts.port);
|
info!("Listening on port {}", opts.port);
|
||||||
let _serve = warp::serve(handler).run(([127, 0, 0, 1], opts.port)).await;
|
let _serve = warp::serve(handler).run(([127, 0, 0, 1], opts.port)).await;
|
||||||
|
|
||||||
|
|
31
src/page.rs
31
src/page.rs
|
@ -113,12 +113,38 @@ fn parse_tags(body: &str) -> Vec<String> {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_title(body: &str) -> String {
|
||||||
|
lazy_static! {
|
||||||
|
static ref RE: regex::Regex =
|
||||||
|
regex::Regex::new(r"#+[[:space:]]+(.*)").expect("correct regex");
|
||||||
|
}
|
||||||
|
|
||||||
|
let title = RE
|
||||||
|
.captures_iter(&body)
|
||||||
|
.map(|m| m.get(1).expect("a value").as_str().trim().to_string())
|
||||||
|
.next()
|
||||||
|
.unwrap_or_else(|| "".to_string());
|
||||||
|
if title == "" {
|
||||||
|
"Untitled".to_string()
|
||||||
|
} else {
|
||||||
|
title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Parsed {
|
impl Parsed {
|
||||||
pub fn id(&self) -> IdRef {
|
pub fn id(&self) -> IdRef {
|
||||||
self.headers.id.as_str()
|
self.headers.id.as_str()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_full_source(source: Source) -> Parsed {
|
pub fn new(body: &str) -> Parsed {
|
||||||
|
let headers = Headers {
|
||||||
|
id: crate::util::random_string(16),
|
||||||
|
..Headers::default()
|
||||||
|
};
|
||||||
|
Self::from_headers_and_body(headers, body.to_owned())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_full_source(source: Source) -> Parsed {
|
||||||
let (headers, body) = split_headers_and_body(&source);
|
let (headers, body) = split_headers_and_body(&source);
|
||||||
let headers = Headers::parse(headers, &source);
|
let headers = Headers::parse(headers, &source);
|
||||||
|
|
||||||
|
@ -128,6 +154,7 @@ impl Parsed {
|
||||||
fn from_headers_and_body(headers: Headers, body: String) -> Parsed {
|
fn from_headers_and_body(headers: Headers, body: String) -> Parsed {
|
||||||
let source = headers.to_markdown_string() + &body;
|
let source = headers.to_markdown_string() + &body;
|
||||||
let parser = pulldown_cmark::Parser::new(&body);
|
let parser = pulldown_cmark::Parser::new(&body);
|
||||||
|
let title = parse_title(&body);
|
||||||
let mut html_output = String::new();
|
let mut html_output = String::new();
|
||||||
pulldown_cmark::html::push_html(&mut html_output, parser);
|
pulldown_cmark::html::push_html(&mut html_output, parser);
|
||||||
|
|
||||||
|
@ -139,7 +166,7 @@ impl Parsed {
|
||||||
source_body: body,
|
source_body: body,
|
||||||
source: Source(source),
|
source: Source(source),
|
||||||
tags,
|
tags,
|
||||||
title: "TODO".into(),
|
title,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@ impl FsStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn title_to_new_rel_path(&self, title: &str) -> PathBuf {
|
fn title_to_new_rel_path(&self, title: &str) -> PathBuf {
|
||||||
|
let title = title.trim();
|
||||||
let mut last_char_was_alphanum = false;
|
let mut last_char_was_alphanum = false;
|
||||||
let mut path_str = String::new();
|
let mut path_str = String::new();
|
||||||
for ch in title.chars() {
|
for ch in title.chars() {
|
||||||
|
@ -56,11 +57,11 @@ impl FsStore {
|
||||||
let initial_title = path_str.clone();
|
let initial_title = path_str.clone();
|
||||||
let mut path = PathBuf::from(&initial_title);
|
let mut path = PathBuf::from(&initial_title);
|
||||||
let mut i = 1;
|
let mut i = 1;
|
||||||
while let Some(_) = self.path_to_page.get(&path) {
|
while let Some(_) = self.path_to_page.get(&path.with_extension("md")) {
|
||||||
path = PathBuf::from(format!("{}-{}", &initial_title, i));
|
path = PathBuf::from(format!("{}-{}", &initial_title, i));
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
path
|
path.with_extension("md")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_reading_page_from_entry_res(
|
fn try_reading_page_from_entry_res(
|
||||||
|
@ -102,7 +103,7 @@ impl FsStore {
|
||||||
async fn write_page_to_file(&self, rel_path: &Path, page: &page::Parsed) -> Result<()> {
|
async fn write_page_to_file(&self, rel_path: &Path, page: &page::Parsed) -> Result<()> {
|
||||||
let page = page.clone();
|
let page = page.clone();
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
let path = self.root_path.join(rel_path);
|
let path = self.root_path.join(rel_path).with_extension("md");
|
||||||
let tmp_path = path.with_extension(format!("md.tmp.{}", crate::util::random_string(8)));
|
let tmp_path = path.with_extension(format!("md.tmp.{}", crate::util::random_string(8)));
|
||||||
|
|
||||||
tokio::task::spawn_blocking(move || -> Result<()> {
|
tokio::task::spawn_blocking(move || -> Result<()> {
|
||||||
|
|
Loading…
Reference in New Issue