feat: Use typst as blog format. #24
@@ -14,6 +14,7 @@
|
|||||||
<Folder Name="/src/">
|
<Folder Name="/src/">
|
||||||
<Project Path="src/YaeBlog.Abstractions/YaeBlog.Abstractions.csproj" />
|
<Project Path="src/YaeBlog.Abstractions/YaeBlog.Abstractions.csproj" />
|
||||||
<Project Path="src/YaeBlog.Tests/YaeBlog.Tests.csproj" />
|
<Project Path="src/YaeBlog.Tests/YaeBlog.Tests.csproj" />
|
||||||
|
<Project Path="src/YaeBlog.Typst/YaeBlog.Typst.csproj" />
|
||||||
<Project Path="src/YaeBlog/YaeBlog.csproj" />
|
<Project Path="src/YaeBlog/YaeBlog.csproj" />
|
||||||
</Folder>
|
</Folder>
|
||||||
<Folder Name="/third-party/" />
|
<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>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\YaeBlog.Typst\YaeBlog.Typst.csproj" />
|
||||||
<ProjectReference Include="..\YaeBlog\YaeBlog.csproj" />
|
<ProjectReference Include="..\YaeBlog\YaeBlog.csproj" />
|
||||||
</ItemGroup>
|
</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