Crate kodiak_taxonomy

source ·
Expand description

Get things organized with this powerful, yet easy to use taxonomy.

This library crate is a building block of the Kodiak project, thus the naming of the crate. Although, Kodiak has some quite specific requirements for a taxonomy, kodiak-taxonomy is kept generic, provides value on its own and might be of interest for other projects as well.

A taxonomy1 is a classification scheme to organize things according to their types. Often, taxonomies offer a strictly hierarchical organization providing a tree-like structure. However, in practice, such taxonomies limit our ability to model our complex reality.

When looking for a powerful taxonomy which overcomes such limitations, kodiak-taxonomy might be a good fit for you. So, feel free to use it. If you consider using kodiak-taxonomy in your project but are missing functionality or have any other concerns, don’t hesitate to file an issue on Github.

We are looking forward to your feedback.

Getting started

The following example showcases how to use kodiak-taxonomy. We build a Taxonomy supporting two processes: Human Resource Management (HRM) and Configuration Management Database (CMDB). In both processes we have to organize users and we want to keep the concept of a User identical across the organization. So, we add User to both processes. This taxonomy would look like:

├── HRM
│   └── User
└── CMDB
    ├── User (identical to HRM::User)
    └── Device
        ├── Server
        └── Client

Here is the code.

use kodiak_taxonomy::{Identity, Taxonomy, TaxonomyError};

// Simplistic type `Class` to store in taxonomy (kodiak-taxonomy supports arbitrary types).
#[derive(Debug)]
struct Class {
  name: String,
}

// As a prerequisite for `kodiak-taxonomy` we have to implement the Identity trait for `Class`.
// String implements `Hash` and `Eq` thus it's easy to implement `Identity` on top of it.
impl Identity<String> for Class {
  fn id(&self) -> String {
    self.name.clone()
  }
}

fn main() -> Result<(), TaxonomyError<String>> {
  let mut tax: Taxonomy<String, Class> = Taxonomy::new();

  // Create various items and store their ids
  let hrm = Class{name: "HRM".to_string()};
  let hrm_id = hrm.id();

  let user = Class{name: "User".to_string()};
  let user_id = user.id();

  let cmdb = Class{name: "CMDB".to_string()};
  let cmdb_id = cmdb.id();

  // Add HRM and CMDB as root-node.
  tax.add(None, hrm)?
     .add(None, cmdb);

  // Add User as a sub-node of HRM
  tax.add(Some(hrm_id), user);

  // Append User as a sub-node of CMDB, we use append() because
  // User has been added to taxonomy before.
  tax.append(Some(cmdb_id.clone()), user_id);

  // Create and add another `Class`. This time we get the id from the taxonomy.
  let device = Class{name: "Device".to_string()};
  tax.add(Some(cmdb_id), device);
  let device_id = tax.last_updated_node().unwrap().to_string();

  // Add additional sub-nodes to Devices.
  let server = Class{name: "Server".to_string()};
  let client = Class{name: "Client".to_string()};
  tax.add(Some(device_id.clone()), server)?
     .add(Some(device_id), client);

  // Traverse the taxonomy and print the classes.
  while let Some(class) = tax.traverse() {
    println!("{:?}", class);
  }

  Ok(())
}

The library provides many more functions. Have a look at detailed documentation provided.

TL;DR

Kodiak’s specific requirements regarding its taxonomy and its implementation status:

  1. The top of the taxonomy allows multiple elements, i.e. users are free to create multiple root-nodes and are not forced to invent a super-node.
  2. An element can have more than one superordinate element
  3. Elements might be complemented by arbitrary meta data (still todo: not implemented yet)
  4. Edges (a tuple of a super and its sub element) might be complemented with arbitrary attributes (still todo: not implemented yet)

Structs

A taxonomy of equally typed nodes which allows a node to have zero, one or more super-nodes.

Enums

Errors that might occur when managing a taxonomy.

Traits

Elements need an identity of type K with K: Hash + Eq.