Repository: bbux-dev/datacraft (language: python)

Issue:
Command Line Usage for Registered Types
Added --type-list --type-help and --type-filter command line arguments and supporting code.


Discussion / hints:

Fix patch:
diff --git a/CHANGES.md b/CHANGES.md
index c4bf508..005aa88 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,3 +1,7 @@
+v0.4.0
+------
+* Added initial built in type help system with command line --type-list and --type-help command line arguments
+
 v0.3.2
 --------
 * Switched to lru_cache for pre python 3.9 compatibility
diff --git a/datacraft/__init__.py b/datacraft/__init__.py
index 4d61f7a..b13ada9 100644
--- a/datacraft/__init__.py
+++ b/datacraft/__init__.py
@@ -5,7 +5,7 @@
 # model classes that may be implemented externally
 from .supplier.model import (
     DataSpec, ValueSupplierInterface, Distribution, CasterInterface, RecordProcessor, OutputHandlerInterface,
-    ResettableIterator)
+    ResettableIterator, KeyProviderInterface)
 # programmatic spec building
 from . import builder
 # expose this at root too
@@ -16,8 +16,8 @@
 from .exceptions import SpecException, ResourceError
 from .supplier.exceptions import SupplierException
 # commonly used by client code
-from .loader import field_loader, Loader
-from . import suppliers, distributions, outputs
+from .loader import preprocess_and_format, field_loader, Loader
+from . import suppliers, distributions, outputs, utils
 # to trigger registered functions
 from . import cli
 
