perf: Optimize final build size
This commit is contained in:
1101
Cargo.lock
generated
1101
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
29
Cargo.toml
29
Cargo.toml
@@ -4,19 +4,26 @@ version = "0.1.1"
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chrono = { version = "0.4.43", features = ["serde"] }
|
chrono = { version = "0.4.39", features = ["serde"] }
|
||||||
futures = "0.3.31"
|
minreq = { version = "2.13", features = ["json-using-serde", "https-rustls"] }
|
||||||
once_cell = "1.21.3"
|
|
||||||
reqwest = { version = "0.11", default-features = false, features = ["json", "rustls-tls"] }
|
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
thiserror = "2.0.18"
|
thiserror = "2.0.11"
|
||||||
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
|
uniffi = { version = "0.27" }
|
||||||
tracing = "0.1.44"
|
|
||||||
tracing-subscriber = "0.3.22"
|
[features]
|
||||||
android_logger = "0.14"
|
uniffi-cli = ["uniffi/cli"]
|
||||||
log = "0.4"
|
|
||||||
uniffi = { version = "0.27", features = ["cli"] }
|
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
crate-type = ["cdylib"]
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "uniffi-bindgen"
|
||||||
|
required-features = ["uniffi-cli"]
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
opt-level = "z"
|
||||||
|
lto = true
|
||||||
|
codegen-units = 1
|
||||||
|
panic = "abort"
|
||||||
|
strip = true
|
||||||
|
|||||||
115
src/lib.rs
115
src/lib.rs
@@ -1,18 +1,10 @@
|
|||||||
use chrono::{Datelike, Local, NaiveDate, Weekday};
|
use chrono::{Datelike, Local, NaiveDate, Weekday};
|
||||||
use once_cell::sync::Lazy;
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::{Once, RwLock};
|
use std::sync::RwLock;
|
||||||
use tokio::runtime::Runtime;
|
|
||||||
use tracing::{debug, error, info};
|
|
||||||
|
|
||||||
uniffi::setup_scaffolding!();
|
uniffi::setup_scaffolding!();
|
||||||
|
|
||||||
static RUNTIME: Lazy<Runtime> =
|
|
||||||
Lazy::new(|| Runtime::new().expect("Failed to create Tokio runtime"));
|
|
||||||
|
|
||||||
static INIT_TRACING: Once = Once::new();
|
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error, uniffi::Error)]
|
#[derive(Debug, thiserror::Error, uniffi::Error)]
|
||||||
pub enum SuplError {
|
pub enum SuplError {
|
||||||
#[error("Network error: {reason}")]
|
#[error("Network error: {reason}")]
|
||||||
@@ -63,31 +55,14 @@ pub struct DailySchedule {
|
|||||||
#[derive(uniffi::Object)]
|
#[derive(uniffi::Object)]
|
||||||
pub struct JecnaSuplClient {
|
pub struct JecnaSuplClient {
|
||||||
provider_url: RwLock<String>,
|
provider_url: RwLock<String>,
|
||||||
client: reqwest::Client,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[uniffi::export]
|
#[uniffi::export]
|
||||||
impl JecnaSuplClient {
|
impl JecnaSuplClient {
|
||||||
#[uniffi::constructor]
|
#[uniffi::constructor]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
INIT_TRACING.call_once(|| {
|
|
||||||
#[cfg(target_os = "android")]
|
|
||||||
android_logger::init_once(
|
|
||||||
android_logger::Config::default()
|
|
||||||
.with_max_level(log::LevelFilter::Debug)
|
|
||||||
.with_tag("JecnaSuplClient"),
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(not(target_os = "android"))]
|
|
||||||
tracing_subscriber::fmt()
|
|
||||||
.with_max_level(tracing::Level::DEBUG)
|
|
||||||
.init();
|
|
||||||
});
|
|
||||||
|
|
||||||
info!("Initializing JecnaSuplClient");
|
|
||||||
Self {
|
Self {
|
||||||
provider_url: RwLock::new("https://jecnarozvrh.jzitnik.dev".to_string()),
|
provider_url: RwLock::new("https://jecnarozvrh.jzitnik.dev".to_string()),
|
||||||
client: reqwest::Client::new(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,103 +77,50 @@ impl JecnaSuplClient {
|
|||||||
let trimmed = provider.trim_end_matches('/');
|
let trimmed = provider.trim_end_matches('/');
|
||||||
format!("{}/versioned/v2", trimmed)
|
format!("{}/versioned/v2", trimmed)
|
||||||
};
|
};
|
||||||
debug!("Target URL: {}", url);
|
|
||||||
|
|
||||||
let client = self.client.clone();
|
let resp: ApiResponse = minreq::get(&url)
|
||||||
let class_name_clone = class_name.clone();
|
.send()
|
||||||
|
.map_err(|e| SuplError::NetworkError {
|
||||||
let (tx, rx) = std::sync::mpsc::channel();
|
|
||||||
|
|
||||||
RUNTIME.spawn(async move {
|
|
||||||
let result = async {
|
|
||||||
let resp = client.get(&url).send().await.map_err(|e| {
|
|
||||||
error!("Network request failed: {}", e);
|
|
||||||
SuplError::NetworkError {
|
|
||||||
reason: e.to_string(),
|
reason: e.to_string(),
|
||||||
}
|
})?
|
||||||
})?;
|
.json()
|
||||||
|
.map_err(|e| SuplError::ParseError {
|
||||||
let resp_text = resp.text().await.map_err(|e| {
|
|
||||||
error!("Failed to read response text: {}", e);
|
|
||||||
SuplError::NetworkError {
|
|
||||||
reason: e.to_string(),
|
reason: e.to_string(),
|
||||||
}
|
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
if resp_text.trim().is_empty() {
|
|
||||||
error!("Received empty response body from {}", url);
|
|
||||||
return Err(SuplError::ParseError {
|
|
||||||
reason: "Empty response body".to_string(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let resp: ApiResponse = serde_json::from_str(&resp_text).map_err(|e| {
|
|
||||||
error!("JSON parse error: {}. Position: line {}, col {}. Response content: {}", e, e.line(), e.column(), resp_text);
|
|
||||||
SuplError::ParseError {
|
|
||||||
reason: e.to_string(),
|
|
||||||
}
|
|
||||||
})?;
|
|
||||||
|
|
||||||
debug!("Successfully parsed response");
|
|
||||||
Ok::<ApiResponse, SuplError>(resp)
|
|
||||||
}
|
|
||||||
.await;
|
|
||||||
|
|
||||||
let _ = tx.send(result);
|
|
||||||
});
|
|
||||||
|
|
||||||
let result = rx.recv().map_err(|e| {
|
|
||||||
error!("Mpsc receive error: {}", e);
|
|
||||||
SuplError::RuntimeError {
|
|
||||||
reason: "Channel closed".to_string(),
|
|
||||||
}
|
|
||||||
})??;
|
|
||||||
|
|
||||||
let today = Local::now().date_naive();
|
let today = Local::now().date_naive();
|
||||||
let current_weekday = today.weekday();
|
let current_weekday = today.weekday();
|
||||||
debug!("Current date: {}, weekday: {:?}", today, current_weekday);
|
|
||||||
|
|
||||||
let start_date;
|
let start_date;
|
||||||
let end_date;
|
let end_date;
|
||||||
|
|
||||||
match current_weekday {
|
match current_weekday {
|
||||||
Weekday::Sat | Weekday::Sun => {
|
Weekday::Sat | Weekday::Sun => {
|
||||||
// Next Monday
|
|
||||||
let days_until_mon = 7 - current_weekday.num_days_from_monday();
|
let days_until_mon = 7 - current_weekday.num_days_from_monday();
|
||||||
let next_mon = today + chrono::Duration::days(days_until_mon as i64);
|
let next_mon = today + chrono::Duration::days(days_until_mon as i64);
|
||||||
start_date = next_mon;
|
start_date = next_mon;
|
||||||
end_date = next_mon + chrono::Duration::days(4); // Mon -> Fri is 4 days diff
|
end_date = next_mon + chrono::Duration::days(4);
|
||||||
info!(
|
|
||||||
"Weekend detected. Showing next week: {} to {}",
|
|
||||||
start_date, end_date
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// Current Monday
|
|
||||||
let days_from_mon = current_weekday.num_days_from_monday();
|
let days_from_mon = current_weekday.num_days_from_monday();
|
||||||
let curr_mon = today - chrono::Duration::days(days_from_mon as i64);
|
let curr_mon = today - chrono::Duration::days(days_from_mon as i64);
|
||||||
start_date = curr_mon;
|
start_date = curr_mon;
|
||||||
end_date = curr_mon + chrono::Duration::days(4);
|
end_date = curr_mon + chrono::Duration::days(4);
|
||||||
info!(
|
|
||||||
"Weekday detected. Showing current week: {} to {}",
|
|
||||||
start_date, end_date
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut output = Vec::new();
|
let mut output = Vec::new();
|
||||||
|
|
||||||
for (i, day_prop) in result.props.iter().enumerate() {
|
for (i, day_prop) in resp.props.iter().enumerate() {
|
||||||
let date = NaiveDate::parse_from_str(&day_prop.date, "%Y-%m-%d").map_err(|e| {
|
let date = NaiveDate::parse_from_str(&day_prop.date, "%Y-%m-%d").map_err(|e| {
|
||||||
error!("Date parsing error for {}: {}", day_prop.date, e);
|
|
||||||
SuplError::DateFormatError {
|
SuplError::DateFormatError {
|
||||||
reason: e.to_string(),
|
reason: e.to_string(),
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
if date >= start_date && date <= end_date {
|
if date >= start_date && date <= end_date {
|
||||||
if let Some(day_schedule_map) = result.schedule.get(i) {
|
if let Some(day_schedule_map) = resp.schedule.get(i) {
|
||||||
if let Some(class_schedule_val) = day_schedule_map.get(&class_name_clone) {
|
if let Some(class_schedule_val) = day_schedule_map.get(&class_name) {
|
||||||
match serde_json::from_value::<Vec<Option<String>>>(
|
match serde_json::from_value::<Vec<Option<String>>>(
|
||||||
class_schedule_val.clone(),
|
class_schedule_val.clone(),
|
||||||
) {
|
) {
|
||||||
@@ -209,30 +131,21 @@ impl JecnaSuplClient {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!(
|
|
||||||
"Failed to parse schedule for class {}: {}",
|
|
||||||
class_name_clone, e
|
|
||||||
);
|
|
||||||
return Err(SuplError::ParseError {
|
return Err(SuplError::ParseError {
|
||||||
reason: format!(
|
reason: format!(
|
||||||
"Invalid schedule format for class {}",
|
"Invalid schedule format for class {}: {}",
|
||||||
class_name_clone
|
class_name, e
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
debug!(
|
|
||||||
"Class {} not found in schedule for {}",
|
|
||||||
class_name_clone, date
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(SuplResult {
|
Ok(SuplResult {
|
||||||
status: result.status,
|
status: resp.status,
|
||||||
schedule: output,
|
schedule: output,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user