Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ jobs:
- name: Install MkDocs and theme
run: pip install --require-hashes -r docs/requirements.txt

- name: Check the natives page generator
run: |
python3 scripts/gen_natives_page.py --selftest
python3 scripts/gen_natives_page.py --check

- name: Strict build (fails on warnings)
run: mkdocs build --strict

Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@
CLAUDE.md
/dist
/reference
/notes

# MkDocs
/site
# Built from the includes on every docs build; see scripts/gen_natives_page.py
/docs/api-reference.md
/.venv
__pycache__/
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ Built on rust-samp v3.5.0, now from crates.io. Additive on the Pawn side: four n
- **rust-samp moves from a git tag to crates.io** (`version = "3.5.0"`). This also clears a long-standing oddity: the `v3.5.0` tag declared `version = "3.4.0"` in its own manifest, so the lockfile recorded 3.4.0 pointing at the v3.5.0 tag. The SDK's new `mainthread` module is **not** adopted yet: its `post` takes a closure with no parameters and there is no route to plugin state from a worker thread, so the plugin's own `mpsc` channel stays until `post_with` / `post_with_amx` land.

- **`mysql` 28.0.0 → 28.0.2**, plus a large round of transitive updates. (#38, #45, and the Dependabot group)
- Routine dependency and action bumps: the `github-actions` group in three rounds, `pymdown-extensions` in the docs requirements, and the cargo group — `lru`, `sha1`, `num-bigint`, `wasip2`, `windows-sys` and `derive_utils` among them. (#39, #40, #44–#54)

### CI / tooling

Expand All @@ -63,6 +64,14 @@ Built on rust-samp v3.5.0, now from crates.io. Additive on the Pawn side: four n

### Documentation

- **The API reference is generated from the includes.** [`api-reference.md`](https://nullsablex.github.io/mysql_samp/api-reference/) was a hand-written set of tables listing every native, its type and a one-line description — the same information the includes already carry beside each declaration, kept in step by hand. It is now produced by `scripts/gen_natives_page.py` on every docs build: natives, the forward, the documented constants and the enumerations with their values, each with its signature, its open.mp alias, its parameters and its return. The page keeps its URL, and the hand-maintained copy is gone. Two comment formats are read: JavaDoc, which the includes here use, and pawndoc (`<summary>`, `<param name="">`, `<returns>`), which a project is likely to meet in includes it pulls in. A `--selftest` covers both against fixtures, and `--check` fails the docs build if an entry has no documentation block.

- **`@example` in a doc block becomes a code block on the page**, and the three natives that accept `MYSQL_SYNC` now carry one, so how to make a call blocking is visible where the native is documented instead of only on the queries page. Native names inside comments are rewritten when the open.mp include is generated, so the same example reads `mysql_query` in one file and `MySQL_Query` in the other.

- **The documentation site carries a database icon** in the header and in the browser tab, in place of the theme's default cloud. The SVG is committed rather than pulled from the theme at build time, so a theme update cannot change it underneath.

- **The includes say more where it counts.** The two prepared-statement natives state that they do not accept `MYSQL_SYNC` rather than leaving it unsaid, and `MYSQL_SAMP_VERSION` carries a doc block explaining that `build.rs` stamps it from `Cargo.toml` and that comparing it at start-up is how a gamemode catches an include left behind by an upgrade. Both reach an editor's hover as well as the page.

- **The callback is optional, and the docs now say so where people read.** Nothing forces a callback on a write: `mysql_query(conn, "UPDATE …")` is one line. That was documented in a subsection at the bottom of one page while the opening sentence and all eleven examples implied the opposite, which is where the complaint that the plugin "makes you write callbacks for everything" came from. (#55)

- **FIFO orders callbacks, not execution.** `mysql_query` dispatches callbacks in submission order, but each statement runs on its own connection, concurrently — a `CREATE TABLE` followed by an `INSERT` races and the insert fails. The comparison table recommended `mysql_query` for exactly that. It now points at `mysql_query_file` and transactions, the two things that actually serialise, and two examples were added for the callback-free and ordering cases. (#55)
Expand Down
19 changes: 19 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,25 @@ greps for errors reports success for a file that never compiled.
in the signature. It is a cross-check by hand, not a replacement for the
`.inc.in`, which carries the Pawn tags, the enums and the JavaDoc that the
generated file has no way to know about.
- **The API reference is generated, not written.** `docs/api-reference.md` is built
from the includes by `scripts/gen_natives_page.py` on every docs build, so
it is not in the repository and editing it is pointless. What reaches that
page is the JavaDoc block in `include/mysql_samp.inc.in` - including
`@example`, which becomes a code block. Native names inside a comment are
rewritten for the open.mp include, so an example can name a native and still
read correctly in both files.

```bash
python3 scripts/gen_natives_page.py # write docs/api-reference.md
python3 scripts/gen_natives_page.py --check # fail if an entry has no doc block
python3 scripts/gen_natives_page.py --selftest # parser fixtures, JavaDoc and pawndoc
```

Both comment formats are understood: JavaDoc (`@param`, `@return`) and
pawndoc (`<summary>`, `<param name="">`, `<returns>`). The includes here use
JavaDoc; pawndoc is covered because a project is likely to pull in includes
that use it.

- **Pawn sources are pure ASCII.** No accents, no typographic dashes, no curly
quotes — the compiler reads bytes, and mojibake reaches the player instead of
failing the build. Markdown is the exception, and the CI guard enforces the
Expand Down
64 changes: 61 additions & 3 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,13 +110,36 @@ fn generate_omp_inc(base_rendered: &str) {
None => base_rendered,
};

// Every base name and the styled name it becomes, longest first so that
// `mysql_query_file` is rewritten before `mysql_query` can match its
// prefix. Needed because doc comments carry usage examples, and an
// example naming `mysql_query` would be wrong in a file where the native
// is called `MySQL_Query`.
let mut renames: Vec<(String, String)> = body
.lines()
.filter_map(|line| {
let rest = line.trim().strip_prefix("native ")?;
let open = rest.find('(')?;
let head = rest[..open].trim();
let name = head.rsplit(':').next()?.trim();
Some((name.to_string(), to_omp_name(name)))
})
.collect();
renames.sort_by_key(|(base, _)| std::cmp::Reverse(base.len()));

for line in body.lines() {
let trimmed = line.trim();

let Some(rest) = trimmed.strip_prefix("native ") else {
// Not a native declaration - copy the line as-is (enum, forward,
// define, comment, blank).
out.push_str(line);
// Not a native declaration. Comments still need the names inside
// them rewritten; everything else is copied verbatim.
let is_comment =
trimmed.starts_with('*') || trimmed.starts_with("/*") || trimmed.starts_with("//");
if is_comment {
out.push_str(&rewrite_names(line, &renames));
} else {
out.push_str(line);
}
out.push('\n');
continue;
};
Expand Down Expand Up @@ -147,6 +170,41 @@ fn generate_omp_inc(base_rendered: &str) {
}
}

/// Replaces whole identifiers inside a line, leaving partial matches alone.
///
/// A plain `str::replace` would turn `mysql_query_file` into
/// `MySQL_Query_file` when rewriting `mysql_query`, so each hit is only taken
/// when the characters around it cannot be part of an identifier.
fn rewrite_names(line: &str, renames: &[(String, String)]) -> String {
let mut out = line.to_string();

for (base, styled) in renames {
let mut from = 0;
while let Some(found) = out[from..].find(base.as_str()) {
let start = from + found;
let end = start + base.len();

let before_ok = out[..start]
.chars()
.next_back()
.is_none_or(|c| !c.is_alphanumeric() && c != '_');
let after_ok = out[end..]
.chars()
.next()
.is_none_or(|c| !c.is_alphanumeric() && c != '_');

if before_ok && after_ok {
out.replace_range(start..end, styled);
from = start + styled.len();
} else {
from = end;
}
}
}

out
}

/// `mysql_stmt_new` -> `MySQL_StmtNew`, `cache_get_row_count` ->
/// `Cache_GetRowCount`, `orm_create` -> `ORM_Create`.
///
Expand Down
Loading
Loading