1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
//! Macros for use with the timescale crate //! //! This crate is re-exported by timescale and should almost never be used alone #![forbid(unsafe_code)] #![warn(missing_docs)] use darling::FromDeriveInput; use derive::{interpolated_data, interpolated_data_table, timescale_derive}; use proc_macro::TokenStream; use syn::{parse_macro_input, DeriveInput, ItemStruct}; mod derive; mod store; /// Load a csv file into a static data table with support for linearly interpolating /// data points between the discreet time intervals /// /// This derive macro goes hand and hand with the [`InterpolatedData`] derive /// macro. Data loaded into this table needs to be decorated with the aforementioned /// [`InterpolatedData`] derive macro so that their structure can be available to /// this macro at build time. /// /// # Arguments /// This macro requires 2 arguments. /// - `st` describes the structure (that derives [`InterpolatedData`]) to represent /// the timescale data. /// - `file` describes the csv file to read in the timescale data from /// /// # Example /// See the data in this [csv file] for context for the following example /// /// ``` /// # use lerp::Lerp; /// use timescale::{InterpolatedData, InterpolatedDataTable}; /// /// #[derive(Debug, Lerp, InterpolatedData)] /// pub struct RocketEngine { /// #[data(rename = "Thrust (N)")] /// pub thrust: f64, /// } /// /// /// The thrust curve of an Estes A8 rocket motor /// #[derive(InterpolatedDataTable)] /// #[table(file = "../assets/motors/Estes_A8.csv", st = "RocketEngine")] /// pub struct EstesA8; /// /// fn main() { /// assert_eq!(EstesA8::get(0.35).thrust, 3.813); // Exact value from data /// assert_eq!(EstesA8::get(0.4).thrust, 3.9468823529411763); // Linear interpolated estimate /// assert_eq!(EstesA8::get(1.0).thrust, 0.0); // Saturated at 0 /// } /// ``` /// /// [csv file]: https://github.com/DusterTheFirst/preflight/blob/master/assets/motors/Estes_A8.csv #[proc_macro_derive(InterpolatedDataTable, attributes(table))] pub fn derive_interpolated_data_table(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); match FromDeriveInput::from_derive_input(&input) { Err(err) => err.write_errors(), Ok(args) => { interpolated_data_table::derive(args).unwrap_or_else(|err| err.to_compile_error()) } } .into() } /// Load the layout of a structure to be consumed by [`InterpolatedDataTable`] /// /// This structure is useless on its own and produces no useable output. This macro /// exists to provide compile-time information about the struct it marks to the /// [`InterpolatedDataTable`] macro /// /// # Arguments /// This macro allows for 1 argument to be provided on either the structure itself /// or on the fields of the structure. /// - `rename` allows you to set the column name as it appears in the csv file /// different than the name of the field in the rust structure. If applied to the /// structure itself, it will rename the time column's header from the default /// `Time (s)` /// /// # The Nitty Gritty /// This macro exists because macros are not able to transverse outside of their /// given token tree (easily). This macro will fill up a HashMap in memory with /// the layouts of any structs marked with this macro which will be searched through /// when the [`InterpolatedDataTable`] macro generates its table at compile time. /// Yes, this derive macro could be an attribute macro, but keeping it as a derive /// macro allows for consistency with the other macros and stops the macro from /// consuming the structure provided. #[proc_macro_derive(InterpolatedData, attributes(data))] pub fn interpolated_data_loader(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); match FromDeriveInput::from_derive_input(&input) { Err(err) => err.write_errors(), Ok(args) => interpolated_data::derive(args).unwrap_or_else(|err| err.to_compile_error()), } .into() } /// Automatically derive the `ToTimescale` trait and generate the expanded /// timescale structure. /// /// # Example /// ```no_run /// use nalgebra::Vector3; /// use timescale::ToTimescale; /// /// #[derive(Debug, Clone, ToTimescale)] /// struct VectorDatapoint { /// position: Vector3<f64>, /// velocity: Vector3<f64>, /// acceleration: Vector3<f64>, /// net_force: Vector3<f64>, /// } /// ``` #[proc_macro_derive(ToTimescale)] pub fn timescale_derive(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as ItemStruct); timescale_derive::derive(input) .unwrap_or_else(|err| err.to_compile_error()) .into() }