mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-09-29 04:42:02 +02:00
chore(rust): update crypto, network and UniFFI dependencies
This commit is contained in:
Generated
+302
-217
File diff suppressed because it is too large
Load Diff
+12
-10
@@ -16,21 +16,22 @@ spotiflac-extensions = { path = "crates/extensions" }
|
||||
spotiflac-network = { path = "crates/network" }
|
||||
spotiflac-providers = { path = "crates/providers" }
|
||||
rquickjs = { version = "=0.13.0", features = ["bindgen"] }
|
||||
base64 = "0.22"
|
||||
sha2 = "0.10"
|
||||
sha1 = "0.10"
|
||||
md-5 = "0.10"
|
||||
hmac = "0.12"
|
||||
aes-gcm = "=0.10.3"
|
||||
aes = { version = "=0.8.4", features = ["zeroize"] }
|
||||
blowfish = { version = "=0.9.1", features = ["zeroize"] }
|
||||
base64 = "0.23"
|
||||
sha2 = "0.11"
|
||||
sha1 = "0.11"
|
||||
md-5 = "0.11"
|
||||
hmac = "0.13"
|
||||
aes-gcm = "=0.11.1"
|
||||
aes = { version = "=0.9.3", features = ["zeroize"] }
|
||||
blowfish = { version = "=0.10.0", features = ["zeroize"] }
|
||||
zeroize = "1.8"
|
||||
getrandom = "0.4"
|
||||
tempfile = "3.27"
|
||||
zip = { version = "=8.6.0", default-features = false, features = ["deflate-flate2-zlib-rs"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = { version = "1.0", features = ["float_roundtrip", "raw_value"] }
|
||||
thiserror = "2.0"
|
||||
uniffi = { version = "=0.31.2", default-features = false }
|
||||
uniffi = { version = "=0.32.1", default-features = false }
|
||||
tokio = { version = "=1.53.1", features = ["rt-multi-thread", "net", "time", "sync", "io-util", "macros"] }
|
||||
hyper = { version = "=1.11.1", features = ["client", "http1", "http2"] }
|
||||
hyper-util = { version = "=0.1.20", features = ["client-legacy", "http1", "http2", "tokio"] }
|
||||
@@ -46,11 +47,12 @@ futures-util = "0.3"
|
||||
tokio-util = { version = "0.7", features = ["io"] }
|
||||
async-compression = { version = "0.4", features = ["tokio", "gzip"] }
|
||||
tower-service = "0.3"
|
||||
hickory-proto = { version = "=0.26.2", default-features = false, features = ["std"] }
|
||||
hickory-proto = { version = "=0.26.3", default-features = false, features = ["std"] }
|
||||
cookie = "0.18"
|
||||
url = "2.5"
|
||||
regex = "=1.13.1"
|
||||
scraper = { version = "=0.27.0", default-features = false }
|
||||
# Keep the parser compatible with scraper's HtmlTreeSink implementation.
|
||||
html5ever = "=0.39.0"
|
||||
# Match the Unicode 15 tables used by the Go backend's title/artist matching.
|
||||
unicode-normalization = "=0.1.22"
|
||||
|
||||
@@ -24,6 +24,7 @@ aes-gcm.workspace = true
|
||||
aes.workspace = true
|
||||
blowfish.workspace = true
|
||||
zeroize.workspace = true
|
||||
getrandom.workspace = true
|
||||
tempfile.workspace = true
|
||||
zip.workspace = true
|
||||
regex.workspace = true
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
//! Shared OAuth state and one-time callback ownership for managed runtimes.
|
||||
|
||||
use aes_gcm::aead::{OsRng, rand_core::RngCore};
|
||||
use base64::{Engine, engine::general_purpose::URL_SAFE_NO_PAD};
|
||||
use serde::Serialize;
|
||||
use serde_json::{Value, json};
|
||||
@@ -297,8 +296,7 @@ impl AuthRegistry {
|
||||
|
||||
pub fn callback_state() -> Result<String, String> {
|
||||
let mut random = Zeroizing::new([0_u8; 32]);
|
||||
OsRng
|
||||
.try_fill_bytes(random.as_mut())
|
||||
getrandom::fill(random.as_mut())
|
||||
.map_err(|error| format!("generate callback state: {error}"))?;
|
||||
Ok(URL_SAFE_NO_PAD.encode(random.as_ref()))
|
||||
}
|
||||
@@ -306,9 +304,7 @@ pub fn callback_state() -> Result<String, String> {
|
||||
pub fn pkce_verifier(length: usize) -> Result<String, String> {
|
||||
let length = length.clamp(43, 128);
|
||||
let mut random = Zeroizing::new(vec![0; length]);
|
||||
OsRng
|
||||
.try_fill_bytes(&mut random)
|
||||
.map_err(|error| error.to_string())?;
|
||||
getrandom::fill(&mut random).map_err(|error| error.to_string())?;
|
||||
let mut verifier = URL_SAFE_NO_PAD.encode(random.as_slice());
|
||||
verifier.truncate(length);
|
||||
Ok(verifier)
|
||||
|
||||
@@ -646,8 +646,12 @@ impl Backend {
|
||||
// Reclaim only that namespace after a process restart, including when
|
||||
// a complete file already exists but its finalizer still needs to run.
|
||||
let staging_prefix = format!(
|
||||
".spotiflac-download-{:x}-",
|
||||
Sha256::digest(output.display().to_lowercase().as_bytes())
|
||||
".spotiflac-download-{}-",
|
||||
crate::binary::encode(
|
||||
&Sha256::digest(output.display().to_lowercase().as_bytes()),
|
||||
"hex"
|
||||
)
|
||||
.expect("hex encoding")
|
||||
);
|
||||
if directory.exists() {
|
||||
for entry in directory.read_dir().map_err(|error| error.to_string())? {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Block modes retained for the existing extension API and media transforms.
|
||||
|
||||
use aes::cipher::{Block, BlockDecrypt, BlockEncrypt, KeyInit};
|
||||
use aes::cipher::{Block, BlockCipherDecrypt, BlockCipherEncrypt, KeyInit};
|
||||
use zeroize::Zeroizing;
|
||||
|
||||
pub enum BlockCipher {
|
||||
@@ -81,7 +81,7 @@ impl BlockCipher {
|
||||
}
|
||||
}
|
||||
|
||||
fn transform<C: BlockEncrypt + BlockDecrypt>(
|
||||
fn transform<C: BlockCipherEncrypt + BlockCipherDecrypt>(
|
||||
cipher: &C,
|
||||
data: &mut [u8],
|
||||
iv: &[u8],
|
||||
@@ -89,7 +89,7 @@ fn transform<C: BlockEncrypt + BlockDecrypt>(
|
||||
decrypt: bool,
|
||||
check: &dyn Fn() -> Result<(), String>,
|
||||
) -> Result<(), String> {
|
||||
let mut state = Block::<C>::clone_from_slice(iv);
|
||||
let mut state = Block::<C>::try_from(iv).expect("validated IV length");
|
||||
for (index, chunk) in data.chunks_mut(iv.len()).enumerate() {
|
||||
if index % 1024 == 0 {
|
||||
check()?;
|
||||
@@ -107,7 +107,7 @@ fn transform<C: BlockEncrypt + BlockDecrypt>(
|
||||
}
|
||||
}
|
||||
} else if decrypt {
|
||||
let encrypted = Block::<C>::clone_from_slice(chunk);
|
||||
let encrypted = Block::<C>::try_from(&*chunk).expect("aligned encrypted block");
|
||||
let mut block = encrypted.clone();
|
||||
cipher.decrypt_block(&mut block);
|
||||
for ((byte, plain), previous) in chunk.iter_mut().zip(block.iter()).zip(state.iter()) {
|
||||
@@ -118,7 +118,7 @@ fn transform<C: BlockEncrypt + BlockDecrypt>(
|
||||
for (byte, previous) in chunk.iter_mut().zip(state.iter()) {
|
||||
*byte ^= previous;
|
||||
}
|
||||
let block = Block::<C>::from_mut_slice(chunk);
|
||||
let block = <&mut Block<C>>::try_from(chunk).expect("aligned plaintext block");
|
||||
cipher.encrypt_block(block);
|
||||
state.clone_from(block);
|
||||
}
|
||||
@@ -148,3 +148,32 @@ pub fn unpad(data: &mut Vec<u8>, block_size: usize) -> Result<(), String> {
|
||||
data.truncate(data.len() - count);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn block_cipher_upgrade_preserves_known_ciphertexts_and_inverse() {
|
||||
for (algorithm, key, expected) in [
|
||||
("aes", vec![0; 16], "66e94bd4ef8a2c3b884cfa59ca342b2e"),
|
||||
("aes", vec![0; 24], "aae06992acbf52a3e8f4a96ec9300bd7"),
|
||||
("aes", vec![0; 32], "dc95c078a2408989ad48a21492842087"),
|
||||
("blowfish", vec![0; 8], "4ef997456198dd78"),
|
||||
] {
|
||||
let cipher = BlockCipher::new(algorithm, &key).unwrap();
|
||||
let iv = vec![0; cipher.block_size()];
|
||||
for mode in ["cbc", "ctr"] {
|
||||
let mut data = iv.clone();
|
||||
cipher
|
||||
.transform(&mut data, &iv, mode, false, &|| Ok(()))
|
||||
.unwrap();
|
||||
assert_eq!(crate::binary::encode(&data, "hex").unwrap(), expected);
|
||||
cipher
|
||||
.transform(&mut data, &iv, mode, true, &|| Ok(()))
|
||||
.unwrap();
|
||||
assert_eq!(data, iv);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ use crate::{
|
||||
runtime::Control,
|
||||
storage,
|
||||
};
|
||||
use aes_gcm::aead::{OsRng, rand_core::RngCore};
|
||||
use base64::{Engine, engine::general_purpose::STANDARD};
|
||||
use rquickjs::{ArrayBuffer, Ctx, Function, Object, Value};
|
||||
use sha2::{Digest, Sha256};
|
||||
@@ -61,9 +60,7 @@ pub(crate) fn register<'js>(
|
||||
return Err("key length must be an integer between 1 and 4096 bytes".to_owned());
|
||||
}
|
||||
let mut bytes = Zeroizing::new(vec![0; length as usize]);
|
||||
OsRng
|
||||
.try_fill_bytes(&mut bytes)
|
||||
.map_err(|error| error.to_string())?;
|
||||
getrandom::fill(&mut bytes).map_err(|error| error.to_string())?;
|
||||
Ok(bytes)
|
||||
})();
|
||||
let object = result_object(&ctx, result.as_ref().err())?;
|
||||
|
||||
@@ -6,7 +6,6 @@ mod store;
|
||||
|
||||
use crate::files::{ExtensionFiles, FilePath};
|
||||
use crate::transfer_policy::DownloadTransferPolicy;
|
||||
use aes_gcm::aead::{OsRng, rand_core::RngCore};
|
||||
use serde::Serialize;
|
||||
use sha2::{Digest, Sha256};
|
||||
use spotiflac_core::app_version::AppVersion;
|
||||
@@ -419,15 +418,16 @@ fn validator(response: &HttpResponse) -> String {
|
||||
|
||||
fn fingerprint(url: &str) -> String {
|
||||
UrlParts::parse(url).map_or(String::new(), |url| {
|
||||
format!(
|
||||
"{:x}",
|
||||
Sha256::digest(format!(
|
||||
crate::binary::encode(
|
||||
&Sha256::digest(format!(
|
||||
"{}://{}{}",
|
||||
url.scheme.to_lowercase(),
|
||||
url.authority().to_lowercase(),
|
||||
url.escaped_path()
|
||||
))
|
||||
)),
|
||||
"hex",
|
||||
)
|
||||
.expect("hex encoding")
|
||||
})
|
||||
}
|
||||
|
||||
@@ -483,7 +483,7 @@ fn next_delay(current: Duration, policy: &DownloadTransferPolicy) -> Duration {
|
||||
return ceiling;
|
||||
}
|
||||
let mut random = [0; 8];
|
||||
if OsRng.try_fill_bytes(&mut random).is_err() {
|
||||
if getrandom::fill(&mut random).is_err() {
|
||||
return ceiling;
|
||||
}
|
||||
floor
|
||||
|
||||
@@ -43,7 +43,7 @@ pub(crate) fn download(
|
||||
}
|
||||
let mut output = SegmentOutput::open(
|
||||
path,
|
||||
format!("{:x}", hash.finalize()),
|
||||
crate::binary::encode(&hash.finalize(), "hex").expect("hex encoding"),
|
||||
segments.len(),
|
||||
options.persistent,
|
||||
)
|
||||
|
||||
@@ -491,7 +491,6 @@ impl FilePath {
|
||||
create_parent: bool,
|
||||
source: Option<(&Self, &File)>,
|
||||
) -> io::Result<(StagedFile, bool)> {
|
||||
use aes_gcm::aead::{OsRng, rand_core::RngCore};
|
||||
if create_parent {
|
||||
self.mkdir_parent()?;
|
||||
}
|
||||
@@ -503,9 +502,7 @@ impl FilePath {
|
||||
.to_owned();
|
||||
for _ in 0..8 {
|
||||
let mut nonce = [0; 16];
|
||||
OsRng
|
||||
.try_fill_bytes(&mut nonce)
|
||||
.map_err(|error| io::Error::other(error.to_string()))?;
|
||||
getrandom::fill(&mut nonce).map_err(|error| io::Error::other(error.to_string()))?;
|
||||
let name = format!(
|
||||
".spotiflac-{}.partial",
|
||||
crate::binary::encode(&nonce, "hex").expect("hex encoding")
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::storage::{StorageError, StoreKind};
|
||||
use base64::Engine;
|
||||
use base64::alphabet;
|
||||
use base64::engine::general_purpose::{GeneralPurpose, GeneralPurposeConfig, STANDARD};
|
||||
use hmac::{Hmac, Mac};
|
||||
use hmac::{Hmac, KeyInit, Mac};
|
||||
use md5::Md5;
|
||||
use rquickjs::{Ctx, Function, Object};
|
||||
use sha1::Sha1;
|
||||
@@ -174,13 +174,13 @@ pub(crate) fn register<'js>(
|
||||
host.set(
|
||||
"md5",
|
||||
Function::new(ctx.clone(), |value: String| {
|
||||
format!("{:x}", Md5::digest(value))
|
||||
crate::binary::encode(&Md5::digest(value), "hex").expect("hex encoding")
|
||||
})?,
|
||||
)?;
|
||||
host.set(
|
||||
"sha256",
|
||||
Function::new(ctx.clone(), |value: String| {
|
||||
format!("{:x}", Sha256::digest(value))
|
||||
crate::binary::encode(&Sha256::digest(value), "hex").expect("hex encoding")
|
||||
})?,
|
||||
)?;
|
||||
host.set(
|
||||
@@ -189,7 +189,7 @@ pub(crate) fn register<'js>(
|
||||
let mut mac = Hmac::<Sha256>::new_from_slice(key.as_bytes())
|
||||
.expect("HMAC accepts any key length");
|
||||
mac.update(message.as_bytes());
|
||||
format!("{:x}", mac.finalize().into_bytes())
|
||||
crate::binary::encode(&mac.finalize().into_bytes(), "hex").expect("hex encoding")
|
||||
})?,
|
||||
)?;
|
||||
host.set(
|
||||
@@ -269,3 +269,29 @@ pub(crate) fn decode_go_utf8(mut bytes: &[u8]) -> String {
|
||||
}
|
||||
decoded
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn digest_upgrade_preserves_extension_hash_and_hmac_encodings() {
|
||||
let runtime = crate::ExtensionRuntime::load(
|
||||
r#"registerExtension({run() { return [
|
||||
utils.md5('abc'), utils.sha256('abc'), utils.hmacSHA256('message', 'key')
|
||||
]; }});"#,
|
||||
"{}",
|
||||
crate::RuntimeLimits::default(),
|
||||
)
|
||||
.unwrap();
|
||||
let result: serde_json::Value =
|
||||
serde_json::from_str(&runtime.call("run", "[]", None, 1000).unwrap()).unwrap();
|
||||
assert_eq!(
|
||||
result,
|
||||
serde_json::json!([
|
||||
"900150983cd24fb0d6963f7d28e17f72",
|
||||
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
|
||||
"6e9ef29b75fffc5b7abae527d58fdadb2fe42e7219011976917343065f58ed4a",
|
||||
])
|
||||
);
|
||||
runtime.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ pub fn write_verified_package(
|
||||
));
|
||||
}
|
||||
if !normalized.is_empty() {
|
||||
let actual = format!("{:x}", hasher.finalize());
|
||||
let actual = crate::binary::encode(&hasher.finalize(), "hex").expect("hex encoding");
|
||||
// The checksum is public, but keep the comparison independent of its
|
||||
// first mismatching byte, matching the Go implementation.
|
||||
if actual
|
||||
|
||||
@@ -317,7 +317,8 @@ impl SignedSessionClient {
|
||||
}
|
||||
|
||||
fn exchange_owned(&self, grant: &str, generation: u64, check: Check<'_>) -> Result<(), String> {
|
||||
let grant_hash = format!("{:x}", Sha256::digest(grant));
|
||||
let grant_hash =
|
||||
crate::binary::encode(&Sha256::digest(grant), "hex").expect("hex encoding");
|
||||
let record = {
|
||||
let mut state = self.scope.lock().expect("signed session coordinator lock");
|
||||
if state.clear_generation != generation {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use crate::manifest::SignedSession;
|
||||
use aes_gcm::aead::{OsRng, rand_core::RngCore};
|
||||
use base64::{Engine, engine::general_purpose::URL_SAFE_NO_PAD};
|
||||
use hmac::{Hmac, Mac};
|
||||
use hmac::{Hmac, KeyInit, Mac};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{Value, json};
|
||||
use sha2::{Digest, Sha256};
|
||||
@@ -371,9 +370,7 @@ impl Record {
|
||||
|
||||
pub fn random_hex(count: usize) -> Result<String, String> {
|
||||
let mut bytes = Zeroizing::new(vec![0; count]);
|
||||
OsRng
|
||||
.try_fill_bytes(&mut bytes)
|
||||
.map_err(|error| error.to_string())?;
|
||||
getrandom::fill(&mut bytes).map_err(|error| error.to_string())?;
|
||||
Ok(hex(&bytes))
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Go-compatible extension persistence. Each file is serialized across runtimes.
|
||||
|
||||
use aes_gcm::aead::{Aead, KeyInit, OsRng, rand_core::RngCore};
|
||||
use aes_gcm::aead::{Aead, KeyInit};
|
||||
use aes_gcm::{Aes256Gcm, Nonce};
|
||||
use base64::Engine;
|
||||
use base64::alphabet;
|
||||
@@ -64,7 +64,7 @@ impl StorageMasterKey {
|
||||
}
|
||||
|
||||
pub fn derive(&self, extension_id: &str, purpose: &str) -> Zeroizing<[u8; 32]> {
|
||||
let mut mac = <Hmac<Sha256> as Mac>::new_from_slice(self.0.as_ref())
|
||||
let mut mac = <Hmac<Sha256> as KeyInit>::new_from_slice(self.0.as_ref())
|
||||
.expect("HMAC accepts any key length");
|
||||
mac.update(b"SpotiFLAC Mobile extension storage v2\0");
|
||||
mac.update(purpose.as_bytes());
|
||||
@@ -77,12 +77,10 @@ impl StorageMasterKey {
|
||||
/// Go's wire format: 12-byte nonce, ciphertext, then the 16-byte authentication tag.
|
||||
pub fn encrypt(plaintext: &[u8], key: &[u8; 32]) -> Result<Vec<u8>, StorageError> {
|
||||
let mut nonce = [0u8; 12];
|
||||
OsRng
|
||||
.try_fill_bytes(&mut nonce)
|
||||
.map_err(|error| StorageError::Random(error.to_string()))?;
|
||||
getrandom::fill(&mut nonce).map_err(|error| StorageError::Random(error.to_string()))?;
|
||||
let cipher = Aes256Gcm::new(key.into());
|
||||
let ciphertext = cipher
|
||||
.encrypt(Nonce::from_slice(&nonce), plaintext)
|
||||
.encrypt(&Nonce::from(nonce), plaintext)
|
||||
.map_err(|_| StorageError::Authentication)?;
|
||||
let mut result = Vec::with_capacity(nonce.len() + ciphertext.len());
|
||||
result.extend_from_slice(&nonce);
|
||||
@@ -96,7 +94,10 @@ pub fn decrypt(ciphertext: &[u8], key: &[u8; 32]) -> Result<Zeroizing<Vec<u8>>,
|
||||
}
|
||||
let cipher = Aes256Gcm::new(key.into());
|
||||
let plaintext = cipher
|
||||
.decrypt(Nonce::from_slice(&ciphertext[..12]), &ciphertext[12..])
|
||||
.decrypt(
|
||||
ciphertext[..12].try_into().expect("checked nonce length"),
|
||||
&ciphertext[12..],
|
||||
)
|
||||
.map_err(|_| StorageError::Authentication)?;
|
||||
Ok(Zeroizing::new(plaintext))
|
||||
}
|
||||
@@ -282,8 +283,7 @@ impl ExtensionStore {
|
||||
Some(salt) if salt.len() == 32 => salt,
|
||||
_ => {
|
||||
let mut salt = vec![0u8; 32];
|
||||
OsRng
|
||||
.try_fill_bytes(&mut salt)
|
||||
getrandom::fill(&mut salt)
|
||||
.map_err(|error| StorageError::Random(error.to_string()))?;
|
||||
atomic_write(&path, &salt)?;
|
||||
salt
|
||||
@@ -450,6 +450,29 @@ pub(crate) fn atomic_write(path: &Path, data: &[u8]) -> Result<(), StorageError>
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn aes_gcm_upgrade_reads_existing_wire_format_and_rejects_tampering() {
|
||||
let mut stored = vec![0; 12];
|
||||
stored.extend(
|
||||
crate::binary::decode_string(
|
||||
"cea7403d4d606b6e074ec5d3baf39d18d0d1c8a799996bf0265b98b5d48ab919",
|
||||
"hex",
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
assert_eq!(&*decrypt(&stored, &[0; 32]).unwrap(), &[0; 16]);
|
||||
stored[15] ^= 1;
|
||||
assert!(matches!(
|
||||
decrypt(&stored, &[0; 32]),
|
||||
Err(StorageError::Authentication)
|
||||
));
|
||||
let generated = encrypt(b"example persisted settings", &[7; 32]).unwrap();
|
||||
assert_eq!(
|
||||
&*decrypt(&generated, &[7; 32]).unwrap(),
|
||||
b"example persisted settings"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn retired_store_cannot_read_or_overwrite_a_reinstalled_extension() {
|
||||
let directory = tempfile::tempdir().unwrap();
|
||||
|
||||
@@ -27,9 +27,8 @@ fi
|
||||
|
||||
cd "$RUST_BACKEND_DIR"
|
||||
export CARGO_TARGET_DIR="$RUST_BACKEND_DIR/target"
|
||||
# AES 0.8 requires this opt-in on ARM64. Runtime detection retains the software
|
||||
# fallback on CPUs without AES; do not force target-feature=+aes on Android.
|
||||
export RUSTFLAGS="${RUSTFLAGS:-} --cfg aes_armv8"
|
||||
# AES 0.9 detects ARM64 AES at runtime and retains the software fallback;
|
||||
# do not force target-feature=+aes on Android.
|
||||
|
||||
case "$(uname -s)" in
|
||||
Darwin)
|
||||
@@ -44,10 +43,10 @@ esac
|
||||
# Build separately from bindgen so CLI and cargo-metadata features are not
|
||||
# enabled in the shipped library through Cargo's workspace feature unification.
|
||||
cargo build --locked --release -p spotiflac-mobile
|
||||
# Bindgen discovers each crate's uniffi.toml through Cargo metadata.
|
||||
for language in kotlin swift; do
|
||||
cargo run --locked -p spotiflac-bindgen -- generate \
|
||||
--library "$HOST_LIBRARY" \
|
||||
--config crates/mobile/uniffi.toml \
|
||||
--language "$language" \
|
||||
--out-dir "$CARGO_TARGET_DIR/bindings/$language" \
|
||||
--no-format
|
||||
|
||||
@@ -35,8 +35,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
|
||||
cd "$PROJECT_DIR/rust_backend"
|
||||
# Exercise the same runtime-detected AES backend as the release build.
|
||||
export RUSTFLAGS="${RUSTFLAGS:-} --cfg aes_armv8"
|
||||
# AES 0.9 uses the same runtime-detected backend as the release build.
|
||||
cargo fmt --all -- --check
|
||||
cargo clippy --workspace --all-targets --locked -- -D warnings
|
||||
cargo test --workspace --locked
|
||||
|
||||
Reference in New Issue
Block a user