Does not include support for directories without direct children, but that should be fixable. Apart from that, mostly robustness work then it should be a good time to start adding support for more Typst functionality.
GYTRFADRDO4SYXV6V3PEPGGFIRDHQH5YBTKEJCWFAIZ5CX4P46NAC 4MMVEN5YUVUQ5RO3X2XKZVBMBMHOEJIYUCLKL63EPRUVQQRQXUAAC C73UJ7ZYG4EE3YTK3N66GXPNWJHEBSRE4PDQBWMN6SKQ3U6ZYKXAC 2N3KOCP74PCK2ETO5PCWBDR5PA57DDNT2KR4JLBPZPQPA56SAR4QC BA5Y6VSEHJQBOYBS6R6FE6IZDRNAPNIN5ITJXWK7L46RJVHNI7JAC BSJYWOYSJRERQ45AD7RN3364RYQ5P3IM76S67262VLFZPFO3B5JQC ZYNEMGAZXWHIWGNPB2RTYG3JWTH5Y5XY4JWJ3TTPANIOORTCLISAC HEUMSBESSTWA6G7ZG5OJP3ZW3FLXHYUCODGDHMZSLE4O777JSDJQC // Build all the fileslet prefix = PathBuf::from(DOCS_SOURCE);for file in typst_files {let parent = file.path().parent().unwrap();// TODO: Handle nested directories correctly// let remainder = if parent != prefix {// assert!(parent.starts_with(&prefix));// parent.strip_prefix(&prefix).unwrap().to_path_buf()// } else {// PathBuf::new()// };
// Keep track of all the filenames to later construct modules// Each folder is a module, each file is a function// For example: docs/test/hello.typ is docs::test::hello()let filenames: Vec<String> = typst_files.map(|entry| entry.path().to_string_lossy().to_string()).collect();
std::fs::create_dir_all(output_path.parent().unwrap()).unwrap();// TODO: this will only work for a single file, in the top-level directorylet rust_code = transform_pandoc(file.path());std::fs::write(&output_path, rust_code).unwrap();}
let rust_code = output_rust(filenames);std::fs::write(&output_path, rust_code).unwrap();
}}pub fn output_rust(files: Vec<String>) -> String {let file = syn::File {shebang: None,attrs: Vec::new(),items: vec![files_to_modules(files)],};let output = quote!(#file).to_string();output}fn nth_deepest_module(global_module: &mut syn::ItemMod, limit: usize) -> &mut syn::ItemMod {dbg!(&global_module, limit);let mut prev_deepest = global_module;let mut counter = 0;while counter < limit {prev_deepest = match prev_deepest.content.as_mut().unwrap().1.last_mut().unwrap() {syn::Item::Mod(next) => next,_ => unreachable!(),};counter += 1;}prev_deepest}fn new_module(name: &str) -> syn::ItemMod {syn::ItemMod {attrs: Vec::new(),vis: syn::Visibility::Public(syn::token::Pub::default()),unsafety: None,mod_token: syn::token::Mod::default(),ident: syn::Ident::new(name, proc_macro2::Span::call_site()),content: Some((syn::token::Brace::default(),vec![syn::Item::Use(syn::ItemUse {attrs: Vec::new(),vis: syn::Visibility::Inherited,use_token: syn::token::Use::default(),leading_colon: None,tree: syn::UseTree::Path(syn::UsePath {ident: syn::Ident::new("xilem_html", proc_macro2::Span::call_site()),colon2_token: syn::token::PathSep::default(),tree: Box::new(syn::UseTree::Group(syn::UseGroup {brace_token: syn::token::Brace::default(),items: Punctuated::<syn::UseTree, syn::token::Comma>::from_iter(vec![syn::UseTree::Name(syn::UseName {ident: syn::Ident::new("elements",proc_macro2::Span::call_site(),),}),syn::UseTree::Name(syn::UseName {ident: syn::Ident::new("ViewSequence",proc_macro2::Span::call_site(),),}),].into_iter(),),})),}),semi_token: syn::token::Semi::default(),})],)),semi: None,
pub fn transform_pandoc(path: &Path) -> String {let mut pandoc = pandoc::new();pandoc.add_input(path);
fn files_to_modules(mut files: Vec<String>) -> syn::Item {let paths = StringPatriciaSet::from_iter(files.iter());// Sorting the list of filenames allows for easier module creation laterfiles.sort();let mut global = new_module(DOCS_DIR);let mut parent = &mut global;let mut depth = 0;for file in &files {// Get the longest shared prefix by getting the parent directorylet file_path = PathBuf::from(&file);let file_parent = file_path.parent().unwrap().to_string_lossy();dbg!(&file_parent);let prefix = paths.get_longest_common_prefix(&file_parent).unwrap_or(&file_parent);dbg!(prefix);let mut common_prefixed = paths.iter_prefix(prefix);let first_common = &common_prefixed.next().unwrap();let last_common = &common_prefixed.last().unwrap_or(file.clone());// Create Rust ASTlet pandoc = typst_to_pandoc(&file).iter().map(pandoc_to_xilem).collect();let function = blocks_to_function(&fs_path_to_module_ident(&file), pandoc);// Increase depth if first item in prefix// Make sure not to immediately jump 1 level too deep if there's only 1 fileif first_common == file && prefix != DOCS_DIR {depth += 1;parent.content.as_mut().unwrap().1.push(syn::Item::Mod(new_module(&file_path.parent().unwrap().file_name().unwrap().to_string_lossy(),)));parent = nth_deepest_module(&mut global, depth);}// Get a mutable reference to the module childrenlet content = &mut parent.content.as_mut();// Append the function corresponding to our Typst filecontent.get_or_insert(&mut ((syn::token::Brace::default()), Vec::new())).1.push(function);// Decrease depth if last item in prefixdbg!(&first_common, &last_common);if last_common == file && depth > 0 {depth -= 1;parent = nth_deepest_module(&mut global, depth);}}syn::Item::Mod(global)}
items: vec![syn::Item::Mod(syn::ItemMod {attrs: Vec::new(),vis: syn::Visibility::Inherited,unsafety: None,mod_token: syn::token::Mod::default(),ident: syn::Ident::new("docs", proc_macro2::Span::call_site()),content: Some((syn::token::Brace::default(), vec![syn::Item::Use(syn::ItemUse {attrs: Vec::new(),vis: syn::Visibility::Inherited,use_token: syn::token::Use::default(),leading_colon: None,tree: syn::UseTree::Path(syn::UsePath {ident: syn::Ident::new("xilem_html", proc_macro2::Span::call_site()),colon2_token: syn::token::PathSep::default(),tree: Box::new(syn::UseTree::Group(syn::UseGroup {brace_token: syn::token::Brace::default(),items: Punctuated::<syn::UseTree, syn::token::Comma>::from_iter(vec![syn::UseTree::Name(syn::UseName {ident: syn::Ident::new("elements",proc_macro2::Span::call_site(),),}),syn::UseTree::Name(syn::UseName {ident: syn::Ident::new("ViewSequence",proc_macro2::Span::call_site(),),}),]
vis: syn::Visibility::Public(syn::token::Pub::default()),sig: syn::Signature {constness: None,asyncness: None,unsafety: None,abi: None,fn_token: syn::token::Fn::default(),ident: syn::Ident::new(name, proc_macro2::Span::call_site()),generics: syn::Generics { lt_token: None, params: Punctuated::from_iter(vec![syn::GenericParam::Type(syn::TypeParam { attrs: Vec::new(), ident: syn::Ident::new("T", proc_macro2::Span::call_site()), colon_token: None, bounds: Punctuated::new(), eq_token: None, default: None })].into_iter()), gt_token: None, where_clause: None },paren_token: syn::token::Paren::default(),inputs: Punctuated::new(),variadic: None,output: syn::ReturnType::Type(syn::token::RArrow::default(),Box::new(syn::Type::ImplTrait(syn::TypeImplTrait {impl_token: syn::token::Impl::default(),bounds: Punctuated::from_iter(vec![syn::TypeParamBound::Trait(syn::TraitBound {paren_token: None,modifier: syn::TraitBoundModifier::None,lifetimes: None,path: syn::Path {leading_colon: None,segments: Punctuated::from_iter(vec![syn::PathSegment {ident: syn::Ident::new("ViewSequence",proc_macro2::Span::call_site(),),arguments: syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments {colon2_token: None,lt_token: syn::token::Lt::default(),args: Punctuated::from_iter(vec![syn::GenericArgument::Type(syn::Type::Path(syn::TypePath {qself: None,path: syn::Path {leading_colon: None,segments: Punctuated::from_iter(vec![syn::PathSegment { ident: syn::Ident::new("T", proc_macro2::Span::call_site()), arguments: syn::PathArguments::None }].into_iter()),},}),)].into_iter(),),gt_token: syn::token::Gt::default(),},),}]
vis: syn::Visibility::Public(syn::token::Pub::default()),sig: syn::Signature {constness: None,asyncness: None,unsafety: None,abi: None,fn_token: syn::token::Fn::default(),ident: syn::Ident::new("test", proc_macro2::Span::call_site()),generics: syn::Generics { lt_token: None, params: Punctuated::from_iter(vec![syn::GenericParam::Type(syn::TypeParam { attrs: Vec::new(), ident: syn::Ident::new("T", proc_macro2::Span::call_site()), colon_token: None, bounds: Punctuated::new(), eq_token: None, default: None })].into_iter()), gt_token: None, where_clause: None },paren_token: syn::token::Paren::default(),inputs: Punctuated::new(),variadic: None,output: syn::ReturnType::Type(syn::token::RArrow::default(),Box::new(syn::Type::ImplTrait(syn::TypeImplTrait {impl_token: syn::token::Impl::default(),bounds: Punctuated::from_iter(vec![syn::TypeParamBound::Trait(syn::TraitBound {paren_token: None,modifier: syn::TraitBoundModifier::None,lifetimes: None,path: syn::Path {leading_colon: None,segments: Punctuated::from_iter(vec![syn::PathSegment {ident: syn::Ident::new("ViewSequence",proc_macro2::Span::call_site(),),arguments: syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments {colon2_token: None,lt_token: syn::token::Lt::default(),args: Punctuated::from_iter(vec![syn::GenericArgument::Type(syn::Type::Path(syn::TypePath {qself: None,path: syn::Path {leading_colon: None,segments: Punctuated::from_iter(vec![syn::PathSegment { ident: syn::Ident::new("T", proc_macro2::Span::call_site()), arguments: syn::PathArguments::None }].into_iter()),},}),)].into_iter(),),gt_token: syn::token::Gt::default(),},),}].into_iter(),),},})].into_iter(),),})),),},block: Box::new(syn::Block {brace_token: syn::token::Brace::default(),stmts: vec![syn::Stmt::Expr(syn::Expr::Tuple(syn::ExprTuple {attrs: Vec::new(),paren_token: syn::token::Paren::default(),elems: Punctuated::from_iter(blocks.into_iter()),}),None,)],}),}),])),semi: None,}),],};let output = quote!(#file).to_string();dbg!(&output);output
paren_token: syn::token::Paren::default(),elems: Punctuated::from_iter(blocks.into_iter()),}),None,)],}),})