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-psl
feature) - 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_json
via a bridging header and wrap it in Swift - Windows → link against the
.dll
and 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 build
With real PSL:
cargo build --features real-psl
This repo ships helper scripts under scripts/
to cross-compile the Rust core into
platform-specific libraries:
-
Android:
scripts/build-android.sh
Producesliburl_predictor.so
for 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
/.dylib
artifacts for Xcode integration. Requires full Xcode installation (xcode-select
). -
Windows:
scripts/build-windows.sh
Producesurl_predictor.dll
for MSVC targets.
Outputs land under dist/
by default. These aren’t checked into git — run the scripts yourself.
Run the Rust test suite:
cargo test
Or run tests using the real PSL:
cargo test --features real-psl
- The included
DemoSuffixDb
is intentionally tiny. For production, enable thereal-psl
feature 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}")
}