max / makenotwork
- Co-Authored-By
- Claude Opus 4.7 (1M context) <noreply@anthropic.com>
23 files changed,
+617 insertions,
-68 deletions
| @@ -1,6 +1,6 @@ | |||
| 1 | 1 | # tagtree | |
| 2 | 2 | ||
| 3 | - | Hierarchical dot-notation tag standard for Rust. Validation, parsing, tree operations, SQL helpers, and autocomplete — with zero runtime dependencies. | |
| 3 | + | Hierarchical dot-notation tag standard for Rust. Validation, parsing, tree operations, SQL helpers, and autocomplete, with zero runtime dependencies. | |
| 4 | 4 | ||
| 5 | 5 | ## Tag format | |
| 6 | 6 | ||
| @@ -28,43 +28,43 @@ | |||
| 28 | 28 | const GO_TAGS: TagConfig = TagConfig { max_depth: 3, max_length: 60, semantic_depth: 0 }; | |
| 29 | 29 | ``` | |
| 30 | 30 | ||
| 31 | - | - `max_depth` — maximum number of segments | |
| 32 | - | - `max_length` — maximum character length of the entire tag | |
| 33 | - | - `semantic_depth` — number of leading segments that carry dispatch meaning (0 = free-form) | |
| 31 | + | - `max_depth`: maximum number of segments | |
| 32 | + | - `max_length`: maximum character length of the entire tag | |
| 33 | + | - `semantic_depth`: number of leading segments that carry dispatch meaning (0 = free-form) | |
| 34 | 34 | ||
| 35 | 35 | ## API | |
| 36 | 36 | ||
| 37 | 37 | ### Validation | |
| 38 | 38 | ||
| 39 | - | - `validate_with(tag, config)` — validate against a `TagConfig` | |
| 40 | - | - `validate(tag)` — validate with defaults (depth 5, length 100, no semantic prefix) | |
| 39 | + | - `validate_with(tag, config)`: validate against a `TagConfig` | |
| 40 | + | - `validate(tag)`: validate with defaults (depth 5, length 100, no semantic prefix) | |
| 41 | 41 | ||
| 42 | 42 | ### Parsing | |
| 43 | 43 | ||
| 44 | - | - `parent(tag)` — `"a.b.c"` -> `Some("a.b")` | |
| 45 | - | - `leaf(tag)` — `"a.b.c"` -> `"c"` | |
| 46 | - | - `depth(tag)` — `"a.b.c"` -> `3` | |
| 47 | - | - `segment(tag, i)` — extract segment by index | |
| 48 | - | - `prefix_at_depth(tag, n)` — first `n` segments | |
| 49 | - | - `ancestors(tag)` — `"a.b.c"` -> `["a", "a.b"]` | |
| 44 | + | - `parent(tag)`: `"a.b.c"` -> `Some("a.b")` | |
| 45 | + | - `leaf(tag)`: `"a.b.c"` -> `"c"` | |
| 46 | + | - `depth(tag)`: `"a.b.c"` -> `3` | |
| 47 | + | - `segment(tag, i)`: extract segment by index | |
| 48 | + | - `prefix_at_depth(tag, n)`: first `n` segments | |
| 49 | + | - `ancestors(tag)`: `"a.b.c"` -> `["a", "a.b"]` | |
| 50 | 50 | ||
| 51 | 51 | ### Tree operations | |
| 52 | 52 | ||
| 53 | - | - `is_ancestor_of(a, b)` — true if `a` is a prefix of `b` | |
| 54 | - | - `common_ancestor(a, b)` — longest shared prefix | |
| 55 | - | - `children_at_prefix(prefix, tags)` — direct children one level below prefix | |
| 56 | - | - `subtree(prefix, tags)` — all descendants of prefix | |
| 57 | - | - `rename_prefix(old, new, tag)` — swap a tag's prefix | |
| 53 | + | - `is_ancestor_of(a, b)`: true if `a` is a prefix of `b` | |
| 54 | + | - `common_ancestor(a, b)`: longest shared prefix | |
| 55 | + | - `children_at_prefix(prefix, tags)`: direct children one level below prefix | |
| 56 | + | - `subtree(prefix, tags)`: all descendants of prefix | |
| 57 | + | - `rename_prefix(old, new, tag)`: swap a tag's prefix | |
| 58 | 58 | ||
| 59 | 59 | ### Semantic splitting | |
| 60 | 60 | ||
| 61 | - | - `semantic_prefix(tag, depth)` — namespace portion (`"genre.rock"` with depth 1 -> `"genre"`) | |
| 62 | - | - `free_suffix(tag, depth)` — value portion (`"genre.rock"` with depth 1 -> `"rock"`) | |
| 61 | + | - `semantic_prefix(tag, depth)`: namespace portion (`"genre.rock"` with depth 1 -> `"genre"`) | |
| 62 | + | - `free_suffix(tag, depth)`: value portion (`"genre.rock"` with depth 1 -> `"rock"`) | |
| 63 | 63 | ||
| 64 | 64 | ### SQL helpers | |
| 65 | 65 | ||
| 66 | - | - `escape_like(s)` — escape `%`, `_`, `\` for safe embedding in `LIKE` patterns | |
| 67 | - | - `like_descendant_pattern(prefix)` — build `prefix.%` pattern for hierarchy queries | |
| 66 | + | - `escape_like(s)`: escape `%`, `_`, `\` for safe embedding in `LIKE` patterns | |
| 67 | + | - `like_descendant_pattern(prefix)`: build `prefix.%` pattern for hierarchy queries | |
| 68 | 68 | ||
| 69 | 69 | Works identically on SQLite and PostgreSQL. | |
| 70 | 70 |
| @@ -232,7 +232,7 @@ | |||
| 232 | 232 | /// Sets status to 'running' and started_at in a single UPDATE with a subquery, | |
| 233 | 233 | /// eliminating the TOCTOU race between checking for running builds and fetching | |
| 234 | 234 | /// a pending one. `FOR UPDATE SKIP LOCKED` means concurrent callers never block | |
| 235 | - | /// — the loser simply gets no row. | |
| 235 | + | ///; the loser simply gets no row. | |
| 236 | 236 | #[tracing::instrument(skip_all)] | |
| 237 | 237 | pub async fn claim_pending_build(pool: &PgPool) -> Result<Option<DbBuild>> { | |
| 238 | 238 | let build = sqlx::query_as::<_, DbBuild>( |
| @@ -144,7 +144,7 @@ | |||
| 144 | 144 | /// Replace the full set of items in a bundle (transactional). | |
| 145 | 145 | /// | |
| 146 | 146 | /// Deletes all existing bundle_items rows for the bundle and inserts the new set. | |
| 147 | - | /// `item_ids` is an ordered list — sort_order is derived from position. | |
| 147 | + | /// `item_ids` is an ordered list; sort_order is derived from position. | |
| 148 | 148 | /// Validates that both the bundle and all items belong to `owner_id`. | |
| 149 | 149 | #[tracing::instrument(skip_all)] | |
| 150 | 150 | pub async fn set_bundle_items( |
| @@ -17,7 +17,7 @@ | |||
| 17 | 17 | Ok(row) | |
| 18 | 18 | } | |
| 19 | 19 | ||
| 20 | - | /// Record a suppressed email address. Idempotent — does nothing if already suppressed. | |
| 20 | + | /// Record a suppressed email address. Idempotent; does nothing if already suppressed. | |
| 21 | 21 | #[tracing::instrument(skip_all)] | |
| 22 | 22 | pub async fn add_suppression(pool: &PgPool, email: &str, reason: &str) -> Result<()> { | |
| 23 | 23 | sqlx::query( |
| @@ -10,7 +10,7 @@ | |||
| 10 | 10 | use super::UserId; | |
| 11 | 11 | use crate::error::Result; | |
| 12 | 12 | ||
| 13 | - | /// Follow a target (user or project). Idempotent — does nothing if already following. | |
| 13 | + | /// Follow a target (user or project). Idempotent; does nothing if already following. | |
| 14 | 14 | #[tracing::instrument(skip_all)] | |
| 15 | 15 | pub async fn follow( | |
| 16 | 16 | pool: &PgPool, |
| @@ -152,7 +152,7 @@ | |||
| 152 | 152 | /// Returns `None` if the activation limit has been reached. | |
| 153 | 153 | /// | |
| 154 | 154 | /// After the upsert, the denormalized `activation_count` on `license_keys` | |
| 155 | - | /// is refreshed with a full COUNT rather than an increment — this avoids | |
| 155 | + | /// is refreshed with a full COUNT rather than an increment; this avoids | |
| 156 | 156 | /// drift if a crash leaves the count out of sync. | |
| 157 | 157 | #[tracing::instrument(skip_all)] | |
| 158 | 158 | pub async fn try_create_activation( | |
| @@ -284,7 +284,7 @@ | |||
| 284 | 284 | /// Revoke a license key and deactivate all its activations. | |
| 285 | 285 | /// | |
| 286 | 286 | /// Wrapped in a transaction so the key revocation and activation | |
| 287 | - | /// deactivation are atomic — a crash between the two statements | |
| 287 | + | /// deactivation are atomic; a crash between the two statements | |
| 288 | 288 | /// cannot leave the key revoked with activations still active. | |
| 289 | 289 | #[tracing::instrument(skip_all)] | |
| 290 | 290 | pub async fn revoke_license_key(pool: &PgPool, key_id: LicenseKeyId) -> Result<()> { |
| @@ -1,7 +1,7 @@ | |||
| 1 | 1 | //! Page view tracking with daily aggregation. | |
| 2 | 2 | //! | |
| 3 | 3 | //! Each page view UPSERTs into `page_view_daily`, incrementing a counter per | |
| 4 | - | //! (target_type, target_id, date). No raw per-request rows — the table stays | |
| 4 | + | //! (target_type, target_id, date). No raw per-request rows; the table stays | |
| 5 | 5 | //! small (365 rows/item/year). | |
| 6 | 6 | ||
| 7 | 7 | use chrono::{DateTime, Utc}; |
| @@ -11,7 +11,7 @@ | |||
| 11 | 11 | use crate::error::Result; | |
| 12 | 12 | ||
| 13 | 13 | /// Insert a pending refund for later matching. Deduplicates on | |
| 14 | - | /// `payment_intent_id` — if a pending (unmatched) refund already exists | |
| 14 | + | /// `payment_intent_id`; if a pending (unmatched) refund already exists | |
| 15 | 15 | /// for this payment intent, the insert is silently skipped. | |
| 16 | 16 | pub async fn insert_pending_refund( | |
| 17 | 17 | pool: &PgPool, |
| @@ -893,7 +893,7 @@ | |||
| 893 | 893 | } | |
| 894 | 894 | ||
| 895 | 895 | /// Mark a user as a founder. Called when they start a creator-tier | |
| 896 | - | /// subscription while the founder pricing window is open. Sticky — never | |
| 896 | + | /// subscription while the founder pricing window is open. Sticky; never | |
| 897 | 897 | /// reset, even on cancellation. Subsequent re-subscriptions during the | |
| 898 | 898 | /// window keep their founder status. After the window closes, eligibility | |
| 899 | 899 | /// is determined by `founder_locked_at` (stamped only for users with an |