Structures and Enumerations

Structures and enumerations are user-defined types that require registration with Definitions before use. This chapter covers creating and working with both.

Creating Structures with Definitions

Structures are defined within a Definitions context:

>>> from dsviper import *

# Create definitions
>>> defs = Definitions()
>>> ns = NameSpace(ValueUUId("f529bc42-0618-4f54-a3fb-d55f95c5ad03"), "Tuto")

# Define structure with descriptor
>>> desc = TypeStructureDescriptor("Login")
>>> desc.add_field("nickname", Type.STRING)
>>> desc.add_field("password", Type.STRING)

# Create the type
>>> t_login = defs.create_structure(ns, desc)
>>> t_login
Tuto::Login

Instantiating Structures

Create structure instances with Value.create():

# Default values (zeros)
>>> login = Value.create(t_login)
>>> login
{nickname='', password=''}

# Initialize from dict
>>> login = Value.create(t_login, {"nickname": "zoop"})
>>> login
{nickname='zoop', password=''}

Field Access

Access fields by name:

# Get field
>>> login.nickname
'zoop'

# Set field
>>> login.password = "secret"
>>> login
{nickname='zoop', password='secret'}

Type Checking

Field assignments are type-checked:

>>> login.nickname = 42
ViperError: expected type 'str', got 'int'

Default Values

Specify default values when defining fields:

>>> desc = TypeStructureDescriptor("Config")
>>> desc.add_field("scale", ValueFloat(1.0))
>>> desc.add_field("items", Value.create(TypeVector(Type.INT64), [1, 2, 3]))

>>> t_config = defs.create_structure(ns, desc)
>>> Value.create(t_config)
{scale=1.0, items=[1, 2, 3]}

Nested Structures

Structures can contain other structures:

# Define Position
>>> desc_pos = TypeStructureDescriptor("Position")
>>> desc_pos.add_field("x", Type.FLOAT)
>>> desc_pos.add_field("y", Type.FLOAT)
>>> t_position = defs.create_structure(ns, desc_pos)

# Define Vertex containing Position
>>> desc_vertex = TypeStructureDescriptor("Vertex")
>>> desc_vertex.add_field("position", t_position)
>>> desc_vertex.add_field("label", Type.STRING)
>>> t_vertex = defs.create_structure(ns, desc_vertex)

# Create instance
>>> vertex = Value.create(t_vertex)
>>> vertex
{position={x=0.0, y=0.0}, label=''}

>>> vertex.position.x = 10.0
>>> vertex.position.y = 20.0
>>> vertex.label = "A"
>>> vertex
{position={x=10.0, y=20.0}, label='A'}

Paths

A Path locates a piece of information within a value:

# Create path to field
>>> p = Path.from_field("nickname").const()
>>> p
.nickname

# Apply path to get value
>>> p.at(login)
'zoop'

# Apply path to set value
>>> p.set(login, "new_nick")
>>> login.nickname
'new_nick'

Complex Paths

Paths can traverse nested structures:

# Path to nested field
>>> p = Path.from_field("position").field("x").const()
>>> p
.position.x

>>> p.at(vertex)
10.0
>>> p.set(vertex, 15.0)

Path Components

Paths can include:

  • Field access: .field("name")

  • Index access: .index(0)

  • Unwrap: .unwrap()

  • Map key: .key(key_value)

# Complex path
>>> p = Path.from_field("items").index(0).const()
>>> p
.items[0]

>>> p.components()
[{type: Field, value: 'items'},
 {type: Index, value: 0}]

Enumerations

Enumerations are types with a fixed set of named cases.

Defining an Enumeration

>>> desc = TypeEnumerationDescriptor("Status", documentation="Task status")
>>> desc.add_case("pending", "Waiting to start")
>>> desc.add_case("active", "In progress")
>>> desc.add_case("completed", "Finished")

>>> t_status = defs.create_enumeration(ns, desc)
>>> t_status
Tuto::Status

Creating Enumeration Values

Create values by case name, index, or default:

# By case name (most common)
>>> status = ValueEnumeration(t_status, "active")
>>> status.name()
'active'

# By index (0-based)
>>> status = ValueEnumeration(t_status, 0)
>>> status.name()
'pending'

# Default (first case)
>>> Value.create(t_status)
.pending

# Specify case with Value.create()
>>> Value.create(t_status, "completed")
.completed

Properties

>>> status = ValueEnumeration(t_status, "active")

>>> status.name()       # Case name
'active'

>>> status.index()      # Case index (0-based)
1

>>> status.type_enumeration()  # Type reference
Tuto::Status

Type Introspection

Query available cases from the type:

>>> cases = t_status.cases()
>>> [c.name() for c in cases]
['pending', 'active', 'completed']

# Query by name
>>> case = t_status.query("active")
>>> case.documentation()
'In progress'

# Check existence (raises on invalid)
>>> t_status.check("unknown")
ViperError: UndefinedEnumerationCaseSymbol

Comparison

Enumerations compare lexicographically by name, not by index:

>>> pending = ValueEnumeration(t_status, "pending")   # index 0
>>> active = ValueEnumeration(t_status, "active")     # index 1

>>> active < pending   # "active" < "pending" alphabetically
True

Constraints

  • Maximum 256 cases per enumeration (uint8 index)

  • Case names must be unique within an enumeration

  • Empty enumerations are not allowed

Type Information

Introspect structure types:

>>> t_login.fields()
[nickname, password]

>>> field = t_login.fields()[0]
>>> field.name()
'nickname'
>>> field.type()
string

Using Structures with Databases

Structures are typically stored via attachments. See DSM for defining attachments and Database for persistence patterns.

Quick preview:

# With DSM definitions loaded
>>> key = TUTO_A_USER_LOGIN.create_key()
>>> login = TUTO_A_USER_LOGIN.create_document()
>>> login.nickname = "alice"

# Store in database
>>> db.set(TUTO_A_USER_LOGIN, key, login)

What’s Next