Skip to content

Commit e650330

Browse files
committed
Cleanup code
1 parent 090530d commit e650330

4 files changed

Lines changed: 92 additions & 29 deletions

File tree

crates/vespera_macro/src/openapi_generator/paths.rs

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,14 @@ pub(super) fn build_path_items(
9494
for (idx, route_meta) in metadata.routes.iter().enumerate() {
9595
// ROUTE_STORAGE first (avoids file_cache dependency for known
9696
// routes) — same priority order as the previous sequential code.
97+
//
98+
// `normalize_path_key` canonicalises the path (allocates + folds
99+
// `.`/`..` components + display-renders + Windows case-folds), so
100+
// compute it ONCE per route and reuse the owned key: the storage
101+
// lookup takes it by reference, and on a storage miss the `fn_index`
102+
// fallback MOVES the same `String` out of `storage_key.0` instead of
103+
// recomputing it. The prior code ran the full normalization twice
104+
// per route.
97105
let storage_key = (
98106
Some(normalize_path_key(&route_meta.file_path, &cwd)),
99107
route_meta.function_name.as_str(),
@@ -106,7 +114,8 @@ pub(super) fn build_path_items(
106114
.or_else(|| storage_fn_sigs.get(&legacy_storage_key).copied().flatten())
107115
{
108116
parallel_jobs.push((idx, route_meta, fn_sig_str));
109-
} else if let Some(fns) = fn_index.get(&normalize_path_key(&route_meta.file_path, &cwd))
117+
} else if let Some(norm_key) = storage_key.0
118+
&& let Some(fns) = fn_index.get(&norm_key)
110119
&& let Some(fn_item) = fns.get(&route_meta.function_name)
111120
{
112121
ast_jobs.push((idx, route_meta, &fn_item.sig));
@@ -186,21 +195,19 @@ fn build_storage_fn_sigs<'a>(
186195
) -> StorageFnSigs<'a> {
187196
let mut storage = HashMap::with_capacity(route_storage.len());
188197
for s in route_storage {
189-
let already_in_ast = s
190-
.file_path
191-
.as_deref()
192-
.map(|fp| normalize_path_key(fp, cwd))
193-
.and_then(|fp| fn_index.get(&fp))
198+
// Canonicalise the stored path ONCE per route (it allocates + folds
199+
// path components + display-renders) and reuse it for both the
200+
// `already_in_ast` skip check (by reference) and the storage key (by
201+
// move) — the prior code ran the full normalization twice per route.
202+
let norm_fp = s.file_path.as_deref().map(|fp| normalize_path_key(fp, cwd));
203+
let already_in_ast = norm_fp
204+
.as_ref()
205+
.and_then(|fp| fn_index.get(fp))
194206
.is_some_and(|fns| fns.contains_key(&s.fn_name));
195207
if already_in_ast {
196208
continue;
197209
}
198-
let key = (
199-
s.file_path
200-
.as_deref()
201-
.map(|path| normalize_path_key(path, cwd)),
202-
s.fn_name.as_str(),
203-
);
210+
let key = (norm_fp, s.fn_name.as_str());
204211
storage
205212
.entry(key)
206213
.and_modify(|slot| *slot = None)

libs/vespera-bridge-gradle-plugin/src/main/kotlin/kr/devfive/vespera/VesperaBridgeExtension.kt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,25 @@ abstract class VesperaBridgeExtension {
5757
* Java build to invoke cargo implicitly.
5858
*/
5959
abstract val autoBuildCargo: Property<Boolean>
60+
61+
/**
62+
* Cargo build profile selecting both the output subdirectory and the
63+
* build flag. `"release"` (default) → `cargo build --release` →
64+
* `target/release/`; `"dev"` (or `"debug"`) → plain `cargo build` →
65+
* `target/debug/`; any other `"<p>"` → `cargo build --profile <p>` →
66+
* `target/<p>/`. Lets debug or custom-profile cdylibs be bundled
67+
* without hand-editing the plugin (the previous hardcoded `--release`
68+
* forced every consumer onto the release profile).
69+
*/
70+
abstract val cargoProfile: Property<String>
71+
72+
/**
73+
* Base Cargo target directory that holds the per-profile output
74+
* subdirectory. Defaults to `<cargoRoot>/target`. Set this when the
75+
* workspace redirects Cargo output via `CARGO_TARGET_DIR` or a
76+
* `.cargo/config.toml` `build.target-dir`, so the plugin locates the
77+
* cdylib (and, when `autoBuildCargo` is on, directs cargo's output)
78+
* at the right place instead of failing on the default `target/`.
79+
*/
80+
abstract val targetDir: DirectoryProperty
6081
}

libs/vespera-bridge-gradle-plugin/src/main/kotlin/kr/devfive/vespera/VesperaBridgePlugin.kt

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,18 +42,24 @@ class VesperaBridgePlugin : Plugin<Project> {
4242
.create("vespera", VesperaBridgeExtension::class.java)
4343
ext.autoBuildCargo.convention(false)
4444
ext.cargoSourceRoots.convention(listOf("src", "crates", "examples"))
45+
ext.cargoProfile.convention("release")
4546

4647
// Compute platform-derived values eagerly (host machine info).
4748
val os = detectOs()
4849
val arch = detectArch()
4950
val generatedResourcesDir = project.layout.buildDirectory.dir("generated/vesperaNativeResources")
5051
val targetSubdir = "native/$os-$arch"
5152

52-
// Lazy file references — evaluated at task execution.
53+
// Lazy file references — evaluated at task execution. The cdylib
54+
// lives under `<targetDir|cargoRoot/target>/<profileDir>/`, so a
55+
// debug / custom-profile build or a redirected CARGO_TARGET_DIR is
56+
// located correctly instead of being hardcoded to `target/release/`.
5357
val cdylibFile = project.provider {
54-
val root = ext.cargoRoot.get().asFile
5558
val name = ext.crateName.get()
56-
File(root, "target/release/" + mapLibraryName(os, name))
59+
val targetBase =
60+
if (ext.targetDir.isPresent) ext.targetDir.get().asFile
61+
else File(ext.cargoRoot.get().asFile, "target")
62+
File(targetBase, profileDir(ext.cargoProfile.get()) + "/" + mapLibraryName(os, name))
5763
}
5864

5965
val cargoBuildTask = project.tasks.register(
@@ -64,7 +70,24 @@ class VesperaBridgePlugin : Plugin<Project> {
6470
t.group = "vespera"
6571
t.description = "Build the Rust cdylib via `cargo build --release`."
6672
t.workingDir = ext.cargoRoot.get().asFile
67-
t.commandLine("cargo", "build", "-p", ext.crateName.get(), "--release")
73+
// Profile-aware command: `release` → `--release`, `dev`/
74+
// `debug` → default build, any other → `--profile <p>`.
75+
val profile = ext.cargoProfile.get()
76+
val cmd = mutableListOf("cargo", "build", "-p", ext.crateName.get())
77+
when (profile) {
78+
"release" -> cmd.add("--release")
79+
"dev", "debug" -> {} // default profile → target/debug
80+
else -> { cmd.add("--profile"); cmd.add(profile) }
81+
}
82+
t.commandLine(cmd)
83+
// Honour a redirected target dir so cargo writes where
84+
// `bundleNativeLib` later looks for the cdylib.
85+
if (ext.targetDir.isPresent) {
86+
t.environment(
87+
"CARGO_TARGET_DIR",
88+
ext.targetDir.get().asFile.absolutePath,
89+
)
90+
}
6891
// Up-to-date check: re-run on workspace manifests, Cargo.lock,
6992
// and Rust sources in configured roots. This repository keeps
7093
// Rust code under crates/* and examples/*, not only src/.
@@ -97,8 +120,12 @@ class VesperaBridgePlugin : Plugin<Project> {
97120
val src = cdylibFile.get()
98121
require(src.exists()) {
99122
"Native library not found: $src\n" +
100-
"Run: cargo build -p ${ext.crateName.get()} --release " +
101-
"(or set vespera.autoBuildCargo = true)"
123+
"Build the '${ext.crateName.get()}' cdylib for the " +
124+
"'${ext.cargoProfile.get()}' profile (or set " +
125+
"vespera.autoBuildCargo = true). If the workspace " +
126+
"redirects Cargo output (CARGO_TARGET_DIR / " +
127+
".cargo/config.toml build.target-dir), set " +
128+
"vespera.targetDir to that directory."
102129
}
103130
}
104131
})
@@ -167,4 +194,14 @@ class VesperaBridgePlugin : Plugin<Project> {
167194
"macos" -> "lib$name.dylib"
168195
else -> "lib$name.so"
169196
}
197+
198+
/**
199+
* Map a Cargo profile name to its `target/` output subdirectory.
200+
* Cargo's built-in `dev` profile emits to `debug`; every other profile
201+
* (`release`, or a custom `[profile.X]`) uses its own name verbatim.
202+
*/
203+
private fun profileDir(profile: String): String = when (profile) {
204+
"dev", "debug" -> "debug"
205+
else -> profile
206+
}
170207
}

libs/vespera-bridge/src/main/java/com/devfive/vespera/bridge/WireHeaderReader.java

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -502,17 +502,15 @@ String readString() {
502502
// decode loop below.
503503
int simpleLen = simpleAsciiRun();
504504
if (simpleLen >= 0) {
505-
String s;
506-
if (buf.hasArray()) {
507-
// Heap-backed buffer (ByteBuffer.wrap on the SYNC / streaming
508-
// / async paths): build the String straight from the backing
509-
// array — one copy, no intermediate byte[]. Direct buffers
510-
// (the DIRECT dispatch path) have no accessible array and keep
511-
// the absolute bulk-get copy below.
512-
s = WireHeaderStringSupport.readAsciiString(buf, pos, simpleLen);
513-
} else {
514-
s = WireHeaderStringSupport.readAsciiString(buf, pos, simpleLen);
515-
}
505+
// `readAsciiString` already branches on `buf.hasArray()` itself:
506+
// heap-backed buffers (SYNC / streaming / async, ByteBuffer.wrap)
507+
// build the String straight from the backing array (one copy, no
508+
// intermediate byte[]), while direct buffers (the DIRECT dispatch
509+
// path) fall back to a pooled-scratch bulk-get — so this single
510+
// call is already optimal for both buffer kinds. The previous outer
511+
// `if (buf.hasArray()) ... else ...` invoked the identical call in
512+
// both arms (dead branch); collapsed here.
513+
String s = WireHeaderStringSupport.readAsciiString(buf, pos, simpleLen);
516514
pos += simpleLen + 1; // consume the run + the closing quote
517515
return s;
518516
}

0 commit comments

Comments
 (0)