feat: Use typst as blog format. #24
@@ -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/" />
|
||||
|
||||
18
src/YaeBlog.Tests/CallRustTests.cs
Normal file
18
src/YaeBlog.Tests/CallRustTests.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\YaeBlog.Typst\YaeBlog.Typst.csproj" />
|
||||
<ProjectReference Include="..\YaeBlog\YaeBlog.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
9
src/YaeBlog.Typst/RustCaller.cs
Normal file
9
src/YaeBlog.Typst/RustCaller.cs
Normal 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);
|
||||
}
|
||||
27
src/YaeBlog.Typst/RustString.cs
Normal file
27
src/YaeBlog.Typst/RustString.cs
Normal 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);
|
||||
}
|
||||
23
src/YaeBlog.Typst/YaeBlog.Typst.csproj
Normal file
23
src/YaeBlog.Typst/YaeBlog.Typst.csproj
Normal 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
6
src/yaeblog-typst/.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
/target
|
||||
/tmp
|
||||
.idea/
|
||||
*.S
|
||||
*.wav
|
||||
.DS_Store
|
||||
2781
src/yaeblog-typst/Cargo.lock
generated
Normal file
2781
src/yaeblog-typst/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
13
src/yaeblog-typst/Cargo.toml
Normal file
13
src/yaeblog-typst/Cargo.toml
Normal 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"
|
||||
85
src/yaeblog-typst/src/ffi_utils.rs
Normal file
85
src/yaeblog-typst/src/ffi_utils.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
17
src/yaeblog-typst/src/lib.rs
Normal file
17
src/yaeblog-typst/src/lib.rs
Normal 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!()
|
||||
}
|
||||
43
src/yaeblog-typst/src/world.rs
Normal file
43
src/yaeblog-typst/src/world.rs
Normal 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!()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user