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
@@ -1,6 +1,6 @@
[package]
name = "scaffolding-core"
version = "0.3.0"
version = "0.4.0"
authors = ["dsietz <davidsietz@yahoo.com>"]
categories = ["development-tools"]
description = "A software development kit that provides the scaffolding for building applications and services."
Expand All @@ -26,7 +26,7 @@ path = "src/lib.rs"

[dependencies]
chrono = "0.4.35"
scaffolding-macros = {path = "./scaffolding-macros", version = "0.3.0"}
scaffolding-macros = {path = "./scaffolding-macros", version = "0.4.0"}
serde = "1.0.197"
serde_derive = "1.0"
serde_json = "1.0"
Expand Down
108 changes: 106 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ For software development teams who appreciate a kick-start to their object orien
- [Scaffolding Core](#scaffolding-core)
- [Table of Contents](#table-of-contents)
- [What's New](#whats-new)
- [Usage](#usage)
- [How to Contribute](#how-to-contribute)
- [License](#license)

Expand All @@ -23,8 +24,111 @@ For software development teams who appreciate a kick-start to their object orien
| ----------------------------------------------------------------------------- |
| This crate is in an `beta` release phase and is only intended as experimental.|

**0.3.0**
+ [Add Tagging](https://github.com/dsietz/scaffolding-core/issues/20)
**0.4.0**
+ [Provide the ability to manage notes](https://github.com/dsietz/scaffolding-core/issues/29)

## Usage
Add Scaffolding to a `struct` and `impl` `::new()` using macros and defaults

```rust
extern crate scaffolding_core;

use scaffolding_core::{defaults, ActivityItem, Note, Scaffolding, ScaffoldingNotes, ScaffoldingTags};
use scaffolding_macros::*;
use serde_derive::{Deserialize, Serialize};
// Required for scaffolding metadata functionality
use std::collections::BTreeMap;

// (1) Define the structure - Required
#[scaffolding_struct("metadata","notes","tags")]
#[derive(Debug, Clone, Deserialize, Serialize, Scaffolding, ScaffoldingNotes, ScaffoldingTags)]
struct MyEntity {
a: bool,
b: String,
}

impl MyEntity {
// (2) Define the constructor - Optional
// Note: Any of the Scaffodling attributes that are set here
// will not be overwritten when generated. For example
// the `id` attribute, if uncommented, would be ignored.
#[scaffolding_fn("metadata","notes","tags")]
fn new(arg: bool) -> Self {
let msg = format!("You said it is {}", arg);
Self {
// id: "my unique identitifer".to_string(),
a: arg,
b: msg
}
}

fn my_func(&self) -> String {
"my function".to_string()
}
}

let mut entity = MyEntity::new(true);

/* scaffolding attributes */
assert_eq!(entity.id.len(), "54324f57-9e6b-4142-b68d-1d4c86572d0a".len());
assert_eq!(entity.created_dtm, defaults::now());
assert_eq!(entity.modified_dtm, defaults::now());
// becomes inactive in 90 days
assert_eq!(entity.inactive_dtm, defaults::add_days(defaults::now(), 90));
// expires in 3 years
assert_eq!(entity.expired_dtm, defaults::add_years(defaults::now(), 3));

/* use the activity log functionality */
// (1) Log an activity
entity.log_activity("cancelled".to_string(), "The customer has cancelled their service".to_string());
// (2) Get activities
assert_eq!(entity.get_activity("cancelled".to_string()).len(), 1);

/* use the notes functionality */
// (1) Insert a note
let note_id = entity.insert_note(
"fsmith".to_string(),
"This was updated".as_bytes().to_vec(),
None,
);
// (2) Modify the note
entity.modify_note(
note_id.clone(),
"fsmith".to_string(),
"This was updated again".as_bytes().to_vec(),
Some("private".to_string()),
);
// (3) Read the note's content
let read_note = entity.get_note(note_id.clone()).unwrap().content_as_string().unwrap();
println!("{}", read_note);
// (4) Search for notes that contain the word `updated`
let search_results = entity.search_notes("updated".to_string());
assert_eq!(search_results.len(), 1);
// (5) Delete the note
entity.remove_note(note_id);

/* use the metadata functionality
Note: `memtadata` is a BTreeMap<String, String>
https://doc.rust-lang.org/std/collections/struct.BTreeMap.html
*/
entity.metadata.insert("field_1".to_string(), "myvalue".to_string());
assert_eq!(entity.metadata.len(), 1);

// manage tags
entity.add_tag("tag_1".to_string());
entity.add_tag("tag_2".to_string());
entity.add_tag("tag_3".to_string());
assert!(entity.has_tag("tag_1".to_string()));
entity.remove_tag("tag_2".to_string());
assert_eq!(entity.tags.len(), 2);

/* extended attributes */
assert_eq!(entity.a, true);
assert_eq!(entity.b, "You said it is true");

/* extended behavior */
assert_eq!(entity.my_func(), "my function");
```

## How to Contribute

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.3.0"
version = "0.4.0"
authors = ["dsietz <davidsietz@yahoo.com>"]
edition = "2021"
readme = "README.md"
Expand Down
11 changes: 5 additions & 6 deletions scaffolding-macros/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,11 @@
- [What's New](#whats-new)

---
:exclamation: This crate is published as a dependency for the [scaffolding-core](https://crates.io/crates/scaffolding-core) crate.

## What's New
| :warning: Please Note! |
| ----------------------------------------------------------------------------- |
| This crate is published as a dependency for the [scaffolding-core](https://crates.io/crates/scaffolding-core) crate. |

This crate is in an `Initial` release phase and is only intended as a PoC.


**0.0.1**
+ [Initial Release](https://github.com/dsietz/class-scaffolding/issues/1)
**0.4.0**
+ [Provide the ability to manage notes](https://github.com/dsietz/scaffolding-core/issues/29)
88 changes: 85 additions & 3 deletions scaffolding-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use syn::{parse_macro_input, parse_quote, punctuated::Punctuated, ItemStruct, Li
// use serde::Serialize;

static METADATA: &str = "metadata";
static NOTES: &str = "notes";
static TAGS: &str = "tags";
static CORE_ATTRS: [&str; 6] = [
"id",
Expand Down Expand Up @@ -82,6 +83,19 @@ pub fn scaffolding_struct(args: TokenStream, input: TokenStream) -> TokenStream
false => {}
}

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

// optional attributes
match attrs.contains(&TAGS.to_string()) {
true => {
Expand Down Expand Up @@ -144,14 +158,70 @@ fn impl_scaffolding(ast: &syn::DeriveInput) -> TokenStream {
};
gen.into()
}

// Notes Trait
#[proc_macro_derive(ScaffoldingNotes)]
pub fn scaffolding_notes_derive(input: TokenStream) -> TokenStream {
let ast: syn::DeriveInput = syn::parse(input).unwrap();

impl_scaffolding_notes(&ast)
}

fn impl_scaffolding_notes(ast: &syn::DeriveInput) -> TokenStream {
let name = &ast.ident;
let gen = quote! {
impl ScaffoldingNotes for #name {
fn get_note(&self, id: String) -> Option<&Note> {
self.notes.get(&id)
}

fn insert_note(&mut self, auth: String, cont: Vec<u8>, acc: Option<String>) -> String {
let note = Note::new(auth, cont, acc);
let id = note.id.clone();
self.notes.insert(id.clone(), note);
id
}

fn modify_note(&mut self, id: String, auth: String, cont: Vec<u8>, acc: Option<String>) {
self.notes
.entry(id)
.and_modify(|note|
note.update(auth, cont, acc)
);
}

fn search_notes(&mut self, search: String) -> Vec<Note> {
let mut results: Vec<Note> = Vec::new();

for (key, note) in self.notes.iter() {
let mut cont = String::from_utf8(note.content.clone())
.map_err(|non_utf8| String::from_utf8_lossy(non_utf8.as_bytes()).into_owned())
.unwrap();

match cont.contains(&search) {
true => {
results.push(note.clone())
},
false => {},
}
}

results
}

fn remove_note(&mut self, id: String) {
self.notes.remove(&id);
}
}
};
gen.into()
}

// Tagging Trait
#[proc_macro_derive(ScaffoldingTags)]
pub fn scaffolding_tags_derive(input: TokenStream) -> TokenStream {
// Construct a representation of Rust code as a syntax tree
// that we can manipulate
let ast: syn::DeriveInput = syn::parse(input).unwrap();

// Build the trait implementation
impl_scaffolding_tags(&ast)
}

Expand Down Expand Up @@ -230,6 +300,13 @@ pub fn scaffolding_fn(args: TokenStream, input: TokenStream) -> TokenStream {
_ => {}
}

match attrs.contains(&NOTES.to_string()) {
true => {
modify_attr_list.push(&NOTES);
}
_ => {}
}

match attrs.contains(&TAGS.to_string()) {
true => {
modify_attr_list.push(&TAGS);
Expand Down Expand Up @@ -290,6 +367,11 @@ pub fn scaffolding_fn(args: TokenStream, input: TokenStream) -> TokenStream {
parse_quote! {metadata: BTreeMap::new()};
expr_struct.fields.insert(0, line);
}
"notes" => {
let line: FieldValue =
parse_quote! {notes: BTreeMap::new()};
expr_struct.fields.insert(0, line);
}
"tags" => {
let line: FieldValue = parse_quote! {tags: Vec::new()};
expr_struct.fields.insert(0, line);
Expand Down
19 changes: 15 additions & 4 deletions src/defaults.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
use chrono::{DateTime, Duration, Months, Utc};
use uuid::Uuid;

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

/// adds x days to the timestamp
Expand Down Expand Up @@ -51,6 +51,17 @@ pub fn add_years(dtm: i64, years: u32) -> i64 {
dt.timestamp()
}

/// 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()
}

/// provided the default unix epoch time (UTC) as seconds
/// for the timestamp: 9999-12-31 23:59:59
///
Expand Down
Loading