Найти тему
Один Rust не п...Rust

SSG на Rust

Оглавление

Для чего нужна данная статья? :

Найти альтернативы реализации SSG, сочетающий AI-контент, анализ данных и обработку изображений с ML.

1. Создание блогов

  • Zola: Поддерживает Markdown, имеет встроенные шаблоны и удобен для создания персональных блогов или небольших сайтов.
  • Cobalt: Поддерживает Markdown и Liquid-шаблонов. Хорошо подходит для ведения блога.

2. Портфолио и персональные сайты

  • Gutenberg (Zola): Подходит для создания портфолио или персональных сайтов с поддержкой различных типов контента.
  • Copper: Для создания легковесных персональных сайтов.

3. Документация

  • mdBook: Для создания книг и документации. Он поддерживает Markdown и имеет функции генерации структуры и навигации по книге.
  • Yewprint: Инструмент на базе Yew для создания документации и стилей компонентов.

4. Корпоративные сайты

  • Tera: Движок шаблонов, который часто используется вместе с Rust для генерации статических сайтов с более сложной логикой.
  • Trillium: Может использоваться для генерации статического контента, особенно если требуется более сложная логика.

5. Одностраничные сайты (Landing Pages)

  • Sycamore: Для создания одностраничных приложений с поддержкой серверного рендеринга, что позволяет использовать его как SSG.
  • Percy: Для создания одностраничных сайтов.

6. Инструменты для генерации HTML из Markdown или других форматов

  • miniserve: Для показа статического контента, который можно использовать вместе с генераторами статических сайтов.
  • cargo-generate: Для генерации шаблонов и структур проектов, включая статические сайты.

Зачем Вам это уметь? :

Написать Static Site Generators (SSG) на Rust с высокой производительностью используя :

pulldown-cmark: Парсер Markdown.

tera: Шаблонизатор для HTML.

walkdir: Обходит директории для поиска файлов..

Создайте новый проект на Rust:

cargo new my_ssg

cd my_ssg
Добавьте необходимые зависимости в Cargo.toml:

[dependencies]

pulldown-cmark = "0.9"

tera = "1.12.0"

walkdir = "2.3.2"

Добавьте следующий код в src/main.rs:

use pulldown_cmark::{Parser, Options, html};

use tera::{Tera, Context};

use std::fs::{self, File};

use std::io::{self, Write};

use std::path::Path;

use walkdir::WalkDir;

fn main() -> io::Result<()> {

let tera = Tera::new("templates/**/*")?;

for entry in WalkDir::new("content") {

let entry = entry?;

if entry.path().extension().and_then(|e| e.to_str()) == Some("md") {

let markdown_input = fs::read_to_string(entry.path())?;

let html_output = markdown_to_html(&markdown_input);

let file_stem = entry.path().file_stem().unwrap().to_str().unwrap();

let mut context = Context::new();

context.insert("content", &html_output);

let rendered_html = tera.render("page.html", &context)?;

let output_path = format!("public/{}.html", file_stem);

let mut output_file = File::create(output_path)?;
output_file.write_all(rendered_html.as_bytes())?;

}

}

Ok(())

}
fn markdown_to_html(markdown_input: &str) ->

String {

let mut options = Options::empty();

options.insert(Options::ENABLE_STRIKETHROUGH);

let parser = Parser::new_ext(markdown_input, options);

let mut html_output = String::new();

html::push_html(&mut html_output, parser);

html_output

}

Создайте следующие директории и файлы:

mkdir content

mkdir templates

mkdir public

Пример контента в content/index.md:

# Welcome

This is Rust!

Пример шаблона в templates/page.html:

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<title>{{ title | default(value="My Site") }}</title>

</head>

<body>

{{ content | safe }}

</body>

</html>

Соберите и запустите проект:

После выполнения команды cargo run, откройте сгенерированный HTML файл (public/index.html) в браузере.

DeepSeek AI с Torch (tch-rs) для анализа текста и imageproc с ndarray для оптимизации изображений

use tch::{CModule, Tensor};

use image::{DynamicImage, GenericImageView, imageops::FilterType};

use std::fs;

use std::path::Path;

use reqwest::Client;

use serde_json::json;

use tokio::task;

use rayon::prelude::*;

// Генерация контента через DeepSeek AI

async fn generate_content(prompt: &str) -> String {

let client = Client::new();

let response = client.post("https://api.deepseek.com/generate")

.json(&json!({"prompt": prompt, "max_length": 500}))

.send().await.unwrap()

.text().await.unwrap();

response

}

// Анализ контента с использованием ML-модели (Torch)

fn analyze_content(model: &CModule, text: &str) -> Tensor {

let input_tensor = Tensor::of_slice(&text.bytes().map(|b| b as f32).collect::<Vec<f32>>());

model.forward_ts(&[input_tensor]).unwrap()

}

// Оптимизация изображения с многопоточностью

fn optimize_image(path: &str) -> DynamicImage {

let img = image::open(path).unwrap();

let (width, height) = img.dimensions();

img.resize_exact(width / 2, height / 2, FilterType::CatmullRom)

}

// Параллельная обработка изображений

fn process_images(input_dir: &str, output_dir: &str) {

let paths: Vec<_> = fs::read_dir(input_dir).unwrap()

.filter_map(Result::ok)

.map(|entry| entry.path())

.collect();

paths.par_iter().for_each(|path| {

if let Some(ext) = path.extension() {

if ext == "jpg" || ext == "png" {

let optimized = optimize_image(path.to_str().unwrap());

let output_path = Path::new(output_dir).join(path.file_name().unwrap());

optimized.save(output_path).unwrap();

}

}

});

}

// Генерация статической страницы с шаблонизацией

fn generate_page(title: &str, content: &str, category: &str) -> String {

format!(

"<html><head><title>{}</title></head><body><h1>{}</h1><p>{}</p></body></html>",

title, category, content

)

}

#[tokio::main]

async fn main() {

let model = CModule::load("content_model.pt").unwrap();

let prompt = "SEO-оптимизированный текст о Rust";

let content_task = task::spawn(async move { generate_content(prompt).await });

let content = content_task.await.unwrap();

let category = analyze_content(&model, &content);

let category_str = format!("Категория: {:.2}", category.double_value(&[]));

let html = generate_page("Rust Blog", &content, &category_str);

fs::write("output/index.html", html).unwrap();

process_images("input", "output");

}