Skip to content

Commit caf5703

Browse files
committed
Add combined documentation
Signed-off-by: Philippe Ombredanne <pombredanne@aboutcode.org>
1 parent 64931fd commit caf5703

11 files changed

Lines changed: 456 additions & 174 deletions

docs/source/conf.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
# -- Project information -----------------------------------------------------
1919

20-
project = "nexb-skeleton"
20+
project = "purl-validator"
2121
copyright = "nexB Inc., AboutCode and others."
2222
author = "AboutCode.org authors and contributors"
2323

@@ -79,9 +79,9 @@
7979

8080
html_context = {
8181
"display_github": True,
82-
"github_user": "nexB",
83-
"github_repo": "nexb-skeleton",
84-
"github_version": "develop", # branch
82+
"github_user": "aboutcode-org",
83+
"github_repo": "purl-validator",
84+
"github_version": "main", # branch
8585
"conf_py_path": "/docs/source/", # path in the checkout to the docs root
8686
}
8787

docs/source/contribute/contrib_doc.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ Style Conventions for the Documentaion
187187

188188
(`Refer <https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html#sections>`_)
189189
Normally, there are no heading levels assigned to certain characters as the structure is
190-
determined from the succession of headings. However, this convention is used in Pythons Style
190+
determined from the succession of headings. However, this convention is used in Python's Style
191191
Guide for documenting which you may follow:
192192

193193
# with overline, for parts
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
.. _data_structure_rationale:
2+
3+
FST Data Structure Rationale
4+
=============================
5+
6+
PurlValidator needs exact membership lookup for a large list of base PURLs. The
7+
lookup data index is built before release and bundled with each library.
8+
9+
10+
See https://github.com/aboutcode-org/purl-validator/tree/main/etc/bench for
11+
actual detailed rationale and bench for the choice of an FST.
12+
13+
14+
Why FSTs are used?
15+
------------------
16+
17+
Finite state transducers store sorted strings in a compact form. PURLs share
18+
prefixes such as ``pkg:npm/``, ``pkg:pypi/``, and ``pkg:maven/``. This makes an
19+
FST useful for exact package identity queries.
20+
21+
FST can be memory-mapped and are super compact. They are not as fast as native
22+
set, but the memory consumption is so much lower than this make them the most
23+
attractive solution, even if it takes more time to build.
24+
25+
26+
Requirements
27+
---------------
28+
29+
The index structure should provide:
30+
31+
And for the library selection, we have these high level requirements:
32+
33+
- We want exact result without false positives, e.g., no bloom filter.
34+
- Offline use, with no network is a must: the dataset must be bundled
35+
in the releases.
36+
- With build time index construction, the construction time is not
37+
critical.
38+
- The bundled index should be small enough to ship below crates, and
39+
Pypi archive size limits.
40+
- No rebuild at startup/runtime, and fast enough load time from disk,
41+
ideally memory-mapped.
42+
- Fast enough lookup.
43+
- Libraries should be maintained, active FOSS for Rust/Go/Python.
44+
45+
46+
47+
48+
Selected FST libraries
49+
--------------------------
50+
51+
Python uses ``ducer.Map`` with ``mmap``. The map is stored on disk and opened
52+
without loading the full catalog into Python objects.
53+
54+
Rust uses ``fst::Set``. The generated FST is embedded into the crate.
55+
56+
Go uses Vellum FST. The generated FST is embedded into the module.
57+
58+
Alternatives
59+
------------
60+
61+
We considered also built-in sets and maps as a baseline:
62+
63+
- Python: ``set`` and ``dict``.
64+
- Rust: ``HashSet`` and ``HashMap``.
65+
- Go: ``map[string]struct{}`` and ``map[string]bool``.
66+
67+
These structures are simple and fast. They require loading all keys into
68+
runtime memory, so they are less useful as the packaged lookup format.
69+
70+
Sorted arrays or slices can use binary search. They are simple and exact, but
71+
lookup takes repeated string comparisons and the strings still need to be
72+
loaded.
73+
74+
SQLite can store the PURLs in an indexed table. It gives exact results, but it
75+
adds a database dependency for a read-only membership check. It has way more
76+
features than needed and is overkill for our use case.
77+
78+
Bloom filters are small and fast, but they can return false positives. They
79+
should cannot be used as validation index.
80+
81+
A DAWG can store a set of strings by sharing prefixes and suffixes. It may be a
82+
valid alternative to an FST (it is very similar to) but there are few maintained
83+
libraries in the target languages.

