1#![recursion_limit = "128"]
239extern crate proc_macro;
240
241use proc_macro::TokenStream;
242use std::collections::{BTreeMap, HashMap};
243
244use syn;
245use syn::NestedMeta;
246use std::str::FromStr;
247
248use quote::{quote, ToTokens};
249
250#[proc_macro_derive(CassandraTable, attributes(column, table))]
251pub fn cassandra_macro_derive(input: TokenStream) -> TokenStream {
252 let ast = syn::parse(input).unwrap();
255
256 impl_cassandra_macro(&ast)
258}
259
260fn impl_cassandra_macro(ast: &syn::DeriveInput) -> TokenStream {
261 let table_name = pascal_case_to_snake_case(&ast.ident.to_string());
262
263 let mut table_meta = TableMeta::with_name(&table_name);
264
265 let fields = match ast.data {
267 syn::Data::Struct(syn::DataStruct { ref fields, .. }) => {
268 if fields.iter().any(|field| field.ident.is_none()) {
269 panic!("struct has unnamed fields");
270 }
271 fields.iter().cloned().collect()
272 }
273 _ => panic!("#[derive(CassandraConfig)] can only be used with structs"),
274 };
275
276 extract_struct_attributes(&mut table_meta, &fields);
277
278 for attr in ast.attrs.iter() {
279 match attr.parse_meta() {
280 Ok(syn::Meta::List(syn::MetaList { ref path, ref nested, .. })) => {
281 let ident = path.get_ident().unwrap();
282 match ident.to_string().as_ref() {
283 "table" => {
284 let mut meta_items_iter = nested.iter();
285
286 let mut meta_items = Vec::new();
287
288 while let Some(n) = meta_items_iter.next() {
289 meta_items.push(n);
290 }
291
292 let (key_space, options) = extract_table_properties(&meta_items);
293
294 table_meta.set_key_space(&key_space);
295 table_meta.set_table_options(&options);
296 }
297 _ => {}
298 }
299 }
300 Err(_) => unreachable!(
301 "Got something other than a list of attributes while checking table attribute"),
302 _ => {}
303 }
304 }
305
306 let create_table_sql = table_meta.create_table_cql();
307 let drop_table_sql = table_meta.drop_table_cql();
308 let key_space = table_meta.key_space();
309 let table_name = table_meta.table_name();
310 let select_by_key = table_meta.select_by_key();
311 let select_by_keys = table_meta.select_by_keys();
312
313 let update_by_key = table_meta.update_by_key();
314 let update_by_keys = table_meta.update_by_keys();
315
316 let delete_by_key = table_meta.delete_by_key();
317 let delete_by_keys = table_meta.delete_by_keys();
318
319 let store_stmt = table_meta.store_stmt();
320 let store_values = table_meta.store_values();
321
322 let (update_stmt, update_values) = table_meta.update_stmt()
323 .unwrap_or((String::new(), proc_macro2::TokenStream::new()));
324
325 let (delete_stmt, delete_values) = table_meta.delete_stmt();
326
327 let ident = &ast.ident;
328
329 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
331 let impl_ast = quote!(
332
333 impl #impl_generics CassandraTable for #ident #ty_generics #where_clause {
334
335 fn create_table_cql() -> &'static str {
336 &#create_table_sql
337 }
338
339 fn drop_table_cql() -> &'static str {
340 &#drop_table_sql
341 }
342
343 fn key_space() -> &'static str {
344 &#key_space
345 }
346
347 fn table_name() -> &'static str {
348 &#table_name
349 }
350
351 fn select_by_primary_keys(projection: cassandra_macro::Projection) -> String {
352 match projection {
353 cassandra_macro::Projection::Count => {
354 #select_by_key.to_string().replace("*", "count(*) as count")
355 },
356 cassandra_macro::Projection::All => {
357 #select_by_key.to_string()
358 },
359 cassandra_macro::Projection::Columns(c) => {
360 let column_selection : String = c.join(",");
361
362 #select_by_key.to_string().replace("*", column_selection.as_str())
363 }
364 }
365 }
366
367 fn select_by_primary_and_cluster_keys(projection: cassandra_macro::Projection) -> String {
368 match projection {
369 cassandra_macro::Projection::Count => {
370 #select_by_keys.to_string().replace("*", "count(*) as count")
371 },
372 cassandra_macro::Projection::All => {
373 #select_by_keys.to_string()
374 },
375 cassandra_macro::Projection::Columns(c) => {
376 let column_selection : String = c.join(",");
377
378 #select_by_keys.to_string().replace("*", column_selection.as_str())
379 }
380 }
381 }
382
383 fn update_by_primary_keys(columns: Vec<String>) -> String {
384
385 let update_columns = columns.iter().map(|c| format!(" {}=?", c)).collect::<Vec<String>>().join(",");
386
387 #update_by_key.to_string().replace(":columns", update_columns.as_str())
388 }
389
390 fn update_by_primary_and_cluster_keys(columns: Vec<String>) -> String {
391
392 let update_columns = columns.iter().map(|c| format!(" {}=?", c)).collect::<Vec<String>>().join(",");
393
394 #update_by_keys.to_string().replace(":columns", update_columns.as_str())
395 }
396
397 fn delete_by_primary_keys() -> String {
398 #delete_by_key.to_string()
399 }
400
401 fn delete_by_primary_and_cluster_keys() -> String {
402 #delete_by_keys.to_string()
403 }
404
405 fn store_query(&self) -> cassandra_macro::StoreQuery {
406 cassandra_macro::StoreQuery::new(#store_stmt.to_string(), query_values!(#store_values))
407 }
408
409 fn update_query(&self) -> Result<cassandra_macro::UpdateQuery, cassandra_macro::TableWithNoUpdatableColumnsError>
410 {
411 if #update_stmt.to_string().is_empty() {
412 return Err(cassandra_macro::TableWithNoUpdatableColumnsError::new(format!("Table {} does not have any updatable column", #table_name)) );
413 }
414
415 Ok(cassandra_macro::UpdateQuery::new(#update_stmt.to_string(), query_values!(#update_values)))
416 }
417
418 fn delete_query(&self) -> cassandra_macro::DeleteQuery {
419 cassandra_macro::DeleteQuery::new(#delete_stmt.to_string(), query_values!(#delete_values))
420 }
421
422 }
423 );
424
425 impl_ast.into()
426}
427
428struct TableMeta {
429 name: String,
430 key_space: String,
431 table_options: String,
432 columns: HashMap<String, String>,
433 static_columns: Vec<String>,
434 primary_keys: BTreeMap<u8, String>,
435 cluster_keys: BTreeMap<u8, (String, String)>,
436}
437
438impl TableMeta {
440 fn with_name(name: &String) -> Self {
441 TableMeta {
442 name: name.to_owned(),
443 key_space: String::new(),
444 table_options: String::new(),
445 columns: HashMap::new(),
446 static_columns: Vec::new(),
447 primary_keys: BTreeMap::new(),
448 cluster_keys: BTreeMap::new(),
449 }
450 }
451
452 fn delete_stmt(&self) -> (String, proc_macro2::TokenStream) {
453 let pk_values: Vec<String> = self.primary_keys.values().map(|p| p.to_owned()).collect();
454
455 let ck_values: Vec<String> = self.cluster_keys.values().map(|(c, _)| c.to_owned()).collect();
456
457 let keys: Vec<(String, String)> = [&pk_values[..], &ck_values[..]]
458 .concat()
459 .iter()
460 .map(|c| {
461 (format!("{}=?", c), format!("self.{}.clone()", c))
462 })
463 .collect::<Vec<(String, String)>>();
464
465 (format!("DELETE FROM {}.{} WHERE {}",
466 self.key_space,
467 self.name,
468 keys.iter().map(|(v, _)| v.to_owned())
469 .collect::<Vec<String>>()
470 .join(" AND ")),
471 proc_macro2::TokenStream::from_str(keys.iter().map(|(_, v)| v.to_owned())
472 .collect::<Vec<String>>()
473 .join(",").as_str()).unwrap()
474 )
475 }
476
477 fn update_stmt(&self) -> Option<(String, proc_macro2::TokenStream)> {
478 let mut updatable_columns = Vec::new();
479
480 for (column_name, _) in self.columns.iter() {
481 if self.primary_keys.values().any(|p| p.eq(column_name)) {
482 continue;
483 }
484
485 if self.cluster_keys.values().any(|ck| ck.0.eq(column_name)) {
486 continue;
487 }
488
489 updatable_columns.push(column_name);
490 }
491
492 if updatable_columns.is_empty() {
493 return None;
494 }
495
496 let update_values = updatable_columns.iter().map(|c| {
497 (format!("{}=?", c), format!("self.{}.clone()", c))
498 }).collect::<Vec<(String, String)>>();
499
500 let p_keys = self.primary_keys.iter().map(|(_, pk)| {
501 (format!("{}=?", pk), format!("self.{}.clone()", pk))
502 }).collect::<Vec<(String, String)>>();
503
504 let ck_keys = self.cluster_keys.iter().map(|(_, (ck, _))| {
505 (format!("{}=?", ck), format!("self.{}.clone()", ck))
506 }).collect::<Vec<(String, String)>>();
507
508 let values: String = [&update_values[..], &p_keys[..], &ck_keys[..]]
509 .concat()
510 .iter()
511 .map(|(_, c)| c.to_owned())
512 .collect::<Vec<String>>()
513 .join(",");
514
515 let pk_values: Vec<String> = self.primary_keys.values().map(|p| p.to_owned()).collect();
516
517 let ck_values: Vec<String> = self.cluster_keys.values().map(|(c, _)| c.to_owned()).collect();
518
519 let keys: Vec<(String, String)> = [&pk_values[..], &ck_values[..]]
520 .concat()
521 .iter()
522 .map(|c| {
523 (format!("{}=?", c), format!("self.{}.clone()", c))
524 })
525 .collect::<Vec<(String, String)>>();
526
527 Some((format!("UPDATE {}.{} SET {} WHERE {}",
528 self.key_space,
529 self.name,
530 update_values.iter().map(|(v, _)| v.to_owned()).collect::<Vec<String>>().join(","),
531 keys.iter().map(|(v, _)| v.to_owned()).collect::<Vec<String>>().join(" AND ")),
532 proc_macro2::TokenStream::from_str(values.as_str()).unwrap()
533 ))
534 }
535
536 fn store_stmt(&self) -> String {
537 let fields = self.columns.iter().map(|(n, _)| n.to_owned()).collect::<Vec<String>>().join(",");
538
539 let mut bind_marks = "?,".repeat(self.columns.len());
540 bind_marks.pop();
541
542 format!("INSERT INTO {}.{} ({}) VALUES ({})", self.key_space, self.name, fields, bind_marks)
543 }
544
545 fn store_values(&self) -> proc_macro2::TokenStream {
546 let fields_tokens = self.columns.iter().map(|(v, _)| {
547 format!("self.{}.clone()", v.to_owned())
548 }).collect::<Vec<String>>().join(",");
549
550 proc_macro2::TokenStream::from_str(fields_tokens.as_str()).unwrap()
551 }
552
553 fn set_key_space(&mut self, key_space: &String) {
554 self.key_space = key_space.to_owned();
555 }
556
557 fn select_by_key(&self) -> String {
558 let where_part = self.primary_keys
559 .iter()
560 .map(|(_, v)| format!(" {}=? ", v))
561 .collect::<Vec<String>>()
562 .join("AND");
563
564 format!("SELECT * FROM {}.{} WHERE {}", self.key_space, self.name, where_part)
565 }
566
567 fn select_by_keys(&self) -> String {
568 let pk_select = self.select_by_key();
569
570 if self.cluster_keys.is_empty() {
571 pk_select
572 } else {
573 let where_part = self.cluster_keys
574 .iter()
575 .map(|(_, (c, _))| format!(" {}=? ", c))
576 .collect::<Vec<String>>()
577 .join("AND");
578
579 format!("{} AND {}", pk_select, where_part)
580 }
581 }
582
583 fn update_by_key(&self) -> String {
584 let where_part = self.primary_keys
585 .iter()
586 .map(|(_, v)| format!(" {}=? ", v))
587 .collect::<Vec<String>>()
588 .join("AND");
589
590 format!("UPDATE {}.{} SET :columns WHERE {}", self.key_space, self.name, where_part)
591 }
592
593 fn update_by_keys(&self) -> String {
594 let pk_select = self.update_by_key();
595
596 if self.cluster_keys.is_empty() {
597 pk_select
598 } else {
599 let where_part = self.cluster_keys
600 .iter()
601 .map(|(_, (c, _))| format!(" {}=? ", c))
602 .collect::<Vec<String>>()
603 .join("AND");
604
605 format!("{} AND {}", pk_select, where_part)
606 }
607 }
608
609 fn delete_by_key(&self) -> String {
610 let where_part = self.primary_keys
611 .iter()
612 .map(|(_, v)| format!(" {}=? ", v))
613 .collect::<Vec<String>>()
614 .join("AND");
615
616 format!("DELETE FROM {}.{} WHERE {}", self.key_space, self.name, where_part)
617 }
618
619 fn delete_by_keys(&self) -> String {
620 let pk_select = self.delete_by_key();
621
622 if self.cluster_keys.is_empty() {
623 pk_select
624 } else {
625 let where_part = self.cluster_keys
626 .iter()
627 .map(|(_, (c, _))| format!(" {}=? ", c))
628 .collect::<Vec<String>>()
629 .join("AND");
630
631 format!("{} AND {}", pk_select, where_part)
632 }
633 }
634
635 fn set_table_options(&mut self, table_options: &String) {
636 self.table_options = table_options.to_owned();
637 }
638
639 fn new_column(&mut self, name: &String, data_type: &String) {
640 self.columns.insert(name.to_owned(), data_type.to_owned());
641 }
642
643 fn set_column_as_static(&mut self, name: &String) {
644 self.static_columns.push(name.to_owned());
645 }
646
647 fn new_primary_key(&mut self, key: &String, position: Option<u8>) {
648 self.primary_keys.insert(position.unwrap_or(1), key.to_owned());
649 }
650
651 fn new_cluster_key(&mut self, name: &String, order: &String, position: Option<u8>) {
652 self.cluster_keys.insert(position.unwrap_or(1), (name.to_owned(), order.to_owned()));
653 }
654
655 fn key_space(&self) -> &String {
656 &self.key_space
657 }
658
659 fn table_name(&self) -> &String {
660 &self.name
661 }
662
663 fn drop_table_cql(&self) -> String {
664 format!("DROP TABLE IF EXISTS {}.{}", self.key_space, self.name)
665 }
666
667 fn create_table_cql(&self) -> String {
668 let mut table_options = String::new();
669 let mut c_order = Vec::new();
670 let mut c_keys = Vec::new();
671
672 let columns: String = self.columns
673 .iter()
674 .map(|(k, t)| {
675 if self.static_columns.contains(k) {
676 format!("{} {} STATIC", k, t)
677 } else {
678 format!("{} {}", k, t.to_uppercase())
679 }
680 })
681 .collect::<Vec<String>>()
682 .join(",");
683
684 let opt_parts: Vec<&str> = self.table_options.split("|").filter(|opt| !opt.is_empty()).collect();
685
686 if self.cluster_keys.len() > 0 {
687 for (_, (column, order)) in self.cluster_keys.iter() {
688 c_order.push(format!("{} {}", column, order));
689 c_keys.push(format!("{}", column))
690 }
691 table_options = format!("WITH CLUSTERING ORDER BY ({})", c_order.join(","));
692
693 if opt_parts.len() > 0 {
694 table_options = format!("{} AND {}", table_options, opt_parts.join(" AND "))
695 }
696 } else {
697 if opt_parts.len() > 0 {
698 table_options = format!("WITH {}", opt_parts.join(" AND "))
699 }
700 }
701
702 let primary_keys: String = self.primary_keys
703 .iter()
704 .map(|(_, k)| format!("{}", k))
705 .collect::<Vec<String>>()
706 .join(",");
707
708 let create_stmt = format!("CREATE TABLE IF NOT EXISTS {}.{} ", self.key_space, self.name);
709
710 if c_keys.len() > 0 {
711 format!("{} ({}, PRIMARY KEY (({}), {}) ) {}", create_stmt, columns, primary_keys, c_keys.join(","), table_options)
712 } else {
713 format!("{} ({}, PRIMARY KEY ({}) ) {}", create_stmt, columns, primary_keys, table_options)
714 }
715 }
716}
717
718fn extract_struct_attributes(table_meta: &mut TableMeta, fields: &Vec<syn::Field>) {
720 for field in fields {
721 let field_ident = field.ident.clone().unwrap().to_string();
722
723 if field.attrs.len() > 0 {
724 for attr in &field.attrs {
725 if !attr.path.to_token_stream().to_string().contains("column") {
726 continue;
727 }
728
729 match attr.parse_meta() {
730 Ok(syn::Meta::List(syn::MetaList { ref nested, .. })) => {
731 let mut meta_items_iter = nested.iter();
732
733 let mut meta_items = Vec::new();
734
735 while let Some(n) = meta_items_iter.next() {
736 meta_items.push(n);
737 }
738
739 for meta_item in meta_items {
741 match *meta_item {
742 syn::NestedMeta::Meta(ref item) => match *item {
743 syn::Meta::Path(ref name) => {
744 match name.get_ident().unwrap().to_string().as_ref() {
745 "primary_key" => {
746 table_meta.new_primary_key(&field_ident, None);
747 }
748 "static" => {
749 table_meta.set_column_as_static(&field_ident);
750 }
751 _ => panic!("Unexpected validator: {:?}", name.get_ident()),
752 }
753 }
754 syn::Meta::NameValue(syn::MetaNameValue { ref path, ref lit, .. }) => {
755 let ident = path.get_ident().unwrap();
756 match ident.to_string().as_ref() {
757 "type" => {
758 table_meta.new_column(&field_ident.clone(), &lit_to_string(lit).unwrap_or(String::new()));
759 }
760 v => panic!("unexpected name value validator: {:?}", v),
761 };
762 }
763 syn::Meta::List(syn::MetaList { ref path, ref nested, .. }) => {
764 let mut meta_items_iter = nested.iter();
765
766 let mut meta_items: Vec<&NestedMeta> = Vec::new();
767
768 while let Some(n) = meta_items_iter.next() {
769 meta_items.push(n);
770 }
771
772 let ident = path.get_ident().unwrap();
773 match ident.to_string().as_ref() {
774 "cluster_key" => {
775 let (order, position) = extract_cluster_properties(&meta_items);
776
777 table_meta.new_cluster_key(&field_ident, &order, Some(position));
778 }
779 "compound_key" => {
780 let (_, position) = extract_cluster_properties(&meta_items);
781
782 table_meta.new_primary_key(&field_ident, Some(position))
783 }
784 v => panic!("unexpected list validator: {:?}", v),
785 }
786 }
787 },
788 _ => unreachable!("Found a non Meta while looking for validators"),
789 };
790 }
791 }
792 Ok(syn::Meta::NameValue(_)) => panic!("Unexpected name=value argument"),
793 Err(e) => unreachable!(
794 "Got something other than a list of attributes while checking field `{}`: {:?}",
795 field_ident, e
796 ),
797 _ => {}
798 }
799 }
800 }
801 }
802}
803
804fn lit_to_string(lit: &syn::Lit) -> Option<String> {
805 match *lit {
806 syn::Lit::Str(ref s) => Some(s.value()),
807 _ => None,
808 }
809}
810
811fn lit_to_int(lit: &syn::Lit) -> Option<i64> {
812 match *lit {
813 syn::Lit::Int(ref s) => Some(s.base10_parse().unwrap()),
814 _ => None,
815 }
816}
817
818fn extract_cluster_properties(meta_items: &Vec<&syn::NestedMeta>) -> (String, u8) {
819 let mut order = String::from("DESC");
820 let mut position = 1;
821
822 for meta_item in meta_items {
823 if let syn::NestedMeta::Meta(ref item) = **meta_item {
824 if let syn::Meta::NameValue(syn::MetaNameValue { ref path, ref lit, .. }) = *item {
825 let ident = path.get_ident().unwrap();
826 match ident.to_string().as_ref() {
827 "order" => {
828 order = lit_to_string(lit).unwrap_or(String::from("DESC"))
829 }
830 "position" => {
831 position = lit_to_int(lit).unwrap_or(1) as u8;
832 }
833 v => panic!("unknown argument `{}` for column `cluster_key`", v)
834 }
835 } else {
836 panic!("unexpected item while parsing `cluster_key` column of field")
837 }
838 }
839 }
840
841 (order, position)
842}
843
844fn extract_table_properties(meta_items: &Vec<&syn::NestedMeta>) -> (String, String) {
845 let mut keyspace = String::new();
846 let mut options = String::new();
847
848 for meta_item in meta_items {
849 if let syn::NestedMeta::Meta(ref item) = **meta_item {
850 if let syn::Meta::NameValue(syn::MetaNameValue { ref path, ref lit, .. }) = *item {
851 let ident = path.get_ident().unwrap();
852 match ident.to_string().as_ref() {
853 "keyspace" => {
854 keyspace = lit_to_string(lit).unwrap_or(String::new())
855 }
856 "options" => {
857 options = lit_to_string(lit).unwrap_or(String::new());
858 }
859 v => panic!("unknown argument `{}` for column `table`", v)
860 }
861 } else {
862 panic!("unexpected item while parsing `table` column of field")
863 }
864 }
865 }
866
867 (keyspace, options)
868}
869
870const OFFSET: u8 = 32;
871const UNDERSCORE: u8 = 95;
872
873fn pascal_case_to_snake_case(table_name: &String) -> String {
874 let word_size = table_name.len();
875
876 if word_size < 2 {
877 return String::from(table_name);
878 }
879
880 let mut counter = 1;
881 let chars = table_name.as_bytes();
882 let mut sk_table_name: Vec<u8> = Vec::new();
883
884 if chars[0] < 90 {
885 sk_table_name.push(chars[0] + OFFSET);
886 } else {
887 sk_table_name.push(chars[0]);
888 }
889
890 while counter < word_size {
891 let current = chars[counter];
892
893 if current < 90 {
894 sk_table_name.push(UNDERSCORE);
895 sk_table_name.push(current + OFFSET)
896 } else {
897 sk_table_name.push(current);
898 }
899
900 counter += 1;
901 }
902
903 String::from_utf8(sk_table_name).unwrap()
904}
905
906#[cfg(test)]
907mod tests {
908 use crate::pascal_case_to_snake_case;
909
910 #[test]
911 fn test_pascal_case_to_snake_case() {
912 let table_1 = String::from("Test");
913
914 let new_table_1 = pascal_case_to_snake_case(&table_1);
915
916 assert_eq!(new_table_1, String::from("test"));
917
918 let table_2 = String::from("TestHello");
919
920 let new_table_2 = pascal_case_to_snake_case(&table_2);
921
922 assert_eq!(new_table_2, String::from("test_hello"));
923 }
924}