1extern crate proc_macro;
2use proc_macro::TokenStream;
3use proc_macro2::{Ident, Span};
4use quote::quote;
5
6fn col_ident(n: usize) -> Ident {
8 Ident::new(&format!("Col{}", n), Span::call_site())
9}
10fn type_ident(n: usize) -> Ident {
11 Ident::new(&format!("T{}", n), Span::call_site())
12}
13fn field_ident(n: usize) -> Ident {
14 Ident::new(&format!("col_{}", n), Span::call_site())
15}
16fn accessor_ident(n: usize) -> Ident {
17 Ident::new(&format!("c{}", n), Span::call_site())
18}
19fn accessor_mut_ident(n: usize) -> Ident {
20 Ident::new(&format!("c{}_mut", n), Span::call_site())
21}
22fn sort_by_ident(n: usize) -> Ident {
23 Ident::new(&format!("sort_by_c{}", n), Span::call_site())
24}
25
26#[proc_macro]
30pub fn multi_col_def(_item: TokenStream) -> TokenStream {
31 let mut all = proc_macro2::TokenStream::new();
32
33 for n in 1..=32 {
34 let col_name = col_ident(n);
35 let type_params: Vec<_> = (1..=n).map(type_ident).collect();
36 let field_names: Vec<_> = (1..=n).map(field_ident).collect();
37 let type_refs: Vec<_> = (1..=n).map(type_ident).collect();
38
39 let fields = field_names.iter().zip(type_refs.iter()).map(|(f, t)| {
40 quote! { pub #f: #t }
41 });
42
43 let where_clauses = type_params.iter().map(|t| {
44 quote! { #t: Column }
45 });
46
47 let def = quote! {
48 #[derive(Debug, Clone)]
49 pub struct #col_name<#(#type_params),*> where #(#where_clauses),* {
50 pub header: Vec<String>,
51 #(#fields),*
52 }
53 };
54 all.extend(def);
55 }
56
57 all.into()
58}
59
60#[proc_macro]
64pub fn multi_col_impl(_item: TokenStream) -> TokenStream {
65 let mut all = proc_macro2::TokenStream::new();
66
67 for n in 1..=32 {
68 let col_name = col_ident(n);
69 let type_params: Vec<_> = (1..=n).map(type_ident).collect();
70 let field_names: Vec<_> = (1..=n).map(field_ident).collect();
71
72 let where_clauses = type_params.iter().map(|t| {
74 quote! { #t: Column + Default }
75 });
76
77 let default_fields = field_names.iter().zip(type_params.iter()).map(|(f, t)| {
79 quote! { #f: #t::default() }
80 });
81
82 let from_cols_params = field_names.iter().zip(type_params.iter()).map(|(f, t)| {
84 quote! { #f: #t }
85 });
86
87 let from_cols_fields = field_names.iter().map(|f| {
89 quote! { #f }
90 });
91
92 let accessors = (1..=n).map(|j| {
94 let acc = accessor_ident(j);
95 let acc_mut = accessor_mut_ident(j);
96 let field = field_ident(j);
97 let ty = type_ident(j);
98 quote! {
99 pub fn #acc(&self) -> &#ty {
100 &self.#field
101 }
102
103 pub fn #acc_mut(&mut self) -> &mut #ty {
104 &mut self.#field
105 }
106 }
107 });
108
109 let n_lit = proc_macro2::Literal::usize_unsuffixed(n);
110
111 let block = quote! {
112 impl<#(#type_params),*> #col_name<#(#type_params),*>
113 where #(#where_clauses),*
114 {
115 pub fn new() -> Self {
116 Self {
117 header: vec![],
118 #(#default_fields),*
119 }
120 }
121
122 pub fn set_header(&mut self, header: Vec<&str>) {
123 self.header = header.into_iter().map(|s| s.to_string()).collect();
124 }
125
126 pub fn header(&self) -> &Vec<String> {
127 &self.header
128 }
129
130 pub fn from_cols(#(#from_cols_params),*) -> Self {
131 Self {
132 header: vec![],
133 #(#from_cols_fields),*
134 }
135 }
136
137 #(#accessors)*
138
139 pub fn nrows(&self) -> usize {
140 self.col_1.row()
141 }
142
143 pub fn ncols(&self) -> usize {
144 #n_lit
145 }
146
147 pub fn shape(&self) -> (usize, usize) {
148 (self.nrows(), self.ncols())
149 }
150
151 pub fn len(&self) -> usize {
152 self.nrows()
153 }
154
155 pub fn is_empty(&self) -> bool {
156 self.nrows() == 0
157 }
158 }
159 };
160 all.extend(block);
161 }
162
163 all.into()
164}
165
166#[proc_macro]
171pub fn multi_col_extra_impl(_item: TokenStream) -> TokenStream {
172 let mut all = proc_macro2::TokenStream::new();
173
174 for n in 1..=32 {
175 let col_name = col_ident(n);
176 let type_params: Vec<_> = (1..=n).map(type_ident).collect();
177 let field_names: Vec<_> = (1..=n).map(field_ident).collect();
178
179 let where_clauses = type_params.iter().map(|t| {
181 quote! { #t: Column + Default }
182 });
183 let clone_clauses = type_params.iter().map(|t| {
184 quote! { #t::DType: Clone }
185 });
186 let into_clauses = type_params.iter().map(|t| {
187 quote! { Vec<#t::DType>: Into<#t> }
188 });
189
190 let head_fields = field_names.iter().zip(type_params.iter()).map(|(f, _t)| {
192 quote! {
193 #f: self.#f.to_vec()[..take].to_vec().into()
194 }
195 });
196
197 let tail_fields = field_names.iter().zip(type_params.iter()).map(|(f, _t)| {
198 quote! {
199 #f: self.#f.to_vec()[start..].to_vec().into()
200 }
201 });
202
203 let slice_fields = field_names.iter().zip(type_params.iter()).map(|(f, _t)| {
204 quote! {
205 #f: self.#f.to_vec()[start..end].to_vec().into()
206 }
207 });
208
209 let filter_fields = field_names.iter().zip(type_params.iter()).map(|(f, _t)| {
210 quote! {
211 #f: {
212 let v = self.#f.to_vec();
213 indices.iter().map(|&i| v[i].clone()).collect::<Vec<_>>().into()
214 }
215 }
216 });
217
218 let push_params = (1..=n).map(|j| {
220 let vname = Ident::new(&format!("v{}", j), Span::call_site());
221 let ty = type_ident(j);
222 quote! { #vname: #ty::DType }
223 });
224 let push_body = (1..=n).map(|j| {
225 let vname = Ident::new(&format!("v{}", j), Span::call_site());
226 let field = field_ident(j);
227 quote! { self.#field.push(#vname); }
228 });
229
230 let append_body = (1..=n).map(|j| {
232 let field = field_ident(j);
233 quote! {
234 for i in 0..other.#field.row() {
235 self.#field.push(other.#field.to_vec()[i].clone());
236 }
237 }
238 });
239
240 let concat_fields = field_names.iter().map(|f| {
242 quote! {
243 #f: {
244 let mut v: Vec<_> = self.#f.to_vec().clone();
245 v.extend(other.#f.to_vec().iter().cloned());
246 v.into()
247 }
248 }
249 });
250
251 let reindex_fields = field_names.iter().map(|f| {
253 quote! {
254 #f: {
255 let v = self.#f.to_vec();
256 indices.iter().map(|&i| v[i].clone()).collect::<Vec<_>>().into()
257 }
258 }
259 });
260
261 let sort_methods = (1..=n).map(|j| {
263 let method_name = sort_by_ident(j);
264 let field = field_ident(j);
265 let ty = type_ident(j);
266 quote! {
267 pub fn #method_name(&self) -> Self where #ty::DType: Ord {
268 let v = self.#field.to_vec();
269 let mut indices: Vec<usize> = (0..v.len()).collect();
270 indices.sort_by(|&a, &b| v[a].cmp(&v[b]));
271 self.reindex(&indices)
272 }
273 }
274 });
275
276 let block = quote! {
277 impl<#(#type_params),*> #col_name<#(#type_params),*>
278 where
279 #(#where_clauses,)*
280 #(#clone_clauses,)*
281 #(#into_clauses,)*
282 {
283 pub fn head(&self, n: usize) -> Self {
284 let take = n.min(self.nrows());
285 Self {
286 header: self.header.clone(),
287 #(#head_fields),*
288 }
289 }
290
291 pub fn tail(&self, n: usize) -> Self {
292 let rows = self.nrows();
293 let start = rows.saturating_sub(n);
294 Self {
295 header: self.header.clone(),
296 #(#tail_fields),*
297 }
298 }
299
300 pub fn slice(&self, start: usize, end: usize) -> Self {
301 let end = end.min(self.nrows());
302 Self {
303 header: self.header.clone(),
304 #(#slice_fields),*
305 }
306 }
307
308 pub fn filter<F: Fn(usize) -> bool>(&self, predicate: F) -> Self {
309 let indices: Vec<usize> = (0..self.nrows())
310 .filter(|&i| predicate(i))
311 .collect();
312 Self {
313 header: self.header.clone(),
314 #(#filter_fields),*
315 }
316 }
317
318 pub fn push_row(&mut self, #(#push_params),*) {
319 #(#push_body)*
320 }
321
322 pub fn append(&mut self, other: &Self) {
323 #(#append_body)*
324 }
325
326 pub fn concat(self, other: Self) -> Self {
327 Self {
328 header: self.header.clone(),
329 #(#concat_fields),*
330 }
331 }
332
333 pub fn reindex(&self, indices: &[usize]) -> Self {
334 Self {
335 header: self.header.clone(),
336 #(#reindex_fields),*
337 }
338 }
339
340 #(#sort_methods)*
341 }
342 };
343 all.extend(block);
344 }
345
346 all.into()
347}
348
349#[proc_macro]
353pub fn multi_col_display_impl(_item: TokenStream) -> TokenStream {
354 let mut all = proc_macro2::TokenStream::new();
355
356 for n in 1..=32 {
357 let col_name = col_ident(n);
358 let type_params: Vec<_> = (1..=n).map(type_ident).collect();
359
360 let where_clauses = type_params.iter().map(|t| {
361 quote! { #t: Column }
362 });
363 let display_clauses = type_params.iter().map(|t| {
364 quote! { #t::DType: std::fmt::Display }
365 });
366
367 let col_indices: Vec<usize> = (1..=n).collect();
368 let field_names_for_width: Vec<_> = (1..=n).map(field_ident).collect();
369
370 let width_calcs = field_names_for_width.iter().enumerate().map(|(idx, f)| {
372 let idx_lit = proc_macro2::Literal::usize_unsuffixed(idx);
373 quote! {
374 let mut w = if #idx_lit < self.header.len() {
375 self.header[#idx_lit].len()
376 } else {
377 0
378 };
379 for i in 0..rows {
380 let s = format!("{}", self.#f.idx(i));
381 if s.len() > w { w = s.len(); }
382 }
383 widths.push(w);
384 }
385 });
386
387 let header_cells = col_indices.iter().map(|&j| {
388 let idx = j - 1;
389 let idx_lit = proc_macro2::Literal::usize_unsuffixed(idx);
390 quote! {
391 let name = if #idx_lit < self.header.len() {
392 &self.header[#idx_lit]
393 } else {
394 ""
395 };
396 write!(f, "{:>width$}", name, width = widths[#idx_lit])?;
397 if #idx_lit < widths.len() - 1 {
398 write!(f, " ")?;
399 }
400 }
401 });
402
403 let sep_cells = col_indices.iter().map(|&j| {
404 let idx = j - 1;
405 let idx_lit = proc_macro2::Literal::usize_unsuffixed(idx);
406 quote! {
407 write!(f, "{:->width$}", "", width = widths[#idx_lit])?;
408 if #idx_lit < widths.len() - 1 {
409 write!(f, " ")?;
410 }
411 }
412 });
413
414 let data_cells = field_names_for_width.iter().enumerate().map(|(idx, f)| {
415 let idx_lit = proc_macro2::Literal::usize_unsuffixed(idx);
416 let n_lit = proc_macro2::Literal::usize_unsuffixed(n);
417 quote! {
418 write!(f, "{:>width$}", format!("{}", self.#f.idx(i)), width = widths[#idx_lit])?;
419 if #idx_lit < #n_lit - 1 {
420 write!(f, " ")?;
421 }
422 }
423 });
424
425 let block = quote! {
426 impl<#(#type_params),*> std::fmt::Display for #col_name<#(#type_params),*>
427 where
428 #(#where_clauses,)*
429 #(#display_clauses,)*
430 {
431 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
432 let rows = self.col_1.row();
433 let mut widths: Vec<usize> = Vec::new();
434 #(#width_calcs)*
435
436 #(#header_cells)*
438 writeln!(f)?;
439
440 #(#sep_cells)*
442 writeln!(f)?;
443
444 for i in 0..rows {
446 #(#data_cells)*
447 if i < rows - 1 {
448 writeln!(f)?;
449 }
450 }
451
452 Ok(())
453 }
454 }
455 };
456 all.extend(block);
457 }
458
459 all.into()
460}
461
462#[proc_macro]
466pub fn col_vec_impl(item: TokenStream) -> TokenStream {
467 let ty: proc_macro2::TokenStream = item.into();
468
469 let expanded = quote! {
470 impl Column for Vec<#ty> {
471 type DType = #ty;
472
473 fn row(&self) -> usize {
474 self.len()
475 }
476
477 fn idx(&self, n: usize) -> &Self::DType {
478 &self[n]
479 }
480
481 fn idx_mut(&mut self, n: usize) -> &mut Self::DType {
482 &mut self[n]
483 }
484
485 fn to_vec(&self) -> &Vec<Self::DType> {
486 &self
487 }
488
489 fn push(&mut self, val: Self::DType) {
490 Vec::push(self, val);
491 }
492 }
493
494 impl ColumnApply for Vec<#ty> {
495 fn apply<F: FnMut(&mut Self::DType)>(&mut self, mut f: F) {
496 for item in self.iter_mut() {
497 f(item);
498 }
499 }
500 }
501 };
502
503 expanded.into()
504}
505
506#[proc_macro]
510pub fn multi_col_csv_impl(_item: TokenStream) -> TokenStream {
511 let mut all = proc_macro2::TokenStream::new();
512
513 for n in 1..=32 {
514 let col_name = col_ident(n);
515 let type_params: Vec<_> = (1..=n).map(type_ident).collect();
516
517 let where_default = type_params.iter().map(|t| {
518 quote! { #t: Column + Default }
519 });
520 let where_tostr = type_params.iter().map(|t| {
521 quote! { #t::DType: ToString + FromStr }
522 });
523 let where_err = type_params.iter().map(|t| {
524 quote! { <#t::DType as FromStr>::Err: std::fmt::Debug + Error }
525 });
526 let where_into = type_params.iter().map(|t| {
527 quote! { Vec<#t::DType>: Into<#t> }
528 });
529
530 let write_lets = (1..=n).map(|j| {
532 let acc = accessor_ident(j);
533 let var = Ident::new(&format!("c{}", j), Span::call_site());
534 quote! { let #var = self.#acc(); }
535 });
536
537 let write_records = (1..=n).map(|j| {
539 let var = Ident::new(&format!("c{}", j), Span::call_site());
540 let idx_lit = proc_macro2::Literal::usize_unsuffixed(j - 1);
541 quote! { record[#idx_lit] = #var.idx(i).to_string(); }
542 });
543
544 let read_decls = (1..=n).map(|j| {
546 let var = Ident::new(&format!("c{}", j), Span::call_site());
547 let ty = type_ident(j);
548 quote! { let mut #var: Vec<#ty::DType> = vec![]; }
549 });
550
551 let read_pushes = (1..=n).map(|j| {
553 let var = Ident::new(&format!("c{}", j), Span::call_site());
554 let idx_lit = proc_macro2::Literal::usize_unsuffixed(j - 1);
555 quote! { #var.push(rec[#idx_lit].parse().unwrap()); }
556 });
557
558 let from_cols_args = (1..=n).map(|j| {
560 let var = Ident::new(&format!("c{}", j), Span::call_site());
561 quote! { #var.into() }
562 });
563
564 let header_names = (1..=n).map(|j| {
566 let s = format!("c{}", j);
567 quote! { #s }
568 });
569
570 let n_lit = proc_macro2::Literal::usize_unsuffixed(n);
571
572 let block = quote! {
573 impl<#(#type_params),*> CSV for #col_name<#(#type_params),*>
574 where
575 #(#where_default,)*
576 #(#where_tostr,)*
577 #(#where_err,)*
578 #(#where_into,)*
579 {
580 fn write_csv(&self, file_path: &str, delimiter: char) -> Result<(), Box<dyn Error>> {
581 let mut wtr = WriterBuilder::new()
582 .delimiter(delimiter as u8)
583 .from_path(file_path)?;
584 #(#write_lets)*
585 let r: usize = self.col_1.row();
586 let c: usize = #n_lit;
587
588 wtr.write_record(self.header())?;
589
590 for i in 0..r {
591 let mut record: Vec<String> = vec!["".to_string(); c];
592 #(#write_records)*
593 wtr.write_record(record)?;
594 }
595 wtr.flush()?;
596 Ok(())
597 }
598
599 fn read_csv(file_path: &str, delimiter: char) -> Result<Self, Box<dyn Error>> {
600 let mut rdr = ReaderBuilder::new()
601 .has_headers(true)
602 .delimiter(delimiter as u8)
603 .trim(Trim::All)
604 .from_path(file_path)?;
605
606 #(#read_decls)*
607
608 for rec in rdr.records() {
609 let rec = rec?;
610 #(#read_pushes)*
611 }
612
613 let mut col = #col_name::from_cols(#(#from_cols_args),*);
614 col.set_header(vec![#(#header_names),*]);
615
616 Ok(col)
617 }
618 }
619 };
620 all.extend(block);
621 }
622
623 all.into()
624}
625
626#[proc_macro]
630pub fn multi_col_json_impl(_item: TokenStream) -> TokenStream {
631 let mut all = proc_macro2::TokenStream::new();
632
633 for n in 1..=32 {
634 let col_name = col_ident(n);
635 let type_params: Vec<_> = (1..=n).map(type_ident).collect();
636 let field_names: Vec<_> = (1..=n).map(field_ident).collect();
637
638 let where_default = type_params.iter().map(|t| {
639 quote! { #t: Column + Default }
640 });
641 let where_display = type_params.iter().map(|t| {
642 quote! { #t::DType: std::fmt::Display + FromStr }
643 });
644 let where_err = type_params.iter().map(|t| {
645 quote! { <#t::DType as FromStr>::Err: std::fmt::Debug + Error }
646 });
647 let where_into = type_params.iter().map(|t| {
648 quote! { Vec<#t::DType>: Into<#t> }
649 });
650
651 let json_col_writes = field_names.iter().enumerate().map(|(idx, f)| {
653 let idx_lit = proc_macro2::Literal::usize_unsuffixed(idx);
654 let n_lit = proc_macro2::Literal::usize_unsuffixed(n);
655 quote! {
656 {
657 let name = if #idx_lit < self.header.len() {
658 &self.header[#idx_lit]
659 } else {
660 ""
661 };
662 s.push_str(&format!(" \"{}\": [", name));
663 let v = self.#f.to_vec();
664 for i in 0..v.len() {
665 let val_str = format!("{}", v[i]);
666 if val_str.parse::<f64>().is_ok() {
668 s.push_str(&val_str);
669 } else if val_str == "true" || val_str == "false" {
670 s.push_str(&val_str);
671 } else {
672 s.push_str(&format!("\"{}\"", val_str));
673 }
674 if i < v.len() - 1 {
675 s.push_str(", ");
676 }
677 }
678 s.push(']');
679 if #idx_lit < #n_lit - 1 {
680 s.push(',');
681 }
682 s.push('\n');
683 }
684 }
685 });
686
687 let read_decls = (1..=n).map(|j| {
689 let var = Ident::new(&format!("c{}", j), Span::call_site());
690 let ty = type_ident(j);
691 quote! { let mut #var: Vec<#ty::DType> = vec![]; }
692 });
693
694 let read_fills = field_names.iter().enumerate().map(|(idx, _f)| {
695 let idx_lit = proc_macro2::Literal::usize_unsuffixed(idx);
696 let var = Ident::new(&format!("c{}", idx + 1), Span::call_site());
697 quote! {
698 if #idx_lit < headers.len() {
699 if let Some(arr) = data.get(&headers[#idx_lit]) {
700 for val in arr {
701 #var.push(val.parse().map_err(|e| format!("Parse error: {:?}", e))?);
702 }
703 }
704 }
705 }
706 });
707
708 let from_cols_args = (1..=n).map(|j| {
709 let var = Ident::new(&format!("c{}", j), Span::call_site());
710 quote! { #var.into() }
711 });
712
713 let block = quote! {
714 impl<#(#type_params),*> JsonIO for #col_name<#(#type_params),*>
715 where
716 #(#where_default,)*
717 #(#where_display,)*
718 #(#where_err,)*
719 #(#where_into,)*
720 {
721 fn to_json_string(&self) -> String {
722 let mut s = String::from("{\n");
723
724 s.push_str(" \"headers\": [");
726 for (i, h) in self.header.iter().enumerate() {
727 s.push_str(&format!("\"{}\"", h));
728 if i < self.header.len() - 1 {
729 s.push_str(", ");
730 }
731 }
732 s.push_str("],\n");
733
734 s.push_str(" \"data\": {\n");
736 #(#json_col_writes)*
737 s.push_str(" }\n");
738 s.push('}');
739 s
740 }
741
742 fn write_json(&self, file_path: &str) -> Result<(), Box<dyn Error>> {
743 std::fs::write(file_path, self.to_json_string())?;
744 Ok(())
745 }
746
747 fn read_json(file_path: &str) -> Result<Self, Box<dyn Error>> {
748 let content = std::fs::read_to_string(file_path)?;
749 Self::from_json_string(&content)
750 }
751
752 fn from_json_string(s: &str) -> Result<Self, Box<dyn Error>> {
753 let (headers, data) = parse_puruda_json(s)?;
754
755 #(#read_decls)*
756
757 #(#read_fills)*
758
759 let mut col = #col_name::from_cols(#(#from_cols_args),*);
760 let header_refs: Vec<&str> = headers.iter().map(|s| s.as_str()).collect();
761 col.set_header(header_refs);
762
763 Ok(col)
764 }
765 }
766 };
767 all.extend(block);
768 }
769
770 all.into()
771}
772
773#[proc_macro]
777pub fn multi_col_describe_impl(_item: TokenStream) -> TokenStream {
778 let mut all = proc_macro2::TokenStream::new();
779
780 for n in 1..=32 {
781 let col_name = col_ident(n);
782 let type_params: Vec<_> = (1..=n).map(type_ident).collect();
783
784 let where_clauses = type_params.iter().map(|t| {
785 quote! { #t: Column + Default }
786 });
787 let display_clauses = type_params.iter().map(|t| {
788 quote! { #t::DType: std::fmt::Display }
789 });
790
791 let describe_cols = (1..=n).map(|j| {
792 let field = field_ident(j);
793 let idx_lit = proc_macro2::Literal::usize_unsuffixed(j - 1);
794 quote! {
795 {
796 let name = if #idx_lit < self.header.len() {
797 self.header[#idx_lit].clone()
798 } else {
799 format!("col_{}", #idx_lit + 1)
800 };
801 let count = self.#field.row();
802 summaries.push((name, count));
803 }
804 }
805 });
806
807 let block = quote! {
808 impl<#(#type_params),*> #col_name<#(#type_params),*>
809 where
810 #(#where_clauses,)*
811 #(#display_clauses,)*
812 {
813 pub fn describe(&self) {
814 let mut summaries: Vec<(String, usize)> = Vec::new();
815 #(#describe_cols)*
816
817 let mut max_name = 4usize; for (name, _) in &summaries {
820 if name.len() > max_name { max_name = name.len(); }
821 }
822 let count_width = 5; println!("{:>nw$} {:>cw$}", "Name", "Count", nw = max_name, cw = count_width);
825 println!("{:->nw$} {:->cw$}", "", "", nw = max_name, cw = count_width);
826 for (name, count) in &summaries {
827 println!("{:>nw$} {:>cw$}", name, count, nw = max_name, cw = count_width);
828 }
829 }
830 }
831 };
832 all.extend(block);
833 }
834
835 all.into()
836}