1use proc_macro::TokenStream;
2use syn::{self, Ident, parse_macro_input, DeriveInput, Fields};
3use quote::{quote, ToTokens};
4
5
6#[proc_macro_derive(ConfigManager)]
7pub fn config_derive_macro(item: TokenStream) -> TokenStream {
8 let ast: DeriveInput = parse_macro_input!(item as DeriveInput);
9 return impl_config_trait(ast);
10}
11
12fn impl_config_trait(ast: syn::DeriveInput) -> TokenStream {
13 let data: syn::DataStruct = match ast.data {
15 syn::Data::Struct(data) => data,
16 syn::Data::Enum(_) => panic!("Enums are not supported by Carpenter"),
17 syn::Data::Union(_) => panic!("Unions are not supported by Carpenter"),
18 };
19
20 let config_id: &Ident = &ast.ident;
22
23 let settings_name: String = format!("{}Factory", config_id);
24 let setting_id: Ident = syn::Ident::new(&settings_name, config_id.span());
25
26 let fields = match data.fields {
27 Fields::Named(fields) => fields.named,
28 _ => panic!("Only named fields are supported by Carpenter")
29 };
30
31 let field_read_statements = fields.iter().map(|field| {
32 let field_name = field.ident.as_ref().expect("Field name not found");
33 let field_type = &field.ty;
34
35 if field_type.to_token_stream().to_string() == "String" {
36 quote! {
37 #field_name: {
38 let mut string_buffer = Vec::new();
39 loop {
40 let mut byte = [0; 1];
41 if stream.read_exact(&mut byte).is_err() || byte[0] == 0 {
42 break;
43 }
44 string_buffer.push(byte[0]);
45 }
46 String::from_utf8_lossy(&string_buffer).to_string()
47 },
48 }
49 } else {
50 quote! {
51 #field_name: #field_type::read_from(stream, order)?,
52 }
53 }
54 });
55
56 let mut field_write_statements = Vec::new();
57 for field in &fields {
58 let field_name = field.ident.as_ref().expect("Field name not found");
59
60 let field_write_expr;
61 if field.ty.to_token_stream().to_string() == "String" {
62 field_write_expr = quote! {
63 for byte in self.#field_name.bytes() {
64 stream.write_all(&[byte])?;
65 }
66 stream.write_all(&[0])?;
67 };
68 }
69 else {
70 field_write_expr = quote! {
71 self.#field_name.write_to(stream, order)?;
72 };
73
74 }
75
76 field_write_statements.push(field_write_expr);
77 }
78
79
80 let expanded = quote!{
82 use carpenter::ConfigPath;
83 use std::fs::File;
84 use std::io::{Cursor, Write, Read};
85 use std::path::PathBuf;
86 use bytestream::{StreamWriter, ByteOrder, StreamReader};
87
88
89 struct #setting_id {
90 path: PathBuf,
91 username: String,
92 application_name: String,
93 config_name: String,
94 }
95
96 impl #setting_id {
97 fn create_dir(&self) -> Result<(), std::io::Error> {
99 std::fs::create_dir_all(&self.path)?;
100 Ok(())
101 }
102
103 fn create_file(&self) -> Result<(), std::io::Error> {
105 let config_file_path = self.path.join(&self.config_name);
106 let config_file = File::create(config_file_path)?;
107 Ok(())
108 }
109
110 fn save(&self, config_struct: &#config_id) -> Result<(), std::io::Error> {
112 self.create_dir()?;
113 self.create_file()?;
114
115 let config_file_path = self.path.join(&self.config_name);
116 let mut buffer = Vec::<u8>::new();
117 config_struct.write_to(&mut buffer, ByteOrder::BigEndian)?;
118
119 std::fs::write(config_file_path, buffer)?;
120 Ok(())
121 }
122
123 fn read(&self) -> Result<#config_id, std::io::Error> {
125 let config_file_path = self.path.join(&self.config_name);
126 let mut buffer = std::fs::read(config_file_path)?;
127 let mut cursor = Cursor::new(buffer);
128 return Ok(#config_id::read_from(&mut cursor, ByteOrder::BigEndian)?);
129 }
130 }
131
132 impl #config_id {
133 pub fn init_config(username: &str, application_name: &str, config_name: &str) -> #setting_id {
159 let builder = #setting_id {
160 path: PathBuf::from(ConfigPath::new(username, application_name).inner.clone()),
161 username: username.to_string(),
162 application_name: application_name.to_string(),
163 config_name: config_name.to_string()
164 };
165 return builder;
166 }
167 }
168
169 impl StreamReader for #config_id {
170 fn read_from<R: Read>(stream: &mut R, order: ByteOrder) -> Result<#config_id, std::io::Error> {
171 Ok(#config_id {
172 #(#field_read_statements)*
173 })
174 }
175 }
176
177 impl StreamWriter for #config_id {
178 fn write_to<W: Write>(&self, stream: &mut W, order: bytestream::ByteOrder) -> Result<(), std::io::Error> {
179 #(#field_write_statements)*
180 Ok(())
181 }
182 }
183 };
184
185 return expanded.into();
186}