A small Rust library that decides whether a piece of text from an address bar should be treated as:
- Navigate → open as a URL
- Search → send to the search engine
The logic is deterministic and tries to stay minimal so it can be audited and reused across platforms.
Browsers and apps often need to guess what the user meant when typing into the omnibox. For example:
example.com→ Navigatewhat is my ip→ Search
This crate provides a reference implementation with a configurable Policy struct so each app can tweak behavior.
- Distinguishes between **Navigate / Search
- Handles schemes (
https://,ftp://,edge://, etc.) - Understands hostnames, localhost, IPs, intranet single-labels
- Optional integration with the Public Suffix List (via the
real-pslfeature) - Cross-platform FFI (Android/iOS/Windows)
Behavior is tuned via a Policy:
#[derive(Serialize, Deserialize)]
pub struct Policy {
pub allow_intranet_single_label: bool,
pub allow_private_suffix: bool,
pub allowed_schemes: BTreeSet<String>,
}Example (Rust):
let mut policy = Policy::default();
policy.allow_intranet_single_label = true;
let decision = classify("duck ai translate hello", &policy);pub enum Decision {
Navigate { url: String },
Search { query: String },
}- Rust → use
classify(&str, &Policy)directly - C/FFI → call
ddg_up_classify_json, which returns JSON-encodedDecision - Android (JNI) →
UrlPredictor.classify(input)in Kotlin - iOS → expose
ddg_up_classify_jsonvia a bridging header and wrap it in Swift - Windows → link against the
.dlland callddg_up_classify_json
ddg_up_classify_json returns a heap-allocated string.
Call ddg_up_free_string(ptr) once you’re done with it to avoid memory leaks.
Example in C:
char* result = ddg_up_classify_json(input, policy_json);
printf("%s\n", result);
ddg_up_free_string(result); // free it!Default (uses a small demo suffix DB):
cargo buildWith real PSL:
cargo build --features real-pslThis repo ships helper scripts under scripts/ to cross-compile the Rust core into
platform-specific libraries:
-
Android:
scripts/build-android.sh
Producesliburl_predictor.sofor all standard ABIs and drops them underandroid/ddg-url-predictor/src/main/jniLibs. -
iOS/macOS:
scripts/build-ios.sh,scripts/build-macos.sh
Produces.a/.dylibartifacts for Xcode integration. Requires full Xcode installation (xcode-select). -
Windows:
scripts/build-windows.sh
Producesurl_predictor.dllfor MSVC targets.
Outputs land under dist/ by default. These aren’t checked into git — run the scripts yourself.
Run the Rust test suite:
cargo testOr run tests using the real PSL:
cargo test --features real-psl- The included
DemoSuffixDbis intentionally tiny. For production, enable thereal-pslfeature and ship a PSL file. - The project does not do DNS or network lookups. Everything is local and deterministic.
- Error cases (like bad policy JSON) fall back to
Policy::default().
If you’re calling from Android, the Kotlin helper wraps the JNI call and returns a type-safe Decision:
object UrlPredictor {
init { System.loadLibrary("url_predictor") }
// JNI call into Rust
@JvmStatic private external fun ddgClassifyJni(input: String, policyJson: String): String
// Type-safe API
@JvmStatic
fun classify(input: String, policy: DecisionJson.Policy = DecisionJson.Policy()): Decision {
val policyJson = DecisionJson.encodePolicy(policy)
val decisionJson = ddgClassifyJni(input, policyJson)
return DecisionJson.decodeDecision(decisionJson)
}
}Usage:
val result = UrlPredictor.classify("duck ai hello world")
when (result) {
is Decision.Navigate -> println("Navigate to ${result.url}")
is Decision.Search -> println("Search for ${result.query}")
}