// Copyright: Ankitects Pty Ltd and contributors // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html mod adding; mod ankidroid; mod ankiweb; mod card_rendering; mod collection; mod config; pub(crate) mod dbproxy; mod error; mod i18n; mod import_export; mod ops; mod sync; use std::ops::Deref; use std::result; use std::sync::Arc; use std::sync::Mutex; use std::thread::JoinHandle; use futures::future::AbortHandle; use once_cell::sync::OnceCell; use prost::Message; use reqwest::Client; use tokio::runtime; use tokio::runtime::Runtime; use crate::backend::dbproxy::db_command_bytes; use crate::backend::sync::SyncState; use crate::prelude::*; use crate::progress::Progress; use crate::progress::ProgressState; use crate::progress::ThrottlingProgressHandler; #[derive(Clone)] #[repr(transparent)] pub struct Backend(Arc); impl Deref for Backend { type Target = BackendInner; fn deref(&self) -> &Self::Target { &self.0 } } pub struct BackendInner { col: Mutex>, pub(crate) tr: I18n, server: bool, sync_abort: Mutex>, progress_state: Arc>, runtime: OnceCell, state: Mutex, backup_task: Mutex>>>, media_sync_task: Mutex>>>, web_client: OnceCell, } #[derive(Default)] struct BackendState { sync: SyncState, } pub fn init_backend(init_msg: &[u8]) -> result::Result { let input: anki_proto::backend::BackendInit = match anki_proto::backend::BackendInit::decode(init_msg) { Ok(req) => req, Err(_) => return Err("couldn't decode init request".into()), }; let tr = I18n::new(&input.preferred_langs); Ok(Backend::new(tr, input.server)) } impl Backend { pub fn new(tr: I18n, server: bool) -> Backend { Backend(Arc::new(BackendInner { col: Mutex::new(None), tr, server, sync_abort: Mutex::new(None), progress_state: Arc::new(Mutex::new(ProgressState { want_abort: false, last_progress: None, })), runtime: OnceCell::new(), state: Mutex::new(BackendState::default()), backup_task: Mutex::new(None), media_sync_task: Mutex::new(None), web_client: OnceCell::new(), })) } pub fn i18n(&self) -> &I18n { &self.tr } pub fn run_db_command_bytes(&self, input: &[u8]) -> result::Result, Vec> { self.db_command(input).map_err(|err| { let backend_err = err.into_protobuf(&self.tr); let mut bytes = Vec::new(); backend_err.encode(&mut bytes).unwrap(); bytes }) } /// If collection is open, run the provided closure while holding /// the mutex. /// If collection is not open, return an error. pub(crate) fn with_col(&self, func: F) -> Result where F: FnOnce(&mut Collection) -> Result, { func( self.col .lock() .unwrap() .as_mut() .ok_or(AnkiError::CollectionNotOpen)?, ) } fn runtime_handle(&self) -> runtime::Handle { self.runtime .get_or_init(|| { runtime::Builder::new_multi_thread() .worker_threads(1) .enable_all() .build() .unwrap() }) .handle() .clone() } fn web_client(&self) -> &Client { // currently limited to http1, as nginx doesn't support http2 proxies self.web_client .get_or_init(|| Client::builder().http1_only().build().unwrap()) } fn db_command(&self, input: &[u8]) -> Result> { self.with_col(|col| db_command_bytes(col, input)) } /// Useful for operations that function with a closed collection, such as /// a colpkg import. For collection operations, you can use /// [Collection::new_progress_handler] instead. pub(crate) fn new_progress_handler + Default + Clone>( &self, ) -> ThrottlingProgressHandler

{ ThrottlingProgressHandler::new(self.progress_state.clone()) } }