rsmack_utils/
fs.rs

1//! [rsmack-fs](../rsmack_fs/index.html) related utils
2use std::{
3    fs::File,
4    path::{Path, PathBuf},
5};
6
7use bon::builder;
8use proc_macro2::{Span, TokenStream};
9use quote::{quote, ToTokens};
10use syn::Ident;
11/// Get the directory (workspace) from which we are compiling
12/// # Panics
13/// This aggressively unwrap some layers of parent path
14pub fn calling_crate_dir() -> &'static Path {
15    // Need a build.rs file
16    let out_dir = Path::new(env!("OUT_DIR"));
17    // TODO This work in a single crate but to check in workspace
18    let calling_crate_dir = out_dir
19        .parent()
20        .expect("Failed to get parent of out directory")
21        .parent()
22        .unwrap()
23        .parent()
24        .unwrap()
25        .parent()
26        .unwrap()
27        .parent()
28        .unwrap();
29    calling_crate_dir
30}
31
32/// Generate a struct which fields match a flat folder of rust modules, each exposing at least a same file named **`PascalCase`** type
33#[builder]
34pub fn folder_iso_struct(
35    name: &str,
36    pre: &TokenStream,
37    folder: &str,
38    // TODO find a way to remove maybe ?
39    from_crate: &str,
40    #[builder(default = false)] log_enabled: bool,
41) {
42    use build_print::*;
43    use stringcase::*;
44    macro_rules! log {
45        ($($arg:tt)+) => {
46            if log_enabled {
47                custom_println!("folder_iso_struct", green, $($arg)+)
48            }
49        };
50    }
51
52    let ccd = calling_crate_dir();
53    let mods_folder_path = ccd.join(from_crate).join("src").join(folder);
54    let paths = std::fs::read_dir(mods_folder_path).unwrap();
55    let struct_mod_folder_id = parse_id_maybe_raw(folder);
56    let fields = paths
57        .into_iter()
58        .filter(|path| {
59            let module_path = path.as_ref().unwrap().path();
60            let struct_folder_file_stem = module_path.file_stem().unwrap().to_str().unwrap();
61            struct_folder_file_stem != "mod"
62        })
63        .map(|path| {
64            let module_path = path.unwrap().path();
65            let struct_folder_file_stem = module_path.file_stem().unwrap().to_str().unwrap();
66            let opts = stringcase::Options {
67                separate_before_non_alphabets: true,
68                separate_after_non_alphabets: true,
69                separators: "_",
70                keep: "",
71            };
72            let field_ty_name = pascal_case_with_options(struct_folder_file_stem, &opts);
73            let struct_mod_id = parse_id_maybe_raw(struct_folder_file_stem);
74            let import_path = quote! {crate::#struct_mod_folder_id::#struct_mod_id};
75            let field_ty_id = parse_id_maybe_raw(&field_ty_name);
76            let field_ty_path = quote! {
77                #import_path::#field_ty_id
78            };
79            log!("{} -> {}", name, field_ty_path.to_string());
80            quote! {
81                #struct_mod_id: #field_ty_path
82            }
83        })
84        .collect::<Vec<_>>();
85    let name_id = parse_id_maybe_raw(name);
86    let output = quote! {
87        #pre
88        pub struct #name_id {
89            #(#fields),*
90        }
91    }
92    .to_token_stream()
93    .to_string();
94    generate_file(format!("{name}.rs"), output.as_bytes());
95}
96
97fn parse_id_maybe_raw(s: &str) -> Ident {
98    syn::parse_str::<Ident>(s).unwrap_or_else(|_| Ident::new_raw(s, Span::call_site()))
99}
100
101/// Generate file in `OUTDIR`
102/// # Panics
103/// - If `OUTDIR` not set
104/// - File creation or write fail
105pub fn generate_file<P: AsRef<Path>>(path: P, text: &[u8]) {
106    use std::io::Write;
107    let out_dir = std::env::var("OUT_DIR").unwrap();
108    let out_path = PathBuf::from(&out_dir);
109    let dest_path = out_path.join(&path);
110    let mut f = File::create(dest_path).unwrap();
111    f.write_all(text).unwrap();
112}
113/// Get the package source folder from `CARGO_MANIFEST_DIR` at runtime
114/// # Panics
115/// If `CARGO_MANIFEST_DIR` does not exist
116pub fn package_src_folder() -> PathBuf {
117    let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
118    PathBuf::from(manifest_dir).join("src")
119}