Make geos+litegis an optional build dependency for the core. Also remove wasm as a default feature to make breakages more apparent (also fixed an issue).

This commit is contained in:
Sebastian Jeltsch 2026-02-20 20:00:09 +01:00
parent b56ce4b544
commit 617045cbd0
13 changed files with 39 additions and 86 deletions

View file

@ -67,7 +67,7 @@ jobs:
-e CARGO_PROFILE_RELEASE_CODEGEN_UNITS=${{ env.CARGO_PROFILE_RELEASE_CODEGEN_UNITS }} \
-e PNPM_OFFLINE=FALSE \
builder:latest \
/root/.cargo/bin/cargo build --target ${{ env.RUST_TARGET }} --release --features=static-geos --bin trail
/root/.cargo/bin/cargo build --target ${{ env.RUST_TARGET }} --release --features=geos-static --bin trail
- name: Package Artifacts
run: |
@ -113,7 +113,7 @@ jobs:
-e CARGO_PROFILE_RELEASE_CODEGEN_UNITS=${{ env.CARGO_PROFILE_RELEASE_CODEGEN_UNITS }} \
-e PNPM_OFFLINE=FALSE \
builder:latest \
/root/.cargo/bin/cargo build --target ${{ env.RUST_TARGET }} --release --features=static-geos --bin trail
/root/.cargo/bin/cargo build --target ${{ env.RUST_TARGET }} --release --features=geos-static --bin trail
- name: Package Artifacts
run: |
@ -148,7 +148,7 @@ jobs:
- name: Rust Build
run: |
env && \
cargo build --target ${{ env.RUST_TARGET }} --release --features=static-geos --bin trail
cargo build --target ${{ env.RUST_TARGET }} --release --features=geos-static --bin trail
- name: Package Artifacts
run: |
zip -r -j trailbase_${{ github.ref_name }}_arm64_apple_darwin.zip target/${{ env.RUST_TARGET }}/release/trail CHANGELOG.md LICENSE
@ -182,7 +182,7 @@ jobs:
- name: Rust Build
run: |
env && \
cargo build --target ${{ env.RUST_TARGET }} --release --features=static-geos --bin trail
cargo build --target ${{ env.RUST_TARGET }} --release --features=geos-static --bin trail
- name: Package Artifacts
run: |
zip -r -j trailbase_${{ github.ref_name }}_x86_64_apple_darwin.zip target/${{ env.RUST_TARGET }}/release/trail CHANGELOG.md LICENSE
@ -217,7 +217,7 @@ jobs:
shell: bash
run: |
env && \
cargo build --target ${{ env.RUST_TARGET }} --release --features=static-geos --bin trail
cargo build --target ${{ env.RUST_TARGET }} --release --features=geos-static --bin trail
- name: Package Artifacts
run: |
zip -r -j trailbase_${{ github.ref_name }}_x86_64_windows.zip target/${{ env.RUST_TARGET }}/release/trail.exe CHANGELOG.md LICENSE

View file

@ -45,7 +45,7 @@ repos:
- id: cargotest
name: Cargo Test
entry: sh -c 'pnpm i --frozen-lockfile --prefer-offline && PNPM_OFFLINE=TRUE cargo test --workspace --exclude=trailbase-extension-so -- --show-output'
entry: sh -c 'pnpm i --frozen-lockfile --prefer-offline && PNPM_OFFLINE=TRUE cargo test --workspace --features=geos,wasm --exclude=trailbase-extension-so -- --show-output'
language: system
types: [rust]
exclude: '^(vendor|bindings)/'

64
Cargo.lock generated
View file

@ -2107,21 +2107,6 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
[[package]]
name = "foreign-types"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
dependencies = [
"foreign-types-shared",
]
[[package]]
name = "foreign-types-shared"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]]
name = "form_urlencoded"
version = "1.2.2"
@ -3834,60 +3819,12 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
[[package]]
name = "openssl"
version = "0.10.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328"
dependencies = [
"bitflags",
"cfg-if",
"foreign-types",
"libc",
"once_cell",
"openssl-macros",
"openssl-sys",
]
[[package]]
name = "openssl-macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "openssl-probe"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe"
[[package]]
name = "openssl-src"
version = "300.5.5+3.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f1787d533e03597a7934fd0a765f0d28e94ecc5fb7789f8053b1e699a56f709"
dependencies = [
"cc",
]
[[package]]
name = "openssl-sys"
version = "0.9.111"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321"
dependencies = [
"cc",
"libc",
"openssl-src",
"pkg-config",
"vcpkg",
]
[[package]]
name = "opentelemetry"
version = "0.31.0"
@ -6596,7 +6533,6 @@ dependencies = [
"log",
"mimalloc",
"minijinja",
"openssl",
"reqwest 0.13.2",
"rusqlite",
"serde",

View file

@ -47,7 +47,7 @@ RUN case ${TARGETPLATFORM} in \
"linux/arm64") RUST_TARGET="aarch64-unknown-linux-musl" ;; \
*) RUST_TARGET="x86_64-unknown-linux-musl" ;; \
esac && \
RUST_BACKTRACE=1 PNPM_OFFLINE="TRUE" cargo build --target ${RUST_TARGET} --features=static-geos --release --bin trail && \
RUST_BACKTRACE=1 PNPM_OFFLINE="TRUE" cargo build --target ${RUST_TARGET} --features=geos-static --release --bin trail && \
mv target/${RUST_TARGET}/release/trail /app/trail.exe