diff --git a/datacraft/_registered_types.py b/datacraft/_registered_types.py
deleted file mode 100644
index 0626ebd..0000000
--- a/datacraft/_registered_types.py
+++ /dev/null
@@ -1,816 +0,0 @@
-"""
-Internal module where the built in types are registered and configured
-"""
-import csv
-import datetime
-import json
-import logging
-import os
-import string
-
-from . import distributions, suppliers, registries, schemas, utils
-from .exceptions import SpecException
-from .loader import Loader
-from .supplier import key_suppliers
-from .supplier.common import weighted_values_explicit
-# for nested
-from .supplier.nested import nested_supplier
-# for refs
-from .supplier.refs import weighted_ref_supplier
-# for calculate
-from .suppliers import calculate
-
-##############
-# Type Keys
-##############
-
-_VALUES_KEY = 'values'
-_CALCULATE_KEY = 'calculate'
-_CHAR_CLASS_KEY = 'char_class'
-_DATE_KEY = 'date'
-_DATE_ISO_KEY = 'date.iso'
-_DATE_ISO_US_KEY = 'date.iso.us'
-_GEO_LAT_KEY = 'geo.lat'
-_GEO_LONG_KEY = 'geo.long'
-_GEO_PAIR_KEY = 'geo.pair'
-_COMBINE_KEY = 'combine'
-_COMBINE_LIST_KEY = 'combine-list'
-_CSV_TYPE = 'csv'
-_WEIGHTED_CSV = 'weighted_csv'
-_IP_KEY = 'ip'
-_IPV4_KEY = 'ipv4'
-_IP_PRECISE_KEY = 'ip.precise'
-_NET_MAC_KEY = 'net.mac'
-_RANGE_KEY = 'range'
-_RAND_RANGE_KEY = 'rand_range'
-_RAND_INT_RANGE_KEY = 'rand_int_range'
-_REF_KEY = 'ref'
-_WEIGHTED_REF_KEY = 'weighted_ref'
-_SELECT_LIST_SUBSET_KEY = 'select_list_subset'
-_UNICODE_RANGE_KEY = 'unicode_range'
-_UUID_KEY = 'uuid'
-_DISTRIBUTION_KEY = 'distribution'
-_TEMPLATED_KEY = 'templated'
-_NESTED_KEY = 'nested'
-_CONFIG_REF_KEY = 'config_ref'
-
-_log = logging.getLogger('datacraft.types')
-
-
-##############
-# values
-##############
-@registries.Registry.schemas(_VALUES_KEY)
-def _get_values_schema():
-    """ returns the values schema """
-    return schemas.load(_VALUES_KEY)
-
-
-@registries.Registry.types(_VALUES_KEY)
-def _handle_values_type(spec, loader):
-    """ handles values types """
-    config = utils.load_config(spec, loader)
-    return suppliers.values(spec, **config)
-
-
-##############
-# calculate
-##############
-@registries.Registry.schemas(_CALCULATE_KEY)
-def _calculate_schema():
-    """ get the schema for the calculate type """
-    return schemas.load(_CALCULATE_KEY)
-
-
-@registries.Registry.types(_CALCULATE_KEY)
-def _configure_calculate_supplier(field_spec: dict, loader: Loader):
-    """ configures supplier for calculate type """
-
-    formula = field_spec.get('formula')
-    if formula is None:
-        raise SpecException('Must define formula for calculate type. %s' % json.dumps(field_spec))
-
-    suppliers_map = _build_suppliers_map(field_spec, loader)
-
-    return calculate(suppliers_map=suppliers_map, formula=formula)
-
-
-@registries.Registry.schemas(_TEMPLATED_KEY)
-def _templated_schema():
-    return schemas.load(_TEMPLATED_KEY)
-
-
-@registries.Registry.types(_TEMPLATED_KEY)
-def _configure_templated_type(field_spec, loader):
-    if 'data' not in field_spec:
-        raise SpecException(f'data is required field for templated specs: {json.dumps(field_spec)}')
-    suppliers_map = _build_suppliers_map(field_spec, loader)
-
-    return suppliers.templated(suppliers_map, field_spec.get('data', None))
-
-
-def _build_suppliers_map(field_spec, loader):
-    if 'refs' not in field_spec and 'fields' not in field_spec:
-        raise SpecException('Must define one of fields or refs. %s' % json.dumps(field_spec))
-    if 'refs' in field_spec and 'fields' in field_spec:
-        raise SpecException('Must define only one of fields or refs. %s' % json.dumps(field_spec))
-    mappings = _get_mappings(field_spec, 'refs')
-    mappings.update(_get_mappings(field_spec, 'fields'))
-    if len(mappings) < 1:
-        raise SpecException('fields or refs empty: %s' % json.dumps(field_spec))
-    suppliers_map = {}
-    for field_or_ref, alias in mappings.items():
-        supplier = loader.get(field_or_ref)
-        suppliers_map[alias] = supplier
-    return suppliers_map
-
-
-def _get_mappings(field_spec, lookup_key):
-    """ retrieve the field aliasing for the given key, refs or fields """
-    mappings = field_spec.get(lookup_key, [])
-    if isinstance(mappings, list):
-        mappings = {_key: _key for _key in mappings}
-    return mappings
-
-
-###############
-# char_class
-###############
-_UNDER_SCORE = '_'
-
-_CLASS_MAPPING = {
-    "ascii": ''.join(chr(x) for x in range(128)),
-    "lower": string.ascii_lowercase,
-    "upper": string.ascii_uppercase,
-    "letters": string.ascii_letters,
-    "word": f'{string.ascii_letters}{string.digits}{_UNDER_SCORE}',
-    "printable": string.printable,
-    "visible": ''.join(string.printable.split()),
-    "punctuation": string.punctuation,
-    "special": string.punctuation,
-    "digits": string.digits,
-    "hex": string.hexdigits,
-    "hex-lower": string.digits + 'abcdef',
-    "hex-upper": string.digits + 'ABCDEF',
-}
-
-
-@registries.Registry.schemas(_CHAR_CLASS_KEY)
-def _get_char_class_schema():
-    """ get the schema for the char_class type """
-    return schemas.load(_CHAR_CLASS_KEY)
-
-
-for class_key in _CLASS_MAPPING:
-    @registries.Registry.schemas("cc-" + class_key)
-    def _get_char_class_alias_schema():
-        """ get the schema for the char_class type """
-        return schemas.load(_CHAR_CLASS_KEY)
-
-
-@registries.Registry.types(_CHAR_CLASS_KEY)
-def _configure_char_class_supplier(spec, loader):
-    """ configure the supplier for char_class types """
-    if 'data' not in spec:
-        raise SpecException(f'Data is required field for char_class type: {json.dumps(spec)}')
-    config = utils.load_config(spec, loader)
-    data = spec['data']
-    if isinstance(data, str) and data in _CLASS_MAPPING:
-        data = _CLASS_MAPPING[data]
-    if isinstance(data, list):
-        new_data = [_CLASS_MAPPING[datum] if datum in _CLASS_MAPPING else datum for datum in data]
-        data = ''.join(new_data)
-
-    if 'join_with' not in config:
-        config['join_with'] = registries.get_default('char_class_join_with')
-
-    return suppliers.character_class(data, **config)
-
-
-for class_key in _CLASS_MAPPING:
-    @registries.Registry.types("cc-" + class_key)
-    def _configure_char_class_alias_supplier(spec, loader):
-        """ configure the supplier for char_class alias types """
-        spec['data'] = class_key
-        return _configure_char_class_supplier(spec, loader)
-
-
-############
-# combine
-############
-@registries.Registry.schemas(_COMBINE_KEY)
-def _get_combine_schema():
-    """ get the schema for the combine type """
-    return schemas.load(_COMBINE_KEY)
-
-
-@registries.Registry.schemas(_COMBINE_LIST_KEY)
-def _get_combine_list_schema():
-    """ get the schema for the combine_list type """
-    return schemas.load(_COMBINE_LIST_KEY)
-
-
-@registries.Registry.types(_COMBINE_KEY)
-def _configure_combine_supplier(field_spec, loader):
-    """ configures supplier for combine type """
-    if 'refs' not in field_spec and 'fields' not in field_spec:
-        raise SpecException('Must define one of fields or refs. %s' % json.dumps(field_spec))
-
-    if 'refs' in field_spec:
-        supplier = _load_combine_from_refs(field_spec, loader)
-    else:
-        supplier = _load_combine_from_fields(field_spec, loader)
-    return supplier
-
-
-@registries.Registry.types(_COMBINE_LIST_KEY)
-def _configure_combine_list_supplier(field_spec, loader):
-    """ configures supplier for combine-list type """
-    if 'refs' not in field_spec:
-        raise SpecException('Must define refs for combine-list type. %s' % json.dumps(field_spec))
-
-    refs_list = field_spec['refs']
-    if len(refs_list) < 1 or not isinstance(refs_list[0], list):
-        raise SpecException(
-            'refs pointer must be list of lists: i.e [["ONE", "TWO"]]. %s' % json.dumps(field_spec))
-
-    suppliers_list = []
-    for ref in refs_list:
-        spec = dict(field_spec)
-        spec['refs'] = ref
-        suppliers_list.append(_load_combine_from_refs(spec, loader))
-    return suppliers.from_list_of_suppliers(suppliers_list, True)
-
-
-def _load_combine_from_refs(combine_field_spec, loader):
-    """ loads the combine type from a set of refs """
-    keys = combine_field_spec.get('refs')
-    return _load_combine(combine_field_spec, keys, loader)
-
-
-def _load_combine_from_fields(combine_field_spec, loader):
-    """ load the combine type from a set of field names """
-    keys = combine_field_spec.get('fields')
-    return _load_combine(combine_field_spec, keys, loader)
-
-
-def _load_combine(combine_field_spec, keys, loader):
-    """ create the combine supplier for the types from the given keys """
-    to_combine = []
-    for key in keys:
-        supplier = loader.get(key)
-        to_combine.append(supplier)
-    config = combine_field_spec.get('config', {})
-    as_list = config.get('as_list', registries.get_default('combine_as_list'))
-    joiner = config.get('join_with', registries.get_default('combine_join_with'))
-    return suppliers.combine(to_combine, joiner, as_list)
-
-
-############
-# config_ref
-############
-@registries.Registry.types(_CONFIG_REF_KEY)
-def _config_ref_handler(_, __):
-    """" Does nothing, just place holder """
-
-
-########
-# csv
-#######
-
-
-@registries.Registry.types(_CSV_TYPE)
-def _configure_csv(field_spec, loader):
-    """ Configures the csv value supplier for this field """
-    config = utils.load_config(field_spec, loader)
-    datafile = config.get('datafile', registries.get_default('csv_file'))
-    csv_path = f'{loader.datadir}/{datafile}'
-    if not os.path.exists(csv_path):
-        raise SpecException(f'Unable to locate data file: {datafile} in data dir: {loader.datadir} for spec: '
-                            + json.dumps(field_spec))
-    return suppliers.csv(csv_path, **config)
-
-
-@registries.Registry.schemas(_CSV_TYPE)
-def _get_csv_schema():
-    """ get the schema for the csv type """
-    return schemas.load(_CSV_TYPE)
-
-
-@registries.Registry.types(_WEIGHTED_CSV)
-def _configure_weighted_csv(field_spec, loader):
-    """ Configures the weighted_csv value supplier for this field """
-
-    config = utils.load_config(field_spec, loader)
-
-    field_name = config.get('column', 1)
-    weight_column = config.get('weight_column', 2)
-    count_supplier = suppliers.count_supplier(**config)
-
-    datafile = config.get('datafile', registries.get_default('csv_file'))
-    csv_path = f'{loader.datadir}/{datafile}'
-    has_headers = utils.is_affirmative('headers', config)
-    numeric_index = isinstance(field_name, int)
-    if numeric_index and field_name < 1:
-        raise SpecException(f'Invalid index {field_name}, one based indexing used for column numbers')
-
-    if has_headers and not numeric_index:
-        choices = _read_named_column(csv_path, field_name)
-        weights = _read_named_column_weights(csv_path, weight_column)
-    else:
-        choices = _read_indexed_column(csv_path, int(field_name), skip_first=numeric_index)
-        weights = _read_indexed_column_weights(csv_path, int(weight_column), skip_first=numeric_index)
-    return weighted_values_explicit(choices, weights, count_supplier)
-
-
-@registries.Registry.schemas(_WEIGHTED_CSV)
-def _get_weighted_csv_schema():
-    """ get the schema for the weighted_csv type """
-    return schemas.load(_WEIGHTED_CSV)
-
-
-def _read_named_column(csv_path: str, column_name: str):
-    """ reads values from a named column into a list """
-    with open(csv_path, newline='') as csvfile:
-        reader = csv.DictReader(csvfile)
-        return [val[column_name] for val in reader]
-
-
-def _read_named_column_weights(csv_path: str, column_name: str):
-    """ reads values for weights for named column into a list """
-    with open(csv_path, newline='') as csvfile:
-        reader = csv.DictReader(csvfile)
-        return [float(val[column_name]) for val in reader]
-
-
-def _read_indexed_column(csv_path: str, column_index: int, skip_first: bool):
-    """ reads values from a indexed column into a list """
-    with open(csv_path, newline='') as csvfile:
-        reader = csv.reader(csvfile)
-        if skip_first:
-            next(reader)
-        return [val[column_index - 1] for val in reader]
-
-
-def _read_indexed_column_weights(csv_path: str, column_index: int, skip_first: bool):
-    """ reads values for weights for indexed column into a list """
-    with open(csv_path, newline='') as csvfile:
-        reader = csv.reader(csvfile)
-        if skip_first:
-            next(reader)
-        return [float(val[column_index - 1]) for val in reader]
-
-
-#######
-# date
-#######
-_ISO_FORMAT_NO_MICRO = '%Y-%m-%dT%H:%M:%S'
-_ISO_FORMAT_WITH_MICRO = '%Y-%m-%dT%H:%M:%S.%f'
-
-
-@registries.Registry.schemas(_DATE_KEY)
-def _get_date_schema():
-    """ returns the schema for date types """
-    return schemas.load(_DATE_KEY)
-
-
-@registries.Registry.schemas(_DATE_ISO_KEY)
-def _get_date_iso_schema():
-    """ returns the schema for date.iso types """
-    # NOTE: These all share a schema
-    return schemas.load(_DATE_KEY)
-
-
-@registries.Registry.schemas(_DATE_ISO_US_KEY)
-def _get_date_iso_us_schema():
-    """ returns the schema for date.iso.us types """
-    # NOTE: These all share a schema
-    return schemas.load(_DATE_KEY)
-
-
-@registries.Registry.types(_DATE_KEY)
-def _configure_date_supplier(field_spec: dict, loader: Loader):
-    """ configures the date value supplier """
-    config = utils.load_config(field_spec, loader)
-    config['hour_supplier'] = _hour_supplier(config, loader)
-    return suppliers.date(**config)
-
-
-def _hour_supplier(config: dict, loader: Loader):
-    """ get hour supplier from config if present """
-    if 'hours' not in config:
-        return None
-    return loader.get_from_spec(config['hours'])
-
-
-@registries.Registry.types(_DATE_ISO_KEY)
-def _configure_supplier_iso(field_spec: dict, loader: Loader):
-    """ configures the date.iso value supplier """
-    return _configure_supplier_iso_date(field_spec, loader, _ISO_FORMAT_NO_MICRO)
-
-
-@registries.Registry.types(_DATE_ISO_US_KEY)
-def _configure_supplier_iso_microseconds(field_spec: dict, loader: Loader):
-    """ configures the date.iso.us value supplier """
-    return _configure_supplier_iso_date(field_spec, loader, _ISO_FORMAT_WITH_MICRO)
-
-
-def _configure_supplier_iso_date(field_spec, loader, iso_date_format):
-    """ configures an iso based date supplier using the provided date format """
-    config = utils.load_config(field_spec, loader)
-    # make sure the start and end dates match the ISO format we are using
-    start = config.get('start')
-    end = config.get('end')
-    date_format = config.get('format', )
-    if start:
-        start_date = datetime.datetime.strptime(start, date_format)
-        config['start'] = start_date.strftime(iso_date_format)
-    if end:
-        end_date = datetime.datetime.strptime(end, date_format)
-        config['end'] = end_date.strftime(iso_date_format)
-    config['format'] = iso_date_format
-    # End fixes to support iso
-    config['hour_supplier'] = _hour_supplier(config, loader)
-    return suppliers.date(**config)
-
-
-############
-# geo
-###########
-@registries.Registry.schemas(_GEO_LAT_KEY)
-def _get_geo_lat_schema():
-    """ schema for geo.lat type """
-    return schemas.load(_GEO_LAT_KEY)
-
-
-@registries.Registry.schemas(_GEO_LONG_KEY)
-def _get_geo_long_schema():
-    """ schema for geo.long type """
-    return schemas.load(_GEO_LONG_KEY)
-
-
-@registries.Registry.schemas(_GEO_PAIR_KEY)
-def _get_geo_pair_schema():
-    """ schema for geo.pair type """
-    return schemas.load(_GEO_PAIR_KEY)
-
-
-@registries.Registry.types(_GEO_LAT_KEY)
-def _configure_geo_lat(field_spec, loader):
-    """ configures value supplier for geo.lat type """
-    config = utils.load_config(field_spec, loader)
-    return suppliers.geo_lat(**config)
-
-
-@registries.Registry.types(_GEO_LONG_KEY)
-def _configure_geo_long(field_spec, loader):
-    """ configures value supplier for geo.long type """
-    config = utils.load_config(field_spec, loader)
-    return suppliers.geo_long(**config)
-
-
-@registries.Registry.types(_GEO_PAIR_KEY)
-def _configure_geo_pair(field_spec, loader):
-    """ configures value supplier for geo.pair type """
-    config = utils.load_config(field_spec, loader)
-    return suppliers.geo_pair(**config)
-
-
-###########
-# nested
-##########
-@registries.Registry.schemas(_NESTED_KEY)
-def _get_nested_schema():
-    """ schema for nested type """
-    return schemas.load(_NESTED_KEY)
-
-
-@registries.Registry.types(_NESTED_KEY)
-def _configure_nested_supplier(spec, loader):
-    """ configure the supplier for nested types """
-    fields = spec['fields']
-    keys = [key for key in fields.keys()]
-    config = utils.load_config(spec, loader)
-    count_supplier = suppliers.count_supplier(**config)
-    if 'field_groups' in spec:
-        key_supplier = key_suppliers.from_spec(spec)
-    else:
-        key_supplier = key_suppliers.from_spec(fields)
-
-    as_list = utils.is_affirmative('as_list', config)
-
-    field_supplier_map = {}
-    # each non reserved key should have a valid spec as a value
-    for key in keys:
-        nested_spec = fields[key]
-        if 'type' in nested_spec and nested_spec.get('type') == _NESTED_KEY:
-            supplier = _configure_nested_supplier(nested_spec, loader)
-        else:
-            supplier = loader.get_from_spec(nested_spec)
-        field_supplier_map[key] = supplier
-    return nested_supplier(field_supplier_map, count_supplier, key_supplier, as_list)
-
-
-###########
-# network
-###########
-@registries.Registry.schemas(_IP_KEY)
-def _get_ip_schema():
-    """ returns the schema for the ip types """
-    return schemas.load(_IP_KEY)
-
-
-@registries.Registry.schemas(_IPV4_KEY)
-def _get_ipv4_schema():
-    """ returns the schema for the ipv4 types """
-    # shares schema with ip
-    return schemas.load(_IP_KEY)
-
-
-@registries.Registry.schemas(_IP_PRECISE_KEY)
-def _get_ip_precise_schema():
-    """ returns the schema for the ip.precise types """
-    return schemas.load(_IP_PRECISE_KEY)
-
-
-@registries.Registry.schemas(_NET_MAC_KEY)
-def _get_mac_addr_schema():
-    """ returns the schema for the net.mac types """
-    return schemas.load(_NET_MAC_KEY)
-
-
-@registries.Registry.types(_IPV4_KEY)
-@registries.Registry.types(_IP_KEY)
-def _configure_ip(field_spec, loader):
-    """ configures value supplier for ip type """
-    config = utils.load_config(field_spec, loader)
-    try:
-        return suppliers.ip_supplier(**config)
-    except ValueError as err:
-        raise SpecException(str(err)) from err
-
-
-@registries.Registry.types(_IP_PRECISE_KEY)
-def _configure_precise_ip(field_spec, _):
-    """ configures value supplier for ip.precise type """
-    config = field_spec.get('config')
-    if config is None:
-        raise SpecException('No config for: ' + json.dumps(field_spec) + ', param cidr required')
-    cidr = config.get('cidr')
-    sample = utils.is_affirmative('sample', config, 'no')
-    if cidr is None:
-        raise SpecException('Invalid config for: ' + json.dumps(field_spec) + ', param cidr required')
-    return suppliers.ip_precise(cidr, sample)
-
-
-@registries.Registry.types(_NET_MAC_KEY)
-def _configure_mac_address_supplier(field_spec, loader):
-    """ configures value supplier for net.mac type """
-    config = utils.load_config(field_spec, loader)
-    if utils.is_affirmative('dashes', config):
-        delim = '-'
-    else:
-        delim = registries.get_default('mac_addr_separator')
-
-    return suppliers.mac_address(delim)
-
-
-###################
-# range_suppliers
-###################
-@registries.Registry.schemas(_RANGE_KEY)
-def _get_range_schema():
-    """ schema for range type """
-    return schemas.load(_RANGE_KEY)
-
-
-@registries.Registry.schemas(_RAND_RANGE_KEY)
-def _get_rand_range_schema():
-    """ schema for rand range type """
-    # This shares a schema with range
-    return schemas.load(_RANGE_KEY)
-
-
-@registries.Registry.schemas(_RAND_INT_RANGE_KEY)
-def _get_rand_int_range_schema():
-    """ schema for rand int range type """
-    # This shares a schema with range
-    return schemas.load(_RANGE_KEY)
-
-
-@registries.Registry.types(_RANGE_KEY)
-def _configure_range_supplier(field_spec, _):
-    """ configures the range value supplier """
-    if 'data' not in field_spec:
-        raise SpecException('No data element defined for: %s' % json.dumps(field_spec))
-
-    data = field_spec.get('data')
-    if not isinstance(data, list) or len(data) < 2:
-        raise SpecException(
-            'data element for ranges type must be list with at least two elements: %s' % json.dumps(field_spec))
-    # we have the nested case
-    if isinstance(data[0], list):
-        suppliers_list = [_configure_range_supplier_for_data(field_spec, subdata) for subdata in data]
-        return suppliers.from_list_of_suppliers(suppliers_list, True)
-    return _configure_range_supplier_for_data(field_spec, data)
-
-
-def _configure_range_supplier_for_data(field_spec, data):
-    """ configures the supplier based on the range data supplied """
-    config = field_spec.get('config', {})
-    precision = config.get('precision', None)
-    if precision and not str(precision).isnumeric():
-        raise SpecException(f'precision must be valid integer {json.dumps(field_spec)}')
-
-    start = data[0]
-    # default for built in range function is exclusive end, we want to default to inclusive as this is the
-    # more intuitive behavior
-    end = data[1] + 1
-    if not end > start:
-        raise SpecException('end element must be larger than start: %s' % json.dumps(field_spec))
-    if len(data) == 2:
-        step = 1
-    else:
-        step = data[2]
-    try:
-        return suppliers.range_supplier(start, end, step, precision=precision)
-    except ValueError as err:
-        raise SpecException(str(err)) from err
-
-
-@registries.Registry.types(_RAND_INT_RANGE_KEY)
-def _configure_rand_int_range_supplier(field_spec, loader):
-    """ configures the random int range value supplier """
-    config = utils.load_config(field_spec, loader)
-    config['cast'] = 'int'
-    field_spec['config'] = config
-    return _configure_rand_range_supplier(field_spec, loader)
-
-
-@registries.Registry.types(_RAND_RANGE_KEY)
-def _configure_rand_range_supplier(field_spec, loader):
-    """ configures the random range value supplier """
-    if 'data' not in field_spec:
-        raise SpecException('No data element defined for: %s' % json.dumps(field_spec))
-    data = field_spec.get('data')
-    config = utils.load_config(field_spec, loader)
-    if not isinstance(data, list) or len(data) == 0:
-        raise SpecException(
-            'rand_range specs require data as array with at least one element: %s' % json.dumps(field_spec))
-    start = 0
-    end = 0
-    if len(data) == 1:
-        end = data[0]
-    if len(data) >= 2:
-        start = data[0]
-        end = data[1]
-    precision = None
-    if len(data) > 2:
-        precision = data[2]
-    # config overrides third data element if specified
-    precision = config.get('precision', precision)
-    return suppliers.random_range(start, end, precision)
-
-
-###################
-# refs
-###################
-@registries.Registry.schemas(_REF_KEY)
-def _get_ref_schema():
-    """ schema for ref type """
-    return schemas.load(_REF_KEY)
-
-
-@registries.Registry.types(_REF_KEY)
-def _configure_ref_supplier(field_spec: dict, loader: Loader):
-    """ configures supplier for ref type """
-    key = None
-    if 'data' in field_spec:
-        key = field_spec.get('data')
-    if 'ref' in field_spec:
-        key = field_spec.get('ref')
-    if key is None:
-        raise SpecException('No key found for spec: ' + json.dumps(field_spec))
-    return loader.get(key)
-
-
-@registries.Registry.schemas(_WEIGHTED_REF_KEY)
-def _weighted_ref_schema():
-    """ schema for weighted_ref type """
-    return schemas.load(_WEIGHTED_REF_KEY)
-
-
-@registries.Registry.types(_WEIGHTED_REF_KEY)
-def _configure_weighted_ref_supplier(parent_field_spec, loader):
-    """ configures supplier for weighted ref specs """
-    config = utils.load_config(parent_field_spec, loader)
-    data = parent_field_spec['data']
-    key_supplier = suppliers.values(data)
-    values_map = {}
-    for key in data.keys():
-        supplier = loader.get(key)
-        values_map[key] = supplier
-    supplier = weighted_ref_supplier(key_supplier, values_map)
-    if 'count' in config:
-        return suppliers.array_supplier(supplier, **config)
-    return supplier
-
-
-####################
-# select_list_subset
-####################
-@registries.Registry.schemas(_SELECT_LIST_SUBSET_KEY)
-def _select_list_subset_schema():
-    """ schema for select_list_subset type """
-    return schemas.load(_SELECT_LIST_SUBSET_KEY)
-
-
-@registries.Registry.types(_SELECT_LIST_SUBSET_KEY)
-def _configure_select_list_subset_supplier(field_spec, loader):
-    """ configures supplier for select_list_subset type """
-    config = utils.load_config(field_spec, loader)
-    data = None
-    if config is None or ('mean' not in config and 'count' not in config):
-        raise SpecException('Config with mean or count defined must be provided: %s' % json.dumps(field_spec))
-    if 'ref' in field_spec and 'data' in field_spec:
-        raise SpecException('Only one of "data" or "ref" can be provided for: %s' % json.dumps(field_spec))
-
-    if 'ref' in field_spec:
-        ref_name = field_spec.get('ref')
-        field_spec = loader.get_ref(ref_name)
-        if field_spec is None:
-            raise SpecException('No ref with name %s found: %s' % (ref_name, json.dumps(field_spec)))
-
-        if 'data' in field_spec:
-            data = field_spec.get('data')
-        else:
-            data = field_spec
-    elif 'data' in field_spec:
-        data = field_spec.get('data')
-    if data is None:
-        raise SpecException(
-            'Unable to identify data for ' + _SELECT_LIST_SUBSET_KEY + ' for spec: ' + json.dumps(field_spec))
-    return suppliers.select_list_subset(data, **config)
-
-
-###################
-# unicode_range
-###################
-@registries.Registry.schemas(_UNICODE_RANGE_KEY)
-def _get_unicode_range_schema():
-    """ get the unicode range schema """
-    return schemas.load(_UNICODE_RANGE_KEY)
-
-
-@registries.Registry.types(_UNICODE_RANGE_KEY)
-def _configure_unicode_range_supplier(spec, loader):
-    """ configure the supplier for unicode_range types """
-    if 'data' not in spec:
-        raise SpecException('data is Required Element for unicode_range specs: ' + json.dumps(spec))
-    data = spec['data']
-    if not isinstance(data, list):
-        raise SpecException(
-            f'data should be a list or list of lists with two elements for {_UNICODE_RANGE_KEY} specs: ' + json.dumps(
-                spec))
-    config = utils.load_config(spec, loader)
-    return suppliers.unicode_range(data, **config)
-
-
-#################
-# uuid_handler
-#################
-@registries.Registry.schemas(_UUID_KEY)
-def _get_uuid_schema():
-    """ get the schema for uuid type """
-    return schemas.load(_UUID_KEY)
-
-
-@registries.Registry.types(_UUID_KEY)
-def _configure_uuid_supplier(field_spec, loader):
-    """ configure the supplier for uuid types """
-    config = utils.load_config(field_spec, loader)
-    variant = int(config.get('variant', registries.get_default('uuid_variant')))
-
-    if variant not in [1, 3, 4, 5]:
-        raise SpecException('Invalid variant for: ' + json.dumps(field_spec))
-    return suppliers.uuid(variant)
-
-
-###################
-# distribution
-###################
-@registries.Registry.schemas(_DISTRIBUTION_KEY)
-def _get_distribution_schema():
-    """ get the schema for distribution type """
-    return schemas.load(_DISTRIBUTION_KEY)
-
-
-@registries.Registry.types(_DISTRIBUTION_KEY)
-def _configure_distribution_supplier(field_spec, loader):
-    """ configure the supplier for distribution types """
-    if 'data' not in field_spec:
-        raise SpecException(
-            'required data element not defined for ' + _DISTRIBUTION_KEY + ' type : ' + json.dumps(field_spec))
-    distribution = distributions.from_string(field_spec['data'])
-    return suppliers.distribution_supplier(distribution)
-
diff --git a/datacraft/schema/__init__.py b/datacraft/_registered_types/__init__.py
similarity index 100%
rename from datacraft/schema/__init__.py
rename to datacraft/_registered_types/__init__.py
diff --git a/datacraft/_registered_types/calculate.py b/datacraft/_registered_types/calculate.py
new file mode 100644
index 0000000..7e075fd
--- /dev/null
+++ b/datacraft/_registered_types/calculate.py
@@ -0,0 +1,43 @@
+import json
+import logging
+
+import datacraft
+import datacraft.spec_formatters
+from . import common
+from . import schemas
+from .common import build_suppliers_map
+
+_log = logging.getLogger(__name__)
+_CALCULATE_KEY = 'calculate'
+_EXAMPLE_SPEC = {
+    "height_in": [60, 70, 80, 90],
+    "height_cm": {
+        "type": "calculate",
+        "fields": ["height_in"],
+        "formula": "{{ height_in }} * 2.54"
+    }
+}
+
+
+@datacraft.registry.schemas(_CALCULATE_KEY)
+def _calculate_schema():
+    """ get the schema for the calculate type """
+    return schemas.load(_CALCULATE_KEY)
+
+
+@datacraft.registry.types(_CALCULATE_KEY)
+def _configure_calculate_supplier(field_spec: dict, loader: datacraft.Loader):
+    """ configures supplier for calculate type """
+
+    formula = field_spec.get('formula')
+    if formula is None:
+        raise datacraft.SpecException('Must define formula for calculate type. %s' % json.dumps(field_spec))
+
+    suppliers_map = build_suppliers_map(field_spec, loader)
+
+    return datacraft.suppliers.calculate(suppliers_map=suppliers_map, formula=formula)
+
+
+@datacraft.registry.usage(_CALCULATE_KEY)
+def _example_usage():
+    return common.standard_example_usage(_EXAMPLE_SPEC, 3)
diff --git a/datacraft/_registered_types/char_class.py b/datacraft/_registered_types/char_class.py
new file mode 100644
index 0000000..e80f742
--- /dev/null
+++ b/datacraft/_registered_types/char_class.py
@@ -0,0 +1,110 @@
+import json
+import logging
+import string
+
+import datacraft
+from . import common
+from . import schemas
+
+_log = logging.getLogger(__name__)
+_CHAR_CLASS_KEY = 'char_class'
+
+_UNDER_SCORE = '_'
+
+_CLASS_MAPPING = {
+    "ascii": string.printable,
+    "lower": string.ascii_lowercase,
+    "upper": string.ascii_uppercase,
+    "letters": string.ascii_letters,
+    "word": f'{string.ascii_letters}{string.digits}{_UNDER_SCORE}',
+    "printable": string.printable,
+    "visible": ''.join(string.printable.split()),
+    "punctuation": string.punctuation,
+    "special": string.punctuation,
+    "digits": string.digits,
+    "hex": string.hexdigits,
+    "hex-lower": string.digits + 'abcdef',
+    "hex-upper": string.digits + 'ABCDEF',
+}
+
+_EXAMPLE_SPEC = {
+    "password": {
+        "type": "char_class",
+        "data": ["word", "special", "hex-lower", "M4$p3c!@l$@uc3"],
+        "config": {
+            "mean": 14,
+            "stddev": 2,
+            "min": 10,
+            "max": 18,
+            "exclude": ["-", "\""]
+        }
+    }
+}
+
+
+@datacraft.registry.schemas(_CHAR_CLASS_KEY)
+def _get_char_class_schema():
+    """ get the schema for the char_class type """
+    return schemas.load(_CHAR_CLASS_KEY)
+
+
+for class_key in _CLASS_MAPPING:
+    @datacraft.registry.schemas("cc-" + class_key)
+    def _get_char_class_alias_schema():
+        """ get the schema for the char_class type """
+        return schemas.load(_CHAR_CLASS_KEY)
+
+
+@datacraft.registry.types(_CHAR_CLASS_KEY)
+def _configure_char_class_supplier(spec, loader):
+    """ configure the supplier for char_class types """
+    if 'data' not in spec:
+        raise datacraft.SpecException(f'Data is required field for char_class type: {json.dumps(spec)}')
+    config = datacraft.utils.load_config(spec, loader)
+    data = spec['data']
+    if isinstance(data, str) and data in _CLASS_MAPPING:
+        data = _CLASS_MAPPING[data]
+    if isinstance(data, list):
+        new_data = [_CLASS_MAPPING[datum] if datum in _CLASS_MAPPING else datum for datum in data]
+        data = ''.join(new_data)
+
+    if 'join_with' not in config:
+        config['join_with'] = datacraft.registries.get_default('char_class_join_with')
+
+    return datacraft.suppliers.character_class(data, **config)
+
+
+def register_alias_type_function(key, name):
+    @datacraft.registry.types(key)
+    def function(spec, loader):
+        """ configure the supplier for char_class alias types """
+        spec['data'] = name
+        return _configure_char_class_supplier(spec, loader)
+
+    return function
+
+
+@datacraft.registry.usage(_CHAR_CLASS_KEY)
+def _char_class_usage():
+    """basic usage example for char class"""
+    return common.standard_example_usage(_EXAMPLE_SPEC, 3)
+
+
+def register_alias_usage_function(key):
+    @datacraft.registry.usage(key)
+    def function():
+        """basic usage example for char class"""
+        spec = {
+            "example": {
+                "type": key,
+                "config": {"count": 5}
+            }
+        }
+        return common.standard_example_usage(spec, 3)
+
+    return function
+
+
+for class_key in _CLASS_MAPPING:
+    register_alias_type_function("cc-" + class_key, class_key)
+    register_alias_usage_function("cc-" + class_key)
diff --git a/datacraft/_registered_types/combine.py b/datacraft/_registered_types/combine.py
new file mode 100644
index 0000000..f4918ef
--- /dev/null
+++ b/datacraft/_registered_types/combine.py
@@ -0,0 +1,138 @@
+import json
+import logging
+
+import datacraft
+from . import common
+from . import schemas
+
+_log = logging.getLogger(__name__)
+_COMBINE_KEY = 'combine'
+_COMBINE_LIST_KEY = 'combine-list'
+_COMBINE_EXAMPLE = {
+    "combine": {
+        "type": "combine",
+        "refs": ["first", "last"],
+        "config": {
+            "join_with": " "
+        }
+    },
+    "refs": {
+        "first": {
+            "type": "values",
+            "data": ["zebra", "hedgehog", "llama", "flamingo"]
+        },
+        "last": {
+            "type": "values",
+            "data": ["jones", "smith", "williams"]
+        }
+    }
+}
+_COMBINE_LIST_EXAMPLE = {
+    "full_name": {
+        "type": "combine-list",
+        "refs": [
+            ["first", "last"],
+            ["first", "middle", "last"],
+            ["first", "middle_initial", "last"]
+        ],
+        "config": {
+            "join_with": " "
+        }
+    },
+    "refs": {
+        "first": {
+            "type": "values",
+            "data": ["zebra", "hedgehog", "llama", "flamingo"]
+        },
+        "last": {
+            "type": "values",
+            "data": ["jones", "smith", "williams"]
+        },
+        "middle": {
+            "type": "values",
+            "data": ["cloud", "sage", "river"]
+        },
+        "middle_initial": {
+            "type": "values",
+            "data": {"a": 0.3, "m": 0.3, "j": 0.1, "l": 0.1, "e": 0.1, "w": 0.1}
+        }
+    }
+}
+
+
+@datacraft.registry.schemas(_COMBINE_KEY)
+def _get_combine_schema():
+    """ get the schema for the combine type """
+    return schemas.load(_COMBINE_KEY)
+
+
+@datacraft.registry.schemas(_COMBINE_LIST_KEY)
+def _get_combine_list_schema():
+    """ get the schema for the combine_list type """
+    return schemas.load(_COMBINE_LIST_KEY)
+
+
+@datacraft.registry.types(_COMBINE_KEY)
+def _configure_combine_supplier(field_spec, loader):
+    """ configures supplier for combine type """
+    if 'refs' not in field_spec and 'fields' not in field_spec:
+        raise datacraft.SpecException('Must define one of fields or refs. %s' % json.dumps(field_spec))
+
+    if 'refs' in field_spec:
+        supplier = _load_combine_from_refs(field_spec, loader)
+    else:
+        supplier = _load_combine_from_fields(field_spec, loader)
+    return supplier
+
+
+@datacraft.registry.types(_COMBINE_LIST_KEY)
+def _configure_combine_list_supplier(field_spec, loader):
+    """ configures supplier for combine-list type """
+    if 'refs' not in field_spec:
+        raise datacraft.SpecException('Must define refs for combine-list type. %s' % json.dumps(field_spec))
+
+    refs_list = field_spec['refs']
+    if len(refs_list) < 1 or not isinstance(refs_list[0], list):
+        raise datacraft.SpecException(
+            'refs pointer must be list of lists: i.e [["ONE", "TWO"]]. %s' % json.dumps(field_spec))
+
+    suppliers_list = []
+    for ref in refs_list:
+        spec = dict(field_spec)
+        spec['refs'] = ref
+        suppliers_list.append(_load_combine_from_refs(spec, loader))
+    return datacraft.suppliers.from_list_of_suppliers(suppliers_list, True)
+
+
+def _load_combine_from_refs(combine_field_spec, loader):
+    """ loads the combine type from a set of refs """
+    keys = combine_field_spec.get('refs')
+    return _load_combine(combine_field_spec, keys, loader)
+
+
+def _load_combine_from_fields(combine_field_spec, loader):
+    """ load the combine type from a set of field names """
+    keys = combine_field_spec.get('fields')
+    return _load_combine(combine_field_spec, keys, loader)
+
+
+def _load_combine(combine_field_spec, keys, loader):
+    """ create the combine supplier for the types from the given keys """
+    to_combine = []
+    for key in keys:
+        supplier = loader.get(key)
+        to_combine.append(supplier)
+    config = combine_field_spec.get('config', {})
+    as_list = config.get('as_list', datacraft.registries.get_default('combine_as_list'))
+    joiner = config.get('join_with', datacraft.registries.get_default('combine_join_with'))
+    return datacraft.suppliers.combine(to_combine, joiner, as_list)
+
+
+@datacraft.registry.usage(_COMBINE_KEY)
+def _example_combine_usage():
+    return common.standard_example_usage(_COMBINE_EXAMPLE, 3)
+
+
+@datacraft.registry.usage(_COMBINE_LIST_KEY)
+def _example_combine_list_usage():
+    return common.standard_example_usage(_COMBINE_LIST_EXAMPLE, 3)
diff --git a/datacraft/_registered_types/common.py b/datacraft/_registered_types/common.py
new file mode 100644
index 0000000..462bf4e
--- /dev/null
+++ b/datacraft/_registered_types/common.py
@@ -0,0 +1,44 @@
+import json
+
+import datacraft
+
+
+def build_suppliers_map(field_spec, loader):
+    if 'refs' not in field_spec and 'fields' not in field_spec:
+        raise datacraft.SpecException('Must define one of fields or refs. %s' % json.dumps(field_spec))
+    if 'refs' in field_spec and 'fields' in field_spec:
+        raise datacraft.SpecException('Must define only one of fields or refs. %s' % json.dumps(field_spec))
+    mappings = _get_mappings(field_spec, 'refs')
+    mappings.update(_get_mappings(field_spec, 'fields'))
+    if len(mappings) < 1:
+        raise datacraft.SpecException('fields or refs empty: %s' % json.dumps(field_spec))
+    suppliers_map = {}
+    for field_or_ref, alias in mappings.items():
+        supplier = loader.get(field_or_ref)
+        suppliers_map[alias] = supplier
+    return suppliers_map
+
+
+def _get_mappings(field_spec, lookup_key):
+    """ retrieve the field aliasing for the given key, refs or fields """
+    mappings = field_spec.get(lookup_key, [])
+    if isinstance(mappings, list):
+        mappings = {_key: _key for _key in mappings}
+    return mappings
+
+
+def standard_example_usage(example: dict, num: int, pretty: bool = False, no_reformat: bool = False):
+    """builds a single usage from given example spec"""
+    if no_reformat:
+        formatted_spec = example  # type: ignore
+    else:
+        formatted_spec = datacraft.preprocess_and_format(example)  # type: ignore
+    datacraft_format = 'json-pretty' if pretty else 'json'
+    command = f'datacraft -s spec.json -i {num} --format {datacraft_format} -x -l off'
+    if pretty:
+        output = json.dumps(datacraft.entries(example, num), indent=4)
+    else:
+        output = json.dumps(
+            datacraft.entries(example, num),
+            ensure_ascii=datacraft.registries.get_default('format_json_ascii'))
+    return f'Example Spec:\n{formatted_spec}\n{command}\n{output}\n'
diff --git a/datacraft/_registered_types/config_ref.py b/datacraft/_registered_types/config_ref.py
new file mode 100644
index 0000000..04182bb
--- /dev/null
+++ b/datacraft/_registered_types/config_ref.py
@@ -0,0 +1,17 @@
+import logging
+
+import datacraft
+from . import common
+
+_log = logging.getLogger(__name__)
+_CONFIG_REF_KEY = 'config_ref'
+
+
+@datacraft.registry.types(_CONFIG_REF_KEY)
+def _config_ref_handler(_, __):
+    """" Does nothing, just place holder """
+
+
+@datacraft.registry.usage(_CONFIG_REF_KEY)
+def _example_usage():
+    return "See csv type"
diff --git a/datacraft/_registered_types/csv.py b/datacraft/_registered_types/csv.py
new file mode 100644
index 0000000..c62ed4d
--- /dev/null
+++ b/datacraft/_registered_types/csv.py
@@ -0,0 +1,142 @@
+import csv
+import json
+import logging
+import os
+
+import datacraft
+from datacraft.supplier.common import weighted_values_explicit
+from . import schemas
+
+_log = logging.getLogger(__name__)
+_CSV_TYPE = 'csv'
+_WEIGHTED_CSV = 'weighted_csv'
+_CSV_EXAMPLE = {
+    "status": {
+        "type": "csv",
+        "config": {
+            "column": 1,
+            "config_ref": "tabs_config"
+        }
+    },
+    "description": {
+        "type": "csv",
+        "config": {
+            "column": 2,
+            "config_ref": "tabs_config"
+        }
+    },
+    "status_type:csv?config_ref=tabs_config&column=3": {},
+    "refs": {
+        "tabs_config": {
+            "type": "config_ref",
+            "config": {
+                "datafile": "tabs.csv",
+                "delimiter": "\\t",
+                "headers": True,
+                "sample_rows": True
+            }
+        }
+    }
+}
+_WEIGHTED_CSV_EXAMPLE = {
+    "cities": {
+        "type": "weighted_csv",
+        "config": {
+            "datafile": "weighted_cities.csv"
+        }
+    }
+}
+
+
+@datacraft.registry.types(_CSV_TYPE)
+def _configure_csv(field_spec, loader):
+    """ Configures the csv value supplier for this field """
+    config = datacraft.utils.load_config(field_spec, loader)
+    datafile = config.get('datafile', datacraft.registries.get_default('csv_file'))
+    csv_path = f'{loader.datadir}/{datafile}'
+    if not os.path.exists(csv_path):
+        raise datacraft.SpecException(f'Unable to locate data file: {datafile} in data dir: {loader.datadir} for spec: '
+                                      + json.dumps(field_spec))
+    return datacraft.suppliers.csv(csv_path, **config)
+
+
+@datacraft.registry.schemas(_CSV_TYPE)
+def _get_csv_schema():
+    """ get the schema for the csv type """
+    return schemas.load(_CSV_TYPE)
+
+
+@datacraft.registry.types(_WEIGHTED_CSV)
+def _configure_weighted_csv(field_spec, loader):
+    """ Configures the weighted_csv value supplier for this field """
+
+    config = datacraft.utils.load_config(field_spec, loader)
+
+    field_name = config.get('column', 1)
+    weight_column = config.get('weight_column', 2)
+    count_supplier = datacraft.suppliers.count_supplier(**config)
+
+    datafile = config.get('datafile', datacraft.registries.get_default('csv_file'))
+    csv_path = f'{loader.datadir}/{datafile}'
+    has_headers = datacraft.utils.is_affirmative('headers', config)
+    numeric_index = isinstance(field_name, int)
+    if numeric_index and field_name < 1:
+        raise datacraft.SpecException(f'Invalid index {field_name}, one based indexing used for column numbers')
+
+    if has_headers and not numeric_index:
+        choices = _read_named_column(csv_path, field_name)
+        weights = _read_named_column_weights(csv_path, weight_column)
+    else:
+        choices = _read_indexed_column(csv_path, int(field_name), skip_first=numeric_index)
+        weights = _read_indexed_column_weights(csv_path, int(weight_column), skip_first=numeric_index)
+    return weighted_values_explicit(choices, weights, count_supplier)
+
+
+@datacraft.registry.schemas(_WEIGHTED_CSV)
+def _get_weighted_csv_schema():
+    """ get the schema for the weighted_csv type """
+    return schemas.load(_WEIGHTED_CSV)
+
+
+@datacraft.registry.usage(_CSV_TYPE)
+def _example_csv_usage():
+    formatted_spec = datacraft.preprocess_and_format(_CSV_EXAMPLE)
+    return f'Example Spec:\n{formatted_spec}'
+
+
+@datacraft.registry.usage(_WEIGHTED_CSV)
+def _example_weighted_csv_usage():
+    formatted_spec = datacraft.preprocess_and_format(_WEIGHTED_CSV_EXAMPLE)
+    return f'Example Spec:\n{formatted_spec}'
+
+
+def _read_named_column(csv_path: str, column_name: str):
+    """ reads values from a named column into a list """
+    with open(csv_path, newline='') as csvfile:
+        reader = csv.DictReader(csvfile)
+        return [val[column_name] for val in reader]
+
+
+def _read_named_column_weights(csv_path: str, column_name: str):
+    """ reads values for weights for named column into a list """
+    with open(csv_path, newline='') as csvfile:
+        reader = csv.DictReader(csvfile)
+        return [float(val[column_name]) for val in reader]
+
+
+def _read_indexed_column(csv_path: str, column_index: int, skip_first: bool):
+    """ reads values from a indexed column into a list """
+    with open(csv_path, newline='') as csvfile:
+        reader = csv.reader(csvfile)
+        if skip_first:
+            next(reader)
+        return [val[column_index - 1] for val in reader]
+
+
+def _read_indexed_column_weights(csv_path: str, column_index: int, skip_first: bool):
+    """ reads values for weights for indexed column into a list """
+    with open(csv_path, newline='') as csvfile:
+        reader = csv.reader(csvfile)
+        if skip_first:
+            next(reader)
+        return [float(val[column_index - 1]) for val in reader]
diff --git a/datacraft/_registered_types/date.py b/datacraft/_registered_types/date.py
new file mode 100644
index 0000000..74b1a4d
--- /dev/null
+++ b/datacraft/_registered_types/date.py
@@ -0,0 +1,140 @@
+import datetime
+import logging
+
+import datacraft
+from . import common
+from . import schemas
+
+_log = logging.getLogger(__name__)
+_DATE_KEY = 'date'
+_DATE_ISO_KEY = 'date.iso'
+_DATE_ISO_US_KEY = 'date.iso.us'
+_ISO_FORMAT_NO_MICRO = '%Y-%m-%dT%H:%M:%S'
+_ISO_FORMAT_WITH_MICRO = '%Y-%m-%dT%H:%M:%S.%f'
+
+
+@datacraft.registry.schemas(_DATE_KEY)
+def _get_date_schema():
+    """ returns the schema for date types """
+    return schemas.load(_DATE_KEY)
+
+
+@datacraft.registry.schemas(_DATE_ISO_KEY)
+def _get_date_iso_schema():
+    """ returns the schema for date.iso types """
+    # NOTE: These all share a schema
+    return schemas.load(_DATE_KEY)
+
+
+@datacraft.registry.schemas(_DATE_ISO_US_KEY)
+def _get_date_iso_us_schema():
+    """ returns the schema for date.iso.us types """
+    # NOTE: These all share a schema
+    return schemas.load(_DATE_KEY)
+
+
+@datacraft.registry.types(_DATE_KEY)
+def _configure_date_supplier(field_spec: dict, loader: datacraft.Loader):
+    """ configures the date value supplier """
+    config = datacraft.utils.load_config(field_spec, loader)
+    config['hour_supplier'] = _hour_supplier(config, loader)
+    return datacraft.suppliers.date(**config)
+
+
+def _hour_supplier(config: dict, loader: datacraft.Loader):
+    """ get hour supplier from config if present """
+    if 'hours' not in config:
+        return None
+    return loader.get_from_spec(config['hours'])
+
+
+@datacraft.registry.types(_DATE_ISO_KEY)
+def _configure_supplier_iso(field_spec: dict, loader: datacraft.Loader):
+    """ configures the date.iso value supplier """
+    return _configure_supplier_iso_date(field_spec, loader, _ISO_FORMAT_NO_MICRO)
+
+
+@datacraft.registry.types(_DATE_ISO_US_KEY)
+def _configure_supplier_iso_microseconds(field_spec: dict, loader: datacraft.Loader):
+    """ configures the date.iso.us value supplier """
+    return _configure_supplier_iso_date(field_spec, loader, _ISO_FORMAT_WITH_MICRO)
+
+
+@datacraft.registry.usage(_DATE_KEY)
+def _example_date_usage():
+    example_one = {
+        "dates": {
+            "type": "date",
+            "config": {
+                "duration_days": "90",
+                "start": "15-Dec-2050 12:00",
+                "format": "%d-%b-%Y %H:%M"
+            }
+        }
+    }
+    example_two = {
+        "dates": {
+            "type": "date",
+            "config": {
+                "center_date": "20500601 12:00",
+                "format": "%Y%m%d %H:%M",
+                "stddev_days": "2"
+            }
+        }
+    }
+    example_tre = {
+        "start_time": {
+            "type": "date",
+            "config": {
+                "center_date": "20500601 12:00",
+                "format": "%Y%m%d %H:%M",
+                "hours": {
+                    "type": "values",
+                    "data": {"7": 0.1, "8": 0.2, "9": 0.4, "10": 0.2, "11": 0.1}
+                }
+            }
+        }
+    }
+    one = common.standard_example_usage(example_one, 3)
+    two = common.standard_example_usage(example_two, 3)
+    tre = common.standard_example_usage(example_tre, 3)
+    return '\n'.join([one, two, tre])
+
+
+@datacraft.registry.usage(_DATE_ISO_KEY)
+def _example_date_iso_usage():
+    example = {
+        "timestamp": {
+            "type": _DATE_ISO_KEY
+        }
+    }
+    return common.standard_example_usage(example, 3)
+
+
+@datacraft.registry.usage(_DATE_ISO_US_KEY)
+def _example_date_iso_micros_usage():
+    example = {
+        "timestamp.us": {
+            "type": _DATE_ISO_US_KEY
+        }
+    }
+    return common.standard_example_usage(example, 3)
+
+
+def _configure_supplier_iso_date(field_spec, loader, iso_date_format):
+    """ configures an iso based date supplier using the provided date format """
+    config = datacraft.utils.load_config(field_spec, loader)
+    # make sure the start and end dates match the ISO format we are using
+    start = config.get('start')
+    end = config.get('end')
+    date_format = config.get('format', )
+    if start:
+        start_date = datetime.datetime.strptime(start, date_format)
+        config['start'] = start_date.strftime(iso_date_format)
+    if end:
+        end_date = datetime.datetime.strptime(end, date_format)
+        config['end'] = end_date.strftime(iso_date_format)
+    config['format'] = iso_date_format
+    # End fixes to support iso
+    config['hour_supplier'] = _hour_supplier(config, loader)
+    return datacraft.suppliers.date(**config)
diff --git a/datacraft/_registered_types/distribution.py b/datacraft/_registered_types/distribution.py
new file mode 100644
index 0000000..3c98e49
--- /dev/null
+++ b/datacraft/_registered_types/distribution.py
@@ -0,0 +1,56 @@
+import json
+import logging
+
+import datacraft
+from . import common
+from . import schemas
+
+_log = logging.getLogger(__name__)
+_DISTRIBUTION_KEY = 'distribution'
+
+
+@datacraft.registry.schemas(_DISTRIBUTION_KEY)
+def _get_distribution_schema():
+    """ get the schema for distribution type """
+    return schemas.load(_DISTRIBUTION_KEY)
+
+
+@datacraft.registry.types(_DISTRIBUTION_KEY)
+def _configure_distribution_supplier(field_spec, _):
+    """ configure the supplier for distribution types """
+    if 'data' not in field_spec:
+        raise datacraft.SpecException(
+            'required data element not defined for ' + _DISTRIBUTION_KEY + ' type : ' + json.dumps(field_spec))
+    distribution = datacraft.distributions.from_string(field_spec['data'])
+    return datacraft.suppliers.distribution_supplier(distribution)
+
+
+@datacraft.registry.usage(_DISTRIBUTION_KEY)
+def _example_distribution_usage():
+    example_one = {
+        "values": {
+            "type": "distribution",
+            "data": "uniform(start=10, end=30)"
+        }
+    }
+    example_two = {
+        "age": {
+            "type": "distribution",
+            "data": "normal(mean=28, stddev=10, min=18, max=40)",
+            "config": {"cast": "int"}
+        }
+    }
+    example_tre = {
+        "pressure": {
+            "type": "distribution",
+            "data": "gauss(mean=33, stddev=3.4756535)",
+            "config": {
+                "count_dist": "normal(mean=2, stddev=1, min=1, max=4)",
+                "as_list": True
+            }
+        }
+    }
+    one = common.standard_example_usage(example_one, 3)
+    two = common.standard_example_usage(example_two, 3)
+    tre = common.standard_example_usage(example_tre, 3)
+    return '\n'.join([one, two, tre])
diff --git a/datacraft/_registered_types/entrypoint.py b/datacraft/_registered_types/entrypoint.py
new file mode 100644
index 0000000..7111d5f
--- /dev/null
+++ b/datacraft/_registered_types/entrypoint.py
@@ -0,0 +1,24 @@
+"""datacraft.custom_type_loader entrypoint for built in types"""
+from . import (
+        calculate,
+        char_class,
+        combine,
+        config_ref,
+        csv,
+        date,
+        distribution,
+        geo,
+        nested,
+        network,
+        range_suppliers,
+        refs,
+        select_list_subset,
+        templated,
+        unicode_range,
+        uuid_handler,
+        values
+    )
+
+
+def load_custom():
+    pass
diff --git a/datacraft/_registered_types/geo.py b/datacraft/_registered_types/geo.py
new file mode 100644
index 0000000..85eaf9d
--- /dev/null
+++ b/datacraft/_registered_types/geo.py
@@ -0,0 +1,99 @@
+import logging
+
+import datacraft
+from . import common
+from . import schemas
+
+_log = logging.getLogger(__name__)
+_GEO_LAT_KEY = 'geo.lat'
+_GEO_LONG_KEY = 'geo.long'
+_GEO_PAIR_KEY = 'geo.pair'
+
+
+@datacraft.registry.schemas(_GEO_LAT_KEY)
+def _get_geo_lat_schema():
+    """ schema for geo.lat type """
+    return schemas.load(_GEO_LAT_KEY)
+
+
+@datacraft.registry.schemas(_GEO_LONG_KEY)
+def _get_geo_long_schema():
+    """ schema for geo.long type """
+    return schemas.load(_GEO_LONG_KEY)
+
+
+@datacraft.registry.schemas(_GEO_PAIR_KEY)
+def _get_geo_pair_schema():
+    """ schema for geo.pair type """
+    return schemas.load(_GEO_PAIR_KEY)
+
+
+@datacraft.registry.types(_GEO_LAT_KEY)
+def _configure_geo_lat(field_spec, loader):
+    """ configures value supplier for geo.lat type """
+    config = datacraft.utils.load_config(field_spec, loader)
+    return datacraft.suppliers.geo_lat(**config)
+
+
+@datacraft.registry.types(_GEO_LONG_KEY)
+def _configure_geo_long(field_spec, loader):
+    """ configures value supplier for geo.long type """
+    config = datacraft.utils.load_config(field_spec, loader)
+    return datacraft.suppliers.geo_long(**config)
+
+
+@datacraft.registry.types(_GEO_PAIR_KEY)
+def _configure_geo_pair(field_spec, loader):
+    """ configures value supplier for geo.pair type """
+    config = datacraft.utils.load_config(field_spec, loader)
+    return datacraft.suppliers.geo_pair(**config)
+
+
+@datacraft.registry.usage(_GEO_LAT_KEY)
+def _get_geo_lat_usage():
+    """ usage for geo.lat type """
+    example = {
+        "lat": {
+            "type": "geo.lat",
+            "config": {
+                "start_lat": -45.0,
+                "end_lat": 45.0
+            }
+        }
+    }
+    return common.standard_example_usage(example, 3)
+
+
+@datacraft.registry.usage(_GEO_LONG_KEY)
+def _get_geo_long_usage():
+    """ usage for geo.long type """
+    example = {
+        "long": {
+            "type": "geo.long",
+            "config": {
+                "start_long": -45.0,
+                "end_long": 45.0
+            }
+        }
+    }
+    return common.standard_example_usage(example, 3)
+
+
+@datacraft.registry.usage(_GEO_PAIR_KEY)
+def _get_geo_pair_usage():
+    """ usage for geo.pair type """
+    example = {
+        "egypt": {
+            "type": "geo.pair",
+            "config": {
+                "bbox": [
+                    31.33134,
+                    22.03795,
+                    34.19295,
+                    25.00562
+                ],
+                "precision": 3
+            }
+        }
+    }
+    return common.standard_example_usage(example, 3)
diff --git a/datacraft/_registered_types/nested.py b/datacraft/_registered_types/nested.py
new file mode 100644
index 0000000..7866fa4
--- /dev/null
+++ b/datacraft/_registered_types/nested.py
@@ -0,0 +1,129 @@
+import logging
+from typing import Dict, Any
+
+import datacraft
+from datacraft import ValueSupplierInterface, KeyProviderInterface, SupplierException
+from datacraft.supplier import key_suppliers
+from . import common
+from . import schemas
+
+_log = logging.getLogger(__name__)
+_NESTED_KEY = 'nested'
+
+
+@datacraft.registry.schemas(_NESTED_KEY)
+def _get_nested_schema():
+    """ schema for nested type """
+    return schemas.load(_NESTED_KEY)
+
+
+@datacraft.registry.types(_NESTED_KEY)
+def _configure_nested_supplier(spec, loader):
+    """ configure the supplier for nested types """
+    fields = spec['fields']
+    keys = [key for key in fields.keys()]
+    config = datacraft.utils.load_config(spec, loader)
+    count_supplier = datacraft.suppliers.count_supplier(**config)
+    if 'field_groups' in spec:
+        key_supplier = key_suppliers.from_spec(spec)
+    else:
+        key_supplier = key_suppliers.from_spec(fields)
+
+    as_list = datacraft.utils.is_affirmative('as_list', config)
+
+    field_supplier_map = {}
+    # each non reserved key should have a valid spec as a value
+    for key in keys:
+        nested_spec = fields[key]
+        if 'type' in nested_spec and nested_spec.get('type') == _NESTED_KEY:
+            supplier = _configure_nested_supplier(nested_spec, loader)
+        else:
+            supplier = loader.get_from_spec(nested_spec)
+        field_supplier_map[key] = supplier
+    return nested_supplier(field_supplier_map, count_supplier, key_supplier, as_list)
+
+
+@datacraft.registry.usage(_NESTED_KEY)
+def _example_nested_usage():
+    example = {
+        "id": {
+            "type": "uuid"
+        },
+        "user": {
+            "type": "nested",
+            "fields": {
+                "user_id": {
+                    "type": "uuid"
+                },
+                "geo": {
+                    "type": "nested",
+                    "fields": {
+                        "place_id:cc-digits?mean=5": {},
+                        "coordinates:geo.pair?as_list=true": {}
+                    }
+                }
+            }
+        }
+    }
+    return common.standard_example_usage(example, 1, pretty=True)
+
+
+def nested_supplier(field_supplier_map: Dict[str, ValueSupplierInterface],
+                    count_supplier: ValueSupplierInterface,
+                    key_supplier: KeyProviderInterface,
+                    as_list: bool) -> ValueSupplierInterface:
+    """
+    Args:
+        field_supplier_map: mapping of nested field name to value supplier for it
+        count_supplier: number of nested objects to create
+        key_supplier: to supply nest fields names
+        as_list: for counts of one, if the result should be a list instead of an object
+    """
+    return _NestedSupplier(field_supplier_map, count_supplier, key_supplier, as_list)
+
+
+class _NestedSupplier(ValueSupplierInterface):
+    """
+    Implementation for Nested Value Supplier
+    """
+
+    def __init__(self,
+                 field_supplier_map: Dict[str, ValueSupplierInterface],
+                 count_supplier: ValueSupplierInterface,
+                 key_supplier: KeyProviderInterface,
+                 as_list: bool):
+        """
+        Args:
+            field_supplier_map: mapping of nested field name to value supplier for it
+            count_supplier: number of nested objects to create
+            key_supplier: to supply nest fields names
+            as_list: for counts of one, if the result should be a list instead of an object
+        """
+        self.field_supplier_map = field_supplier_map
+        self.count_supplier = count_supplier
+        self.key_supplier = key_supplier
+        self.as_list = as_list
+
+    def next(self, iteration: int):
+        count = int(self.count_supplier.next(iteration))
+        if count == 0:
+            if self.as_list:
+                return []
+            return None
+        if count > 1:
+            vals = [self._single_pass(iteration + i) for i in range(count)]
+            return vals
+        # this is dict
+        vals = self._single_pass(iteration)  # type: ignore
+        if self.as_list:
+            return [vals]
+        return vals
+
+    def _single_pass(self, iteration: int) -> Dict[str, Any]:
+        """ get set of values for this iteration """
+        _, keys = self.key_supplier.get()
+        subset = {key: self.field_supplier_map.get(key) for key in keys}
+        if any(val is None for val in subset.values()):
+            raise SupplierException(f'One or more keys provided in nested spec are not valid: {keys}, valid keys: '
+                                    f'{list(self.field_supplier_map.keys())}')
+        return {key: supplier.next(iteration) for key, supplier in subset.items()}  # type: ignore
diff --git a/datacraft/_registered_types/network.py b/datacraft/_registered_types/network.py
new file mode 100644
index 0000000..9901a68
--- /dev/null
+++ b/datacraft/_registered_types/network.py
@@ -0,0 +1,118 @@
+import json
+import logging
+
+import datacraft
+from . import common
+from . import schemas
+
+_log = logging.getLogger(__name__)
+_IP_KEY = 'ip'
+_IPV4_KEY = 'ipv4'
+_IP_PRECISE_KEY = 'ip.precise'
+_NET_MAC_KEY = 'net.mac'
+
+
+@datacraft.registry.schemas(_IP_KEY)
+def _get_ip_schema():
+    """ returns the schema for the ip types """
+    return schemas.load(_IP_KEY)
+
+
+@datacraft.registry.schemas(_IPV4_KEY)
+def _get_ipv4_schema():
+    """ returns the schema for the ipv4 types """
+    # shares schema with ip
+    return schemas.load(_IP_KEY)
+
+
+@datacraft.registry.schemas(_IP_PRECISE_KEY)
+def _get_ip_precise_schema():
+    """ returns the schema for the ip.precise types """
+    return schemas.load(_IP_PRECISE_KEY)
+
+
+@datacraft.registry.schemas(_NET_MAC_KEY)
+def _get_mac_addr_schema():
+    """ returns the schema for the net.mac types """
+    return schemas.load(_NET_MAC_KEY)
+
+
+@datacraft.registry.types(_IPV4_KEY)
+@datacraft.registry.types(_IP_KEY)
+def _configure_ip(field_spec, loader):
+    """ configures value supplier for ip type """
+    config = datacraft.utils.load_config(field_spec, loader)
+    try:
+        return datacraft.suppliers.ip_supplier(**config)
+    except ValueError as err:
+        raise datacraft.SpecException(str(err)) from err
+
+
+@datacraft.registry.types(_IP_PRECISE_KEY)
+def _configure_precise_ip(field_spec, _):
+    """ configures value supplier for ip.precise type """
+    config = field_spec.get('config')
+    if config is None:
+        raise datacraft.SpecException('No config for: ' + json.dumps(field_spec) + ', param cidr required')
+    cidr = config.get('cidr')
+    sample = datacraft.utils.is_affirmative('sample', config, 'no')
+    if cidr is None:
+        raise datacraft.SpecException('Invalid config for: ' + json.dumps(field_spec) + ', param cidr required')
+    return datacraft.suppliers.ip_precise(cidr, sample)
+
+
+@datacraft.registry.types(_NET_MAC_KEY)
+def _configure_mac_address_supplier(field_spec, loader):
+    """ configures value supplier for net.mac type """
+    config = datacraft.utils.load_config(field_spec, loader)
+    if datacraft.utils.is_affirmative('dashes', config):
+        delim = '-'
+    else:
+        delim = datacraft.registries.get_default('mac_addr_separator')
+
+    return datacraft.suppliers.mac_address(delim)
+
+
+@datacraft.registry.usage(_IP_KEY)
+def _example_ip_usage():
+    return "Alias for ipv4"
+
+
+@datacraft.registry.usage(_IPV4_KEY)
+def _example_ipv4_usage():
+    example = {
+        "network": {
+            "type": _IPV4_KEY,
+            "config": {
+                "cidr": "2.22.222.0/16"
+            }
+        }
+    }
+    return common.standard_example_usage(example, 3)
+
+
+@datacraft.registry.usage(_IP_PRECISE_KEY)
+def _example_ip_precise_usage():
+    example = {
+        "network": {
+            "type": _IP_PRECISE_KEY,
+            "config": {
+                "cidr": "192.168.0.0/14",
+                "sample": "true"
+            }
+        }
+    }
+    return common.standard_example_usage(example, 3)
+
+
+@datacraft.registry.usage(_NET_MAC_KEY)
+def _example_net_mac_usage():
+    example = {
+        "network": {
+            "type": _NET_MAC_KEY,
+            "config": {
+                "dashes": "true"
+            }
+        }
+    }
+    return common.standard_example_usage(example, 3)
diff --git a/datacraft/_registered_types/range_suppliers.py b/datacraft/_registered_types/range_suppliers.py
new file mode 100644
index 0000000..57184f3
--- /dev/null
+++ b/datacraft/_registered_types/range_suppliers.py
@@ -0,0 +1,138 @@
+import json
+import logging
+
+import datacraft
+from . import common
+from . import schemas
+
+_log = logging.getLogger(__name__)
+_RANGE_KEY = 'range'
+_RAND_RANGE_KEY = 'rand_range'
+_RAND_INT_RANGE_KEY = 'rand_int_range'
+
+
+@datacraft.registry.schemas(_RANGE_KEY)
+def _get_range_schema():
+    """ schema for range type """
+    return schemas.load(_RANGE_KEY)
+
+
+@datacraft.registry.schemas(_RAND_RANGE_KEY)
+def _get_rand_range_schema():
+    """ schema for rand range type """
+    # This shares a schema with range
+    return schemas.load(_RANGE_KEY)
+
+
+@datacraft.registry.schemas(_RAND_INT_RANGE_KEY)
+def _get_rand_int_range_schema():
+    """ schema for rand int range type """
+    # This shares a schema with range
+    return schemas.load(_RANGE_KEY)
+
+
+@datacraft.registry.types(_RANGE_KEY)
+def _configure_range_supplier(field_spec, _):
+    """ configures the range value supplier """
+    if 'data' not in field_spec:
+        raise datacraft.SpecException('No data element defined for: %s' % json.dumps(field_spec))
+
+    data = field_spec.get('data')
+    if not isinstance(data, list) or len(data) < 2:
+        raise datacraft.SpecException(
+            'data element for ranges type must be list with at least two elements: %s' % json.dumps(field_spec))
+    # we have the nested case
+    if isinstance(data[0], list):
+        suppliers_list = [_configure_range_supplier_for_data(field_spec, subdata) for subdata in data]
+        return datacraft.suppliers.from_list_of_suppliers(suppliers_list, True)
+    return _configure_range_supplier_for_data(field_spec, data)
+
+
+@datacraft.registry.usage(_RANGE_KEY)
+def _example_range_usage():
+    example = {
+        "zero_to_ten_step_half": {
+            "type": _RANGE_KEY,
+            "data": [0, 10, 0.5]
+        }
+    }
+    return common.standard_example_usage(example, 3)
+
+
+@datacraft.registry.usage(_RAND_RANGE_KEY)
+def _example_rand_range_usage():
+    example = {
+        "four_digits_precision": {
+            "type": _RAND_RANGE_KEY,
+            "data": [0, 10, 4]
+        }
+    }
+    return common.standard_example_usage(example, 3)
+
+
+@datacraft.registry.usage(_RAND_INT_RANGE_KEY)
+def _example_range_int_range_usage():
+    example = {
+        "rand_0_to_99": {
+            "type": _RAND_INT_RANGE_KEY,
+            "data": [0, 100]
+        }
+    }
+    return common.standard_example_usage(example, 3)
+
+
+def _configure_range_supplier_for_data(field_spec, data):
+    """ configures the supplier based on the range data supplied """
+    config = field_spec.get('config', {})
+    precision = config.get('precision', None)
+    if precision and not str(precision).isnumeric():
+        raise datacraft.SpecException(f'precision must be valid integer {json.dumps(field_spec)}')
+
+    start = data[0]
+    # default for built in range function is exclusive end, we want to default to inclusive as this is the
+    # more intuitive behavior
+    end = data[1] + 1
+    if not end > start:
+        raise datacraft.SpecException('end element must be larger than start: %s' % json.dumps(field_spec))
+    if len(data) == 2:
+        step = 1
+    else:
+        step = data[2]
+    try:
+        return datacraft.suppliers.range_supplier(start, end, step, precision=precision)
+    except ValueError as err:
+        raise datacraft.SpecException(str(err)) from err
+
+
+@datacraft.registry.types(_RAND_INT_RANGE_KEY)
+def _configure_rand_int_range_supplier(field_spec, loader):
+    """ configures the random int range value supplier """
+    config = datacraft.utils.load_config(field_spec, loader)
+    config['cast'] = 'int'
+    field_spec['config'] = config
+    return _configure_rand_range_supplier(field_spec, loader)
+
+
+@datacraft.registry.types(_RAND_RANGE_KEY)
+def _configure_rand_range_supplier(field_spec, loader):
+    """ configures the random range value supplier """
+    if 'data' not in field_spec:
+        raise datacraft.SpecException('No data element defined for: %s' % json.dumps(field_spec))
+    data = field_spec.get('data')
+    config = datacraft.utils.load_config(field_spec, loader)
+    if not isinstance(data, list) or len(data) == 0:
+        raise datacraft.SpecException(
+            'rand_range specs require data as array with at least one element: %s' % json.dumps(field_spec))
+    start = 0
+    end = 0
+    if len(data) == 1:
+        end = data[0]
+    if len(data) >= 2:
+        start = data[0]
+        end = data[1]
+    precision = None
+    if len(data) > 2:
+        precision = data[2]
+    # config overrides third data element if specified
+    precision = config.get('precision', precision)
+    return datacraft.suppliers.random_range(start, end, precision)
diff --git a/datacraft/_registered_types/refs.py b/datacraft/_registered_types/refs.py
new file mode 100644
index 0000000..325e1bb
--- /dev/null
+++ b/datacraft/_registered_types/refs.py
@@ -0,0 +1,118 @@
+import json
+import logging
+from typing import Dict
+
+import datacraft
+from datacraft import ValueSupplierInterface
+from . import common
+from . import schemas
+
+_log = logging.getLogger(__name__)
+_REF_KEY = 'ref'
+_WEIGHTED_REF_KEY = 'weighted_ref'
+
+
+@datacraft.registry.schemas(_REF_KEY)
+def _get_ref_schema():
+    """ schema for ref type """
+    return schemas.load(_REF_KEY)
+
+
+@datacraft.registry.types(_REF_KEY)
+def _configure_ref_supplier(field_spec: dict, loader: datacraft.Loader):
+    """ configures supplier for ref type """
+    key = None
+    if 'data' in field_spec:
+        key = field_spec.get('data')
+    if 'ref' in field_spec:
+        key = field_spec.get('ref')
+    if key is None:
+        raise datacraft.SpecException('No key found for spec: ' + json.dumps(field_spec))
+    return loader.get(key)
+
+
+@datacraft.registry.schemas(_WEIGHTED_REF_KEY)
+def _weighted_ref_schema():
+    """ schema for weighted_ref type """
+    return schemas.load(_WEIGHTED_REF_KEY)
+
+
+@datacraft.registry.types(_WEIGHTED_REF_KEY)
+def _configure_weighted_ref_supplier(parent_field_spec, loader):
+    """ configures supplier for weighted ref specs """
+    config = datacraft.utils.load_config(parent_field_spec, loader)
+    data = parent_field_spec['data']
+    key_supplier = datacraft.suppliers.values(data)
+    values_map = {}
+    for key in data.keys():
+        supplier = loader.get(key)
+        values_map[key] = supplier
+    supplier = weighted_ref_supplier(key_supplier, values_map)
+    if 'count' in config:
+        return datacraft.suppliers.array_supplier(supplier, **config)
+    return supplier
+
+
+@datacraft.registry.usage(_REF_KEY)
+def _example_ref_usage():
+    example = {"pointer": {"type": _REF_KEY, "data": "ref_name"}, "refs": {"ref_name": 42}}
+    return common.standard_example_usage(example, 3)
+
+
+@datacraft.registry.usage(_WEIGHTED_REF_KEY)
+def _example_weighted_ref_usage():
+    example = {
+        "http_code": {
+            "type": _WEIGHTED_REF_KEY,
+            "data": {"GOOD_CODES": 0.7, "BAD_CODES": 0.3}
+        },
+        "refs": {
+            "GOOD_CODES": {
+                "200": 0.5,
+                "202": 0.3,
+                "203": 0.1,
+                "300": 0.1
+            },
+            "BAD_CODES": {
+                "400": 0.5,
+                "403": 0.3,
+                "404": 0.1,
+                "500": 0.1
+            }
+        }
+    }
+    return common.standard_example_usage(example, 3)
+
+
+def weighted_ref_supplier(key_supplier: ValueSupplierInterface,
+                          values_map: Dict[str, ValueSupplierInterface]) -> ValueSupplierInterface:
+    """
+    Args:
+        key_supplier: supplier for ref keys
+        values_map: mapping of ref name to supplier for ref
+    """
+    return _WeightedRefsSupplier(key_supplier, values_map)
+
+
+class _WeightedRefsSupplier(ValueSupplierInterface):
+    """
+    Value supplier that uses a weighted scheme to supply values from different reference value suppliers
+    """
+
+    def __init__(self,
+                 key_supplier: ValueSupplierInterface,
+                 values_map: Dict[str, ValueSupplierInterface]):
+        """
+        Args:
+            key_supplier: supplier for ref keys
+            values_map: mapping of ref name to supplier for ref
+        """
+        self.key_supplier = key_supplier
+        self.values_map = values_map
+
+    def next(self, iteration):
+        key = self.key_supplier.next(iteration)
+        supplier = self.values_map.get(key)
+        if supplier is None:
+            raise datacraft.SupplierException("Unknown Key '%s' for Weighted Reference" % key)
+        return supplier.next(iteration)
diff --git a/datacraft/_registered_types/schema/__init__.py b/datacraft/_registered_types/schema/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/datacraft/schema/calculate.schema.json b/datacraft/_registered_types/schema/calculate.schema.json
similarity index 100%
rename from datacraft/schema/calculate.schema.json
rename to datacraft/_registered_types/schema/calculate.schema.json
diff --git a/datacraft/schema/char_class.schema.json b/datacraft/_registered_types/schema/char_class.schema.json
similarity index 100%
rename from datacraft/schema/char_class.schema.json
rename to datacraft/_registered_types/schema/char_class.schema.json
diff --git a/datacraft/schema/combine-list.schema.json b/datacraft/_registered_types/schema/combine-list.schema.json
similarity index 100%
rename from datacraft/schema/combine-list.schema.json
rename to datacraft/_registered_types/schema/combine-list.schema.json
diff --git a/datacraft/schema/combine.schema.json b/datacraft/_registered_types/schema/combine.schema.json
similarity index 100%
rename from datacraft/schema/combine.schema.json
rename to datacraft/_registered_types/schema/combine.schema.json
diff --git a/datacraft/schema/configref.schema.json b/datacraft/_registered_types/schema/configref.schema.json
similarity index 100%
rename from datacraft/schema/configref.schema.json
rename to datacraft/_registered_types/schema/configref.schema.json
diff --git a/datacraft/schema/csv.schema.json b/datacraft/_registered_types/schema/csv.schema.json
similarity index 100%
rename from datacraft/schema/csv.schema.json
rename to datacraft/_registered_types/schema/csv.schema.json
diff --git a/datacraft/schema/date.schema.json b/datacraft/_registered_types/schema/date.schema.json
similarity index 100%
rename from datacraft/schema/date.schema.json
rename to datacraft/_registered_types/schema/date.schema.json
diff --git a/datacraft/schema/definitions.json b/datacraft/_registered_types/schema/definitions.json
similarity index 100%
rename from datacraft/schema/definitions.json
rename to datacraft/_registered_types/schema/definitions.json
diff --git a/datacraft/schema/distribution.schema.json b/datacraft/_registered_types/schema/distribution.schema.json
similarity index 100%
rename from datacraft/schema/distribution.schema.json
rename to datacraft/_registered_types/schema/distribution.schema.json
diff --git a/datacraft/schema/geo.lat.schema.json b/datacraft/_registered_types/schema/geo.lat.schema.json
similarity index 100%
rename from datacraft/schema/geo.lat.schema.json
rename to datacraft/_registered_types/schema/geo.lat.schema.json
diff --git a/datacraft/schema/geo.long.schema.json b/datacraft/_registered_types/schema/geo.long.schema.json
similarity index 100%
rename from datacraft/schema/geo.long.schema.json
rename to datacraft/_registered_types/schema/geo.long.schema.json
diff --git a/datacraft/schema/geo.pair.schema.json b/datacraft/_registered_types/schema/geo.pair.schema.json
similarity index 100%
rename from datacraft/schema/geo.pair.schema.json
rename to datacraft/_registered_types/schema/geo.pair.schema.json
diff --git a/datacraft/schema/ip.precise.schema.json b/datacraft/_registered_types/schema/ip.precise.schema.json
similarity index 100%
rename from datacraft/schema/ip.precise.schema.json
rename to datacraft/_registered_types/schema/ip.precise.schema.json
diff --git a/datacraft/schema/ip.schema.json b/datacraft/_registered_types/schema/ip.schema.json
similarity index 100%
rename from datacraft/schema/ip.schema.json
rename to datacraft/_registered_types/schema/ip.schema.json
diff --git a/datacraft/schema/nested.schema.json b/datacraft/_registered_types/schema/nested.schema.json
similarity index 100%
rename from datacraft/schema/nested.schema.json
rename to datacraft/_registered_types/schema/nested.schema.json
diff --git a/datacraft/schema/net.mac.schema.json b/datacraft/_registered_types/schema/net.mac.schema.json
similarity index 100%
rename from datacraft/schema/net.mac.schema.json
rename to datacraft/_registered_types/schema/net.mac.schema.json
diff --git a/datacraft/schema/range.schema.json b/datacraft/_registered_types/schema/range.schema.json
similarity index 100%
rename from datacraft/schema/range.schema.json
rename to datacraft/_registered_types/schema/range.schema.json
diff --git a/datacraft/schema/ref.schema.json b/datacraft/_registered_types/schema/ref.schema.json
similarity index 100%
rename from datacraft/schema/ref.schema.json
rename to datacraft/_registered_types/schema/ref.schema.json
diff --git a/datacraft/schema/select_list_subset.schema.json b/datacraft/_registered_types/schema/select_list_subset.schema.json
similarity index 100%
rename from datacraft/schema/select_list_subset.schema.json
rename to datacraft/_registered_types/schema/select_list_subset.schema.json
diff --git a/datacraft/schema/templated.schema.json b/datacraft/_registered_types/schema/templated.schema.json
similarity index 100%
rename from datacraft/schema/templated.schema.json
rename to datacraft/_registered_types/schema/templated.schema.json
diff --git a/datacraft/schema/unicode_range.schema.json b/datacraft/_registered_types/schema/unicode_range.schema.json
similarity index 100%
rename from datacraft/schema/unicode_range.schema.json
rename to datacraft/_registered_types/schema/unicode_range.schema.json
diff --git a/datacraft/schema/uuid.schema.json b/datacraft/_registered_types/schema/uuid.schema.json
similarity index 100%
rename from datacraft/schema/uuid.schema.json
rename to datacraft/_registered_types/schema/uuid.schema.json
diff --git a/datacraft/schema/values.schema.json b/datacraft/_registered_types/schema/values.schema.json
similarity index 100%
rename from datacraft/schema/values.schema.json
rename to datacraft/_registered_types/schema/values.schema.json
diff --git a/datacraft/schema/weighted_csv.schema.json b/datacraft/_registered_types/schema/weighted_csv.schema.json
similarity index 100%
rename from datacraft/schema/weighted_csv.schema.json
rename to datacraft/_registered_types/schema/weighted_csv.schema.json
diff --git a/datacraft/schema/weighted_ref.schema.json b/datacraft/_registered_types/schema/weighted_ref.schema.json
similarity index 100%
rename from datacraft/schema/weighted_ref.schema.json
rename to datacraft/_registered_types/schema/weighted_ref.schema.json
diff --git a/datacraft/_registered_types/schemas.py b/datacraft/_registered_types/schemas.py
new file mode 100644
index 0000000..204a7bc
--- /dev/null
+++ b/datacraft/_registered_types/schemas.py
@@ -0,0 +1,35 @@
+import json
+import logging
+
+import importlib_resources as pkg_resources
+
+from . import schema
+
+_log = logging.getLogger(__name__)
+
+
+def load_resource_as_json(resource_name: str) -> dict:
+    """ loads the internal json resource with the given resource_name """
+    try:
+        with pkg_resources.open_text(schema, resource_name) as schema_resource:
+            return json.load(schema_resource)
+    except FileNotFoundError as err:
+        from datacraft import ResourceError
+        raise ResourceError(f'No resource with name {resource_name} was found') from err
+
+
+def load(key: str) -> dict:
+    """
+    load the internal schema file for the provided key
+
+    Args:
+        key: type name for schema
+
+    Returns:
+        Schema as dictionary for given key
+
+    Raises:
+        ResourceError if schema for key not found
+    """
+    naming_convention = f'{key}.schema.json'
+    return load_resource_as_json(naming_convention)
diff --git a/datacraft/_registered_types/select_list_subset.py b/datacraft/_registered_types/select_list_subset.py
new file mode 100644
index 0000000..1edb939
--- /dev/null
+++ b/datacraft/_registered_types/select_list_subset.py
@@ -0,0 +1,70 @@
+import json
+import logging
+
+import datacraft
+from . import common
+from . import schemas
+
+_log = logging.getLogger(__name__)
+_SELECT_LIST_SUBSET_KEY = 'select_list_subset'
+# alias
+_SAMPLE_KEY = 'sample'
+
+
+@datacraft.registry.schemas(_SAMPLE_KEY)
+@datacraft.registry.schemas(_SELECT_LIST_SUBSET_KEY)
+def _select_list_subset_schema():
+    """ schema for select_list_subset type """
+    return schemas.load(_SELECT_LIST_SUBSET_KEY)
+
+
+@datacraft.registry.types(_SAMPLE_KEY)
+@datacraft.registry.types(_SELECT_LIST_SUBSET_KEY)
+def _configure_select_list_subset_supplier(field_spec, loader):
+    """ configures supplier for select_list_subset type """
+    config = datacraft.utils.load_config(field_spec, loader)
+    data = None
+    if config is None or ('mean' not in config and 'count' not in config):
+        raise datacraft.SpecException('Config with mean or count defined must be provided: %s' % json.dumps(field_spec))
+    if 'ref' in field_spec and 'data' in field_spec:
+        raise datacraft.SpecException('Only one of "data" or "ref" can be provided for: %s' % json.dumps(field_spec))
+
+    if 'ref' in field_spec:
+        ref_name = field_spec.get('ref')
+        field_spec = loader.get_ref(ref_name)
+        if field_spec is None:
+            raise datacraft.SpecException('No ref with name %s found: %s' % (ref_name, json.dumps(field_spec)))
+
+        if 'data' in field_spec:
+            data = field_spec.get('data')
+        else:
+            data = field_spec
+    elif 'data' in field_spec:
+        data = field_spec.get('data')
+    if data is None:
+        raise datacraft.SpecException(
+            'Unable to identify data for ' + _SELECT_LIST_SUBSET_KEY + ' for spec: ' + json.dumps(field_spec))
+    return datacraft.suppliers.select_list_subset(data, **config)
+
+
+@datacraft.registry.usage(_SAMPLE_KEY)
+def _example_sample_usage():
+    return "alias for " + _SELECT_LIST_SUBSET_KEY
+
+
+@datacraft.registry.usage(_SELECT_LIST_SUBSET_KEY)
+def _example_select_list_subset_usage():
+    example = {
+        "ingredients": {
+            "type": _SELECT_LIST_SUBSET_KEY,
+            "data": ["onions", "mushrooms", "garlic", "bell peppers", "spinach", "potatoes", "carrots"],
+            "config": {
+                "mean": 3,
+                "stddev": 1,
+                "min": 2,
+                "max": 4,
+                "join_with": ", "
+            }
+        }
+    }
+    return common.standard_example_usage(example, 3)
diff --git a/datacraft/_registered_types/templated.py b/datacraft/_registered_types/templated.py
new file mode 100644
index 0000000..6a7b912
--- /dev/null
+++ b/datacraft/_registered_types/templated.py
@@ -0,0 +1,49 @@
+import json
+import logging
+
+import datacraft
+from . import common
+from . import schemas
+from .common import build_suppliers_map
+
+_log = logging.getLogger(__name__)
+_TEMPLATED_KEY = 'templated'
+
+
+@datacraft.registry.schemas(_TEMPLATED_KEY)
+def _templated_schema():
+    return schemas.load(_TEMPLATED_KEY)
+
+
+@datacraft.registry.types(_TEMPLATED_KEY)
+def _configure_templated_type(field_spec, loader):
+    if 'data' not in field_spec:
+        raise datacraft.SpecException(f'data is required field for templated specs: {json.dumps(field_spec)}')
+    suppliers_map = build_suppliers_map(field_spec, loader)
+
+    return datacraft.suppliers.templated(suppliers_map, field_spec.get('data', None))
+
+
+@datacraft.registry.usage(_TEMPLATED_KEY)
+def _example_templated_usage():
+    example = {
+        "user_agent": {
+            "type": "templated",
+            "data": "Mozilla/5.0 ({{ system }}) {{ platform }}",
+            "refs": ["system", "platform"],
+        },
+        "refs": {
+            "system": {
+                "type": "values",
+                "data": [
+                    "Windows NT 6.1; Win64; x64; rv:47.0",
+                    "Macintosh; Intel Mac OS X x.y; rv:42.0"
+                ]
+            },
+            "platform": {
+                "type": "values",
+                "data": ["Gecko/20100101 Firefox/47.0", "Gecko/20100101 Firefox/42.0"]
+            }
+        }
+    }
+    return common.standard_example_usage(example, 1, pretty=True)
diff --git a/datacraft/_registered_types/unicode_range.py b/datacraft/_registered_types/unicode_range.py
new file mode 100644
index 0000000..6bf17bd
--- /dev/null
+++ b/datacraft/_registered_types/unicode_range.py
@@ -0,0 +1,43 @@
+import json
+import logging
+
+import datacraft
+from . import common
+from . import schemas
+
+_log = logging.getLogger(__name__)
+_UNICODE_RANGE_KEY = 'unicode_range'
+
+
+@datacraft.registry.schemas(_UNICODE_RANGE_KEY)
+def _get_unicode_range_schema():
+    """ get the unicode range schema """
+    return schemas.load(_UNICODE_RANGE_KEY)
+
+
+@datacraft.registry.types(_UNICODE_RANGE_KEY)
+def _configure_unicode_range_supplier(spec, loader):
+    """ configure the supplier for unicode_range types """
+    if 'data' not in spec:
+        raise datacraft.SpecException('data is Required Element for unicode_range specs: ' + json.dumps(spec))
+    data = spec['data']
+    if not isinstance(data, list):
+        raise datacraft.SpecException(
+            f'data should be a list or list of lists with two elements for {_UNICODE_RANGE_KEY} specs: ' + json.dumps(
+                spec))
+    config = datacraft.utils.load_config(spec, loader)
+    return datacraft.suppliers.unicode_range(data, **config)
+
+
+@datacraft.registry.usage(_UNICODE_RANGE_KEY)
+def _example_unicode_range_usage():
+    example = {
+        "text": {
+            "type": _UNICODE_RANGE_KEY,
+            "data": ["3040", "309f"],
+            "config": {
+                "mean": 5
+            }
+        }
+    }
+    return common.standard_example_usage(example, 3)
diff --git a/datacraft/_registered_types/uuid_handler.py b/datacraft/_registered_types/uuid_handler.py
new file mode 100644
index 0000000..11dfb4c
--- /dev/null
+++ b/datacraft/_registered_types/uuid_handler.py
@@ -0,0 +1,42 @@
+import json
+import logging
+
+from . import common
+import datacraft
+from . import schemas
+
+_log = logging.getLogger(__name__)
+_UUID_KEY = 'uuid'
+
+
+@datacraft.registry.schemas(_UUID_KEY)
+def _get_uuid_schema():
+    """ get the schema for uuid type """
+    return schemas.load(_UUID_KEY)
+
+
+@datacraft.registry.types(_UUID_KEY)
+def _configure_uuid_supplier(field_spec, loader):
+    """ configure the supplier for uuid types """
+    config = datacraft.utils.load_config(field_spec, loader)
+    variant = int(config.get('variant', datacraft.registries.get_default('uuid_variant')))
+
+    if variant not in [1, 3, 4, 5]:
+        raise datacraft.SpecException('Invalid variant for: ' + json.dumps(field_spec))
+    return datacraft.suppliers.uuid(variant)
+
+
+@datacraft.registry.usage(_UUID_KEY)
+def _example_uuid_usage():
+    example = {
+      "id": {
+        "type": _UUID_KEY,
+      },
+      "id_variant3": {
+        "type": _UUID_KEY,
+        "config": {
+          "variant": 3
+        }
+      }
+    }
+    return common.standard_example_usage(example, 3, pretty=True)
diff --git a/datacraft/_registered_types/values.py b/datacraft/_registered_types/values.py
new file mode 100644
index 0000000..9d54291
--- /dev/null
+++ b/datacraft/_registered_types/values.py
@@ -0,0 +1,32 @@
+import logging
+
+import datacraft
+from . import common
+from . import schemas
+
+_log = logging.getLogger(__name__)
+_VALUES_KEY = 'values'
+
+
+@datacraft.registry.schemas(_VALUES_KEY)
+def _get_values_schema():
+    """ returns the values schema """
+    return schemas.load(_VALUES_KEY)
+
+
+@datacraft.registry.types(_VALUES_KEY)
+def _handle_values_type(spec, loader):
+    """ handles values types """
+    config = datacraft.utils.load_config(spec, loader)
+    return datacraft.suppliers.values(spec, **config)
+
+
+@datacraft.registry.usage(_VALUES_KEY)
+def _example_uuid_usage():
+    example = {
+        "field_constant": {"type": "values", "data": 42},
+        "field_list": {"type": "values", "data": [1, 2, 3, 5, 8, 13, None]},
+        "field_weighted": {"type": "values", "data": {"200": 0.6, "404": 0.1, "303": 0.3}},
+        "field_weighted_with_null": {"type": "values", "data": {"200": 0.5, "404": 0.1, "303": 0.3, "_NULL_": 0.1}}
+    }
+    return common.standard_example_usage(example, 1, pretty=True)
diff --git a/datacraft/cli.py b/datacraft/cli.py
index af832cf..a1559dd 100644
--- a/datacraft/cli.py
+++ b/datacraft/cli.py
@@ -8,14 +8,13 @@
 import logging
 import yaml
 
