use std::fs; use std::path; use serde::Deserialize; use serde::Deserializer; pub fn register<'a>(pool: &'a sqlx::Pool) { let p = pool.clone(); tokio::spawn(async move { import_posts("/app", &p).await; }); } async fn import_posts(dir_path: &str, pool: &sqlx::Pool) { println!("hello from import_posts"); let entries = fs::read_dir(dir_path).unwrap(); let options = MarkdownOptions { options: markdown::Constructs::gfm(), }; for f in entries { let file = f.unwrap(); let file_path = file.path(); if file_path.is_file() { let file_name = file.file_name(); let file_name_final = &file_name.to_str().unwrap(); let exists = sqlx::query_as::<_, FilenameExists>( "SELECT EXISTS(SELECT 1 FROM posts WHERE filename = $1)", ) .bind(file_name_final) .fetch_one(pool) .await .unwrap() .filename; if !exists.is_empty() { println!( "File does not exist! Inserting: {:?}", file_path.file_name() ); let file_md_contents = process_read_file(file_path, &options); let content = markdown::to_html(&file_md_contents); let metadata = crate::utils::front_matter::YamlFrontMatter::parse::(&content).unwrap(); let title = metadata.metadata.title; sqlx::query_as::<_, InsertPosts>( "INSERT INTO posts (title, body, filename, author_id) VALUES ($1, $2, $3, $4) RETURNING (title, body, filename, author_id)", ) .bind(title) .bind(content) .bind(file_name_final) .bind(1) .fetch_one(pool) .await .unwrap(); } } } } fn process_read_file>(path: P, md_opts: &MarkdownOptions) -> String { let file_contents = fs::read_to_string(path).unwrap(); markdown::to_html(file_contents.as_str()) } #[derive(Debug, sqlx::FromRow)] struct FilenameExists { filename: String, } #[derive(Debug, sqlx::FromRow)] struct InsertPosts { title: String, body: String, filename: String, author_id: i32, } struct MarkdownOptions { options: markdown::Constructs, } #[derive(Deserialize)] struct MarkdownMetadata { layout: String, title: String, #[serde(deserialize_with = "deserialize_datetime")] date: chrono::DateTime, published: bool, } fn deserialize_datetime<'de, D>(deserializer: D) -> Result, D::Error> where D: serde::Deserializer<'de>, { let s = String::deserialize(deserializer)?; chrono::DateTime::parse_from_rfc3339(&s) .map(|dt| dt.with_timezone(&chrono::Utc)) .map_err(serde::de::Error::custom) }