rusty-validator 0.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rusty_validator-0.1.1/.github/workflows/CI.yml +184 -0
- rusty_validator-0.1.1/.gitignore +73 -0
- rusty_validator-0.1.1/.python-version +1 -0
- rusty_validator-0.1.1/CLAUDE.md +170 -0
- rusty_validator-0.1.1/CONTEXT.md +83 -0
- rusty_validator-0.1.1/Cargo.lock +532 -0
- rusty_validator-0.1.1/Cargo.toml +16 -0
- rusty_validator-0.1.1/PKG-INFO +14 -0
- rusty_validator-0.1.1/README.md +85 -0
- rusty_validator-0.1.1/docs/adr/0001-treues-binding-der-validator-crate.md +27 -0
- rusty_validator-0.1.1/docs/adr/0002-reines-praedikat-binding-ergonomie-separat.md +28 -0
- rusty_validator-0.1.1/docs/adr/0003-pythonische-parameternamen.md +18 -0
- rusty_validator-0.1.1/docs/adr/0004-strikter-typ-vertrag.md +20 -0
- rusty_validator-0.1.1/pyproject.toml +37 -0
- rusty_validator-0.1.1/python/rusty_validator/__init__.py +3 -0
- rusty_validator-0.1.1/python/rusty_validator/__init__.pyi +5 -0
- rusty_validator-0.1.1/python/rusty_validator/py.typed +0 -0
- rusty_validator-0.1.1/src/lib.rs +28 -0
- rusty_validator-0.1.1/tests/__init__.py +0 -0
- rusty_validator-0.1.1/tests/test_email_validation.py +106 -0
- rusty_validator-0.1.1/tests/test_ip_validation.py +114 -0
- rusty_validator-0.1.1/tests/test_url_validation.py +26 -0
- rusty_validator-0.1.1/uv.lock +330 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# This file is autogenerated by maturin v1.6.0
|
|
2
|
+
# To update, run
|
|
3
|
+
#
|
|
4
|
+
# maturin generate-ci github
|
|
5
|
+
#
|
|
6
|
+
name: CI
|
|
7
|
+
|
|
8
|
+
on:
|
|
9
|
+
push:
|
|
10
|
+
branches:
|
|
11
|
+
- main
|
|
12
|
+
- master
|
|
13
|
+
tags:
|
|
14
|
+
- '*'
|
|
15
|
+
pull_request:
|
|
16
|
+
workflow_dispatch:
|
|
17
|
+
|
|
18
|
+
permissions:
|
|
19
|
+
contents: read
|
|
20
|
+
|
|
21
|
+
jobs:
|
|
22
|
+
test:
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@v4
|
|
26
|
+
- uses: astral-sh/setup-uv@v5
|
|
27
|
+
with:
|
|
28
|
+
enable-cache: true
|
|
29
|
+
- uses: dtolnay/rust-toolchain@stable
|
|
30
|
+
- name: Sync deps and build extension
|
|
31
|
+
run: uv sync
|
|
32
|
+
- name: Test
|
|
33
|
+
run: uv run pytest
|
|
34
|
+
- name: Type-check
|
|
35
|
+
run: uv run mypy python
|
|
36
|
+
|
|
37
|
+
linux:
|
|
38
|
+
runs-on: ${{ matrix.platform.runner }}
|
|
39
|
+
strategy:
|
|
40
|
+
matrix:
|
|
41
|
+
platform:
|
|
42
|
+
- runner: ubuntu-latest
|
|
43
|
+
target: x86_64
|
|
44
|
+
- runner: ubuntu-latest
|
|
45
|
+
target: x86
|
|
46
|
+
- runner: ubuntu-latest
|
|
47
|
+
target: aarch64
|
|
48
|
+
- runner: ubuntu-latest
|
|
49
|
+
target: armv7
|
|
50
|
+
- runner: ubuntu-latest
|
|
51
|
+
target: s390x
|
|
52
|
+
- runner: ubuntu-latest
|
|
53
|
+
target: ppc64le
|
|
54
|
+
steps:
|
|
55
|
+
- uses: actions/checkout@v4
|
|
56
|
+
- uses: actions/setup-python@v5
|
|
57
|
+
with:
|
|
58
|
+
python-version: '3.13'
|
|
59
|
+
- name: Build wheels
|
|
60
|
+
uses: PyO3/maturin-action@v1
|
|
61
|
+
with:
|
|
62
|
+
target: ${{ matrix.platform.target }}
|
|
63
|
+
# abi3-py313: one cp313-abi3 wheel per platform, runs on all CPython >=3.13.
|
|
64
|
+
args: --release --out dist -i 3.13
|
|
65
|
+
sccache: 'true'
|
|
66
|
+
manylinux: auto
|
|
67
|
+
- name: Upload wheels
|
|
68
|
+
uses: actions/upload-artifact@v4
|
|
69
|
+
with:
|
|
70
|
+
name: wheels-linux-${{ matrix.platform.target }}
|
|
71
|
+
path: dist
|
|
72
|
+
|
|
73
|
+
musllinux:
|
|
74
|
+
runs-on: ${{ matrix.platform.runner }}
|
|
75
|
+
strategy:
|
|
76
|
+
matrix:
|
|
77
|
+
platform:
|
|
78
|
+
- runner: ubuntu-latest
|
|
79
|
+
target: x86_64
|
|
80
|
+
- runner: ubuntu-latest
|
|
81
|
+
target: x86
|
|
82
|
+
- runner: ubuntu-latest
|
|
83
|
+
target: aarch64
|
|
84
|
+
- runner: ubuntu-latest
|
|
85
|
+
target: armv7
|
|
86
|
+
steps:
|
|
87
|
+
- uses: actions/checkout@v4
|
|
88
|
+
- uses: actions/setup-python@v5
|
|
89
|
+
with:
|
|
90
|
+
python-version: '3.13'
|
|
91
|
+
- name: Build wheels
|
|
92
|
+
uses: PyO3/maturin-action@v1
|
|
93
|
+
with:
|
|
94
|
+
target: ${{ matrix.platform.target }}
|
|
95
|
+
# abi3-py313: one cp313-abi3 wheel per platform, runs on all CPython >=3.13.
|
|
96
|
+
args: --release --out dist -i 3.13
|
|
97
|
+
sccache: 'true'
|
|
98
|
+
manylinux: musllinux_1_2
|
|
99
|
+
- name: Upload wheels
|
|
100
|
+
uses: actions/upload-artifact@v4
|
|
101
|
+
with:
|
|
102
|
+
name: wheels-musllinux-${{ matrix.platform.target }}
|
|
103
|
+
path: dist
|
|
104
|
+
|
|
105
|
+
windows:
|
|
106
|
+
runs-on: ${{ matrix.platform.runner }}
|
|
107
|
+
strategy:
|
|
108
|
+
matrix:
|
|
109
|
+
platform:
|
|
110
|
+
- runner: windows-latest
|
|
111
|
+
target: x64
|
|
112
|
+
- runner: windows-latest
|
|
113
|
+
target: x86
|
|
114
|
+
steps:
|
|
115
|
+
- uses: actions/checkout@v4
|
|
116
|
+
- uses: actions/setup-python@v5
|
|
117
|
+
with:
|
|
118
|
+
python-version: '3.13'
|
|
119
|
+
architecture: ${{ matrix.platform.target }}
|
|
120
|
+
- name: Build wheels
|
|
121
|
+
uses: PyO3/maturin-action@v1
|
|
122
|
+
with:
|
|
123
|
+
target: ${{ matrix.platform.target }}
|
|
124
|
+
# abi3-py313: one cp313-abi3 wheel per platform, runs on all CPython >=3.13.
|
|
125
|
+
args: --release --out dist -i 3.13
|
|
126
|
+
sccache: 'true'
|
|
127
|
+
- name: Upload wheels
|
|
128
|
+
uses: actions/upload-artifact@v4
|
|
129
|
+
with:
|
|
130
|
+
name: wheels-windows-${{ matrix.platform.target }}
|
|
131
|
+
path: dist
|
|
132
|
+
|
|
133
|
+
macos:
|
|
134
|
+
# Single universal2 build on Apple Silicon: Intel macOS runners (macos-13) queue for
|
|
135
|
+
# hours / don't schedule on public repos, while one ARM runner emits a fat x86_64+arm64
|
|
136
|
+
# wheel. abi3 cross-compiles cleanly, so this keeps Intel coverage without an Intel runner.
|
|
137
|
+
runs-on: macos-14
|
|
138
|
+
steps:
|
|
139
|
+
- uses: actions/checkout@v4
|
|
140
|
+
- uses: actions/setup-python@v5
|
|
141
|
+
with:
|
|
142
|
+
python-version: '3.13'
|
|
143
|
+
- name: Build wheels
|
|
144
|
+
uses: PyO3/maturin-action@v1
|
|
145
|
+
with:
|
|
146
|
+
target: universal2-apple-darwin
|
|
147
|
+
# abi3-py313: one cp313-abi3 wheel, runs on all CPython >=3.13.
|
|
148
|
+
args: --release --out dist -i 3.13
|
|
149
|
+
sccache: 'true'
|
|
150
|
+
- name: Upload wheels
|
|
151
|
+
uses: actions/upload-artifact@v4
|
|
152
|
+
with:
|
|
153
|
+
name: wheels-macos-universal2
|
|
154
|
+
path: dist
|
|
155
|
+
|
|
156
|
+
sdist:
|
|
157
|
+
runs-on: ubuntu-latest
|
|
158
|
+
steps:
|
|
159
|
+
- uses: actions/checkout@v4
|
|
160
|
+
- name: Build sdist
|
|
161
|
+
uses: PyO3/maturin-action@v1
|
|
162
|
+
with:
|
|
163
|
+
command: sdist
|
|
164
|
+
args: --out dist
|
|
165
|
+
- name: Upload sdist
|
|
166
|
+
uses: actions/upload-artifact@v4
|
|
167
|
+
with:
|
|
168
|
+
name: wheels-sdist
|
|
169
|
+
path: dist
|
|
170
|
+
|
|
171
|
+
release:
|
|
172
|
+
name: Release
|
|
173
|
+
runs-on: ubuntu-latest
|
|
174
|
+
if: "startsWith(github.ref, 'refs/tags/')"
|
|
175
|
+
needs: [test, linux, musllinux, windows, macos, sdist]
|
|
176
|
+
steps:
|
|
177
|
+
- uses: actions/download-artifact@v4
|
|
178
|
+
- name: Publish to PyPI
|
|
179
|
+
uses: PyO3/maturin-action@v1
|
|
180
|
+
env:
|
|
181
|
+
MATURIN_PYPI_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
|
|
182
|
+
with:
|
|
183
|
+
command: upload
|
|
184
|
+
args: --non-interactive --skip-existing wheels-*/*
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/target
|
|
2
|
+
.claude
|
|
3
|
+
graphify-out/
|
|
4
|
+
|
|
5
|
+
# Byte-compiled / optimized / DLL files
|
|
6
|
+
__pycache__/
|
|
7
|
+
.pytest_cache/
|
|
8
|
+
*.py[cod]
|
|
9
|
+
|
|
10
|
+
# C extensions
|
|
11
|
+
*.so
|
|
12
|
+
|
|
13
|
+
# Distribution / packaging
|
|
14
|
+
.Python
|
|
15
|
+
.venv/
|
|
16
|
+
env/
|
|
17
|
+
bin/
|
|
18
|
+
build/
|
|
19
|
+
develop-eggs/
|
|
20
|
+
dist/
|
|
21
|
+
eggs/
|
|
22
|
+
lib/
|
|
23
|
+
lib64/
|
|
24
|
+
parts/
|
|
25
|
+
sdist/
|
|
26
|
+
var/
|
|
27
|
+
include/
|
|
28
|
+
man/
|
|
29
|
+
venv/
|
|
30
|
+
*.egg-info/
|
|
31
|
+
.installed.cfg
|
|
32
|
+
*.egg
|
|
33
|
+
|
|
34
|
+
# Installer logs
|
|
35
|
+
pip-log.txt
|
|
36
|
+
pip-delete-this-directory.txt
|
|
37
|
+
pip-selfcheck.json
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.coverage
|
|
43
|
+
.cache
|
|
44
|
+
nosetests.xml
|
|
45
|
+
coverage.xml
|
|
46
|
+
|
|
47
|
+
# Translations
|
|
48
|
+
*.mo
|
|
49
|
+
|
|
50
|
+
# Mr Developer
|
|
51
|
+
.mr.developer.cfg
|
|
52
|
+
.project
|
|
53
|
+
.pydevproject
|
|
54
|
+
|
|
55
|
+
# Rope
|
|
56
|
+
.ropeproject
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
*.pot
|
|
61
|
+
|
|
62
|
+
.DS_Store
|
|
63
|
+
|
|
64
|
+
# Sphinx documentation
|
|
65
|
+
docs/_build/
|
|
66
|
+
|
|
67
|
+
# PyCharm
|
|
68
|
+
.idea/
|
|
69
|
+
|
|
70
|
+
# VSCode
|
|
71
|
+
.vscode/
|
|
72
|
+
|
|
73
|
+
# Pyenv / uv: .python-version is committed (pins the dev interpreter)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.13
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
Dieser Leitfaden hilft Claude Code (claude.ai/code) bei der Arbeit in diesem Repository.
|
|
4
|
+
|
|
5
|
+
## Projektüberblick
|
|
6
|
+
|
|
7
|
+
**rusty-validator** ist ein schmales **Python-Binding** der Rust-Crate
|
|
8
|
+
[`validator`](https://github.com/Keats/validator), gebaut mit **PyO3** und
|
|
9
|
+
**maturin**. Es stellt Validierungs-**Prädikate** bereit: Funktionen, die prüfen, ob
|
|
10
|
+
eine Zeichenkette wohlgeformt ist (E-Mail-Adresse, URL, IP-Adresse) und `True`/`False`
|
|
11
|
+
zurückgeben. Die eigentliche Prüf-Logik liefert die Crate — dieses Projekt reicht sie
|
|
12
|
+
nur treu nach Python durch.
|
|
13
|
+
|
|
14
|
+
**Vor Änderungen lesen** — die Domänensprache und die tragenden Entscheidungen sind
|
|
15
|
+
hier festgehalten:
|
|
16
|
+
|
|
17
|
+
- [CONTEXT.md](CONTEXT.md) — Glossar (Validieren, Format-/Constraint-Validator,
|
|
18
|
+
E-Mail-Adresse, URL, IP-Adresse) + Beispieldialog. Reines Glossar, keine
|
|
19
|
+
Implementierungsdetails.
|
|
20
|
+
- [docs/adr/](docs/adr/) — Architektur-Entscheidungen:
|
|
21
|
+
- [0001](docs/adr/0001-treues-binding-der-validator-crate.md) — Treues Binding der
|
|
22
|
+
`validator`-Crate (Crate = Vorgabe, mit dokumentierten Ausnahmen)
|
|
23
|
+
- [0002](docs/adr/0002-reines-praedikat-binding-ergonomie-separat.md) — Reines
|
|
24
|
+
Prädikat-Binding; Ergonomie (Guards/Decorators/Constraint-Objekte) in separatem Paket
|
|
25
|
+
- [0003](docs/adr/0003-pythonische-parameternamen.md) — Pythonische Parameternamen
|
|
26
|
+
- [0004](docs/adr/0004-strikter-typ-vertrag.md) — Strikter Typ-Vertrag: `TypeError`
|
|
27
|
+
bei Typfehler, sonst Prädikat
|
|
28
|
+
|
|
29
|
+
## Architektur
|
|
30
|
+
|
|
31
|
+
Drei Schichten, von Rust nach Python:
|
|
32
|
+
|
|
33
|
+
1. **Rust** ([src/lib.rs](src/lib.rs)): Jede Validierung ist eine `#[pyfunction]`
|
|
34
|
+
(`validate_email`/`validate_url`/`validate_ip`), die an die `validator`-Crate
|
|
35
|
+
delegiert. Registriert werden sie im `#[pymodule] _validator`.
|
|
36
|
+
2. **maturin** baut die Rust-`cdylib` zur nativen Erweiterung `rusty_validator._validator`
|
|
37
|
+
(siehe `[tool.maturin]` in [pyproject.toml](pyproject.toml): `python-source = "python"`,
|
|
38
|
+
`module-name = "rusty_validator._validator"`).
|
|
39
|
+
3. **Python** ([`python/rusty_validator/__init__.py`](python/rusty_validator/__init__.py))
|
|
40
|
+
re-exportiert die Funktionen aus `._validator`; die Typen stehen im Stub
|
|
41
|
+
[`__init__.pyi`](python/rusty_validator/__init__.pyi).
|
|
42
|
+
|
|
43
|
+
**Leitprinzipien** (Details in den ADRs):
|
|
44
|
+
|
|
45
|
+
- **Treues Binding**: Verhalten = `validator`-Crate. Überraschende Crate-Ergebnisse
|
|
46
|
+
(z. B. `validate_url("ftp://x")` ist `True`, `validate_ip("::1")` ist `True`) sind
|
|
47
|
+
beabsichtigt geerbt — **nicht** „reparieren". Abweichungen sind dokumentierte
|
|
48
|
+
Ausnahmen (ADR 0001).
|
|
49
|
+
- **Reines Prädikat**: Jede Funktion gibt `bool` zurück. Kein „wirf-bei-ungültig"-Stil
|
|
50
|
+
(Guard), keine Constraint-Objekte, keine Decorators — die gehören in ein **separates**
|
|
51
|
+
Ergonomie-Paket (ADR 0002).
|
|
52
|
+
- **Pythonische Parameternamen**: Parametrisierte Validatoren bekommen lesbare Namen
|
|
53
|
+
(z. B. `min_length` statt Crate-`min`) — bewusste Ausnahme zu ADR 0001 (ADR 0003).
|
|
54
|
+
- **Strikter Typ-Vertrag**: Korrekt typisierte, aber ungültige Eingabe → `False`;
|
|
55
|
+
**typ-falsche** Eingabe (z. B. `None`) → `TypeError`, kein stilles `False` (ADR 0004).
|
|
56
|
+
|
|
57
|
+
## Repository-Struktur
|
|
58
|
+
|
|
59
|
+
```text
|
|
60
|
+
rusty-validator/
|
|
61
|
+
├── CLAUDE.md # Diese Datei
|
|
62
|
+
├── CONTEXT.md # Domänen-Glossar
|
|
63
|
+
├── README.md # Nutzer-Doku
|
|
64
|
+
├── Cargo.toml / Cargo.lock # Rust-Crate (Name, Version, Dependencies)
|
|
65
|
+
├── pyproject.toml # maturin-Build + mypy-Konfiguration
|
|
66
|
+
├── docs/adr/ # Architektur-Entscheidungen (ADRs)
|
|
67
|
+
├── src/lib.rs # Rust: #[pyfunction]s + #[pymodule]
|
|
68
|
+
├── python/rusty_validator/
|
|
69
|
+
│ ├── __init__.py # Re-Export der nativen Funktionen
|
|
70
|
+
│ ├── __init__.pyi # Typ-Stubs der öffentlichen API
|
|
71
|
+
│ └── py.typed # Markiert das Paket als typisiert (PEP 561)
|
|
72
|
+
├── tests/ # pytest-Suite (parametrisiert)
|
|
73
|
+
└── .github/workflows/CI.yml # Wheels bauen (Multi-Target) + PyPI-Release bei Tags
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Entwicklungsbefehle
|
|
77
|
+
|
|
78
|
+
[uv](https://docs.astral.sh/uv/) verwaltet venv, Dev-Dependencies (`[dependency-groups]`
|
|
79
|
+
in [pyproject.toml](pyproject.toml)) und die Python-Version ([.python-version](.python-version));
|
|
80
|
+
**maturin bleibt das Build-Backend** (`[build-system]`). Einmal `uv sync` holt die Tools
|
|
81
|
+
ins `.venv`; danach laufen alle Befehle ohne globale Installation via `uv run`. (Ohne uv
|
|
82
|
+
funktionieren die blanken Befehle im aktiven venv weiterhin.)
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
uv sync # .venv + Dev-Tools (pytest, mypy, maturin) aus uv.lock
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Bauen & in das venv installieren (Entwicklung)
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
uv run maturin develop # kompiliert die Rust-Erweiterung in das venv
|
|
92
|
+
uv run maturin develop --release # optimierter Build (langsamer zu bauen, schneller zur Laufzeit)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Nach jeder Änderung an [src/lib.rs](src/lib.rs) muss `maturin develop` neu laufen, bevor
|
|
96
|
+
die Python-Tests die Änderung sehen — **uv recompiliert kompilierte Projekte nicht
|
|
97
|
+
automatisch**.
|
|
98
|
+
|
|
99
|
+
### Tests
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
uv run pytest # Python-Tests (benötigt vorher: maturin develop)
|
|
103
|
+
uv run pytest --cov # mit Coverage
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Es gibt bewusst **keine** Rust-`#[cfg(test)]`-Tests: das Binding wird über die
|
|
107
|
+
pytest-Suite gegen die echte Extension geprüft (treuer als ein In-Process-Embedded-Test),
|
|
108
|
+
und die `validator`-Crate testen wir nicht nach (ADR 0001).
|
|
109
|
+
|
|
110
|
+
### Typprüfung (mypy-Config in pyproject.toml)
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
uv run mypy python # prüft Stubs/Python-Quelle (disallow_untyped_defs aktiv)
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Rust-Standardwerkzeuge
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
cargo fmt # formatieren
|
|
120
|
+
cargo clippy # Lints
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Wheels bauen
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
uv run maturin build --release # baut ein Wheel nach target/wheels/
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Einen neuen Validator hinzufügen
|
|
130
|
+
|
|
131
|
+
Ziel des Projekts ist breite Abdeckung der `validator`-Crate (siehe CONTEXT.md). Beim
|
|
132
|
+
Hinzufügen eines Validators die ADR-Prinzipien einhalten:
|
|
133
|
+
|
|
134
|
+
1. **Rust** ([src/lib.rs](src/lib.rs)): `#[pyfunction] fn validate_x(...) -> PyResult<bool>`
|
|
135
|
+
anlegen, an die passende `validator`-Trait-Methode delegieren, und im
|
|
136
|
+
`#[pymodule] _validator` per `wrap_pyfunction!` registrieren.
|
|
137
|
+
- **Format-Validator** (argumentlos): Signatur `f(value: String) -> PyResult<bool>`.
|
|
138
|
+
- **Constraint-Validator** (parametrisiert): pythonische Keyword-Parameter (ADR 0003);
|
|
139
|
+
Ergebnis bleibt `bool` (ADR 0002).
|
|
140
|
+
2. **Python-Oberfläche**: Funktion in [`__init__.py`](python/rusty_validator/__init__.py)
|
|
141
|
+
(`__all__`) und im Stub [`__init__.pyi`](python/rusty_validator/__init__.pyi) ergänzen.
|
|
142
|
+
3. **Tests** ([tests/](tests/)): parametrisierte `pytest`-Fälle (gültig **und** ungültig);
|
|
143
|
+
für den Typ-Vertrag auch einen `TypeError`-Fall (ADR 0004).
|
|
144
|
+
4. `maturin develop` ausführen, dann `pytest`.
|
|
145
|
+
5. **`graphify update .`** ausführen, um den Wissensgraphen aktuell zu halten.
|
|
146
|
+
|
|
147
|
+
## Release
|
|
148
|
+
|
|
149
|
+
- Die veröffentlichte Version steht in [Cargo.toml](Cargo.toml) (`[package].version`);
|
|
150
|
+
Python bezieht sie dynamisch daraus.
|
|
151
|
+
- Ein **Git-Tag** auslösen → [CI.yml](.github/workflows/CI.yml) baut Wheels für alle
|
|
152
|
+
Targets und published zu PyPI (`MATURIN_PYPI_TOKEN`).
|
|
153
|
+
|
|
154
|
+
## Codebase-Navigation (graphify)
|
|
155
|
+
|
|
156
|
+
Dieses Projekt hat einen Wissensgraphen unter `graphify-out/` (god nodes, Community-
|
|
157
|
+
Struktur, dateiübergreifende Beziehungen).
|
|
158
|
+
|
|
159
|
+
Regeln:
|
|
160
|
+
|
|
161
|
+
- Für Fragen zur Codebasis zuerst `graphify query "<frage>"` ausführen, wenn
|
|
162
|
+
`graphify-out/graph.json` existiert. `graphify path "<A>" "<B>"` für Beziehungen,
|
|
163
|
+
`graphify explain "<konzept>"` für fokussierte Konzepte. Diese liefern einen
|
|
164
|
+
begrenzten Subgraphen, meist deutlich kleiner als `GRAPH_REPORT.md` oder rohe
|
|
165
|
+
grep-Ausgabe.
|
|
166
|
+
- Existiert `graphify-out/wiki/index.md`, dieses zur groben Navigation nutzen statt
|
|
167
|
+
rohem Quelltext-Browsing.
|
|
168
|
+
- `graphify-out/GRAPH_REPORT.md` nur für breite Architektur-Reviews lesen oder wenn
|
|
169
|
+
query/path/explain nicht genug Kontext liefern.
|
|
170
|
+
- Nach Code-Änderungen `graphify update .` ausführen (nur AST, keine API-Kosten).
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Rusty Validator
|
|
2
|
+
|
|
3
|
+
Ein schmales Python-Binding der Rust-Crate `validator` (über PyO3). Es prüft, ob
|
|
4
|
+
ein Wert eine bestimmte Regel erfüllt — die Form einer E-Mail/URL/IP oder eine
|
|
5
|
+
Bedingung wie Länge oder Wertebereich — und gibt das Ergebnis als `bool` zurück.
|
|
6
|
+
Eigene Ergonomie (Decorators, Guards, Constraint-Objekte) gehört bewusst in ein
|
|
7
|
+
separates Paket, nicht hierher.
|
|
8
|
+
|
|
9
|
+
## Language
|
|
10
|
+
|
|
11
|
+
**Validieren / Validierung**:
|
|
12
|
+
Die Prüfung, ob ein Wert eine **definierte Regel** erfüllt. Das Ergebnis ist ein
|
|
13
|
+
**Prädikat**: für korrekt typisierte Eingaben immer ein `bool`, nie ein
|
|
14
|
+
Validierungs-*Fehler* — der „wirf bei ungültig"-Stil (Guard) lebt im separaten
|
|
15
|
+
Ergonomie-Paket, nicht hier. **Typ-falsche** Eingaben (z. B. `None`) sind ein
|
|
16
|
+
Programmierfehler und lösen wie üblich einen `TypeError` aus, kein stilles `False`.
|
|
17
|
+
Invariante: rein lokal — es wird nie geprüft, ob etwas real existiert oder
|
|
18
|
+
erreichbar ist (kein DNS, kein Netzwerk, keine IO).
|
|
19
|
+
*Avoid*: Prüfen auf Existenz, Verifizieren, Zustellbarkeit; Guard/Erzwingen (andere Operation)
|
|
20
|
+
|
|
21
|
+
**Format-Validator**:
|
|
22
|
+
Ein **argumentloser** Validator, der die **Form** einer Zeichenkette prüft:
|
|
23
|
+
`email`, `url`, `ip` (perspektivisch auch `credit_card`, `phone`,
|
|
24
|
+
`non_control_character`). Signatur stets `f(str) -> bool`.
|
|
25
|
+
|
|
26
|
+
**Constraint-Validator**:
|
|
27
|
+
Ein **parametrisierter** Validator, der eine **Bedingung** an einem Wert prüft:
|
|
28
|
+
`length`, `range`, `contains`, `must_match`. Braucht Argumente (z. B. Grenzen) und
|
|
29
|
+
arbeitet nicht zwingend auf Zeichenketten (`range` prüft Zahlen).
|
|
30
|
+
*Avoid*: Format-Validator (die andere Unterart)
|
|
31
|
+
|
|
32
|
+
**E-Mail-Adresse**:
|
|
33
|
+
Eine **einzelne**, wohlgeformte Adresse `lokalteil@domain` im Sinne von
|
|
34
|
+
`validator::ValidateEmail`. Rein syntaktisch — keine Existenz-/MX-Prüfung. **Keine**
|
|
35
|
+
Anzeigenamen-Form (`Max <max@x.de>`) und **keine** Liste mehrerer Adressen.
|
|
36
|
+
*Avoid*: E-Mail (zu unscharf, wenn die Adresse gemeint ist), Mail-Konto
|
|
37
|
+
|
|
38
|
+
**URL**:
|
|
39
|
+
Eine wohlgeformte **absolute** URL im Sinne von `validator::ValidateUrl` (RFC 3986),
|
|
40
|
+
also *mit* Schema und Host. Das Schema ist **nicht** auf Web beschränkt: `http`,
|
|
41
|
+
`https`, `ftp`, `mailto`, `file` und sogar erfundene Schemata gelten als gültig.
|
|
42
|
+
„example.com" (ohne Schema) ist hingegen keine gültige URL. Die Grenze definiert
|
|
43
|
+
allein die Crate — wir engen sie nicht auf http/https ein.
|
|
44
|
+
*Avoid*: URI, Link, Web-Adresse (suggeriert fälschlich „nur http/https")
|
|
45
|
+
|
|
46
|
+
**IP-Adresse**:
|
|
47
|
+
Eine wohlgeformte IP-Adresse im Sinne von `validator::ValidateIp` — **IPv4 oder
|
|
48
|
+
IPv6** sind beide eine IP-Adresse (`127.0.0.1` ebenso wie `::1`). Rein syntaktisch,
|
|
49
|
+
keine Erreichbarkeitsprüfung. **IPv4** und **IPv6** sind unterscheidbare Unterarten,
|
|
50
|
+
die getrennt prüfbar gemacht werden.
|
|
51
|
+
*Avoid*: Host, Hostname (das ist ein Name, keine Adresse)
|
|
52
|
+
|
|
53
|
+
## Beispieldialog
|
|
54
|
+
|
|
55
|
+
**Entwickler:** `validate_url("ftp://files.example.com")` gibt `True` — ist das ein Bug?
|
|
56
|
+
|
|
57
|
+
**Expertin:** Nein. Eine **URL** ist bei uns jede wohlgeformte absolute URL, egal
|
|
58
|
+
welches Schema. Wir reichen die `validator`-Crate treu durch und engen sie nicht auf
|
|
59
|
+
http/https ein.
|
|
60
|
+
|
|
61
|
+
**Entwickler:** Ich *will* aber nur Web-Adressen zulassen.
|
|
62
|
+
|
|
63
|
+
**Expertin:** Das ist eine eigene **Regel** über der Validierung — die gehört ins
|
|
64
|
+
Ergonomie-Paket, nicht ins Binding. Das Binding sagt nur „wohlgeformt: ja/nein".
|
|
65
|
+
|
|
66
|
+
**Entwickler:** Und `validate_length`? Das ist doch keine Form.
|
|
67
|
+
|
|
68
|
+
**Expertin:** Genau. `email`/`url`/`ip` sind **Format-Validatoren** (argumentlos,
|
|
69
|
+
prüfen die Form). `length`/`range`/`contains` sind **Constraint-Validatoren**
|
|
70
|
+
(parametrisiert, prüfen eine Bedingung). Beides ist **Validierung**, beides liefert
|
|
71
|
+
ein **Prädikat**.
|
|
72
|
+
|
|
73
|
+
**Entwickler:** Und wenn ich `validate_ip(None)` übergebe?
|
|
74
|
+
|
|
75
|
+
**Expertin:** `TypeError` — ein Programmierfehler, kein „ungültig". Ein kaputter
|
|
76
|
+
*String* gibt `False`; ein **falscher Typ** wirft. Und wenn bei Ungültigkeit ein
|
|
77
|
+
Fehler fliegen soll, brauchst du einen **Guard** — den gibt es hier nicht, er lebt
|
|
78
|
+
im Ergonomie-Paket.
|
|
79
|
+
|
|
80
|
+
**Entwickler:** `validate_ip("::1")`?
|
|
81
|
+
|
|
82
|
+
**Expertin:** `True` — eine **IP-Adresse** ist v4 *oder* v6. Willst du gezielt nur
|
|
83
|
+
v6, nimm `validate_ipv6`.
|