-from . import outputs, utils
-from . import template_engines, builder, spec_formatters, loader, registries
+from . import outputs, utils, usage
+from . import template_engines, builder, spec_formatters, loader, registries, entrypoints
 # this activates the decorators, so they will be discoverable
 from .exceptions import SpecException
 
 # need to import this to trigger registration process
 from . import preprocessor, defaults, distributions
-from . import _registered_types
 
 _log = logging.getLogger(__name__)
 
@@ -48,7 +47,7 @@ def parseargs(argv):
                         help='Path to template to populate, or template inline as a string')
     parser.add_argument('-r', '--records-per-file', dest='records_per_file', default=None, type=int,
                         help='Number of records to place in each file, default is all, requires -o to be specified')
-    parser.add_argument('-k', '--printkey', action='store_true', default=False,
+    parser.add_argument('-k', '--printkey', action='store_true',
                         help='When printing to stdout field name should be printed along with value')
     parser.add_argument('-c', '--code', nargs='+',
                         help='Path to custom defined functions in one or more modules to load')
@@ -62,14 +61,20 @@ def parseargs(argv):
     parser.add_argument('--strict', action='store_true',
                         default=registries.get_default('strict_mode'),
                         help='Enforce schema validation for all registered field specs')
-    parser.add_argument('--apply-raw', action='store_true', dest='apply_raw', default=False,
+    parser.add_argument('--apply-raw', action='store_true', dest='apply_raw',
                         help='Data from -s argument should be applied to the template with out treating as a Data Spec')
-    parser.add_argument('--debug-spec', dest='debug_spec', action='store_true', default=False,
-                        help='Debug spec after internal reformatting')
-    parser.add_argument('--debug-spec-yaml', dest='debug_spec_yaml', action='store_true', default=False,
-                        help='Debug spec after internal reformatting, write out as yaml')
-    parser.add_argument('--debug-defaults', dest='debug_defaults', action='store_true', default=False,
-                        help='List default values from registry after any external code loading')
+    debug_group = parser.add_argument_group('info')
+    debug_group.add_argument('--debug-spec', dest='debug_spec', action='store_true',
+                             help='Debug spec after internal reformatting')
+    debug_group.add_argument('--debug-spec-yaml', dest='debug_spec_yaml', action='store_true',
+                             help='Debug spec after internal reformatting, write out as yaml')
+    debug_group.add_argument('--debug-defaults', dest='debug_defaults', action='store_true',
+                             help='List default values from registry after any external code loading')
+    debug_group.add_argument('--type-list', dest='type_list', action='store_true',
+                             help='Write out the list of registered types')
+    debug_group.add_argument('--type-help', dest='type_help', default=argparse.SUPPRESS, nargs='*',
+                             help='Write out the help for registered types, specify one or more types to limit help '
+                                  'to, no arguments means show help for all types')
     parser.add_argument('-x', '--exclude-internal', dest='exclude_internal', action='store_true',
                         default=registries.get_default('exclude_internal'),
                         help='Do not include non data fields in output records')
@@ -141,6 +146,22 @@ def process_args(args):
     # Load Data Spec
     ###################
 
+    # this would bypass spec loading
+    if args.type_list:
+        writer = outputs.get_writer(args.outdir, outfile='type_list.json', overwrite=True)
+        # writer = _get_writer(args)
+        entrypoints.load_eps()
+        all_types = registries.registered_types()
+        for registered_type in all_types:
+            writer.write(registered_type)
+        return None
+    if 'type_help' in args:
+        writer = outputs.get_writer(args.outdir, outfile='type-help.txt', overwrite=True)
+        entrypoints.load_eps()
+        usage_str = usage.build_cli_help(args.type_help)
+        writer.write(usage_str)
+        return None
+
     _log.debug('Attempting to load Data Spec from %s', args.spec if args.spec else args.inline)
     spec = _load_spec(args)
     if spec is None:
diff --git a/datacraft/entrypoints.py b/datacraft/entrypoints.py
index b5fded9..1742756 100644
--- a/datacraft/entrypoints.py
+++ b/datacraft/entrypoints.py
@@ -3,6 +3,8 @@
 import logging
 import importlib_metadata as metadata
 
+import datacraft._registered_types.schemas
+
 _log = logging.getLogger(__name__)
 
 
diff --git a/datacraft/loader.py b/datacraft/loader.py
index be380c3..03cb60c 100644
--- a/datacraft/loader.py
+++ b/datacraft/loader.py
@@ -8,7 +8,7 @@
 from typing import Any, Dict, Union
 from abc import ABC, abstractmethod
 
-from . import utils, suppliers, entrypoints
+from . import utils, suppliers, preprocessor, spec_formatters
 from .exceptions import SpecException
 from .supplier.model import DataSpec, ValueSupplierInterface
 from .schemas import validate_schema_for_spec
@@ -197,7 +197,7 @@ def get_ref(self, key: str) -> dict:
         return self.refs.get(key)
 
 
-def _validate_schema_for_spec(spec_type, field_spec):
+def _validate_schema_for_spec(spec_type: str, field_spec: dict):
     """ validates the schema for the given spec type and field spec """
     type_schema = lookup_schema(spec_type)
     if type_schema is None:
@@ -205,6 +205,26 @@ def _validate_schema_for_spec(spec_type, field_spec):
     validate_schema_for_spec(spec_type, field_spec, type_schema)
 
 
+def preprocess_and_format(raw_spec: dict) -> str:
+    """Runs all preprocessors and formats spec as string
+
+    Args:
+        raw_spec: to process
+
+    Returns:
+        formatted spec as a string
+
+    Examples:
+        >>> import datacraft
+        >>> @datacraft.registry.usage('custom')
+        ... def _custom_type_usage():
+        ...     spec = datacraft.preprocess_and_format(_EXAMPLE)
+        ...     return f'Example Spec:\\n{spec}'
+    """
+    preprocessed = preprocess_spec(raw_spec)
+    return spec_formatters.format_json(preprocessed)
+
+
 def preprocess_spec(data_spec: Union[Dict[str, Dict], DataSpec]):
     """
     Uses the registered preprocessors to cumulatively update the spec
@@ -219,8 +239,9 @@ def preprocess_spec(data_spec: Union[Dict[str, Dict], DataSpec]):
     updated = dict(raw_spec)
     preprocessors = Registry.preprocessors.get_all()
     for name in preprocessors:
-        preprocessor = Registry.preprocessors.get(name)
-        processed = preprocessor(updated, False)
+        preprocessor_func = Registry.preprocessors.get(name)
+        _log.debug('Running Preprocessor: %s', name)
+        processed = preprocessor_func(updated, False)
         if processed is None:
             _log.error('Invalid preprocessor %s, returned None instead of updated spec, skipping', name)
             continue
diff --git a/datacraft/registries.py b/datacraft/registries.py
index 978d6f4..619f51a 100644
--- a/datacraft/registries.py
+++ b/datacraft/registries.py
@@ -30,6 +30,13 @@ class Registry:
             ... def _special_sauce_schema() -> dict:
             ...    # return JSON schema validating specs with type: special_sauce
 
+        usage:
+            Usage for field spec types, used to provide command line help and examples
+
+            >>> @datacraft.registry.usage('special_sauce')
+            ... def _special_sauce_usage() -> str:
+            ...    # return string describing how to use special_sauce
+
         preprocessors:
             Functions to modify specs before data generations process. If there is a customization you want to do for
             every data spec, or an extenstion you added that requires modifications to the spec before they are run,
@@ -84,6 +91,7 @@ class Registry:
     """
     types = catalogue.create('datacraft', 'type')
     schemas = catalogue.create('datacraft', 'schemas')
+    usage = catalogue.create('datacraft', 'usage')
     preprocessors = catalogue.create('datacraft', 'preprocessor')
     logging = catalogue.create('datacraft', 'logging')
     formats = catalogue.create('datacraft', 'format')
@@ -162,6 +170,11 @@ def registered_types():
     return list(Registry.types.get_all().keys())
 
 
+def registered_usage():
+    """ list of registered usage """
+    return list(Registry.usage.get_all().keys())
+
+
 def registered_casters():
     """ list of registered casters """
     return list(Registry.casters.get_all().keys())
diff --git a/datacraft/schemas.py b/datacraft/schemas.py
index 9499675..7ab59d2 100644
--- a/datacraft/schemas.py
+++ b/datacraft/schemas.py
@@ -1,43 +1,15 @@
 """
-Module for loading and validating schemas for various types
+Module for validating schemas for various types
 """
-import json
 import logging
-import importlib_resources as pkg_resources
 from jsonschema import Draft7Validator  # type: ignore
-from . import schema
-from .exceptions import ResourceError, SpecException
+from ._registered_types.schemas import load_resource_as_json
+from .exceptions import SpecException
 
 _log = logging.getLogger(__name__)
 
 
-def load(key: str) -> dict:
-    """
-    load the internal schema file for the provided key
-
-    Args:
-        key: type name for schema
-
-    Returns:
-        Schema as dictionary for given key
-
-    Raises:
-        ResourceError if schema for key not found
-    """
-    naming_convention = f'{key}.schema.json'
-    return _load_resource_as_json(naming_convention)
-
-
-def _load_resource_as_json(resource_name: str) -> dict:
-    """ loads the internal json resource with the given resource_name """
-    try:
-        with pkg_resources.open_text(schema, resource_name) as schema_resource:
-            return json.load(schema_resource)
-    except FileNotFoundError as err:
-        raise ResourceError(f'No resource with name {resource_name} was found') from err
-
-
-def validate_schema_for_spec(spec_type, field_spec, type_schema):
+def validate_schema_for_spec(spec_type: str, field_spec: dict, type_schema: dict):
     """
     performs schema validation for the provided field_spec
 
@@ -49,7 +21,7 @@ def validate_schema_for_spec(spec_type, field_spec, type_schema):
     Raises:
         SpecException if validation fails
     """
-    definitions = _load_resource_as_json('definitions.json')
+    definitions = load_resource_as_json('definitions.json')
     if 'definitions' in type_schema:
         type_schema['definitions'].update(definitions['definitions'])
     else:
diff --git a/datacraft/spec_formatters.py b/datacraft/spec_formatters.py
index c206303..b41d6a1 100644
--- a/datacraft/spec_formatters.py
+++ b/datacraft/spec_formatters.py
@@ -83,6 +83,8 @@ def _clean_semi_formatted_yaml(toclean: str):
 
 def _order_spec(raw_spec):
     """ orders the spec by ordering the underlying fields """
+    if isinstance(raw_spec, list):
+        return sorted(raw_spec)
     outer = {}
     for field, spec in raw_spec.items():
         if field == 'refs':
@@ -115,7 +117,7 @@ def _order_field_spec(field_spec):
         ordered['ref'] = field_spec['ref']
     if 'fields' in field_spec:
         fields = field_spec['fields']
-        # these should be full specs themselves
+        # these should be full specs themselves, or possibly lists for case of calculate
         ordered['fields'] = _order_spec(fields)
     if 'config' in field_spec:
         ordered['config'] = field_spec['config']
diff --git a/datacraft/supplier/nested.py b/datacraft/supplier/nested.py
index 59b8b03..46f1d6f 100644
--- a/datacraft/supplier/nested.py
+++ b/datacraft/supplier/nested.py
@@ -7,62 +7,4 @@
 from .exceptions import SupplierException
 
 
-def nested_supplier(field_supplier_map: Dict[str, ValueSupplierInterface],
-                    count_supplier: ValueSupplierInterface,
-                    key_supplier: KeyProviderInterface,
-                    as_list: bool) -> ValueSupplierInterface:
-    """
-    Args:
-        field_supplier_map: mapping of nested field name to value supplier for it
-        count_supplier: number of nested objects to create
-        key_supplier: to supply nest fields names
-        as_list: for counts of one, if the result should be a list instead of an object
-    """
-    return _NestedSupplier(field_supplier_map, count_supplier, key_supplier, as_list)
 
-
-class _NestedSupplier(ValueSupplierInterface):
-    """
-    Implementation for Nested Value Supplier
-    """
-
-    def __init__(self,
-                 field_supplier_map: Dict[str, ValueSupplierInterface],
-                 count_supplier: ValueSupplierInterface,
-                 key_supplier: KeyProviderInterface,
-                 as_list: bool):
-        """
-        Args:
-            field_supplier_map: mapping of nested field name to value supplier for it
-            count_supplier: number of nested objects to create
-            key_supplier: to supply nest fields names
-            as_list: for counts of one, if the result should be a list instead of an object
-        """
-        self.field_supplier_map = field_supplier_map
-        self.count_supplier = count_supplier
-        self.key_supplier = key_supplier
-        self.as_list = as_list
-
-    def next(self, iteration: int):
-        count = int(self.count_supplier.next(iteration))
-        if count == 0:
-            if self.as_list:
-                return []
-            return None
-        if count > 1:
-            vals = [self._single_pass(iteration + i) for i in range(count)]
-            return vals
-        # this is dict
-        vals = self._single_pass(iteration)  # type: ignore
-        if self.as_list:
-            return [vals]
-        return vals
-
-    def _single_pass(self, iteration: int) -> Dict[str, Any]:
-        """ get set of values for this iteration """
-        _, keys = self.key_supplier.get()
-        subset = {key: self.field_supplier_map.get(key) for key in keys}
-        if any(val is None for val in subset.values()):
-            raise SupplierException(f'One or more keys provided in nested spec are not valid: {keys}, valid keys: '
-                                    f'{list(self.field_supplier_map.keys())}')
-        return {key: supplier.next(iteration) for key, supplier in subset.items()}  # type: ignore
diff --git a/datacraft/supplier/refs.py b/datacraft/supplier/refs.py
deleted file mode 100644
index 93aae2c..0000000
--- a/datacraft/supplier/refs.py
+++ /dev/null
@@ -1,42 +0,0 @@
-"""
-Module for ref related value supplier implementations
-"""
-
-from typing import Dict
-
-from .model import ValueSupplierInterface
-from .exceptions import SupplierException
-
-
-def weighted_ref_supplier(key_supplier: ValueSupplierInterface,
-                          values_map: Dict[str, ValueSupplierInterface]) -> ValueSupplierInterface:
-    """
-    Args:
-        key_supplier: supplier for ref keys
-        values_map: mapping of ref name to supplier for ref
-    """
-    return _WeightedRefsSupplier(key_supplier, values_map)
-
-
-class _WeightedRefsSupplier(ValueSupplierInterface):
-    """
-    Value supplier that uses a weighted scheme to supply values from different reference value suppliers
-    """
-
-    def __init__(self,
-                 key_supplier: ValueSupplierInterface,
-                 values_map: Dict[str, ValueSupplierInterface]):
-        """
-        Args:
-            key_supplier: supplier for ref keys
-            values_map: mapping of ref name to supplier for ref
-        """
-        self.key_supplier = key_supplier
-        self.values_map = values_map
-
-    def next(self, iteration):
-        key = self.key_supplier.next(iteration)
-        supplier = self.values_map.get(key)
-        if supplier is None:
-            raise SupplierException("Unknown Key '%s' for Weighted Reference" % key)
-        return supplier.next(iteration)
diff --git a/datacraft/usage.py b/datacraft/usage.py
new file mode 100644
index 0000000..fda7144
--- /dev/null
+++ b/datacraft/usage.py
@@ -0,0 +1,39 @@
+""" Module to gather usage info from registered types """
+import logging
+
+from . import registries
+
+_TYPE_BREAK = '-------------------------------------'
+
+_log = logging.getLogger(__name__)
+
+
+def build_cli_help(included_types: list = None):
+    """Builds the command line interface help from the registered usage
+
+    Args:
+        included_types: list of type to include, default is to include all
+
+    Returns:
+        Formatted usage string from types
+    """
+    registered_types = registries.registered_types()
+    if included_types is None or len(included_types) == 0:
+        included_types = registered_types
+    usage_keys = registries.registered_usage()
+
+    width = max(len(key) for key in included_types)  # type: ignore
+
+    entries = [_TYPE_BREAK]
+    for type_key in included_types:  # type: ignore
+        if type_key in usage_keys:
+            func = registries.Registry.usage.get(type_key)
+            description = func()
+
+            entries.append(f'{type_key.ljust(width)} | {description}')
+        elif type_key not in registered_types:
+            entries.append(f'{type_key.ljust(width)} | unknown type')
+        else:
+            entries.append(f'{type_key.ljust(width)} | no usage defined')
+        entries.append(_TYPE_BREAK)
+    return '\n'.join(entries)
diff --git a/docs/source/cli.rst b/docs/source/cli.rst
index b93f812..c3e0312 100644
--- a/docs/source/cli.rst
+++ b/docs/source/cli.rst
@@ -27,6 +27,79 @@ Log Levels
 You can change the logging levels to one of ``debug, info, warn, error, or off`` by using the ``-l`` or
 ``--log-level`` flag. See example above.
 
+Type Help
+---------
+
+List Types
+^^^^^^^^^^
+
+To see a list of the registered types that can be used in specs use the ``--type-list`` command line flag:
+
+.. code-block:: shell
+
+    datacraft --type-list
+
+.. code-block::
+
+   INFO [22-May-2050 06:20:02 AM] Starting Loading Configurations...
+   INFO [22-May-2050 06:20:02 AM] Loading custom type loader: core
+   INFO [22-May-2050 06:20:02 AM] Loading custom type loader: xeger
+   calculate
+   char_class
+   cc-ascii
+   cc-lower
+   cc-upper
+   ...
+   uuid
+   values
+   xeger
+
+Type Usage
+^^^^^^^^^^
+
+To get detailed usage for all of the types use the ``--type-help`` flag. The flag with no arguments will list all of
+the available usage for all registered types. You can limit the usage printed to specific types by providing them as
+args to the ``--type-help`` flag:
+
+.. code-block:: shell
+
+   # lists all usage
+   datacraft --type-help
+   # only lists help for calculate type
+   datacraft --type-help calculate -l warn
+
+.. code-block::
+
+   -------------------------------------
+   calculate | Example Spec:
+   {
+     "height_in": {
+       "type": "values",
+       "data": [60, 70, 80, 90]
+     },
+     "height_cm": {
+       "type": "calculate",
+       "fields": [
+         "height_in"
+       ],
+       "formula": "{{ height_in }} * 2.54"
+     }
+   }
+   datacraft -s spec.json -i 3 --format json -x -l off
+   [{"height_in": 60, "height_cm": 152.4}, {"height_in": 70, "height_cm": 177.8}, {"height_in": 80, "height_cm": 203.2}]
+
+   -------------------------------------
+
+Specify the ``-o <directory>`` option to create a file type-help.txt, with the full usage info:
+
+.. code-block::
+
+   datacraft --type-help -o .
+   INFO [22-May-2050 01:13:15 PM] Starting Loading Configurations...
+   INFO [22-May-2050 01:13:15 PM] Loading custom type loader: core
+   INFO [22-May-2050 01:13:15 PM] Loading custom type loader: xeger
+   INFO [22-May-2050 01:13:15 PM] Wrote data to .\type-help.txt
+
 Formatting Output
 -----------------
 
@@ -68,7 +141,7 @@ separated value line. Examples:
 
 .. code-block:: shell
 
-    [{"id": "732376df-9adc-413e-8493-73555fae51f9", "ts": "21-04-2022"}, {"id": "d826774a-1eeb-4e35-8253-0b00a514c0d1", "ts": "02-04-2022"}]
+    [{"id": "732376df-9adc-413e-8493-73555fae51f9", "ts": "21-04-2050"}, {"id": "d826774a-1eeb-4e35-8253-0b00a514c0d1", "ts": "02-04-2050"}]
 
 .. code-block:: shell
 
@@ -79,11 +152,11 @@ separated value line. Examples:
    [
        {
            "id": "4a75d0fc-46b7-4c9b-82f1-c87dcee13674",
-           "ts": "09-04-2022"
+           "ts": "09-04-2050"
        },
        {
            "id": "62db293b-d8f8-4c9a-8653-6dba8713bab9",
-           "ts": "13-04-2022"
+           "ts": "13-04-2050"
        }
    ]
 
@@ -109,14 +182,14 @@ Examples:
 .. code-block:: shell
 
    datacraft --inline "{timestamp:date: {}}" -i 4 -r 2 --log-level off --format json -x
-   [{"timestamp": "25-04-2022"}, {"timestamp": "06-04-2022"}]
-   [{"timestamp": "09-04-2022"}, {"timestamp": "09-04-2022"}]
+   [{"timestamp": "25-04-2050"}, {"timestamp": "06-04-2050"}]
+   [{"timestamp": "09-04-2050"}, {"timestamp": "09-04-2050"}]
 
 
 .. code-block:: shell
 
    datacraft --inline "{timestamp:date: {}}" -i 4  --log-level off --format json -x
-   [{"timestamp": "22-04-2022"}, {"timestamp": "03-04-2022"}, {"timestamp": "10-04-2022"}, {"timestamp": "06-04-2022"}]
+   [{"timestamp": "22-04-2050"}, {"timestamp": "03-04-2050"}, {"timestamp": "10-04-2050"}, {"timestamp": "06-04-2050"}]
 
 
 Apply Raw
diff --git a/docs/source/conf.py b/docs/source/conf.py
index 1082a80..fe70448 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -8,8 +8,8 @@
 copyright = '2022, Brian Buxton'
 author = 'Brian Buxton'
 
-release = '0.3'
-version = '0.3.2'
+release = '0.4'
+version = '0.4.0'
 
 # -- General configuration
 
diff --git a/docs/source/types/date.rst b/docs/source/types/date.rst
index 3edee11..80c0d48 100644
--- a/docs/source/types/date.rst
+++ b/docs/source/types/date.rst
@@ -113,7 +113,7 @@ Examples:
         "config": {
           "center_date": "20500601 12:00",
           "format": "%Y%m%d %H:%M",
-          "hours": { "type": "values", "data": { "7": 0.1, "8": 0.2, "9": 0.4, "10": 0.2, "11": 0.1 }
+          "hours": { "type": "values", "data": { "7": 0.1, "8": 0.2, "9": 0.4, "10": 0.2, "11": 0.1 } }
         }
       }
     }
diff --git a/docs/source/types/distribution.rst b/docs/source/types/distribution.rst
index 1e25655..64866d8 100644
--- a/docs/source/types/distribution.rst
+++ b/docs/source/types/distribution.rst
@@ -46,12 +46,13 @@ Examples:
 
 .. code-block:: json
 
-    {
-      "ages": {
-        "type": "distribution",
-        "data": "normal(mean=28, stddev=10, min=18, max=40)"
-      }
-    }
+   {
+     "age": {
+       "type": "distribution",
+       "data": "normal(mean=28, stddev=10, min=18, max=40)",
+       "config": {"cast": "int"}
+     }
+   }
 
 .. code-block:: json
 
diff --git a/docs/source/types/refs.rst b/docs/source/types/refs.rst
index 9db76ff..a6bf81f 100644
--- a/docs/source/types/refs.rst
+++ b/docs/source/types/refs.rst
@@ -23,12 +23,12 @@ Examples:
 
 .. code-block:: json
 
-    { "pointer": { "type": "ref", "data": "ref_name" } }, "refs": { "ref_name": 42 } }
+    { "pointer": { "type": "ref", "data": "ref_name" }, "refs": { "ref_name": 42 } }
 
