soa_derive_internal 0.12.0

Internal implementation crate for soa-derive
Documentation
use proc_macro2::TokenStream;
use quote::quote;

use crate::input::{Input, TokenStreamIterator};
use crate::names;

pub fn derive(input: &Input) -> TokenStream {
    let name = &input.name;
    let visibility = &input.visibility;
    let attrs = &input.attrs.ptr;
    let mut_attrs = &input.attrs.ptr_mut;
    let vec_name = names::vec_name(&input.name);
    let ptr_name = names::ptr_name(&input.name);
    let ptr_mut_name = names::ptr_mut_name(&input.name);
    let ref_name = names::ref_name(&input.name);
    let ref_mut_name = names::ref_mut_name(&input.name);

    let doc_url = format!("[`{0}`](struct.{0}.html)", name);
    let ptr_doc_url = format!("[`{0}`](struct.{0}.html)", ptr_name);
    let ptr_mut_doc_url = format!("[`{0}`](struct.{0}.html)", ptr_mut_name);
    let ref_doc_url = format!("[`{0}`](struct.{0}.html)", ref_name);
    let ref_mut_doc_url = format!("[`{0}`](struct.{0}.html)", ref_mut_name);

    let fields_names = input.fields.iter()
                                   .map(|field| field.ident.clone().unwrap())
                                   .collect::<Vec<_>>();
    let fields_names_1 = &fields_names;
    let fields_names_2 = &fields_names;

    let ptr_fields = input.iter_fields().map(
        |(field_ident, field_type, is_nested)| {
            let doc = format!("A pointer to a `{0}` from a [`{1}`](struct.{1}.html)", field_ident, vec_name);
            if is_nested {
                let field_ptr_type = names::ptr_name(field_type);
                quote! {
                    #[doc = #doc]
                    pub #field_ident: #field_ptr_type,
                }
            }
            else {
                quote! {
                    #[doc = #doc]
                    pub #field_ident: *const #field_type,
                }
            }
        },
    ).concat();

    let ptr_mut_fields = input.iter_fields().map(
        |(field_ident, field_type, is_nested)| {
            let doc = format!("A mutable pointer to a `{0}` from a [`{1}`](struct.{1}.html)", field_ident, vec_name);
            if is_nested {
                let field_ptr_mut_type = names::ptr_mut_name(field_type);
                quote! {
                    #[doc = #doc]
                    pub #field_ident: #field_ptr_mut_type,
                }
            }
            else {
                quote! {
                    #[doc = #doc]
                    pub #field_ident: *mut #field_type,
                }
            }
        },
    ).concat();

    let as_mut_ptr = input.iter_fields().map(
        |(field_ident, _, is_nested)| {
            if is_nested {
                quote! {
                    #field_ident: self.#field_ident.as_mut_ptr(),
                }
            }
            else {
                quote! {
                    #field_ident: self.#field_ident as *mut _,
                }
            }
        },
    ).concat();

    let as_ptr = input.iter_fields().map(
        |(field_ident, _, is_nested)| {
            if is_nested {
                quote! {
                    #field_ident: self.#field_ident.as_ptr(),
                }
            }
            else {
                quote! {
                    #field_ident: self.#field_ident,
                }
            }
        },
    ).concat();

    quote! {
        /// An analog of a pointer to
        #[doc = #doc_url]
        /// with struct of array layout.
        #(#[#attrs])*
        #[derive(Copy, Clone)]
        #visibility struct #ptr_name {
            #ptr_fields
        }

        /// An analog of a mutable pointer to
        #[doc = #doc_url]
        /// with struct of array layout.
        #(#[#mut_attrs])*
        #[derive(Copy, Clone)]
        #visibility struct #ptr_mut_name {
            #ptr_mut_fields
        }

        #[allow(dead_code)]
        impl #ptr_name {
            /// Convert a
            #[doc = #ptr_doc_url]
            /// to a
            #[doc = #ptr_mut_doc_url]
            /// ; *i.e.* do a `*const T as *mut T` transformation.
            #visibility fn as_mut_ptr(&self) -> #ptr_mut_name {
                #ptr_mut_name {
                    #as_mut_ptr
                }
            }

            /// Similar to [`*const T::is_null()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.is_null).
            pub fn is_null(self) -> bool {
                false #( || self.#fields_names_1.is_null())*
            }

            /// Similar to [`*const T::as_ref()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.as_ref),
            /// with the same safety caveats.
            pub unsafe fn as_ref<'a>(self) -> Option<#ref_name<'a>> {
                if self.is_null() {
                    None
                } else {
                    Some(#ref_name {
                        #(#fields_names_1: self.#fields_names_2.as_ref().expect("should not be null"), )*
                    })
                }
            }

            /// Similar to [`*const T::offset()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.offset),
            /// with the same safety caveats.
            pub unsafe fn offset(self, count: isize) -> #ptr_name {
                #ptr_name {
                    #(#fields_names_1: self.#fields_names_2.offset(count), )*
                }
            }

            /// Similar to [`*const T::offset()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.offset).
            pub fn wrapping_offset(self, count: isize) -> #ptr_name {
                #ptr_name {
                    #(#fields_names_1: self.#fields_names_2.wrapping_offset(count), )*
                }
            }

            /// Similar to [`*const T::add()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.add),
            /// with the same safety caveats.
            pub unsafe fn add(self, count: usize) -> #ptr_name {
                #ptr_name {
                    #(#fields_names_1: self.#fields_names_2.add(count), )*
                }
            }

            /// Similar to [`*const T::sub()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.sub),
            /// with the same safety caveats.
            pub unsafe fn sub(self, count: usize) -> #ptr_name {
                #ptr_name {
                    #(#fields_names_1: self.#fields_names_2.sub(count), )*
                }
            }

            /// Similar to [`*const T::wrapping_add()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.wrapping_add).
            pub fn wrapping_add(self, count: usize) -> #ptr_name {
                #ptr_name {
                    #(#fields_names_1: self.#fields_names_2.wrapping_add(count), )*
                }
            }

            /// Similar to [`*const T::wrapping_sub()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.wrapping_sub).
            pub fn wrapping_sub(self, count: usize) -> #ptr_name {
                #ptr_name {
                    #(#fields_names_1: self.#fields_names_2.wrapping_sub(count), )*
                }
            }

            /// Similar to [`*const T::read()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.read),
            /// with the same safety caveats.
            pub unsafe fn read(self) -> #name {
                #name {
                    #(#fields_names_1: self.#fields_names_2.read(), )*
                }
            }

            /// Similar to [`*const T::read_volatile()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.read_volatile),
            /// with the same safety caveats.
            pub unsafe fn read_volatile(self) -> #name {
                #name {
                    #(#fields_names_1: self.#fields_names_2.read_volatile(), )*
                }
            }

            /// Similar to [`*const T::read_unaligned()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.read_unaligned),
            /// with the same safety caveats.
            pub unsafe fn read_unaligned(self) -> #name {
                #name {
                    #(#fields_names_1: self.#fields_names_2.read_unaligned(), )*
                }
            }
        }

        #[allow(dead_code)]
        impl #ptr_mut_name {
            /// Convert a
            #[doc = #ptr_mut_doc_url]
            /// to a
            #[doc = #ptr_doc_url]
            /// ; *i.e.* do a `*mut T as *const T` transformation
            #visibility fn as_ptr(&self) -> #ptr_name {
                #ptr_name {
                    #as_ptr
                }
            }

            /// Similar to [`*mut T::is_null()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.is_null).
            pub fn is_null(self) -> bool {
                false #( || self.#fields_names_1.is_null())*
            }

            /// Similar to [`*mut T::as_ref()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.as_ref),
            /// with the same safety caveats.
            pub unsafe fn as_ref<'a>(self) -> Option<#ref_name<'a>> {
                if self.is_null() {
                    None
                } else {
                    Some(#ref_name {
                        #(#fields_names_1: self.#fields_names_2.as_ref().expect("should not be null"), )*
                    })
                }
            }

            /// Similar to [`*mut T::as_mut()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.as_mut),
            /// with the same safety caveats.
            pub unsafe fn as_mut<'a>(self) -> Option<#ref_mut_name<'a>> {
                if self.is_null() {
                    None
                } else {
                    Some(#ref_mut_name {
                        #(#fields_names_1: self.#fields_names_2.as_mut().expect("should not be null"), )*
                    })
                }
            }

            /// Similar to [`*mut T::offset()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.offset),
            /// with the same safety caveats.
            pub unsafe fn offset(self, count: isize) -> #ptr_mut_name {
                #ptr_mut_name {
                    #(#fields_names_1: self.#fields_names_2.offset(count), )*
                }
            }

            /// Similar to [`*mut T::wrapping_offset()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.wrapping_offset)
            pub fn wrapping_offset(self, count: isize) -> #ptr_mut_name {
                #ptr_mut_name {
                    #(#fields_names_1: self.#fields_names_2.wrapping_offset(count), )*
                }
            }

            /// Similar to [`*mut T::add()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.add),
            /// with the same safety caveats.
            pub unsafe fn add(self, count: usize) -> #ptr_mut_name {
                #ptr_mut_name {
                    #(#fields_names_1: self.#fields_names_2.add(count), )*
                }
            }

            /// Similar to [`*mut T::sub()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.sub),
            /// with the same safety caveats.
            pub unsafe fn sub(self, count: usize) -> #ptr_mut_name {
                #ptr_mut_name {
                    #(#fields_names_1: self.#fields_names_2.sub(count), )*
                }
            }

            /// Similar to [`*mut T::wrapping_add()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.wrapping_add),
            /// with the same safety caveats.
            pub fn wrapping_add(self, count: usize) -> #ptr_mut_name {
                #ptr_mut_name {
                    #(#fields_names_1: self.#fields_names_2.wrapping_add(count), )*
                }
            }

            /// Similar to [`*mut T::wrapping_sub()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.wrapping_sub),
            /// with the same safety caveats.
            pub fn wrapping_sub(self, count: usize) -> #ptr_mut_name {
                #ptr_mut_name {
                    #(#fields_names_1: self.#fields_names_2.wrapping_sub(count), )*
                }
            }

            /// Similar to [`*mut T::read()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.read),
            /// with the same safety caveats.
            pub unsafe fn read(self) -> #name {
                #name {
                    #(#fields_names_1: self.#fields_names_2.read(), )*
                }
            }

            /// Similar to [`*mut T::read_volatile()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.read_volatile),
            /// with the same safety caveats.
            pub unsafe fn read_volatile(self) -> #name {
                #name {
                    #(#fields_names_1: self.#fields_names_2.read_volatile(), )*
                }
            }

            /// Similar to [`*mut T::read_unaligned()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.read_unaligned),
            /// with the same safety caveats.
            pub unsafe fn read_unaligned(self) -> #name {
                #name {
                    #(#fields_names_1: self.#fields_names_2.read_unaligned(), )*
                }
            }

            /// Similar to [`*mut T::write()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.write),
            /// with the same safety caveats.
            pub unsafe fn write(self, val: #name) {
                #(self.#fields_names_1.write(val.#fields_names_2); )*
            }

            /// Similar to [`*mut T::write_volatile()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.write_volatile),
            /// with the same safety caveats.
            pub unsafe fn write_volatile(self, val: #name) {
                #(self.#fields_names_1.write_volatile(val.#fields_names_2); )*
            }

            /// Similar to [`*mut T::write_unaligned()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.write_unaligned),
            /// with the same safety caveats.
            pub unsafe fn write_unaligned(self, val: #name) {
                #(self.#fields_names_1.write_unaligned(val.#fields_names_2); )*
            }
        }

        #[allow(dead_code)]
        impl<'a> #ref_name<'a> {
            /// Convert a
            #[doc = #ref_doc_url]
            /// to a
            #[doc = #ptr_doc_url]
            /// ; *i.e.* do a `&T as *const T` transformation
            #visibility fn as_ptr(&self) -> #ptr_name {
                #ptr_name {
                    #as_ptr
                }
            }
        }

        #[allow(dead_code)]
        impl<'a> #ref_mut_name<'a> {
            /// Convert a
            #[doc = #ref_mut_doc_url]
            /// to a
            #[doc = #ptr_doc_url]
            /// ; *i.e.* do a `&mut T as *const T` transformation
            #visibility fn as_ptr(&self) -> #ptr_name {
                #ptr_name {
                    #as_ptr
                }
            }

            /// Convert a
            #[doc = #ref_mut_doc_url]
            /// to a
            #[doc = #ptr_mut_doc_url]
            /// ; *i.e.* do a `&mut T as *mut T` transformation
            #visibility fn as_mut_ptr(&mut self) -> #ptr_mut_name {
                #ptr_mut_name {
                    #as_mut_ptr
                }
            }
        }
    }
}