# TOSCA-Solver
This crate infers the relationships between [TOSCA](https://docs.unfurl.run/tosca.html) node templates when given a set of node templates and their requirements.
Each TOSCA requirement is encoded as a set of constraints including:
* node and capability types
* the relationship's valid_target_types
* node_filter constraints
* node_filter match expressions
``solve()`` will return the nodes that match the requirements associated with a given set of nodes.
By default this crate is exposed as a Python extension module and is used by [Unfurl](https://github.com/onecommons/unfurl), but it could be used by any TOSCA 1.3 processor.
## Usage
By default, this crate is built as a Python extension, to disable this feature, set ``default-features = false``. The ``solve`` function is its main entry point and can invoked from the Rust or Python. Your TOSCA topology will have to be encoded as a HashMap of Nodes -- see https://github.com/onecommons/unfurl/blob/main/unfurl/solver.py for an example.
## Design notes
Finding the match for a TOSCA requirement with a node_filter can't be determined with a simple algorithm. For example, a requirement node_filter's match can depend on a property value that is itself computed from a requirement (e.g. via TOSCA's ``get_property`` function). So finding a node filter match might change a property's value, which in turn could affect the node_filter match.
In addition to this basic functionality, this solver will use type inference to resolve requirements that haven't defined explicit node targets and supports Unfurl's node_filter extensions for applying constraints and graph querying.
Luckily, by eschewing negation and quantification we can avoid the need for a full SAT solver and instead encode the inference rules as [Datalog](https://blogit.michelin.io/an-introduction-to-datalog/)-like query rules using [Ascent](https://github.com/s-arash/ascent/) -- a simpler and more scalable approach.
## Why?
One of Unfurl's goal is to enable adaptable blueprints that can easily compose independent, open-source components -- imagine something like a [package manager for the cloud](https://github.com/onecommons/cloudmap). This crate allows blueprints to express their requirements in a loosely-coupled, generic manner.
## Development
You can use the standard cargo commands for development if you use its ``--no-default-features`` flag to skip compiling with the "pyo3/extension-module" feature.
To compile the Python extension, in the parent of this directory, run:
``python setup.py build_rust --debug --inplace``
Run `pip install setuptools-rust>=1.7.0 pbr` to install `setup.py`'s requirements.
## Tests
Cargo tests should be invoked with ``cargo test --no-default-features`` because the "pyo3/extension-module" feature doesn't work with cargo test.
Unfurl's Python unit tests have more extensive tests in https://github.com/onecommons/unfurl/blob/main/tests/test_solver.py. See Unfurl's main README for instructions on running its unit tests.