docs/source/explanations.rst

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
.. _explanations:
2+
3+
Explanations
4+
============
5+
6+
Syntax validation and identity validation
7+
-----------------------------------------
8+
9+
The Package-URL spec defines the PURL format. A PURL can follow the spec
10+
format and still name a package that is not known in the package ecosystems.
11+
12+
PurlValidator checks the package PURL against reference data of known PURLs. This
13+
helps find misspelled names, wrong package types, and PURL that
14+
do not appear in the reference upstream ecosystem package repositories.
15+
16+
17+
Offline validation
18+
------------------
19+
20+
SBOM and compliance workflows may run in CI systems, private networks, or
21+
air-gapped environments. PurlValidator packages lookup data with each released
22+
library so validation does not need a network registry access at runtime.
23+
24+
25+
Base PURL validation
26+
--------------------
27+
28+
PURL existence is checked before version existence.
29+
30+
The current libraries validate base PURLs only, no versions. Version support
31+
can be a future enhancement.

docs/source/how-to-guides.rst

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
.. _how_to_guides:
2+
3+
How-to Guides
4+
=============
5+
6+
Choose an implementation
7+
------------------------
8+
9+
Use the implementation that matches the application:
10+
11+
- Use Python for Python scripts, data pipelines, etc.
12+
- Use Rust for Rust appss.
13+
- Use Go for Go apps and command-line tools.
14+
15+
All implementations package PURL index data with the released library.
16+
17+
18+
Update validation data
19+
----------------------
20+
21+
PurlValidator index data is released with each package. To update the
22+
data used by an application, update the PurlValidator package version.
23+
24+
25+
Validation results
26+
--------------------------
27+
28+
Treat validation results in these groups:
29+
30+
- Known: the PURL is valid and exists in the reference data.
31+
- Unknown: the PURL is valid (parsing) but not present in the reference data.
32+
- Invalid or unsupported: the input is not a supported or known PURL.
33+
34+
For SBOM checks, you should report unknown and invalid PURLs separately.
35+
Invalid PURLs are usually an error of the SBOM or SCA producer tool.
36+
Unknown PURLs could be new packages, or typos, or SCA tools inventions.

docs/source/index.rst

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,57 @@
1-
Welcome to nexb-skeleton's documentation!
2-
=========================================
1+
PurlValidator Documentation
2+
===========================
33

4-
.. toctree::
5-
:maxdepth: 2
6-
:caption: Contents:
4+
PurlValidator checks whether a base Package-URL (PURL) is present in a known
5+
package catalog. It works without a network connection after installation.
76

8-
skeleton-usage
9-
contribute/contrib_doc
7+
A valid PURL string can still name a package that is not known. PurlValidator
8+
adds this package identity check for SBOM, VEX, SCA, and compliance workflows.
9+
10+
Documentation overview
11+
----------------------
12+
13+
Getting started
14+
~~~~~~~~~~~~~~~
15+
16+
- :ref:`quickstart`
17+
- :ref:`introduction`
18+
19+
Tutorials
20+
~~~~~~~~~
21+
22+
- :ref:`tutorials`
23+
24+
How-to guides
25+
~~~~~~~~~~~~~
26+
27+
- :ref:`how_to_guides`
28+
29+
Reference
30+
~~~~~~~~~
31+
32+
- :ref:`reference`
33+
34+
Explanations
35+
~~~~~~~~~~~~
36+
37+
- :ref:`explanations`
38+
- :ref:`data_structure_rationale`
1039

1140
Indices and tables
12-
==================
41+
------------------
1342