View file

@ -12,11 +12,11 @@ doctest = false
name = "trail"
[features]
default = []
default = ["trailbase/wasm", "trailbase/geos"]
geos-static = ["trailbase/geos-static"]
geos = ["trailbase/geos"]
swagger = ["dep:utoipa-swagger-ui"]
vendor-ssl = ["dep:openssl"]
ws = ["trailbase/ws"]
static-geos = ["trailbase/static-geos"]
[dependencies]
axum = { version = "^0.8.1", features=["multipart"] }
@ -29,7 +29,6 @@ itertools = "0.14.0"
log = "^0.4.21"
mimalloc = { version = "^0.1.41", default-features = false }
minijinja = { workspace = true }
openssl = { version = "0.10.73", features = ["vendored"], optional = true }
reqwest = { workspace = true }
rusqlite = { workspace = true }
serde = { workspace = true }

View file

@ -24,12 +24,13 @@ name = "benchmark"
harness = false
[features]
default = ["wasm"]
wasm = ["dep:trailbase-wasm-runtime-host"]
default = []
otel = ["dep:axum-tracing-opentelemetry", "dep:init-tracing-opentelemetry"]
geos = ["dep:litegis", "dep:geos"]
geos-static = ["litegis/static", "dep:geos"]
wasm = ["dep:trailbase-wasm-runtime-host"]
# Enable axum's "ws" feature: https://doc.rust-lang.org/cargo/reference/features.html#dependency-features
ws = ["axum/ws"]
static-geos = ["litegis/static"]
[dependencies]
aes-gcm-siv = "0.11.1"
@ -49,7 +50,7 @@ ed25519-dalek = { version = "2.1.1", features = ["pkcs8", "pem", "rand_core"] }
fallible-iterator = "0.3.0"
form_urlencoded = "1.2.1"
futures-util = { version = "0.3", default-features = false, features = ["alloc"] }
geos = { version = "11.0.0", default-features = false, features = ["geo", "json"] }
geos = { version = "11.0.0", default-features = false, features = ["geo", "json"], optional = true }
http-body-util = "0.1.3"
hyper = "1.6.0"
hyper-util = "0.1.7"
@ -61,7 +62,7 @@ jsonwebtoken = { version = "^10.2.0", default-features = false, features = ["use
kanal = "0.1.1"
lazy_static = "1.4.0"
lettre = { version = "^0.11.7", default-features = false, features = ["tokio1-rustls-tls", "sendmail-transport", "smtp-transport", "builder"] }
litegis = { workspace = true }
litegis = { workspace = true, optional = true }
log = { version = "^0.4.21", default-features = false }
mini-moka = "0.10.3"
minijinja = { workspace = true }

View file

@ -342,6 +342,7 @@ fn init_main_db_impl(
let mut conn =
trailbase_extension::connect_sqlite(main_path.clone(), json_registry.clone())?;
#[cfg(any(feature = "geos", feature = "geos-static"))]
litegis::register(&conn)?;
if main_migrations {
@ -379,6 +380,7 @@ fn init_main_db_impl(
let mut secondary =
trailbase_extension::connect_sqlite(Some(path.clone()), json_registry.clone())?;
#[cfg(any(feature = "geos", feature = "geos-static"))]
litegis::register(&secondary)?;
apply_base_migrations(&mut secondary, Some(migrations_path), &schema_name)?;

View file

@ -83,7 +83,7 @@ mod wasm {
_components_path: PathBuf,
_fs_root_path: Option<&std::path::Path>,
_dev: bool,
) -> Result<Vec<(Arc<parking_lot::Mutex<SqliteStore>>, SqliteFunctions)>, AnyError> {
) -> Result<Vec<(SqliteStore, SqliteFunctions)>, AnyError> {
return Ok(vec![]);
}

View file

@ -32,6 +32,7 @@ pub enum JsonError {
// NOTE: This is the only extra error to schema::JsonError. Can we collapse?
#[error("SerdeJson error: {0}")]
SerdeJson(#[from] serde_json::Error),
#[cfg(any(feature = "geos", feature = "geos-static"))]
#[error("Geos: {0}")]
Geos(#[from] geos::Error),
}
@ -139,6 +140,7 @@ pub(crate) fn row_to_json_expand(
}
// De-serialize WKB Geometry.
#[cfg(any(feature = "geos", feature = "geos-static"))]
if let types::Value::Blob(wkb) = value
&& meta.is_geometry
{

View file

@ -115,7 +115,6 @@ fn compare_values(
record_value: &rusqlite::types::Value,
filter_value: &rusqlite::types::Value,
) -> bool {
use geos::Geom;
use rusqlite::types::Value;
return match op {
@ -175,7 +174,9 @@ fn compare_values(
_ => false,
},
CompareOp::StWithin => match (record_value, filter_value) {
#[cfg(any(feature = "geos", feature = "geos-static"))]
(Value::Blob(record), Value::Text(filter)) => {
use geos::Geom;
let Some((record_geometry, filter_geometry)) = parse_geometries(record, filter) else {
return false;
};
@ -184,7 +185,9 @@ fn compare_values(
_ => false,
},
CompareOp::StIntersects => match (record_value, filter_value) {
#[cfg(any(feature = "geos", feature = "geos-static"))]
(Value::Blob(record), Value::Text(filter)) => {
use geos::Geom;
let Some((record_geometry, filter_geometry)) = parse_geometries(record, filter) else {
return false;
};
@ -195,7 +198,9 @@ fn compare_values(
_ => false,
},
CompareOp::StContains => match (record_value, filter_value) {
#[cfg(any(feature = "geos", feature = "geos-static"))]
(Value::Blob(record), Value::Text(filter)) => {
use geos::Geom;
let Some((record_geometry, filter_geometry)) = parse_geometries(record, filter) else {
return false;
};
@ -206,6 +211,7 @@ fn compare_values(
};
}
#[cfg(any(feature = "geos", feature = "geos-static"))]
#[inline]
fn parse_geometries(record: &[u8], filter: &str) -> Option<(geos::Geometry, geos::Geometry)> {
let record_geometry = geos::Geometry::new_from_wkb(record).ok()?;

View file

@ -11,7 +11,6 @@ use std::convert::TryInto;
use std::sync::LazyLock;
use trailbase_qs::OrderPrecedent;
use trailbase_schema::QualifiedNameEscaped;
use trailbase_schema::metadata::ColumnMetadata;
use trailbase_sqlite::Value;
use crate::app_state::AppState;
@ -37,6 +36,7 @@ pub struct ListResponse {
#[derive(Debug)]
pub enum ListOrGeoJSONResponse {
List(ListResponse),
#[cfg(any(feature = "geos", feature = "geos-static"))]
GeoJSON(geos::geojson::FeatureCollection),
}
@ -48,6 +48,7 @@ impl Serialize for ListOrGeoJSONResponse {
{
return match self {
Self::List(v) => v.serialize(serializer),
#[cfg(any(feature = "geos", feature = "geos-static"))]
Self::GeoJSON(v) => v.serialize(serializer),
};
}
@ -345,6 +346,7 @@ pub async fn list_records_handler(
.collect::<Result<Vec<_>, RecordError>>()?
};
#[cfg(any(feature = "geos", feature = "geos-static"))]
if let Some(meta) = geojson_geometry_column {
return Ok(Json(ListOrGeoJSONResponse::GeoJSON(
build_feature_collection(meta, &pk_column.name, cursor, total_count, records)?,
@ -358,8 +360,9 @@ pub async fn list_records_handler(
})));
}
#[cfg(any(feature = "geos", feature = "geos-static"))]
fn build_feature_collection(
meta: &ColumnMetadata,
meta: &trailbase_schema::metadata::ColumnMetadata,
pk_column_name: &str,
cursor: Option<String>,
total_count: Option<usize>,
@ -1262,6 +1265,7 @@ mod tests {
assert_eq!(1, resp_filtered1.total_count.unwrap());
}
#[cfg(any(feature = "geos", feature = "geos-static"))]
#[tokio::test]
async fn test_record_api_geojson_list() {
let state = test_state(None).await.unwrap();

View file

@ -42,6 +42,7 @@ pub enum ParamsError {
Storage(Arc<object_store::Error>),
#[error("SqlValueDecode: {0}")]
SqlValueDecode(#[from] trailbase_sqlvalue::DecodeError),
#[cfg(any(feature = "geos", feature = "geos-static"))]
#[error("Geos: {0}")]
Geos(#[from] geos::Error),
}
@ -528,6 +529,7 @@ fn extract_params_and_files_from_json(
) -> Result<(Value, Option<FileMetadataContents>), ParamsError> {
// If this is *not* a JSON column convert the value trivially.
let Some(json_metadata) = json_metadata else {
#[cfg(any(feature = "geos", feature = "geos-static"))]
if is_geometry && col.data_type == ColumnDataType::Blob {
use geos::Geom;
@ -541,10 +543,10 @@ fn extract_params_and_files_from_json(
}
return Ok((Value::Blob(writer.write_wkb(&geometry)?), None));
} else {
debug_assert!(!is_geometry);
}
debug_assert!(!is_geometry);
return Ok((flat_json_to_value(col.data_type, value)?, None));
};

View file

@ -1266,6 +1266,7 @@ mod test {
assert_eq!(read_response, record);
}
#[cfg(any(feature = "geos", feature = "geos-static"))]
#[tokio::test]
async fn test_geometry_columns_and_geojson() {
let state = test_state(None).await.unwrap();