-    { "pointer": { "type": "ref", "ref": "ref_name" } }, "refs": { "ref_name": 42 } }
+    { "pointer": { "type": "ref", "ref": "ref_name" }, "refs": { "ref_name": 42 } }
 
-    { "pointer:ref": { "ref": "ref_name" } }, "refs": { "ref_name": 42 } }
+    { "pointer:ref": { "ref": "ref_name" }, "refs": { "ref_name": 42 } }
 
-    { "pointer:ref": { "data": "ref_name" } }, "refs": { "ref_name": 42 } }
+    { "pointer:ref": { "data": "ref_name" }, "refs": { "ref_name": 42 } }
 
     { "pointer:ref": "ref_name", "refs": { "ref_name": 42 } }
\ No newline at end of file
diff --git a/docs/source/types/uuid.rst b/docs/source/types/uuid.rst
index c9430c6..0510034 100644
--- a/docs/source/types/uuid.rst
+++ b/docs/source/types/uuid.rst
@@ -25,8 +25,8 @@ Examples:
       "id": {
         "type": "uuid"
       },
-      "id_shorthand:uuid": {}
-      "id_variant3" {
+      "id_shorthand:uuid": {},
+      "id_variant3": {
         "type": "uuid",
         "config": {
           "variant": 3
diff --git a/docs/source/usage.rst b/docs/source/usage.rst
index 31ce626..756d178 100644
--- a/docs/source/usage.rst
+++ b/docs/source/usage.rst
@@ -1024,7 +1024,6 @@ datacraft dependencies. Please install it first with pip or conda. Example using
 REST Server
 -----------
 
-<<<<<<< HEAD
 Datacraft comes with a lightweight Flask server to use to retrieve generated data. Use the ``--server`` with the optional
 ``--server-endpoint /someendpoint`` flags to launch this server.  The default end point will be found at
 http://127.0.0.1:5000/data. If using a template, each call to the endpoint will return the results of applying a
@@ -1040,9 +1039,6 @@ For this example we use the inline yaml spec: ``{id:uuid: {}, ts:date.iso: {}}``
 endpoint. The command below will spin up a flask server that will format the record using the json-pretty formatter.
 The records contain a uuid and a timestamp field.
 
-The code block below is the server side of the transaction. Here the tool is serving up data formatted using the
-json-pretty formatter. The records contain a uuid and a timestamp field.
-
 .. code-block:: shell
 
     $ datacraft --inline "{id:uuid: {}, ts:date.iso: {}}" -i 2 --log-level debug --format json-pretty --server
@@ -1057,7 +1053,6 @@ json-pretty formatter. The records contain a uuid and a timestamp field.
     No more iterations available
     127.0.0.1 - - [23/Nov/2050 20:48:46] "GET /data HTTP/1.1" 204 -
 
-
 Here is the client side of the transaction, where we perform a GET request on the /data endpoint.
 
 .. code-block:: bash
diff --git a/docs/templates/demo.json b/docs/templates/demo.json
new file mode 100644
index 0000000..a4daaf0
--- /dev/null
+++ b/docs/templates/demo.json
@@ -0,0 +1,5 @@
+{
+  "demo": {
+    "type": "uuid"
+  }
+}
diff --git a/docs/templates/raw_examples.json b/docs/templates/raw_examples.json
new file mode 100644
index 0000000..b139166
--- /dev/null
+++ b/docs/templates/raw_examples.json
@@ -0,0 +1,36 @@
+{
+  "examples": {
+    "calculate_1": {
+      "spec": {
+        "height_in": [ 60, 70, 80, 90 ],
+        "height_cm": {
+          "type": "calculate",
+          "fields": [
+            "height_in"
+          ],
+          "formula": "{{ height_in }} * 2.54"
+        }
+      }
+    },
+    "calculate_2": {
+      "spec": {
+        "long_name_one": {
+          "type": "values",
+          "data": [ 4, 5, 6 ]
+        },
+        "long_name_two": {
+          "type": "values",
+          "data": [ 3, 6, 9 ]
+        },
+        "c": {
+          "type": "calculate",
+          "fields": {
+            "long_name_one": "a",
+            "long_name_two": "b"
+          },
+          "formula": "sqrt({{a}}*{{a}} + {{b}}*{{b}})"
+        }
+      }
+    }
+  }
+}
diff --git a/docs/templates/tool.py b/docs/templates/tool.py
new file mode 100644
index 0000000..04e0b4b
--- /dev/null
+++ b/docs/templates/tool.py
@@ -0,0 +1,15 @@
+import argparse
+import json
+
+from datacraft import template_engines
+
+
+def main():
+    with open('raw_examples.json', 'r') as handle:
+        record = json.load(handle)
+    engine = template_engines.for_file('types/calculate.rst')
+    print(engine.process(record))
+
+
+if __name__ == '__main__':
+    main()
diff --git a/docs/templates/types/calculate.rst b/docs/templates/types/calculate.rst
new file mode 100644
index 0000000..69646bb
--- /dev/null
+++ b/docs/templates/types/calculate.rst
@@ -0,0 +1,56 @@
+{%- from "macros.jinja" import show_example with context %}
+calculate
+---------
+
+There are times when one field needs the value of another field in order to
+calculate its own value. For example, if you wanted to produce values that
+represented a users' height in inches and in centimeters, you would want them to
+correlate. You could use the `calculate` type to specify a `formula` to do this
+calculation. There are two ways to specify the fields to calculate a value from.
+The first is to use the `fields` and/or the `refs` keys with an array of fields
+or refs to use in the formula.  The second is the use a map where the field
+or ref name to be used is mapped to a string that will be used as an alias for
+it in the formula. See second example below for the mapped alias version.
+
+Prototype:
+
+.. code-block:: python
+
+    {
+      "<field name>": {
+        "type": "calculate",
+        "fields": List[str],
+        or
+        "refs": List[str],
+        "formula": <formula>
+        "config": {
+          "key": Any
+        }
+      }
+    }
+
+    formula (str): The formula to use in calculations
+
+Examples:
+
+{{ show_example(examples.calculate_1) }}
+
+{{ show_example(examples.calculate_2) }}
+
+
+
+We use the `asteval <http://newville.github.io/asteval/basics.html>`_
+package to do formula evaluation. This provides a fairly safe way to do
+evaluation. The package provides a bunch of
+`built-in-functions <http://newville.github.io/asteval/basics.html#built-in-functions>`_
+as well. We also use the `Jinja2 <https://pypi.org/project/Jinja2/>`_ templating
+engine format for specifying variable names to substitute. In theory, you
+could use any valid jinja2 syntax i.e.:
+
+{% raw %}
+.. code-block:: json
+
+    {
+      "formula": "sqrt({{ value_that_might_be_a_string | int }})"
+    }
+{% endraw -%}
\ No newline at end of file
diff --git a/docs/templates/types/char_class.rst b/docs/templates/types/char_class.rst
new file mode 100644
index 0000000..556461f
--- /dev/null
+++ b/docs/templates/types/char_class.rst
@@ -0,0 +1,95 @@
+char_class
+----------
+
+A ``char_class`` type is used to create strings that are made up of characters
+from specific character classes. The strings can be of fixed or variable length.
+There are several built in character classes. You can also provide your own set
+of characters to sample from. Below is the list of supported character classes:
+
+Built In Classes
+^^^^^^^^^^^^^^^^
+
+.. list-table::
+   :header-rows: 1
+
+   * - class
+     - description
+   * - ascii
+     - All valid ascii characters including control
+   * - lower
+     - ascii lowercase
+   * - upper
+     - ascii uppercase
+   * - digits
+     - Numbers 0 through 9
+   * - letters
+     - lowercase and uppercase
+   * - word
+     - letters + digits + '_'
+   * - printable
+     - All printable ascii chars including whitespace
+   * - visible
+     - All printable ascii chars excluding whitespace
+   * - punctuation
+     - local specific punctuation
+   * - special
+     - local specific punctuation
+   * - hex
+     - Hexadecimal digits including upper and lower case a-f
+   * - hex-lower
+     - Hexadecimal digits only including lower case a-f
+   * - hex-upper
+     - Hexadecimal digits only including upper case A-F
+
+Prototype:
+
+.. code-block:: python
+
+    {
+      "<field name>": {
+        "type": "char_class":
+        or
+        "type": "cc-<char_class_name>",
+        "data": <char_class_name>,
+        or
+        "data": <string with custom set of characters to sample from>
+        or
+        "data": [<char_class_name1>, <char_class_name2>, ..., <custom characters>]
+        "config":{
+          "exclude": <string of characters to exclude from output>,
+          "min": <min number of characters in string>,
+          "max": <max number of characters in string>,
+          or
+          "count": <exact number of characters in string>
+          or
+          "mean": <mean number of characters in string>
+          "stddev": <standard deviation from mean for number of characters in string>
+          "min": <optional min>
+          "max": <optional max>
+        }
+      }
+    }
+
+Examples:
+
+.. code-block:: json
+
+    {
+      "password": {
+        "type": "char_class",
+        "data": ["word", "special", "hex-lower", "M4$p3c!@l$@uc3"],
+        "config": {
+          "mean": 14,
+          "stddev": 2,
+          "min": 10,
+          "max": 18,
+          "exclude": ["-", "\""]
+        }
+      }
+    }
+
+.. code-block:: json
+
+    {
+      "one_to_five_digits:cc-digits?min=1&max=5": {}
+    }
diff --git a/docs/templates/types/combine.rst b/docs/templates/types/combine.rst
new file mode 100644
index 0000000..b488a1a
--- /dev/null
+++ b/docs/templates/types/combine.rst
@@ -0,0 +1,105 @@
+combine
+-------
+
+A combine Field Spec is used to concatenate or append two or more fields or reference to one another.
+There are two combine types: ``combine`` and ``combine-list``.
+
+combine
+^^^^^^^
+
+Prototype:
+
+.. code-block:: python
+
+    {
+      "<field name>": {
+        "type": "combine",
+        "fields": ["valid field name1", "valid field name2"],
+        OR
+        "refs": ["valid ref1", "valid ref2"],
+        "config": {
+          "join_with": "<optional string to use to join fields or refs, default is none>"
+        }
+      }
+    }
+
+Examples:
+
+.. code-block:: json
+
+    {
+      "combine": {
+        "type": "combine",
+        "refs": ["first", "last"],
+        "config": {
+          "join_with": " "
+        }
+      },
+      "refs": {
+        "first": {
+          "type": "values",
+          "data": ["zebra", "hedgehog", "llama", "flamingo"]
+        },
+        "last": {
+          "type": "values",
+          "data": ["jones", "smith", "williams"]
+        }
+      }
+    }
+
+combine-list
+^^^^^^^^^^^^
+
+Prototype:
+
+.. code-block:: python
+
+    {
+      "<field name>": {
+        "type": "combine-list",
+        "refs": [
+          ["valid ref1", "valid ref2"],
+          ["valid ref1", "valid ref2", "valid_ref3", ...], ...
+          ["another_ref", "one_more_ref"]
+        ],
+        "config": {
+          "join_with": "<optional string to use to join fields or refs, default is none>"
+        }
+      }
+    }
+
+Examples:
+
+.. code-block:: json
+
+    {
+      "full_name": {
+        "type": "combine-list",
+        "refs": [
+          ["first", "last"],
+          ["first", "middle", "last"],
+          ["first", "middle_initial", "last"]
+        ],
+        "config": {
+          "join_with": " "
+        }
+      },
+      "refs": {
+        "first": {
+          "type": "values",
+          "data": ["zebra", "hedgehog", "llama", "flamingo"]
+        },
+        "last": {
+          "type": "values",
+          "data": ["jones", "smith", "williams"]
+        },
+        "middle": {
+          "type": "values",
+          "data": ["cloud", "sage", "river"]
+        },
+        "middle_initial": {
+          "type": "values",
+          "data": {"a": 0.3, "m": 0.3, "j": 0.1, "l": 0.1, "e": 0.1, "w": 0.1}
+        }
+      }
+    }
diff --git a/docs/templates/types/config_ref.rst b/docs/templates/types/config_ref.rst
new file mode 100644
index 0000000..05ceec0
--- /dev/null
+++ b/docs/templates/types/config_ref.rst
@@ -0,0 +1,53 @@
+config_ref
+----------
+
+Reference for holding configurations common to multiple fields.
+
+Prototype:
+
+.. code-block:: python
+
+    {
+      "refs": {
+        "<config ref name>": {
+          "type": "config_ref",
+          "config": {
+            "key1": Any,
+            ...
+            "key2": Any
+          }
+        }
+      }
+    }
+
+Examples:
+
+.. code-block:: json
+
+    {
+      "status": {
+        "type": "csv",
+        "config": {
+          "column": 1,
+          "config_ref": "tabs_config"
+        }
+      },
+      "description": {
+        "type": "csv",
+        "config": {
+          "column": 2,
+          "config_ref": "tabs_config"
+        }
+      },
+      "status_type:csv?config_ref=tabs_config&column=3": {},
+      "refs": {
+        "tabs_config": {
+          "type": "config_ref",
+          "config": {
+            "datafile": "tabs.csv",
+            "delimiter": "\t",
+            "headers": true
+          }
+        }
+      }
+    }
\ No newline at end of file
diff --git a/docs/templates/types/csv.rst b/docs/templates/types/csv.rst
new file mode 100644
index 0000000..566d976
--- /dev/null
+++ b/docs/templates/types/csv.rst
@@ -0,0 +1,154 @@
+csv types
+---------
+
+If you have an existing large set of data in a tabular format that you want to
+use, it would be burdensome to copy and paste the data into a spec. To make use
+of data already in a tabular format you can use a ``csv`` Field Spec. These specs
+allow you to identify a column from a tabular data file to use to provide the
+values for a field. Another advantage of using a csv spec is that it is easy to
+have fields that are correlated be generated together. All rows will be selected
+incrementally, unless any of the fields are configured to use ``sample`` mode. You
+can use ``sample`` mode on individual columns, or you can use it across all
+columns by creating a ``config_ref`` spec. See ``csv_select`` for an
+efficient way to select multiple columns from a csv file.
+
+csv
+^^^
+
+Prototype:
+
+.. code-block:: python
+
+    {
+      "<field name>": {
+        "type": "csv",
+        "config": {
+          "datafile": "filename in datedir",
+          "headers": "yes, on, true for affirmative",
+          "column": "1 based column number or field name if headers are present",
+          "delimiter": "how values are separated, default is comma",
+          "quotechar": "how values are quoted, default is double quote",
+          "sample": "If the values should be selected at random, default is false",
+          "count": "Number of values in column to use for value"
+        }
+      }
+    }
+
+Examples:
+
+.. code-block:: json
+
+    {
+      "cities": {
+        "type": "csv",
+        "config": {
+          "datafile": "cities.csv",
+          "delimiter": "~",
+          "sample": true
+        }
+      }
+    }
+
+.. code-block:: json
+
+    {
+      "status": {
+        "type": "csv",
+        "config": {
+          "column": 1,
+          "config_ref": "tabs_config"
+        }
+      },
+      "description": {
+        "type": "csv",
+        "config": {
+          "column": 2,
+          "config_ref": "tabs_config"
+        }
+      },
+      "status_type:csv?config_ref=tabs_config&column=3": {},
+      "refs": {
+        "tabs_config": {
+          "type": "config_ref",
+          "config": {
+            "datafile": "tabs.csv",
+            "delimiter": "\\t",
+            "headers": true,
+            "sample_rows": true
+          }
+        }
+      }
+    }
+
+csv_select
+^^^^^^^^^^
+
+Prototype:
+
+.. code-block:: python
+
+    {
+      "<field name>": {
+        "type": "csv_select",
+        "data": {"<field_one>": <1 based column index for field 1>, ..., "<field n>": }
+        "config": {
+          "datafile": "filename in datedir",
+          "headers": "yes, on, true for affirmative",
+          "delimiter": "how values are separated, default is comma",
+          "quotechar": "how values are quoted, default is double quote"
+        }
+      }
+    }
+
+Examples:
+
+.. code-block:: json
+
+    {
+      "placeholder": {
+        "type": "csv_select",
+        "data": {"geonameid": 1, "name": 2, "latitude": 5, "longitude": 6, "country_code": 9, "population": 15},
+        "config": {
+          "datafile": "allCountries.txt",
+          "headers": false,
+          "delimiter": "\t"
+        }
+      }
+    }
+
+
+weighted_csv
+^^^^^^^^^^^^
+
+Prototype:
+
+.. code-block:: python
+
+    {
+      "<field name>": {
+        "type": "weighted_csv",
+        "config": {
+          "datafile": "filename in datedir",
+          "headers": "yes, on, true for affirmative",
+          "column": "1 based column number or field name if headers are present",
+          "weight_column": "1 based column number or field name if headers are present where weights are defined"
+          "delimiter": "how values are separated, default is comma",
+          "quotechar": "how values are quoted, default is double quote",
+          "sample": "If the values should be selected at random, default is false",
+          "count": "Number of values in column to use for value"
+        }
+      }
+    }
+
+Examples:
+
+.. code-block:: json
+
+    {
+      "cities": {
+        "type": "weighted_csv",
+        "config": {
+          "datafile": "weighted_cities.csv"
+        }
+      }
+    }
\ No newline at end of file
diff --git a/docs/templates/types/date.rst b/docs/templates/types/date.rst
new file mode 100644
index 0000000..3edee11
--- /dev/null
+++ b/docs/templates/types/date.rst
@@ -0,0 +1,119 @@
+date
+----
+
+A Date Field Spec is used to generate date strings. The default format is day-month-year i.e. Christmas 2050 would
+be: 25-12-2050. There is also a `date.iso` type that generates ISO8601 formatted date strings without microseconds
+and a `date.iso.us` for one that generates them with microseconds. We use the `format specification <https://docs
+.python.org/3/library/datetime.html#strftime-and-strptime-format-codes>`_ from the datetime module.
+
+Uniformly Sampled Dates
+^^^^^^^^^^^^^^^^^^^^^^^
+
+The default strategy is to create random dates within a 30 day range, where the start date is today. You can use the
+``start`` parameter to set a specific start date for the dates. You can also explicitly specify an ``end`` date. The
+``start`` and ``end`` parameters should conform to the specified date format, or the default
+if none is provided. The ``offset`` parameter can be used to shift the dates by a specified number of days. A
+positive ``offset`` will shift the start date back. A negative ``offset`` will shift the date forward. The
+``duration_days`` parameter can be used to specify the number of days that should be covered in the date range,
+instead of the default 30 days. This parameter is usually specified as an integer constant.
+
+.. code-block:: text
+
+       start                              end (default start + 30 days)
+          |--------------------------------|
+  |+offset|                           start+duration_days
+  |--------------------------------|
+          |-offset|
+                  |--------------------------------|
+
+
+Dates Distributed around a Center Point
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+An alternative strategy is to specify a ``center_date`` parameter with an optional ``stddev_days``. This will create
+a normal or gaussian distribution of dates around the center point.
+
+.. code-block:: text
+
+                       |
+                       |
+                    |  |  |
+                 |  |  |  |  |
+              |  |  |  |  |  |  |
+     |  |  |  |  |  |  |  |  |  |  |  |  |
+    |-------------------------------------|
+    |         | stddev | stddev |         |
+                    center
+
+
+Restricting Hours
+^^^^^^^^^^^^^^^^^
+
+If you want your generated dates to be restricted to certain hours of the day, you provide the ``hours`` config param.
+The value of this parameter can be any type of Field Spec that produces valid integers in the range of 0 to 23. See
+examples below.
+
+Prototype:
+
+.. code-block:: python
+
+    {
+      "<field name>": {
+        "type": "date",
+        OR,
+        "type": "date.iso",
+        OR,
+        "type": "date.iso.us",
+        "config": {
+          "format": "Valid datetime format string",
+          "duration_days": "The number of days from the start date to create date strings for",
+          "start": "date string matching format or default format to use for start date",
+          "end": "date string matching format or default  format to use for end date",
+          "offset": "number of days to shift base date by, positive means shift backwards, negative means forward",
+          "center_date": "date string matching format or default format to use for center date",
+          "stddev_days": "The standard deviation in days from the center date that dates should be distributed",
+          "hours": "spec describing how the hours should be populated, i.e. only between 9am and 5pm"
+        }
+      }
+    }
+
+Examples:
+
+.. code-block:: json
+
+    {
+      "dates": {
+        "type": "date",
+        "config": {
+          "duration_days": "90",
+          "start": "15-Dec-2050 12:00",
+          "format": "%d-%b-%Y %H:%M"
+        }
+      }
+    }
+
+.. code-block:: json
+
+    {
+      "dates": {
+        "type": "date",
+        "config": {
+          "center_date": "20500601 12:00",
+          "format": "%Y%m%d %H:%M",
+          "stddev_days": "2"
+        }
+      }
+    }
+
+.. code-block:: json
+
+    {
+      "start_time": {
+        "type": "date",
+        "config": {
+          "center_date": "20500601 12:00",
+          "format": "%Y%m%d %H:%M",
+          "hours": { "type": "values", "data": { "7": 0.1, "8": 0.2, "9": 0.4, "10": 0.2, "11": 0.1 }
+        }
+      }
+    }
diff --git a/docs/templates/types/distribution.rst b/docs/templates/types/distribution.rst
new file mode 100644
index 0000000..1e25655
--- /dev/null
+++ b/docs/templates/types/distribution.rst
@@ -0,0 +1,102 @@
+distribution
+------------
+
+A distribution spec can be built from one of the registered distribution types. Below is the table of the built in ones.
+Custom distributions can be registered using :ref:`Custom Code<custom_code>` Loading. See :ref:`Custom Count
+Distributions<custom_count_dist>` for an example.
+
+.. table::
+   :widths: 15 15 15 60
+
+   +--------------+--------------------+---------------+--------------------------------------+
+   | distribution | required arguments | optional args | examples                             |
+   +==============+====================+===============+======================================+
+   | uniform      | start,end          |               | "uniform(start=10, end=30)"          |
+   +--------------+--------------------+---------------+--------------------------------------+
+   |              |                    |               | "uniform(start=1, end=3)"            |
+   +--------------+--------------------+---------------+--------------------------------------+
+   | guass        | mean,stddev        | min,max       | "gauss(mean=2, stddev=1)"            |
+   +--------------+--------------------+---------------+--------------------------------------+
+   | guassian     |                    |               | "guassian(mean=7, stddev=1, min=4)"  |
+   +--------------+--------------------+---------------+--------------------------------------+
+   | normal       |                    |               | "normal(mean=25, stddev=10, max=40)" |
+   +--------------+--------------------+---------------+--------------------------------------+
+
+Prototype:
+
+.. code-block:: python
+
+    {
+      "<field name>": {
+        "type": "distribution",
+        "data": "<dist func name>(<param1>=<val1>, ..., <paramN>=<valN>)
+      }
+    }
+
+Examples:
+
+.. code-block:: json
+
+    {
+      "values": {
+        "type": "distribution",
+        "data": "uniform(start=10, end=30)"
+      }
+    }
+
+.. code-block:: json
+
+    {
+      "ages": {
+        "type": "distribution",
+        "data": "normal(mean=28, stddev=10, min=18, max=40)"
+      }
+    }
+
+.. code-block:: json
+
+    {
+      "pressure": {
+        "type": "distribution",
+        "data": "gauss(mean=33, stddev=3.4756535)",
+        "config": {
+          "count_dist": "normal(mean=2, stddev=1, min=1, max=4)",
+          "as_list": true
+        }
+      }
+    }
+
+A ``distribution`` type field with a uniform distribution will produce similar values to a ``rand_range`` field. With
+rand_range it is easier to specify a specific number of decimal places to keep. To do this for the distribution type,
+you need to make use of the ``cast`` config with a ``roundN`` :ref:`caster<casters>`. See example below.
+
+.. code-block:: json
+
+   {
+     "values1": {
+       "type": "rand_range",
+       "data": [10, 30, 4]
+     },
+     "values2": {
+       "type": "distribution",
+       "data": "uniform(start=10, end=30)",
+       "config": {
+         "cast": "round4"
+       }
+     },
+     "values3:rand_range": [10, 30, 4],
+     "values4:distribution?cast=round4": "uniform(start=10, end=30)"
+   }
+
+
+.. code-block:: shell
+
+   $ datacraft -s spec.json -i2 --log-level off --printkey
+   values1 -> 29.7907
+   values2 -> 18.9114
+   values3 -> 13.5495
+   values4 -> 15.5935
+   values1 -> 22.0634
+   values2 -> 17.8552
+   values3 -> 22.982
+   values4 -> 20.5616
\ No newline at end of file
diff --git a/docs/templates/types/geo.rst b/docs/templates/types/geo.rst
new file mode 100644
index 0000000..8d1873c
--- /dev/null
+++ b/docs/templates/types/geo.rst
@@ -0,0 +1,91 @@
+geo types
+---------
+
+There are three main geo types: ``geo.lat``, ``geo.long``, and ``geo.pair``. The defaults will create decimal string
+values in the valid ranges: -90 to 90 for latitude and -180 to 180 for longitude. You can bound the ranges in several
+ways. The first is with the start_lat, end_lat, start_long, end_long config params. These will set the individual
+bounds for each of the segments. You can use one or more of them. The other mechanism is by defining a bbox array
+which consists of the lower left geo point and the upper right one.
+
+.. list-table::
+   :header-rows: 1
+
+   * - type
+     - param
+     - description
+   * - all
+     - precision
+     - number of decimal places for lat or long, default is 4
+   * -
+     - bbox
+     - array of [min Longitude, min Latitude, max Longitude, max Latitude]
+   * - geo.lat
+     - start_lat
+     - lower bound for latitude
+   * -
+     - end_lat
+     - upper bound for latitude
+   * - geo.long
+     - start_long
+     - lower bound for longitude
+   * -
+     - end_long
+     - upper bound for longitude
+   * - geo.pair
+     - join_with
+     - delimiter to join long and lat with, default is comma
+   * -
+     - as_list
+     - One of yes, true, or on if the pair should be returned as a list instead of as a joined string
+   * -
+     - lat_first
+     - if latitude should be first in the generated pair, default is longitude first
+   * -
+     - start_lat
+     - lower bound for latitude
+   * -
+     - end_lat
+     - upper bound for latitude
+   * -
+     - start_long
+     - lower bound for longitude
+   * -
+     - end_long
+     - upper bound for longitude
+
+
+Prototype:
+
+.. code-block:: python
+
+    {
+      "<field name>": {
+        "type": "geo.lat",
+        or
+        "type": "geo.long",
+        or
+        "type": "geo.pair",
+        "config": {
+          "key": Any
+        }
+      }
+    }
+
+Examples:
+
+.. code-block:: json
+
+    {
+      "egypt": {
+        "type": "geo.pair",
+        "config": {
+          "bbox": [
+            31.33134,
+            22.03795,
+            34.19295,
+            25.00562
+          ],
+          "precision": 3
+        }
+      }
+    }
\ No newline at end of file
diff --git a/docs/templates/types/macros.jinja b/docs/templates/types/macros.jinja
new file mode 100644
index 0000000..0b9486b
--- /dev/null
+++ b/docs/templates/types/macros.jinja
@@ -0,0 +1,16 @@
+{%- macro show_example(example) %}
+.. tabs::
+
+   .. tab:: Data Spec
+
+      .. code-block:: json
+{% filter indent(width=9) %}
+{{ example.spec }}
+{% endfilter %}
+   .. tab:: Command and Output
+
+      .. code-block:: shell
+{% filter indent(width=9) %}
+{{ example.command }}
+{% endfilter %}
+{%- endmacro %}
\ No newline at end of file
diff --git a/docs/templates/types/nested.rst b/docs/templates/types/nested.rst
new file mode 100644
index 0000000..cb9c0f5
--- /dev/null
+++ b/docs/templates/types/nested.rst
@@ -0,0 +1,94 @@
+nested
+------
+
+Nested types are used to create fields that contain subfields. Nested types can
+also contain nested fields to allow multiple levels of nesting. Use the ``nested``
+type to generate a field that contains subfields. The subfields are defined in
+the ``fields`` element of the nested spec. The ``fields`` element will be treated
+like a top level DataSpec and has access to the ``refs`` and other elements of the
+root.
+
+Prototype:
+
+.. code-block:: python
+
+    {
+      "<field name>": {
+        "type": "nested",
+        "config": {
+          "count": "Values Spec for Counts, default is 1"
+        },
+        "fields": {
+          "<sub field one>": { spec definition here },
+          "<sub field two>": { spec definition here },
+          ...
+        },
+        "field_groups": <field groups format>
+      }
+    }
+
+Examples:
+
+.. code-block:: json
+
+    {
+      "id": {
+        "type": "uuid"
+      },
+      "user": {
+        "type": "nested",
+        "fields": {
+          "user_id": {
+            "type": "uuid"
+          },
+          "geo": {
+            "type": "nested",
+            "fields": {
+              "place_id:cc-digits?mean=5": {},
+              "coordinates:geo.pair?as_list=true": {}
+            }
+          }
+        }
+      }
+    }
+
+The same spec in a slightly more compact format
+
+.. code-block:: json
+
+    {
+      "id:uuid": {},
+      "user:nested": {
+        "fields": {
+          "user_id:uuid": {},
+          "geo:nested": {
+            "fields": {
+              "place_id:cc-digits?mean=5": {},
+              "coordinates:geo.pair?as_list=true": {}
+            }
+          }
+        }
+      }
+    }
+
+Generates the following structure
+
+.. code-block:: shell
+
+    datacraft -s tweet-geo.json --log-level off -x -i 1 --format json-pretty
+
+.. code-block:: json
+
+    {
+        "id": "68092478-2234-41aa-bcc6-e679950770d7",
+        "user": {
+            "user_id": "93b3c62e-76ad-4272-b3c1-b434be2c8c30",
+            "geo": {
+                "place_id": "5104987632",
+                "coordinates": [
+                    -93.0759,
+                    68.2469
+                ]
+            }
+        }
+    }
diff --git a/docs/templates/types/network.rst b/docs/templates/types/network.rst
new file mode 100644
index 0000000..595900f
--- /dev/null
+++ b/docs/templates/types/network.rst
@@ -0,0 +1,117 @@
+network types
+-------------
+
+Network related types
+
+ip/ipv4
+^^^^^^^
+
+Ip addresses can be generated
+using `CIDR notation <https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing>`_
+or by specifying a base. For example, if you wanted to generate ips in the
+10.0.0.0 to 10.0.0.255 range, you could either specify a ``cidr`` param of
+10.0.0.0/24 or a ``base`` param of 10.0.0.
+
+Prototype:
+
+.. code-block:: python
+
+    {
+      "<field name>": {
+        "type": "ipv4",
+        "config": {
+          "cidr": "<cidr value /8 /16 /24 only>",
+          OR
+          "base": "<beginning of ip i.e. 10.0>"
+        }
+      }
+    }
+
+Examples:
+
+.. code-block:: json
+
+    {
+      "network": {
+        "type": "ipv4",
+        "config": {
+          "cidr": "2.22.222.0/16"
+        }
+      },
+      "network_shorthand:ip?cidr=2.22.222.0/16": {},
+      "network_with_base:ip?base=192.168.0": {}
+    }
+
+ip.precise
+^^^^^^^^^^
+
+The default ip type only supports cidr masks of /8 /16 and /24. If you want more precise ip ranges you need to use the
+``ip.precise`` type. This type requires a cidr as the single config param. The default mode for ``ip.precise`` is to
+increment the ip addresses. Set config param sample to one of true, on, or yes to enable random ip addresses selected
+from the generated ranges.
+
+Prototype:
+
+.. code-block:: python
+
+    {
+      "<field name>": {
+        "type": "ipv4",
+        "config": {
+          "cidr": "<valid cidr value>",
+        }
+      }
+    }
+
+Examples:
+
+.. code-block:: json
+
+    {
+      "network": {
+        "type": "ip.precise",
+        "config": {
+          "cidr": "192.168.0.0/14",
+          "sample": "true"
+        }
+      }
+    }
+
+net.mac
+^^^^^^^
+
+For creating MAC addresses
+
+Prototype:
+
+.. code-block:: python
+
+    {
+      "<field name>": {
+        "type": "net.mac",
+        "config": {
+          "dashes": "If dashes should be used as the separator one of on, yes, 'true', or True"
+        }
+      }
+    }
+
+Examples:
+
+.. code-block:: json
+
+    {
+      "network": {
+        "type": "net.mac"
+      }
+    }
+
+.. code-block:: json
+
+    {
+      "network": {
+        "type": "net.mac",
+        "config": {
+          "dashes": "true"
+        }
+      }
+    }
\ No newline at end of file
diff --git a/docs/templates/types/ranges.rst b/docs/templates/types/ranges.rst
new file mode 100644
index 0000000..e6a0d09
--- /dev/null
+++ b/docs/templates/types/ranges.rst
@@ -0,0 +1,75 @@
+range_suppliers
+---------------
+
+There are two main range types sequential and random.  A sequential range is
+specified using the ``range`` type.  A random one uses the ``rand_range`` type.
+
+range
+^^^^^
+
+Prototype:
+
+.. code-block:: python
+
+    {
+      "<field name>": {
+        "type": "range",
+        "data": [<start>, <end>, <step> (optional)],
+        or
+        "data": [
+          [<start>, <end>, <step> (optional)],
+          [<start>, <end>, <step> (optional)],
+          ...
+          [<start>, <end>, <step> (optional)],
+        ],
+      }
+    }
+
+    start: (Union[int, float]) - start of range
+    end: (Union[int, float]) - end of range
+    step: (Union[int, float]) - step for range, default is 1
+
+Examples:
+
+.. code-block:: json
+
+    {
+      "zero_to_ten_step_half": {
+        "type": "range",
+        "data": [0, 10, 0.5]
+      }
+    }
+
+.. code-block:: json
+
+    {
+      "range_shorthand1:range": {
+        "data": [0, 10, 0.5]
+      }
+    }
+
+.. code-block:: json
+
+    {"range_shorthand2:range": [0, 10, 0.5]},
+
+rand_range
+^^^^^^^^^^
+
+Prototype:
+
+.. code-block:: python
+
+    {
+      "<field name>": {
+        "type": "rand_range",
+        "data": [<upper>],
+        or
+        "data": [<lower>, <upper>],
+        or
+        "data": [<lower>, <upper>, <precision> (optional)]
+      }
+    }
+
+    upper: (Union[int, float]) - upper limit of random range
+    lower: (Union[int, float]) - lower limit of random range
+    precision: (int) - Number of digits after decimal point
\ No newline at end of file
diff --git a/docs/templates/types/refs.rst b/docs/templates/types/refs.rst
new file mode 100644
index 0000000..9db76ff
--- /dev/null
+++ b/docs/templates/types/refs.rst
@@ -0,0 +1,34 @@
+refs
+----
+
+Pointer to a field spec defined in references section
+
+Prototype:
+
+.. code-block:: python
+
+    {
+      "<field name>": {
+        "type": "ref":
+        "ref": "<ref_name>",
+        or
+        "data": <ref_name>,
+        "config": {
+          "key": Any
+        }
+      }
+    }
+
+Examples:
+
+.. code-block:: json
+
+    { "pointer": { "type": "ref", "data": "ref_name" } }, "refs": { "ref_name": 42 } }
+
+    { "pointer": { "type": "ref", "ref": "ref_name" } }, "refs": { "ref_name": 42 } }
+
+    { "pointer:ref": { "ref": "ref_name" } }, "refs": { "ref_name": 42 } }
+
+    { "pointer:ref": { "data": "ref_name" } }, "refs": { "ref_name": 42 } }
+
+    { "pointer:ref": "ref_name", "refs": { "ref_name": 42 } }
\ No newline at end of file
diff --git a/docs/templates/types/select_list.rst b/docs/templates/types/select_list.rst
new file mode 100644
index 0000000..ad1bd21
--- /dev/null
+++ b/docs/templates/types/select_list.rst
@@ -0,0 +1,61 @@
+select_list_subset
+------------------
+
+A select_list_subset spec is used to select multiple values from a list to use as the value for a field.
+
+Prototype:
+
+.. code-block:: python
+
+    {
+      "<field name>": {
+        "type": "select_list_subset",
+        "config": {
+          "mean": N,
+          "stddev": N,
+          "min": N,
+          "max": N,
+          or
+          "count": N,
+          "join_with": "<optional delimiter to join with>"
+        },
+        "data": ["data", "to", "select", "from"],
+        OR
+        "ref": "REF_WITH_DATA_AS_LIST"
+      }
+    }
+
+Examples:
+
+.. code-block:: json
+
+    {
+      "ingredients": {
+        "type": "select_list_subset",
+        "data": ["onions", "mushrooms", "garlic", "bell peppers", "spinach", "potatoes", "carrots"],
+        "config": {
+          "mean": 3,
+          "stddev": 1,
+          "min": 2,
+          "max": 4,
+          "join_with": ", "
+        }
+      }
+    }
+
+.. code-block:: json
+
+    {
+      "ingredients": {
+        "type": "select_list_subset",
+        "data": ["onions", "mushrooms", "garlic", "bell peppers", "spinach", "potatoes", "carrots"],
+        "config": {
+          "mean": 3,
+          "stddev": 1,
+          "min": 2,
+          "max": 4,
+          "join_with": "\", \"",
+          "quote": "\""
+        }
+      }
+    }
diff --git a/docs/templates/types/templated.rst b/docs/templates/types/templated.rst
new file mode 100644
index 0000000..d72f453
--- /dev/null
+++ b/docs/templates/types/templated.rst
@@ -0,0 +1,45 @@
+templated
+---------
+
+A templated Field Spec is used to create strings by injecting the values from other fields into them. The other
+fields must be defined.  The values can come from references or other defined fields. Use the jinja2 ``{{ field }}``
+syntax to signify where the field should be injected.
+
+Prototype:
+
+.. code-block:: python
+
+    {
+      "<field name>": {
+        "type": "templated",
+        "data": "string with {{ jinja2 }} syntax fields",
+        "fields": ["valid field name1", "valid field name2"],
+        OR
+        "refs": ["valid ref1", "valid ref2"]
+      }
+    }
+
+Examples:
+
+.. code-block:: json
+
+    {
+      "templated": {
+        "type": "templated",
+        "data": "Mozilla/5.0 ({{ system }}) {{ platform }}",
+        "refs": ["system", "platform"],
+      },
+      "refs": {
+        "system": {
+          "type": "values",
+          "data": [
+            "(Windows NT 6.1; Win64; x64; rv:47.0)",
+            "(Macintosh; Intel Mac OS X x.y; rv:42.0)"
+          ]
+        },
+        "platform": {
+          "type": "values",
+          "data": ["Gecko/20100101 Firefox/47.0", "Gecko/20100101 Firefox/42.0"]
+        }
+      }
+    }
\ No newline at end of file
diff --git a/docs/templates/types/unicode.rst b/docs/templates/types/unicode.rst
new file mode 100644
index 0000000..5060e2c
--- /dev/null
+++ b/docs/templates/types/unicode.rst
@@ -0,0 +1,48 @@
+unicode_range
+-------------
+
+Generates strings from unicode ranges
+
+Prototype:
+
+.. code-block:: python
+
+    {
+      "<field>": {
+        "type": "unicode_range":
+        "data": [<start_code_point_in_hex>, <end_code_point_in_hex>],
+        or
+        "data": [
+            [<start_code_point_in_hex>, <end_code_point_in_hex>],
+            [<start_code_point_in_hex>, <end_code_point_in_hex>],
+            ...
+            [<start_code_point_in_hex>, <end_code_point_in_hex>],
+        ],
+        "config":{
+          # String Size Based Config Parameters
+          "min": <min number of characters in string>,
+          "max": <max number of characters in string>,
+          or
+          "count": <exact number of characters in string>
+          or
+          "mean": <mean number of characters in string>
+          "stddev": <standard deviation from mean for number of characters in string>
+          "min": <optional min>
+          "max": <optional max>
+        }
+      }
+    }
+
+Examples:
+
+.. code-block:: json
+
+    {
+      "text": {
+        "type": "unicode_range",
+        "data": ["3040", "309f"],
+        "config": {
+          "mean": 5
+        }
+      }
+    }
diff --git a/docs/templates/types/uuid.rst b/docs/templates/types/uuid.rst
new file mode 100644
index 0000000..c9430c6
--- /dev/null
+++ b/docs/templates/types/uuid.rst
@@ -0,0 +1,35 @@
+uuid
+----
+
+A standard uuid
+
+Prototype:
+
+.. code-block:: python
+
+    {
+      "<field name>": {
+        "type": "uuid",
+        "config": {
+          "variant": 1, 3, 4, or 5, default is 4, optional
+        }
+      }
+    }
+
+
+Examples:
+
+.. code-block:: json
+
+    {
+      "id": {
+        "type": "uuid"
+      },
+      "id_shorthand:uuid": {}
+      "id_variant3" {
+        "type": "uuid",
+        "config": {
+          "variant": 3
+        }
+      }
+    }
diff --git a/docs/templates/types/values.rst b/docs/templates/types/values.rst
new file mode 100644
index 0000000..b0266d1
--- /dev/null
+++ b/docs/templates/types/values.rst
@@ -0,0 +1,64 @@
+values
+------
+There are three types of values specs: Constants, List, and Weighted. Values specs have a shorthand notation where
+the value of the data element replaces the full spec. See examples below.
+
+Prototype:
+
+.. code-block:: python
+
+    {
+      "<field_name>": {
+        "type": "values",
+        "data": Union[str, bool, int, float, list, dict],
+        "config": {
+          "key": Any
+        }
+      }
+    }
+
+Examples:
+
+.. code-block:: json
+
+    {"field_constant": {"type": "values", "data": 42}}
+
+.. code-block:: python
+
+    {"field_list": {"type": "values", "data": [1, 2, 3, 5, 8, 13, None]}}
+
+.. code-block:: json
+
+    {"field_weighted": {"type": "values", "data": {"200": 0.6, "404": 0.1, "303": 0.3}}}
+
+.. code-block:: json
+
+    {"field_weighted_with_null": {"type": "values", "data": {"200": 0.5, "404": 0.1, "303": 0.3, "_NULL_": 0.1}}}
+
+.. code-block:: json
+
+    {"shorthand_field_constant": 42}
+
+.. code-block:: json
+
+    {"shorthand_field_list": [1, 2, 3, 5, 8, 13]}
+
+.. code-block:: json
+
+    {"shorthand_field_weighted": {"200": 0.6, "404": 0.1, "303": 0.3}}
+
+.. code-block:: json
+
+    {
+        "short_hand_field_weighted_with_null": {
+            "type": "values",
+            "data": {"200": 0.5, "404": 0.1, "303": 0.3, "_NONE_": 0.1}
+        }
+    }
+
+.. code-block:: text
+
+    $ datacraft -s spec.json -i 3 --format json -x --log-level off
+    {"short_hand_field_weighted_with_null": "200"}
+    {"short_hand_field_weighted_with_null": null}
+    {"short_hand_field_weighted_with_null": "200"}
\ No newline at end of file
diff --git a/docs/templates/types/weighted_refs.rst b/docs/templates/types/weighted_refs.rst
new file mode 100644
index 0000000..5613f9c
--- /dev/null
+++ b/docs/templates/types/weighted_refs.rst
@@ -0,0 +1,42 @@
+weighted_refs
+-------------
+A weighted_ref spec is used to select the values from a set of refs in a weighted fashion.
+
+Prototype:
+
+.. code-block:: python
+
+    {
+      "<field name>": {
+        "type": "weighted_ref",
+        "data": {"valid_ref_1": 0.N, "valid_ref_2": 0.N, ...}
+        "config": {
+          "key": Any
+        }
+      }
+    }
+
+Examples:
+
+.. code-block:: json
+
+    {
+      "http_code": {
+        "type": "weighted_ref",
+        "data": {"GOOD_CODES": 0.7, "BAD_CODES": 0.3}
+      },
+      "refs": {
+        "GOOD_CODES": {
+          "200": 0.5,
+          "202": 0.3,
+          "203": 0.1,
+          "300": 0.1
+        },
+        "BAD_CODES": {
+          "400": 0.5,
+          "403": 0.3,
+          "404": 0.1,
+          "500": 0.1
+        }
+      }
+    }
diff --git a/setup.cfg b/setup.cfg
index 3d89068..6fd3d3d 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,6 +1,6 @@
 [metadata]
 name = datacraft