1443
* :ref:`genindex`
15-
* :ref:`modindex`
1644
* :ref:`search`
45+
46+
.. toctree::
47+
:maxdepth: 2
48+
:hidden:
49+
50+
quickstart
51+
introduction
52+
tutorials
53+
how-to-guides
54+
reference
55+
explanations
56+
data-structure-rationale
57+
contribute/contrib_doc

docs/source/introduction.rst

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
.. _introduction:
2+
3+
Introduction
4+
============
5+
6+
PurlValidator checks package identity for Package-URLs (PURLs). It does
7+
not replace syntax validation. It adds a lookup against an index of packaged
8+
reference data.
9+
10+
Why this exists?
11+
-----------------
12+
13+
PURL is used in SBOMs, VEX documents, SCA tools, and vulnerability databases.
14+
The PURL spec tells tools how to write a package identifier, but does
15+
not prove that the package exists.
16+
17+
Common PURL data problems include:
18+
19+
- Misspelled package names.
20+
- Wrong or made up package types.
21+
- Package that are not present in an ecosystem.
22+
23+
PurlValidator answers this question:
24+
25+
Does this PURL exists for a known package?
26+
27+
Repositories
28+
------------
29+
30+
We have three implementations in Rust, Go and Python.
31+
Each repository has language-specific usage notes in its README.
32+
33+
- Python: https://github.com/aboutcode-org/purl-validator
34+
- Rust: https://github.com/aboutcode-org/purl-validator.rs
35+
- Go: https://github.com/aboutcode-org/purlvalidator-go
36+
37+
38+
Validation scope
39+
----------------
40+
41+
PurlValidator validates PURLs, ignoring version. A base PURL contains:
42+
43+
- Type, such as ``npm`` or ``pypi``.
44+
- Optional namespace, such as an npm scope or Maven groupid.
45+
- Name.
46+
47+
Versions, qualifiers, and subpaths are not part of the lookup query.

docs/source/quickstart.rst

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
.. _quickstart:
2+
3+
Quickstart
4+
==========
5+
6+
Python
7+
------
8+
9+
Install the Python package:
10+
11+
.. code-block:: bash
12+
13+
pip install purl-validator
14+
15+
Validate a PURL:
16+
17+
.. code-block:: python
18+
19+
from purl_validator import PurlValidator
20+
21+
validator = PurlValidator()
22+
23+
print(validator.validate_purl("pkg:nuget/FluentValidation"))
24+
print(validator.validate_purl("pkg:nuget/non-existent-foo-bar"))
25+
26+
Rust
27+
----
28+
29+
Install the Rust crate:
30+
31+
.. code-block:: bash
32+
33+
cargo add purl_validator
34+
35+
Validate a PURL:
36+
37+
.. code-block:: rust
38+
39+
use purl_validator::validate;
40+
41+
fn main() {
42+
let exists = validate("pkg:nuget/FluentValidation")
43+
.expect("input must be a supported base PURL");
44+
45+
println!("{exists}");
46+
}
47+
48+
Go
49+
--
50+
51+
Install the Go module:
52+
53+
.. code-block:: bash
54+
55+
go get github.com/aboutcode-org/purlvalidator-go
56+
57+
Validate a PURL:
58+
59+
.. code-block:: go
60+
61+
package main
62+
63+
import (
64+
"fmt"
65+
"log"
66+
67+
purlvalidator "github.com/aboutcode-org/purlvalidator-go"
68+
)
69+
70+
func main() {
71+
exists, err := purlvalidator.Validate("pkg:nuget/FluentValidation")
72+
if err != nil {
73+
log.Fatal(err)
74+
}
75+
76+
fmt.Println(exists)
77+
}
78+
79+
Next steps
80+
----------
81+
82+
- Use the Python README for Python-specific helper APIs: https://github.com/aboutcode-org/purl-validator
83+
- Use the Rust README for error handling with ``ValidateError``: https://github.com/aboutcode-org/purl-validator.rs
84+
- Use the Go README for ``Validate`` return values and integration examples: https://github.com/aboutcode-org/purlvalidator-go

0 commit comments

Comments
 (0)