diff --git a/Cargo.lock b/Cargo.lock index 0502c04..5d1f28a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,179 +2,22 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "autocfg" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" - [[package]] name = "fst" version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ab85b9b05e3978cc9a9cf8fea7f01b494e1a09ed3037e16ba39edc7a29eb61a" -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "indoc" -version = "2.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" -dependencies = [ - "rustversion", -] - -[[package]] -name = "libc" -version = "0.2.177" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" - -[[package]] -name = "memoffset" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" -dependencies = [ - "autocfg", -] - [[package]] name = "once_cell" version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" -[[package]] -name = "portable-atomic" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" - -[[package]] -name = "proc-macro2" -version = "1.0.103" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" -dependencies = [ - "unicode-ident", -] - [[package]] name = "purl_validator" version = "0.1.0" dependencies = [ "fst", "once_cell", - "pyo3", -] - -[[package]] -name = "pyo3" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37a6df7eab65fc7bee654a421404947e10a0f7085b6951bf2ea395f4659fb0cf" -dependencies = [ - "indoc", - "libc", - "memoffset", - "once_cell", - "portable-atomic", - "pyo3-build-config", - "pyo3-ffi", - "pyo3-macros", - "unindent", -] - -[[package]] -name = "pyo3-build-config" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f77d387774f6f6eec64a004eac0ed525aab7fa1966d94b42f743797b3e395afb" -dependencies = [ - "target-lexicon", ] - -[[package]] -name = "pyo3-ffi" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dd13844a4242793e02df3e2ec093f540d948299a6a77ea9ce7afd8623f542be" -dependencies = [ - "libc", - "pyo3-build-config", -] - -[[package]] -name = "pyo3-macros" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf8f9f1108270b90d3676b8679586385430e5c0bb78bb5f043f95499c821a71" -dependencies = [ - "proc-macro2", - "pyo3-macros-backend", - "quote", - "syn", -] - -[[package]] -name = "pyo3-macros-backend" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a3b2274450ba5288bc9b8c1b69ff569d1d61189d4bff38f8d22e03d17f932b" -dependencies = [ - "heck", - "proc-macro2", - "pyo3-build-config", - "quote", - "syn", -] - -[[package]] -name = "quote" -version = "1.0.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rustversion" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" - -[[package]] -name = "syn" -version = "2.0.108" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "target-lexicon" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df7f62577c25e07834649fc3b39fafdc597c0a3527dc1c60129201ccfcbaa50c" - -[[package]] -name = "unicode-ident" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" - -[[package]] -name = "unindent" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3" diff --git a/Cargo.toml b/Cargo.toml index 0ecfbf1..427e57b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,20 +5,9 @@ edition = "2024" authors = ["Keshav Priyadarshi ", "AboutCode "] description = "PackageURL validator using prebuilt FST" license = "Apache-2.0" -repository = "https://github.com/aboutcode-org/purl-validator" +repository = "https://github.com/aboutcode-org/purl-validator-rust" -[lib] -name = "purl_validator" -crate-type = ["cdylib", "rlib"] - -[dependencies] -fst = "0.4.7" -once_cell = "1.21" -pyo3 = { version = "0.27.1", features = ["extension-module"] } - -[[bin]] -name = "fst_builder" -path = "fst_builder/main.rs" +exclude = ["fst_builder/*"] include = [ "src/**", @@ -28,8 +17,14 @@ include = [ "LICENSE" ] -[package.metadata.maturin] +[lib] name = "purl_validator" +crate-type = ["rlib"] -[tool.maturin] -include = ["purls.fst"] +[dependencies] +fst = "0.4.7" +once_cell = "1.21" + +[[bin]] +name = "fst_builder" +path = "fst_builder/main.rs" diff --git a/Makefile b/Makefile index 948415a..331be53 100644 --- a/Makefile +++ b/Makefile @@ -1,12 +1,10 @@ build-fst: cargo run --bin fst_builder -build-python: - maturin build --release clean: cargo clean rm -f purls.fst rm -rf target -.PHONY: build-fst build-python clean \ No newline at end of file +.PHONY: build-fst clean \ No newline at end of file diff --git a/README.md b/README.md index bb8c05f..a4fe7ba 100644 --- a/README.md +++ b/README.md @@ -1 +1,76 @@ -# purl-validator \ No newline at end of file +# purl-validator + +[![License](https://img.shields.io/badge/License-Apache--2.0-blue.svg?style=for-the-badge)](https://opensource.org/licenses/Apache-2.0) +[![Version](https://img.shields.io/github/v/release/aboutcode-org/purl-validator-rust?style=for-the-badge)](https://github.com/aboutcode-org/purl-validator-rust/releases) +[![Test](https://img.shields.io/github/actions/workflow/status/aboutcode-org/purl-validator-rust/run-test.yml?style=for-the-badge&logo=github)](https://github.com/aboutcode-org/purl-validator-rust/actions) + +**purl-validator** is a Rust library for validating [Package URLs (PURLs)](https://github.com/package-url/purl-spec). It works fully offline, including in **air-gapped** or **restricted environments**, and answers one key question: **Does the package this PURL represents actually exist?** + +## How It Works? + +**purl-validator** is shipped with a pre-built FST (Finite State Transducer), a set of compact automata containing latest Package URLs mined by the MineCode[^1]. Library uses this FST to perform lookups and confirm whether the **base PURL**[^2] exists. + +## Currently Supported Ecosystems + +- **nuget**: [https://www.nuget.org/](https://www.nuget.org/) + +## Usage + +Add `purl-validator` to your Rust dependency + +```bash +cargo add purl-validator +``` + +Use it in your code like this + +```rust +use purl_validator::validate; + +let result: bool = validate("pkg:nuget/FluentValidation"); +``` + +## Contribution + +We welcome contributions from the community! If you find a bug or have an idea for a new feature, please open an issue on the GitHub repository. If you want to contribute code, you can fork the repository, make your changes, and submit a pull request. + +* Please try to write a good commit message, see [good commit message wiki](https://aboutcode.readthedocs.io/en/latest/contributing/writing_good_commit_messages.html). +* Add DCO `Sign Off` to your commits. + +## Development Setup + +Run these commands, starting from a git clone of [https://github.com/aboutcode-org/purl-validator-rust.git](https://github.com/aboutcode-org/purl-validator-rust.git) + +Generate FST: + +```bash +make build-fst +``` + +Run tests: + +```bash +make test +``` + +## License + +SPDX-License-Identifier: Apache-2.0 + +purl-validator is licensed under Apache License version 2.0. + +```text +You may not use this software except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +``` + +[^1]: MineCode continuously collects package metadata from various package ecosystems to maintain an up-to-date catalog of known packages. +[^2]: A Base Package URL is a Package URL without a version or subpath. \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml deleted file mode 100644 index 61e7c81..0000000 --- a/pyproject.toml +++ /dev/null @@ -1,18 +0,0 @@ -[build-system] -requires = ["maturin>=1.5"] -build-backend = "maturin" - -[project] -name = "purl-validator" -version = "0.1.0" -description = "PackageURL validator using prebuilt FST" -authors = [ - { name = "Keshav Priyadarshi", email = "git@keshav.space" }, - { name = "AboutCode", email = "info@aboutcode.org" }, -] -license = "Apache-2.0" -readme = "README.md" -requires-python = ">=3.10" - -[tool.maturin] -name = "purl_validator" \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 50f32a7..a2f6085 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,26 +1,12 @@ use fst::Set; use once_cell::sync::Lazy; - static FST_BYTES: &[u8] = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/purls.fst")); -static VALIDATOR: Lazy> = Lazy::new(|| { - Set::new(FST_BYTES).expect("Failed to load FST from embedded bytes") -}); - - -pub fn validate(word: &str) -> bool { - VALIDATOR.contains(word) -} - - -#[pyo3::pymodule] -mod purl_validator { - use pyo3::prelude::*; - use crate::validate; +static VALIDATOR: Lazy> = + Lazy::new(|| Set::new(FST_BYTES).expect("Failed to load FST from embedded bytes")); - #[pyfunction(name = "validate")] - fn py_validate(word: &str) -> PyResult { - Ok(validate(word)) - } +pub fn validate(packageurl: &str) -> bool { + let trimmed_packageurl = packageurl.trim_end_matches("/"); + VALIDATOR.contains(trimmed_packageurl) }