feat: Use typst as blog format. #24

Open
jackfiled wants to merge 2 commits from feat/typst into master
12 changed files with 3024 additions and 0 deletions
Showing only changes of commit 8964e760ee - Show all commits

View File

@@ -14,6 +14,7 @@
<Folder Name="/src/">
<Project Path="src/YaeBlog.Abstractions/YaeBlog.Abstractions.csproj" />
<Project Path="src/YaeBlog.Tests/YaeBlog.Tests.csproj" />
<Project Path="src/YaeBlog.Typst/YaeBlog.Typst.csproj" />
<Project Path="src/YaeBlog/YaeBlog.csproj" />
</Folder>
<Folder Name="/third-party/" />

View File

@@ -0,0 +1,18 @@
using YaeBlog.Typst;
namespace YaeBlog.Tests;
public class CallRustTests
{
[Fact]
public void ProcessStringTest()
{
RustString test1 = RustCaller.ProcessString("123");
Assert.NotNull(test1.Value);
Assert.Equal("Process 123", test1.Value);
RustString test2 = RustCaller.ProcessString("世界");
Assert.NotNull(test2.Value);
Assert.Equal("Process 世界", test2.Value);
}
}

View File

@@ -24,6 +24,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\YaeBlog.Typst\YaeBlog.Typst.csproj" />
<ProjectReference Include="..\YaeBlog\YaeBlog.csproj" />
</ItemGroup>

View File

@@ -0,0 +1,9 @@
using System.Runtime.InteropServices;
namespace YaeBlog.Typst;
public static partial class RustCaller
{
[LibraryImport("yaeblog_typst", EntryPoint = "process_string", StringMarshalling = StringMarshalling.Utf8)]
public static partial RustString ProcessString(string value);
}

View File

@@ -0,0 +1,27 @@
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
namespace YaeBlog.Typst;
[NativeMarshalling(typeof(RustStringMarshaller))]
public struct RustString
{
public string? Value;
}
/// <summary>
/// Customize string marshaller used to handle `CString` returned value in Rust side.
/// </summary>
[CustomMarshaller(typeof(RustString), MarshalMode.ManagedToUnmanagedOut, typeof(RustStringMarshaller))]
internal static unsafe partial class RustStringMarshaller
{
public static RustString ConvertToManaged(byte* ptr)
{
return new RustString { Value = Utf8StringMarshaller.ConvertToManaged(ptr) };
}
[LibraryImport("yaeblog_typst", EntryPoint = "free_rust_string")]
private static partial void FreeRustString(byte* ptr);
public static void Free(byte* ptr) => FreeRustString(ptr);
}

View File

@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup>
<RustProjectPath>$([System.IO.Path]::Combine($(MSBuildProjectDirectory), `..`, `yaeblog-typst`))</RustProjectPath>
</PropertyGroup>
<Target Name="BuildRustProject" BeforeTargets="Build">
<Exec Command="cargo build --release" WorkingDirectory="$(RustProjectPath)"/>
</Target>
<ItemGroup>
<Content Include="../yaeblog-typst/target/release/libyaeblog_typst.so"
Link="runtimes/linux-x64/native/libyaeblog_typst.so" CopyToOutputDirectory="Always"/>
</ItemGroup>
</Project>

6
src/yaeblog-typst/.gitignore vendored Normal file
View File

@@ -0,0 +1,6 @@
/target
/tmp
.idea/
*.S
*.wav
.DS_Store

2781
src/yaeblog-typst/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,13 @@
[package]
name = "yaeblog-typst"
version = "0.1.0"
edition = "2024"
[lib]
crate-type = ["cdylib"]
[dependencies]
anyhow = "1.0.104"
libc = "0.2.187"
typst = "0.15.1"
typst-kit = "0.15.1"

View File

@@ -0,0 +1,85 @@
use std::ffi::{CStr, CString, c_char, c_void};
#[repr(C)]
pub struct ExternResult<T> {
pub content: T,
pub error_message: *mut c_char,
pub succeeded: bool,
}
impl<T> ExternResult<T> where T : Default {
pub fn from_result(r: anyhow::Result<T>) -> Self {
match r {
Ok(c) => {
ExternResult {
content: c,
error_message: std::ptr::null_mut(),
succeeded: true
}
},
Err(e) => {
ExternResult {
content: T::default(),
error_message: return_str(e.to_string()),
succeeded: false
}
}
}
}
}
pub fn extract_str<'a>(str: *const c_char) -> anyhow::Result<&'a str> {
let c_str = unsafe { CStr::from_ptr(str) };
let result = c_str.to_str()?;
Ok(result)
}
pub fn return_str(str: String) -> *mut c_char {
let returned_c_str = match CString::new(str) {
Ok(s) => s,
Err(_) => {
return std::ptr::null_mut();
}
};
returned_c_str.into_raw()
}
#[unsafe(no_mangle)]
pub extern "C" fn free_rust_string(ptr: *mut c_char) {
if ptr.is_null() {
return;
}
unsafe {
// Retake the ownership of string and drop it.
let _ = CString::from_raw(ptr);
}
}
#[unsafe(no_mangle)]
pub extern "C" fn process_string(str: *const c_char) -> *mut c_char {
let rust_str = match extract_str(str) {
Ok(s) => s,
Err(e) => {
return return_str(format!("Encounter error: {e}"))
}
};
let mut returned_str = String::new();
returned_str.push_str("Process ");
returned_str.push_str(rust_str);
return_str(returned_str)
}
pub fn into_handler<T>(handler: Box<T>) -> *mut c_void {
Box::into_raw(handler) as *mut c_void
}
pub fn from_handler<'a, T>(handler: *mut c_void) -> &'a mut T {
unsafe {
&mut *(handler as *mut T)
}
}

View File

@@ -0,0 +1,17 @@
mod ffi_utils;
mod world;
use std::ffi::{c_char, c_void};
use crate::ffi_utils::ExternResult;
pub struct TypstCompiler {
}
pub extern "C" fn init_compiler(work_dir: *const c_char) -> ExternResult<*mut c_void> {
todo!()
}
pub extern "C" fn typst_compile(handler: *mut c_void, str: *const c_char) -> ExternResult<*mut c_char> {
todo!()
}

View File

@@ -0,0 +1,43 @@
use std::path::PathBuf;
use std::sync::LazyLock;
use typst::utils::LazyHash;
use typst::{Library, World};
use typst::diag::FileResult;
use typst::foundations::{Bytes, Datetime, Duration};
use typst::syntax::{FileId, Source};
use typst::text::{Font, FontBook};
pub struct SystemWorld {
work_dir: PathBuf,
library: LazyHash<Library>,
}
impl World for SystemWorld {
fn library(&self) -> &LazyHash<Library> {
todo!()
}
fn book(&self) -> &LazyHash<FontBook> {
todo!()
}
fn main(&self) -> FileId {
todo!()
}
fn source(&self, id: FileId) -> FileResult<Source> {
todo!()
}
fn file(&self, id: FileId) -> FileResult<Bytes> {
todo!()
}
fn font(&self, index: usize) -> Option<Font> {
todo!()
}
fn today(&self, offset: Option<Duration>) -> Option<Datetime> {
todo!()
}
}