Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ license = "Apache-2.0"
name = "scaffolding-core"
readme = "README.md"
repository = "https://github.com/dsietz/scaffolding-core"
version = "0.0.2"
version = "0.0.3"

[badges]
maintenance = {status = "experimental"}
Expand All @@ -26,7 +26,7 @@ path = "src/lib.rs"

[dependencies]
chrono = "0.4.35"
scaffolding-macros = {path = "./scaffolding-macros", version = "0.0.2"}
scaffolding-macros = {path = "./scaffolding-macros", version = "0.0.3"}

[dependencies.uuid]
features = ["v4"]
Expand Down
2 changes: 1 addition & 1 deletion scaffolding-macros/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "scaffolding-macros"
version = "0.0.2"
version = "0.0.3"
authors = ["dsietz <davidsietz@yahoo.com>"]
edition = "2021"
readme = "README.md"
Expand Down
45 changes: 43 additions & 2 deletions scaffolding-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,19 @@ use quote::quote;
use syn::parse::{Parse, ParseStream, Parser, Result};
use syn::{parse, parse_macro_input, punctuated::Punctuated, ItemStruct, LitStr, Token};

static METADATA: &str = "metadata";

#[proc_macro_attribute]
pub fn as_entity(args: TokenStream, input: TokenStream) -> TokenStream {
pub fn scaffolding_entity(args: TokenStream, input: TokenStream) -> TokenStream {
// println!("attr: \"{}\"", args.to_string());

let mut item_struct = parse_macro_input!(input as ItemStruct);
let _ = parse_macro_input!(args as parse::Nothing);
let attrs = parse_macro_input!(args as Args)
.vars
.iter()
.map(|a| a.value())
.collect::<Vec<_>>();
// println!("metadata: {:?}", attrs.contains(&"metadata".to_string()));

if let syn::Fields::Named(ref mut fields) = item_struct.fields {
// The unique identifier of the object
Expand Down Expand Up @@ -39,6 +48,18 @@ pub fn as_entity(args: TokenStream, input: TokenStream) -> TokenStream {
.parse2(quote! { expired_dtm: i64 })
.unwrap(),
);

match attrs.contains(&METADATA.to_string()) {
true => {
// The metadata handler
fields.named.push(
syn::Field::parse_named
.parse2(quote! { metadata: BTreeMap<String, String> })
.unwrap(),
);
}
false => {}
}
}

return quote! {
Expand All @@ -47,6 +68,26 @@ pub fn as_entity(args: TokenStream, input: TokenStream) -> TokenStream {
.into();
}

// #[proc_macro_attribute]
// pub fn scaffolding_metadata(args: TokenStream, input: TokenStream) -> TokenStream {
// let mut item_struct = parse_macro_input!(input as ItemStruct);
// let _ = parse_macro_input!(args as parse::Nothing);

// if let syn::Fields::Named(ref mut fields) = item_struct.fields {
// // The unique identifier of the object
// fields.named.push(
// syn::Field::parse_named
// .parse2(quote! { metadata: BTreeMap::new() })
// .unwrap(),
// );
// }

// return quote! {
// #item_struct
// }
// .into();
// }

#[derive(Debug)]
struct Args {
pub vars: Vec<LitStr>,
Expand Down
4 changes: 3 additions & 1 deletion scaffolding-macros/tests/macro_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ extern crate scaffolding_macros;
#[cfg(test)]
mod tests {
use scaffolding_macros::*;
use std::collections::BTreeMap;

#[as_entity]
#[scaffolding_entity("metadata")]
#[derive(Debug, Clone)]
struct MyEntity {
b: bool,
Expand All @@ -19,6 +20,7 @@ mod tests {
modified_dtm: 1711281509,
inactive_dtm: 1711281509,
expired_dtm: 1711281509,
metadata: BTreeMap::new(),
b: true,
};
println!("struct is {:?}", entity);
Expand Down
117 changes: 117 additions & 0 deletions src/defaults.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
//! The defaults module provides the methods for creating deafult values
//! for the Scaffolding common attributes

use chrono::{DateTime, Duration, Months, Utc};
use uuid::Uuid;

/// generates a uuid v4 value
///
/// ```rust
/// use scaffolding_core::defaults::*;
///
/// assert_eq!(id().len(), "54324f57-9e6b-4142-b68d-1d4c86572d0a".len());
/// ```
pub fn id() -> String {
Uuid::new_v4().to_string()
}

/// adds x days to the timestamp
///
/// ```rust
/// use scaffolding_core::defaults::*;
///
/// assert_eq!(add_days(1711295319, 1), 1711381719);
/// ```
pub fn add_days(dtm: i64, days: i64) -> i64 {
let dt = DateTime::from_timestamp(dtm, 0).unwrap() + Duration::try_days(days).unwrap();
dt.timestamp()
}

/// adds x months to the timestamp
///
/// ```rust
/// use scaffolding_core::defaults::*;
///
/// assert_eq!(add_months(1711295319, 1), 1713973719);
/// ```
pub fn add_months(dtm: i64, months: u32) -> i64 {
let dt = DateTime::from_timestamp(dtm, 0).unwrap() + Months::new(months);
dt.timestamp()
}

/// adds x years to the timestamp
///
/// ```rust
/// use scaffolding_core::defaults::*;
///
/// assert_eq!(add_years(1711295319, 1), 1742831319);
/// ```
pub fn add_years(dtm: i64, years: u32) -> i64 {
let dt = DateTime::from_timestamp(dtm, 0).unwrap() + Months::new(years * 12);
dt.timestamp()
}

/// provided the default unix epoch time (UTC) as seconds
/// for the timestamp: 9999-12-31 23:59:59
///
/// ```rust
/// use scaffolding_core::defaults::*;
///
/// assert_eq!(never(), 253402261199);
/// ```
pub fn never() -> i64 {
253402261199
}

/// generate the current unix epoch time (UTC) as seconds
///
/// ```rust
/// use chrono::Utc;
/// use scaffolding_core::defaults::*;
///
/// assert_eq!(now(), Utc::now().timestamp());
/// ```
pub fn now() -> i64 {
Utc::now().timestamp()
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_id() {
assert_eq!(id().len(), "54324f57-9e6b-4142-b68d-1d4c86572d0a".len());
}

#[test]
fn test_add_days() {
assert_eq!(add_days(1711295319, 1), 1711381719);
}

#[test]
fn test_add_months() {
assert_eq!(add_months(1711295319, 1), 1713973719);
// test for a leap year
// 2023-1-29 +1 = 2023-2-28
assert_eq!(add_months(1674993600, 1), 1677585600);
}

#[test]
fn test_add_years() {
assert_eq!(add_years(1711295319, 1), 1742831319);
// test for a leap year
// 2024-2-29 +1 = 2025-2-28
assert_eq!(add_years(1709208000, 1), 1740744000);
}

#[test]
fn test_never() {
assert_eq!(never(), 253402261199);
}

#[test]
fn test_now() {
assert_eq!(now(), Utc::now().timestamp());
}
}
61 changes: 13 additions & 48 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@
//! `extend` a class - both data structure and behavior.
//!
//! ## Scaffolding Concept
//! I. A class that `extends` the "Scaffolding class" should inherate all the "parent" data structure and behavior,
//! 1. A class that `extends` the "Scaffolding class" should inherate all the "parent" data structure and behavior,
//! as well as append the "child" specific data structure and behavior
//! II. The developer should have the flexibility to adopt the default "parent" characteristics or overwrite them as desired.
//! III. There are common class attributes that are required in order to manage it using CRUD
//! 2. The developer should have the flexibility to adopt the default "parent" characteristics or overwrite them as desired.
//! 3. There are common class attributes that are required in order to manage it using CRUD
//! + `id` - The unique identifier of the object.
//! + `created_dtm` - The unix epoch (UTC) representation of when the object was created
//! + `modified_dtm` - The unix epoch (UTC) representation of when the object was last updated
//! + `inactive_dtm` - The unix epoch (UTC) representation of when the object was/will be considered obsolete
//! + `expired_dtm` - The unix epoch (UTC) representation of when the object was/will be ready for deletion
//! IV. There is common class behaviors that are required in order to manage it using CRUD
//! 4. There is common class behaviors that are required in order to manage it using CRUD
//! + The `id` is not optional. It must be either provided or automatically generated during instantiation.
//! This can be done by calling the `Scaffolding` trait's `id()` method
//! + The `created_dtm` is not optional. It must be either provided or automatically generated during instantiation.
Expand All @@ -34,10 +34,10 @@
//! ```rust
//! extern crate scaffolding_core;
//!
//! use scaffolding_core::*;
//! use scaffolding_core::{defaults, Scaffolding};
//! use scaffolding_macros::*;
//!
//! #[as_entity]
//! #[scaffolding_entity]
//! #[derive(Debug, Clone, Scaffolding)]
//! struct MyEntity {
//! b: bool,
Expand All @@ -46,11 +46,11 @@
//! impl MyEntity {
//! fn new(arg: bool) -> Self {
//! Self {
//! id: <Self as Scaffolding>::id(),
//! created_dtm: <Self as Scaffolding>::now(),
//! modified_dtm: <Self as Scaffolding>::now(),
//! inactive_dtm: <Self as Scaffolding>::add_months(<Self as Scaffolding>::now(), 12),
//! expired_dtm: <Self as Scaffolding>::add_years(<Self as Scaffolding>::now(), 3),
//! id: defaults::id(),
//! created_dtm: defaults::now(),
//! modified_dtm: defaults::now(),
//! inactive_dtm: defaults::add_months(defaults::now(), 12),
//! expired_dtm: defaults::add_years(defaults::now(), 3),
//! b: arg,
//! }
//! }
Expand All @@ -70,42 +70,7 @@
//! assert_eq!(entity.my_func(), "my function");
//! ```

use chrono::{DateTime, Duration, Months, Utc};
use uuid::Uuid;

/// The core behavior of an Scaffolding object
pub trait Scaffolding {
/// generates a uuid v4 value
fn id() -> String {
Uuid::new_v4().to_string()
}

/// adds x days to the timestamp
fn add_days(dtm: i64, days: i64) -> i64 {
let dt = DateTime::from_timestamp(dtm, 0).unwrap() + Duration::try_days(days).unwrap();
dt.timestamp()
}

/// adds x months to the timestamp
fn add_months(dtm: i64, months: u32) -> i64 {
let dt = DateTime::from_timestamp(dtm, 0).unwrap() + Months::new(months);
dt.timestamp()
}

/// adds x years to the timestamp
fn add_years(dtm: i64, years: u32) -> i64 {
let dt = DateTime::from_timestamp(dtm, 0).unwrap() + Months::new(years * 12);
dt.timestamp()
}

/// provided the default unix epoch time (UTC) as seconds
/// for the timestamp: 9999-12-31 23:59:59
fn never() -> i64 {
253402261199
}
pub trait Scaffolding {}

/// generate the current unix epoch time (UTC) as seconds
fn now() -> i64 {
Utc::now().timestamp()
}
}
pub mod defaults;
26 changes: 11 additions & 15 deletions tests/integration_test.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
// #[macro_use]
extern crate scaffolding_core;
extern crate scaffolding_macros;

#[cfg(test)]
mod tests {
use chrono::Utc;
use scaffolding_core::*;
use scaffolding_core::{defaults, Scaffolding};
use scaffolding_macros::*;
use std::collections::BTreeMap;

#[as_entity]
#[scaffolding_entity("metadata")]
#[derive(Debug, Clone, Scaffolding)]
struct MyEntity {
b: bool,
Expand All @@ -17,13 +19,14 @@ mod tests {
impl MyEntity {
fn new(arg: bool) -> Self {
Self {
id: <Self as Scaffolding>::id(),
created_dtm: <Self as Scaffolding>::now(),
modified_dtm: <Self as Scaffolding>::now(),
inactive_dtm: <Self as Scaffolding>::add_days(<Self as Scaffolding>::now(), 90),
expired_dtm: <Self as Scaffolding>::add_years(<Self as Scaffolding>::now(), 3),
id: defaults::id(),
created_dtm: defaults::now(),
modified_dtm: defaults::now(),
inactive_dtm: defaults::add_days(defaults::now(), 90),
expired_dtm: defaults::add_years(defaults::now(), 3),
metadata: BTreeMap::new(),
b: arg,
n: <Self as Scaffolding>::never(),
n: defaults::never(),
}
}

Expand All @@ -32,13 +35,6 @@ mod tests {
}
}

// #[test]
// fn test_entity_hello() {
// let mut entity = MyEntity::new(true);
// entity.hello();
// assert_eq!(entity.my_func(), "my function");
// }

#[test]
fn test_entity_new() {
let now = Utc::now().timestamp();
Expand Down