-version = 0.3.2
+version = 0.4.0
 description = Data Generation Through Specification
 long_description = file: README.md
 long_description_content_type = text/markdown; charset=UTF-8
@@ -38,6 +38,8 @@ exclude = tests, docs
 [options.entry_points]
 console_scripts =
     datacraft = datacraft.__main__:wrap_main
+datacraft.custom_type_loader =
+    core = datacraft._registered_types.entrypoint:load_custom
 
 [options.package_data]
 * = *.json
@@ -47,6 +49,8 @@ test =
     pytest >= 6.2.2
     pycodestyle
     pytest-cov
+    pytest-mock
     pandas # for some tests
+    flask # for server tests
 all =
     %(test)s


Test patch:
diff --git a/docs/templates/run_tests.py b/docs/templates/run_tests.py
new file mode 100644
index 0000000..daf5271
--- /dev/null
+++ b/docs/templates/run_tests.py
@@ -0,0 +1,41 @@
+import re
+import json
+import subprocess
+
+
+def single_command(name, info):
+    cmd = info['command']
+    out = subprocess.check_output(cmd, shell=True).decode("utf-8")
+    match = info['match']
+    if match and 'lines' in match:
+        _verify_lines(match, out)
+    if match and 'exact' in match:
+        clean = out.strip().replace('\r\n', '\n')
+        assert clean == match['exact'].strip(), f"Output differs from expected, actual: {out.strip()}"
+
+
+def _verify_lines(match, out):
+    expected_lines = match['lines']
+    pattern = re.compile(match['pattern'])
+    # non blank parts
+    out_parts = [p for p in out.split('\n') if p.strip() != '']
+    # verify non blank lines is expected number
+    assert len(out_parts) == expected_lines
+    # verify pattern of output matches expected
+    non_matches = [line for line in out_parts if pattern.match(line) is None]
+    assert len(non_matches) == 0, f"Not all lines match pattern: {match['pattern']}"
+
+
+def multi_command(name, info):
+    for _, details in info.items():
+        single_command(name, details)
+
+
+def test_commands():
+    with open('tests.json') as f:
+        data = json.load(f)
+    for name, info in data.items():
+        if 'command' in info:
+            single_command(name, info)
+        elif 'commands' in info:
+            multi_command(name, info['commands'])
diff --git a/docs/templates/tests.json b/docs/templates/tests.json
new file mode 100644
index 0000000..eea8aa7
--- /dev/null
+++ b/docs/templates/tests.json
@@ -0,0 +1,30 @@
+{
+  "example_one": {
+    "command": "datacraft --inline \"{demo: {type: uuid}}\" --log-level off -i 5",
+    "match": {
+      "lines": 5,
+      "pattern": "[a-f0-9]{8}-?[a-f0-9]{4}-?4[a-f0-9]{3}-?[89ab][a-f0-9]{3}-?[a-f0-9]{12}"
+    }
+  },
+  "example_two": {
+    "commands": {
+      "1": {
+        "command":  "datacraft --inline \"{demo: {type: uuid}}\" --log-level off --debug-spec > demo.json",
+        "match": null
+      },
+      "2": {
+        "command": "cat demo.json",
+        "match": {
+          "exact": "{\n  \"demo\": {\n    \"type\": \"uuid\"\n  }\n}"
+        }
+      },
+      "3": {
+        "command": "datacraft -s demo.json --log-level off -i 5",
+        "match": {
+          "lines": 5,
+          "pattern": "[a-f0-9]{8}-?[a-f0-9]{4}-?4[a-f0-9]{3}-?[89ab][a-f0-9]{3}-?[a-f0-9]{12}"
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/tests/conftest.py b/tests/conftest.py
new file mode 100644
index 0000000..886e7fb
--- /dev/null
+++ b/tests/conftest.py
@@ -0,0 +1,9 @@
+import pytest
+
+import datacraft._registered_types.entrypoint
+
+
+@pytest.fixture(scope="session", autouse=True)
+def setup(request):
+    # since these may not be installed yet
+    datacraft._registered_types.entrypoint.load_custom()
diff --git a/tests/data/EXAMPLE.tests.json b/tests/data/EXAMPLE.tests.json
index 71af541..cdcec9f 100644
--- a/tests/data/EXAMPLE.tests.json
+++ b/tests/data/EXAMPLE.tests.json
@@ -1,12 +1,31 @@
 {
   "EXAMPLE.schema.json": {
     "valid": [
-      {"instance": {"type": "EXAMPLE", "data": "DO I HAVE A DATA ELEMENT?"}, "note": "explanatory..."}
+      {
+        "instance": {
+          "type": "EXAMPLE",
+          "data": "DO I HAVE A DATA ELEMENT?"
+        },
+        "note": "explanatory..."
+      }
     ],
     "invalid": [
-      {"instance": {}, "note": "empty"},
-      {"instance": {"type": "EXAMPLEs"}, "note": "misspelled"},
-      {"instance": {"type": "EXAMPLE"}, "note": "no fields or refs or data or stuff?"}
+      {
+        "instance": {},
+        "note": "empty"
+      },
+      {
+        "instance": {
+          "type": "EXAMPLEs"
+        },
+        "note": "misspelled"
+      },
+      {
+        "instance": {
+          "type": "EXAMPLE"
+        },
+        "note": "no fields or refs or data or stuff?"
+      }
     ]
   }
 }
\ No newline at end of file
diff --git a/tests/data/calculate.tests.json b/tests/data/calculate.tests.json
index 0fcb134..bc82c41 100644
--- a/tests/data/calculate.tests.json
+++ b/tests/data/calculate.tests.json
@@ -1,20 +1,114 @@
 {
   "calculate.schema.json": {
     "valid": [
-      {"instance": {"type": "calculate", "formula": "{{a}} + {{b}}", "refs": {"one": "a", "two": "b"}}},
-      {"instance": {"type": "calculate", "formula": "{{a}} + {{b}}", "fields": {"one": "a", "two": "b"}}},
-      {"instance": {"type": "calculate", "formula": "{{one}} + {{two}}", "refs": ["one", "two"]}},
-      {"instance": {"type": "calculate", "formula": "{{one}} + {{two}}", "fields": ["one", "two"]}},
-      {"instance": {"type": "calculate", "formula": "{{one}} + {{two}}", "fields": ["one"], "refs": ["two"]}}
+      {
+        "instance": {
+          "type": "calculate",
+          "formula": "{{a}} + {{b}}",
+          "refs": {
+            "one": "a",
+            "two": "b"
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "calculate",
+          "formula": "{{a}} + {{b}}",
+          "fields": {
+            "one": "a",
+            "two": "b"
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "calculate",
+          "formula": "{{one}} + {{two}}",
+          "refs": [
+            "one",
+            "two"
+          ]
+        }
+      },
+      {
+        "instance": {
+          "type": "calculate",
+          "formula": "{{one}} + {{two}}",
+          "fields": [
+            "one",
+            "two"
+          ]
+        }
+      },
+      {
+        "instance": {
+          "type": "calculate",
+          "formula": "{{one}} + {{two}}",
+          "fields": [
+            "one"
+          ],
+          "refs": [
+            "two"
+          ]
+        }
+      }
     ],
     "invalid": [
-      {"instance": {}, "note": "empty"},
-      {"instance": {"type": "calcalate"}, "note": "misspelled"},
-      {"instance": {"type": "calculate"}, "note": "no fields or refs"},
-      {"instance": {"type": "calculate", "refs": {"one": "a", "two": "b"}}, "note":  "missing formula"},
-      {"instance": {"type": "calculate", "fields": {"one": "a", "two": "b"}}, "note":  "missing formula"},
-      {"instance": {"type": "calculate", "refs": ["one", "two"]}},
-      {"instance": {"type": "calculate", "fields": ["one", "two"]}}
+      {
+        "instance": {},
+        "note": "empty"
+      },
+      {
+        "instance": {
+          "type": "calcalate"
+        },
+        "note": "misspelled"
+      },
+      {
+        "instance": {
+          "type": "calculate"
+        },
+        "note": "no fields or refs"
+      },
+      {
+        "instance": {
+          "type": "calculate",
+          "refs": {
+            "one": "a",
+            "two": "b"
+          }
+        },
+        "note": "missing formula"
+      },
+      {
+        "instance": {
+          "type": "calculate",
+          "fields": {
+            "one": "a",
+            "two": "b"
+          }
+        },
+        "note": "missing formula"
+      },
+      {
+        "instance": {
+          "type": "calculate",
+          "refs": [
+            "one",
+            "two"
+          ]
+        }
+      },
+      {
+        "instance": {
+          "type": "calculate",
+          "fields": [
+            "one",
+            "two"
+          ]
+        }
+      }
     ]
   }
 }
\ No newline at end of file
diff --git a/tests/data/char_class.tests.json b/tests/data/char_class.tests.json
index ddbb730..443d9b0 100644
--- a/tests/data/char_class.tests.json
+++ b/tests/data/char_class.tests.json
@@ -1,58 +1,237 @@
 {
   "char_class.schema.json": {
     "valid": [
-      {"instance": {"type": "char_class", "data": "sequence0fV@l!dCharact3rs"}},
-      {"instance": {"type": "char_class", "data": "ascii"}},
-      {"instance": {"type": "char_class", "data": "lower"}},
-      {"instance": {"type": "char_class", "data": "upper"}},
-      {"instance": {"type": "char_class", "data": "digits"}},
-      {"instance": {"type": "char_class", "data": "letters"}},
-      {"instance": {"type": "char_class", "data": "word"}},
-      {"instance": {"type": "char_class", "data": "printable"}},
-      {"instance": {"type": "char_class", "data": "visible"}},
-      {"instance": {"type": "char_class", "data": "punctuation"}},
-      {"instance": {"type": "char_class", "data": "special"}},
-      {"instance": {"type": "char_class", "data": "hex"}},
-      {"instance": {"type": "char_class", "data": "hex-lower"}},
-      {"instance": {"type": "char_class", "data": "hex-upper"}},
-      {"instance": {"type": "char_class", "data": ["visible"]}},
-      {"instance": {"type": "char_class", "data": ["digit", "word"]}},
-      {"instance": {"type": "char_class", "data": ["digit", "word", "CUSTOM12358"]}, "note": "two built in one custom"},
-      {"instance": {"type": "char_class", "data": ["CUSTOM12358", "!@#$%^&*()", ":):(:p;)"]}, "note":  "all custom"},
+      {
+        "instance": {
+          "type": "char_class",
+          "data": "sequence0fV@l!dCharact3rs"
+        }
+      },
+      {
+        "instance": {
+          "type": "char_class",
+          "data": "ascii"
+        }
+      },
+      {
+        "instance": {
+          "type": "char_class",
+          "data": "lower"
+        }
+      },
+      {
+        "instance": {
+          "type": "char_class",
+          "data": "upper"
+        }
+      },
+      {
+        "instance": {
+          "type": "char_class",
+          "data": "digits"
+        }
+      },
+      {
+        "instance": {
+          "type": "char_class",
+          "data": "letters"
+        }
+      },
+      {
+        "instance": {
+          "type": "char_class",
+          "data": "word"
+        }
+      },
+      {
+        "instance": {
+          "type": "char_class",
+          "data": "printable"
+        }
+      },
+      {
+        "instance": {
+          "type": "char_class",
+          "data": "visible"
+        }
+      },
+      {
+        "instance": {
+          "type": "char_class",
+          "data": "punctuation"
+        }
+      },
+      {
+        "instance": {
+          "type": "char_class",
+          "data": "special"
+        }
+      },
+      {
+        "instance": {
+          "type": "char_class",
+          "data": "hex"
+        }
+      },
+      {
+        "instance": {
+          "type": "char_class",
+          "data": "hex-lower"
+        }
+      },
+      {
+        "instance": {
+          "type": "char_class",
+          "data": "hex-upper"
+        }
+      },
+      {
+        "instance": {
+          "type": "char_class",
+          "data": [
+            "visible"
+          ]
+        }
+      },
+      {
+        "instance": {
+          "type": "char_class",
+          "data": [
+            "digit",
+            "word"
+          ]
+        }
+      },
+      {
+        "instance": {
+          "type": "char_class",
+          "data": [
+            "digit",
+            "word",
+            "CUSTOM12358"
+          ]
+        },
+        "note": "two built in one custom"
+      },
+      {
+        "instance": {
+          "type": "char_class",
+          "data": [
+            "CUSTOM12358",
+            "!@#$%^&*()",
+            ":):(:p;)"
+          ]
+        },
+        "note": "all custom"
+      },
       {
         "note": "count by itself is valid",
-        "instance": {"type": "char_class", "data": "word", "config": {"count": 5}}
+        "instance": {
+          "type": "char_class",
+          "data": "word",
+          "config": {
+            "count": 5
+          }
+        }
       },
       {
         "note": "min and max alone are valid",
-        "instance": {"type": "char_class", "data": "word", "config": {"min": 3, "max": 5}}
+        "instance": {
+          "type": "char_class",
+          "data": "word",
+          "config": {
+            "min": 3,
+            "max": 5
+          }
+        }
       },
       {
         "note": "mean by itself is valid",
-        "instance": {"type": "char_class", "data": "word", "config": {"mean": 5}}
+        "instance": {
+          "type": "char_class",
+          "data": "word",
+          "config": {
+            "mean": 5
+          }
+        }
       },
       {
         "note": "mean with stddev is valid",
-        "instance": {"type": "char_class", "data": "word", "config": {"mean": 5, "stddev":  1}}
+        "instance": {
+          "type": "char_class",
+          "data": "word",
+          "config": {
+            "mean": 5,
+            "stddev": 1
+          }
+        }
       }
     ],
     "invalid": [
-      {"instance": {}, "note": "empty"},
-      {"instance": {"type": "char_classes"}, "note": "misspelled"},
-      {"instance": {"type": "char_class"}, "note": "no data element defined"},
-      {"instance": {"type": "char_class", "data": 42}, "note": "data not a string"},
-      {"instance": {"type": "char_class", "data": [42]}, "note": "data not a string"},
+      {
+        "instance": {},
+        "note": "empty"
+      },
+      {
+        "instance": {
+          "type": "char_classes"
+        },
+        "note": "misspelled"
+      },
+      {
+        "instance": {
+          "type": "char_class"
+        },
+        "note": "no data element defined"
+      },
+      {
+        "instance": {
+          "type": "char_class",
+          "data": 42
+        },
+        "note": "data not a string"
+      },
+      {
+        "instance": {
+          "type": "char_class",
+          "data": [
+            42
+          ]
+        },
+        "note": "data not a string"
+      },
       {
         "note": "count and min together is invalid",
-        "instance": {"type": "char_class", "data": "word", "config": {"count": 5, "min": 3}}
+        "instance": {
+          "type": "char_class",
+          "data": "word",
+          "config": {
+            "count": 5,
+            "min": 3
+          }
+        }
       },
       {
         "note": "count and max together is invalid",
-        "instance": {"type": "char_class", "data": "word", "config": {"count": 5, "max": 5}}
+        "instance": {
+          "type": "char_class",
+          "data": "word",
+          "config": {
+            "count": 5,
+            "max": 5
+          }
+        }
       },
       {
         "note": "count and mean together is invalid",
-        "instance": {"type": "char_class", "data": "word", "config": {"count": 5, "mean": 5}}
+        "instance": {
+          "type": "char_class",
+          "data": "word",
+          "config": {
+            "count": 5,
+            "mean": 5
+          }
+        }
       }
     ]
   }
diff --git a/tests/data/combine.tests.json b/tests/data/combine.tests.json
index f1f3d3d..969c41c 100644
--- a/tests/data/combine.tests.json
+++ b/tests/data/combine.tests.json
@@ -1,31 +1,159 @@
 {
   "combine.schema.json": {
     "valid": [
-      {"instance": {"type": "combine", "refs": ["one", "two"]}},
-      {"instance": {"type": "combine", "fields": ["one", "two"]}},
-      {"instance": {"type": "combine", "refs": ["one", "two"], "config": {"join_with": " "}}},
-      {"instance": {"type": "combine", "refs": ["one", "two"], "config": {"as_list": true}}},
-      {"instance": {"type": "combine", "refs": ["one", "two"], "config": {"as_list": "true"}}},
-      {"instance": {"type": "combine", "refs": ["one", "two"], "config": {"as_list": "yes"}}},
-      {"instance": {"type": "combine", "refs": ["one", "two"], "config": {"as_list": "on"}}},
+      {
+        "instance": {
+          "type": "combine",
+          "refs": [
+            "one",
+            "two"
+          ]
+        }
+      },
+      {
+        "instance": {
+          "type": "combine",
+          "fields": [
+            "one",
+            "two"
+          ]
+        }
+      },
+      {
+        "instance": {
+          "type": "combine",
+          "refs": [
+            "one",
+            "two"
+          ],
+          "config": {
+            "join_with": " "
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "combine",
+          "refs": [
+            "one",
+            "two"
+          ],
+          "config": {
+            "as_list": true
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "combine",
+          "refs": [
+            "one",
+            "two"
+          ],
+          "config": {
+            "as_list": "true"
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "combine",
+          "refs": [
+            "one",
+            "two"
+          ],
+          "config": {
+            "as_list": "yes"
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "combine",
+          "refs": [
+            "one",
+            "two"
+          ],
+          "config": {
+            "as_list": "on"
+          }
+        }
+      },
       {
         "note": "valid, if confusing, to have both join_with and as_list",
-        "instance": {"type": "combine", "refs": ["one", "two"], "config": {"as_list": "true", "join_with": ":"}}
+        "instance": {
+          "type": "combine",
+          "refs": [
+            "one",
+            "two"
+          ],
+          "config": {
+            "as_list": "true",
+            "join_with": ":"
+          }
+        }
       }
     ],
     "invalid": [
-      {"instance": {}, "note": "empty"},
-      {"instance": {"type": "cobine"}, "note": "misspelled"},
-      {"instance": {"type": "combine"}, "note": "no fields or refs"},
-      {"instance": {"type": "combine", "refs": ["one"]}, "note": "only one ref"},
-      {"instance": {"type": "combine", "fields": ["one"]}, "note": "only one field"},
+      {
+        "instance": {},
+        "note": "empty"
+      },
+      {
+        "instance": {
+          "type": "cobine"
+        },
+        "note": "misspelled"
+      },
+      {
+        "instance": {
+          "type": "combine"
+        },
+        "note": "no fields or refs"
+      },
+      {
+        "instance": {
+          "type": "combine",
+          "refs": [
+            "one"
+          ]
+        },
+        "note": "only one ref"
+      },
+      {
+        "instance": {
+          "type": "combine",
+          "fields": [
+            "one"
+          ]
+        },
+        "note": "only one field"
+      },
       {
         "note": "invalid join_with param",
-        "instance": {"type": "combine", "refs": ["one", "two"], "config": {"join_with": true}}
+        "instance": {
+          "type": "combine",
+          "refs": [
+            "one",
+            "two"
+          ],
+          "config": {
+            "join_with": true
+          }
+        }
       },
       {
         "note": "invalid as_list param",
-        "instance": {"type": "combine", "refs": ["one", "two"], "config": {"as_list": "tru"}}
+        "instance": {
+          "type": "combine",
+          "refs": [
+            "one",
+            "two"
+          ],
+          "config": {
+            "as_list": "tru"
+          }
+        }
       }
     ]
   }
diff --git a/tests/data/combine_list.tests.json b/tests/data/combine_list.tests.json
index 2bdc6ad..8c24db0 100644
--- a/tests/data/combine_list.tests.json
+++ b/tests/data/combine_list.tests.json
@@ -1,31 +1,187 @@
 {
   "combine-list.schema.json": {
     "valid": [
-      {"instance": {"type": "combine-list", "refs": [["one", "two"]]}, "note":  "single list is still a list"},
-      {"instance": {"type": "combine-list", "fields": [["one", "two"],["one", "two", "tre"]]}},
-      {"instance": {"type": "combine-list", "refs": [["one", "two"]], "config": {"join_with": " "}}},
-      {"instance": {"type": "combine-list", "refs": [["one", "two"]], "config": {"as_list": true}}},
-      {"instance": {"type": "combine-list", "refs": [["one", "two"]], "config": {"as_list": "true"}}},
-      {"instance": {"type": "combine-list", "refs": [["one", "two"]], "config": {"as_list": "yes"}}},
-      {"instance": {"type": "combine-list", "refs": [["one", "two"]], "config": {"as_list": "on"}}}
+      {
+        "instance": {
+          "type": "combine-list",
+          "refs": [
+            [
+              "one",
+              "two"
+            ]
+          ]
+        },
+        "note": "single list is still a list"
+      },
+      {
+        "instance": {
+          "type": "combine-list",
+          "fields": [
+            [
+              "one",
+              "two"
+            ],
+            [
+              "one",
+              "two",
+              "tre"
+            ]
+          ]
+        }
+      },
+      {
+        "instance": {
+          "type": "combine-list",
+          "refs": [
+            [
+              "one",
+              "two"
+            ]
+          ],
+          "config": {
+            "join_with": " "
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "combine-list",
+          "refs": [
+            [
+              "one",
+              "two"
+            ]
+          ],
+          "config": {
+            "as_list": true
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "combine-list",
+          "refs": [
+            [
+              "one",
+              "two"
+            ]
+          ],
+          "config": {
+            "as_list": "true"
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "combine-list",
+          "refs": [
+            [
+              "one",
+              "two"
+            ]
+          ],
+          "config": {
+            "as_list": "yes"
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "combine-list",
+          "refs": [
+            [
+              "one",
+              "two"
+            ]
+          ],
+          "config": {
+            "as_list": "on"
+          }
+        }
+      }
     ],
     "invalid": [
-      {"instance": {}, "note": "empty"},
-      {"instance": {"type": "combine-lists"}, "note": "misspelled"},
-      {"instance": {"type": "combine-list"}, "note": "no fields or refs"},
-      {"instance": {"type": "combine-list", "refs": [["one"]]}, "note": "only one ref"},
-      {"instance": {"type": "combine-list", "fields": [["one"]]}, "note": "only one field"},
+      {
+        "instance": {},
+        "note": "empty"
+      },
+      {
+        "instance": {
+          "type": "combine-lists"
+        },
+        "note": "misspelled"
+      },
+      {
+        "instance": {
+          "type": "combine-list"
+        },
+        "note": "no fields or refs"
+      },
+      {
+        "instance": {
+          "type": "combine-list",
+          "refs": [
+            [
+              "one"
+            ]
+          ]
+        },
+        "note": "only one ref"
+      },
+      {
+        "instance": {
+          "type": "combine-list",
+          "fields": [
+            [
+              "one"
+            ]
+          ]
+        },
+        "note": "only one field"
+      },
       {
         "note": "invalid join_with param",
-        "instance": {"type": "combine-list", "refs": [["one", "two"]], "config": {"join_with": true}}
+        "instance": {
+          "type": "combine-list",
+          "refs": [
+            [
+              "one",
+              "two"
+            ]
+          ],
+          "config": {
+            "join_with": true
+          }
+        }
       },
       {
         "note": "invalid as_list param",
-        "instance": {"type": "combine-list", "refs": [["one", "two"]], "config": {"as_list": "tru"}}
+        "instance": {
+          "type": "combine-list",
+          "refs": [
+            [
+              "one",
+              "two"
+            ]
+          ],
+          "config": {
+            "as_list": "tru"
+          }
+        }
       },
       {
         "note": "invalid to have both join_with and as_list",
-        "instance": {"type": "combine", "refs": ["one", "two"], "config": {"as_list": "true", "join_with": ":"}}
+        "instance": {
+          "type": "combine",
+          "refs": [
+            "one",
+            "two"
+          ],
+          "config": {
+            "as_list": "true",
+            "join_with": ":"
+          }
+        }
       }
     ]
   }
diff --git a/tests/data/csv.tests.json b/tests/data/csv.tests.json
index aa3d581..c9e1129 100644
--- a/tests/data/csv.tests.json
+++ b/tests/data/csv.tests.json
@@ -1,23 +1,138 @@
 {
   "csv.schema.json": {
     "valid": [
-      {"instance": {"type": "csv", "config": {"datafile": "test.csv"}}, "note": "minimum config 1"},
-      {"instance": {"type": "csv", "config": {"config_ref": "has_the_info", "column": 1}}, "note": "minimum config 2"},
-      {"instance": {"type": "csv", "config": {"datafile": "tabs.csv", "delimiter": "\t", "headers": "true"}}},
-      {"instance": {"type": "csv", "config": {"datafile": "test.csv", "column": "status"}}},
-      {"instance": {"type": "csv", "config": {"datafile": "test.csv", "quotechar": "'"}}},
-      {"instance": {"type": "csv", "config": {"datafile": "test.csv", "sample": true}}},
-      {"instance": {"type": "csv", "config": {"datafile": "test.csv", "sample_rows": true}}},
-      {"instance": {"type": "csv", "config": {"datafile": "test.csv", "count": 2}}},
-      {"instance": {"type": "csv", "config": {"datafile": "test.csv", "count": [1, 1, 2], "join_with": " "}}}
+      {
+        "instance": {
+          "type": "csv",
+          "config": {
+            "datafile": "test.csv"
+          }
+        },
+        "note": "minimum config 1"
+      },
+      {
+        "instance": {
+          "type": "csv",
+          "config": {
+            "config_ref": "has_the_info",
+            "column": 1
+          }
+        },
+        "note": "minimum config 2"
+      },
+      {
+        "instance": {
+          "type": "csv",
+          "config": {
+            "datafile": "tabs.csv",
+            "delimiter": "\t",
+            "headers": "true"
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "csv",
+          "config": {
+            "datafile": "test.csv",
+            "column": "status"
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "csv",
+          "config": {
+            "datafile": "test.csv",
+            "quotechar": "'"
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "csv",
+          "config": {
+            "datafile": "test.csv",
+            "sample": true
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "csv",
+          "config": {
+            "datafile": "test.csv",
+            "sample_rows": true
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "csv",
+          "config": {
+            "datafile": "test.csv",
+            "count": 2
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "csv",
+          "config": {
+            "datafile": "test.csv",
+            "count": [
+              1,
+              1,
+              2
+            ],
+            "join_with": " "
+          }
+        }
+      }
     ],
     "invalid": [
-      {"instance": {}, "note": "empty"},
-      {"instance": {"type": "seesv"}, "note": "misspelled"},
-      {"instance": {"type": "csv"}, "note": "no config"},
-      {"instance": {"type": "csv", "config": {}}, "note": "empty config"},
-      {"instance": {"type": "csv", "config": {"datafile": "test.csv", "headers": "missing"}}, "note": "invalid headers"},
-      {"instance": {"type": "csv", "config": {"config_ref": "pointer"}}, "note": "config ref, no column, gives misleading error"}
+      {
+        "instance": {},
+        "note": "empty"
+      },
+      {
+        "instance": {
+          "type": "seesv"
+        },
+        "note": "misspelled"
+      },
+      {
+        "instance": {
+          "type": "csv"
+        },
+        "note": "no config"
+      },
+      {
+        "instance": {
+          "type": "csv",
+          "config": {}
+        },
+        "note": "empty config"
+      },
+      {
+        "instance": {
+          "type": "csv",
+          "config": {
+            "datafile": "test.csv",
+            "headers": "missing"
+          }
+        },
+        "note": "invalid headers"
+      },
+      {
+        "instance": {
+          "type": "csv",
+          "config": {
+            "config_ref": "pointer"
+          }
+        },
+        "note": "config ref, no column, gives misleading error"
+      }
     ]
   }
 }
\ No newline at end of file
diff --git a/tests/data/date.tests.json b/tests/data/date.tests.json
index a697862..46e48cc 100644
--- a/tests/data/date.tests.json
+++ b/tests/data/date.tests.json
@@ -1,25 +1,119 @@
 {
   "date.schema.json": {
     "valid": [
-      {"instance": {"type": "date"}, "note": "empty date spec is valid"},
-      {"instance": {"type": "date", "config": {"offset": 1}}},
-      {"instance": {"type": "date", "config": {"duration_days": 1}}},
-      {"instance": {"type": "date", "config": {"duration_days": "22"}}},
-      {"instance": {"type": "date", "config": {"duration_days": 1, "offset": 1}}},
-      {"instance": {"type": "date", "config": {"duration_days": 1, "offset": -1}}},
-      {"instance": {"type": "date", "config": {"duration_days": 1, "offset": 1, "start": "15-12-2050"}}},
-      {"instance": {"type": "date", "config": {"duration_days": 1, "start": "15-Dec-2050", "format": "%d-%b-%Y"}}},
-      {"instance": {"type": "date", "config": {"duration_days": 1, "end": "15-Dec-2050"}}},
-      {"instance": {"type": "date", "config": {"duration_days": 1, "offset": 1, "start": "15-12-2050", "end": "15-12-2051" }}},
-      {"instance": {"type": "date", "config": {"duration_days": 1, "start": "15-Dec-2050", "end": "15-Dec-2051", "format": "%d-%b-%Y"}}},
-      {"instance": {"type": "date", "config": {"center_date": "15-Dec-2050"}}},
-      {"instance": {"type": "date", "config": {"center_date": "15-Dec-2050", "stddev_days": 30}}},
-      {"instance": {"type": "date", "config": {"stddev_days": 30}}},
+      {
+        "instance": {
+          "type": "date"
+        },
+        "note": "empty date spec is valid"
+      },
+      {
+        "instance": {
+          "type": "date",
+          "config": {
+            "offset": 1
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "date",
+          "config": {
+            "duration_days": 1
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "date",
+          "config": {
+            "duration_days": "22"
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "date",
+          "config": {
+            "duration_days": 1,
+            "offset": 1
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "date",
+          "config": {
+            "duration_days": 1,
+            "offset": -1
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "date",
+          "config": {
+            "duration_days": 1,
+            "offset": 1,
+            "start": "15-12-2050"
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "date",
+          "config": {
+            "duration_days": 1,
+            "start": "15-Dec-2050",
+            "format": "%d-%b-%Y"
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "date",
+          "config": {
+            "duration_days": 1,
+            "end": "15-Dec-2050"
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "date",
+          "config": {
+            "duration_days": 1,
+            "offset": 1,
+            "start": "15-12-2050",
+            "end": "15-12-2051"
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "date",
+          "config": {
+            "duration_days": 1,
+            "start": "15-Dec-2050",
+            "end": "15-Dec-2051",
+            "format": "%d-%b-%Y"
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "date",
+          "config": {
+            "center_date": "15-Dec-2050"
+          }
+        }
+      },
       {
         "instance": {
           "type": "date",
           "config": {
-            "hours": { "type": "values", "data": { "7": 0.3, "8": 0.2, "9": 0.1, "10": 0.2, "11": 0.2 } }
+            "center_date": "15-Dec-2050",
+            "stddev_days": 30
           }
         }
       },
@@ -27,7 +121,7 @@
         "instance": {
           "type": "date",
           "config": {
-            "hours": {"type": "rand_range", "data": [9, 17]}
+            "stddev_days": 30
           }
         }
       },
@@ -35,47 +129,148 @@
         "instance": {
           "type": "date",
           "config": {
-            "hours": {"type": "distribution", "data": "normal(mean=12, stddev=5, min=6, max=21)"}
+            "hours": {
+              "type": "values",
+              "data": {
+                "7": 0.3,
+                "8": 0.2,
+                "9": 0.1,
+                "10": 0.2,
+                "11": 0.2
+              }
+            }
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "date",
+          "config": {
+            "hours": {
+              "type": "rand_range",
+              "data": [
+                9,
+                17
+              ]
+            }
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "date",
+          "config": {
+            "hours": {
+              "type": "distribution",
+              "data": "normal(mean=12, stddev=5, min=6, max=21)"
+            }
           }
         }
       }
     ],
     "invalid": [
-      {"instance": {}, "note": "empty"},
-      {"instance": {"type": "dates"}, "note": "misspelled"},
-      {"instance": {"type": "date,iso"}, "note": "misspelled"},
-      {"instance": {"type": "date", "config": {"offset": 1.5}}, "note": "only integer offsets supported"},
+      {
+        "instance": {},
+        "note": "empty"
+      },
+      {
+        "instance": {
+          "type": "dates"
+        },
+        "note": "misspelled"
+      },
+      {
+        "instance": {
+          "type": "date,iso"
+        },
+        "note": "misspelled"
+      },
+      {
+        "instance": {
+          "type": "date",
+          "config": {
+            "offset": 1.5
+          }
+        },
+        "note": "only integer offsets supported"
+      },
       {
         "note": "only string formatted start dates supported",
-        "instance": {"type": "date", "config": {"start": 1615477175}}
+        "instance": {
+          "type": "date",
+          "config": {
+            "start": 1615477175
+          }
+        }
       },
       {
         "note": "only string formatted end dates supported",
-        "instance": {"type": "date", "config": {"end": 1615477175}}
+        "instance": {
+          "type": "date",
+          "config": {
+            "end": 1615477175
+          }
+        }
       },
       {
         "note": "cannot supply start and center_date",
-        "instance": {"type": "date", "config": {"start": "15-12-2050", "center_date": "25-12-2050"}}
+        "instance": {
+          "type": "date",
+          "config": {
+            "start": "15-12-2050",
+            "center_date": "25-12-2050"
+          }
+        }
       },
       {
         "note": "cannot supply end and center_date",
-        "instance": {"type": "date", "config": {"center_date": "25-12-2050", "end": "31-12-2050"}}
+        "instance": {
+          "type": "date",
+          "config": {
+            "center_date": "25-12-2050",
+            "end": "31-12-2050"
+          }
+        }
       },
       {
         "note": "cannot supply duration_days and center_date",
-        "instance": {"type": "date", "config": {"center_date": "25-12-2050", "duration_days": 22}}
+        "instance": {
+          "type": "date",
+          "config": {
+            "center_date": "25-12-2050",
+            "duration_days": 22
+          }
+        }
       },
       {
         "note": "cannot supply start and stddev_days",
-        "instance": {"type": "date", "config": {"start": "15-12-2050", "stddev_days": 30}}
+        "instance": {
+          "type": "date",
+          "config": {
+            "start": "15-12-2050",
+            "stddev_days": 30
+          }
+        }
       },
       {
         "note": "cannot supply end and stddev_days",
-        "instance": {"type": "date", "config": {"stddev_days": 30, "end": "31-12-2050"}}
+        "instance": {
+          "type": "date",
+          "config": {
+            "stddev_days": 30,
+            "end": "31-12-2050"
+          }
+        }
       },
       {
         "note": "cannot supply duration_days and stddev_days",
-        "instance": {"type": "date", "config": {"stddev_days": 30, "duration_days": 22}}
+        "instance": {
+          "type": "date",
+          "config": {
+            "stddev_days": 30,
+            "duration_days": 22
+          }
+        }
       }
     ]
   }
diff --git a/tests/data/distribution.tests.json b/tests/data/distribution.tests.json
index d6eda7b..75bf6bf 100644
--- a/tests/data/distribution.tests.json
+++ b/tests/data/distribution.tests.json
@@ -1,23 +1,45 @@
 {
   "distribution.schema.json": {
     "valid": [
-      {"instance": {"type": "distribution", "data": "uniform(start=10, end=25)"}},
-      {"instance": {"type": "distribution", "data": "normal(mean=10, stddev=2.532)"}}
+      {
+        "instance": {
+          "type": "distribution",
+          "data": "uniform(start=10, end=25)"
+        }
+      },
+      {
+        "instance": {
+          "type": "distribution",
+          "data": "normal(mean=10, stddev=2.532)"
+        }
+      }
     ],
     "invalid": [
-      {"instance": {}, "note": "empty"},
-      {"instance": {"type": "distributions"}, "note": "misspelled"},
+      {
+        "instance": {},
+        "note": "empty"
+      },
+      {
+        "instance": {
+          "type": "distributions"
+        },
+        "note": "misspelled"
+      },
       {
         "instance": {
-          "type": "distribution", "data": {"formula": "uniform(start=10, end=25)"}
+          "type": "distribution",
+          "data": {
+            "formula": "uniform(start=10, end=25)"
+          }
         },
-        "note":  "wrong type"
+        "note": "wrong type"
       },
       {
         "instance": {
-          "type": "distribution", "formula": "uniform(start=10, end=25)"
+          "type": "distribution",
+          "formula": "uniform(start=10, end=25)"
         },
-        "note":  "data not specified"
+        "note": "data not specified"
       }
     ]
   }
diff --git a/tests/data/geo.lat.tests.json b/tests/data/geo.lat.tests.json
index dbcd46a..771d95a 100644
--- a/tests/data/geo.lat.tests.json
+++ b/tests/data/geo.lat.tests.json
@@ -1,27 +1,94 @@
 {
   "geo.lat.schema.json": {
     "valid": [
-      {"instance": {"type": "geo.lat"}},
-      {"instance": {"type": "geo.lat", "config": {"start_lat": -89.0}}},
-      {"instance": {"type": "geo.lat", "config": {"end_lat": 89.9}}},
-      {"instance": {"type": "geo.lat", "config": {"start_lat": -90.0, "end_lat": 90.0}}},
-      {"instance": {"type": "geo.lat", "config": {"bbox": [31.33134, 22.03795, 34.19295, 25.00562]}}}
+      {
+        "instance": {
+          "type": "geo.lat"
+        }
+      },
+      {
+        "instance": {
+          "type": "geo.lat",
+          "config": {
+            "start_lat": -89.0
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "geo.lat",
+          "config": {
+            "end_lat": 89.9
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "geo.lat",
+          "config": {
+            "start_lat": -90.0,
+            "end_lat": 90.0
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "geo.lat",
+          "config": {
+            "bbox": [
+              31.33134,
+              22.03795,
+              34.19295,
+              25.00562
+            ]
+          }
+        }
+      }
     ],
     "invalid": [
-      {"instance": {}, "note": "empty"},
-      {"instance": {"type": "geo.lats"}, "note": "misspelled"},
-      {"instance": {"type": "geo-lat"}, "note": "misspelled"},
+      {
+        "instance": {},
+        "note": "empty"
+      },
+      {
+        "instance": {
+          "type": "geo.lats"
+        },
+        "note": "misspelled"
+      },
+      {
+        "instance": {
+          "type": "geo-lat"
+        },
+        "note": "misspelled"
+      },
       {
         "note": "out of range of valid latitude",
-        "instance": {"type": "geo.lat", "config": {"start_lat": -99.0}}
+        "instance": {
+          "type": "geo.lat",
+          "config": {
+            "start_lat": -99.0
+          }
+        }
       },
       {
         "note": "out of range of valid latitude",
-        "instance": {"type": "geo.lat", "config": {"end_lat": 99.9}}
+        "instance": {
+          "type": "geo.lat",
+          "config": {
+            "end_lat": 99.9
+          }
+        }
       },
       {
         "note": "out of range of valid latitude",
-        "instance": {"type": "geo.lat", "config": {"start_lat": -90.1, "end_lat": 90.1  }}
+        "instance": {
+          "type": "geo.lat",
+          "config": {
+            "start_lat": -90.1,
+            "end_lat": 90.1
+          }
+        }
       }
     ]
   }
diff --git a/tests/data/geo.long.tests.json b/tests/data/geo.long.tests.json
index 3b337ad..d3ce3aa 100644
--- a/tests/data/geo.long.tests.json
+++ b/tests/data/geo.long.tests.json
@@ -1,31 +1,108 @@
 {
   "geo.long.schema.json": {
     "valid": [
-      {"instance": {"type": "geo.long"}},
-      {"instance": {"type": "geo.long", "config": {"start_long": -179.0}}},
-      {"instance": {"type": "geo.long", "config": {"end_long": 179.9}}},
-      {"instance": {"type": "geo.long", "config": {"start_long": -180.0, "end_long": 180.0}}},
-      {"instance": {"type": "geo.long", "config": {"bbox": [31.33134, 22.03795, 34.19295, 25.00562]}}}
+      {
+        "instance": {
+          "type": "geo.long"
+        }
+      },
+      {
+        "instance": {
+          "type": "geo.long",
+          "config": {
+            "start_long": -179.0
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "geo.long",
+          "config": {
+            "end_long": 179.9
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "geo.long",
+          "config": {
+            "start_long": -180.0,
+            "end_long": 180.0
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "geo.long",
+          "config": {
+            "bbox": [
+              31.33134,
+              22.03795,
+              34.19295,
+              25.00562
+            ]
+          }
+        }
+      }
     ],
     "invalid": [
-      {"instance": {}, "note": "empty"},
-      {"instance": {"type": "geo.longs"}, "note": "misspelled"},
-      {"instance": {"type": "geo-long"}, "note": "misspelled"},
+      {
+        "instance": {},
+        "note": "empty"
+      },
+      {
+        "instance": {
+          "type": "geo.longs"
+        },
+        "note": "misspelled"
+      },
+      {
+        "instance": {
+          "type": "geo-long"
+        },
+        "note": "misspelled"
+      },
       {
         "note": "out of range of valid longitude",
-        "instance": {"type": "geo.long", "config": {"start_long": -189.0}}
+        "instance": {
+          "type": "geo.long",
+          "config": {
+            "start_long": -189.0
+          }
+        }
       },
       {
         "note": "out of range of valid longitude",
-        "instance": {"type": "geo.long", "config": {"end_long": 189.9}}
+        "instance": {
+          "type": "geo.long",
+          "config": {
+            "end_long": 189.9
+          }
+        }
       },
       {
         "note": "out of range of valid longitude",
-        "instance": {"type": "geo.long", "config": {"start_long": -180.1, "end_long": 180.1}}
+        "instance": {
+          "type": "geo.long",
+          "config": {
+            "start_long": -180.1,
+            "end_long": 180.1
+          }
+        }
       },
       {
         "note": "bounding box has out of range coordinates",
-        "instance": {"type": "geo.long", "config": {"bbox": [31.33134, 99.03795, 34.19295, 25.00562]}}
+        "instance": {
+          "type": "geo.long",
+          "config": {
+            "bbox": [
+              31.33134,
+              99.03795,
+              34.19295,
+              25.00562
+            ]
+          }
+        }
       }
     ]
   }
diff --git a/tests/data/geo.pair.tests.json b/tests/data/geo.pair.tests.json
index 126ddbc..0aa43f9 100644
--- a/tests/data/geo.pair.tests.json
+++ b/tests/data/geo.pair.tests.json
@@ -1,54 +1,204 @@
 {
   "geo.pair.schema.json": {
     "valid": [
-      {"instance": {"type": "geo.pair"}},
-      {"instance": {"type": "geo.pair", "config": {"start_lat": -89.0}}},
-      {"instance": {"type": "geo.pair", "config": {"end_lat": 89.9}}},
-      {"instance": {"type": "geo.pair", "config": {"start_lat": -90.0, "end_lat": 90.0}}},
-      {"instance": {"type": "geo.pair", "config": {"start_long": -179.0}}},
-      {"instance": {"type": "geo.pair", "config": {"end_long": 179.9}}},
-      {"instance": {"type": "geo.pair", "config": {"start_long": -180.0, "end_long": 180.0}}},
-      {"instance": {"type": "geo.pair", "config": {"bbox": [31.33134, 22.03795, 34.19295, 25.00562]}}},
-      {"instance": {"type": "geo.pair", "config": {"lat_first": true}}},
-      {"instance": {"type": "geo.pair", "config": {"as_list": "true"}}},
-      {"instance": {"type": "geo.pair", "config": {"join_with": ":"}}},
+      {
+        "instance": {
+          "type": "geo.pair"
+        }
+      },
+      {
+        "instance": {
+          "type": "geo.pair",
+          "config": {
+            "start_lat": -89.0
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "geo.pair",
+          "config": {
+            "end_lat": 89.9
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "geo.pair",
+          "config": {
+            "start_lat": -90.0,
+            "end_lat": 90.0
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "geo.pair",
+          "config": {
+            "start_long": -179.0
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "geo.pair",
+          "config": {
+            "end_long": 179.9
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "geo.pair",
+          "config": {
+            "start_long": -180.0,
+            "end_long": 180.0
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "geo.pair",
+          "config": {
+            "bbox": [
+              31.33134,
+              22.03795,
+              34.19295,
+              25.00562
+            ]
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "geo.pair",
+          "config": {
+            "lat_first": true
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "geo.pair",
+          "config": {
+            "as_list": "true"
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "geo.pair",
+          "config": {
+            "join_with": ":"
+          }
+        }
+      },
       {
         "note": "valid, if confusing, to have both join_with and as_list",
-        "instance": {"type": "geo.pair", "config": {"as_list": "true", "join_with": ":"}}
+        "instance": {
+          "type": "geo.pair",
+          "config": {
+            "as_list": "true",
+            "join_with": ":"
+          }
+        }
       }
     ],
     "invalid": [
-      {"instance": {}, "note": "empty"},
-      {"instance": {"type": "geo.pairs"}, "note": "misspelled"},
-      {"instance": {"type": "geo-pair"}, "note": "misspelled"},
-      {"instance": {"type": "geo.pair", "config": {"lat_first": "tru"}}, "note": "invalid lat_first param"},
+      {
+        "instance": {},
+        "note": "empty"
+      },
+      {
+        "instance": {
+          "type": "geo.pairs"
+        },
+        "note": "misspelled"
+      },
+      {
+        "instance": {
+          "type": "geo-pair"
+        },
+        "note": "misspelled"
+      },
+      {
+        "instance": {
+          "type": "geo.pair",
+          "config": {
+            "lat_first": "tru"
+          }
+        },
+        "note": "invalid lat_first param"
+      },
       {
         "note": "out of range of valid latitude",
-        "instance": {"type": "geo.pair", "config": {"start_lat": -99.0}}
+        "instance": {
+          "type": "geo.pair",
+          "config": {
+            "start_lat": -99.0
+          }
+        }
       },
       {
         "note": "out of range of valid latitude",
-        "instance": {"type": "geo.pair", "config": {"end_lat": 99.9}}
+        "instance": {
+          "type": "geo.pair",
+          "config": {
+            "end_lat": 99.9
+          }
+        }
       },
       {
         "note": "out of range of valid latitude",
-        "instance": {"type": "geo.pair", "config": {"start_lat": -90.1, "end_lat": 90.1  }}
+        "instance": {
+          "type": "geo.pair",
+          "config": {
+            "start_lat": -90.1,
+            "end_lat": 90.1
+          }
+        }
       },
       {
         "note": "out of range of valid longitude",
-        "instance": {"type": "geo.pair", "config": {"start_long": -189.0}}
+        "instance": {
+          "type": "geo.pair",
+          "config": {
+            "start_long": -189.0
+          }
+        }
       },
       {
         "note": "out of range of valid longitude",
-        "instance": {"type": "geo.pair", "config": {"end_long": 189.9}}
+        "instance": {
+          "type": "geo.pair",
+          "config": {
+            "end_long": 189.9
+          }
+        }
       },
       {
         "note": "out of range of valid longitude",
-        "instance": {"type": "geo.pair", "config": {"start_long": -180.1, "end_long": 180.1}}
+        "instance": {
+          "type": "geo.pair",
+          "config": {
+            "start_long": -180.1,
+            "end_long": 180.1
+          }
+        }
       },
       {
         "note": "bounding box has out of range coordinates",
-        "instance": {"type": "geo.pair", "config": {"bbox": [31.33134, 99.03795, 34.19295, 25.00562]}}
+        "instance": {
+          "type": "geo.pair",
+          "config": {
+            "bbox": [
+              31.33134,
+              99.03795,
+              34.19295,
+              25.00562
+            ]
+          }
+        }
       }
     ]
   }
diff --git a/tests/data/ip.precise.tests.json b/tests/data/ip.precise.tests.json
index caa25af..0f0eb19 100644
--- a/tests/data/ip.precise.tests.json
+++ b/tests/data/ip.precise.tests.json
@@ -1,12 +1,32 @@
 {
   "ip.precise.schema.json": {
     "valid": [
-      {"instance": {"type": "ip.precise", "config": {"cidr": "192.168.0.0/22"}}}
+      {
+        "instance": {
+          "type": "ip.precise",
+          "config": {
+            "cidr": "192.168.0.0/22"
+          }
+        }
+      }
     ],
     "invalid": [
-      {"instance": {}, "note": "empty"},
-      {"instance": {"type": "ip.precises"}, "note": "misspelled"},
-      {"instance": {"type": "ip.precise"}, "note": "no fields or refs or data or stuff?"}
+      {
+        "instance": {},
+        "note": "empty"
+      },
+      {
+        "instance": {
+          "type": "ip.precises"
+        },
+        "note": "misspelled"
+      },
+      {
+        "instance": {
+          "type": "ip.precise"
+        },
+        "note": "no fields or refs or data or stuff?"
+      }
     ]
   }
 }
\ No newline at end of file
diff --git a/tests/data/ip.tests.json b/tests/data/ip.tests.json
index 2d661da..62a78d2 100644
--- a/tests/data/ip.tests.json
+++ b/tests/data/ip.tests.json
@@ -1,25 +1,124 @@
 {
   "ip.schema.json": {
     "valid": [
-      {"instance": {"type": "ip"}, "note": "empty spec is valid"},
-      {"instance": {"type": "ip", "config": {"base": "10"}}},
-      {"instance": {"type": "ip", "config": {"base": "10.20"}}},
-      {"instance": {"type": "ip", "config": {"base": "10.20.30"}}},
-      {"instance": {"type": "ip", "config": {"base": "10.20.30.40"}}},
-      {"instance": {"type": "ipv4", "config": {"base": "10."}}, "note": "trailing dot is fine"},
-      {"instance": {"type": "ipv4", "config": {"base": "10.20."}}, "note": "trailing dot is fine"},
-      {"instance": {"type": "ipv4", "config": {"base": "10.20.30."}}, "note": "trailing dot is fine"},
-      {"instance": {"type": "ip", "config": {"cidr": "10.20.30.0/8"}}},
-      {"instance": {"type": "ip", "config": {"cidr": "10.20.30.0/16"}}},
-      {"instance": {"type": "ip", "config": {"cidr": "10.20.30.0/24"}}}
+      {
+        "instance": {
+          "type": "ip"
+        },
+        "note": "empty spec is valid"
+      },
+      {
+        "instance": {
+          "type": "ip",
+          "config": {
+            "base": "10"
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "ip",
+          "config": {
+            "base": "10.20"
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "ip",
+          "config": {
+            "base": "10.20.30"
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "ip",
+          "config": {
+            "base": "10.20.30.40"
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "ipv4",
+          "config": {
+            "base": "10."
+          }
+        },
+        "note": "trailing dot is fine"
+      },
+      {
+        "instance": {
+          "type": "ipv4",
+          "config": {
+            "base": "10.20."
+          }
+        },
+        "note": "trailing dot is fine"
+      },
+      {
+        "instance": {
+          "type": "ipv4",
+          "config": {
+            "base": "10.20.30."
+          }
+        },
+        "note": "trailing dot is fine"
+      },
+      {
+        "instance": {
+          "type": "ip",
+          "config": {
+            "cidr": "10.20.30.0/8"
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "ip",
+          "config": {
+            "cidr": "10.20.30.0/16"
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "ip",
+          "config": {
+            "cidr": "10.20.30.0/24"
+          }
+        }
+      }
     ],
     "invalid": [
-      {"instance": {}, "note": "empty"},
-      {"instance": {"type": "ips"}, "note": "misspelled"},
-      {"instance": {"type": "ip", "config": {"base": "10.20.30.40.50"}}, "note": "invalid base"},
+      {
+        "instance": {},
+        "note": "empty"
+      },
+      {
+        "instance": {
+          "type": "ips"
+        },
+        "note": "misspelled"
+      },
+      {
+        "instance": {
+          "type": "ip",
+          "config": {
+            "base": "10.20.30.40.50"
+          }
+        },
+        "note": "invalid base"
+      },
       {
         "note": "cidr range not supported, only /8 /16 /24",
-        "instance": {"type": "ip", "config": {"cidr": "10.20.30.0/12"}}
+        "instance": {
+          "type": "ip",
+          "config": {
+            "cidr": "10.20.30.0/12"
+          }
+        }
       }
     ]
   }
diff --git a/tests/data/nested.tests.json b/tests/data/nested.tests.json
index efb580f..8d2186e 100644
--- a/tests/data/nested.tests.json
+++ b/tests/data/nested.tests.json
@@ -1,42 +1,175 @@
 {
   "nested.schema.json": {
     "valid": [
-      {"instance": {"type": "nested", "fields": {"one": 1}}},
-      {"instance": {"type": "nested", "fields": {"one": 1, "two": [2, 4]}}},
-      {"instance": {"type": "nested", "fields": {"one": 1, "two": [2, 4]}, "field_groups": [["one", "two"]]}},
-      {"instance": {"type": "nested", "fields": {"one": 1, "two": [2, 4]}, "field_groups": [["one"],["one", "two"]]}},
       {
         "instance": {
-          "type": "nested", "fields": {"one": 1, "two": [2, 4]}, "field_groups": {"0.4": ["one"], "0.6": ["one", "two"]}
+          "type": "nested",
+          "fields": {
+            "one": 1
+          }
         }
       },
       {
         "instance": {
-          "type": "nested", "fields": {"one": 1, "two": [2, 4]}, "field_groups": {"a": ["one"], "b": ["one", "two"]}
+          "type": "nested",
+          "fields": {
+            "one": 1,
+            "two": [
+              2,
+              4
+            ]
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "nested",
+          "fields": {
+            "one": 1,
+            "two": [
+              2,
+              4
+            ]
+          },
+          "field_groups": [
+            [
+              "one",
+              "two"
+            ]
+          ]
+        }
+      },
+      {
+        "instance": {
+          "type": "nested",
+          "fields": {
+            "one": 1,
+            "two": [
+              2,
+              4
+            ]
+          },
+          "field_groups": [
+            [
+              "one"
+            ],
+            [
+              "one",
+              "two"
+            ]
+          ]
+        }
+      },
+      {
+        "instance": {
+          "type": "nested",
+          "fields": {
+            "one": 1,
+            "two": [
+              2,
+              4
+            ]
+          },
+          "field_groups": {
+            "0.4": [
+              "one"
+            ],
+            "0.6": [
+              "one",
+              "two"
+            ]
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "nested",
+          "fields": {
+            "one": 1,
+            "two": [
+              2,
+              4
+            ]
+          },
+          "field_groups": {
+            "a": [
+              "one"
+            ],
+            "b": [
+              "one",
+              "two"
+            ]
+          }
         }
       }
     ],
     "invalid": [
-      {"instance": {}, "note": "empty"},
-      {"instance": {"type": "nesteds"}, "note": "misspelled"},
-      {"instance": {"type": "nested"}, "note":  "no fields"},
+      {
+        "instance": {},
+        "note": "empty"
+      },
+      {
+        "instance": {
+          "type": "nesteds"
+        },
+        "note": "misspelled"
+      },
+      {
+        "instance": {
+          "type": "nested"
+        },
+        "note": "no fields"
+      },
       {
         "instance": {
-          "type": "nested", "fields": {"one": 1, "two": [2, 4]}, "field_groups": ["one", "two"]
+          "type": "nested",
+          "fields": {
+            "one": 1,
+            "two": [
+              2,
+              4
+            ]
+          },
+          "field_groups": [
+            "one",
+            "two"
+          ]
         },
-        "note":  "Lists is not List of Lists"
+        "note": "Lists is not List of Lists"
       },
       {
         "instance": {
-          "type": "nested", "fields": {"one": 1, "two": [2, 4]}, "field_groups": { "0.4": "one", "0.6": "two"}
+          "type": "nested",
+          "fields": {
+            "one": 1,
+            "two": [
+              2,
+              4
+            ]
+          },
+          "field_groups": {
+            "0.4": "one",
+            "0.6": "two"
+          }
         },
-        "note":  "Values of weighted should be lists"
+        "note": "Values of weighted should be lists"
       },
       {
         "instance": {
-          "type": "nested","fields": {"one": 1, "two": [2, 4]}, "field_groups": { "a": "one", "b": "two"}
+          "type": "nested",
+          "fields": {
+            "one": 1,
+            "two": [
+              2,
+              4
+            ]
+          },
+          "field_groups": {
+            "a": "one",
+            "b": "two"
+          }
         },
-        "note":  "Values of named should be lists"
+        "note": "Values of named should be lists"
       }
     ]
   }
diff --git a/tests/data/range.tests.json b/tests/data/range.tests.json
index a2592cd..c026214 100644
--- a/tests/data/range.tests.json
+++ b/tests/data/range.tests.json
@@ -1,60 +1,377 @@
 {
   "range.schema.json": {
     "valid": [
-      {"instance": {"type": "range", "data": [10]}, "note": "upper bound only"},
-      {"instance": {"type": "range", "data": [0, 10]}},
-      {"instance": {"type": "range", "data": [0, 10, 2]}, "note": "start stop step"},
-      {"instance": {"type": "range", "data": [[0, 10]]}, "note": "single nested"},
-      {"instance": {"type": "range", "data": [[0, 10, 2]]}, "note":  "single nested start stop step"},
+      {
+        "instance": {
+          "type": "range",
+          "data": [
+            10
+          ]
+        },
+        "note": "upper bound only"
+      },
+      {
+        "instance": {
+          "type": "range",
+          "data": [
+            0,
+            10
+          ]
+        }
+      },
+      {
+        "instance": {
+          "type": "range",
+          "data": [
+            0,
+            10,
+            2
+          ]
+        },
+        "note": "start stop step"
+      },
+      {
+        "instance": {
+          "type": "range",
+          "data": [
+            [
+              0,
+              10
+            ]
+          ]
+        },
+        "note": "single nested"
+      },
+      {
+        "instance": {
+          "type": "range",
+          "data": [
+            [
+              0,
+              10,
+              2
+            ]
+          ]
+        },
+        "note": "single nested start stop step"
+      },
       {
         "note": "multi sized nested",
-        "instance": {"type": "range", "data": [[10], [10, 100, 2], [100, 1000, 100]]}
+        "instance": {
+          "type": "range",
+          "data": [
+            [
+              10
+            ],
+            [
+              10,
+              100,
+              2
+            ],
+            [
+              100,
+              1000,
+              100
+            ]
+          ]
+        }
+      },
+      {
+        "instance": {
+          "type": "rand_range",
+          "data": [
+            10
+          ]
+        },
+        "note": "upper bound only"
+      },
+      {
+        "instance": {
+          "type": "rand_range",
+          "data": [
+            0,
+            10
+          ]
+        }
+      },
+      {
+        "instance": {
+          "type": "rand_range",
+          "data": [
+            0,
+            10,
+            2
+          ]
+        },
+        "note": "start stop step"
+      },
+      {
+        "instance": {
+          "type": "rand_range",
+          "data": [
+            [
+              0,
+              10
+            ]
+          ]
+        },
+        "note": "single nested"
+      },
+      {
+        "instance": {
+          "type": "rand_range",
+          "data": [
+            [
+              0,
+              10,
+              2
+            ]
+          ]
+        },
+        "note": "single nested start stop step"
       },
-      {"instance": {"type": "rand_range", "data": [10]}, "note": "upper bound only"},
-      {"instance": {"type": "rand_range", "data": [0, 10]}},
-      {"instance": {"type": "rand_range", "data": [0, 10, 2]}, "note": "start stop step"},
-      {"instance": {"type": "rand_range", "data": [[0, 10]]}, "note": "single nested"},
-      {"instance": {"type": "rand_range", "data": [[0, 10, 2]]}, "note":  "single nested start stop step"},
       {
         "note": "multi sized nested",
-        "instance": {"type": "rand_range", "data": [[10], [10, 100, 2], [100, 1000, 100]]}
+        "instance": {
+          "type": "rand_range",
+          "data": [
+            [
+              10
+            ],
+            [
+              10,
+              100,
+              2
+            ],
+            [
+              100,
+              1000,
+              100
+            ]
+          ]
+        }
+      },
+      {
+        "instance": {
+          "type": "rand_int_range",
+          "data": [
+            10
+          ]
+        },
+        "note": "upper bound only"
+      },
+      {
+        "instance": {
+          "type": "rand_int_range",
+          "data": [
+            0,
+            10
+          ]
+        }
+      },
+      {
+        "instance": {
+          "type": "rand_int_range",
+          "data": [
+            0,
+            10,
+            2
+          ]
+        },
+        "note": "start stop step"
+      },
+      {
+        "instance": {
+          "type": "rand_int_range",
+          "data": [
+            [
+              0,
+              10
+            ]
+          ]
+        },
+        "note": "single nested"
+      },
+      {
+        "instance": {
+          "type": "rand_int_range",
+          "data": [
+            [
+              0,
+              10,
+              2
+            ]
+          ]
+        },
+        "note": "single nested start stop step"
       },
-      {"instance": {"type": "rand_int_range", "data": [10]}, "note": "upper bound only"},
-      {"instance": {"type": "rand_int_range", "data": [0, 10]}},
-      {"instance": {"type": "rand_int_range", "data": [0, 10, 2]}, "note": "start stop step"},
-      {"instance": {"type": "rand_int_range", "data": [[0, 10]]}, "note": "single nested"},
-      {"instance": {"type": "rand_int_range", "data": [[0, 10, 2]]}, "note":  "single nested start stop step"},
       {
         "note": "multi sized nested",
-        "instance": {"type": "rand_int_range", "data": [[10], [10, 100, 2], [100, 1000, 100]]}
+        "instance": {
+          "type": "rand_int_range",
+          "data": [
+            [
+              10
+            ],
+            [
+              10,
+              100,
+              2
+            ],
+            [
+              100,
+              1000,
+              100
+            ]
+          ]
+        }
       }
     ],
     "invalid": [
-      {"instance": {}, "note": "empty"},
-      {"instance": {"type": "ranges"}, "note": "misspelled"},
-      {"instance": {"type": "range"}, "note": "no data"},
-      {"instance": {"type": "range", "data": []}, "note": "empty data"},
-      {"instance": {"type": "range", "data": [1, 10, 2, 4]}, "note": "too many entries"},
-      {"instance": {"type": "range", "data": {}}, "note": "wrong data type"},
+      {
+        "instance": {},
+        "note": "empty"
+      },
+      {
+        "instance": {
+          "type": "ranges"
+        },
+        "note": "misspelled"
+      },
+      {
+        "instance": {
+          "type": "range"
+        },
+        "note": "no data"
+      },
+      {
+        "instance": {
+          "type": "range",
+          "data": []
+        },
+        "note": "empty data"
+      },
+      {
+        "instance": {
+          "type": "range",
+          "data": [
+            1,
+            10,
+            2,
+            4
+          ]
+        },
+        "note": "too many entries"
+      },
+      {
+        "instance": {
+          "type": "range",
+          "data": {}
+        },
+        "note": "wrong data type"
+      },
       {
         "note": "invalid count type,",
-        "instance": {"type": "range", "data": [0, 10], "config": {"count": true}}
+        "instance": {
+          "type": "range",
+          "data": [
+            0,
+            10
+          ],
+          "config": {
+            "count": true
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "rand_range"
+        },
+        "note": "no data"
+      },
+      {
+        "instance": {
+          "type": "rand_range",
+          "data": []
+        },
+        "note": "empty data"
+      },
+      {
+        "instance": {
+          "type": "rand_range",
+          "data": [
+            1,
+            10,
+            2,
+            4
+          ]
+        },
+        "note": "too many entries"
+      },
+      {
+        "instance": {
+          "type": "rand_range",
+          "data": {}
+        },
+        "note": "wrong data type"
       },
-      {"instance": {"type": "rand_range"}, "note": "no data"},
-      {"instance": {"type": "rand_range", "data": []}, "note": "empty data"},
-      {"instance": {"type": "rand_range", "data": [1, 10, 2, 4]}, "note": "too many entries"},
-      {"instance": {"type": "rand_range", "data": {}}, "note": "wrong data type"},
       {
         "note": "invalid count type,",
-        "instance": {"type": "rand_range", "data": [0, 10], "config": {"count": "blue"}}
+        "instance": {
+          "type": "rand_range",
+          "data": [
+            0,
+            10
+          ],
+          "config": {
+            "count": "blue"
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "rand_int_range"
+        },
+        "note": "no data"
+      },
+      {
+        "instance": {
+          "type": "rand_int_range",
+          "data": []
+        },
+        "note": "empty data"
+      },
+      {
+        "instance": {
+          "type": "rand_int_range",
+          "data": [
+            1,
+            10,
+            2,
+            4
+          ]
+        },
+        "note": "too many entries"
+      },
+      {
+        "instance": {
+          "type": "rand_int_range",
+          "data": {}
+        },
+        "note": "wrong data type"
       },
-      {"instance": {"type": "rand_int_range"}, "note": "no data"},
-      {"instance": {"type": "rand_int_range", "data": []}, "note": "empty data"},
-      {"instance": {"type": "rand_int_range", "data": [1, 10, 2, 4]}, "note": "too many entries"},
-      {"instance": {"type": "rand_int_range", "data": {}}, "note": "wrong data type"},
       {
         "note": "invalid count type,",
-        "instance": {"type": "rand_int_range", "data": [0, 10], "config": {"count": "blue"}}
+        "instance": {
+          "type": "rand_int_range",
+          "data": [
+            0,
+            10
+          ],
+          "config": {
+            "count": "blue"
+          }
+        }
       }
     ]
   }
diff --git a/tests/data/ref.tests.json b/tests/data/ref.tests.json
index b9fae8b..87f3ccf 100644
--- a/tests/data/ref.tests.json
+++ b/tests/data/ref.tests.json
@@ -1,13 +1,36 @@
 {
   "ref.schema.json": {
     "valid": [
-      {"instance": {"type": "ref", "data": "ref_name"}},
-      {"instance": {"type": "ref", "ref": "ref_name"}}
+      {
+        "instance": {
+          "type": "ref",
+          "data": "ref_name"
+        }
+      },
+      {
+        "instance": {
+          "type": "ref",
+          "ref": "ref_name"
+        }
+      }
     ],
     "invalid": [
-      {"instance": {}, "note": "empty"},
-      {"instance": {"type": "refs"}, "note": "misspelled"},
-      {"instance": {"type": "ref"}, "note":  "no data or ref supplied"}
+      {
+        "instance": {},
+        "note": "empty"
+      },
+      {
+        "instance": {
+          "type": "refs"
+        },
+        "note": "misspelled"
+      },
+      {
+        "instance": {
+          "type": "ref"
+        },
+        "note": "no data or ref supplied"
+      }
     ]
   }
 }
\ No newline at end of file
diff --git a/tests/data/select_list_subset.tests.json b/tests/data/select_list_subset.tests.json
index 6da06f8..9a212e7 100644
--- a/tests/data/select_list_subset.tests.json
+++ b/tests/data/select_list_subset.tests.json
@@ -1,43 +1,173 @@
 {
   "select_list_subset.schema.json": {
     "valid": [
-      {"instance": {"type": "select_list_subset", "data": ["one"]}},
-      {"instance": {"type": "select_list_subset", "data": ["one", "two"]}},
-      {"instance": {"type": "select_list_subset", "data": ["one", "two", "tre"]}},
-      {"instance": {"type": "select_list_subset", "ref": "has_the_list"}},
+      {
+        "instance": {
+          "type": "select_list_subset",
+          "data": [
+            "one"
+          ]
+        }
+      },
+      {
+        "instance": {
+          "type": "select_list_subset",
+          "data": [
+            "one",
+            "two"
+          ]
+        }
+      },
+      {
+        "instance": {
+          "type": "select_list_subset",
+          "data": [
+            "one",
+            "two",
+            "tre"
+          ]
+        }
+      },
+      {
+        "instance": {
+          "type": "select_list_subset",
+          "ref": "has_the_list"
+        }
+      },
       {
         "note": "count by itself is valid",
-        "instance": {"type": "select_list_subset", "data": ["a", "b", "c"], "config": {"count": 5}}
+        "instance": {
+          "type": "select_list_subset",
+          "data": [
+            "a",
+            "b",
+            "c"
+          ],
+          "config": {
+            "count": 5
+          }
+        }
       },
       {
-        "instance": {"type": "select_list_subset", "data": ["a", "b", "c"], "config": {"mean": 2, "min": 1, "max": 3}}
+        "instance": {
+          "type": "select_list_subset",
+          "data": [
+            "a",
+            "b",
+            "c"
+          ],
+          "config": {
+            "mean": 2,
+            "min": 1,
+            "max": 3
+          }
+        }
       },
       {
         "note": "mean by itself is valid",
-        "instance": {"type": "select_list_subset", "data": ["a", "b", "c"], "config": {"mean": 2}}
+        "instance": {
+          "type": "select_list_subset",
+          "data": [
+            "a",
+            "b",
+            "c"
+          ],
+          "config": {
+            "mean": 2
+          }
+        }
       },
       {
         "note": "mean with stddev is valid",
-        "instance": {"type": "select_list_subset", "data": ["a", "b", "c"], "config": {"mean": 2, "stddev":  1}}
+        "instance": {
+          "type": "select_list_subset",
+          "data": [
+            "a",
+            "b",
+            "c"
+          ],
+          "config": {
+            "mean": 2,
+            "stddev": 1
+          }
+        }
       }
     ],
     "invalid": [
-      {"instance": {}, "note": "empty"},
-      {"instance": {"type": "select_list_subsets"}, "note": "misspelled"},
-      {"instance": {"type": "select_list_subset"}, "note": "no data element defined"},
-      {"instance": {"type": "select_list_subset", "data": 42}, "note": "data not a list"},
-      {"instance": {"type": "select_list_subset", "data": "foobar"}, "note": "data not a list"},
+      {
+        "instance": {},
+        "note": "empty"
+      },
+      {
+        "instance": {
+          "type": "select_list_subsets"
+        },
+        "note": "misspelled"
+      },
+      {
+        "instance": {
+          "type": "select_list_subset"
+        },
+        "note": "no data element defined"
+      },
+      {
+        "instance": {
+          "type": "select_list_subset",
+          "data": 42
+        },
+        "note": "data not a list"
+      },
+      {
+        "instance": {
+          "type": "select_list_subset",
+          "data": "foobar"
+        },
+        "note": "data not a list"
+      },
       {
         "note": "count and min together is invalid",
-        "instance": {"type": "select_list_subset", "data": ["a", "b", "c"], "config": {"count": 5, "min": 3}}
+        "instance": {
+          "type": "select_list_subset",
+          "data": [
+            "a",
+            "b",
+            "c"
+          ],
+          "config": {
+            "count": 5,
+            "min": 3
+          }
+        }
       },
       {
         "note": "count and max together is invalid",
-        "instance": {"type": "select_list_subset", "data": ["a", "b", "c"], "config": {"count": 5, "max": 5}}
+        "instance": {
+          "type": "select_list_subset",
+          "data": [
+            "a",
+            "b",
+            "c"
+          ],
+          "config": {
+            "count": 5,
+            "max": 5
+          }
+        }
       },
       {
         "note": "count and mean together is invalid",
-        "instance": {"type": "select_list_subset", "data": ["a", "b", "c"], "config": {"count": 5, "mean": 5}}
+        "instance": {
+          "type": "select_list_subset",
+          "data": [
+            "a",
+            "b",
+            "c"
+          ],
+          "config": {
+            "count": 5,
+            "mean": 5
+          }
+        }
       }
     ]
   }
diff --git a/tests/data/spec.json b/tests/data/spec.json
index 2f21d17..d329b40 100644
--- a/tests/data/spec.json
+++ b/tests/data/spec.json
@@ -1,6 +1,12 @@
 {
   "field_name": {
     "type": "values",
-    "data": [1, 2, 3, 4, 5]
+    "data": [
+      1,
+      2,
+      3,
+      4,
+      5
+    ]
   }
 }
\ No newline at end of file
diff --git a/tests/data/templated.tests.json b/tests/data/templated.tests.json
index 746732a..3c32d9a 100644
--- a/tests/data/templated.tests.json
+++ b/tests/data/templated.tests.json
@@ -1,18 +1,80 @@
 {
   "templated.schema.json": {
     "valid": [
-      {"instance": {"type": "templated", "data": "{{ one }} and {{ two }}", "refs": ["one", "two"]}},
-      {"instance": {"type": "templated", "data": "{{ one }} and {{ two }}", "fields": ["one", "two"]}}
+      {
+        "instance": {
+          "type": "templated",
+          "data": "{{ one }} and {{ two }}",
+          "refs": [
+            "one",
+            "two"
+          ]
+        }
+      },
+      {
+        "instance": {
+          "type": "templated",
+          "data": "{{ one }} and {{ two }}",
+          "fields": [
+            "one",
+            "two"
+          ]
+        }
+      }
     ],
     "invalid": [
-      {"instance": {}, "note": "empty"},
-      {"instance": {"type": "templateds"}, "note": "misspelled"},
-      {"instance": {"type": "templated"}, "note": "no fields or refs"},
-      {"instance": {"type": "templated", "data": "{{ one }} and {{ two }}", "refs": []}, "note":  "empty refs"},
-      {"instance": {"type": "templated", "data": "{{ one }} and {{ two }}", "refs": [""]}, "note":  "empty string ref"},
-      {"instance": {"type": "templated", "data": "{{ one }} and {{ two }}", "fields": []}, "note":  "empty fields"},
-      {"instance": {
-        "type": "templated", "data": "{{ one }} and {{ two }}", "fields": [""]}, "note":  "empty string field"}
+      {
+        "instance": {},
+        "note": "empty"
+      },
+      {
+        "instance": {
+          "type": "templateds"
+        },
+        "note": "misspelled"
+      },
+      {
+        "instance": {
+          "type": "templated"
+        },
+        "note": "no fields or refs"
+      },
+      {
+        "instance": {
+          "type": "templated",
+          "data": "{{ one }} and {{ two }}",
+          "refs": []
+        },
+        "note": "empty refs"
+      },
+      {
+        "instance": {
+          "type": "templated",
+          "data": "{{ one }} and {{ two }}",
+          "refs": [
+            ""
+          ]
+        },
+        "note": "empty string ref"
+      },
+      {
+        "instance": {
+          "type": "templated",
+          "data": "{{ one }} and {{ two }}",
+          "fields": []
+        },
+        "note": "empty fields"
+      },
+      {
+        "instance": {
+          "type": "templated",
+          "data": "{{ one }} and {{ two }}",
+          "fields": [
+            ""
+          ]
+        },
+        "note": "empty string field"
+      }
     ]
   }
 }
\ No newline at end of file
diff --git a/tests/data/unicode_range.tests.json b/tests/data/unicode_range.tests.json
index 441c430..b9f29c1 100644
--- a/tests/data/unicode_range.tests.json
+++ b/tests/data/unicode_range.tests.json
@@ -1,53 +1,179 @@
 {
   "unicode_range.schema.json": {
     "valid": [
-      {"instance": {"type": "unicode_range", "data": [1024, 1279]}, "note": "hex 0400 to 04ff as integer"},
-      {"instance": {"type": "unicode_range", "data": ["0400", "04ff"]}, "note": "hex 0400 to 04ff string no leading 0x"},
-      {"instance": {"type": "unicode_range", "data": ["0x0400", "0x04ff"]}, "note": "hex 0400 to 04ff with leading 0x"},
+      {
+        "instance": {
+          "type": "unicode_range",
+          "data": [
+            1024,
+            1279
+          ]
+        },
+        "note": "hex 0400 to 04ff as integer"
+      },
+      {
+        "instance": {
+          "type": "unicode_range",
+          "data": [
+            "0400",
+            "04ff"
+          ]
+        },
+        "note": "hex 0400 to 04ff string no leading 0x"
+      },
+      {
+        "instance": {
+          "type": "unicode_range",
+          "data": [
+            "0x0400",
+            "0x04ff"
+          ]
+        },
+        "note": "hex 0400 to 04ff with leading 0x"
+      },
       {
         "note": "count by itself is valid",
-        "instance": {"type": "unicode_range", "data": ["0400", "04ff"], "config": {"count": 5}}
+        "instance": {
+          "type": "unicode_range",
+          "data": [
+            "0400",
+            "04ff"
+          ],
+          "config": {
+            "count": 5
+          }
+        }
       },
       {
         "note": "min and max alone are valid",
-        "instance": {"type": "unicode_range", "data": ["0400", "04ff"], "config": {"min": 3, "max": 5}}
+        "instance": {
+          "type": "unicode_range",
+          "data": [
+            "0400",
+            "04ff"
+          ],
+          "config": {
+            "min": 3,
+            "max": 5
+          }
+        }
       },
       {
         "note": "mean by itself is valid",
-        "instance": {"type": "unicode_range", "data": ["0400", "04ff"], "config": {"mean": 5}}
+        "instance": {
+          "type": "unicode_range",
+          "data": [
+            "0400",
+            "04ff"
+          ],
+          "config": {
+            "mean": 5
+          }
+        }
       },
       {
         "note": "mean with stddev is valid",
-        "instance": {"type": "unicode_range", "data": ["0400", "04ff"], "config": {"mean": 5, "stddev":  1}}
+        "instance": {
+          "type": "unicode_range",
+          "data": [
+            "0400",
+            "04ff"
+          ],
+          "config": {
+            "mean": 5,
+            "stddev": 1
+          }
+        }
       }
     ],
     "invalid": [
-      {"instance": {}, "note": "empty"},
-      {"instance": {"type": "unicode_ranges"}, "note": "misspelled"},
-      {"instance": {"type": "unicode_range"}, "note": "no fields or refs or data or stuff?"},
+      {
+        "instance": {},
+        "note": "empty"
+      },
+      {
+        "instance": {
+          "type": "unicode_ranges"
+        },
+        "note": "misspelled"
+      },
+      {
+        "instance": {
+          "type": "unicode_range"
+        },
+        "note": "no fields or refs or data or stuff?"
+      },
       {
         "note": "to few characters",
-        "instance": {"type": "char_class", "data": ["400", "4ff"]}
+        "instance": {
+          "type": "char_class",
+          "data": [
+            "400",
+            "4ff"
+          ]
+        }
       },
       {
         "note": "to many characters",
-        "instance": {"type": "char_class", "data": ["40000", "4fff"]}
+        "instance": {
+          "type": "char_class",
+          "data": [
+            "40000",
+            "4fff"
+          ]
+        }
       },
       {
         "note": "to many characters",
-        "instance": {"type": "char_class", "data": ["0400", "4ffff"]}
+        "instance": {
+          "type": "char_class",
+          "data": [
+            "0400",
+            "4ffff"
+          ]
+        }
       },
       {
         "note": "count and min together is invalid",
-        "instance": {"type": "char_class", "data": ["0400", "04ff"], "config": {"count": 5, "min": 3}}
+        "instance": {
+          "type": "char_class",
+          "data": [
+            "0400",
+            "04ff"
+          ],
+          "config": {
+            "count": 5,
+            "min": 3
+          }
+        }
       },
       {
         "note": "count and max together is invalid",
-        "instance": {"type": "char_class", "data": ["0400", "04ff"], "config": {"count": 5, "max": 5}}
+        "instance": {
+          "type": "char_class",
+          "data": [
+            "0400",
+            "04ff"
+          ],
+          "config": {
+            "count": 5,
+            "max": 5
+          }
+        }
       },
       {
         "note": "count and mean together is invalid",
-        "instance": {"type": "char_class", "data": ["0400", "04ff"], "config": {"count": 5, "mean": 5}}
+        "instance": {
+          "type": "char_class",
+          "data": [
+            "0400",
+            "04ff"
+          ],
+          "config": {
+            "count": 5,
+            "mean": 5
+          }
+        }
       }
     ]
   }
diff --git a/tests/data/uuid.tests.json b/tests/data/uuid.tests.json
index bf3189b..f3559e5 100644
--- a/tests/data/uuid.tests.json
+++ b/tests/data/uuid.tests.json
@@ -1,17 +1,73 @@
 {
   "uuid.schema.json": {
     "valid": [
-      {"instance": {"type": "uuid"}},
-      {"instance": {"type": "uuid", "config": {"variant": 1}}},
-      {"instance": {"type": "uuid", "config": {"variant": 3}}},
-      {"instance": {"type": "uuid", "config": {"variant": 4}}},
-      {"instance": {"type": "uuid", "config": {"variant": 5}}}
+      {
+        "instance": {
+          "type": "uuid"
+        }
+      },
+      {
+        "instance": {
+          "type": "uuid",
+          "config": {
+            "variant": 1
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "uuid",
+          "config": {
+            "variant": 3
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "uuid",
+          "config": {
+            "variant": 4
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "uuid",
+          "config": {
+            "variant": 5
+          }
+        }
+      }
     ],
     "invalid": [
-      {"instance": {}, "note": "empty"},
-      {"instance": {"type": "uuids"}, "note": "misspelled"},
-      {"instance": {"type": "uuid", "config": {"variant": 2}}, "note":  "invalid variant"},
-      {"instance": {"type": "uuid", "config": {"variant": 6}}, "note":  "invalid variant"}
+      {
+        "instance": {},
+        "note": "empty"
+      },
+      {
+        "instance": {
+          "type": "uuids"
+        },
+        "note": "misspelled"
+      },
+      {
+        "instance": {
+          "type": "uuid",
+          "config": {
+            "variant": 2
+          }
+        },
+        "note": "invalid variant"
+      },
+      {
+        "instance": {
+          "type": "uuid",
+          "config": {
+            "variant": 6
+          }
+        },
+        "note": "invalid variant"
+      }
     ]
   }
 }
\ No newline at end of file
diff --git a/tests/data/values.tests.json b/tests/data/values.tests.json
index c650e32..35fc236 100644
--- a/tests/data/values.tests.json
+++ b/tests/data/values.tests.json
@@ -1,45 +1,199 @@
 {
   "values.schema.json": {
     "valid": [
-      {"instance": {"type": "values", "data": "constant"}},
+      {
+        "instance": {
+          "type": "values",
+          "data": "constant"
+        }
+      },
       {
         "note": "blank string still a constant",
-        "instance": {"type": "values", "data": ""}
-      },
-      {"instance": {"type": "values", "data": 1}},
-      {"instance": {"type": "values", "data": 1.5}},
-      {"instance": {"type": "values", "data": true}},
-      {"instance": {"type": "values", "data": ["one", "two"]}},
-      {"instance": {"type": "values", "data": {"one": 0.4, "two": 0.5}}},
-      {"instance": {"type": "values", "data": ["one", "two"], "config": {"count": 2}}},
-      {"instance": {"type": "values", "data": ["one", "two"], "config": {"count": [1, 2]}}},
-      {"instance": {"type": "values", "data": ["one", "two"], "config": {"count": {"1": 0.7, "2": 0.3}}}}
+        "instance": {
+          "type": "values",
+          "data": ""
+        }
+      },
+      {
+        "instance": {
+          "type": "values",
+          "data": 1
+        }
+      },
+      {
+        "instance": {
+          "type": "values",
+          "data": 1.5
+        }
+      },
+      {
+        "instance": {
+          "type": "values",
+          "data": true
+        }
+      },
+      {
+        "instance": {
+          "type": "values",
+          "data": [
+            "one",
+            "two"
+          ]
+        }
+      },
+      {
+        "instance": {
+          "type": "values",
+          "data": {
+            "one": 0.4,
+            "two": 0.5
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "values",
+          "data": [
+            "one",
+            "two"
+          ],
+          "config": {
+            "count": 2
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "values",
+          "data": [
+            "one",
+            "two"
+          ],
+          "config": {
+            "count": [
+              1,
+              2
+            ]
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "values",
+          "data": [
+            "one",
+            "two"
+          ],
+          "config": {
+            "count": {
+              "1": 0.7,
+              "2": 0.3
+            }
+          }
+        }
+      }
     ],
     "invalid": [
-      {"instance": {}, "note": "empty"},
-      {"instance": {"type": "value"}, "note": "misspelled"},
-      {"instance": {"type": "values"}, "note": "no fields or refs"},
-      {"instance": {"type": "values", "data": []}, "note": "empty data"},
-      {"instance": {"type": "values", "data": {}}, "note": "empty data,"},
+      {
+        "instance": {},
+        "note": "empty"
+      },
+      {
+        "instance": {
+          "type": "value"
+        },
+        "note": "misspelled"
+      },
+      {
+        "instance": {
+          "type": "values"
+        },
+        "note": "no fields or refs"
+      },
+      {
+        "instance": {
+          "type": "values",
+          "data": []
+        },
+        "note": "empty data"
+      },
+      {
+        "instance": {
+          "type": "values",
+          "data": {}
+        },
+        "note": "empty data,"
+      },
       {
         "note": "invalid count type,",
-        "instance": {"type": "values", "data": ["one", "two"], "config": {"count": true}}
+        "instance": {
+          "type": "values",
+          "data": [
+            "one",
+            "two"
+          ],
+          "config": {
+            "count": true
+          }
+        }
       },
       {
         "note": "empty count type,",
-        "instance": {"type": "values", "data": ["one", "two"], "config": {"count": []}}
+        "instance": {
+          "type": "values",
+          "data": [
+            "one",
+            "two"
+          ],
+          "config": {
+            "count": []
+          }
+        }
       },
       {
         "note": "counts not integers",
-        "instance": {"type": "values", "data": ["one", "two"], "config": {"count": [1.1, 1.7]}}
+        "instance": {
+          "type": "values",
+          "data": [
+            "one",
+            "two"
+          ],
+          "config": {
+            "count": [
+              1.1,
+              1.7
+            ]
+          }
+        }
       },
       {
         "note": "empty count type",
-        "instance": {"type": "values", "data": ["one", "two"], "config": {"count": {}}}
+        "instance": {
+          "type": "values",
+          "data": [
+            "one",
+            "two"
+          ],
+          "config": {
+            "count": {}
+          }
+        }
       },
       {
         "note": "bad count keys",
-        "instance": {"type": "values", "data": ["one", "two"], "config": {"count": {"one": 0.7, "two": 0.3}}}
+        "instance": {
+          "type": "values",
+          "data": [
+            "one",
+            "two"
+          ],
+          "config": {
+            "count": {
+              "one": 0.7,
+              "two": 0.3
+            }
+          }
+        }
       }
     ]
   }
diff --git a/tests/data/weighted_csv.tests.json b/tests/data/weighted_csv.tests.json
index a641259..7a04dda 100644
--- a/tests/data/weighted_csv.tests.json
+++ b/tests/data/weighted_csv.tests.json
@@ -1,21 +1,121 @@
 {
   "weighted_csv.schema.json": {
     "valid": [
-      {"instance": {"type": "weighted_csv", "config": {"datafile": "test.csv"}}, "note": "minimum config 1"},
-      {"instance": {"type": "weighted_csv", "config": {"config_ref": "has_the_info", "column": 1}}, "note": "minimum config 2"},
-      {"instance": {"type": "weighted_csv", "config": {"datafile": "tabs.csv", "delimiter": "\t", "headers": "true"}}},
-      {"instance": {"type": "weighted_csv", "config": {"datafile": "test.csv", "column": "status", "weight_column": "weight"}}},
-      {"instance": {"type": "weighted_csv", "config": {"datafile": "test.csv", "quotechar": "'"}}},
-      {"instance": {"type": "weighted_csv", "config": {"datafile": "test.csv", "count": 2}}},
-      {"instance": {"type": "weighted_csv", "config": {"datafile": "test.csv", "count": [1, 1, 2], "join_with": " "}}}
+      {
+        "instance": {
+          "type": "weighted_csv",
+          "config": {
+            "datafile": "test.csv"
+          }
+        },
+        "note": "minimum config 1"
+      },
+      {
+        "instance": {
+          "type": "weighted_csv",
+          "config": {
+            "config_ref": "has_the_info",
+            "column": 1
+          }
+        },
+        "note": "minimum config 2"
+      },
+      {
+        "instance": {
+          "type": "weighted_csv",
+          "config": {
+            "datafile": "tabs.csv",
+            "delimiter": "\t",
+            "headers": "true"
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "weighted_csv",
+          "config": {
+            "datafile": "test.csv",
+            "column": "status",
+            "weight_column": "weight"
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "weighted_csv",
+          "config": {
+            "datafile": "test.csv",
+            "quotechar": "'"
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "weighted_csv",
+          "config": {
+            "datafile": "test.csv",
+            "count": 2
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "weighted_csv",
+          "config": {
+            "datafile": "test.csv",
+            "count": [
+              1,
+              1,
+              2
+            ],
+            "join_with": " "
+          }
+        }
+      }
     ],
     "invalid": [
-      {"instance": {}, "note": "empty"},
-      {"instance": {"type": "wayted_csv"}, "note": "misspelled"},
-      {"instance": {"type": "weighted_csv"}, "note": "no config"},
-      {"instance": {"type": "weighted_csv", "config": {}}, "note": "empty config"},
-      {"instance": {"type": "weighted_csv", "config": {"datafile": "test.csv", "headers": "missing"}}, "note": "invalid headers"},
-      {"instance": {"type": "weighted_csv", "config": {"config_ref": "pointer"}}, "note": "config ref, no column, gives misleading error"}
+      {
+        "instance": {},
+        "note": "empty"
+      },
+      {
+        "instance": {
+          "type": "wayted_csv"
+        },
+        "note": "misspelled"
+      },
+      {
+        "instance": {
+          "type": "weighted_csv"
+        },
+        "note": "no config"
+      },
+      {
+        "instance": {
+          "type": "weighted_csv",
+          "config": {}
+        },
+        "note": "empty config"
+      },
+      {
+        "instance": {
+          "type": "weighted_csv",
+          "config": {
+            "datafile": "test.csv",
+            "headers": "missing"
+          }
+        },
+        "note": "invalid headers"
+      },
+      {
+        "instance": {
+          "type": "weighted_csv",
+          "config": {
+            "config_ref": "pointer"
+          }
+        },
+        "note": "config ref, no column, gives misleading error"
+      }
     ]
   }
 }
\ No newline at end of file
diff --git a/tests/data/weighted_ref.tests.json b/tests/data/weighted_ref.tests.json
index 0201d16..fbc3958 100644
--- a/tests/data/weighted_ref.tests.json
+++ b/tests/data/weighted_ref.tests.json
@@ -1,17 +1,76 @@
 {
   "weighted_ref.schema.json": {
     "valid": [
-      {"instance": {"type": "weighted_ref", "data": {"one": 1.0}}},
-      {"instance": {"type": "weighted_ref", "data": {"one": 0.5, "two":  0.5}}},
-      {"instance": {"type": "weighted_ref", "data": {"one": 0.33, "two":  0.33, "tre":  0.33}}}
+      {
+        "instance": {
+          "type": "weighted_ref",
+          "data": {
+            "one": 1.0
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "weighted_ref",
+          "data": {
+            "one": 0.5,
+            "two": 0.5
+          }
+        }
+      },
+      {
+        "instance": {
+          "type": "weighted_ref",
+          "data": {
+            "one": 0.33,
+            "two": 0.33,
+            "tre": 0.33
+          }
+        }
+      }
     ],
     "invalid": [
-      {"instance": {}, "note": "empty"},
-      {"instance": {"type": "weighted_refs"}, "note": "misspelled"},
-      {"instance": {"type": "weighted_ref"}, "note":  "no data supplied"},
-      {"instance": {"type": "weighted_ref", "data": {}}, "note":  "data is empty"},
-      {"instance": {"type": "weighted_ref", "data": {"foo":  "bar"}}, "note":  "value is not number"},
-      {"instance": {"type": "weighted_ref", "data": ["one"]}, "note":  "wrong type for data"}
+      {
+        "instance": {},
+        "note": "empty"
+      },
+      {
+        "instance": {
+          "type": "weighted_refs"
+        },
+        "note": "misspelled"
+      },
+      {
+        "instance": {
+          "type": "weighted_ref"
+        },
+        "note": "no data supplied"
+      },
+      {
+        "instance": {
+          "type": "weighted_ref",
+          "data": {}
+        },
+        "note": "data is empty"
+      },
+      {
+        "instance": {
+          "type": "weighted_ref",
+          "data": {
+            "foo": "bar"
+          }
+        },
+        "note": "value is not number"
+      },
+      {
+        "instance": {
+          "type": "weighted_ref",
+          "data": [
+            "one"
+          ]
+        },
+        "note": "wrong type for data"
+      }
     ]
   }
 }
\ No newline at end of file
diff --git a/tests/test_buffering.py b/tests/test_buffering.py
index fc8787b..c625b96 100644
--- a/tests/test_buffering.py
+++ b/tests/test_buffering.py
@@ -71,4 +71,3 @@ def test_buffered_supplier_from_spec():
     for i in range(10):
         value = supplier.next(i)
         assert value == supplier.next(i)
-
diff --git a/tests/test_builder.py b/tests/test_builder.py
index 2ee0afc..6f4b54c 100644
--- a/tests/test_builder.py
+++ b/tests/test_builder.py
@@ -1,4 +1,5 @@
 import json
+
 import pytest
 
 import datacraft
diff --git a/tests/test_calculate.py b/tests/test_calculate.py
index cef3083..fbfd71a 100644
--- a/tests/test_calculate.py
+++ b/tests/test_calculate.py
@@ -1,8 +1,7 @@
 import pytest
+
 import datacraft
 import datacraft.supplier.calculate
-# to trigger registration
-from datacraft import cli
 
 simple_calc_data = [
     (
@@ -59,12 +58,12 @@ def test_keywords_are_now_valid(keyword):
 
 
 missing_required_invalid_inputs = [
-    (None,               None,             '{{ a }} + 2'),
+    (None, None, '{{ a }} + 2'),
     ({'not_used': 'a'}, {'not_used': 'a'}, '{{ a }} + 2'),
-    (None,              {'not_used': 'a'}, None),
-    ({'not_used': 'a'}, None,              None),
-    ({},                None,              '{{ a }} + 2'),
-    (None,              ['not_used', 'a'], '{{ a }} + 2')
+    (None, {'not_used': 'a'}, None),
+    ({'not_used': 'a'}, None, None),
+    ({}, None, '{{ a }} + 2'),
+    (None, ['not_used', 'a'], '{{ a }} + 2')
 ]
 
 
diff --git a/tests/test_casters.py b/tests/test_casters.py
index 2a66219..a04f2c8 100644
--- a/tests/test_casters.py
+++ b/tests/test_casters.py
@@ -1,4 +1,5 @@
 import pytest
+
 from datacraft import SpecException
 from datacraft.casters import from_config
 
@@ -82,9 +83,9 @@ def test_hex_cast_value_error():
 
     (space_aBcD_space, 'lower', ' abcd '),
     (space_aBcD_space, 'upper', ' ABCD '),
-    (space_aBcD_space, 'trim',  'aBcD'),
-    (space_aBcD_space, 'l;t',   'abcd'),
-    (space_aBcD_space, 'u;t',   'ABCD'),
+    (space_aBcD_space, 'trim', 'aBcD'),
+    (space_aBcD_space, 'l;t', 'abcd'),
+    (space_aBcD_space, 'u;t', 'ABCD'),
     (space_aBcD_space, 'trim;lower', 'abcd'),
     (space_aBcD_space, 'trim;str;upper;lower', 'abcd'),
 
diff --git a/tests/test_char_class.py b/tests/test_char_class.py
index 534613f..daceb81 100644
--- a/tests/test_char_class.py
+++ b/tests/test_char_class.py
@@ -1,9 +1,10 @@
 import string
+
 import pytest
-from datacraft import builder, field_loader, SpecException, SupplierException
-# to trigger registration
-from datacraft import cli
-from datacraft import _registered_types
+
+import datacraft
+from datacraft import builder, field_loader, SpecException
+from datacraft._registered_types.char_class import _CLASS_MAPPING
 
 
 def test_char_class_no_data_element():
@@ -44,7 +45,7 @@ def test_char_class_printable():
 
 
 def test_char_class_abbreviations():
-    abbreviations = ['cc-' + key for key in _registered_types._CLASS_MAPPING.keys()]
+    abbreviations = ['cc-' + key for key in _CLASS_MAPPING.keys()]
 
     for abbreviation in abbreviations:
         spec = _cc_abbrev_spec(abbrev=abbreviation, count=7)
@@ -64,6 +65,14 @@ def test_char_class_multiple_classes():
         assert char in string.ascii_lowercase or char in string.digits
 
 
+def test_verify_class_mappings():
+    for key, values in _CLASS_MAPPING.items():
+        spec = _cc_abbrev_spec(f"cc-{key}", count=5)
+        single_item = datacraft.entries(spec, 1)[0]['name']
+        for c in single_item:
+            assert c in values, f'{c} not expected to be part of {key} type. values: {values}'
+
+
 def _verify_values(supplier, min_size, max_size, exclude='', iterations=100):
     for i in range(iterations):
         value = supplier.next(i)
@@ -82,3 +91,4 @@ def _cc_abbrev_spec(abbrev, **config):
     return builder.spec_builder() \
         .add_field("name", builder.char_class_abbrev(cc_abbrev=abbrev, **config)) \
         .build()
+
diff --git a/tests/test_combine.py b/tests/test_combine.py
index 02decc9..110f899 100644
--- a/tests/test_combine.py
+++ b/tests/test_combine.py
@@ -1,7 +1,8 @@
 import pytest
-from datacraft.loader import field_loader
+
 import datacraft.suppliers as suppliers
 from datacraft import builder, SpecException
+from datacraft.loader import field_loader
 
 
 def test_neither_refs_nor_fields_specified():
diff --git a/tests/test_csv.py b/tests/test_csv.py
index 115c178..a1d6361 100644
--- a/tests/test_csv.py
+++ b/tests/test_csv.py
@@ -1,10 +1,9 @@
 import os
+
 import pytest
 
 import datacraft
 import datacraft.supplier.csv
-# to trigger registration
-from datacraft import cli
 
 test_dir = os.sep.join([os.path.dirname(os.path.realpath(__file__)), 'data'])
 
diff --git a/tests/test_date.py b/tests/test_date.py
index d77b881..ea6f9b1 100644
--- a/tests/test_date.py
+++ b/tests/test_date.py
@@ -3,10 +3,8 @@
 import pytest
 
 import datacraft
-from datacraft.loader import field_loader
 from datacraft import builder
-# to trigger registration
-from datacraft import cli
+from datacraft.loader import field_loader
 
 
 def test_basic_spec():
diff --git a/tests/test_distributions.py b/tests/test_distributions.py
index 73e9afa..bca2b75 100644
--- a/tests/test_distributions.py
+++ b/tests/test_distributions.py
@@ -1,6 +1,6 @@
 import pytest
+
 import datacraft
-from datacraft import distributions
 
 
 @pytest.fixture()
@@ -52,14 +52,14 @@ def test_from_string_normal(string_func, min_values_generated):
 
 
 invalid_funcs = [
-    "normal(5, 2)",               # no names
+    "normal(5, 2)",  # no names
     "noormal(mean=5, stddev=2)",  # invalid spelling of distribution
     "normal(means=5, stddev=2)",  # invalid spelling of param
-    "",                           # empty string not valid
-    " ",                          # white space not valid
-    "()",                         # open close parens not valid
-    "normal()",                   # no args
-    "uniform(start=20, end=30",   # missing closing paren
+    "",  # empty string not valid
+    " ",  # white space not valid
+    "()",  # open close parens not valid
+    "normal()",  # no args
+    "uniform(start=20, end=30",  # missing closing paren
     "uniform)start=20, end=30(",  # inverted parens
     "gauss(mean=33, stddev=5, maximum=50)"  # maximum is not a valid extra arg
     "gauss(mean=33, stddev=5, )"  # extra comma
diff --git a/tests/test_generate.py b/tests/test_generate.py
index ae78c03..a723342 100644
--- a/tests/test_generate.py
+++ b/tests/test_generate.py
@@ -1,11 +1,12 @@
 import os
 from pathlib import Path
+
 import datacraft
 
 
 def test_generator_string_template():
     names = ['bob', 'bobby', 'robert', 'bobo']
-    spec =datacraft. builder.single_field('name', datacraft. builder.values(names)).build()
+    spec = datacraft.builder.single_field('name', datacraft.builder.values(names)).build()
 
     template = 'Name: {{ name }}'
     processor = datacraft.outputs.processor(template=template)
@@ -35,7 +36,7 @@ def test_generator_path_template():
 
 def test_generator_no_template():
     names = ['bob', 'bobby', 'robert', 'bobo']
-    spec =datacraft. builder.single_field('name', datacraft. builder.values(names)).build()
+    spec = datacraft.builder.single_field('name', datacraft.builder.values(names)).build()
 
     gen = spec.generator(
         iterations=4,
@@ -46,7 +47,7 @@ def test_generator_no_template():
 
 
 def build_spec(data):
-    spec_builder =datacraft. builder.spec_builder()
+    spec_builder = datacraft.builder.spec_builder()
     for key, value in data.items():
         spec_builder.add_field(key, value)
     return spec_builder.build()
diff --git a/tests/test_geo.py b/tests/test_geo.py
index 1391694..ab0eaac 100644
--- a/tests/test_geo.py
+++ b/tests/test_geo.py
@@ -4,8 +4,6 @@
 
 import datacraft
 from datacraft import builder, field_loader
-# to trigger registration
-from datacraft import cli
 
 
 def test_geo_lat_default_precision():
@@ -104,7 +102,7 @@ def test_geo_pair_as_list():
     end_long = 60.0
 
     spec = _geo_pair_spec(bbox=[start_long, start_lat, end_long, end_lat], as_list=True)
-    
+
     supplier = field_loader(spec).get('pair')
     value = supplier.next(0)
     assert isinstance(value, list)
diff --git a/tests/test_loader.py b/tests/test_loader.py
index c8ea5fb..521818e 100644
--- a/tests/test_loader.py
+++ b/tests/test_loader.py
@@ -1,10 +1,9 @@
-import datacraft
-from datacraft import builder
-
 from collections import Counter
+
 import pytest
-# to trigger registration
-from datacraft import cli
+
+import datacraft
+from datacraft import builder
 
 spec = datacraft.builder.spec_builder() \
     .add_field('foo', builder.combine(['ONE', 'TWO'], join_with='')) \
@@ -101,6 +100,7 @@ def test_registered_invalid_preprocessor():
     @datacraft.registry.preprocessors('test_invalid')
     def _do_stuff(_, __):
         return None
+
     raw_spec = {
         'foo': {'type': 'values', 'data': 42},
         # this one doesn't exist, so should get logged as such
diff --git a/tests/test_main.py b/tests/test_main.py
index 8620b62..d50dc82 100644
--- a/tests/test_main.py
+++ b/tests/test_main.py
@@ -1,8 +1,8 @@
 import os
 
+import catalogue
 import pytest
 
-import catalogue
 import datacraft
 from datacraft import __main__ as dgmain
 
@@ -72,6 +72,30 @@ def test_parse_debug_defaults(tmpdir):
     assert os.path.exists(os.path.join(tmpdir, 'dataspec_defaults.json'))
 
 
+def test_type_list(tmpdir):
+    args = ['--type-list', '-o', str(tmpdir)]
+    dgmain.main(args)
+    assert os.path.exists(os.path.join(tmpdir, 'type_list.json'))
+
+
+def test_type_help_no_filter(tmpdir):
+    args = ['--type-help', '-o', str(tmpdir)]
+    dgmain.main(args)
+    assert os.path.exists(os.path.join(tmpdir, 'type-help.txt'))
+
+
+def test_type_help_with_filter(tmpdir):
+    args = ['--type-help', 'calculate', 'sample', '-o', str(tmpdir)]
+    dgmain.main(args)
+    assert os.path.exists(os.path.join(tmpdir, 'type-help.txt'))
+
+
+def test_parse_debug_defaults(tmpdir):
+    args = ['--debug-defaults', '-o', str(tmpdir)]
+    dgmain.main(args)
+    assert os.path.exists(os.path.join(tmpdir, 'dataspec_defaults.json'))
+
+
 def test_parse_apply_raw(tmpdir):
     args = ['-o', str(tmpdir),
             '--apply-raw',
diff --git a/tests/test_nested.py b/tests/test_nested.py
index f434168..ea21e95 100644
--- a/tests/test_nested.py
+++ b/tests/test_nested.py
@@ -2,8 +2,6 @@
 
 import datacraft
 from datacraft import builder, field_loader
-# to trigger registration
-from datacraft import cli
 
 
 def test_single_nested():
diff --git a/tests/test_network.py b/tests/test_network.py
index 29236d5..95ce501 100644
--- a/tests/test_network.py
+++ b/tests/test_network.py
@@ -1,8 +1,7 @@
 import pytest
-from datacraft.loader import field_loader
+
 from datacraft import builder, SpecException
-# to trigger registration
-from datacraft import cli
+from datacraft.loader import field_loader
 
 
 def test_ip_v4_spec():
@@ -147,7 +146,7 @@ def test_mac_address_dashes():
     spec_builder = builder.spec_builder()
     spec_builder.mac('mac', dashes='true')
     spec = spec_builder.build()
-    value = next(spec.generator(1,enforce_schema=True))['mac']
+    value = next(spec.generator(1, enforce_schema=True))['mac']
     assert len(value) == 17
     assert '-' in value, f'No dashes in: {value}'
 
diff --git a/tests/test_preprocessor.py b/tests/test_preprocessor.py
index 534cf40..858db72 100644
--- a/tests/test_preprocessor.py
+++ b/tests/test_preprocessor.py
@@ -115,7 +115,8 @@ def test_preprocess_valid_specs(input_spec, expected_output_spec):
             "six": {"type": "csv", "config": {"column": 6, "config_ref": "placeholder_config_ref"}},
             "another": {"type": "range", "data": [1, 10]},
             "refs": {
-                "placeholder_config_ref": {"type": "config_ref", "config": {"datafile": "not_real.csv", "headers": "no"}}
+                "placeholder_config_ref": {"type": "config_ref",
+                                           "config": {"datafile": "not_real.csv", "headers": "no"}}
             }
         }
     ),
@@ -132,33 +133,33 @@ def test_preprocess_valid_specs(input_spec, expected_output_spec):
             }
         },
         {
-          "field": {"type": "ref", "data": "CSV_ONE"},
-          "refs": {
-            "ONE": {
-              "type": "values",
-              "data": "Test Conflict"
-            },
-            "CSV_CONFIG_REF": {
-              "type": "config_ref",
-              "config": {
-                "datafile": "{{ injected }}"
-              }
-            },
-            "ONE-1": {
-              "type": "csv",
-              "config": {
-                "column": 1,
-                "config_ref": "CSV_CONFIG_REF"
-              }
-            },
-            "TWO": {
-              "type": "csv",
-              "config": {
-                "column": 2,
-                "config_ref": "CSV_CONFIG_REF"
-              }
+            "field": {"type": "ref", "data": "CSV_ONE"},
+            "refs": {
+                "ONE": {
+                    "type": "values",
+                    "data": "Test Conflict"
+                },
+                "CSV_CONFIG_REF": {
+                    "type": "config_ref",
+                    "config": {
+                        "datafile": "{{ injected }}"
+                    }
+                },
+                "ONE-1": {
+                    "type": "csv",
+                    "config": {
+                        "column": 1,
+                        "config_ref": "CSV_CONFIG_REF"
+                    }
+                },
+                "TWO": {
+                    "type": "csv",
+                    "config": {
+                        "column": 2,
+                        "config_ref": "CSV_CONFIG_REF"
+                    }
+                }
             }
-          }
         }
     ),
     (
@@ -266,7 +267,7 @@ def _builder_nested(outer_key, inner_key, inner_spec):
         },
         {
             "refs": {"placeholder_config_ref": {"type": "config_ref",
-                                               "config": {"datafile": "not_real.csv", "headers": "no"}}},
+                                                "config": {"datafile": "not_real.csv", "headers": "no"}}},
             "outer": {
                 "type": "nested",
                 "fields": {
diff --git a/tests/test_random_range.py b/tests/test_random_range.py
index 16daa64..80fc40f 100644
--- a/tests/test_random_range.py
+++ b/tests/test_random_range.py
@@ -1,5 +1,7 @@
 import decimal
+
 import pytest
+
 import datacraft
 
 
diff --git a/tests/test_range_supplier.py b/tests/test_range_supplier.py
index b9261f5..1e98b7b 100644
--- a/tests/test_range_supplier.py
+++ b/tests/test_range_supplier.py
@@ -1,8 +1,4 @@
-import pytest
-
-from datacraft import builder, field_loader, SpecException
-# to trigger registration
-from datacraft import cli
+from datacraft import builder, field_loader
 
 
 def test_rand_range():
@@ -65,4 +61,3 @@ def test_range_wrap_around():
 
     vals = [supplier.next(i) for i in range(4)]
     assert vals == [1, 2, 3, 1]
-
diff --git a/tests/test_refs.py b/tests/test_refs.py
index e01954f..f9ef43d 100644
--- a/tests/test_refs.py
+++ b/tests/test_refs.py
@@ -1,7 +1,6 @@
 import pytest
+
 import datacraft
-# to trigger registration
-from datacraft import cli
 
 
 def test_ref_with_ref_name():
@@ -34,4 +33,4 @@ def test_config_ref_in_refs():
     spec_builder.refs().config_ref('test', key1='value1', key2='value2')
     spec = spec_builder.build()
     assert 'refs' in spec
-    assert spec['refs'].get('test') == {'type': 'config_ref', 'config': {'key1': 'value1', 'key2': 'value2'}}
\ No newline at end of file
+    assert spec['refs'].get('test') == {'type': 'config_ref', 'config': {'key1': 'value1', 'key2': 'value2'}}
diff --git a/tests/test_registry.py b/tests/test_registry.py
index 6d6d9be..3e2c02a 100644
--- a/tests/test_registry.py
+++ b/tests/test_registry.py
@@ -1,10 +1,11 @@
 import os
+
 import pytest
+
 import datacraft
-from datacraft.loader import field_loader
 import datacraft.registries as registries
+from datacraft.loader import field_loader
 from datacraft.utils import load_custom_code
-from datacraft import casters
 
 spec = {
     'foo': {
diff --git a/tests/test_schemas.py b/tests/test_schemas.py
index d77ee72..e86acc7 100644
--- a/tests/test_schemas.py
+++ b/tests/test_schemas.py
@@ -1,8 +1,9 @@
 import pytest
-from datacraft import ResourceError, SpecException
-from datacraft.schemas import load, validate_schema_for_spec
-from datacraft import cli
+
 import datacraft.registries as types
+from datacraft import ResourceError, SpecException
+from datacraft._registered_types.schemas import load
+from datacraft.schemas import validate_schema_for_spec
 
 
 def test_load_unknown_key():
diff --git a/tests/test_schemas_data.py b/tests/test_schemas_data.py
index 1763e9d..f6e29ce 100644
--- a/tests/test_schemas_data.py
+++ b/tests/test_schemas_data.py
@@ -1,7 +1,8 @@
-import os
 import json
-import pytest
 import logging
+import os
+
+import pytest
 from jsonschema import validate
 from jsonschema.exceptions import ValidationError
 
@@ -15,7 +16,7 @@
 MESSAGE = "note"
 
 root_dir = os.path.dirname(os.path.realpath(__file__))
-schema_dir = os.path.realpath(os.sep.join([root_dir, '..', 'datacraft', 'schema']))
+schema_dir = os.path.realpath(os.sep.join([root_dir, '..', 'datacraft', '_registered_types', 'schema']))
 tests_dir = os.sep.join([root_dir, 'data'])
 
 TEST_FILES = [
diff --git a/tests/test_select_list.py b/tests/test_select_list.py
index 8d31291..548c071 100644
--- a/tests/test_select_list.py
+++ b/tests/test_select_list.py
@@ -1,8 +1,7 @@
 import pytest
-from datacraft.loader import field_loader
+
 from datacraft import builder, suppliers, SpecException
-# to trigger registration
-from datacraft import cli
+from datacraft.loader import field_loader
 
 
 def test_invalid_when_no_config():
diff --git a/tests/test_server.py b/tests/test_server.py
index 63a9d71..4cac81b 100644
--- a/tests/test_server.py
+++ b/tests/test_server.py
@@ -1,5 +1,7 @@
 import json
+
 import pytest
+
 import datacraft
 
 
diff --git a/tests/test_template_engine.py b/tests/test_template_engine.py
index 2a7fa0b..739946a 100644
--- a/tests/test_template_engine.py
+++ b/tests/test_template_engine.py
@@ -1,5 +1,6 @@
 import os
 from pathlib import Path
+
 import datacraft.template_engines as engines
 
 
diff --git a/tests/test_unicode_range.py b/tests/test_unicode_range.py
index d8c86bd..f3474a2 100644
--- a/tests/test_unicode_range.py
+++ b/tests/test_unicode_range.py
@@ -1,8 +1,6 @@
-import string
 import pytest
+
 from datacraft import builder, field_loader, SpecException
-# to trigger registration
-from datacraft import cli
 
 
 def test_unicode_no_data_element():
diff --git a/tests/test_usage.py b/tests/test_usage.py
new file mode 100644
index 0000000..91bdbbb
--- /dev/null
+++ b/tests/test_usage.py
@@ -0,0 +1,42 @@
+import datacraft
+import datacraft.usage
+
+USAGE_MESSAGE = 'Usage For Test Only!!!'
+
+DEMO_FOR_TEST = 'demo-for-test'
+DEMO_FOR_TEST_NO_USAGE_DEFINED = 'demo-for-test-no-usage'
+
+
+@datacraft.registry.types(DEMO_FOR_TEST)
+def _test_supplier(_, __):
+    return None
+
+
+@datacraft.registry.usage(DEMO_FOR_TEST)
+def _test_usage():
+    return USAGE_MESSAGE
+
+
+@datacraft.registry.types(DEMO_FOR_TEST_NO_USAGE_DEFINED)
+def _test_supplier_no_usage(_, __):
+    return None
+
+
+def test_usage_misspelled_type():
+    usage_string = datacraft.usage.build_cli_help([DEMO_FOR_TEST+'zzz'])
+    assert DEMO_FOR_TEST+'zzz' in usage_string
+    assert 'unknown type' in usage_string
+
+
+def test_usage_no_filter():
+    usage_string = datacraft.usage.build_cli_help()
+    assert DEMO_FOR_TEST in usage_string
+    assert USAGE_MESSAGE in usage_string
+    assert f'{DEMO_FOR_TEST_NO_USAGE_DEFINED} | no usage defined' in usage_string
+
+
+def test_usage_constrained():
+    usage_string = datacraft.usage.build_cli_help([DEMO_FOR_TEST, DEMO_FOR_TEST_NO_USAGE_DEFINED])
+    assert DEMO_FOR_TEST in usage_string
+    assert USAGE_MESSAGE in usage_string
+    assert f'{DEMO_FOR_TEST_NO_USAGE_DEFINED} | no usage defined' in usage_string
diff --git a/tests/test_uuid.py b/tests/test_uuid.py
index e3842f4..6ec9dab 100644
--- a/tests/test_uuid.py
+++ b/tests/test_uuid.py
@@ -3,9 +3,6 @@
 import pytest
 
 from datacraft import builder, field_loader
-# to trigger registration
-from datacraft import cli
-
 
 UUID_REGEX = re.compile('[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}', re.I)
 
diff --git a/tests/test_values.py b/tests/test_values.py
index a5675ce..cf14c31 100644
--- a/tests/test_values.py
+++ b/tests/test_values.py
@@ -1,11 +1,13 @@
-from datacraft import field_loader
-import datacraft.suppliers as suppliers
-from datacraft.loader import preprocess_spec
-from datacraft.exceptions import SpecException
-import datacraft.builder as builder
 from collections import Counter
+
 import pytest
 
+import datacraft.builder as builder
+import datacraft.suppliers as suppliers
+from datacraft import field_loader
+from datacraft.exceptions import SpecException
+from datacraft.loader import preprocess_spec
+
 
 def test_single_value():
     spec = {'type': 'values', 'data': 42}
diff --git a/tests/test_weighted_csv.py b/tests/test_weighted_csv.py
index 30133bc..b2d43e9 100644
--- a/tests/test_weighted_csv.py
+++ b/tests/test_weighted_csv.py
@@ -3,8 +3,6 @@
 import pytest
 
 import datacraft
-# to trigger registration
-from datacraft import cli
 
 test_dir = os.sep.join([os.path.dirname(os.path.realpath(__file__)), 'data'])
 
diff --git a/tests/test_weighted_ref.py b/tests/test_weighted_ref.py
index c7b8d04..7ec25c2 100644
--- a/tests/test_weighted_ref.py
+++ b/tests/test_weighted_ref.py
@@ -1,10 +1,7 @@
-# to trigger registration
 import pytest
 
 from datacraft import builder, suppliers, field_loader, SupplierException
-from datacraft.supplier.refs import weighted_ref_supplier
-# to trigger registration
-from datacraft import cli
+from datacraft._registered_types.refs import weighted_ref_supplier
 
 
 def test_weighted_ref_missing_key():
diff --git a/tests/test_yaml_specs.py b/tests/test_yaml_specs.py
index 7dbb363..5085787 100644
--- a/tests/test_yaml_specs.py
+++ b/tests/test_yaml_specs.py
@@ -1,10 +1,6 @@
 import yaml
-import json
 
 from datacraft.loader import field_loader
-# to trigger registration
-from datacraft import cli
-
 
 yaml_spec = '''
 ---