The HolonDescriptor Builder allows fine-grained update operations to be staged prior to committing them to the HolonDescriptor in the backing store. See UC: Create Holon Type and the Metaspace Design Spec for more details.
Eventually, the Builder will offer three creation options (new, clone, derive). This story is only intended to deliver the 'new' option.
Design Sketch
This design is informed with a thought towards DAHN design.
The interactions between client and server are split following the general principle that the client is responsible for presentation and use case flow, while the server is responsible for security, persistence and validation.
This implies the Use Case Controllers should be added to the stable of DAHN Visualizer types.
In order to allow maximum latitude in use case flow, the Builder object should not make assumptions (or dictate) what information is known when. Thus, all fields within Builder objects should be Option<> fields. This gives maximal freedom to use case designers by allowing them to fall anywhere on the spectrum between creating empty builders at the beginning of a flow to creating fully populated Builders the beginning of a flow.
Note, the also means that the Builder API can be used by External Adaptors to create Descriptors.
At some point, we may chose to persist Builder objects as EntryTypes to allow continuous saving during the building process, but for now we are only maintaining builder information temporarily. Thus, Builders are not EntryTypes.
Until we wrap them behind the MAP Uniform API, the Builder functions need to be visible via the Holochain Conductor, so they are exposed as #[hdk_extern]functions.
Builders incrementally accumulate state until they are committed. Since our servers are stateless, this means state needs to be either maintained on the client side and passed back and forth between the client and server.
Builders are responsible for maintaining the Builder state and, on commit, assigning Semantic Version Numbers to the HolonDescriptors they build as shown in the diagram.
HolonDescriptor properties (including the properties of Composites) reference a PropertyDescriptor which may or may not already be existing. PropertyDescriptorBuilders are used to create new PropertyDescriptors.
PropertyDescriptors may either be dedicated or shared. Dedicated PropertyDescriptors are owned by their HolonDescriptor. Shared PropertyDescriptors are independently stored as Entries and referenced by HolonDescriptors. See Shared or Dedicated PropertyDescriptors for details.
Holon Descriptor Builder Data Structure Definitions
Property Descriptor Builder Data Structure Definitions
#[hdk_entry_helper]
#[derive(new, Clone, PartialEq, Eq)]
pub struct PropertyDescriptorBuilder {
pub header: Option<TypeHeaderBuilder>,
pub details: Option<PropertyDescriptorDetailsBuilder>,
}
pub struct PropertyDescriptorUsageBuilder {
pub description: Option<String>,
pub descriptor: Option<PropertyDescriptor>,
}
pub struct PropertyDescriptorMap {
pub properties: Option<BTreeMap<String, PropertyDescriptorUsage>>,
}
#[hdk_entry_helper]
#[derive(new, Clone, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub enum PropertyDescriptorDetailsBuilder {
Boolean(BooleanDescriptorBuilder),
Composite(CompositeDescriptorBuilder),
//Enum(EnumDescriptorBuilder),
Integer(IntegerDescriptorBuilder),
String(StringDescriptorBuilder),
ValueCollection(ValueCollectionDescriptorBuilder), // can only contain collections of PropertyTypes (not Holons)
}
#[hdk_entry_helper]
#[derive(new, Default, Clone, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct BooleanDescriptorBuilder {
pub is_fuzzy: Option<bool>, // if true, this property has FuzzyBoolean value, otherwise just true or false
}
#[hdk_entry_helper]
#[derive(new, Clone, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct CompositeDescriptorBuilder {
pub properties: Option<PropertyDescriptorMapBuilder>,
}
#[hdk_entry_helper]
#[derive(new, Clone, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct IntegerDescriptorBuilder {
pub format: Option<IntegerFormat>,
pub min_value: Option<i64>,
pub max_value: Option<i64>,
}
#[hdk_entry_helper]
#[derive(Clone, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub enum IntegerFormat {
I8(),
I16(),
I32(),
I64(),
// I128(),
U8(),
U16(),
U32(),
U64(),
// U128(),
}
#[hdk_entry_helper]
#[derive(new, Default, Clone, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct StringDescriptorBuilder {
pub min_length: Option<u32>,
pub max_length: Option<u32>,
//pattern: Option<String>,
}
// This is just a first cut at ValueCollectionDescriptor
// It identifies the kinds of items the collection contains via a string
#[hdk_entry_helper]
#[derive(new, Clone, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct ValueCollectionDescriptorBuilder {
pub contains_items_of_type: Option<HolonReference>,
pub min_items: Option<u32>,
pub max_items: Option<u32>,
pub unique_items: Option<bool>, // true means duplicate items are not allowed
pub is_ordered: Option<bool>, // if items have an intrinsic order
}
Holon Descriptor Builder Creational Operations:
This function creates a new HolonDescriptorBuilder as an object to incrementally stage updates. It is up to the caller to decide how much information to supply to this function -- the function itself accepts completely empty to fully populated parameters to be provided. Invoking commit on the builder will request creation of a new HolonDescriptor (with no prior versions) from the Builder and persisting it in the backing store.
add_string_property_descriptor_builder( map: PropertyDescriptorMap // the property map that will contain the new descriptor type_name: String, description: String, is_dependent: bool, min_length: u32, max_length: u32, ) -> ExternResult<PropertyDescriptorMap>
This function creates a new (empty) PropertyDescriptorBuilder as an object to incrementally stage updates. At this stage, the BaseType of the PropertyDescriptor must be specified before updating its details. Once the Builder is fully populated (i.e., the human agent has specified values for all the meta-properties of the PropertyDescriptor, the PropertyDescriptorBuilder can then be:
committed to persist this propertyType as an independent (a.k.a., shared propertyType)
AND/OR added to a PropertyMap within a HolonDescriptorBuilder.
Creates a new (empty) PropertyDescriptorBuilder. At this point, the next step should be to select the BaseType for the property descriptor, so that the appropriate details can be captured.
removeProperties(
property_map: PropertyDescriptorMap,
properties_to_remove: Vec<String>, // the list of property_names to remove from PropertyMap
)-> ExternResult<PropertyMap>
Marks an existing property as removed from a PropertyMap and returns the updated PropertyMap
FUTURE:
makeIdentifying -- adds an existing property to the list of identfying_properties -- returns Some/Error enum.
makeNotIdentifying -- removes a property from the list of identifying properties -- returns Some/Error enum.
The HolonDescriptor Builder allows fine-grained update operations to be staged prior to committing them to the HolonDescriptor in the backing store. See UC: Create Holon Type and the Metaspace Design Spec for more details.
Eventually, the Builder will offer three creation options (
new
,clone
,derive
). This story is only intended to deliver the 'new' option.Design Sketch
Option<>
fields. This gives maximal freedom to use case designers by allowing them to fall anywhere on the spectrum between creating empty builders at the beginning of a flow to creating fully populated Builders the beginning of a flow.Builder
objects asEntryTypes
to allow continuous saving during the building process, but for now we are only maintaining builder information temporarily. Thus,Builders
are notEntryTypes
.#[hdk_extern]
functions.commit
, assigning Semantic Version Numbers to theHolonDescriptors
they build as shown in the diagram.HolonDescriptor
properties (including the properties of Composites) reference aPropertyDescriptor
which may or may not already be existing.PropertyDescriptorBuilders
are used to create newPropertyDescriptors
.Holon Descriptor Builder Data Structure Definitions
Property Descriptor Builder Data Structure Definitions
Holon Descriptor Builder Creational Operations:
This function creates a new
HolonDescriptorBuilder
as an object to incrementally stage updates. It is up to the caller to decide how much information to supply to this function -- the function itself accepts completely empty to fully populated parameters to be provided. Invokingcommit
on the builder will request creation of a new HolonDescriptor (with no prior versions) from the Builder and persisting it in the backing store.add_string_property_descriptor_builder( map: PropertyDescriptorMap // the property map that will contain the new descriptor type_name: String, description: String, is_dependent: bool, min_length: u32, max_length: u32, ) -> ExternResult<PropertyDescriptorMap>
This function creates a new (empty)
PropertyDescriptorBuilder
as an object to incrementally stage updates. At this stage, theBaseType
of thePropertyDescriptor
must be specified before updating its details. Once the Builder is fully populated (i.e., the human agent has specified values for all the meta-properties of the PropertyDescriptor, the PropertyDescriptorBuilder can then be:getStringPropertyDescriptorBuilder(PropertyDescriptorBuilder)-> ExternResult
Creates a new (empty) PropertyDescriptorBuilder. At this point, the next step should be to select the BaseType for the property descriptor, so that the appropriate details can be captured.
addPropertyDescriptor(within_builder: HolonDescriptorBuilder, at_path: PropertyPath, ) -> ExternResult<HolonDescriptorBuilder>
Adds a new (empty) String property to the
PropertyMap
found via the specifiedat_path
. The PropertyDescribpub fn new_composite_descriptor( type_name: String, description: String, is_dependent: bool, properties: PropertyDescriptorMap, ) -> Result<PropertyDescriptor, DescriptorsError> { let details = PropertyDescriptorDetails::Composite(CompositeDescriptor::new(properties));
}
pub fn new_string_descriptor( type_name: String, description: String, is_dependent: bool, min_length: u32, max_length: u32, ) -> Result<PropertyDescriptor, DescriptorsError> { let details = PropertyDescriptorDetails::String(StringDescriptor::new(min_length, max_length)); let desc = new_property_descriptor( type_name, description, BaseType::String, is_dependent, details, )?; Ok(desc) }
pub fn new_integer_descriptor( type_name: String, description: String, is_dependent: bool, format: IntegerFormat, min_value: i64, max_value: i64, ) -> Result<PropertyDescriptor, DescriptorsError> { let details = PropertyDescriptorDetails::Integer(IntegerDescriptor::new(format, min_value, max_value)); let desc = new_property_descriptor( type_name, description, BaseType::Integer, is_dependent, details, )?; Ok(desc) } pub fn new_boolean_descriptor( type_name: String, description: String, is_dependent: bool, is_fuzzy: bool, ) -> Result<PropertyDescriptor, DescriptorsError> { let details = PropertyDescriptorDetails::Boolean(BooleanDescriptor::new(is_fuzzy)); let desc = new_property_descriptor( type_name, description, BaseType::Boolean, is_dependent, details, )?; Ok(desc) }
Marks an existing property as removed from a PropertyMap and returns the updated PropertyMap
FUTURE:
makeIdentifying -- adds an existing property to the list of identfying_properties -- returns Some/Error enum.
makeNotIdentifying -- removes a property from the list of identifying properties -- returns Some/Error enum.