refactor: restructure project into a workspace by introducing a plugin API crate and modularizing individual plugin definitions.
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
[package]
|
||||
name = "convertis-html"
|
||||
build = "../../plugin-build.rs"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
base64.workspace = true
|
||||
convertis-plugin-api.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile.workspace = true
|
||||
@@ -0,0 +1,178 @@
|
||||
// Copyright (C) 2026 Elias Wendland <eliaswendland@pm.me>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 3 exclusively.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use base64::{Engine, engine::general_purpose::STANDARD};
|
||||
use convertis_plugin_api::{Conversion, ConversionRequest, Plugin, PluginMetadata};
|
||||
|
||||
const INPUTS: &[&str] = &[
|
||||
"png",
|
||||
"jpeg",
|
||||
"gif",
|
||||
"animated-gif",
|
||||
"webp",
|
||||
"animated-webp",
|
||||
"apng",
|
||||
"bmp",
|
||||
"tiff",
|
||||
"ico",
|
||||
"avif",
|
||||
"heic",
|
||||
"jxl",
|
||||
"svg",
|
||||
"pdf",
|
||||
"mp4",
|
||||
"webm",
|
||||
"mkv",
|
||||
"avi",
|
||||
"mov",
|
||||
"mpeg",
|
||||
"ogv",
|
||||
"mp3",
|
||||
"ogg",
|
||||
"aac",
|
||||
"m4a",
|
||||
"opus",
|
||||
"wma",
|
||||
"wav",
|
||||
"flac",
|
||||
"aiff",
|
||||
"au",
|
||||
"text",
|
||||
];
|
||||
struct Html;
|
||||
|
||||
fn escape(value: &str) -> String {
|
||||
value
|
||||
.chars()
|
||||
.map(|character| match character {
|
||||
'&' => "&".to_owned(),
|
||||
'<' => "<".to_owned(),
|
||||
'>' => ">".to_owned(),
|
||||
'"' => """.to_owned(),
|
||||
'\'' => "'".to_owned(),
|
||||
other => other.to_string(),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn mime(format: &str) -> &'static str {
|
||||
match format {
|
||||
"png" | "apng" => "image/png",
|
||||
"jpeg" => "image/jpeg",
|
||||
"gif" | "animated-gif" => "image/gif",
|
||||
"webp" | "animated-webp" => "image/webp",
|
||||
"bmp" => "image/bmp",
|
||||
"tiff" => "image/tiff",
|
||||
"ico" => "image/x-icon",
|
||||
"avif" => "image/avif",
|
||||
"heic" => "image/heic",
|
||||
"jxl" => "image/jxl",
|
||||
"svg" => "image/svg+xml",
|
||||
"mp4" => "video/mp4",
|
||||
"webm" => "video/webm",
|
||||
"ogv" => "video/ogg",
|
||||
"mp3" => "audio/mpeg",
|
||||
"ogg" => "audio/ogg",
|
||||
"wav" => "audio/wav",
|
||||
"flac" => "audio/flac",
|
||||
"pdf" => "application/pdf",
|
||||
_ => "application/octet-stream",
|
||||
}
|
||||
}
|
||||
|
||||
impl Plugin for Html {
|
||||
fn metadata(&self) -> PluginMetadata {
|
||||
PluginMetadata {
|
||||
id: "html".into(),
|
||||
package: "convertis-html".into(),
|
||||
description: "Create self-contained HTML".into(),
|
||||
conversions: INPUTS
|
||||
.iter()
|
||||
.map(|from| Conversion::file(from, "html", (255, 255, 255)))
|
||||
.collect(),
|
||||
options: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
fn convert(&self, request: &ConversionRequest) -> Result<(), String> {
|
||||
let body = if request.from == "text" {
|
||||
let text =
|
||||
std::fs::read_to_string(&request.input).map_err(|error| error.to_string())?;
|
||||
format!("<p style=\"white-space:pre-wrap\">{}</p>", escape(&text))
|
||||
} else {
|
||||
let data =
|
||||
STANDARD.encode(std::fs::read(&request.input).map_err(|error| error.to_string())?);
|
||||
let uri = format!("data:{};base64,{}", mime(&request.from), data);
|
||||
if matches!(
|
||||
request.from.as_str(),
|
||||
"png"
|
||||
| "apng"
|
||||
| "jpeg"
|
||||
| "gif"
|
||||
| "animated-gif"
|
||||
| "webp"
|
||||
| "animated-webp"
|
||||
| "bmp"
|
||||
| "tiff"
|
||||
| "ico"
|
||||
| "avif"
|
||||
| "heic"
|
||||
| "jxl"
|
||||
| "svg"
|
||||
) {
|
||||
format!("<img alt=\"Embedded media\" src=\"{uri}\">")
|
||||
} else if matches!(
|
||||
request.from.as_str(),
|
||||
"mp3" | "ogg" | "aac" | "m4a" | "opus" | "wma" | "wav" | "flac" | "aiff" | "au"
|
||||
) {
|
||||
format!("<audio controls src=\"{uri}\"></audio>")
|
||||
} else if request.from == "pdf" {
|
||||
format!("<embed type=\"application/pdf\" src=\"{uri}\">")
|
||||
} else {
|
||||
format!("<video controls src=\"{uri}\"></video>")
|
||||
}
|
||||
};
|
||||
let document = format!(
|
||||
"<!doctype html>\n<html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width\"><title>Converted media</title></head><body>{body}</body></html>\n"
|
||||
);
|
||||
std::fs::write(&request.output, document).map_err(|error| error.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
convertis_plugin_api::export_plugin!(Html, "html");
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
#[test]
|
||||
fn text_is_escaped_inside_a_paragraph() {
|
||||
let directory = tempfile::tempdir().unwrap();
|
||||
let input = directory.path().join("input.txt");
|
||||
let output = directory.path().join("output.html");
|
||||
std::fs::write(&input, "<script>alert('x')</script>").unwrap();
|
||||
Html.convert(&ConversionRequest {
|
||||
input,
|
||||
output: output.clone(),
|
||||
from: "text".into(),
|
||||
to: "html".into(),
|
||||
options: BTreeMap::new(),
|
||||
})
|
||||
.unwrap();
|
||||
let html = std::fs::read_to_string(output).unwrap();
|
||||
assert!(html.contains("<script>alert('x')</script>"));
|
||||
assert!(!html.contains("<script>"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user