merge branches and cleanup db
This commit is contained in:
@@ -323,16 +323,6 @@ where
|
||||
assigned_id
|
||||
};
|
||||
|
||||
// Always create a primary key index entry for this model type
|
||||
// This ensures get_all() can find all objects of this type, even if they have no explicit indexed fields
|
||||
let primary_index_key = format!("{}::primary", M::db_prefix());
|
||||
let mut primary_ids: HashSet<u32> =
|
||||
Self::get_tst_value(&mut index_db, &primary_index_key)?
|
||||
.unwrap_or_else(HashSet::new);
|
||||
primary_ids.insert(assigned_id);
|
||||
let raw_primary_ids = bincode::serde::encode_to_vec(&primary_ids, BINCODE_CONFIG)?;
|
||||
index_db.set(&primary_index_key, raw_primary_ids)?;
|
||||
|
||||
// Now add the new indices
|
||||
for index_key in indices_to_add {
|
||||
let key = Self::index_key(M::db_prefix(), index_key.name, &index_key.value);
|
||||
@@ -430,22 +420,6 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
// Also remove from the primary key index
|
||||
let primary_index_key = format!("{}::primary", M::db_prefix());
|
||||
if let Some(mut primary_ids) =
|
||||
Self::get_tst_value::<HashSet<u32>>(&mut index_db, &primary_index_key)?
|
||||
{
|
||||
primary_ids.remove(&id);
|
||||
if primary_ids.is_empty() {
|
||||
// This was the last object of this type, remove the primary index entirely
|
||||
index_db.delete(&primary_index_key)?;
|
||||
} else {
|
||||
// There are still other objects of this type, write back updated set
|
||||
let raw_primary_ids = bincode::serde::encode_to_vec(&primary_ids, BINCODE_CONFIG)?;
|
||||
index_db.set(&primary_index_key, raw_primary_ids)?;
|
||||
}
|
||||
}
|
||||
|
||||
// Finally delete the object itself
|
||||
Ok(data_db.delete(id)?)
|
||||
}
|
||||
@@ -476,7 +450,18 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
Err(tst::Error::PrefixNotFound(_)) => {
|
||||
// No index entries found for this prefix, meaning no objects of this type exist.
|
||||
// Note: tst::getall might return Ok(vec![]) in this case instead of PrefixNotFound.
|
||||
// Depending on tst implementation, this arm might be redundant if getall returns empty vec.
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
Err(e) => {
|
||||
// Other TST errors.
|
||||
return Err(super::Error::DB(e));
|
||||
}
|
||||
}
|
||||
|
||||
let mut results: Vec<M> = Vec::with_capacity(all_object_ids.len());
|
||||
for obj_id in all_object_ids {
|
||||
|
65
heromodels/src/models/access/README.md
Normal file
65
heromodels/src/models/access/README.md
Normal file
@@ -0,0 +1,65 @@
|
||||
# Access Control Model
|
||||
|
||||
The `access` model provides a system for managing permissions, defining which users or groups can access specific resources (objects) within the application.
|
||||
|
||||
## `Access` Struct
|
||||
|
||||
The core of this module is the `Access` struct, which acts as an Access Control Entry (ACE). It creates a link between a resource and an entity being granted permission.
|
||||
|
||||
### Fields
|
||||
|
||||
- `base_data`: Standard `BaseModelData` for a unique ID and timestamps.
|
||||
- `object_type`: A `String` identifying the type of the resource (e.g., `"Project"`, `"Document"`).
|
||||
- `object_id`: The `u32` unique ID of the resource instance.
|
||||
- `circle_pk`: The public key (`String`) of the user or entity being granted access.
|
||||
- `contact_id`: The ID of a `Contact` being granted access.
|
||||
- `group_id`: The ID of a `Group` being granted access.
|
||||
- `expires_at`: An optional `u64` timestamp for when the access grant expires.
|
||||
|
||||
All key fields are indexed for efficient lookups.
|
||||
|
||||
## Core Functions
|
||||
|
||||
The module provides functions to check permissions based on the created `Access` records.
|
||||
|
||||
- `can_access_resource(db, public_key, object_id, object_type) -> bool`:
|
||||
This is the primary function for permission checking. It determines if a user, identified by their `public_key`, can access a given object. The current logic is as follows:
|
||||
1. It first checks if the `public_key` belongs to a member of a globally defined `Circle`. If so, access is granted (this acts as a super-admin or owner role).
|
||||
2. If the user is not a global member, it queries for all `Access` records associated with the `object_id`.
|
||||
3. It returns `true` if it finds any `Access` record where the `circle_pk` matches the user's `public_key`.
|
||||
|
||||
- `is_circle_member(db, public_key) -> bool`:
|
||||
A helper function that checks if a user is part of the global `Circle`, effectively checking for super-admin privileges.
|
||||
|
||||
## Usage Example
|
||||
|
||||
Here is a conceptual walkthrough of how to grant and check access:
|
||||
|
||||
1. **A resource is created**, for example, a `Project` with ID `789`.
|
||||
|
||||
2. **To grant access** to a user with the public key `"pubkey_of_user_b"`, you create an `Access` record:
|
||||
|
||||
```rust
|
||||
use heromodels::models::access::Access;
|
||||
|
||||
let access_grant = Access::new()
|
||||
.object_type("Project".to_string())
|
||||
.object_id(789)
|
||||
.circle_pk("pubkey_of_user_b".to_string());
|
||||
|
||||
// This record would then be saved to the database.
|
||||
```
|
||||
|
||||
3. **To check access**, when `user_b` attempts to view the project, the application would call `can_access_resource`:
|
||||
|
||||
```rust
|
||||
// let can_access = can_access_resource(
|
||||
// db_connection,
|
||||
// "pubkey_of_user_b",
|
||||
// 789,
|
||||
// "Project"
|
||||
// );
|
||||
// assert!(can_access);
|
||||
```
|
||||
|
||||
This system allows for flexible, object-by-object permission management.
|
@@ -1,8 +1,8 @@
|
||||
use std::sync::Arc;
|
||||
use crate::db::{Collection, Db, hero::OurDB};
|
||||
use crate::models::Circle;
|
||||
use crate::db::{hero::OurDB, Collection, Db};
|
||||
use heromodels_core::BaseModelData;
|
||||
use heromodels_derive::model;
|
||||
use std::sync::Arc;
|
||||
// Temporarily removed to fix compilation issues
|
||||
// use rhai_autobind_macros::rhai_model_export;
|
||||
use rhai::{CustomType, TypeBuilder};
|
||||
@@ -71,17 +71,16 @@ impl Access {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Checks if a caller has permission to access a specific resource.
|
||||
/// Access is granted if the caller is a super admin or if an `Access` record exists
|
||||
/// granting them `can_access = true` for the given resource type and ID.
|
||||
///
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `db`: An `Arc<OurDB>` for database interaction.
|
||||
/// * `public_key`: The public key of the caller.
|
||||
/// * `_resource_id_to_check`: The ID of the resource being accessed (now unused).
|
||||
/// * `_resource_type_to_check`: The type of the resource (e.g., "Collection", "Image") (now unused).
|
||||
///
|
||||
///
|
||||
/// # Errors
|
||||
/// Returns `Err(EvalAltResult::ErrorRuntime)` if there's a database error during the check.
|
||||
pub fn can_access_resource(
|
||||
@@ -94,7 +93,8 @@ pub fn can_access_resource(
|
||||
.collection::<Circle>()
|
||||
.expect("Failed to get Circle collection")
|
||||
.get_all()
|
||||
.unwrap()[0].clone();
|
||||
.unwrap()[0]
|
||||
.clone();
|
||||
|
||||
// Circle members can access everything
|
||||
if circle.members.contains(&public_key.to_string()) {
|
||||
@@ -121,18 +121,18 @@ pub fn can_access_resource(
|
||||
println!("Access records: {:#?}", access_records);
|
||||
|
||||
// if circle_pk is in access records true
|
||||
return access_records.iter().any(|record| record.circle_pk == public_key)
|
||||
return access_records
|
||||
.iter()
|
||||
.any(|record| record.circle_pk == public_key);
|
||||
}
|
||||
|
||||
pub fn is_circle_member(
|
||||
db: Arc<OurDB>,
|
||||
public_key: &str,
|
||||
) -> bool {
|
||||
pub fn is_circle_member(db: Arc<OurDB>, public_key: &str) -> bool {
|
||||
let circle = db
|
||||
.collection::<Circle>()
|
||||
.expect("Failed to get Circle collection")
|
||||
.get_all()
|
||||
.unwrap()[0].clone();
|
||||
.unwrap()[0]
|
||||
.clone();
|
||||
|
||||
// Circle members can access everything
|
||||
if circle.members.contains(&public_key.to_string()) {
|
||||
|
58
heromodels/src/models/biz/README.md
Normal file
58
heromodels/src/models/biz/README.md
Normal file
@@ -0,0 +1,58 @@
|
||||
# Business Models (`biz`)
|
||||
|
||||
The `biz` module provides a suite of models for handling core business operations, including company management, product catalogs, sales, payments, and shareholder records.
|
||||
|
||||
## Core Models
|
||||
|
||||
### `Company`
|
||||
|
||||
The `Company` struct is the central model, representing a business entity.
|
||||
|
||||
- **Key Fields**: `name`, `registration_number`, `incorporation_date`, `address`, `business_type`, and `status`.
|
||||
- **Enums**:
|
||||
- `CompanyStatus`: Tracks the company's state (`PendingPayment`, `Active`, `Suspended`, `Inactive`).
|
||||
- `BusinessType`: Categorizes the company (e.g., `Coop`, `Single`, `Global`).
|
||||
- **Functionality**: Provides a foundation for linking other business models like products, sales, and shareholders.
|
||||
|
||||
### `Product`
|
||||
|
||||
The `Product` model defines goods or services offered by a company.
|
||||
|
||||
- **Key Fields**: `name`, `description`, `price`, `category`, `status`, and `components`.
|
||||
- **Nested Struct**: `ProductComponent` allows for defining complex products with sub-parts.
|
||||
- **Enums**:
|
||||
- `ProductType`: Differentiates between a `Product` and a `Service`.
|
||||
- `ProductStatus`: Indicates if a product is `Available` or `Unavailable`.
|
||||
|
||||
### `Sale`
|
||||
|
||||
The `Sale` struct records a transaction, linking a buyer to products.
|
||||
|
||||
- **Key Fields**: `company_id`, `buyer_id`, `total_amount`, `sale_date`, and `status`.
|
||||
- **Nested Struct**: `SaleItem` captures a snapshot of each product at the time of sale, including `product_id`, `quantity`, and `unit_price`.
|
||||
- **Enum**: `SaleStatus` tracks the state of the sale (`Pending`, `Completed`, `Cancelled`).
|
||||
|
||||
### `Payment`
|
||||
|
||||
The `Payment` model handles financial transactions, often linked to sales or subscriptions.
|
||||
|
||||
- **Key Fields**: `payment_intent_id` (e.g., for Stripe), `company_id`, `total_amount`, `currency`, and `status`.
|
||||
- **Functionality**: Includes methods to manage the payment lifecycle (`process_payment`, `complete_payment`, `fail_payment`, `refund_payment`).
|
||||
- **Enum**: `PaymentStatus` provides a detailed state of the payment (`Pending`, `Processing`, `Completed`, `Failed`, `Refunded`).
|
||||
|
||||
### `Shareholder`
|
||||
|
||||
The `Shareholder` model tracks ownership of a company.
|
||||
|
||||
- **Key Fields**: `company_id`, `user_id`, `name`, `shares`, and `percentage`.
|
||||
- **Enum**: `ShareholderType` distinguishes between `Individual` and `Corporate` shareholders.
|
||||
|
||||
## Workflow Example
|
||||
|
||||
1. A `Company` is created.
|
||||
2. The company defines several `Product` models representing its offerings.
|
||||
3. A customer (buyer) initiates a purchase, which creates a `Sale` record containing multiple `SaleItem`s.
|
||||
4. A `Payment` record is generated to process the transaction for the `Sale`'s total amount.
|
||||
5. As the company grows, `Shareholder` records are created to track equity distribution.
|
||||
|
||||
All models use the builder pattern for easy and readable instance creation.
|
@@ -17,10 +17,3 @@ pub use shareholder::{Shareholder, ShareholderType};
|
||||
|
||||
pub mod sale;
|
||||
pub use sale::{Sale, SaleItem, SaleStatus};
|
||||
|
||||
// pub use user::{User}; // Assuming a simple User model for now
|
||||
|
||||
#[cfg(feature = "rhai")]
|
||||
pub mod rhai;
|
||||
#[cfg(feature = "rhai")]
|
||||
pub use rhai::register_biz_rhai_module;
|
||||
|
@@ -1,6 +1,6 @@
|
||||
use heromodels_core::BaseModelData;
|
||||
use rhai::{CustomType, TypeBuilder};
|
||||
use heromodels_derive::model;
|
||||
use rhai::{CustomType, TypeBuilder};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
// ProductType represents the type of a product
|
||||
|
70
heromodels/src/models/calendar/README.md
Normal file
70
heromodels/src/models/calendar/README.md
Normal file
@@ -0,0 +1,70 @@
|
||||
# Calendar Model
|
||||
|
||||
The `calendar` model provides the data structures for managing calendars, events, and attendees.
|
||||
|
||||
## Core Components
|
||||
|
||||
### 1. `Calendar`
|
||||
|
||||
Represents a calendar, which is a collection of events. Each calendar has a name, an optional description, and a list of event IDs.
|
||||
|
||||
- `name`: The name of the calendar (e.g., "Work Calendar", "Personal Calendar").
|
||||
- `description`: An optional text description for the calendar.
|
||||
- `events`: A `Vec<i64>` containing the IDs of the `Event` models associated with this calendar.
|
||||
- `owner_id`: The ID of the user who owns the calendar.
|
||||
- `is_public`: A boolean indicating if the calendar is visible to others.
|
||||
|
||||
### 2. `Event`
|
||||
|
||||
Represents a single event within a calendar. It contains all the details for a specific appointment or occasion.
|
||||
|
||||
- `title`: The title of the event.
|
||||
- `description`: An optional detailed description.
|
||||
- `start_time` & `end_time`: Unix timestamps for when the event begins and ends.
|
||||
- `attendees`: A `Vec<Attendee>` listing who is invited to the event and their status.
|
||||
- `location`: The physical or virtual location of the event.
|
||||
- `status`: The current state of the event, defined by the `EventStatus` enum.
|
||||
|
||||
### 3. `Attendee`
|
||||
|
||||
Represents a person invited to an `Event`.
|
||||
|
||||
- `contact_id`: The ID of the user or contact who is the attendee.
|
||||
- `status`: The attendee's response to the invitation, defined by the `AttendanceStatus` enum.
|
||||
|
||||
## Enums
|
||||
|
||||
### `EventStatus`
|
||||
|
||||
Defines the lifecycle of an `Event`:
|
||||
- `Draft`: The event is being planned and is not yet visible to attendees.
|
||||
- `Published`: The event is confirmed and visible.
|
||||
- `Cancelled`: The event has been cancelled.
|
||||
|
||||
### `AttendanceStatus`
|
||||
|
||||
Defines the status of an `Attendee` for an event:
|
||||
- `Accepted`: The attendee has confirmed they will attend.
|
||||
- `Declined`: The attendee has declined the invitation.
|
||||
- `Tentative`: The attendee is unsure if they will attend.
|
||||
- `NoResponse`: The attendee has not yet responded.
|
||||
|
||||
## Usage
|
||||
|
||||
The `Calendar` model uses a builder pattern for creating and modifying instances. You can create a new `Calendar` or `Event` and chain methods to set its properties.
|
||||
|
||||
```rust
|
||||
use heromodels::models::calendar::{Calendar, Event, Attendee, AttendanceStatus};
|
||||
|
||||
// Create a new event
|
||||
let event = Event::new()
|
||||
.title("Team Meeting")
|
||||
.description("Weekly sync-up.")
|
||||
.reschedule(1672531200, 1672534800) // Set start and end times
|
||||
.add_attendee(Attendee::new(101).status(AttendanceStatus::Accepted));
|
||||
|
||||
// Create a new calendar and add the event to it (assuming event has been saved and has an ID)
|
||||
let calendar = Calendar::new(None, "Work Events")
|
||||
.owner_id(1)
|
||||
.add_event(event.base_data.id); // Add event by ID
|
||||
```
|
@@ -1,12 +1,12 @@
|
||||
use heromodels_core::BaseModelData;
|
||||
use heromodels_derive::model;
|
||||
use rhai::{CustomType, TypeBuilder};
|
||||
use rhai_autobind_macros::rhai_model_export;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Represents the status of an attendee for an event
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
|
||||
pub enum AttendanceStatus {
|
||||
#[default]
|
||||
Accepted = 0,
|
||||
Declined = 1,
|
||||
Tentative = 2,
|
||||
@@ -14,8 +14,9 @@ pub enum AttendanceStatus {
|
||||
}
|
||||
|
||||
/// Represents the status of an event
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
|
||||
pub enum EventStatus {
|
||||
#[default]
|
||||
Draft = 0,
|
||||
Published = 1,
|
||||
Cancelled = 2,
|
||||
@@ -87,7 +88,7 @@ impl Attendee {
|
||||
|
||||
/// Represents an event in a calendar
|
||||
#[model]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, CustomType)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, CustomType, Default)]
|
||||
pub struct Event {
|
||||
/// Base model data
|
||||
pub base_data: BaseModelData,
|
||||
@@ -224,10 +225,14 @@ impl Event {
|
||||
}
|
||||
|
||||
/// Adds an attendee ID to the event
|
||||
pub fn add_attendee(mut self, attendee_id: u32) -> Self {
|
||||
pub fn add_attendee(mut self, attendee: Attendee) -> Self {
|
||||
// Prevent duplicate attendees by ID
|
||||
if !self.attendees.iter().any(|&a_id| a_id == attendee_id) {
|
||||
self.attendees.push(attendee_id);
|
||||
if !self
|
||||
.attendees
|
||||
.iter()
|
||||
.any(|a| a.contact_id == attendee.contact_id)
|
||||
{
|
||||
self.attendees.push(attendee);
|
||||
}
|
||||
self
|
||||
}
|
||||
|
@@ -2,4 +2,4 @@
|
||||
pub mod calendar;
|
||||
|
||||
// Re-export Calendar, Event, Attendee, AttendanceStatus, and EventStatus from the inner calendar module (calendar.rs) within src/models/calendar/mod.rs
|
||||
pub use self::calendar::{AttendanceStatus, Attendee, Calendar, Event, EventStatus};
|
||||
pub use self::calendar::{AttendanceStatus, Attendee, Calendar, Event, EventStatus};
|
||||
|
59
heromodels/src/models/circle/README.md
Normal file
59
heromodels/src/models/circle/README.md
Normal file
@@ -0,0 +1,59 @@
|
||||
# Circle Model
|
||||
|
||||
The `circle` model defines a `Circle` struct, which represents a group or community of members. It includes metadata for customization and relationship mapping between different circles.
|
||||
|
||||
## `Circle` Struct
|
||||
|
||||
The `Circle` struct is the primary model in this module.
|
||||
|
||||
### Fields
|
||||
|
||||
- `base_data`: Standard `BaseModelData` for a unique ID and timestamps.
|
||||
- `title`: The name of the circle.
|
||||
- `ws_url`: A WebSocket URL associated with the circle.
|
||||
- `description`: An optional, longer description of the circle's purpose.
|
||||
- `logo`: An optional URL or symbol for the circle's logo.
|
||||
- `members`: A `Vec<String>` containing the public keys of the members of the circle.
|
||||
- `circles`: A `Vec<String>` containing the titles or IDs of other related circles, allowing for a network of circles.
|
||||
- `theme`: A `ThemeData` struct for customizing the visual appearance of the circle.
|
||||
|
||||
### `ThemeData` Struct
|
||||
|
||||
This struct holds visual customization options for a circle:
|
||||
|
||||
- `primary_color`: The primary color for the circle's theme.
|
||||
- `background_color`: The background color.
|
||||
- `background_pattern`: A pattern for the background.
|
||||
- `logo_symbol`: A symbol to use for the logo.
|
||||
- `logo_url`: A URL for the logo image.
|
||||
- `nav_dashboard_visible`: A boolean to control the visibility of the dashboard navigation.
|
||||
- `nav_timeline_visible`: A boolean to control the visibility of the timeline navigation.
|
||||
|
||||
## Usage Example
|
||||
|
||||
Here's how you might create a new circle and add members to it:
|
||||
|
||||
```rust
|
||||
use heromodels::models::circle::{Circle, ThemeData};
|
||||
|
||||
let mut my_circle = Circle::new()
|
||||
.title("My Awesome Circle".to_string())
|
||||
.description("A circle for awesome people.".to_string())
|
||||
.ws_url("wss://example.com/my_circle".to_string());
|
||||
|
||||
my_circle = my_circle.add_member("pubkey_of_member_1".to_string());
|
||||
my_circle = my_circle.add_member("pubkey_of_member_2".to_string());
|
||||
|
||||
let theme = ThemeData {
|
||||
primary_color: "#FF5733".to_string(),
|
||||
background_color: "#FFFFFF".to_string(),
|
||||
// ... other theme fields
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
my_circle = my_circle.theme(theme);
|
||||
|
||||
// The circle is now ready to be saved to the database.
|
||||
```
|
||||
|
||||
The `Circle` model is useful for creating social groups, teams, or any other collection of users who need to be grouped together.
|
66
heromodels/src/models/contact/README.md
Normal file
66
heromodels/src/models/contact/README.md
Normal file
@@ -0,0 +1,66 @@
|
||||
# Contact and Group Models
|
||||
|
||||
The `contact` module provides models for managing a personal or organizational address book. It includes the `Contact` struct for individual entries and the `Group` struct for organizing contacts.
|
||||
|
||||
## `Contact` Struct
|
||||
|
||||
The `Contact` model stores detailed information about a single contact.
|
||||
|
||||
### Fields
|
||||
|
||||
- `base_data`: Standard `BaseModelData` for a unique ID and timestamps.
|
||||
- `name`: The contact's name (indexed for easy searching).
|
||||
- `description`: An optional, longer description.
|
||||
- `address`: The physical or mailing address.
|
||||
- `phone`: The contact's phone number.
|
||||
- `email`: The contact's email address.
|
||||
- `notes`: Optional field for any additional notes.
|
||||
- `circle`: A `String` to associate the contact with a specific `Circle` or social group.
|
||||
|
||||
## `Group` Struct
|
||||
|
||||
The `Group` model allows for the creation of contact lists, making it easy to manage related contacts together.
|
||||
|
||||
### Fields
|
||||
|
||||
- `base_data`: Standard `BaseModelData`.
|
||||
- `name`: The name of the group (e.g., "Family", "Work Colleagues").
|
||||
- `description`: An optional description of the group.
|
||||
- `contacts`: A `Vec<u32>` containing the unique IDs of the `Contact` models that belong to this group.
|
||||
|
||||
## Usage Example
|
||||
|
||||
Here is a conceptual example of how to create contacts and organize them into a group:
|
||||
|
||||
1. **Create individual contacts**:
|
||||
|
||||
```rust
|
||||
use heromodels::models::contact::Contact;
|
||||
|
||||
let contact1 = Contact::new()
|
||||
.name("Alice")
|
||||
.email("alice@example.com");
|
||||
|
||||
let contact2 = Contact::new()
|
||||
.name("Bob")
|
||||
.email("bob@example.com");
|
||||
|
||||
// Save contact1 and contact2 to the database and get their IDs (e.g., 1 and 2).
|
||||
```
|
||||
|
||||
2. **Create a group and add the contacts**:
|
||||
|
||||
```rust
|
||||
use heromodels::models::contact::Group;
|
||||
|
||||
let mut friends_group = Group::new()
|
||||
.name("Friends")
|
||||
.description("My closest friends.");
|
||||
|
||||
friends_group = friends_group.add_contact(1); // Add Alice's ID
|
||||
friends_group = friends_group.add_contact(2); // Add Bob's ID
|
||||
|
||||
// Save the group to the database.
|
||||
```
|
||||
|
||||
Both models use the builder pattern, providing a fluent and readable way to construct instances.
|
50
heromodels/src/models/core/README.md
Normal file
50
heromodels/src/models/core/README.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# Core Model
|
||||
|
||||
The `core` model contains fundamental, reusable components that are shared across various other models in the `heromodels` library. The primary component in this module is the `Comment` struct.
|
||||
|
||||
## `Comment` Struct
|
||||
|
||||
The `Comment` struct is designed to provide a generic commenting functionality that can be associated with any other model. It supports threaded conversations.
|
||||
|
||||
### Fields
|
||||
|
||||
- `base_data`: The standard `BaseModelData`, which provides a unique ID and timestamps for each comment.
|
||||
- `user_id`: The ID of the user who posted the comment. This field is indexed.
|
||||
- `content`: The text content of the comment.
|
||||
- `parent_comment_id`: An `Option<u32>` that holds the ID of the parent comment. If this is `None`, the comment is a top-level comment. If it contains an ID, it is a reply to another comment.
|
||||
|
||||
## Usage
|
||||
|
||||
The `Comment` model uses a builder pattern for easy instantiation. You can create top-level comments or replies.
|
||||
|
||||
### Creating a Top-Level Comment
|
||||
|
||||
```rust
|
||||
use heromodels::models::core::Comment;
|
||||
|
||||
let top_level_comment = Comment::new()
|
||||
.user_id(101) // ID of the user posting
|
||||
.content("This is the first comment on the topic.");
|
||||
|
||||
assert!(top_level_comment.parent_comment_id.is_none());
|
||||
```
|
||||
|
||||
### Creating a Threaded Reply
|
||||
|
||||
To create a reply, you set the `parent_comment_id` to the ID of the comment you are replying to.
|
||||
|
||||
```rust
|
||||
use heromodels::models::core::Comment;
|
||||
|
||||
// Assume the top_level_comment from the previous example was saved and has ID 1
|
||||
let top_level_comment_id = 1;
|
||||
|
||||
let reply_comment = Comment::new()
|
||||
.user_id(102)
|
||||
.content("This is a reply to the first comment.")
|
||||
.parent_comment_id(Some(top_level_comment_id));
|
||||
|
||||
assert_eq!(reply_comment.parent_comment_id, Some(1));
|
||||
```
|
||||
|
||||
This `Comment` model can be linked from other models (like `User`, `Article`, `Project`, etc.) by storing a `Vec<u32>` of comment IDs within them, as demonstrated by the `add_comment` method in the `userexample` model.
|
89
heromodels/src/models/finance/README.md
Normal file
89
heromodels/src/models/finance/README.md
Normal file
@@ -0,0 +1,89 @@
|
||||
# Finance Model
|
||||
|
||||
The `finance` model provides a suite of data structures for managing financial accounts, digital assets, and a marketplace for trading them.
|
||||
|
||||
## Core Components
|
||||
|
||||
### 1. `Account`
|
||||
|
||||
Represents a financial account, typically owned by a user. It acts as a container for various assets.
|
||||
|
||||
- `name`: An internal name for the account (e.g., "My Savings").
|
||||
- `user_id`: The ID of the user who owns the account.
|
||||
- `ledger`: The blockchain or financial system where the account exists (e.g., "Ethereum").
|
||||
- `address`: The account's public address.
|
||||
- `assets`: A `Vec<u32>` of asset IDs associated with this account.
|
||||
|
||||
### 2. `Asset`
|
||||
|
||||
Represents a digital or tokenized asset.
|
||||
|
||||
- `name`: The name of the asset (e.g., "Bitcoin", "MyToken").
|
||||
- `amount`: The quantity of the asset held.
|
||||
- `asset_type`: The type of the asset, defined by the `AssetType` enum.
|
||||
- `address`: The contract address of the token (if applicable).
|
||||
|
||||
### 3. `Marketplace`
|
||||
|
||||
The marketplace components facilitate the trading of assets.
|
||||
|
||||
- **`Listing`**: Represents an asset listed for sale. It can be a fixed-price sale, an auction, or an exchange.
|
||||
- `title`: The title of the listing.
|
||||
- `asset_id`: The ID of the asset being sold.
|
||||
- `seller_id`: The ID of the user selling the asset.
|
||||
- `price`: The asking price or starting bid.
|
||||
- `listing_type`: The type of sale, defined by `ListingType`.
|
||||
- `status`: The current state of the listing, defined by `ListingStatus`.
|
||||
|
||||
- **`Bid`**: Represents a bid made on an auction-style `Listing`.
|
||||
- `bidder_id`: The ID of the user placing the bid.
|
||||
- `amount`: The value of the bid.
|
||||
- `status`: The current state of the bid, defined by `BidStatus`.
|
||||
|
||||
## Enums
|
||||
|
||||
### `AssetType`
|
||||
- `Erc20`, `Erc721`, `Erc1155`: Standard Ethereum token types.
|
||||
- `Native`: The native currency of a blockchain (e.g., ETH).
|
||||
|
||||
### `ListingType`
|
||||
- `FixedPrice`: The asset is sold for a set price.
|
||||
- `Auction`: The asset is sold to the highest bidder.
|
||||
- `Exchange`: The asset is offered in trade for other assets.
|
||||
|
||||
### `ListingStatus`
|
||||
- `Active`, `Sold`, `Cancelled`, `Expired`: Defines the lifecycle of a listing.
|
||||
|
||||
### `BidStatus`
|
||||
- `Active`, `Accepted`, `Rejected`, `Cancelled`: Defines the lifecycle of a bid.
|
||||
|
||||
## Usage
|
||||
|
||||
The models use a builder pattern for easy instantiation.
|
||||
|
||||
```rust
|
||||
use heromodels::models::finance::{Account, Asset, Listing, ListingType};
|
||||
|
||||
// 1. Create a user account
|
||||
let account = Account::new()
|
||||
.name("Trading Account")
|
||||
.user_id(101)
|
||||
.ledger("Ethereum")
|
||||
.address("0x123...");
|
||||
|
||||
// 2. Create an asset (assuming it's saved and has an ID)
|
||||
let asset = Asset::new()
|
||||
.name("Hero Token")
|
||||
.amount(1000.0);
|
||||
// In a real scenario, you would save the asset to get an ID.
|
||||
let asset_id = asset.base_data.id.to_string();
|
||||
|
||||
// 3. Create a marketplace listing for the asset
|
||||
let listing = Listing::new()
|
||||
.title("1000 Hero Tokens for Sale")
|
||||
.asset_id(asset_id)
|
||||
.seller_id(account.user_id.to_string())
|
||||
.price(0.5)
|
||||
.currency("USD")
|
||||
.listing_type(ListingType::FixedPrice);
|
||||
```
|
60
heromodels/src/models/flow/README.md
Normal file
60
heromodels/src/models/flow/README.md
Normal file
@@ -0,0 +1,60 @@
|
||||
# Flow Model
|
||||
|
||||
The `flow` model provides a framework for creating and managing multi-step workflows, particularly those requiring digital signatures. It is designed to orchestrate a sequence of actions that must be completed in a specific order.
|
||||
|
||||
## Core Components
|
||||
|
||||
### 1. `Flow`
|
||||
|
||||
The top-level container for a workflow.
|
||||
|
||||
- `flow_uuid`: A unique identifier for the entire flow, used for external references.
|
||||
- `name`: A human-readable name for the flow (e.g., "Document Approval Process").
|
||||
- `status`: The overall status of the flow (e.g., "Pending", "InProgress", "Completed").
|
||||
- `steps`: A `Vec<FlowStep>` that defines the sequence of steps in the workflow.
|
||||
|
||||
### 2. `FlowStep`
|
||||
|
||||
Represents a single, distinct step within a `Flow`.
|
||||
|
||||
- `step_order`: A `u32` that determines the position of this step in the sequence.
|
||||
- `description`: An optional text description of what this step entails.
|
||||
- `status`: The status of this individual step.
|
||||
|
||||
### 3. `SignatureRequirement`
|
||||
|
||||
Defines a requirement for a digital signature within a `FlowStep`. A single step can have multiple signature requirements.
|
||||
|
||||
- `flow_step_id`: A foreign key linking the requirement to its parent `FlowStep`.
|
||||
- `public_key`: The public key of the entity that is required to sign.
|
||||
- `message`: The plaintext message that needs to be signed.
|
||||
- `signature`: The resulting signature, once provided.
|
||||
- `status`: The status of the signature requirement (e.g., "Pending", "Signed", "Failed").
|
||||
|
||||
## Usage
|
||||
|
||||
The models use a builder pattern to construct complex flows. You create a `Flow`, add `FlowStep`s to it, and associate `SignatureRequirement`s with each step.
|
||||
|
||||
```rust
|
||||
use heromodels::models::flow::{Flow, FlowStep, SignatureRequirement};
|
||||
use uuid::Uuid;
|
||||
|
||||
// 1. Define a signature requirement
|
||||
let requirement = SignatureRequirement::new(
|
||||
0, // ID is managed by the database
|
||||
1, // Belongs to flow step 1
|
||||
"0xPublicKey1...",
|
||||
"I approve this document."
|
||||
);
|
||||
|
||||
// 2. Create a flow step
|
||||
// In a real application, you would add the signature requirement to the step.
|
||||
let step1 = FlowStep::new(0, 1) // ID, step_order
|
||||
.description("Initial review and approval");
|
||||
|
||||
// 3. Create the main flow and add the step
|
||||
let flow = Flow::new(Uuid::new_v4().to_string())
|
||||
.name("Contract Signing Flow")
|
||||
.add_step(step1);
|
||||
|
||||
```
|
@@ -9,7 +9,7 @@ use serde::{Deserialize, Serialize};
|
||||
#[model]
|
||||
pub struct Flow {
|
||||
/// Base model data (id, created_at, updated_at).
|
||||
#[rhai_type(skip)]
|
||||
#[rhai_type(skip)]
|
||||
pub base_data: BaseModelData,
|
||||
|
||||
/// A unique UUID for the flow, for external reference.
|
||||
|
@@ -9,7 +9,7 @@ use std::default::Default;
|
||||
#[model]
|
||||
pub struct FlowStep {
|
||||
/// Base model data.
|
||||
#[rhai_type(skip)]
|
||||
#[rhai_type(skip)]
|
||||
pub base_data: BaseModelData,
|
||||
|
||||
/// Optional description for the step.
|
||||
|
@@ -6,4 +6,4 @@ pub mod signature_requirement;
|
||||
// Re-export key types for convenience
|
||||
pub use flow::Flow;
|
||||
pub use flow_step::FlowStep;
|
||||
pub use signature_requirement::SignatureRequirement;
|
||||
pub use signature_requirement::SignatureRequirement;
|
||||
|
@@ -9,7 +9,7 @@ use std::default::Default;
|
||||
#[model]
|
||||
pub struct SignatureRequirement {
|
||||
/// Base model data.
|
||||
#[rhai_type(skip)]
|
||||
#[rhai_type(skip)]
|
||||
pub base_data: BaseModelData,
|
||||
|
||||
/// Foreign key to the FlowStep this requirement belongs to.
|
||||
|
64
heromodels/src/models/gov/README.md
Normal file
64
heromodels/src/models/gov/README.md
Normal file
@@ -0,0 +1,64 @@
|
||||
# Corporate Governance (`gov`) Model
|
||||
|
||||
The `gov` module provides a comprehensive suite of models for managing corporate governance structures and processes. It allows for the detailed representation of companies, their ownership, governing bodies, and decision-making workflows.
|
||||
|
||||
## Core Models
|
||||
|
||||
### `Company`
|
||||
|
||||
The `Company` struct is the central entity in this module. It is similar to the `Company` model in the `biz` module but is specifically tailored for governance, with direct implementation of the `Model` trait for database interaction.
|
||||
|
||||
- **Key Fields**: `name`, `registration_number`, `incorporation_date`, `status`, `business_type`.
|
||||
- **Enums**: `CompanyStatus`, `BusinessType`.
|
||||
|
||||
### `Shareholder`
|
||||
|
||||
The `Shareholder` model is used to track ownership of a company.
|
||||
|
||||
- **Key Fields**: `company_id`, `name`, `shares`, `percentage`, `shareholder_type`.
|
||||
- **Enums**: `ShareholderType` (e.g., Individual, Corporate).
|
||||
|
||||
### `Committee`
|
||||
|
||||
Companies can have `Committee`s to oversee specific functions (e.g., Audit Committee, Compensation Committee). Each committee is composed of `CommitteeMember`s.
|
||||
|
||||
- **`Committee` Fields**: `company_id`, `name`, `description`, `members`.
|
||||
- **`CommitteeMember` Fields**: `user_id`, `name`, `role`.
|
||||
- **Enums**: `CommitteeRole` (e.g., Chair, Member, Advisor).
|
||||
|
||||
### `Meeting`
|
||||
|
||||
The `Meeting` model is used to schedule and document official meetings for a company or its committees.
|
||||
|
||||
- **Key Fields**: `company_id`, `title`, `meeting_type`, `status`, `start_time`, `end_time`, `agenda`, `minutes`, `attendees`.
|
||||
- **`Attendee` Fields**: `user_id`, `name`, `status`.
|
||||
- **Enums**: `MeetingStatus`, `MeetingType`, `AttendanceStatus`.
|
||||
|
||||
### `Resolution`
|
||||
|
||||
A `Resolution` represents a formal proposal or decision that requires a vote.
|
||||
|
||||
- **Key Fields**: `company_id`, `title`, `description`, `resolution_type`, `status`, `proposed_date`, `effective_date`.
|
||||
- **Enums**: `ResolutionStatus`, `ResolutionType` (e.g., Ordinary, Special).
|
||||
|
||||
### `Vote` and `Ballot`
|
||||
|
||||
The `Vote` model facilitates the voting process for a specific `Resolution`. Each `Vote` consists of multiple `Ballot`s cast by voters.
|
||||
|
||||
- **`Vote` Fields**: `company_id`, `resolution_id`, `title`, `status`, `start_date`, `end_date`, `ballots`.
|
||||
- **`Ballot` Fields**: `user_id`, `option`, `weight`, `cast_at`.
|
||||
- **Enums**: `VoteStatus`, `VoteOption` (Yes, No, Abstain).
|
||||
|
||||
## Workflow Example
|
||||
|
||||
A typical governance workflow might look like this:
|
||||
|
||||
1. A `Company` is established with several `Shareholder`s.
|
||||
2. A `Committee` (e.g., the Board of Directors) is formed by adding `CommitteeMember`s.
|
||||
3. The committee proposes a `Resolution` to approve the annual budget.
|
||||
4. A `Vote` is created for this resolution, with a defined start and end date.
|
||||
5. A `Meeting` is scheduled to discuss the resolution.
|
||||
6. During the voting period, shareholders or committee members cast their `Ballot`s.
|
||||
7. Once the `Vote` is closed, the results are tallied, and the `Resolution` status is updated to `Approved` or `Rejected`.
|
||||
|
||||
This module provides the foundational data structures for building robust corporate governance applications.
|
79
heromodels/src/models/governance/README.md
Normal file
79
heromodels/src/models/governance/README.md
Normal file
@@ -0,0 +1,79 @@
|
||||
# Governance Model
|
||||
|
||||
The `governance` model provides a robust framework for managing decentralized governance processes, including proposals, voting, and activity tracking.
|
||||
|
||||
## Core Components
|
||||
|
||||
### 1. `Proposal`
|
||||
|
||||
The central element of the governance model. A `Proposal` represents a formal suggestion submitted to the community for a vote.
|
||||
|
||||
- `title` & `description`: The substance of the proposal.
|
||||
- `creator_id`: The ID of the user who submitted the proposal.
|
||||
- `status`: The current state of the proposal (e.g., `Draft`, `Active`, `Approved`), defined by the `ProposalStatus` enum.
|
||||
- `options`: A `Vec<VoteOption>` defining the choices voters can select (e.g., "For", "Against", "Abstain").
|
||||
- `vote_start_date` & `vote_end_date`: Timestamps that define the voting period.
|
||||
|
||||
### 2. `Ballot`
|
||||
|
||||
Represents a single vote cast by a user on a specific `Proposal`.
|
||||
|
||||
- `user_id`: The ID of the voter.
|
||||
- `vote_option_id`: The specific `VoteOption` the user selected.
|
||||
- `shares_count`: The voting power or weight of the vote.
|
||||
- `comment`: An optional comment from the voter.
|
||||
|
||||
### 3. `GovernanceActivity`
|
||||
|
||||
A detailed record of every significant event that occurs within the governance system. This is crucial for transparency and auditing.
|
||||
|
||||
- `activity_type`: The type of event that occurred (e.g., `ProposalCreated`, `VoteCast`), defined by the `ActivityType` enum.
|
||||
- `actor_id` & `actor_name`: Who performed the action.
|
||||
- `target_id` & `target_type`: The object the action was performed on (e.g., a `Proposal`).
|
||||
- `title` & `description`: A summary of the activity.
|
||||
|
||||
### 4. `AttachedFile`
|
||||
|
||||
A simple struct to link external documents or files to a proposal, such as technical specifications or legal drafts.
|
||||
|
||||
## Enums
|
||||
|
||||
The model includes several enums to manage the state of proposals, voting, and activities:
|
||||
|
||||
- `ProposalStatus`: Tracks the lifecycle of a proposal (`Draft`, `Active`, `Approved`, `Rejected`).
|
||||
- `VoteEventStatus`: Tracks the status of the voting period (`Upcoming`, `Open`, `Closed`).
|
||||
- `ActivityType`: Categorizes different governance actions.
|
||||
- `ActivityStatus`: Tracks the status of a recorded activity (`Pending`, `Completed`, `Failed`).
|
||||
|
||||
## Usage
|
||||
|
||||
```rust
|
||||
use heromodels::models::governance::{Proposal, Ballot, VoteOption, ProposalStatus};
|
||||
use chrono::Utc;
|
||||
|
||||
// 1. Create a new proposal
|
||||
let mut proposal = Proposal::new(
|
||||
None, // ID is managed by the database
|
||||
"user-123".to_string(),
|
||||
"Alice".to_string(),
|
||||
"Adopt New Logo".to_string(),
|
||||
"Proposal to update the community logo.".to_string(),
|
||||
ProposalStatus::Draft,
|
||||
vec![],
|
||||
None
|
||||
);
|
||||
|
||||
// 2. Add voting options
|
||||
proposal = proposal.add_option(1, "Approve New Logo", None);
|
||||
proposal = proposal.add_option(2, "Reject New Logo", None);
|
||||
|
||||
// 3. An eligible user casts a vote
|
||||
// This would typically be done by finding the proposal and then calling cast_vote.
|
||||
let proposal_after_vote = proposal.cast_vote(
|
||||
None, // Ballot ID
|
||||
456, // Voter's user_id
|
||||
1, // Voting for option 1
|
||||
100 // With 100 shares/votes
|
||||
);
|
||||
|
||||
```
|
@@ -3,7 +3,6 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use heromodels_derive::model;
|
||||
use rhai::{CustomType, TypeBuilder};
|
||||
use rhai_autobind_macros::rhai_model_export;
|
||||
use serde::{Deserialize, Serialize};
|
||||
// use std::collections::HashMap;
|
||||
|
||||
@@ -47,7 +46,6 @@ impl Default for ActivityStatus {
|
||||
/// GovernanceActivity represents a single activity or event in the governance system
|
||||
/// This model tracks all significant actions and changes for audit and transparency purposes
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, CustomType)]
|
||||
#[rhai_model_export(db_type = "std::sync::Arc<crate::db::hero::OurDB>")]
|
||||
#[model]
|
||||
pub struct GovernanceActivity {
|
||||
pub base_data: BaseModelData,
|
||||
|
@@ -1,7 +1,9 @@
|
||||
// heromodels/src/models/governance/mod.rs
|
||||
// This module will contain the Proposal model and related types.
|
||||
pub mod activity;
|
||||
pub mod attached_file;
|
||||
pub mod proposal;
|
||||
|
||||
pub use self::activity::{ActivityStatus, ActivityType, GovernanceActivity};
|
||||
pub use self::attached_file::AttachedFile;
|
||||
pub use self::proposal::{Ballot, Proposal, ProposalStatus, VoteEventStatus, VoteOption};
|
||||
|
@@ -343,7 +343,6 @@ impl ToString for ActivityType {
|
||||
|
||||
/// Represents a governance activity in the system
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, CustomType)]
|
||||
#[rhai_model_export(db_type = "std::sync::Arc<crate::db::hero::OurDB>")]
|
||||
#[model] // Has base.Base in V spec
|
||||
pub struct Activity {
|
||||
/// Base model data
|
||||
|
79
heromodels/src/models/legal/README.md
Normal file
79
heromodels/src/models/legal/README.md
Normal file
@@ -0,0 +1,79 @@
|
||||
# Legal Model
|
||||
|
||||
The `legal` model provides a structured way to create, manage, and track the lifecycle of digital contracts.
|
||||
|
||||
## Core Components
|
||||
|
||||
### 1. `Contract`
|
||||
|
||||
The main struct representing a legal agreement. It serves as a container for all contract-related data.
|
||||
|
||||
- `contract_id`: A unique identifier for the contract.
|
||||
- `title` & `description`: A summary of the contract's purpose.
|
||||
- `status`: The current state of the contract (e.g., `Draft`, `Active`), managed by the `ContractStatus` enum.
|
||||
- `signers`: A `Vec<ContractSigner>` listing all parties required to sign.
|
||||
- `revisions`: A `Vec<ContractRevision>` that provides a history of the contract's content, allowing for version control.
|
||||
- `current_version`: The active version of the contract.
|
||||
|
||||
### 2. `ContractSigner`
|
||||
|
||||
Represents an individual or entity required to sign the contract.
|
||||
|
||||
- `id`, `name`, `email`: Identifying information for the signer.
|
||||
- `status`: The signer's current status (`Pending`, `Signed`, `Rejected`), defined by the `SignerStatus` enum.
|
||||
- `signed_at`: A timestamp indicating when the signature was provided.
|
||||
- `signature_data`: Stores the actual signature, for example, as a Base64 encoded image.
|
||||
|
||||
### 3. `ContractRevision`
|
||||
|
||||
Represents a specific version of the contract's text.
|
||||
|
||||
- `version`: A number identifying the revision.
|
||||
- `content`: The full text of the contract for that version.
|
||||
- `created_at` & `created_by`: Audit fields to track who created the revision and when.
|
||||
|
||||
## Enums
|
||||
|
||||
The model uses two key enums to manage state:
|
||||
|
||||
- `ContractStatus`: Defines the lifecycle of the entire contract, from `Draft` to `PendingSignatures`, `Active`, `Expired`, or `Cancelled`.
|
||||
- `SignerStatus`: Tracks the state of each individual signer (`Pending`, `Signed`, `Rejected`).
|
||||
|
||||
## Usage
|
||||
|
||||
The models are constructed using a builder pattern, allowing for clear and flexible creation of complex contracts.
|
||||
|
||||
```rust
|
||||
use heromodels::models::legal::{Contract, ContractSigner, ContractRevision, ContractStatus};
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
fn current_timestamp_secs() -> u64 {
|
||||
SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap_or_default()
|
||||
.as_secs()
|
||||
}
|
||||
|
||||
// 1. Create a signer
|
||||
let signer1 = ContractSigner::new(
|
||||
"signer-uuid-1".to_string(),
|
||||
"Alice".to_string(),
|
||||
"alice@example.com".to_string()
|
||||
);
|
||||
|
||||
// 2. Create a revision
|
||||
let revision1 = ContractRevision::new(
|
||||
1,
|
||||
"This is the first version of the contract...".to_string(),
|
||||
current_timestamp_secs(),
|
||||
"creator-uuid-1".to_string()
|
||||
);
|
||||
|
||||
// 3. Create the contract
|
||||
let contract = Contract::new(1, "contract-uuid-1".to_string())
|
||||
.title("Service Agreement")
|
||||
.status(ContractStatus::PendingSignatures)
|
||||
.add_signer(signer1)
|
||||
.add_revision(revision1)
|
||||
.current_version(1);
|
||||
```
|
49
heromodels/src/models/library/README.md
Normal file
49
heromodels/src/models/library/README.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# Library Model
|
||||
|
||||
The `library` model provides a flexible system for managing various types of digital assets and organizing them into collections.
|
||||
|
||||
## Library Item Types
|
||||
|
||||
The model supports several distinct types of library items, each with its own specific metadata:
|
||||
|
||||
- `Image`: Represents an image file with properties like `title`, `url`, `width`, and `height`.
|
||||
- `Pdf`: Represents a PDF document with a `title`, `url`, and `page_count`.
|
||||
- `Markdown`: Represents a text document written in Markdown, with `title` and `content`.
|
||||
- `Book`: A more complex item that consists of a `title`, a `table_of_contents` (a nested structure of `TocEntry` items), and a `Vec<String>` of pages (in Markdown).
|
||||
- `Slideshow`: Represents a presentation, containing a `title` and a `Vec<Slide>`, where each slide has an `image_url` and optional text.
|
||||
|
||||
All items share a common `BaseModelData` field, providing them with a unique ID and timestamps.
|
||||
|
||||
## `Collection`
|
||||
|
||||
The `Collection` struct is used to group various library items together. It does not store the items directly but rather holds vectors of their unique IDs.
|
||||
|
||||
- `title` & `description`: To name and describe the collection.
|
||||
- `images`: A `Vec<u32>` of `Image` item IDs.
|
||||
- `pdfs`: A `Vec<u32>` of `Pdf` item IDs.
|
||||
- `markdowns`: A `Vec<u32>` of `Markdown` item IDs.
|
||||
- `books`: A `Vec<u32>` of `Book` item IDs.
|
||||
- `slides`: A `Vec<u32>` of `Slideshow` item IDs.
|
||||
|
||||
## Usage
|
||||
|
||||
First, you create individual library items. Then, you create a collection and add the IDs of those items to it.
|
||||
|
||||
```rust
|
||||
use heromodels::models::library::{Image, Collection};
|
||||
|
||||
// 1. Create a library item (e.g., an Image)
|
||||
let image1 = Image::new()
|
||||
.title("Company Logo")
|
||||
.url("https://example.com/logo.png");
|
||||
// In a real app, this would be saved to a database, and we'd get an ID.
|
||||
let image1_id = image1.id(); // Assuming this ID is now 1
|
||||
|
||||
// 2. Create a collection
|
||||
let mut marketing_assets = Collection::new()
|
||||
.title("Marketing Assets");
|
||||
|
||||
// 3. Add the item's ID to the collection
|
||||
marketing_assets = marketing_assets.add_image(image1_id);
|
||||
|
||||
```
|
@@ -112,10 +112,10 @@ impl Pdf {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Gets the ID of the image.
|
||||
pub fn id(&self) -> u32 {
|
||||
self.base_data.id
|
||||
}
|
||||
/// Gets the ID of the image.
|
||||
pub fn id(&self) -> u32 {
|
||||
self.base_data.id
|
||||
}
|
||||
|
||||
/// Sets the title of the PDF.
|
||||
pub fn title(mut self, title: impl Into<String>) -> Self {
|
||||
@@ -163,10 +163,10 @@ impl Markdown {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Gets the ID of the image.
|
||||
pub fn id(&self) -> u32 {
|
||||
self.base_data.id
|
||||
}
|
||||
/// Gets the ID of the image.
|
||||
pub fn id(&self) -> u32 {
|
||||
self.base_data.id
|
||||
}
|
||||
|
||||
/// Sets the title of the document.
|
||||
pub fn title(mut self, title: impl Into<String>) -> Self {
|
||||
|
@@ -1,3 +1,36 @@
|
||||
## Object Model
|
||||
# Log Model
|
||||
|
||||
This is a generic object model mostly used for testing purposes.
|
||||
The `log` model provides a generic `Log` struct for creating audit trails, recording events, or tracking activities within the system. It is designed to be flexible, linking a subject (who performed the action) to an object (what was affected).
|
||||
|
||||
## `Log` Struct
|
||||
|
||||
The `Log` struct is the core of this module.
|
||||
|
||||
### Fields
|
||||
|
||||
- `base_data`: Standard `BaseModelData` for a unique ID and timestamps.
|
||||
- `title`: A short, descriptive title for the log entry (e.g., "User Login", "File Deletion"). This field is indexed.
|
||||
- `description`: A more detailed description of the event.
|
||||
- `subject_pk`: The public key of the user or entity that initiated the event. This field is indexed.
|
||||
- `object_id`: The unique ID of the object or resource that was the target of the event. This field is indexed.
|
||||
|
||||
## Usage Example
|
||||
|
||||
Here is how you might create a log entry when a user updates a contact:
|
||||
|
||||
```rust
|
||||
use heromodels::models::log::Log;
|
||||
|
||||
let user_public_key = "pubkey_of_acting_user";
|
||||
let updated_contact_id = 123;
|
||||
|
||||
let log_entry = Log::new()
|
||||
.title("Contact Updated".to_string())
|
||||
.description(format!("User {} updated contact with ID {}.", user_public_key, updated_contact_id))
|
||||
.subject_pk(user_public_key.to_string())
|
||||
.object_id(updated_contact_id);
|
||||
|
||||
// Save the log_entry to the database.
|
||||
```
|
||||
|
||||
By indexing `title`, `subject_pk`, and `object_id`, the `Log` model allows for efficient querying of activities, such as retrieving all actions performed by a specific user or all events related to a particular object.
|
@@ -21,15 +21,15 @@ pub use userexample::User;
|
||||
// pub use productexample::Product; // Temporarily remove
|
||||
pub use biz::{Payment, PaymentStatus, Sale, SaleItem, SaleStatus};
|
||||
pub use calendar::{AttendanceStatus, Attendee, Calendar, Event};
|
||||
pub use circle::{Circle, ThemeData};
|
||||
pub use finance::marketplace::{Bid, BidStatus, Listing, ListingStatus, ListingType};
|
||||
pub use finance::{Account, Asset, AssetType};
|
||||
pub use flow::{Flow, FlowStep, SignatureRequirement};
|
||||
pub use legal::{Contract, ContractRevision, ContractSigner, ContractStatus, SignerStatus};
|
||||
pub use library::collection::Collection;
|
||||
pub use library::items::{Image, Markdown, Pdf};
|
||||
pub use projects::{Project, ProjectStatus};
|
||||
pub use governance::{
|
||||
ActivityStatus, ActivityType, Ballot, GovernanceActivity, Proposal, ProposalStatus,
|
||||
VoteEventStatus, VoteOption,
|
||||
};
|
||||
pub use circle::{Circle, ThemeData};
|
||||
pub use legal::{Contract, ContractRevision, ContractSigner, ContractStatus, SignerStatus};
|
||||
pub use library::collection::Collection;
|
||||
pub use library::items::{Image, Markdown, Pdf};
|
||||
pub use projects::{Project, Status};
|
||||
|
@@ -1,3 +1,27 @@
|
||||
## Object Model
|
||||
# Object Model
|
||||
|
||||
This is a generic object model mostly used for testing purposes.
|
||||
The `object` model provides a simple, generic `Object` struct. It is intended for use in situations where a basic, identifiable data container is needed, such as for testing, prototyping, or representing simple items that do not require a more complex, specific model.
|
||||
|
||||
## `Object` Struct
|
||||
|
||||
The `Object` struct contains the following fields:
|
||||
|
||||
- `base_data`: Standard `BaseModelData` for a unique ID and timestamps.
|
||||
- `title`: A string for the object's title or name. This field is indexed for efficient lookups.
|
||||
- `description`: A string for a more detailed description of the object.
|
||||
|
||||
## Usage Example
|
||||
|
||||
Creating a new `Object` is straightforward using the builder pattern:
|
||||
|
||||
```rust
|
||||
use heromodels::models::object::Object;
|
||||
|
||||
let my_object = Object::new()
|
||||
.title("My Test Object".to_string())
|
||||
.description("This is an object created for a test scenario.".to_string());
|
||||
|
||||
// The object is now ready to be saved to the database.
|
||||
```
|
||||
|
||||
Due to its simplicity, the `Object` model is a versatile tool for various development and testing needs.
|
@@ -1,9 +1,9 @@
|
||||
use heromodels_core::BaseModelData;
|
||||
use heromodels_derive::model;
|
||||
use rhai::CustomType;
|
||||
use rhai::TypeBuilder;
|
||||
use rhailib_derive::RhaiApi;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use rhai::TypeBuilder;
|
||||
|
||||
/// Represents an event in a contact
|
||||
#[model]
|
||||
@@ -13,7 +13,7 @@ pub struct Object {
|
||||
pub base_data: BaseModelData,
|
||||
#[index]
|
||||
pub title: String,
|
||||
pub description: String
|
||||
pub description: String,
|
||||
}
|
||||
|
||||
impl Object {
|
||||
@@ -28,7 +28,7 @@ impl Object {
|
||||
pub fn id(&self) -> u32 {
|
||||
self.base_data.id
|
||||
}
|
||||
|
||||
|
||||
pub fn title(mut self, title: String) -> Self {
|
||||
self.title = title;
|
||||
self
|
||||
@@ -38,4 +38,4 @@ impl Object {
|
||||
self.description = description;
|
||||
self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
76
heromodels/src/models/projects/README.md
Normal file
76
heromodels/src/models/projects/README.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# Projects Model
|
||||
|
||||
The `projects` model provides a comprehensive suite of tools for managing software development projects, based on common agile methodologies.
|
||||
|
||||
## Core Components
|
||||
|
||||
The model is built around a hierarchy of work items:
|
||||
|
||||
- **`Project`**: The highest-level container. A `Project` holds information about its members, and contains lists of IDs for associated epics, sprints, and boards.
|
||||
|
||||
- **`Epic`**: Represents a large body of work or a major feature. An `Epic` is broken down into smaller tasks and can be associated with a project. It tracks its own status, start/due dates, and a list of `child_task_ids`.
|
||||
|
||||
- **`Sprint`**: A time-boxed iteration (e.g., two weeks) during which a team works to complete a set of tasks. A `Sprint` has a goal, a start and end date, its own status (`Planned`, `Active`, `Completed`), and a list of `task_ids`.
|
||||
|
||||
- **`Task`**: The most granular unit of work. A `Task` has a title, description, status, priority, and can be assigned to a user. It can be linked to a parent `Project`, `Epic`, and `Sprint`. Tasks can also be nested using the `parent_task_id` field.
|
||||
|
||||
- **`Label`**: A simple struct for creating tags with a name and a color, which can be used to categorize items.
|
||||
|
||||
## Enums and Statuses
|
||||
|
||||
The model uses several enums to manage the state of work items:
|
||||
|
||||
- **`Priority`**: A general enum (`Critical`, `High`, `Medium`, `Low`) used across different models.
|
||||
- **`Status`**: A general status enum (`Todo`, `InProgress`, `Done`, etc.) for projects.
|
||||
- **`ItemType`**: Describes the type of work item (`Epic`, `Story`, `Task`, `Bug`).
|
||||
- **`SprintStatus`**: Specific statuses for sprints (`Planned`, `Active`, `Completed`).
|
||||
- **`TaskStatus`** and **`TaskPriority`**: Specific enums for the detailed states and priorities of individual tasks.
|
||||
|
||||
## Usage
|
||||
|
||||
The typical workflow involves creating a `Project`, then populating it with `Epic`s and `Sprint`s. `Task`s are then created and associated with these epics and sprints.
|
||||
|
||||
```rust
|
||||
use heromodels::models::projects::{Project, Epic, Sprint, Task, task_enums::{TaskStatus, TaskPriority}};
|
||||
|
||||
// 1. Create a Project
|
||||
let project = Project::new(1, "New Website".to_string(), "Build a new company website".to_string(), 101);
|
||||
|
||||
// 2. Create an Epic for a major feature
|
||||
let mut epic = Epic::new(
|
||||
"User Authentication".to_string(),
|
||||
Some("Implement login, registration, and profile management".to_string()),
|
||||
Default::default(),
|
||||
Some(project.get_id()),
|
||||
None, None, vec![]
|
||||
);
|
||||
|
||||
// 3. Create a Sprint
|
||||
let mut sprint = Sprint::new(
|
||||
"Sprint 1".to_string(),
|
||||
None,
|
||||
Default::default(),
|
||||
Some("Focus on core auth endpoints".to_string()),
|
||||
Some(project.get_id()),
|
||||
None, None
|
||||
);
|
||||
|
||||
// 4. Create a Task and link it to the Epic and Sprint
|
||||
let task = Task::new(
|
||||
"Create login endpoint".to_string(),
|
||||
None,
|
||||
TaskStatus::Todo,
|
||||
TaskPriority::High,
|
||||
Some(102), // assignee_id
|
||||
Some(101), // reporter_id
|
||||
None, // parent_task_id
|
||||
Some(epic.get_id()),
|
||||
Some(sprint.get_id()),
|
||||
Some(project.get_id()),
|
||||
None, None, None, vec!["backend".to_string()]
|
||||
);
|
||||
|
||||
// 5. Add the task ID to the epic and sprint
|
||||
epic = epic.add_task_id(task.get_id());
|
||||
sprint = sprint.add_task_id(task.get_id());
|
||||
```
|
55
heromodels/src/models/userexample/README.md
Normal file
55
heromodels/src/models/userexample/README.md
Normal file
@@ -0,0 +1,55 @@
|
||||
# User Example Model
|
||||
|
||||
The `userexample` model provides a basic but complete example of a model within the `heromodels` ecosystem. It defines a `User` struct that can be used as a template or reference for creating more complex models.
|
||||
|
||||
## `User` Struct
|
||||
|
||||
The `User` struct represents a user in the system and contains the following fields:
|
||||
|
||||
- `base_data`: The standard `BaseModelData` struct, providing a unique ID, timestamps, and comment tracking.
|
||||
- `username`: The user's unique username.
|
||||
- `email`: The user's email address.
|
||||
- `full_name`: The user's full name.
|
||||
- `is_active`: A boolean flag to indicate if the user's account is active.
|
||||
|
||||
The `username`, `email`, and `is_active` fields are indexed for efficient database lookups.
|
||||
|
||||
## Usage
|
||||
|
||||
The `User` model uses the builder pattern for easy and readable instance creation and modification.
|
||||
|
||||
### Creating a User
|
||||
|
||||
You can create a new user and set their properties fluently.
|
||||
|
||||
```rust
|
||||
use heromodels::models::userexample::User;
|
||||
|
||||
// Create a new user and set their details
|
||||
let mut user = User::new()
|
||||
.username("jdoe")
|
||||
.email("jdoe@example.com")
|
||||
.full_name("John Doe");
|
||||
|
||||
// The user is active by default
|
||||
assert_eq!(user.is_active, true);
|
||||
|
||||
// Deactivate the user
|
||||
user.deactivate();
|
||||
assert_eq!(user.is_active, false);
|
||||
|
||||
// Activate the user again
|
||||
user.activate();
|
||||
assert_eq!(user.is_active, true);
|
||||
```
|
||||
|
||||
### Adding Comments
|
||||
|
||||
The model also demonstrates how to interact with the underlying `BaseModelData` to add associated comment IDs.
|
||||
|
||||
```rust
|
||||
use heromodels::models::userexample::User;
|
||||
|
||||
let mut user = User::new().username("jdoe");
|
||||
user = user.add_comment(101); // Add the ID of a comment
|
||||
```
|
Reference in New Issue
Block a user