Skip to content

Commit

Permalink
Create a filesystem generator procedural macro
Browse files Browse the repository at this point in the history
Given a filesystem layout in a custom syntax (as a string), the new
procedural macro will generate a struct impl that allows each distinct
file declared to be resolved by setting values for the relevant
placeholders in the file path.

Moving forward, the resolved file struct can be extended to support the
desired operations.

A declarative filesystem layout is useful because it allows structural
re-organisation with little effort.
  • Loading branch information
Aleksbgbg committed Feb 15, 2024
1 parent 64f03f4 commit ce64c81
Show file tree
Hide file tree
Showing 8 changed files with 754 additions and 1 deletion.
14 changes: 14 additions & 0 deletions backend-rs/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion backend-rs/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[workspace]
resolver = "2"
members = ["backend"]
members = ["backend", "fs", "fs-file"]
6 changes: 6 additions & 0 deletions backend-rs/fs-file/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "fs-file"
version = "0.0.0"
edition = "2021"

[dependencies]
18 changes: 18 additions & 0 deletions backend-rs/fs-file/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use std::path::{Path, PathBuf};

#[derive(Debug)]
pub struct File {
path: PathBuf,
}

impl File {
pub fn from(path: String) -> Self {
Self {
path: PathBuf::from(path),
}
}

pub fn path(&self) -> &Path {
&self.path
}
}
13 changes: 13 additions & 0 deletions backend-rs/fs/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "fs"
version = "0.0.0"
edition = "2021"

[lib]
proc-macro = true

[dependencies]
fs-file = { version = "0.0.0", path = "../fs-file", default-features = false }
proc-macro2 = { version = "1.0.78", default-features = false }
quote = { version = "1.0.35", default-features = false }
syn = { version = "2.0.48", default-features = false, features = ["parsing", "proc-macro"] }
83 changes: 83 additions & 0 deletions backend-rs/fs/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
mod parse_fs;

use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use std::str::FromStr;
use syn::parse::{Parse, ParseStream};
use syn::{parse_macro_input, Ident, LitStr, Token};

struct Filesystem {
name: Ident,
tree: LitStr,
}

impl Parse for Filesystem {
fn parse(input: ParseStream) -> syn::Result<Self> {
let name: Ident = input.parse()?;
input.parse::<Token![,]>()?;
let tree: LitStr = input.parse()?;

Ok(Filesystem { name, tree })
}
}

fn as_tokens(string: String) -> TokenStream {
TokenStream::from_str(&string).unwrap()
}

#[proc_macro]
pub fn filesystem(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let Filesystem { name, tree } = parse_macro_input!(input as Filesystem);

let tree = parse_fs::parse_fs(&tree.value());

let vars_enum = format_ident!("{}Var", name);
let unique_vars = tree.unique_vars.into_iter().map(as_tokens);
let funcs = tree.paths.into_iter().map(|path| {
let name = as_tokens(path.name);
let path_format = path.path_format;
let vars: TokenStream = path
.vars
.into_iter()
.map(as_tokens)
.map(|var| quote!(self.vars.get(&#vars_enum::#var).unwrap(),))
.collect();

quote! {
pub fn #name(&self) -> ::fs_file::File {
::fs_file::File::from(format!(#path_format, #vars))
}
}
});

proc_macro::TokenStream::from(quote! {
#[derive(
::std::cmp::PartialEq,
::std::cmp::Eq,
::std::hash::Hash,
::std::clone::Clone,
::std::marker::Copy,
)]
pub enum #vars_enum {
#(#unique_vars),*
}

#[derive(::std::clone::Clone, ::std::default::Default)]
pub struct #name {
vars: ::std::collections::HashMap<#vars_enum, String>,
}

impl #name {
pub fn new() -> Self {
Default::default()
}

pub fn set(&mut self, var: #vars_enum, val: String) -> &mut Self {
self.vars.insert(var, val);
self
}

#(#funcs)*
}
})
}
Loading

0 comments on commit ce64c81

Please sign in to comment.