660 lines
No EOL
562 KiB
HTML
660 lines
No EOL
562 KiB
HTML
<!doctype html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<style>html, body {
|
|
margin: 0;
|
|
padding: 0;
|
|
}
|
|
|
|
.app {
|
|
margin: 10px;
|
|
padding: 0;
|
|
}
|
|
|
|
.files-list {
|
|
margin: 10px 0 0;
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
}
|
|
.files-list__head {
|
|
border: 1px solid #999;
|
|
}
|
|
.files-list__head > tr > th {
|
|
padding: 10px;
|
|
border: 1px solid #999;
|
|
text-align: left;
|
|
font-weight: normal;
|
|
background: #ddd;
|
|
}
|
|
.files-list__body {
|
|
}
|
|
.files-list__file {
|
|
cursor: pointer;
|
|
}
|
|
.files-list__file:hover {
|
|
background: #ccf;
|
|
}
|
|
.files-list__file > td {
|
|
padding: 10px;
|
|
border: 1px solid #999;
|
|
}
|
|
.files-list__file > td:first-child::before {
|
|
content: '\01F4C4';
|
|
margin-right: 1em;
|
|
}
|
|
.files-list__file_low {
|
|
background: #fcc;
|
|
}
|
|
.files-list__file_medium {
|
|
background: #ffc;
|
|
}
|
|
.files-list__file_high {
|
|
background: #cfc;
|
|
}
|
|
.files-list__file_folder > td:first-child::before {
|
|
content: '\01F4C1';
|
|
margin-right: 1em;
|
|
}
|
|
|
|
.file-header {
|
|
border: 1px solid #999;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
}
|
|
|
|
.file-header__back {
|
|
margin: 10px;
|
|
cursor: pointer;
|
|
flex-shrink: 0;
|
|
flex-grow: 0;
|
|
text-decoration: underline;
|
|
color: #338;
|
|
}
|
|
|
|
.file-header__name {
|
|
margin: 10px;
|
|
flex-shrink: 2;
|
|
flex-grow: 2;
|
|
}
|
|
|
|
.file-header__stat {
|
|
margin: 10px;
|
|
flex-shrink: 0;
|
|
flex-grow: 0;
|
|
}
|
|
|
|
.file-content {
|
|
margin: 10px 0 0;
|
|
border: 1px solid #999;
|
|
padding: 10px;
|
|
}
|
|
|
|
.code-line {
|
|
margin: 0;
|
|
padding: 0.3em;
|
|
height: 1em;
|
|
}
|
|
.code-line_covered {
|
|
background: #cfc;
|
|
}
|
|
.code-line_uncovered {
|
|
background: #fcc;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="root"></div>
|
|
<script>
|
|
var data = {"files":[{"path":["/","home","edward","Documents","repos","mangadex-home","build.rs"],"content":"use std::error::Error;\n\nuse vergen::{vergen, Config, ShaKind};\n\nfn main() -\u003e Result\u003c(), Box\u003cdyn Error\u003e\u003e {\n // Initialize vergen stuff\n let mut config = Config::default();\n *config.git_mut().sha_kind_mut() = ShaKind::Short;\n vergen(config)?;\n\n Ok(())\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","edward","Documents","repos","mangadex-home","src","cache","compat.rs"],"content":"//! These structs have alternative deserialize and serializations\n//! implementations to assist reading from the official client file format.\n\nuse std::str::FromStr;\n\nuse chrono::{DateTime, FixedOffset};\nuse serde::de::{Unexpected, Visitor};\nuse serde::{Deserialize, Serialize};\n\nuse super::ImageContentType;\n\n#[derive(Copy, Clone, Deserialize)]\npub struct LegacyImageMetadata {\n pub(crate) content_type: Option\u003cLegacyImageContentType\u003e,\n pub(crate) size: Option\u003cu32\u003e,\n pub(crate) last_modified: Option\u003cLegacyDateTime\u003e,\n}\n\n#[derive(Copy, Clone, Serialize)]\npub struct LegacyDateTime(pub DateTime\u003cFixedOffset\u003e);\n\nimpl\u003c'de\u003e Deserialize\u003c'de\u003e for LegacyDateTime {\n fn deserialize\u003cD\u003e(deserializer: D) -\u003e Result\u003cSelf, D::Error\u003e\n where\n D: serde::Deserializer\u003c'de\u003e,\n {\n struct LegacyDateTimeVisitor;\n\n impl\u003c'de\u003e Visitor\u003c'de\u003e for LegacyDateTimeVisitor {\n type Value = LegacyDateTime;\n\n fn expecting(\u0026self, f: \u0026mut std::fmt::Formatter) -\u003e std::fmt::Result {\n write!(f, \"a valid image type\")\n }\n\n fn visit_str\u003cE\u003e(self, v: \u0026str) -\u003e Result\u003cSelf::Value, E\u003e\n where\n E: serde::de::Error,\n {\n DateTime::parse_from_rfc2822(v)\n .map(LegacyDateTime)\n .map_err(|_| E::invalid_value(Unexpected::Str(v), \u0026\"a valid image type\"))\n }\n }\n\n deserializer.deserialize_str(LegacyDateTimeVisitor)\n }\n}\n\n#[derive(Copy, Clone)]\npub struct LegacyImageContentType(pub ImageContentType);\n\nimpl\u003c'de\u003e Deserialize\u003c'de\u003e for LegacyImageContentType {\n fn deserialize\u003cD\u003e(deserializer: D) -\u003e Result\u003cSelf, D::Error\u003e\n where\n D: serde::Deserializer\u003c'de\u003e,\n {\n struct LegacyImageContentTypeVisitor;\n\n impl\u003c'de\u003e Visitor\u003c'de\u003e for LegacyImageContentTypeVisitor {\n type Value = LegacyImageContentType;\n\n fn expecting(\u0026self, f: \u0026mut std::fmt::Formatter) -\u003e std::fmt::Result {\n write!(f, \"a valid image type\")\n }\n\n fn visit_str\u003cE\u003e(self, v: \u0026str) -\u003e Result\u003cSelf::Value, E\u003e\n where\n E: serde::de::Error,\n {\n ImageContentType::from_str(v)\n .map(LegacyImageContentType)\n .map_err(|_| E::invalid_value(Unexpected::Str(v), \u0026\"a valid image type\"))\n }\n }\n\n deserializer.deserialize_str(LegacyImageContentTypeVisitor)\n }\n}\n","traces":[{"line":23,"address":[4324992],"length":1,"stats":{"Line":1},"fn_name":"deserialize\u003c\u0026mut serde_json::de::Deserializer\u003cserde_json::read::IoRead\u003cstd::fs::File\u003e\u003e\u003e"},{"line":29,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":30,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":32,"address":[4325024],"length":1,"stats":{"Line":0},"fn_name":"expecting"},{"line":33,"address":[4325043],"length":1,"stats":{"Line":0},"fn_name":null},{"line":36,"address":[4325120],"length":1,"stats":{"Line":1},"fn_name":"visit_str\u003cserde_json::error::Error\u003e"},{"line":40,"address":[4325195,4325144],"length":1,"stats":{"Line":2},"fn_name":null},{"line":41,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":42,"address":[4325232,4325248,4325185],"length":1,"stats":{"Line":1},"fn_name":"{{closure}}\u003cserde_json::error::Error\u003e"},{"line":46,"address":[4325009],"length":1,"stats":{"Line":1},"fn_name":null},{"line":54,"address":[4325312],"length":1,"stats":{"Line":1},"fn_name":"deserialize\u003c\u0026mut serde_json::de::Deserializer\u003cserde_json::read::IoRead\u003cstd::fs::File\u003e\u003e\u003e"},{"line":60,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":61,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":63,"address":[4325376],"length":1,"stats":{"Line":0},"fn_name":"expecting"},{"line":64,"address":[4325395],"length":1,"stats":{"Line":0},"fn_name":null},{"line":67,"address":[4325472],"length":1,"stats":{"Line":1},"fn_name":"visit_str\u003cserde_json::error::Error\u003e"},{"line":71,"address":[4325486,4325535],"length":1,"stats":{"Line":2},"fn_name":null},{"line":72,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":73,"address":[4325609,4325600,4325530],"length":1,"stats":{"Line":1},"fn_name":"{{closure}}\u003cserde_json::error::Error\u003e"},{"line":77,"address":[4325321],"length":1,"stats":{"Line":1},"fn_name":null}],"covered":10,"coverable":20},{"path":["/","home","edward","Documents","repos","mangadex-home","src","cache","disk.rs"],"content":"//! Low memory caching stuff\n\nuse std::convert::TryFrom;\nuse std::hint::unreachable_unchecked;\nuse std::os::unix::prelude::OsStrExt;\nuse std::path::{Path, PathBuf};\nuse std::str::FromStr;\nuse std::sync::atomic::{AtomicU64, Ordering};\nuse std::sync::Arc;\n\nuse async_trait::async_trait;\nuse futures::StreamExt;\nuse log::{debug, error, warn, LevelFilter};\nuse md5::digest::generic_array::GenericArray;\nuse md5::{Digest, Md5};\nuse sqlx::sqlite::SqliteConnectOptions;\nuse sqlx::{ConnectOptions, Sqlite, SqlitePool, Transaction};\nuse tokio::fs::remove_file;\nuse tokio::sync::mpsc::{channel, Receiver, Sender};\nuse tokio_stream::wrappers::ReceiverStream;\n\nuse crate::units::Bytes;\n\nuse super::{Cache, CacheError, CacheKey, CacheStream, CallbackCache, ImageMetadata};\n\npub struct DiskCache {\n disk_path: PathBuf,\n disk_cur_size: AtomicU64,\n db_update_channel_sender: Sender\u003cDbMessage\u003e,\n}\n\nenum DbMessage {\n Get(Arc\u003cPathBuf\u003e),\n Put(Arc\u003cPathBuf\u003e, u64),\n}\n\nimpl DiskCache {\n /// Constructs a new low memory cache at the provided path and capacity.\n /// This internally spawns a task that will wait for filesystem\n /// notifications when a file has been written.\n pub async fn new(disk_max_size: Bytes, disk_path: PathBuf) -\u003e Arc\u003cSelf\u003e {\n let (db_tx, db_rx) = channel(128);\n let db_pool = {\n let db_url = format!(\"sqlite:{}/metadata.sqlite\", disk_path.to_string_lossy());\n let mut options = SqliteConnectOptions::from_str(\u0026db_url)\n .unwrap()\n .create_if_missing(true);\n options.log_statements(LevelFilter::Trace);\n let db = SqlitePool::connect_with(options).await.unwrap();\n\n // Run db init\n sqlx::query_file!(\"./db_queries/init.sql\")\n .execute(\u0026mut db.acquire().await.unwrap())\n .await\n .unwrap();\n\n db\n };\n\n // This is intentional.\n #[allow(clippy::cast_sign_loss)]\n let disk_cur_size = {\n let mut conn = db_pool.acquire().await.unwrap();\n sqlx::query!(\"SELECT IFNULL(SUM(size), 0) AS size FROM Images\")\n .fetch_one(\u0026mut conn)\n .await\n .map(|record| record.size)\n .unwrap_or_default()\n .unwrap_or_default() as u64\n };\n\n let new_self = Arc::new(Self {\n disk_path,\n disk_cur_size: AtomicU64::new(disk_cur_size),\n db_update_channel_sender: db_tx,\n });\n\n // maybe this is better? in theory this sounds like it should be faster\n // but in practice I haven't seen any perf improvements\n\n // let ret = Arc::clone(\u0026new_self);\n // std::thread::spawn(move || {\n // tokio::runtime::Builder::new_current_thread()\n // .enable_all()\n // .build()\n // .unwrap()\n // .block_on(db_listener(\n // Arc::clone(\u0026new_self),\n // db_rx,\n // db_pool,\n // disk_max_size.get() as u64 / 20 * 19,\n // ))\n // });\n // ret\n\n tokio::spawn(db_listener(\n Arc::clone(\u0026new_self),\n db_rx,\n db_pool,\n disk_max_size.get() as u64 / 20 * 19,\n ));\n\n new_self\n }\n\n #[cfg(test)]\n fn in_memory() -\u003e (Self, Receiver\u003cDbMessage\u003e) {\n let (db_tx, db_rx) = channel(128);\n (\n Self {\n disk_path: PathBuf::new(),\n disk_cur_size: AtomicU64::new(0),\n db_update_channel_sender: db_tx,\n },\n db_rx,\n )\n }\n}\n\n/// Spawn a new task that will listen for updates to the db, pruning if the size\n/// becomes too large.\nasync fn db_listener(\n cache: Arc\u003cDiskCache\u003e,\n db_rx: Receiver\u003cDbMessage\u003e,\n db_pool: SqlitePool,\n max_on_disk_size: u64,\n) {\n let mut recv_stream = ReceiverStream::new(db_rx).ready_chunks(128);\n while let Some(messages) = recv_stream.next().await {\n let mut transaction = match db_pool.begin().await {\n Ok(transaction) =\u003e transaction,\n Err(e) =\u003e {\n error!(\"Failed to start a transaction to DB, cannot update DB. Disk cache may be losing track of files! {}\", e);\n continue;\n }\n };\n\n for message in messages {\n match message {\n DbMessage::Get(entry) =\u003e handle_db_get(\u0026entry, \u0026mut transaction).await,\n DbMessage::Put(entry, size) =\u003e {\n handle_db_put(\u0026entry, size, \u0026cache, \u0026mut transaction).await;\n }\n }\n }\n\n if let Err(e) = transaction.commit().await {\n error!(\n \"Failed to commit transaction to DB. Disk cache may be losing track of files! {}\",\n e\n );\n }\n\n let on_disk_size = (cache.disk_cur_size.load(Ordering::Acquire) + 4095) / 4096 * 4096;\n if on_disk_size \u003e= max_on_disk_size {\n let mut conn = match db_pool.acquire().await {\n Ok(conn) =\u003e conn,\n Err(e) =\u003e {\n error!(\n \"Failed to get a DB connection and cannot prune disk cache: {}\",\n e\n );\n continue;\n }\n };\n\n let items = {\n let request =\n sqlx::query!(\"select id, size from Images order by accessed asc limit 1000\")\n .fetch_all(\u0026mut conn)\n .await;\n match request {\n Ok(items) =\u003e items,\n Err(e) =\u003e {\n error!(\n \"Failed to fetch oldest images and cannot prune disk cache: {}\",\n e\n );\n continue;\n }\n }\n };\n\n let mut size_freed = 0;\n #[allow(clippy::cast_sign_loss)]\n for item in items {\n debug!(\"deleting file due to exceeding cache size\");\n size_freed += item.size as u64;\n tokio::spawn(async move {\n let key = item.id;\n if let Err(e) = remove_file(key.clone()).await {\n match e.kind() {\n std::io::ErrorKind::NotFound =\u003e {\n let hash = Md5Hash(*GenericArray::from_slice(key.as_bytes()));\n let path: PathBuf = hash.into();\n if let Err(e) = remove_file(\u0026path).await {\n warn!(\n \"Failed to delete file `{}` from cache: {}\",\n path.to_string_lossy(),\n e\n );\n }\n }\n _ =\u003e {\n warn!(\"Failed to delete file `{}` from cache: {}\", \u0026key, e);\n }\n }\n }\n });\n }\n\n cache.disk_cur_size.fetch_sub(size_freed, Ordering::Release);\n }\n }\n}\n\nasync fn handle_db_get(entry: \u0026Path, transaction: \u0026mut Transaction\u003c'_, Sqlite\u003e) {\n let hash = if let Ok(hash) = Md5Hash::try_from(entry) {\n hash\n } else {\n error!(\n \"Failed to derive hash from entry, dropping message: {}\",\n entry.to_string_lossy()\n );\n return;\n };\n\n let hash_str = hash.to_hex_string();\n let key = entry.as_os_str().to_str();\n let now = chrono::Utc::now();\n let query = sqlx::query!(\n \"update Images set accessed = ? where id = ? or id = ?\",\n now,\n key,\n hash_str\n )\n .execute(transaction)\n .await;\n if let Err(e) = query {\n warn!(\"Failed to update timestamp in db for {:?}: {}\", key, e);\n }\n}\n\nasync fn handle_db_put(\n entry: \u0026Path,\n size: u64,\n cache: \u0026DiskCache,\n transaction: \u0026mut Transaction\u003c'_, Sqlite\u003e,\n) {\n let key = entry.as_os_str().to_str();\n let now = chrono::Utc::now();\n\n // This is intentional.\n #[allow(clippy::cast_possible_wrap)]\n let casted_size = size as i64;\n let query = sqlx::query!(\n \"insert into Images (id, size, accessed) values (?, ?, ?) on conflict do nothing\",\n key,\n casted_size,\n now,\n )\n .execute(transaction)\n .await;\n\n if let Err(e) = query {\n warn!(\"Failed to add {:?} to db: {}\", key, e);\n }\n\n cache.disk_cur_size.fetch_add(size, Ordering::Release);\n}\n\n/// Represents a Md5 hash that can be converted to and from a path. This is used\n/// for compatibility with the official client, where the image id and on-disk\n/// path is determined by file path.\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\nstruct Md5Hash(GenericArray\u003cu8, \u003cMd5 as md5::Digest\u003e::OutputSize\u003e);\n\nimpl Md5Hash {\n fn to_hex_string(self) -\u003e String {\n format!(\"{:x}\", self.0)\n }\n}\n\nimpl TryFrom\u003c\u0026Path\u003e for Md5Hash {\n type Error = ();\n\n fn try_from(path: \u0026Path) -\u003e Result\u003cSelf, Self::Error\u003e {\n let mut iter = path.iter();\n let file_name = iter.next_back().ok_or(())?;\n let chapter_hash = iter.next_back().ok_or(())?;\n let is_data_saver = iter.next_back().ok_or(())? == \"saver\";\n let mut hasher = Md5::new();\n if is_data_saver {\n hasher.update(\"saver\");\n }\n hasher.update(chapter_hash.as_bytes());\n hasher.update(\".\");\n hasher.update(file_name.as_bytes());\n Ok(Self(hasher.finalize()))\n }\n}\n\nimpl From\u003cMd5Hash\u003e for PathBuf {\n fn from(hash: Md5Hash) -\u003e Self {\n let hex_value = dbg!(hash.to_hex_string());\n let path = hex_value[0..3]\n .chars()\n .rev()\n .map(|char| Self::from(char.to_string()))\n .reduce(|first, second| first.join(second));\n\n match path {\n Some(p) =\u003e p.join(hex_value),\n None =\u003e unsafe { unreachable_unchecked() }, // literally not possible\n }\n }\n}\n\n#[async_trait]\nimpl Cache for DiskCache {\n async fn get(\n \u0026self,\n key: \u0026CacheKey,\n ) -\u003e Option\u003cResult\u003c(CacheStream, ImageMetadata), CacheError\u003e\u003e {\n let channel = self.db_update_channel_sender.clone();\n\n let path = Arc::new(self.disk_path.clone().join(PathBuf::from(key)));\n let path_0 = Arc::clone(\u0026path);\n\n tokio::spawn(async move { channel.send(DbMessage::Get(path_0)).await });\n\n super::fs::read_file_from_path(\u0026path).await.map(|res| {\n let (inner, maybe_header, metadata) = res?;\n CacheStream::new(inner, maybe_header)\n .map(|stream| (stream, metadata))\n .map_err(|_| CacheError::DecryptionFailure)\n })\n }\n\n async fn put(\n \u0026self,\n key: CacheKey,\n image: bytes::Bytes,\n metadata: ImageMetadata,\n ) -\u003e Result\u003c(), CacheError\u003e {\n let channel = self.db_update_channel_sender.clone();\n\n let path = Arc::new(self.disk_path.clone().join(PathBuf::from(\u0026key)));\n let path_0 = Arc::clone(\u0026path);\n\n let db_callback = |size: u64| async move {\n std::mem::drop(channel.send(DbMessage::Put(path_0, size)).await);\n };\n\n super::fs::write_file(\u0026path, key, image, metadata, db_callback, None)\n .await\n .map_err(CacheError::from)\n }\n}\n\n#[async_trait]\nimpl CallbackCache for DiskCache {\n async fn put_with_on_completed_callback(\n \u0026self,\n key: CacheKey,\n image: bytes::Bytes,\n metadata: ImageMetadata,\n on_complete: Sender\u003c(CacheKey, bytes::Bytes, ImageMetadata, u64)\u003e,\n ) -\u003e Result\u003c(), CacheError\u003e {\n let channel = self.db_update_channel_sender.clone();\n\n let path = Arc::new(self.disk_path.clone().join(PathBuf::from(\u0026key)));\n let path_0 = Arc::clone(\u0026path);\n\n let db_callback = |size: u64| async move {\n // We don't care about the result of the send\n std::mem::drop(channel.send(DbMessage::Put(path_0, size)).await);\n };\n\n super::fs::write_file(\u0026path, key, image, metadata, db_callback, Some(on_complete))\n .await\n .map_err(CacheError::from)\n }\n}\n\n#[cfg(test)]\nmod db {\n use chrono::{DateTime, Utc};\n use sqlx::{Connection, Row, SqliteConnection};\n use std::error::Error;\n\n use super::*;\n\n #[tokio::test]\n async fn get() -\u003e Result\u003c(), Box\u003cdyn Error\u003e\u003e {\n let (cache, _) = DiskCache::in_memory();\n let path = PathBuf::from_str(\"a/b/c\")?;\n let mut conn = SqliteConnection::connect(\"sqlite::memory:\").await?;\n sqlx::query_file!(\"./db_queries/init.sql\")\n .execute(\u0026mut conn)\n .await?;\n\n // Add an entry\n let mut transaction = conn.begin().await?;\n handle_db_put(\u0026path, 10, \u0026cache, \u0026mut transaction).await;\n transaction.commit().await?;\n\n let time_fence = Utc::now();\n\n let mut transaction = conn.begin().await?;\n handle_db_get(\u0026path, \u0026mut transaction).await;\n transaction.commit().await?;\n\n let mut rows: Vec\u003c_\u003e = sqlx::query(\"select * from Images\")\n .fetch(\u0026mut conn)\n .collect()\n .await;\n assert_eq!(rows.len(), 1);\n let entry = rows.pop().unwrap()?;\n assert!(time_fence \u003c entry.get::\u003c'_, DateTime\u003cUtc\u003e, _\u003e(\"accessed\"));\n\n Ok(())\n }\n\n #[tokio::test]\n async fn put() -\u003e Result\u003c(), Box\u003cdyn Error\u003e\u003e {\n let (cache, _) = DiskCache::in_memory();\n let path = PathBuf::from_str(\"a/b/c\")?;\n let mut conn = SqliteConnection::connect(\"sqlite::memory:\").await?;\n sqlx::query_file!(\"./db_queries/init.sql\")\n .execute(\u0026mut conn)\n .await?;\n\n let mut transaction = conn.begin().await?;\n let transaction_time = Utc::now();\n handle_db_put(\u0026path, 10, \u0026cache, \u0026mut transaction).await;\n transaction.commit().await?;\n\n let mut rows: Vec\u003c_\u003e = sqlx::query(\"select * from Images\")\n .fetch(\u0026mut conn)\n .collect()\n .await;\n assert_eq!(rows.len(), 1);\n\n let entry = rows.pop().unwrap()?;\n assert_eq!(entry.get::\u003c'_, \u0026str, _\u003e(\"id\"), \"a/b/c\");\n assert_eq!(entry.get::\u003c'_, i64, _\u003e(\"size\"), 10);\n\n let accessed: DateTime\u003cUtc\u003e = entry.get(\"accessed\");\n assert!(transaction_time \u003c accessed);\n assert!(accessed \u003c Utc::now());\n\n assert_eq!(cache.disk_cur_size.load(Ordering::SeqCst), 10);\n\n Ok(())\n }\n}\n\n#[cfg(test)]\nmod md5_hash {\n use super::*;\n\n #[test]\n fn to_cache_path() {\n let hash = Md5Hash(\n *GenericArray::\u003c_, \u003cMd5 as md5::Digest\u003e::OutputSize\u003e::from_slice(\u0026[\n 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd,\n 0xef, 0xab,\n ]),\n );\n assert_eq!(\n PathBuf::from(hash).to_str(),\n Some(\"c/b/a/abcdefabcdefabcdefabcdefabcdefab\")\n )\n }\n\n #[test]\n fn from_data_path() {\n let mut expected_hasher = Md5::new();\n expected_hasher.update(\"foo.bar.png\");\n assert_eq!(\n Md5Hash::try_from(Path::new(\"data/foo/bar.png\")),\n Ok(Md5Hash(expected_hasher.finalize()))\n );\n }\n\n #[test]\n fn from_data_saver_path() {\n let mut expected_hasher = Md5::new();\n expected_hasher.update(\"saverfoo.bar.png\");\n assert_eq!(\n Md5Hash::try_from(Path::new(\"saver/foo/bar.png\")),\n Ok(Md5Hash(expected_hasher.finalize()))\n );\n }\n\n #[test]\n fn can_handle_long_paths() {\n assert_eq!(\n Md5Hash::try_from(Path::new(\"a/b/c/d/e/f/g/saver/foo/bar.png\")),\n Md5Hash::try_from(Path::new(\"saver/foo/bar.png\")),\n );\n }\n\n #[test]\n fn from_invalid_paths() {\n assert!(Md5Hash::try_from(Path::new(\"foo/bar.png\")).is_err());\n assert!(Md5Hash::try_from(Path::new(\"bar.png\")).is_err());\n assert!(Md5Hash::try_from(Path::new(\"\")).is_err());\n }\n}\n","traces":[{"line":41,"address":[5949909,5949856,5954232,5954434,5953667,5954576,5949327,5949296],"length":1,"stats":{"Line":0},"fn_name":"new"},{"line":42,"address":[5950066],"length":1,"stats":{"Line":0},"fn_name":null},{"line":43,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":44,"address":[5954118,5950141],"length":1,"stats":{"Line":0},"fn_name":null},{"line":45,"address":[5950435],"length":1,"stats":{"Line":0},"fn_name":null},{"line":48,"address":[5950580],"length":1,"stats":{"Line":0},"fn_name":null},{"line":49,"address":[5954479,5950834,5954074,5950623],"length":1,"stats":{"Line":0},"fn_name":null},{"line":52,"address":[5951187,5951764,5953942,5954521,5951663],"length":1,"stats":{"Line":0},"fn_name":null},{"line":53,"address":[5951377,5951276,5954500,5953985],"length":1,"stats":{"Line":0},"fn_name":null},{"line":54,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":57,"address":[5952097],"length":1,"stats":{"Line":0},"fn_name":null},{"line":62,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":63,"address":[5952243,5953896,5954542,5952145],"length":1,"stats":{"Line":0},"fn_name":null},{"line":64,"address":[5952647,5954563,5952519,5952730,5953807],"length":1,"stats":{"Line":0},"fn_name":null},{"line":65,"address":[5952624],"length":1,"stats":{"Line":0},"fn_name":null},{"line":66,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":67,"address":[5949824,5949840],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":68,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":69,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":72,"address":[5953218],"length":1,"stats":{"Line":0},"fn_name":null},{"line":73,"address":[5953124],"length":1,"stats":{"Line":0},"fn_name":null},{"line":74,"address":[5953155],"length":1,"stats":{"Line":0},"fn_name":null},{"line":75,"address":[5953207],"length":1,"stats":{"Line":0},"fn_name":null},{"line":96,"address":[5953568,5953481],"length":1,"stats":{"Line":0},"fn_name":null},{"line":97,"address":[5953318],"length":1,"stats":{"Line":0},"fn_name":null},{"line":98,"address":[5953362],"length":1,"stats":{"Line":0},"fn_name":null},{"line":99,"address":[5953389],"length":1,"stats":{"Line":0},"fn_name":null},{"line":100,"address":[5955204,5953416],"length":1,"stats":{"Line":0},"fn_name":null},{"line":103,"address":[5953638],"length":1,"stats":{"Line":0},"fn_name":null},{"line":107,"address":[5955525,5955264],"length":1,"stats":{"Line":1},"fn_name":"in_memory"},{"line":108,"address":[5955281],"length":1,"stats":{"Line":1},"fn_name":null},{"line":110,"address":[5955391],"length":1,"stats":{"Line":1},"fn_name":null},{"line":111,"address":[5955326],"length":1,"stats":{"Line":1},"fn_name":null},{"line":112,"address":[5955337],"length":1,"stats":{"Line":1},"fn_name":null},{"line":113,"address":[5955386],"length":1,"stats":{"Line":1},"fn_name":null},{"line":115,"address":[5955442],"length":1,"stats":{"Line":1},"fn_name":null},{"line":122,"address":[5955584],"length":1,"stats":{"Line":0},"fn_name":"db_listener"},{"line":128,"address":[5960243,5968080],"length":1,"stats":{"Line":0},"fn_name":null},{"line":129,"address":[5960653,5967923,5960304,5960395,5966937],"length":1,"stats":{"Line":0},"fn_name":null},{"line":130,"address":[5967944,5961120,5967473,5960780,5960682],"length":1,"stats":{"Line":0},"fn_name":null},{"line":131,"address":[5961011,5961122],"length":1,"stats":{"Line":0},"fn_name":null},{"line":132,"address":[5961026],"length":1,"stats":{"Line":0},"fn_name":null},{"line":133,"address":[5961292,5961377,5961074],"length":1,"stats":{"Line":0},"fn_name":null},{"line":138,"address":[5961229,5962882,5961682,5961828],"length":1,"stats":{"Line":0},"fn_name":null},{"line":139,"address":[5962068],"length":1,"stats":{"Line":0},"fn_name":null},{"line":140,"address":[5967380,5961978,5962279,5962078,5967965],"length":1,"stats":{"Line":0},"fn_name":null},{"line":141,"address":[5962000],"length":1,"stats":{"Line":0},"fn_name":null},{"line":142,"address":[5962693,5962464,5962047,5967986,5967338],"length":1,"stats":{"Line":0},"fn_name":null},{"line":147,"address":[5962895,5963042,5967304,5968007],"length":1,"stats":{"Line":0},"fn_name":null},{"line":148,"address":[5963391,5963519],"length":1,"stats":{"Line":0},"fn_name":null},{"line":154,"address":[5967600,5963824,5968620],"length":1,"stats":{"Line":0},"fn_name":null},{"line":155,"address":[5963976,5964009],"length":1,"stats":{"Line":0},"fn_name":null},{"line":156,"address":[5964433,5968028,5964093,5964022,5963990,5967263],"length":1,"stats":{"Line":0},"fn_name":null},{"line":157,"address":[5964435,5964324],"length":1,"stats":{"Line":0},"fn_name":null},{"line":158,"address":[5964339],"length":1,"stats":{"Line":0},"fn_name":null},{"line":159,"address":[5964387,5964548,5964633],"length":1,"stats":{"Line":0},"fn_name":null},{"line":168,"address":[5965051,5964930,5967198,5965137,5968049,5964538],"length":1,"stats":{"Line":0},"fn_name":null},{"line":170,"address":[5965028],"length":1,"stats":{"Line":0},"fn_name":null},{"line":172,"address":[5965517],"length":1,"stats":{"Line":0},"fn_name":null},{"line":173,"address":[5965408,5965519],"length":1,"stats":{"Line":0},"fn_name":null},{"line":174,"address":[5965423],"length":1,"stats":{"Line":0},"fn_name":null},{"line":175,"address":[5965471,5965738,5965653],"length":1,"stats":{"Line":0},"fn_name":null},{"line":184,"address":[5965583],"length":1,"stats":{"Line":0},"fn_name":null},{"line":186,"address":[5965595,5966178,5966817,5966035],"length":1,"stats":{"Line":0},"fn_name":null},{"line":187,"address":[5966338,5966457],"length":1,"stats":{"Line":0},"fn_name":null},{"line":188,"address":[5968932,5966616],"length":1,"stats":{"Line":0},"fn_name":null},{"line":189,"address":[5956631,5966661,5956592,5959330,5959588],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":190,"address":[5956691],"length":1,"stats":{"Line":0},"fn_name":null},{"line":191,"address":[5956841,5959554,5959271,5956725],"length":1,"stats":{"Line":0},"fn_name":null},{"line":192,"address":[5958951,5959357,5957138],"length":1,"stats":{"Line":0},"fn_name":null},{"line":193,"address":[5957168],"length":1,"stats":{"Line":0},"fn_name":null},{"line":194,"address":[5957236],"length":1,"stats":{"Line":0},"fn_name":null},{"line":195,"address":[5957346],"length":1,"stats":{"Line":0},"fn_name":null},{"line":196,"address":[5957490,5959132,5959575,5957397],"length":1,"stats":{"Line":0},"fn_name":null},{"line":197,"address":[5958048,5957788,5957916,5958092,5959042],"length":1,"stats":{"Line":0},"fn_name":null},{"line":199,"address":[5958015,5958077],"length":1,"stats":{"Line":0},"fn_name":null},{"line":205,"address":[5958479,5957182,5958555],"length":1,"stats":{"Line":0},"fn_name":null},{"line":212,"address":[5966830],"length":1,"stats":{"Line":0},"fn_name":null},{"line":217,"address":[5969014,5971696,5968976,5971739,5969072,5969111,5969816],"length":1,"stats":{"Line":4},"fn_name":"handle_db_get"},{"line":218,"address":[5969328,5969249],"length":1,"stats":{"Line":2},"fn_name":null},{"line":219,"address":[5969344],"length":1,"stats":{"Line":1},"fn_name":null},{"line":221,"address":[5969406,5969274,5969613,5971596,5969491],"length":1,"stats":{"Line":0},"fn_name":null},{"line":223,"address":[5969606],"length":1,"stats":{"Line":0},"fn_name":null},{"line":228,"address":[5969360],"length":1,"stats":{"Line":1},"fn_name":null},{"line":229,"address":[5969837],"length":1,"stats":{"Line":1},"fn_name":null},{"line":230,"address":[5969970],"length":1,"stats":{"Line":1},"fn_name":null},{"line":231,"address":[5971942,5970616,5971726,5970085,5971543],"length":1,"stats":{"Line":3},"fn_name":null},{"line":239,"address":[5970886],"length":1,"stats":{"Line":1},"fn_name":null},{"line":240,"address":[5971074,5970958],"length":1,"stats":{"Line":0},"fn_name":null},{"line":244,"address":[5972112],"length":1,"stats":{"Line":1},"fn_name":"handle_db_put"},{"line":250,"address":[5972393],"length":1,"stats":{"Line":1},"fn_name":null},{"line":251,"address":[5972526],"length":1,"stats":{"Line":1},"fn_name":null},{"line":255,"address":[5972641],"length":1,"stats":{"Line":1},"fn_name":null},{"line":256,"address":[5974120,5974485,5973180,5974319,5972664],"length":1,"stats":{"Line":3},"fn_name":null},{"line":265,"address":[5973450],"length":1,"stats":{"Line":1},"fn_name":null},{"line":266,"address":[5973522,5973638],"length":1,"stats":{"Line":0},"fn_name":null},{"line":269,"address":[5974045],"length":1,"stats":{"Line":1},"fn_name":null},{"line":279,"address":[5974656],"length":1,"stats":{"Line":1},"fn_name":"to_hex_string"},{"line":280,"address":[5974703],"length":1,"stats":{"Line":1},"fn_name":null},{"line":287,"address":[5974848],"length":1,"stats":{"Line":2},"fn_name":"try_from"},{"line":288,"address":[5974887],"length":1,"stats":{"Line":2},"fn_name":null},{"line":289,"address":[5975115,5974900],"length":1,"stats":{"Line":4},"fn_name":null},{"line":290,"address":[5975309,5975157,5975084],"length":1,"stats":{"Line":8},"fn_name":null},{"line":291,"address":[5975345,5975284],"length":1,"stats":{"Line":8},"fn_name":null},{"line":292,"address":[5975524],"length":1,"stats":{"Line":3},"fn_name":null},{"line":293,"address":[5975574,5975541],"length":1,"stats":{"Line":4},"fn_name":null},{"line":294,"address":[5975547],"length":1,"stats":{"Line":2},"fn_name":null},{"line":296,"address":[5975588],"length":1,"stats":{"Line":3},"fn_name":null},{"line":297,"address":[5975626],"length":1,"stats":{"Line":3},"fn_name":null},{"line":298,"address":[5975661],"length":1,"stats":{"Line":3},"fn_name":null},{"line":299,"address":[5975699],"length":1,"stats":{"Line":3},"fn_name":null},{"line":304,"address":[5142487,5141232],"length":1,"stats":{"Line":1},"fn_name":"from"},{"line":305,"address":[5141287,5142473],"length":1,"stats":{"Line":1},"fn_name":null},{"line":306,"address":[5142032],"length":1,"stats":{"Line":1},"fn_name":null},{"line":309,"address":[5975952,5975974],"length":1,"stats":{"Line":2},"fn_name":"{{closure}}"},{"line":310,"address":[5976016,5976052],"length":1,"stats":{"Line":2},"fn_name":"{{closure}}"},{"line":312,"address":[5142271],"length":1,"stats":{"Line":0},"fn_name":null},{"line":313,"address":[5142457,5142238,5142273],"length":1,"stats":{"Line":2},"fn_name":null},{"line":321,"address":[5976912],"length":1,"stats":{"Line":0},"fn_name":"get"},{"line":325,"address":[5979082],"length":1,"stats":{"Line":0},"fn_name":null},{"line":327,"address":[5980083,5979144],"length":1,"stats":{"Line":0},"fn_name":null},{"line":328,"address":[5979349],"length":1,"stats":{"Line":0},"fn_name":null},{"line":330,"address":[5977256,5977099,5979381,5977056],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":332,"address":[5979653,5979510,5980170,5980001,5978737,5978000],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":333,"address":[5978016,5978571],"length":1,"stats":{"Line":0},"fn_name":null},{"line":334,"address":[5978454,5978666],"length":1,"stats":{"Line":0},"fn_name":null},{"line":335,"address":[5977760,5977803,5978658],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":336,"address":[5977975,5977968],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":340,"address":[5980496],"length":1,"stats":{"Line":0},"fn_name":"put"},{"line":346,"address":[5981997],"length":1,"stats":{"Line":0},"fn_name":null},{"line":348,"address":[5983256,5982051],"length":1,"stats":{"Line":0},"fn_name":null},{"line":349,"address":[5982268],"length":1,"stats":{"Line":0},"fn_name":null},{"line":351,"address":[5980801,5981237,5981440,5981277,5980768,5981479,5982311],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":352,"address":[5980970,5981264,5981177,5980852],"length":1,"stats":{"Line":0},"fn_name":null},{"line":355,"address":[5982343,5983559,5982716,5983148],"length":1,"stats":{"Line":0},"fn_name":null},{"line":356,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":357,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":363,"address":[5983904],"length":1,"stats":{"Line":0},"fn_name":"put_with_on_completed_callback"},{"line":370,"address":[5985468],"length":1,"stats":{"Line":0},"fn_name":null},{"line":372,"address":[5986745,5985522],"length":1,"stats":{"Line":0},"fn_name":null},{"line":373,"address":[5985739],"length":1,"stats":{"Line":0},"fn_name":null},{"line":375,"address":[5984661,5984225,5984192,5984864,5984701,5984903,5985782],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":377,"address":[5984276,5984394,5984688,5984601],"length":1,"stats":{"Line":0},"fn_name":null},{"line":380,"address":[5986198,5987167,5985814,5986637],"length":1,"stats":{"Line":0},"fn_name":null},{"line":381,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":382,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":395,"address":[4126848,4134824,4126896,4127018,4134613,4127248,4127176,4126857,4127309],"length":1,"stats":{"Line":6},"fn_name":"{{closure}}"},{"line":396,"address":[4127372],"length":1,"stats":{"Line":1},"fn_name":null},{"line":397,"address":[4127478,4134479],"length":1,"stats":{"Line":2},"fn_name":null},{"line":398,"address":[4134423,4127678,4134643,4128077,4127549],"length":1,"stats":{"Line":3},"fn_name":null},{"line":399,"address":[4128373,4128479,4129039,4134664,4128067,4128816,4134390,4128294,4128888],"length":1,"stats":{"Line":6},"fn_name":null},{"line":400,"address":[4128369],"length":1,"stats":{"Line":1},"fn_name":null},{"line":401,"address":[4134380,4128791,4128890],"length":1,"stats":{"Line":1},"fn_name":null},{"line":404,"address":[4134340,4134685,4129129,4129627,4129189,4128848],"length":1,"stats":{"Line":4},"fn_name":null},{"line":405,"address":[4129591,4129868,4134323,4129977,4134706],"length":1,"stats":{"Line":4},"fn_name":null},{"line":406,"address":[4130655,4134727,4134284,4130143,4130293],"length":1,"stats":{"Line":3},"fn_name":null},{"line":408,"address":[4130605,4130904],"length":1,"stats":{"Line":2},"fn_name":null},{"line":410,"address":[4131499,4131061,4134748,4134241,4130940],"length":1,"stats":{"Line":3},"fn_name":null},{"line":411,"address":[4131841,4131740,4131463,4134224,4134769],"length":1,"stats":{"Line":4},"fn_name":null},{"line":412,"address":[4132157,4132495,4134185,4134790,4132007],"length":1,"stats":{"Line":3},"fn_name":null},{"line":415,"address":[4132720],"length":1,"stats":{"Line":1},"fn_name":null},{"line":418,"address":[4133067],"length":1,"stats":{"Line":1},"fn_name":null},{"line":419,"address":[4133269,4133510,4134140],"length":1,"stats":{"Line":1},"fn_name":null},{"line":420,"address":[4133731,4133453],"length":1,"stats":{"Line":2},"fn_name":null},{"line":422,"address":[4127062,4133842,4126909],"length":1,"stats":{"Line":3},"fn_name":null},{"line":426,"address":[4135568,4136029,4135896,4142416,4135968,4135577,4135738,4135616,4142268],"length":1,"stats":{"Line":6},"fn_name":"{{closure}}"},{"line":427,"address":[4136092],"length":1,"stats":{"Line":1},"fn_name":null},{"line":428,"address":[4142204,4136191],"length":1,"stats":{"Line":2},"fn_name":null},{"line":429,"address":[4142148,4136391,4136262,4142298,4136790],"length":1,"stats":{"Line":3},"fn_name":null},{"line":430,"address":[4142115,4142319,4137086,4137752,4136780,4137007,4137529,4137601,4137192],"length":1,"stats":{"Line":6},"fn_name":null},{"line":431,"address":[4137082],"length":1,"stats":{"Line":1},"fn_name":null},{"line":432,"address":[4137603,4142105,4137504],"length":1,"stats":{"Line":1},"fn_name":null},{"line":434,"address":[4138354,4142065,4142340,4137842,4137902,4137561],"length":1,"stats":{"Line":4},"fn_name":null},{"line":435,"address":[4138603,4138304],"length":1,"stats":{"Line":2},"fn_name":null},{"line":436,"address":[4142361,4138805,4138639,4142030],"length":1,"stats":{"Line":3},"fn_name":null},{"line":437,"address":[4141997,4142382,4139121,4138971,4139459],"length":1,"stats":{"Line":3},"fn_name":null},{"line":440,"address":[4139684],"length":1,"stats":{"Line":1},"fn_name":null},{"line":443,"address":[4140055],"length":1,"stats":{"Line":1},"fn_name":null},{"line":445,"address":[4141958,4140275,4140503],"length":1,"stats":{"Line":1},"fn_name":null},{"line":446,"address":[4140743,4140451],"length":1,"stats":{"Line":2},"fn_name":null},{"line":447,"address":[4140929],"length":1,"stats":{"Line":1},"fn_name":null},{"line":449,"address":[4141135],"length":1,"stats":{"Line":1},"fn_name":null},{"line":450,"address":[4141241],"length":1,"stats":{"Line":1},"fn_name":null},{"line":451,"address":[4141314],"length":1,"stats":{"Line":1},"fn_name":null},{"line":453,"address":[4141478],"length":1,"stats":{"Line":1},"fn_name":null},{"line":455,"address":[4135782,4135629,4141682],"length":1,"stats":{"Line":3},"fn_name":null},{"line":464,"address":[5158176,5158208,5158181,5158564],"length":1,"stats":{"Line":3},"fn_name":"{{closure}}"},{"line":466,"address":[5158215],"length":1,"stats":{"Line":1},"fn_name":null},{"line":471,"address":[5158394],"length":1,"stats":{"Line":1},"fn_name":null},{"line":472,"address":[5158260],"length":1,"stats":{"Line":1},"fn_name":null},{"line":478,"address":[5158613,5158640,5158608],"length":1,"stats":{"Line":3},"fn_name":"{{closure}}"},{"line":479,"address":[5158647],"length":1,"stats":{"Line":1},"fn_name":null},{"line":480,"address":[5158657],"length":1,"stats":{"Line":1},"fn_name":null},{"line":481,"address":[5158944],"length":1,"stats":{"Line":1},"fn_name":null},{"line":482,"address":[5158679],"length":1,"stats":{"Line":1},"fn_name":null},{"line":483,"address":[5158730],"length":1,"stats":{"Line":1},"fn_name":null},{"line":488,"address":[5159136,5159109,5159104],"length":1,"stats":{"Line":3},"fn_name":"{{closure}}"},{"line":489,"address":[5159143],"length":1,"stats":{"Line":1},"fn_name":null},{"line":490,"address":[5159153],"length":1,"stats":{"Line":1},"fn_name":null},{"line":491,"address":[5159440],"length":1,"stats":{"Line":1},"fn_name":null},{"line":492,"address":[5159175],"length":1,"stats":{"Line":1},"fn_name":null},{"line":493,"address":[5159226],"length":1,"stats":{"Line":1},"fn_name":null},{"line":498,"address":[5159600,5159605,5159632],"length":1,"stats":{"Line":3},"fn_name":"{{closure}}"},{"line":499,"address":[5159735],"length":1,"stats":{"Line":1},"fn_name":null},{"line":500,"address":[5159639],"length":1,"stats":{"Line":1},"fn_name":null},{"line":501,"address":[5159687],"length":1,"stats":{"Line":1},"fn_name":null},{"line":506,"address":[5159877,5159904,5159872],"length":1,"stats":{"Line":3},"fn_name":"{{closure}}"},{"line":507,"address":[5159911],"length":1,"stats":{"Line":1},"fn_name":null},{"line":508,"address":[5160015],"length":1,"stats":{"Line":1},"fn_name":null},{"line":509,"address":[5160119],"length":1,"stats":{"Line":1},"fn_name":null}],"covered":106,"coverable":210},{"path":["/","home","edward","Documents","repos","mangadex-home","src","cache","fs.rs"],"content":"//! This module contains two functions whose sole purpose is to allow a single\n//! producer multiple consumer (SPMC) system using the filesystem as an\n//! intermediate.\n//!\n//! Consider the scenario where two clients, A and B, request the same uncached\n//! file, one after the other. In a typical caching system, both requests would\n//! result in a cache miss, and both requests would then be proxied from\n//! upstream. But, we can do better. We know that by the time one request\n//! begins, there should be a file on disk for us to read from. Why require\n//! subsequent requests to read from upstream, when we can simply fetch one and\n//! read from the filesystem that we know will have the exact same data?\n//! Instead, we can just read from the filesystem and just inform all readers\n//! when the file is done. This is beneficial to both downstream and upstream as\n//! upstream no longer needs to process duplicate requests and sequential cache\n//! misses are treated as closer as a cache hit.\n\nuse std::error::Error;\nuse std::fmt::Display;\nuse std::io::{Seek, SeekFrom};\nuse std::path::Path;\nuse std::pin::Pin;\nuse std::task::{Context, Poll};\n\nuse actix_web::error::PayloadError;\nuse bytes::Bytes;\nuse futures::Future;\nuse log::{debug, warn};\nuse serde::{Deserialize, Serialize};\nuse sodiumoxide::crypto::secretstream::{\n Header, Pull, Push, Stream as SecretStream, Tag, HEADERBYTES,\n};\nuse tokio::fs::{create_dir_all, remove_file, File};\nuse tokio::io::{AsyncRead, AsyncReadExt, AsyncSeekExt, AsyncWrite, AsyncWriteExt, ReadBuf};\nuse tokio::sync::mpsc::Sender;\nuse tokio_util::codec::{BytesCodec, FramedRead};\n\nuse super::compat::LegacyImageMetadata;\nuse super::{CacheKey, ImageMetadata, InnerStream, ENCRYPTION_KEY};\n\n#[derive(Serialize, Deserialize)]\npub enum OnDiskMetadata {\n Encrypted(Header, ImageMetadata),\n Plaintext(ImageMetadata),\n}\n\n/// Attempts to lookup the file on disk, returning a byte stream if it exists.\n/// Note that this could return two types of streams, depending on if the file\n/// is in progress of being written to.\n#[inline]\npub(super) async fn read_file_from_path(\n path: \u0026Path,\n) -\u003e Option\u003cResult\u003c(InnerStream, Option\u003cHeader\u003e, ImageMetadata), std::io::Error\u003e\u003e {\n read_file(std::fs::File::open(path).ok()?).await\n}\n\nasync fn read_file(\n file: std::fs::File,\n) -\u003e Option\u003cResult\u003c(InnerStream, Option\u003cHeader\u003e, ImageMetadata), std::io::Error\u003e\u003e {\n let mut file_0 = file.try_clone().unwrap();\n let file_1 = file.try_clone().unwrap();\n\n // Try reading decrypted header first...\n let mut deserializer = serde_json::Deserializer::from_reader(file);\n let mut maybe_metadata = ImageMetadata::deserialize(\u0026mut deserializer);\n\n // Failed to parse normally, see if we have a legacy file format\n if maybe_metadata.is_err() {\n file_0.seek(SeekFrom::Start(2)).ok()?;\n let mut deserializer = serde_json::Deserializer::from_reader(file_0);\n maybe_metadata =\n LegacyImageMetadata::deserialize(\u0026mut deserializer).map(LegacyImageMetadata::into);\n }\n\n let parsed_metadata;\n let mut maybe_header = None;\n let mut reader: Option\u003cPin\u003cBox\u003cdyn AsyncRead + Send\u003e\u003e\u003e = None;\n if let Ok(metadata) = maybe_metadata {\n // image is decrypted\n if ENCRYPTION_KEY.get().is_some() {\n // invalidate cache since we're running in at-rest encryption and\n // the file wasn't encrypted.\n warn!(\"Found file but was not encrypted!\");\n return None;\n }\n\n reader = Some(Box::pin(File::from_std(file_1)));\n parsed_metadata = Some(metadata);\n debug!(\"Found not encrypted file\");\n } else {\n let mut file = File::from_std(file_1);\n file.seek(SeekFrom::Start(0)).await.ok()?;\n let file_0 = file.try_clone().await.unwrap();\n\n // image is encrypted or corrupt\n\n // If the encryption key was set, use the encrypted disk reader instead;\n // else, just directly read from file.\n if let Some(key) = ENCRYPTION_KEY.get() {\n let mut header_bytes = [0; HEADERBYTES];\n if let Err(e) = file.read_exact(\u0026mut header_bytes).await {\n warn!(\"Found file but failed reading header: {}\", e);\n return None;\n }\n\n let file_header = if let Some(header) = Header::from_slice(\u0026header_bytes) {\n header\n } else {\n warn!(\"Found file, but encrypted header was invalid. Assuming corrupted!\");\n return None;\n };\n\n let secret_stream = if let Ok(stream) = SecretStream::init_pull(\u0026file_header, key) {\n stream\n } else {\n warn!(\"Failed to init secret stream with key and header. Assuming corrupted!\");\n return None;\n };\n\n maybe_header = Some(file_header);\n\n reader = Some(Box::pin(EncryptedDiskReader::new(file, secret_stream)));\n }\n\n let mut deserializer = serde_json::Deserializer::from_reader(file_0.into_std().await);\n parsed_metadata = ImageMetadata::deserialize(\u0026mut deserializer).ok();\n\n if parsed_metadata.is_some() {\n debug!(\"Found encrypted file\");\n }\n }\n\n // parsed_metadata is either set or unset here. If it's set then we\n // successfully decoded the data; otherwise the file is garbage.\n\n if let Some(reader) = reader {\n let stream = InnerStream::Completed(FramedRead::new(reader, BytesCodec::new()));\n parsed_metadata.map(|metadata| Ok((stream, maybe_header, metadata)))\n } else {\n debug!(\"Reader was invalid, file is corrupt\");\n None\n }\n}\n\nstruct EncryptedDiskReader {\n file: Pin\u003cBox\u003cFile\u003e\u003e,\n stream: SecretStream\u003cPull\u003e,\n buf: Vec\u003cu8\u003e,\n}\n\nimpl EncryptedDiskReader {\n fn new(file: File, stream: SecretStream\u003cPull\u003e) -\u003e Self {\n Self {\n file: Box::pin(file),\n stream,\n buf: vec![],\n }\n }\n}\n\nimpl AsyncRead for EncryptedDiskReader {\n fn poll_read(\n mut self: Pin\u003c\u0026mut Self\u003e,\n cx: \u0026mut Context\u003c'_\u003e,\n buf: \u0026mut ReadBuf\u003c'_\u003e,\n ) -\u003e Poll\u003cstd::io::Result\u003c()\u003e\u003e {\n let cursor_start = buf.filled().len();\n\n let res = self.as_mut().file.as_mut().poll_read(cx, buf);\n if res.is_pending() {\n return Poll::Pending;\n }\n\n let cursor_new = buf.filled().len();\n\n // pull_to_vec internally calls vec.clear() and vec.reserve(). Generally\n // speaking we should be reading about the same amount of data each time\n // so we shouldn't experience too much of a slow down w.r.t resizing the\n // buffer...\n let new_self = Pin::into_inner(self);\n new_self\n .stream\n .pull_to_vec(\n \u0026buf.filled()[cursor_start..cursor_new],\n None,\n \u0026mut new_self.buf,\n )\n .unwrap();\n\n // data is strictly smaller than the encrypted stream, since you need to\n // encode tags as well, so this is always safe.\n\n // rewrite encrypted data into decrypted data\n let buffer = buf.filled_mut();\n for (old, new) in buffer[cursor_start..].iter_mut().zip(\u0026new_self.buf) {\n *old = *new;\n }\n buf.set_filled(cursor_start + new_self.buf.len());\n\n res\n }\n}\n\n/// Writes the metadata and input stream (in that order) to a file, returning a\n/// stream that reads from that file. Accepts a db callback function that is\n/// provided the number of bytes written, and an optional on-complete callback\n/// that is called with a completed cache entry.\npub(super) async fn write_file\u003cFut, DbCallback\u003e(\n path: \u0026Path,\n cache_key: CacheKey,\n bytes: Bytes,\n metadata: ImageMetadata,\n db_callback: DbCallback,\n on_complete: Option\u003cSender\u003c(CacheKey, Bytes, ImageMetadata, u64)\u003e\u003e,\n) -\u003e Result\u003c(), std::io::Error\u003e\nwhere\n Fut: 'static + Send + Sync + Future\u003cOutput = ()\u003e,\n DbCallback: 'static + Send + Sync + FnOnce(u64) -\u003e Fut,\n{\n let file = {\n let parent = path.parent().expect(\"The path to have a parent\");\n create_dir_all(parent).await?;\n let file = File::create(path).await?; // we need to make sure the file exists and is truncated.\n file\n };\n\n let metadata_string = serde_json::to_string(\u0026metadata).expect(\"serialization to work\");\n let metadata_size = metadata_string.len();\n\n let (mut writer, maybe_header): (Pin\u003cBox\u003cdyn AsyncWrite + Send\u003e\u003e, _) =\n if let Some((enc, header)) = ENCRYPTION_KEY\n .get()\n .map(|key| SecretStream::init_push(key).expect(\"Failed to init enc stream\"))\n {\n (Box::pin(EncryptedDiskWriter::new(file, enc)), Some(header))\n } else {\n (Box::pin(file), None)\n };\n\n let mut error = if let Some(header) = maybe_header {\n writer.write_all(header.as_ref()).await.err()\n } else {\n None\n };\n\n if error.is_none() {\n error = writer.write_all(metadata_string.as_bytes()).await.err();\n }\n if error.is_none() {\n error = writer.write_all(\u0026bytes).await.err();\n }\n\n if let Some(e) = error {\n // It's ok if the deleting the file fails, since we truncate on\n // create anyways, but it should be best effort.\n //\n // We don't care about the result of the call.\n std::mem::drop(remove_file(path).await);\n return Err(e);\n }\n\n writer.flush().await?;\n debug!(\"writing to file done\");\n\n let bytes_written = (metadata_size + bytes.len()) as u64;\n tokio::spawn(db_callback(bytes_written));\n\n if let Some(sender) = on_complete {\n tokio::spawn(async move {\n sender\n .send((cache_key, bytes, metadata, bytes_written))\n .await\n });\n }\n\n Ok(())\n}\n\nstruct EncryptedDiskWriter {\n file: Pin\u003cBox\u003cFile\u003e\u003e,\n stream: Option\u003cSecretStream\u003cPush\u003e\u003e,\n encryption_buffer: Vec\u003cu8\u003e,\n write_buffer: Vec\u003cu8\u003e,\n}\n\nimpl EncryptedDiskWriter {\n fn new(file: File, stream: SecretStream\u003cPush\u003e) -\u003e Self {\n Self {\n file: Box::pin(file),\n stream: Some(stream),\n encryption_buffer: vec![],\n write_buffer: vec![],\n }\n }\n}\n\nimpl AsyncWrite for EncryptedDiskWriter {\n fn poll_write(\n self: Pin\u003c\u0026mut Self\u003e,\n cx: \u0026mut Context\u003c'_\u003e,\n buf: \u0026[u8],\n ) -\u003e Poll\u003cResult\u003cusize, std::io::Error\u003e\u003e {\n let new_self = Pin::into_inner(self);\n {\n let encryption_buffer = \u0026mut new_self.encryption_buffer;\n if let Some(stream) = new_self.stream.as_mut() {\n stream\n .push_to_vec(buf, None, Tag::Message, encryption_buffer)\n .expect(\"Failed to write encrypted data to buffer\");\n }\n }\n\n new_self.write_buffer.extend(\u0026new_self.encryption_buffer);\n\n match new_self\n .file\n .as_mut()\n .poll_write(cx, \u0026new_self.write_buffer)\n {\n Poll::Ready(Ok(n)) =\u003e {\n new_self.write_buffer.drain(..n);\n // We buffered all the bytes that were provided to use.\n Poll::Ready(Ok(buf.len()))\n }\n poll =\u003e poll,\n }\n }\n\n fn poll_flush(\n mut self: Pin\u003c\u0026mut Self\u003e,\n cx: \u0026mut Context\u003c'_\u003e,\n ) -\u003e Poll\u003cResult\u003c(), std::io::Error\u003e\u003e {\n if self.as_ref().write_buffer.is_empty() {\n self.file.as_mut().poll_flush(cx)\n } else {\n let new_self = Pin::into_inner(self);\n let buffer = new_self.write_buffer.as_ref();\n match new_self.file.as_mut().poll_write(cx, buffer) {\n Poll::Ready(res) =\u003e {\n let n = res?;\n new_self.write_buffer.drain(..n);\n // We're immediately ready to do some more flushing!\n cx.waker().wake_by_ref();\n // Return pending here because we still need to flush the\n // file\n Poll::Pending\n }\n Poll::Pending =\u003e Poll::Pending,\n }\n }\n }\n\n fn poll_shutdown(\n mut self: Pin\u003c\u0026mut Self\u003e,\n cx: \u0026mut Context\u003c'_\u003e,\n ) -\u003e Poll\u003cResult\u003c(), std::io::Error\u003e\u003e {\n self.as_mut()\n .stream\n .take()\n .map(|stream| stream.finalize(None));\n self.file.as_mut().poll_shutdown(cx)\n }\n}\n\n/// Represents some upstream error.\n#[derive(Debug, PartialEq, Eq)]\npub struct UpstreamError;\n\nimpl Error for UpstreamError {}\n\nimpl Display for UpstreamError {\n fn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n write!(f, \"An upstream error occurred\")\n }\n}\n\nimpl From\u003cUpstreamError\u003e for actix_web::Error {\n #[inline]\n fn from(_: UpstreamError) -\u003e Self {\n PayloadError::Incomplete(None).into()\n }\n}\n\n#[cfg(test)]\nmod read_file {\n use crate::cache::{ImageContentType, ImageMetadata};\n\n use super::read_file;\n use bytes::Bytes;\n use chrono::DateTime;\n use futures::StreamExt;\n use std::io::{Seek, SeekFrom, Write};\n use tempfile::tempfile;\n\n #[tokio::test]\n async fn can_read() {\n let mut temp_file = tempfile().unwrap();\n temp_file\n .write_all(\n br#\"{\"content_type\":0,\"content_length\":708370,\"last_modified\":\"2021-04-13T04:37:41+00:00\"}abc\"#,\n )\n .unwrap();\n temp_file.seek(SeekFrom::Start(0)).unwrap();\n\n let (inner_stream, maybe_header, metadata) = read_file(temp_file).await.unwrap().unwrap();\n\n let foo: Vec\u003c_\u003e = inner_stream.collect().await;\n assert_eq!(foo, vec![Ok(Bytes::from(\"abc\"))]);\n assert!(maybe_header.is_none());\n assert_eq!(\n metadata,\n ImageMetadata {\n content_length: Some(708_370),\n content_type: Some(ImageContentType::Png),\n last_modified: Some(\n DateTime::parse_from_rfc3339(\"2021-04-13T04:37:41+00:00\").unwrap()\n )\n }\n );\n }\n}\n\n#[cfg(test)]\nmod read_file_compat {\n use crate::cache::{ImageContentType, ImageMetadata};\n\n use super::read_file;\n use bytes::Bytes;\n use chrono::DateTime;\n use futures::StreamExt;\n use std::io::{Seek, SeekFrom, Write};\n use tempfile::tempfile;\n\n #[tokio::test]\n async fn can_read_legacy() {\n let mut temp_file = tempfile().unwrap();\n temp_file\n .write_all(\n b\"\\x00\\x5b{\\\"content_type\\\":\\\"image/jpeg\\\",\\\"last_modified\\\":\\\"Sat, 10 Apr 2021 10:55:22 GMT\\\",\\\"size\\\":117888}abc\",\n )\n .unwrap();\n temp_file.seek(SeekFrom::Start(0)).unwrap();\n\n let (inner_stream, maybe_header, metadata) = read_file(temp_file).await.unwrap().unwrap();\n\n let foo: Vec\u003c_\u003e = inner_stream.collect().await;\n assert_eq!(foo, vec![Ok(Bytes::from(\"abc\"))]);\n assert!(maybe_header.is_none());\n assert_eq!(\n metadata,\n ImageMetadata {\n content_length: Some(117_888),\n content_type: Some(ImageContentType::Jpeg),\n last_modified: Some(\n DateTime::parse_from_rfc2822(\"Sat, 10 Apr 2021 10:55:22 GMT\").unwrap()\n )\n }\n );\n }\n}\n","traces":[{"line":50,"address":[4325680],"length":1,"stats":{"Line":0},"fn_name":"read_file_from_path"},{"line":53,"address":[4326359,4326054,4326111,4325885,4326404],"length":1,"stats":{"Line":0},"fn_name":null},{"line":56,"address":[4326576],"length":1,"stats":{"Line":2},"fn_name":"read_file"},{"line":59,"address":[4327189],"length":1,"stats":{"Line":2},"fn_name":null},{"line":60,"address":[4327293],"length":1,"stats":{"Line":2},"fn_name":null},{"line":63,"address":[4327379],"length":1,"stats":{"Line":2},"fn_name":null},{"line":64,"address":[4327407],"length":1,"stats":{"Line":2},"fn_name":null},{"line":67,"address":[4327429,4327537],"length":1,"stats":{"Line":3},"fn_name":null},{"line":68,"address":[4327550,4327729,4335462,4327470],"length":1,"stats":{"Line":2},"fn_name":null},{"line":69,"address":[4327701],"length":1,"stats":{"Line":1},"fn_name":null},{"line":70,"address":[4334492,4327814],"length":1,"stats":{"Line":1},"fn_name":null},{"line":71,"address":[4327776],"length":1,"stats":{"Line":1},"fn_name":null},{"line":75,"address":[4327836],"length":1,"stats":{"Line":1},"fn_name":null},{"line":76,"address":[4327843],"length":1,"stats":{"Line":1},"fn_name":null},{"line":77,"address":[4327858,4327915,4328840],"length":1,"stats":{"Line":3},"fn_name":null},{"line":79,"address":[4327939],"length":1,"stats":{"Line":1},"fn_name":null},{"line":82,"address":[4328192,4328017,4328107],"length":1,"stats":{"Line":0},"fn_name":null},{"line":83,"address":[4328360],"length":1,"stats":{"Line":0},"fn_name":null},{"line":86,"address":[4328071,4328532,4334589],"length":1,"stats":{"Line":2},"fn_name":null},{"line":87,"address":[4334664],"length":1,"stats":{"Line":1},"fn_name":null},{"line":88,"address":[4328654,4334760,4328569],"length":1,"stats":{"Line":2},"fn_name":null},{"line":90,"address":[4327875,4328853],"length":1,"stats":{"Line":0},"fn_name":null},{"line":91,"address":[4329391,4335368,4328860,4329018,4335467],"length":1,"stats":{"Line":0},"fn_name":null},{"line":92,"address":[4329501,4329368,4335389,4329430,4334258],"length":1,"stats":{"Line":0},"fn_name":null},{"line":98,"address":[4329926,4334889],"length":1,"stats":{"Line":0},"fn_name":null},{"line":99,"address":[4330021],"length":1,"stats":{"Line":0},"fn_name":null},{"line":100,"address":[4330052,4330411,4330155,4335410],"length":1,"stats":{"Line":0},"fn_name":null},{"line":101,"address":[4330555,4330427],"length":1,"stats":{"Line":0},"fn_name":null},{"line":102,"address":[4330830],"length":1,"stats":{"Line":0},"fn_name":null},{"line":105,"address":[4330975,4330872],"length":1,"stats":{"Line":0},"fn_name":null},{"line":106,"address":[4331007],"length":1,"stats":{"Line":0},"fn_name":null},{"line":108,"address":[4331076,4330921,4331161],"length":1,"stats":{"Line":0},"fn_name":null},{"line":109,"address":[4331329],"length":1,"stats":{"Line":0},"fn_name":null},{"line":112,"address":[4331346,4331039,4331430],"length":1,"stats":{"Line":0},"fn_name":null},{"line":113,"address":[4331494],"length":1,"stats":{"Line":0},"fn_name":null},{"line":115,"address":[4331870,4331946,4331376],"length":1,"stats":{"Line":0},"fn_name":null},{"line":116,"address":[4332105],"length":1,"stats":{"Line":0},"fn_name":null},{"line":119,"address":[4331558],"length":1,"stats":{"Line":0},"fn_name":null},{"line":121,"address":[4334814,4331660,4332253],"length":1,"stats":{"Line":0},"fn_name":null},{"line":124,"address":[4334214,4332513,4332298,4335431],"length":1,"stats":{"Line":0},"fn_name":null},{"line":125,"address":[4332712],"length":1,"stats":{"Line":0},"fn_name":null},{"line":127,"address":[4332782,4333082,4332845],"length":1,"stats":{"Line":0},"fn_name":null},{"line":128,"address":[4332926,4332850,4332805],"length":1,"stats":{"Line":0},"fn_name":null},{"line":135,"address":[4333223,4333809,4333150],"length":1,"stats":{"Line":2},"fn_name":null},{"line":136,"address":[4333254,4334161,4335097],"length":1,"stats":{"Line":1},"fn_name":null},{"line":137,"address":[4335472,4326667,4326640,4333420],"length":1,"stats":{"Line":4},"fn_name":"{{closure}}"},{"line":139,"address":[4333641,4333172,4333565],"length":1,"stats":{"Line":0},"fn_name":null},{"line":140,"address":[4333797],"length":1,"stats":{"Line":0},"fn_name":null},{"line":151,"address":[4336064,4336325],"length":1,"stats":{"Line":0},"fn_name":"new"},{"line":153,"address":[4336085,4336151],"length":1,"stats":{"Line":0},"fn_name":null},{"line":155,"address":[4336207],"length":1,"stats":{"Line":0},"fn_name":null},{"line":161,"address":[4337678,4336368],"length":1,"stats":{"Line":0},"fn_name":"poll_read"},{"line":166,"address":[4336434],"length":1,"stats":{"Line":0},"fn_name":null},{"line":168,"address":[4336652,4336546],"length":1,"stats":{"Line":0},"fn_name":null},{"line":169,"address":[4336717],"length":1,"stats":{"Line":0},"fn_name":null},{"line":170,"address":[4336746],"length":1,"stats":{"Line":0},"fn_name":null},{"line":173,"address":[4336781],"length":1,"stats":{"Line":0},"fn_name":null},{"line":179,"address":[4336864],"length":1,"stats":{"Line":0},"fn_name":null},{"line":180,"address":[4336912,4337075],"length":1,"stats":{"Line":0},"fn_name":null},{"line":181,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":183,"address":[4336921],"length":1,"stats":{"Line":0},"fn_name":null},{"line":184,"address":[4337059],"length":1,"stats":{"Line":0},"fn_name":null},{"line":185,"address":[4337071],"length":1,"stats":{"Line":0},"fn_name":null},{"line":193,"address":[4337138],"length":1,"stats":{"Line":0},"fn_name":null},{"line":194,"address":[4337557,4337195,4337487],"length":1,"stats":{"Line":0},"fn_name":null},{"line":195,"address":[4337553],"length":1,"stats":{"Line":0},"fn_name":null},{"line":197,"address":[4337464,4337715,4337575],"length":1,"stats":{"Line":0},"fn_name":null},{"line":199,"address":[4337608],"length":1,"stats":{"Line":0},"fn_name":null},{"line":207,"address":[4337760,4338080],"length":1,"stats":{"Line":0},"fn_name":"write_file\u003ccore::future::from_generator::GenFuture\u003cgenerator-0\u003e,closure-0\u003e"},{"line":220,"address":[4341062,4350630],"length":1,"stats":{"Line":0},"fn_name":null},{"line":221,"address":[4350781,4348730,4349598,4341299,4341698,4350867,4351266,4358298,4359166,4341213],"length":1,"stats":{"Line":0},"fn_name":null},{"line":222,"address":[4342026,4351594,4341666,4342819,4352387,4358274,4351523,4351234,4359187,4349619,4348706,4341955],"length":1,"stats":{"Line":0},"fn_name":null},{"line":223,"address":[4342675,4352243],"length":1,"stats":{"Line":0},"fn_name":null},{"line":226,"address":[4342794,4352644,4343076,4352362],"length":1,"stats":{"Line":0},"fn_name":null},{"line":227,"address":[4352693,4343125],"length":1,"stats":{"Line":0},"fn_name":null},{"line":229,"address":[4343186,4352754,4353759,4353113,4344191,4343545],"length":1,"stats":{"Line":0},"fn_name":null},{"line":232,"address":[4338400,4338419,4338480,4338499],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}\u003ccore::future::from_generator::GenFuture\u003cgenerator-0\u003e,closure-0\u003e"},{"line":234,"address":[4343641,4353209,4353570,4344002],"length":1,"stats":{"Line":0},"fn_name":null},{"line":236,"address":[4352844,4343276,4353769,4344201],"length":1,"stats":{"Line":0},"fn_name":null},{"line":239,"address":[4354002,4344394,4353962,4344434],"length":1,"stats":{"Line":0},"fn_name":null},{"line":240,"address":[4354055,4344668,4354236,4349640,4344487,4359208],"length":1,"stats":{"Line":0},"fn_name":null},{"line":242,"address":[4353988,4344420],"length":1,"stats":{"Line":0},"fn_name":null},{"line":245,"address":[4358580,4345040,4349012,4354608,4345137,4354705],"length":1,"stats":{"Line":0},"fn_name":null},{"line":246,"address":[4345084,4348925,4349661,4345174,4354652,4345280,4354848,4354742,4358493,4359229],"length":1,"stats":{"Line":0},"fn_name":null},{"line":248,"address":[4345726,4345629,4355294,4358680,4355197,4349112],"length":1,"stats":{"Line":0},"fn_name":null},{"line":249,"address":[4349682,4349025,4355437,4355241,4345869,4345763,4358593,4345673,4355331,4359250],"length":1,"stats":{"Line":0},"fn_name":null},{"line":252,"address":[4346291,4355859,4355786,4346218],"length":1,"stats":{"Line":0},"fn_name":null},{"line":257,"address":[4355992,4349703,4355880,4358164,4346424,4348596,4359271,4346312],"length":1,"stats":{"Line":0},"fn_name":null},{"line":258,"address":[4346708,4356276],"length":1,"stats":{"Line":0},"fn_name":null},{"line":261,"address":[4355823,4346255,4347206,4346791,4356359,4356774,4348583,4346844,4349724,4356412,4358151,4359292],"length":1,"stats":{"Line":0},"fn_name":null},{"line":262,"address":[4347431,4347163,4347507,4356731,4356999,4357075],"length":1,"stats":{"Line":0},"fn_name":null},{"line":264,"address":[4347671,4350115,4359683,4357239],"length":1,"stats":{"Line":0},"fn_name":null},{"line":265,"address":[4347755,4357323],"length":1,"stats":{"Line":0},"fn_name":null},{"line":267,"address":[4347877,4348179,4357445,4357747],"length":1,"stats":{"Line":0},"fn_name":null},{"line":268,"address":[4357510,4339691,4347942,4338560,4339448,4338603,4339488,4340536,4340576,4339648],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}\u003ccore::future::from_generator::GenFuture\u003cgenerator-0\u003e,closure-0\u003e"},{"line":269,"address":[4339475,4339997,4340462,4338909,4340563,4338978,4340066,4339374],"length":1,"stats":{"Line":0},"fn_name":null},{"line":270,"address":[4339742,4338654],"length":1,"stats":{"Line":0},"fn_name":null},{"line":275,"address":[4357757,4348189],"length":1,"stats":{"Line":0},"fn_name":null},{"line":286,"address":[4359872,4360266],"length":1,"stats":{"Line":0},"fn_name":"new"},{"line":288,"address":[4359959,4359893],"length":1,"stats":{"Line":0},"fn_name":null},{"line":289,"address":[4359968],"length":1,"stats":{"Line":0},"fn_name":null},{"line":290,"address":[4360079],"length":1,"stats":{"Line":0},"fn_name":null},{"line":291,"address":[4360098],"length":1,"stats":{"Line":0},"fn_name":null},{"line":297,"address":[4360336,4361110],"length":1,"stats":{"Line":0},"fn_name":"poll_write"},{"line":302,"address":[4360400],"length":1,"stats":{"Line":0},"fn_name":null},{"line":304,"address":[4360434],"length":1,"stats":{"Line":0},"fn_name":null},{"line":305,"address":[4360454,4360609],"length":1,"stats":{"Line":0},"fn_name":null},{"line":306,"address":[4360538],"length":1,"stats":{"Line":0},"fn_name":null},{"line":307,"address":[4360521],"length":1,"stats":{"Line":0},"fn_name":null},{"line":312,"address":[4360616],"length":1,"stats":{"Line":0},"fn_name":null},{"line":314,"address":[4360725,4360887],"length":1,"stats":{"Line":0},"fn_name":null},{"line":315,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":316,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":317,"address":[4360685],"length":1,"stats":{"Line":0},"fn_name":null},{"line":319,"address":[4360747,4360897],"length":1,"stats":{"Line":0},"fn_name":null},{"line":320,"address":[4360913],"length":1,"stats":{"Line":0},"fn_name":null},{"line":322,"address":[4360981],"length":1,"stats":{"Line":0},"fn_name":null},{"line":324,"address":[4360796],"length":1,"stats":{"Line":0},"fn_name":null},{"line":328,"address":[4361168],"length":1,"stats":{"Line":0},"fn_name":"poll_flush"},{"line":332,"address":[4362012,4361240],"length":1,"stats":{"Line":0},"fn_name":null},{"line":333,"address":[4361289,4361391],"length":1,"stats":{"Line":0},"fn_name":null},{"line":335,"address":[4361306],"length":1,"stats":{"Line":0},"fn_name":null},{"line":336,"address":[4361452],"length":1,"stats":{"Line":0},"fn_name":null},{"line":337,"address":[4361590,4361555],"length":1,"stats":{"Line":0},"fn_name":null},{"line":338,"address":[4361597,4361566],"length":1,"stats":{"Line":0},"fn_name":null},{"line":339,"address":[4361794,4361639],"length":1,"stats":{"Line":0},"fn_name":null},{"line":340,"address":[4361970,4361755],"length":1,"stats":{"Line":0},"fn_name":null},{"line":342,"address":[4361989],"length":1,"stats":{"Line":0},"fn_name":null},{"line":345,"address":[4362007],"length":1,"stats":{"Line":0},"fn_name":null},{"line":347,"address":[4361585],"length":1,"stats":{"Line":0},"fn_name":null},{"line":352,"address":[4362032],"length":1,"stats":{"Line":0},"fn_name":"poll_shutdown"},{"line":356,"address":[4362104],"length":1,"stats":{"Line":0},"fn_name":null},{"line":357,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":359,"address":[4362320,4362334,4362164],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":360,"address":[4362174,4362246],"length":1,"stats":{"Line":0},"fn_name":null},{"line":371,"address":[4362400],"length":1,"stats":{"Line":0},"fn_name":"fmt"},{"line":372,"address":[4362419],"length":1,"stats":{"Line":0},"fn_name":null},{"line":378,"address":[4362496],"length":1,"stats":{"Line":0},"fn_name":"from"},{"line":379,"address":[4362500],"length":1,"stats":{"Line":0},"fn_name":null},{"line":395,"address":[4143296,4142976,4142981,4143122,4143335,4143236,4145567,4145675,4143008],"length":1,"stats":{"Line":6},"fn_name":"{{closure}}"},{"line":396,"address":[4143395],"length":1,"stats":{"Line":1},"fn_name":null},{"line":397,"address":[4143498],"length":1,"stats":{"Line":1},"fn_name":null},{"line":402,"address":[4143633],"length":1,"stats":{"Line":1},"fn_name":null},{"line":404,"address":[4145641,4143723,4143818,4145544],"length":1,"stats":{"Line":2},"fn_name":null},{"line":406,"address":[4144206,4145499,4145662,4144368],"length":1,"stats":{"Line":3},"fn_name":null},{"line":407,"address":[4144572,4145448],"length":1,"stats":{"Line":1},"fn_name":null},{"line":408,"address":[4144895],"length":1,"stats":{"Line":1},"fn_name":null},{"line":409,"address":[4145233,4144965,4143015,4143166],"length":1,"stats":{"Line":4},"fn_name":null},{"line":411,"address":[4145161],"length":1,"stats":{"Line":1},"fn_name":null},{"line":412,"address":[4144974],"length":1,"stats":{"Line":1},"fn_name":null},{"line":413,"address":[4144996],"length":1,"stats":{"Line":1},"fn_name":null},{"line":414,"address":[4145134],"length":1,"stats":{"Line":1},"fn_name":null},{"line":415,"address":[4145018],"length":1,"stats":{"Line":1},"fn_name":null},{"line":434,"address":[4145973,4146000,4146327,4148559,4146288,4146228,4145968,4146114,4148667],"length":1,"stats":{"Line":6},"fn_name":"{{closure}}"},{"line":435,"address":[4146387],"length":1,"stats":{"Line":1},"fn_name":null},{"line":436,"address":[4146490],"length":1,"stats":{"Line":1},"fn_name":null},{"line":441,"address":[4146625],"length":1,"stats":{"Line":1},"fn_name":null},{"line":443,"address":[4146810,4146715,4148633,4148536],"length":1,"stats":{"Line":2},"fn_name":null},{"line":445,"address":[4147360,4147198,4148491,4148654],"length":1,"stats":{"Line":3},"fn_name":null},{"line":446,"address":[4148440,4147564],"length":1,"stats":{"Line":1},"fn_name":null},{"line":447,"address":[4147887],"length":1,"stats":{"Line":1},"fn_name":null},{"line":448,"address":[4146158,4147957,4148225,4146007],"length":1,"stats":{"Line":4},"fn_name":null},{"line":450,"address":[4148153],"length":1,"stats":{"Line":1},"fn_name":null},{"line":451,"address":[4147966],"length":1,"stats":{"Line":1},"fn_name":null},{"line":452,"address":[4147988],"length":1,"stats":{"Line":1},"fn_name":null},{"line":453,"address":[4148126],"length":1,"stats":{"Line":1},"fn_name":null},{"line":454,"address":[4148010],"length":1,"stats":{"Line":1},"fn_name":null}],"covered":48,"coverable":167},{"path":["/","home","edward","Documents","repos","mangadex-home","src","cache","mem.rs"],"content":"use std::sync::atomic::{AtomicU64, Ordering};\nuse std::sync::Arc;\n\nuse super::{\n Cache, CacheError, CacheKey, CacheStream, CallbackCache, ImageMetadata, InnerStream, MemStream,\n};\nuse async_trait::async_trait;\nuse bytes::Bytes;\nuse futures::FutureExt;\nuse lfu_cache::LfuCache;\nuse lru::LruCache;\nuse tokio::sync::mpsc::{channel, Sender};\nuse tokio::sync::Mutex;\n\ntype CacheValue = (Bytes, ImageMetadata, u64);\n\n/// Use LRU as the eviction strategy\npub type Lru = LruCache\u003cCacheKey, CacheValue\u003e;\n/// Use LFU as the eviction strategy\npub type Lfu = LfuCache\u003cCacheKey, CacheValue\u003e;\n\n/// Adapter trait for memory cache backends\npub trait InternalMemoryCache: Sync + Send {\n fn unbounded() -\u003e Self;\n fn get(\u0026mut self, key: \u0026CacheKey) -\u003e Option\u003c\u0026CacheValue\u003e;\n fn push(\u0026mut self, key: CacheKey, data: CacheValue);\n fn pop(\u0026mut self) -\u003e Option\u003c(CacheKey, CacheValue)\u003e;\n}\n\nimpl InternalMemoryCache for Lfu {\n #[inline]\n fn unbounded() -\u003e Self {\n Self::unbounded()\n }\n\n #[inline]\n fn get(\u0026mut self, key: \u0026CacheKey) -\u003e Option\u003c\u0026CacheValue\u003e {\n self.get(key)\n }\n\n #[inline]\n fn push(\u0026mut self, key: CacheKey, data: CacheValue) {\n self.insert(key, data);\n }\n\n #[inline]\n fn pop(\u0026mut self) -\u003e Option\u003c(CacheKey, CacheValue)\u003e {\n self.pop_lfu_key_value()\n }\n}\n\nimpl InternalMemoryCache for Lru {\n #[inline]\n fn unbounded() -\u003e Self {\n Self::unbounded()\n }\n\n #[inline]\n fn get(\u0026mut self, key: \u0026CacheKey) -\u003e Option\u003c\u0026CacheValue\u003e {\n self.get(key)\n }\n\n #[inline]\n fn push(\u0026mut self, key: CacheKey, data: CacheValue) {\n self.put(key, data);\n }\n\n #[inline]\n fn pop(\u0026mut self) -\u003e Option\u003c(CacheKey, CacheValue)\u003e {\n self.pop_lru()\n }\n}\n\n/// Memory accelerated disk cache. Uses the internal cache implementation in\n/// memory to speed up reads.\npub struct MemoryCache\u003cMemoryCacheImpl, ColdCache\u003e {\n inner: ColdCache,\n cur_mem_size: AtomicU64,\n mem_cache: Mutex\u003cMemoryCacheImpl\u003e,\n master_sender: Sender\u003c(CacheKey, Bytes, ImageMetadata, u64)\u003e,\n}\n\nimpl\u003cMemoryCacheImpl, ColdCache\u003e MemoryCache\u003cMemoryCacheImpl, ColdCache\u003e\nwhere\n MemoryCacheImpl: 'static + InternalMemoryCache,\n ColdCache: 'static + Cache,\n{\n pub async fn new(inner: ColdCache, max_mem_size: crate::units::Bytes) -\u003e Arc\u003cSelf\u003e {\n let (tx, mut rx) = channel(100);\n let new_self = Arc::new(Self {\n inner,\n cur_mem_size: AtomicU64::new(0),\n mem_cache: Mutex::new(MemoryCacheImpl::unbounded()),\n master_sender: tx,\n });\n\n let new_self_0 = Arc::clone(\u0026new_self);\n tokio::spawn(async move {\n let new_self = new_self_0;\n let max_mem_size = max_mem_size.get() / 20 * 19;\n while let Some((key, bytes, metadata, size)) = rx.recv().await {\n // Add to memory cache\n // We can add first because we constrain our memory usage to 95%\n new_self\n .cur_mem_size\n .fetch_add(size as u64, Ordering::Release);\n new_self\n .mem_cache\n .lock()\n .await\n .push(key, (bytes, metadata, size));\n\n // Pop if too large\n while new_self.cur_mem_size.load(Ordering::Acquire) \u003e= max_mem_size as u64 {\n let popped = new_self\n .mem_cache\n .lock()\n .await\n .pop()\n .map(|(key, (bytes, metadata, size))| (key, bytes, metadata, size));\n if let Some((_, _, _, size)) = popped {\n new_self\n .cur_mem_size\n .fetch_sub(size as u64, Ordering::Release);\n } else {\n break;\n }\n }\n }\n });\n\n new_self\n }\n}\n\n#[async_trait]\nimpl\u003cMemoryCacheImpl, ColdCache\u003e Cache for MemoryCache\u003cMemoryCacheImpl, ColdCache\u003e\nwhere\n MemoryCacheImpl: InternalMemoryCache,\n ColdCache: CallbackCache,\n{\n #[inline]\n async fn get(\n \u0026self,\n key: \u0026CacheKey,\n ) -\u003e Option\u003cResult\u003c(CacheStream, ImageMetadata), super::CacheError\u003e\u003e {\n match self.mem_cache.lock().now_or_never() {\n Some(mut mem_cache) =\u003e match mem_cache.get(key).map(|(bytes, metadata, _)| {\n Ok((InnerStream::Memory(MemStream(bytes.clone())), *metadata))\n }) {\n Some(v) =\u003e Some(v.and_then(|(inner, metadata)| {\n CacheStream::new(inner, None)\n .map(|v| (v, metadata))\n .map_err(|_| CacheError::DecryptionFailure)\n })),\n None =\u003e self.inner.get(key).await,\n },\n None =\u003e self.inner.get(key).await,\n }\n }\n\n #[inline]\n async fn put(\n \u0026self,\n key: CacheKey,\n image: Bytes,\n metadata: ImageMetadata,\n ) -\u003e Result\u003c(), super::CacheError\u003e {\n self.inner\n .put_with_on_completed_callback(key, image, metadata, self.master_sender.clone())\n .await\n }\n}\n","traces":[{"line":32,"address":[5343056],"length":1,"stats":{"Line":0},"fn_name":"unbounded"},{"line":33,"address":[5343064],"length":1,"stats":{"Line":0},"fn_name":null},{"line":37,"address":[5343088],"length":1,"stats":{"Line":0},"fn_name":"get"},{"line":38,"address":[5343102],"length":1,"stats":{"Line":0},"fn_name":null},{"line":42,"address":[5343120],"length":1,"stats":{"Line":0},"fn_name":"push"},{"line":43,"address":[5343145],"length":1,"stats":{"Line":0},"fn_name":null},{"line":47,"address":[5343232],"length":1,"stats":{"Line":0},"fn_name":"pop"},{"line":48,"address":[5343249],"length":1,"stats":{"Line":0},"fn_name":null},{"line":54,"address":[4126032],"length":1,"stats":{"Line":0},"fn_name":"unbounded"},{"line":55,"address":[4126040],"length":1,"stats":{"Line":0},"fn_name":null},{"line":59,"address":[4126064],"length":1,"stats":{"Line":0},"fn_name":"get"},{"line":60,"address":[4126078],"length":1,"stats":{"Line":0},"fn_name":null},{"line":64,"address":[4126096],"length":1,"stats":{"Line":0},"fn_name":"push"},{"line":65,"address":[4126121],"length":1,"stats":{"Line":0},"fn_name":null},{"line":69,"address":[4126208],"length":1,"stats":{"Line":0},"fn_name":"pop"},{"line":70,"address":[4126225],"length":1,"stats":{"Line":0},"fn_name":null},{"line":88,"address":[4370948,4362662,4370177,4369072,4362576,4369105,4370144,4370857,4371014,4369884,4362640,4369950,4362598,4369793],"length":1,"stats":{"Line":0},"fn_name":"new\u003clfu_cache::lfu::LfuCache\u003cmangadex_home::cache::CacheKey, (bytes::bytes::Bytes, mangadex_home::cache::ImageMetadata, u64)\u003e,alloc::sync::Arc\u003cmangadex_home::cache::disk::DiskCache\u003e\u003e"},{"line":89,"address":[4369219,4370291],"length":1,"stats":{"Line":0},"fn_name":null},{"line":90,"address":[4369398,4370470],"length":1,"stats":{"Line":0},"fn_name":null},{"line":91,"address":[4370350,4369278],"length":1,"stats":{"Line":0},"fn_name":null},{"line":92,"address":[4369303,4370375],"length":1,"stats":{"Line":0},"fn_name":null},{"line":93,"address":[4369355,4370427],"length":1,"stats":{"Line":0},"fn_name":null},{"line":94,"address":[4369385,4370457],"length":1,"stats":{"Line":0},"fn_name":null},{"line":97,"address":[4369604,4370670],"length":1,"stats":{"Line":0},"fn_name":null},{"line":98,"address":[4368638,4363536,4365955,4368723,4365870,4369637,4366304,4366343,4363575,4370703],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}\u003clru::LruCache\u003cmangadex_home::cache::CacheKey, (bytes::bytes::Bytes, mangadex_home::cache::ImageMetadata, u64), ahash::random_state::RandomState\u003e,alloc::sync::Arc\u003cmangadex_home::cache::disk::DiskCache\u003e\u003e"},{"line":99,"address":[4366406,4363638],"length":1,"stats":{"Line":0},"fn_name":null},{"line":100,"address":[4368826,4366427,4363659,4366058],"length":1,"stats":{"Line":0},"fn_name":null},{"line":101,"address":[4366896,4366657,4363889,4364128,4365543,4366524,4368311,4368668,4365900,4363756],"length":1,"stats":{"Line":0},"fn_name":null},{"line":104,"address":[4367018,4367069,4364250,4364301],"length":1,"stats":{"Line":0},"fn_name":null},{"line":105,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":106,"address":[4364286,4367054],"length":1,"stats":{"Line":0},"fn_name":null},{"line":107,"address":[4365664,4367092,4368432,4368689,4364324,4365921,4367649,4364881,4367211,4364443],"length":1,"stats":{"Line":0},"fn_name":null},{"line":108,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":109,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":110,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":111,"address":[4364896,4368409,4365641,4367420,4367664,4364652],"length":1,"stats":{"Line":0},"fn_name":null},{"line":114,"address":[4364911,4367679],"length":1,"stats":{"Line":0},"fn_name":null},{"line":115,"address":[4367770,4368395,4365121,4368710,4365002,4365942,4367889,4365627],"length":1,"stats":{"Line":0},"fn_name":null},{"line":116,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":117,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":118,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":119,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":120,"address":[4365368,4365604,4362721,4363120,4368136,4362704,4363137,4368372],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}\u003clru::LruCache\u003cmangadex_home::cache::CacheKey, (bytes::bytes::Bytes, mangadex_home::cache::ImageMetadata, u64), ahash::random_state::RandomState\u003e,alloc::sync::Arc\u003cmangadex_home::cache::disk::DiskCache\u003e\u003e"},{"line":121,"address":[4368143,4365428,4365375,4368196],"length":1,"stats":{"Line":0},"fn_name":null},{"line":122,"address":[4365448,4368216,4365488,4368256],"length":1,"stats":{"Line":0},"fn_name":null},{"line":123,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":124,"address":[4368248,4365480],"length":1,"stats":{"Line":0},"fn_name":null},{"line":126,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":132,"address":[4370836,4369772],"length":1,"stats":{"Line":0},"fn_name":null},{"line":143,"address":[4393504,4393648],"length":1,"stats":{"Line":0},"fn_name":"get\u003clru::LruCache\u003cmangadex_home::cache::CacheKey, (bytes::bytes::Bytes, mangadex_home::cache::ImageMetadata, u64), ahash::random_state::RandomState\u003e,alloc::sync::Arc\u003cmangadex_home::cache::disk::DiskCache\u003e\u003e"},{"line":147,"address":[4397787,4395727,4395883,4397631],"length":1,"stats":{"Line":0},"fn_name":null},{"line":148,"address":[4397723,4394203,4398207,4393792,4393819,4397797,4396045,4395819,4395893,4394176,4396303,4397949],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}\u003clru::LruCache\u003cmangadex_home::cache::CacheKey, (bytes::bytes::Bytes, mangadex_home::cache::ImageMetadata, u64), ahash::random_state::RandomState\u003e,alloc::sync::Arc\u003cmangadex_home::cache::disk::DiskCache\u003e\u003e"},{"line":149,"address":[4393847,4394231],"length":1,"stats":{"Line":0},"fn_name":null},{"line":151,"address":[4395040,4395061,4397959,4395983,4396055,4395269,4395248,4397887],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}\u003clfu_cache::lfu::LfuCache\u003cmangadex_home::cache::CacheKey, (bytes::bytes::Bytes, mangadex_home::cache::ImageMetadata, u64)\u003e,alloc::sync::Arc\u003cmangadex_home::cache::disk::DiskCache\u003e\u003e"},{"line":152,"address":[4395190,4395325,4395398,4395117],"length":1,"stats":{"Line":0},"fn_name":null},{"line":153,"address":[4395385,4394603,4394768,4394560,4394811,4395177],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}\u003clru::LruCache\u003cmangadex_home::cache::CacheKey, (bytes::bytes::Bytes, mangadex_home::cache::ImageMetadata, u64), ahash::random_state::RandomState\u003e,alloc::sync::Arc\u003cmangadex_home::cache::disk::DiskCache\u003e\u003e"},{"line":154,"address":[4394983,4395015,4394976,4395008],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}\u003clru::LruCache\u003cmangadex_home::cache::CacheKey, (bytes::bytes::Bytes, mangadex_home::cache::ImageMetadata, u64), ahash::random_state::RandomState\u003e,alloc::sync::Arc\u003cmangadex_home::cache::disk::DiskCache\u003e\u003e"},{"line":156,"address":[4398871,4398976,4396383,4396019,4396326,4396967,4398230,4398287,4397923,4397072],"length":1,"stats":{"Line":0},"fn_name":null},{"line":158,"address":[4398586,4398852,4397093,4395854,4396633,4396948,4397758,4398997,4398537,4396682],"length":1,"stats":{"Line":0},"fn_name":null},{"line":163,"address":[4399264,4399536],"length":1,"stats":{"Line":0},"fn_name":"put\u003clfu_cache::lfu::LfuCache\u003cmangadex_home::cache::CacheKey, (bytes::bytes::Bytes, mangadex_home::cache::ImageMetadata, u64)\u003e,alloc::sync::Arc\u003cmangadex_home::cache::disk::DiskCache\u003e\u003e"},{"line":169,"address":[4401044,4400518,4401974,4402500,4400378,4401928,4400472,4402279,4400823,4401834],"length":1,"stats":{"Line":0},"fn_name":null},{"line":170,"address":[4401658,4400202,4400837,4400456,4400902,4402358,4402293,4401912],"length":1,"stats":{"Line":0},"fn_name":null},{"line":171,"address":[],"length":0,"stats":{"Line":0},"fn_name":null}],"covered":0,"coverable":63},{"path":["/","home","edward","Documents","repos","mangadex-home","src","cache","mod.rs"],"content":"use std::fmt::Display;\nuse std::path::PathBuf;\nuse std::pin::Pin;\nuse std::str::FromStr;\nuse std::sync::Arc;\nuse std::task::{Context, Poll};\n\nuse actix_web::http::HeaderValue;\nuse async_trait::async_trait;\nuse bytes::{Bytes, BytesMut};\nuse chrono::{DateTime, FixedOffset};\nuse futures::{Stream, StreamExt};\nuse once_cell::sync::OnceCell;\nuse serde::{Deserialize, Serialize};\nuse serde_repr::{Deserialize_repr, Serialize_repr};\nuse sodiumoxide::crypto::secretstream::{Header, Key, Pull, Stream as SecretStream};\nuse thiserror::Error;\nuse tokio::io::AsyncRead;\nuse tokio::sync::mpsc::Sender;\nuse tokio_util::codec::{BytesCodec, FramedRead};\n\npub use disk::DiskCache;\npub use fs::UpstreamError;\npub use mem::MemoryCache;\n\nuse self::compat::LegacyImageMetadata;\n\npub static ENCRYPTION_KEY: OnceCell\u003cKey\u003e = OnceCell::new();\n\nmod compat;\nmod disk;\nmod fs;\npub mod mem;\n\n#[derive(PartialEq, Eq, Hash, Clone)]\npub struct CacheKey(pub String, pub String, pub bool);\n\nimpl Display for CacheKey {\n fn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n if self.2 {\n write!(f, \"saver/{}/{}\", self.0, self.1)\n } else {\n write!(f, \"data/{}/{}\", self.0, self.1)\n }\n }\n}\n\nimpl From\u003cCacheKey\u003e for PathBuf {\n #[inline]\n fn from(key: CacheKey) -\u003e Self {\n key.to_string().into()\n }\n}\n\nimpl From\u003c\u0026CacheKey\u003e for PathBuf {\n #[inline]\n fn from(key: \u0026CacheKey) -\u003e Self {\n key.to_string().into()\n }\n}\n\n#[derive(Clone)]\npub struct CachedImage(pub Bytes);\n\n#[derive(Copy, Clone, Serialize, Deserialize, Debug, PartialEq)]\npub struct ImageMetadata {\n pub content_type: Option\u003cImageContentType\u003e,\n pub content_length: Option\u003cu32\u003e,\n pub last_modified: Option\u003cDateTime\u003cFixedOffset\u003e\u003e,\n}\n\n// Confirmed by Ply to be these types: https://link.eddie.sh/ZXfk0\n#[derive(Copy, Clone, Serialize_repr, Deserialize_repr, Debug, PartialEq, Eq)]\n#[repr(u8)]\npub enum ImageContentType {\n Png = 0,\n Jpeg,\n Gif,\n}\n\npub struct InvalidContentType;\n\nimpl FromStr for ImageContentType {\n type Err = InvalidContentType;\n\n #[inline]\n fn from_str(s: \u0026str) -\u003e Result\u003cSelf, Self::Err\u003e {\n match s {\n \"image/png\" =\u003e Ok(Self::Png),\n \"image/jpeg\" =\u003e Ok(Self::Jpeg),\n \"image/gif\" =\u003e Ok(Self::Gif),\n _ =\u003e Err(InvalidContentType),\n }\n }\n}\n\nimpl AsRef\u003cstr\u003e for ImageContentType {\n #[inline]\n fn as_ref(\u0026self) -\u003e \u0026str {\n match self {\n Self::Png =\u003e \"image/png\",\n Self::Jpeg =\u003e \"image/jpeg\",\n Self::Gif =\u003e \"image/gif\",\n }\n }\n}\n\nimpl From\u003cLegacyImageMetadata\u003e for ImageMetadata {\n fn from(legacy: LegacyImageMetadata) -\u003e Self {\n Self {\n content_type: legacy.content_type.map(|v| v.0),\n content_length: legacy.size,\n last_modified: legacy.last_modified.map(|v| v.0),\n }\n }\n}\n\n#[derive(Debug)]\npub enum ImageRequestError {\n ContentType,\n ContentLength,\n LastModified,\n}\n\nimpl ImageMetadata {\n pub fn new(\n content_type: Option\u003cHeaderValue\u003e,\n content_length: Option\u003cHeaderValue\u003e,\n last_modified: Option\u003cHeaderValue\u003e,\n ) -\u003e Result\u003cSelf, ImageRequestError\u003e {\n Ok(Self {\n content_type: content_type\n .map(|v| match v.to_str() {\n Ok(v) =\u003e ImageContentType::from_str(v),\n Err(_) =\u003e Err(InvalidContentType),\n })\n .transpose()\n .map_err(|_| ImageRequestError::ContentType)?,\n content_length: content_length\n .map(|header_val| {\n header_val\n .to_str()\n .map_err(|_| ImageRequestError::ContentLength)?\n .parse()\n .map_err(|_| ImageRequestError::ContentLength)\n })\n .transpose()?,\n last_modified: last_modified\n .map(|header_val| {\n DateTime::parse_from_rfc2822(\n header_val\n .to_str()\n .map_err(|_| ImageRequestError::LastModified)?,\n )\n .map_err(|_| ImageRequestError::LastModified)\n })\n .transpose()?,\n })\n }\n}\n\n#[derive(Error, Debug)]\npub enum CacheError {\n #[error(transparent)]\n Io(#[from] std::io::Error),\n #[error(transparent)]\n Reqwest(#[from] reqwest::Error),\n #[error(transparent)]\n Upstream(#[from] UpstreamError),\n #[error(\"An error occurred while reading the decryption header\")]\n DecryptionFailure,\n}\n\n#[async_trait]\npub trait Cache: Send + Sync {\n async fn get(\u0026self, key: \u0026CacheKey)\n -\u003e Option\u003cResult\u003c(CacheStream, ImageMetadata), CacheError\u003e\u003e;\n\n async fn put(\n \u0026self,\n key: CacheKey,\n image: Bytes,\n metadata: ImageMetadata,\n ) -\u003e Result\u003c(), CacheError\u003e;\n}\n\n#[async_trait]\nimpl\u003cT: Cache\u003e Cache for Arc\u003cT\u003e {\n #[inline]\n async fn get(\n \u0026self,\n key: \u0026CacheKey,\n ) -\u003e Option\u003cResult\u003c(CacheStream, ImageMetadata), CacheError\u003e\u003e {\n self.as_ref().get(key).await\n }\n\n #[inline]\n async fn put(\n \u0026self,\n key: CacheKey,\n image: Bytes,\n metadata: ImageMetadata,\n ) -\u003e Result\u003c(), CacheError\u003e {\n self.as_ref().put(key, image, metadata).await\n }\n}\n\n#[async_trait]\npub trait CallbackCache: Cache {\n async fn put_with_on_completed_callback(\n \u0026self,\n key: CacheKey,\n image: Bytes,\n metadata: ImageMetadata,\n on_complete: Sender\u003c(CacheKey, Bytes, ImageMetadata, u64)\u003e,\n ) -\u003e Result\u003c(), CacheError\u003e;\n}\n\n#[async_trait]\nimpl\u003cT: CallbackCache\u003e CallbackCache for Arc\u003cT\u003e {\n #[inline]\n async fn put_with_on_completed_callback(\n \u0026self,\n key: CacheKey,\n image: Bytes,\n metadata: ImageMetadata,\n on_complete: Sender\u003c(CacheKey, Bytes, ImageMetadata, u64)\u003e,\n ) -\u003e Result\u003c(), CacheError\u003e {\n self.as_ref()\n .put_with_on_completed_callback(key, image, metadata, on_complete)\n .await\n }\n}\n\npub struct CacheStream {\n inner: InnerStream,\n decrypt: Option\u003cSecretStream\u003cPull\u003e\u003e,\n}\n\nimpl CacheStream {\n pub(self) fn new(inner: InnerStream, header: Option\u003cHeader\u003e) -\u003e Result\u003cSelf, ()\u003e {\n Ok(Self {\n inner,\n decrypt: header\n .and_then(|header| {\n ENCRYPTION_KEY\n .get()\n .map(|key| SecretStream::init_pull(\u0026header, key))\n })\n .transpose()?,\n })\n }\n}\n\nimpl Stream for CacheStream {\n type Item = CacheStreamItem;\n\n fn poll_next(mut self: Pin\u003c\u0026mut Self\u003e, cx: \u0026mut Context\u003c'_\u003e) -\u003e Poll\u003cOption\u003cSelf::Item\u003e\u003e {\n self.inner.poll_next_unpin(cx).map(|data| {\n // False positive (`data`): https://link.eddie.sh/r1fXX\n #[allow(clippy::option_if_let_else)]\n if let Some(keystream) = self.decrypt.as_mut() {\n data.map(|bytes_res| {\n bytes_res.and_then(|bytes| {\n keystream\n .pull(\u0026bytes, None)\n .map(|(data, _tag)| Bytes::from(data))\n .map_err(|_| UpstreamError)\n })\n })\n } else {\n data\n }\n })\n }\n}\n\npub(self) enum InnerStream {\n Memory(MemStream),\n Completed(FramedRead\u003cPin\u003cBox\u003cdyn AsyncRead + Send\u003e\u003e, BytesCodec\u003e),\n}\n\nimpl From\u003cCachedImage\u003e for InnerStream {\n fn from(image: CachedImage) -\u003e Self {\n Self::Memory(MemStream(image.0))\n }\n}\n\ntype CacheStreamItem = Result\u003cBytes, UpstreamError\u003e;\n\nimpl Stream for InnerStream {\n type Item = CacheStreamItem;\n\n fn poll_next(self: Pin\u003c\u0026mut Self\u003e, cx: \u0026mut Context\u003c'_\u003e) -\u003e Poll\u003cOption\u003cSelf::Item\u003e\u003e {\n match self.get_mut() {\n Self::Memory(stream) =\u003e stream.poll_next_unpin(cx),\n Self::Completed(stream) =\u003e stream\n .poll_next_unpin(cx)\n .map_ok(BytesMut::freeze)\n .map_err(|_| UpstreamError),\n }\n }\n}\n\npub struct MemStream(pub Bytes);\n\nimpl Stream for MemStream {\n type Item = CacheStreamItem;\n\n fn poll_next(mut self: Pin\u003c\u0026mut Self\u003e, _: \u0026mut Context\u003c'_\u003e) -\u003e Poll\u003cOption\u003cSelf::Item\u003e\u003e {\n let mut new_bytes = Bytes::new();\n std::mem::swap(\u0026mut self.0, \u0026mut new_bytes);\n if new_bytes.is_empty() {\n Poll::Ready(None)\n } else {\n Poll::Ready(Some(Ok(new_bytes)))\n }\n }\n}\n\n#[cfg(test)]\nmod tests {\n use super::*;\n\n #[test]\n fn metadata_size() {\n assert_eq!(std::mem::size_of::\u003cImageMetadata\u003e(), 32);\n }\n}\n","traces":[{"line":39,"address":[4955168],"length":1,"stats":{"Line":0},"fn_name":"fmt"},{"line":40,"address":[4955201],"length":1,"stats":{"Line":0},"fn_name":null},{"line":41,"address":[4955212,4955395],"length":1,"stats":{"Line":0},"fn_name":null},{"line":43,"address":[4955536,4955302],"length":1,"stats":{"Line":0},"fn_name":null},{"line":50,"address":[5142640,5142718],"length":1,"stats":{"Line":0},"fn_name":"from"},{"line":51,"address":[5142664],"length":1,"stats":{"Line":0},"fn_name":null},{"line":57,"address":[5142752],"length":1,"stats":{"Line":0},"fn_name":"from"},{"line":58,"address":[5142771],"length":1,"stats":{"Line":0},"fn_name":null},{"line":87,"address":[4955696],"length":1,"stats":{"Line":1},"fn_name":"from_str"},{"line":88,"address":[4955852,4955845,4955838],"length":1,"stats":{"Line":1},"fn_name":null},{"line":89,"address":[4955720,4955840],"length":1,"stats":{"Line":1},"fn_name":null},{"line":90,"address":[4955761,4955847],"length":1,"stats":{"Line":2},"fn_name":null},{"line":91,"address":[4955802,4955854],"length":1,"stats":{"Line":0},"fn_name":null},{"line":92,"address":[4955833],"length":1,"stats":{"Line":0},"fn_name":null},{"line":99,"address":[4955872],"length":1,"stats":{"Line":0},"fn_name":"as_ref"},{"line":100,"address":[4955924,4955949],"length":1,"stats":{"Line":0},"fn_name":null},{"line":101,"address":[4955881,4955928],"length":1,"stats":{"Line":0},"fn_name":null},{"line":102,"address":[4955951],"length":1,"stats":{"Line":0},"fn_name":null},{"line":103,"address":[4955903],"length":1,"stats":{"Line":0},"fn_name":null},{"line":109,"address":[4956000],"length":1,"stats":{"Line":1},"fn_name":"from"},{"line":111,"address":[4956160,4956019,4956168],"length":1,"stats":{"Line":3},"fn_name":"{{closure}}"},{"line":112,"address":[4956037],"length":1,"stats":{"Line":1},"fn_name":null},{"line":113,"address":[4956199,4956049,4956176],"length":1,"stats":{"Line":3},"fn_name":"{{closure}}"},{"line":126,"address":[4956224,4957383],"length":1,"stats":{"Line":0},"fn_name":"new"},{"line":131,"address":[4956254,4957173],"length":1,"stats":{"Line":0},"fn_name":null},{"line":132,"address":[4956286,4956555,4956471,4956597,4957465],"length":1,"stats":{"Line":0},"fn_name":null},{"line":133,"address":[4957615,4957545,4957683,4957536],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":134,"address":[4957592,4957619,4957699],"length":1,"stats":{"Line":0},"fn_name":null},{"line":135,"address":[4957610],"length":1,"stats":{"Line":0},"fn_name":null},{"line":137,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":138,"address":[4957732,4956557,4956450,4957728],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":139,"address":[4957467,4956489,4956841,4956985,4956615,4956943],"length":1,"stats":{"Line":0},"fn_name":null},{"line":140,"address":[4958158,4957824],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":141,"address":[4957915,4957836,4957994,4958056],"length":1,"stats":{"Line":0},"fn_name":null},{"line":142,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":143,"address":[4957764,4957902,4957962,4957760],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":144,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":145,"address":[4957792,4957803],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":147,"address":[4956820,4956945],"length":1,"stats":{"Line":0},"fn_name":null},{"line":148,"address":[4956877,4957285,4957014,4957472,4957327,4957081],"length":1,"stats":{"Line":0},"fn_name":null},{"line":149,"address":[4958537,4958272],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":150,"address":[4958495,4958407],"length":1,"stats":{"Line":0},"fn_name":null},{"line":151,"address":[4958302,4958462,4958423,4958381],"length":1,"stats":{"Line":0},"fn_name":null},{"line":152,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":153,"address":[4958425,4958212,4958368,4958208],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":155,"address":[4958251,4958240],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":157,"address":[4957044,4957287],"length":1,"stats":{"Line":0},"fn_name":null},{"line":190,"address":[4970128],"length":1,"stats":{"Line":0},"fn_name":"get\u003cmangadex_home::cache::disk::DiskCache\u003e"},{"line":194,"address":[4970855,4970480,4970589,4970893],"length":1,"stats":{"Line":0},"fn_name":null},{"line":198,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":204,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":222,"address":[4971040],"length":1,"stats":{"Line":0},"fn_name":"put_with_on_completed_callback\u003cmangadex_home::cache::disk::DiskCache\u003e"},{"line":229,"address":[4972727,4971982,4972399,4971776,4972066],"length":1,"stats":{"Line":0},"fn_name":null},{"line":230,"address":[4971801],"length":1,"stats":{"Line":0},"fn_name":null},{"line":231,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":241,"address":[4958944,4958576],"length":1,"stats":{"Line":0},"fn_name":"new"},{"line":242,"address":[4958808],"length":1,"stats":{"Line":0},"fn_name":null},{"line":243,"address":[4958596],"length":1,"stats":{"Line":0},"fn_name":null},{"line":244,"address":[4958898,4958756,4958643,4958880],"length":1,"stats":{"Line":0},"fn_name":null},{"line":245,"address":[4959040],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":246,"address":[4959059,4959096],"length":1,"stats":{"Line":0},"fn_name":null},{"line":247,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":248,"address":[4958992,4959013,4959091],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":250,"address":[4958882,4958735],"length":1,"stats":{"Line":0},"fn_name":null},{"line":258,"address":[4959120],"length":1,"stats":{"Line":0},"fn_name":"poll_next"},{"line":259,"address":[4959149,4959648,4959892],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":261,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":262,"address":[4959694,4959799],"length":1,"stats":{"Line":0},"fn_name":null},{"line":263,"address":[4959568,4959821,4959928],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":264,"address":[4959529,4959344,4959584],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":265,"address":[4959380,4959435],"length":1,"stats":{"Line":0},"fn_name":null},{"line":266,"address":[4959388],"length":1,"stats":{"Line":0},"fn_name":null},{"line":267,"address":[4959232,4959244],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":268,"address":[4959328],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":272,"address":[4959784],"length":1,"stats":{"Line":0},"fn_name":null},{"line":284,"address":[4960000],"length":1,"stats":{"Line":0},"fn_name":"from"},{"line":285,"address":[4960011],"length":1,"stats":{"Line":0},"fn_name":null},{"line":294,"address":[4960112],"length":1,"stats":{"Line":1},"fn_name":"poll_next"},{"line":295,"address":[4960204],"length":1,"stats":{"Line":0},"fn_name":null},{"line":296,"address":[4960161,4960220],"length":1,"stats":{"Line":1},"fn_name":null},{"line":297,"address":[4960180,4960239],"length":1,"stats":{"Line":2},"fn_name":null},{"line":298,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":299,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":300,"address":[4960321,4960288],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":310,"address":[4960747,4960336],"length":1,"stats":{"Line":0},"fn_name":"poll_next"},{"line":311,"address":[4960366],"length":1,"stats":{"Line":0},"fn_name":null},{"line":312,"address":[4960397],"length":1,"stats":{"Line":0},"fn_name":null},{"line":313,"address":[4960486,4960434],"length":1,"stats":{"Line":0},"fn_name":null},{"line":314,"address":[4960462],"length":1,"stats":{"Line":0},"fn_name":null},{"line":316,"address":[4960496],"length":1,"stats":{"Line":0},"fn_name":null},{"line":326,"address":[4402725,4402752,4402720],"length":1,"stats":{"Line":3},"fn_name":"{{closure}}"},{"line":327,"address":[4402756],"length":1,"stats":{"Line":1},"fn_name":null}],"covered":13,"coverable":92},{"path":["/","home","edward","Documents","repos","mangadex-home","src","client.rs"],"content":"use std::collections::HashMap;\nuse std::sync::Arc;\nuse std::time::Duration;\n\nuse actix_web::http::{HeaderMap, HeaderName, HeaderValue};\nuse actix_web::web::Data;\nuse bytes::Bytes;\nuse log::{debug, error, warn};\nuse once_cell::sync::Lazy;\nuse parking_lot::RwLock;\nuse reqwest::header::{\n ACCESS_CONTROL_ALLOW_ORIGIN, ACCESS_CONTROL_EXPOSE_HEADERS, CACHE_CONTROL, CONTENT_LENGTH,\n CONTENT_TYPE, LAST_MODIFIED, X_CONTENT_TYPE_OPTIONS,\n};\nuse reqwest::{Client, StatusCode};\nuse tokio::sync::watch::{channel, Receiver};\nuse tokio::sync::Notify;\n\nuse crate::cache::{Cache, CacheKey, ImageMetadata};\n\npub static HTTP_CLIENT: Lazy\u003cCachingClient\u003e = Lazy::new(|| CachingClient {\n inner: Client::builder()\n .pool_idle_timeout(Duration::from_secs(180))\n .https_only(true)\n .http2_prior_knowledge()\n .build()\n .expect(\"Client initialization to work\"),\n locks: RwLock::new(HashMap::new()),\n});\n\npub static DEFAULT_HEADERS: Lazy\u003cHeaderMap\u003e = Lazy::new(|| {\n let mut headers = HeaderMap::with_capacity(8);\n headers.insert(X_CONTENT_TYPE_OPTIONS, HeaderValue::from_static(\"nosniff\"));\n headers.insert(\n ACCESS_CONTROL_ALLOW_ORIGIN,\n HeaderValue::from_static(\"https://mangadex.org\"),\n );\n headers.insert(ACCESS_CONTROL_EXPOSE_HEADERS, HeaderValue::from_static(\"*\"));\n headers.insert(\n CACHE_CONTROL,\n HeaderValue::from_static(\"public, max-age=1209600\"),\n );\n headers.insert(\n HeaderName::from_static(\"timing-allow-origin\"),\n HeaderValue::from_static(\"https://mangadex.org\"),\n );\n headers\n});\n\npub struct CachingClient {\n inner: Client,\n locks: RwLock\u003cHashMap\u003cString, Receiver\u003cFetchResult\u003e\u003e\u003e,\n}\n\n#[derive(Clone, Debug)]\npub enum FetchResult {\n ServiceUnavailable,\n InternalServerError,\n Data(StatusCode, HeaderMap, Bytes),\n Processing,\n}\n\nimpl CachingClient {\n pub async fn fetch_and_cache(\n \u0026'static self,\n url: String,\n key: CacheKey,\n cache: Data\u003cdyn Cache\u003e,\n ) -\u003e FetchResult {\n if let Some(recv) = self.locks.read().get(\u0026url) {\n let mut recv = recv.clone();\n loop {\n if !matches!(*recv.borrow(), FetchResult::Processing) {\n break;\n }\n if recv.changed().await.is_err() {\n break;\n }\n }\n\n return recv.borrow().clone();\n }\n let url_0 = url.clone();\n\n let notify = Arc::new(Notify::new());\n let notify2 = Arc::clone(\u0026notify);\n\n tokio::spawn(async move {\n let (tx, rx) = channel(FetchResult::Processing);\n\n self.locks.write().insert(url.clone(), rx);\n notify.notify_one();\n let resp = self.inner.get(\u0026url).send().await;\n\n let resp = match resp {\n Ok(mut resp) =\u003e {\n let content_type = resp.headers().get(CONTENT_TYPE);\n\n let is_image = content_type\n .map(|v| String::from_utf8_lossy(v.as_ref()).contains(\"image/\"))\n .unwrap_or_default();\n\n if resp.status() != StatusCode::OK || !is_image {\n warn!(\"Got non-OK or non-image response code from upstream, proxying and not caching result.\");\n\n let mut headers = DEFAULT_HEADERS.clone();\n\n if let Some(content_type) = content_type {\n headers.insert(CONTENT_TYPE, content_type.clone());\n }\n\n FetchResult::Data(\n resp.status(),\n headers,\n resp.bytes().await.unwrap_or_default(),\n )\n } else {\n let (content_type, length, last_mod) = {\n let headers = resp.headers_mut();\n (\n headers.remove(CONTENT_TYPE),\n headers.remove(CONTENT_LENGTH),\n headers.remove(LAST_MODIFIED),\n )\n };\n\n let body = resp.bytes().await.unwrap();\n\n debug!(\"Inserting into cache\");\n\n let metadata = ImageMetadata::new(\n content_type.clone(),\n length.clone(),\n last_mod.clone(),\n )\n .unwrap();\n\n match cache.put(key, body.clone(), metadata).await {\n Ok(()) =\u003e {\n debug!(\"Done putting into cache\");\n\n let mut headers = DEFAULT_HEADERS.clone();\n if let Some(content_type) = content_type {\n headers.insert(CONTENT_TYPE, content_type);\n }\n\n if let Some(content_length) = length {\n headers.insert(CONTENT_LENGTH, content_length);\n }\n\n if let Some(last_modified) = last_mod {\n headers.insert(LAST_MODIFIED, last_modified);\n }\n\n FetchResult::Data(StatusCode::OK, headers, body)\n }\n Err(e) =\u003e {\n warn!(\"Failed to insert into cache: {}\", e);\n FetchResult::InternalServerError\n }\n }\n }\n }\n Err(e) =\u003e {\n error!(\"Failed to fetch image from server: {}\", e);\n FetchResult::ServiceUnavailable\n }\n };\n // This shouldn't happen\n tx.send(resp).unwrap();\n self.locks.write().remove(\u0026url);\n });\n\n notify2.notified().await;\n\n let mut recv = self.locks.read().get(\u0026url_0).unwrap().clone();\n loop {\n if !matches!(*recv.borrow(), FetchResult::Processing) {\n break;\n }\n if recv.changed().await.is_err() {\n break;\n }\n }\n let resp = recv.borrow().clone();\n resp\n }\n\n #[inline]\n pub const fn inner(\u0026self) -\u003e \u0026Client {\n \u0026self.inner\n }\n}\n","traces":[{"line":21,"address":[4514688,4514967,4515021],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":22,"address":[4514713,4514778,4514817],"length":1,"stats":{"Line":0},"fn_name":null},{"line":23,"address":[4514748,4514809,4515036],"length":1,"stats":{"Line":0},"fn_name":null},{"line":28,"address":[4514932],"length":1,"stats":{"Line":0},"fn_name":null},{"line":31,"address":[4515120,4515848],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":32,"address":[4515147],"length":1,"stats":{"Line":0},"fn_name":null},{"line":33,"address":[4515166],"length":1,"stats":{"Line":0},"fn_name":null},{"line":34,"address":[4515322],"length":1,"stats":{"Line":0},"fn_name":null},{"line":36,"address":[4515291],"length":1,"stats":{"Line":0},"fn_name":null},{"line":38,"address":[4515422],"length":1,"stats":{"Line":0},"fn_name":null},{"line":39,"address":[4515584],"length":1,"stats":{"Line":0},"fn_name":null},{"line":41,"address":[4515553],"length":1,"stats":{"Line":0},"fn_name":null},{"line":43,"address":[4515754],"length":1,"stats":{"Line":0},"fn_name":null},{"line":44,"address":[4515684],"length":1,"stats":{"Line":0},"fn_name":null},{"line":45,"address":[4515715],"length":1,"stats":{"Line":0},"fn_name":null},{"line":64,"address":[4515936],"length":1,"stats":{"Line":0},"fn_name":"fetch_and_cache"},{"line":70,"address":[4527088,4526883],"length":1,"stats":{"Line":0},"fn_name":null},{"line":71,"address":[4527104,4530331],"length":1,"stats":{"Line":0},"fn_name":null},{"line":72,"address":[4527638],"length":1,"stats":{"Line":0},"fn_name":null},{"line":73,"address":[4527158,4529955],"length":1,"stats":{"Line":0},"fn_name":null},{"line":74,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":76,"address":[4527318,4530234,4527419,4529941],"length":1,"stats":{"Line":0},"fn_name":null},{"line":77,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":81,"address":[4529918,4527651],"length":1,"stats":{"Line":0},"fn_name":null},{"line":83,"address":[4527767],"length":1,"stats":{"Line":0},"fn_name":null},{"line":85,"address":[4527792],"length":1,"stats":{"Line":0},"fn_name":null},{"line":86,"address":[4527893],"length":1,"stats":{"Line":0},"fn_name":null},{"line":88,"address":[4524686,4525631,4516349,4516304,4527962],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":89,"address":[4516412],"length":1,"stats":{"Line":0},"fn_name":null},{"line":91,"address":[4516584,4524631],"length":1,"stats":{"Line":0},"fn_name":null},{"line":92,"address":[4516828],"length":1,"stats":{"Line":0},"fn_name":null},{"line":93,"address":[4516877,4517005,4524613,4525555],"length":1,"stats":{"Line":0},"fn_name":null},{"line":95,"address":[4517339,4522978],"length":1,"stats":{"Line":0},"fn_name":null},{"line":96,"address":[4517349,4517259],"length":1,"stats":{"Line":0},"fn_name":null},{"line":97,"address":[4517400],"length":1,"stats":{"Line":0},"fn_name":null},{"line":99,"address":[4517517],"length":1,"stats":{"Line":0},"fn_name":null},{"line":100,"address":[4516121,4516112],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":101,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":103,"address":[4517595,4524930,4518980],"length":1,"stats":{"Line":0},"fn_name":null},{"line":104,"address":[4517720,4517802,4517887],"length":1,"stats":{"Line":0},"fn_name":null},{"line":106,"address":[4518055],"length":1,"stats":{"Line":0},"fn_name":null},{"line":108,"address":[4518110,4518303],"length":1,"stats":{"Line":0},"fn_name":null},{"line":109,"address":[4518165],"length":1,"stats":{"Line":0},"fn_name":null},{"line":112,"address":[4518862],"length":1,"stats":{"Line":0},"fn_name":null},{"line":113,"address":[4518313],"length":1,"stats":{"Line":0},"fn_name":null},{"line":114,"address":[4518383],"length":1,"stats":{"Line":0},"fn_name":null},{"line":115,"address":[4518589,4518439,4525576,4524488],"length":1,"stats":{"Line":0},"fn_name":null},{"line":118,"address":[4519385],"length":1,"stats":{"Line":0},"fn_name":null},{"line":119,"address":[4518993,4517774],"length":1,"stats":{"Line":0},"fn_name":null},{"line":121,"address":[4519001],"length":1,"stats":{"Line":0},"fn_name":null},{"line":122,"address":[4519081],"length":1,"stats":{"Line":0},"fn_name":null},{"line":123,"address":[4519161],"length":1,"stats":{"Line":0},"fn_name":null},{"line":127,"address":[4519541,4519691,4524337,4525597],"length":1,"stats":{"Line":0},"fn_name":null},{"line":129,"address":[4519985,4520113],"length":1,"stats":{"Line":0},"fn_name":null},{"line":131,"address":[4520470,4520399],"length":1,"stats":{"Line":0},"fn_name":null},{"line":132,"address":[4520289],"length":1,"stats":{"Line":0},"fn_name":null},{"line":133,"address":[4520331],"length":1,"stats":{"Line":0},"fn_name":null},{"line":134,"address":[4520373],"length":1,"stats":{"Line":0},"fn_name":null},{"line":136,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":138,"address":[4520894,4522533,4524115,4525618,4521204,4525324,4520508,4523978],"length":1,"stats":{"Line":0},"fn_name":null},{"line":139,"address":[4521104],"length":1,"stats":{"Line":0},"fn_name":null},{"line":140,"address":[4521206,4521334],"length":1,"stats":{"Line":0},"fn_name":null},{"line":142,"address":[4521496],"length":1,"stats":{"Line":0},"fn_name":null},{"line":143,"address":[4521796,4521546],"length":1,"stats":{"Line":0},"fn_name":null},{"line":144,"address":[4521640],"length":1,"stats":{"Line":0},"fn_name":null},{"line":147,"address":[4522056,4521806],"length":1,"stats":{"Line":0},"fn_name":null},{"line":148,"address":[4521900],"length":1,"stats":{"Line":0},"fn_name":null},{"line":151,"address":[4522316,4522066],"length":1,"stats":{"Line":0},"fn_name":null},{"line":152,"address":[4522160],"length":1,"stats":{"Line":0},"fn_name":null},{"line":155,"address":[4522318],"length":1,"stats":{"Line":0},"fn_name":null},{"line":157,"address":[4521126],"length":1,"stats":{"Line":0},"fn_name":null},{"line":158,"address":[4521158,4522538,4522614],"length":1,"stats":{"Line":0},"fn_name":null},{"line":159,"address":[4522864],"length":1,"stats":{"Line":0},"fn_name":null},{"line":164,"address":[4517278],"length":1,"stats":{"Line":0},"fn_name":null},{"line":165,"address":[4517293,4522983,4523059],"length":1,"stats":{"Line":0},"fn_name":null},{"line":166,"address":[4523309],"length":1,"stats":{"Line":0},"fn_name":null},{"line":170,"address":[4523346],"length":1,"stats":{"Line":0},"fn_name":null},{"line":171,"address":[4523515,4523719],"length":1,"stats":{"Line":0},"fn_name":null},{"line":174,"address":[4528372,4529831,4530255,4528241],"length":1,"stats":{"Line":0},"fn_name":null},{"line":176,"address":[4528544,4529808],"length":1,"stats":{"Line":0},"fn_name":null},{"line":177,"address":[4529195],"length":1,"stats":{"Line":0},"fn_name":null},{"line":178,"address":[4529773,4528757],"length":1,"stats":{"Line":0},"fn_name":null},{"line":179,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":181,"address":[4528997,4530276,4529759,4528899],"length":1,"stats":{"Line":0},"fn_name":null},{"line":182,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":185,"address":[4529208,4529736],"length":1,"stats":{"Line":0},"fn_name":null},{"line":186,"address":[4529309],"length":1,"stats":{"Line":0},"fn_name":null},{"line":190,"address":[4530880],"length":1,"stats":{"Line":0},"fn_name":"inner"},{"line":191,"address":[],"length":0,"stats":{"Line":0},"fn_name":null}],"covered":0,"coverable":89},{"path":["/","home","edward","Documents","repos","mangadex-home","src","config.rs"],"content":"use std::fmt::{Display, Formatter};\nuse std::fs::{File, OpenOptions};\nuse std::hint::unreachable_unchecked;\nuse std::io::{ErrorKind, Write};\nuse std::net::{IpAddr, Ipv4Addr, SocketAddr};\nuse std::num::NonZeroU16;\nuse std::path::{Path, PathBuf};\nuse std::str::FromStr;\nuse std::sync::atomic::{AtomicBool, Ordering};\n\nuse clap::{crate_authors, crate_description, crate_version, Clap};\nuse log::LevelFilter;\nuse serde::{Deserialize, Serialize};\nuse thiserror::Error;\nuse url::Url;\n\nuse crate::units::{KilobitsPerSecond, Mebibytes, Port};\n\n// Validate tokens is an atomic because it's faster than locking on rwlock.\npub static VALIDATE_TOKENS: AtomicBool = AtomicBool::new(false);\npub static OFFLINE_MODE: AtomicBool = AtomicBool::new(false);\n\n#[derive(Error, Debug)]\npub enum ConfigError {\n #[error(\"No config found. One has been created for you to modify.\")]\n NotInitialized,\n #[error(transparent)]\n Io(#[from] std::io::Error),\n #[error(transparent)]\n Parse(#[from] serde_yaml::Error),\n}\n\npub fn load_config() -\u003e Result\u003cConfig, ConfigError\u003e {\n // Load cli args first\n let cli_args: CliArgs = CliArgs::parse();\n\n // Load yaml file next\n let config_file: Result\u003cYamlArgs, _\u003e = {\n let config_path = cli_args\n .config_path\n .as_deref()\n .unwrap_or_else(|| Path::new(\"./settings.yaml\"));\n match File::open(config_path) {\n Ok(file) =\u003e serde_yaml::from_reader(file),\n Err(e) if e.kind() == ErrorKind::NotFound =\u003e {\n let mut file = OpenOptions::new()\n .write(true)\n .create_new(true)\n .open(config_path)\n .unwrap();\n\n let default_config = include_str!(\"../settings.sample.yaml\");\n file.write_all(default_config.as_bytes()).unwrap();\n\n return Err(ConfigError::NotInitialized);\n }\n Err(e) =\u003e return Err(e.into()),\n }\n };\n\n // generate config\n let config = Config::from_cli_and_file(cli_args, config_file?);\n\n // initialize globals\n OFFLINE_MODE.store(\n config\n .unstable_options\n .contains(\u0026UnstableOptions::OfflineMode),\n Ordering::Release,\n );\n\n Ok(config)\n}\n\n#[derive(Debug)]\n/// Represents a fully parsed config, from a variety of sources.\npub struct Config {\n pub cache_type: CacheType,\n pub cache_path: PathBuf,\n pub shutdown_timeout: NonZeroU16,\n pub log_level: LevelFilter,\n pub client_secret: ClientSecret,\n pub port: Port,\n pub bind_address: SocketAddr,\n pub external_address: Option\u003cSocketAddr\u003e,\n pub ephemeral_disk_encryption: bool,\n pub network_speed: KilobitsPerSecond,\n pub disk_quota: Mebibytes,\n pub memory_quota: Mebibytes,\n pub unstable_options: Vec\u003cUnstableOptions\u003e,\n pub override_upstream: Option\u003cUrl\u003e,\n pub enable_metrics: bool,\n}\n\nimpl Config {\n fn from_cli_and_file(cli_args: CliArgs, file_args: YamlArgs) -\u003e Self {\n let file_extended_options = file_args.extended_options.unwrap_or_default();\n\n let log_level = match (cli_args.quiet, cli_args.verbose) {\n (n, _) if n \u003e 2 =\u003e LevelFilter::Off,\n (2, _) =\u003e LevelFilter::Error,\n (1, _) =\u003e LevelFilter::Warn,\n // Use log level from file if no flags were provided to CLI\n (0, 0) =\u003e file_extended_options\n .logging_level\n .unwrap_or(LevelFilter::Info),\n (_, 1) =\u003e LevelFilter::Debug,\n (_, n) if n \u003e 1 =\u003e LevelFilter::Trace,\n // compiler can't figure it out\n _ =\u003e unsafe { unreachable_unchecked() },\n };\n\n let bind_port = cli_args\n .port\n .unwrap_or(file_args.server_settings.port)\n .get();\n\n // This needs to be outside because rust isn't smart enough yet to\n // realize a disjointed borrow of a moved value is ok. This will be\n // fixed in Rust 2021.\n let external_port = file_args\n .server_settings\n .external_port\n .map_or(bind_port, Port::get);\n\n Self {\n cache_type: cli_args\n .cache_type\n .or(file_extended_options.cache_type)\n .unwrap_or_default(),\n cache_path: cli_args\n .cache_path\n .or(file_extended_options.cache_path)\n .unwrap_or_else(|| PathBuf::from_str(\"./cache\").unwrap()),\n shutdown_timeout: file_args\n .server_settings\n .graceful_shutdown_wait_seconds\n .unwrap_or(unsafe { NonZeroU16::new_unchecked(60) }),\n log_level,\n // secret should never be in CLI\n client_secret: if let Ok(v) = std::env::var(\"CLIENT_SECRET\") {\n ClientSecret(v)\n } else {\n file_args.server_settings.secret\n },\n port: cli_args.port.unwrap_or(file_args.server_settings.port),\n bind_address: SocketAddr::new(\n file_args\n .server_settings\n .hostname\n .unwrap_or_else(|| IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0))),\n bind_port,\n ),\n external_address: file_args\n .server_settings\n .external_ip\n .map(|ip_addr| SocketAddr::new(ip_addr, external_port)),\n ephemeral_disk_encryption: cli_args.ephemeral_disk_encryption\n || file_extended_options\n .ephemeral_disk_encryption\n .unwrap_or_default(),\n network_speed: cli_args\n .network_speed\n .unwrap_or(file_args.server_settings.external_max_kilobits_per_second),\n disk_quota: cli_args\n .disk_quota\n .unwrap_or(file_args.max_cache_size_in_mebibytes),\n memory_quota: cli_args\n .memory_quota\n .or(file_extended_options.memory_quota)\n .unwrap_or_default(),\n enable_metrics: file_extended_options.enable_metrics.unwrap_or_default(),\n\n // Unstable options (and related) should never be in yaml config\n unstable_options: cli_args.unstable_options,\n override_upstream: cli_args.override_upstream,\n }\n }\n}\n\n// this intentionally does not implement display\n#[derive(Deserialize, Serialize, Clone)]\npub struct ClientSecret(String);\n\nimpl std::fmt::Debug for ClientSecret {\n fn fmt(\u0026self, f: \u0026mut Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n write!(f, \"[client secret]\")\n }\n}\n\n#[derive(Deserialize, Copy, Clone, Debug)]\n#[serde(rename_all = \"snake_case\")]\npub enum CacheType {\n OnDisk,\n Lru,\n Lfu,\n}\n\nimpl FromStr for CacheType {\n type Err = String;\n\n fn from_str(s: \u0026str) -\u003e Result\u003cSelf, Self::Err\u003e {\n match s {\n \"on_disk\" =\u003e Ok(Self::OnDisk),\n \"lru\" =\u003e Ok(Self::Lru),\n \"lfu\" =\u003e Ok(Self::Lfu),\n _ =\u003e Err(format!(\"Unknown option: {}\", s)),\n }\n }\n}\n\nimpl Default for CacheType {\n fn default() -\u003e Self {\n Self::OnDisk\n }\n}\n\n#[derive(Deserialize)]\nstruct YamlArgs {\n // Naming is legacy\n max_cache_size_in_mebibytes: Mebibytes,\n server_settings: YamlServerSettings,\n // This implementation custom options\n extended_options: Option\u003cYamlExtendedOptions\u003e,\n}\n\n// Naming is legacy\n#[derive(Deserialize)]\nstruct YamlServerSettings {\n secret: ClientSecret,\n #[serde(default)]\n port: Port,\n external_max_kilobits_per_second: KilobitsPerSecond,\n external_port: Option\u003cPort\u003e,\n graceful_shutdown_wait_seconds: Option\u003cNonZeroU16\u003e,\n hostname: Option\u003cIpAddr\u003e,\n external_ip: Option\u003cIpAddr\u003e,\n}\n\n#[derive(Deserialize, Default)]\nstruct YamlExtendedOptions {\n memory_quota: Option\u003cMebibytes\u003e,\n cache_type: Option\u003cCacheType\u003e,\n ephemeral_disk_encryption: Option\u003cbool\u003e,\n enable_metrics: Option\u003cbool\u003e,\n logging_level: Option\u003cLevelFilter\u003e,\n cache_path: Option\u003cPathBuf\u003e,\n}\n\n#[derive(Clap, Clone)]\n#[clap(version = crate_version!(), author = crate_authors!(), about = crate_description!())]\nstruct CliArgs {\n /// The port to listen on.\n #[clap(short, long)]\n pub port: Option\u003cPort\u003e,\n /// How large, in mebibytes, the in-memory cache should be. Note that this\n /// does not include runtime memory usage.\n #[clap(long)]\n pub memory_quota: Option\u003cMebibytes\u003e,\n /// How large, in mebibytes, the on-disk cache should be. Note that actual\n /// values may be larger for metadata information.\n #[clap(long)]\n pub disk_quota: Option\u003cMebibytes\u003e,\n /// Sets the location of the disk cache.\n #[clap(long)]\n pub cache_path: Option\u003cPathBuf\u003e,\n /// The network speed to advertise to Mangadex@Home control server.\n #[clap(long)]\n pub network_speed: Option\u003cKilobitsPerSecond\u003e,\n /// Changes verbosity. Default verbosity is INFO, while increasing counts of\n /// verbose flags increases the verbosity to DEBUG and TRACE, respectively.\n #[clap(short, long, parse(from_occurrences), conflicts_with = \"quiet\")]\n pub verbose: usize,\n /// Changes verbosity. Default verbosity is INFO, while increasing counts of\n /// quiet flags decreases the verbosity to WARN, ERROR, and no logs,\n /// respectively.\n #[clap(short, long, parse(from_occurrences), conflicts_with = \"verbose\")]\n pub quiet: usize,\n #[clap(short = 'Z', long)]\n pub unstable_options: Vec\u003cUnstableOptions\u003e,\n #[clap(long)]\n pub override_upstream: Option\u003cUrl\u003e,\n /// Enables ephemeral disk encryption. Items written to disk are first\n /// encrypted with a key generated at runtime. There are implications to\n /// performance, privacy, and usability with this flag enabled.\n #[clap(short, long)]\n pub ephemeral_disk_encryption: bool,\n #[clap(short, long)]\n pub config_path: Option\u003cPathBuf\u003e,\n #[clap(short = 't', long)]\n pub cache_type: Option\u003cCacheType\u003e,\n}\n\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\npub enum UnstableOptions {\n /// Overrides the upstream URL to fetch images from. Don't use this unless\n /// you know what you're dealing with.\n OverrideUpstream,\n\n /// Disables token validation. Don't use this unless you know the\n /// ramifications of this command.\n DisableTokenValidation,\n\n /// Tries to run without communication to MangaDex.\n OfflineMode,\n\n /// Serves HTTP in plaintext\n DisableTls,\n}\n\nimpl FromStr for UnstableOptions {\n type Err = String;\n\n fn from_str(s: \u0026str) -\u003e Result\u003cSelf, Self::Err\u003e {\n match s {\n \"override-upstream\" =\u003e Ok(Self::OverrideUpstream),\n \"disable-token-validation\" =\u003e Ok(Self::DisableTokenValidation),\n \"offline-mode\" =\u003e Ok(Self::OfflineMode),\n \"disable-tls\" =\u003e Ok(Self::DisableTls),\n _ =\u003e Err(format!(\"Unknown unstable option '{}'\", s)),\n }\n }\n}\n\nimpl Display for UnstableOptions {\n fn fmt(\u0026self, f: \u0026mut Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n match self {\n Self::OverrideUpstream =\u003e write!(f, \"override-upstream\"),\n Self::DisableTokenValidation =\u003e write!(f, \"disable-token-validation\"),\n Self::OfflineMode =\u003e write!(f, \"offline-mode\"),\n Self::DisableTls =\u003e write!(f, \"disable-tls\"),\n }\n }\n}\n\n#[cfg(test)]\nmod sample_yaml {\n use crate::config::YamlArgs;\n\n #[test]\n fn sample_yaml_parses() {\n assert!(serde_yaml::from_str::\u003cYamlArgs\u003e(include_str!(\"../settings.sample.yaml\")).is_ok());\n }\n}\n","traces":[{"line":33,"address":[4742464,4744180],"length":1,"stats":{"Line":0},"fn_name":"load_config"},{"line":35,"address":[4742487],"length":1,"stats":{"Line":0},"fn_name":null},{"line":39,"address":[4742540],"length":1,"stats":{"Line":0},"fn_name":null},{"line":42,"address":[4744548,4744544],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":43,"address":[4742684,4742718],"length":1,"stats":{"Line":0},"fn_name":null},{"line":44,"address":[4742720,4742699],"length":1,"stats":{"Line":0},"fn_name":null},{"line":45,"address":[4742892],"length":1,"stats":{"Line":0},"fn_name":null},{"line":46,"address":[4743123,4743006],"length":1,"stats":{"Line":0},"fn_name":null},{"line":52,"address":[4743296],"length":1,"stats":{"Line":0},"fn_name":null},{"line":53,"address":[4743399],"length":1,"stats":{"Line":0},"fn_name":null},{"line":55,"address":[4743518],"length":1,"stats":{"Line":0},"fn_name":null},{"line":57,"address":[4743591,4743036],"length":1,"stats":{"Line":0},"fn_name":null},{"line":62,"address":[4742765,4744210,4744039,4743639,4744133],"length":1,"stats":{"Line":0},"fn_name":null},{"line":65,"address":[4743922],"length":1,"stats":{"Line":0},"fn_name":null},{"line":66,"address":[4743849],"length":1,"stats":{"Line":0},"fn_name":null},{"line":69,"address":[4743914],"length":1,"stats":{"Line":0},"fn_name":null},{"line":72,"address":[4743950],"length":1,"stats":{"Line":0},"fn_name":null},{"line":96,"address":[4744592,4746720],"length":1,"stats":{"Line":0},"fn_name":"from_cli_and_file"},{"line":97,"address":[4744625],"length":1,"stats":{"Line":0},"fn_name":null},{"line":99,"address":[4744908,4744971,4745055,4745116,4744792,4744988],"length":1,"stats":{"Line":0},"fn_name":null},{"line":100,"address":[4744880,4744816],"length":1,"stats":{"Line":0},"fn_name":null},{"line":101,"address":[4744913],"length":1,"stats":{"Line":0},"fn_name":null},{"line":102,"address":[4744976],"length":1,"stats":{"Line":0},"fn_name":null},{"line":104,"address":[4747078,4744845,4744993,4745013],"length":1,"stats":{"Line":0},"fn_name":null},{"line":105,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":106,"address":[4745001],"length":1,"stats":{"Line":0},"fn_name":null},{"line":107,"address":[4745043,4744860],"length":1,"stats":{"Line":0},"fn_name":null},{"line":108,"address":[4745057],"length":1,"stats":{"Line":0},"fn_name":null},{"line":113,"address":[4745138],"length":1,"stats":{"Line":0},"fn_name":null},{"line":114,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":115,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":121,"address":[4745203],"length":1,"stats":{"Line":0},"fn_name":null},{"line":122,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":123,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":124,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":127,"address":[4745243],"length":1,"stats":{"Line":0},"fn_name":null},{"line":131,"address":[4745295,4745391],"length":1,"stats":{"Line":0},"fn_name":null},{"line":135,"address":[4745465,4745426],"length":1,"stats":{"Line":0},"fn_name":null},{"line":141,"address":[4745499,4745598],"length":1,"stats":{"Line":0},"fn_name":null},{"line":146,"address":[4745765],"length":1,"stats":{"Line":0},"fn_name":null},{"line":147,"address":[4745844],"length":1,"stats":{"Line":0},"fn_name":null},{"line":154,"address":[4745917,4745879],"length":1,"stats":{"Line":0},"fn_name":null},{"line":158,"address":[4745953,4746034],"length":1,"stats":{"Line":0},"fn_name":null},{"line":162,"address":[4746015,4746007],"length":1,"stats":{"Line":0},"fn_name":null},{"line":165,"address":[4746066,4746055],"length":1,"stats":{"Line":0},"fn_name":null},{"line":168,"address":[4746109,4746086],"length":1,"stats":{"Line":0},"fn_name":null},{"line":172,"address":[4746157],"length":1,"stats":{"Line":0},"fn_name":null},{"line":175,"address":[4746187],"length":1,"stats":{"Line":0},"fn_name":null},{"line":176,"address":[4746231],"length":1,"stats":{"Line":0},"fn_name":null},{"line":186,"address":[4747472],"length":1,"stats":{"Line":0},"fn_name":"fmt"},{"line":187,"address":[4747491],"length":1,"stats":{"Line":0},"fn_name":null},{"line":202,"address":[4747568],"length":1,"stats":{"Line":0},"fn_name":"from_str"},{"line":203,"address":[4747805,4747788,4747822],"length":1,"stats":{"Line":0},"fn_name":null},{"line":204,"address":[4747781,4747595],"length":1,"stats":{"Line":0},"fn_name":null},{"line":205,"address":[4747640,4747798],"length":1,"stats":{"Line":0},"fn_name":null},{"line":206,"address":[4747681,4747815],"length":1,"stats":{"Line":0},"fn_name":null},{"line":207,"address":[4747837,4747722],"length":1,"stats":{"Line":0},"fn_name":null},{"line":213,"address":[4747984],"length":1,"stats":{"Line":0},"fn_name":"default"},{"line":214,"address":[4747988],"length":1,"stats":{"Line":0},"fn_name":null},{"line":314,"address":[4748000],"length":1,"stats":{"Line":0},"fn_name":"from_str"},{"line":315,"address":[4748269,4748303,4748286,4748320],"length":1,"stats":{"Line":0},"fn_name":null},{"line":316,"address":[4748027,4748262],"length":1,"stats":{"Line":0},"fn_name":null},{"line":317,"address":[4748072,4748279],"length":1,"stats":{"Line":0},"fn_name":null},{"line":318,"address":[4748117,4748296],"length":1,"stats":{"Line":0},"fn_name":null},{"line":319,"address":[4748313,4748162],"length":1,"stats":{"Line":0},"fn_name":null},{"line":320,"address":[4748335,4748203],"length":1,"stats":{"Line":0},"fn_name":null},{"line":326,"address":[4748496],"length":1,"stats":{"Line":0},"fn_name":"fmt"},{"line":327,"address":[4748594],"length":1,"stats":{"Line":0},"fn_name":null},{"line":328,"address":[4748596,4748523],"length":1,"stats":{"Line":0},"fn_name":null},{"line":329,"address":[4748656],"length":1,"stats":{"Line":0},"fn_name":null},{"line":330,"address":[4748713],"length":1,"stats":{"Line":0},"fn_name":null},{"line":331,"address":[4748552,4748774],"length":1,"stats":{"Line":0},"fn_name":null},{"line":341,"address":[6338112,6338269,6338117,6338144],"length":1,"stats":{"Line":3},"fn_name":"{{closure}}"},{"line":342,"address":[6338151,6338259],"length":1,"stats":{"Line":1},"fn_name":null}],"covered":2,"coverable":74},{"path":["/","home","edward","Documents","repos","mangadex-home","src","main.rs"],"content":"#![warn(clippy::pedantic, clippy::nursery)]\n// We're end users, so these is ok\n#![allow(clippy::module_name_repetitions)]\n\nuse std::env::VarError;\nuse std::error::Error;\nuse std::fmt::Display;\nuse std::num::ParseIntError;\nuse std::sync::atomic::{AtomicBool, Ordering};\nuse std::sync::Arc;\nuse std::time::Duration;\n\nuse actix_web::rt::{spawn, time, System};\nuse actix_web::web::{self, Data};\nuse actix_web::{App, HttpResponse, HttpServer};\nuse cache::{Cache, DiskCache};\nuse config::Config;\nuse log::{debug, error, info, warn};\nuse parking_lot::RwLock;\nuse rustls::{NoClientAuth, ServerConfig};\nuse simple_logger::SimpleLogger;\nuse sodiumoxide::crypto::secretstream::gen_key;\nuse state::{RwLockServerState, ServerState};\nuse stop::send_stop;\nuse thiserror::Error;\n\nuse crate::cache::mem::{Lfu, Lru};\nuse crate::cache::{MemoryCache, ENCRYPTION_KEY};\nuse crate::config::{CacheType, UnstableOptions, OFFLINE_MODE};\nuse crate::state::DynamicServerCert;\n\nmod cache;\nmod client;\nmod config;\nmod metrics;\nmod ping;\nmod routes;\nmod state;\nmod stop;\nmod units;\n\nconst CLIENT_API_VERSION: usize = 31;\n\n#[derive(Error, Debug)]\nenum ServerError {\n #[error(\"There was a failure parsing config\")]\n Config(#[from] VarError),\n #[error(\"Failed to parse an int\")]\n ParseInt(#[from] ParseIntError),\n}\n\n#[actix_web::main]\nasync fn main() -\u003e Result\u003c(), Box\u003cdyn Error\u003e\u003e {\n sodiumoxide::init().expect(\"Failed to initialize crypto\");\n // It's ok to fail early here, it would imply we have a invalid config.\n dotenv::dotenv().ok();\n\n //\n // Config loading\n //\n\n let config = match config::load_config() {\n Ok(c) =\u003e c,\n Err(e) =\u003e {\n eprintln!(\"{}\", e);\n return Err(Box::new(e) as Box\u003c_\u003e);\n }\n };\n\n let memory_quota = config.memory_quota;\n let disk_quota = config.disk_quota;\n let cache_type = config.cache_type;\n let cache_path = config.cache_path.clone();\n let disable_tls = config\n .unstable_options\n .contains(\u0026UnstableOptions::DisableTls);\n let bind_address = config.bind_address;\n\n //\n // Logging and warnings\n //\n\n SimpleLogger::new().with_level(config.log_level).init()?;\n\n if let Err(e) = print_preamble_and_warnings(\u0026config) {\n error!(\"{}\", e);\n return Err(e);\n }\n\n debug!(\"{:?}\", \u0026config);\n\n let client_secret = config.client_secret.clone();\n let client_secret_1 = config.client_secret.clone();\n\n if config.ephemeral_disk_encryption {\n info!(\"Running with at-rest encryption!\");\n ENCRYPTION_KEY.set(gen_key()).unwrap();\n }\n\n if config.enable_metrics {\n metrics::init();\n }\n\n // HTTP Server init\n\n let server = if OFFLINE_MODE.load(Ordering::Acquire) {\n ServerState::init_offline()\n } else {\n ServerState::init(\u0026client_secret, \u0026config).await?\n };\n let data_0 = Arc::new(RwLockServerState(RwLock::new(server)));\n let data_1 = Arc::clone(\u0026data_0);\n\n //\n // At this point, the server is ready to start, and starts the necessary\n // threads.\n //\n\n // Set ctrl+c to send a stop message\n let running = Arc::new(AtomicBool::new(true));\n let running_1 = running.clone();\n let system = System::current();\n ctrlc::set_handler(move || {\n let system = \u0026system;\n let client_secret = client_secret.clone();\n let running_2 = Arc::clone(\u0026running_1);\n if !OFFLINE_MODE.load(Ordering::Acquire) {\n System::new().block_on(async move {\n if running_2.load(Ordering::SeqCst) {\n send_stop(\u0026client_secret).await;\n } else {\n warn!(\"Got second Ctrl-C, forcefully exiting\");\n system.stop();\n }\n });\n }\n running_1.store(false, Ordering::SeqCst);\n })\n .expect(\"Error setting Ctrl-C handler\");\n\n // Spawn ping task\n if !OFFLINE_MODE.load(Ordering::Acquire) {\n spawn(async move {\n let mut interval = time::interval(Duration::from_secs(90));\n let mut data = Arc::clone(\u0026data_0);\n loop {\n interval.tick().await;\n debug!(\"Sending ping!\");\n ping::update_server_state(\u0026client_secret_1, \u0026config, \u0026mut data).await;\n }\n });\n }\n\n let memory_max_size = memory_quota.into();\n let cache = DiskCache::new(disk_quota.into(), cache_path.clone()).await;\n let cache: Arc\u003cdyn Cache\u003e = match cache_type {\n CacheType::OnDisk =\u003e cache,\n CacheType::Lru =\u003e MemoryCache::\u003cLfu, _\u003e::new(cache, memory_max_size).await,\n CacheType::Lfu =\u003e MemoryCache::\u003cLru, _\u003e::new(cache, memory_max_size).await,\n };\n\n let cache_0 = Arc::clone(\u0026cache);\n\n // Start HTTPS server\n let server = HttpServer::new(move || {\n App::new()\n .service(routes::index)\n .service(routes::token_data)\n .service(routes::token_data_saver)\n .service(routes::metrics)\n .route(\n \"/data/{tail:.*}\",\n web::get().to(HttpResponse::UnavailableForLegalReasons),\n )\n .route(\n \"/data-saver/{tail:.*}\",\n web::get().to(HttpResponse::UnavailableForLegalReasons),\n )\n .route(\"{tail:.*}\", web::get().to(routes::default))\n .app_data(Data::from(Arc::clone(\u0026data_1)))\n .app_data(Data::from(Arc::clone(\u0026cache_0)))\n })\n .shutdown_timeout(60);\n\n if disable_tls {\n server.bind(bind_address)?.run().await?;\n } else {\n // Rustls only supports TLS 1.2 and 1.3.\n let tls_config = {\n let mut tls_config = ServerConfig::new(NoClientAuth::new());\n tls_config.cert_resolver = Arc::new(DynamicServerCert);\n tls_config\n };\n\n server.bind_rustls(bind_address, tls_config)?.run().await?;\n }\n\n // Waiting for us to finish sending stop message\n while running.load(Ordering::SeqCst) {\n std::thread::sleep(Duration::from_millis(250));\n }\n\n Ok(())\n}\n\n#[derive(Debug)]\nenum InvalidCombination {\n MissingUnstableOption(\u0026'static str, UnstableOptions),\n}\n\nimpl Display for InvalidCombination {\n fn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n match self {\n InvalidCombination::MissingUnstableOption(opt, arg) =\u003e {\n write!(\n f,\n \"The option '{}' requires the unstable option '-Z {}'\",\n opt, arg\n )\n }\n }\n }\n}\n\nimpl Error for InvalidCombination {}\n\nfn print_preamble_and_warnings(args: \u0026Config) -\u003e Result\u003c(), Box\u003cdyn Error\u003e\u003e {\n println!(concat!(\n env!(\"CARGO_PKG_NAME\"),\n \" \",\n env!(\"CARGO_PKG_VERSION\"),\n \" (\",\n env!(\"VERGEN_GIT_SHA_SHORT\"),\n \")\",\n \" Copyright (C) 2021 \",\n env!(\"CARGO_PKG_AUTHORS\"),\n \"\\n\\n\",\n env!(\"CARGO_PKG_NAME\"),\n \" is free software: you can redistribute it and/or modify\\n\\\n it under the terms of the GNU General Public License as published by\\n\\\n the Free Software Foundation, either version 3 of the License, or\\n\\\n (at your option) any later version.\\n\\n\",\n env!(\"CARGO_PKG_NAME\"),\n \" is distributed in the hope that it will be useful,\\n\\\n but WITHOUT ANY WARRANTY; without even the implied warranty of\\n\\\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\\n\\\n GNU General Public License for more details.\\n\\n\\\n You should have received a copy of the GNU General Public License\\n\\\n along with \",\n env!(\"CARGO_PKG_NAME\"),\n \". If not, see \u003chttps://www.gnu.org/licenses/\u003e.\\n\"\n ));\n\n if args.ephemeral_disk_encryption {\n error!(\"Encrypted files are _very_ broken; caveat emptor!\");\n }\n\n if !args.unstable_options.is_empty() {\n warn!(\"Unstable options are enabled. These options should not be used in production!\");\n }\n\n if args\n .unstable_options\n .contains(\u0026UnstableOptions::OfflineMode)\n {\n warn!(\"Running in offline mode. No communication to MangaDex will be made!\");\n }\n\n if args.unstable_options.contains(\u0026UnstableOptions::DisableTls) {\n warn!(\"Serving insecure traffic! You better be running this for development only.\");\n }\n\n if args.override_upstream.is_some()\n \u0026\u0026 !args\n .unstable_options\n .contains(\u0026UnstableOptions::OverrideUpstream)\n {\n Err(Box::new(InvalidCombination::MissingUnstableOption(\n \"override-upstream\",\n UnstableOptions::OverrideUpstream,\n )))\n } else {\n Ok(())\n }\n}\n","traces":[{"line":52,"address":[5349925,5349856,5358153,5358413,5345797,5359789,5359641,5359929],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":53,"address":[5345776],"length":1,"stats":{"Line":0},"fn_name":"main"},{"line":54,"address":[5349988],"length":1,"stats":{"Line":0},"fn_name":null},{"line":56,"address":[5350109],"length":1,"stats":{"Line":0},"fn_name":null},{"line":62,"address":[5350315,5350182],"length":1,"stats":{"Line":0},"fn_name":null},{"line":63,"address":[5350317,5350189],"length":1,"stats":{"Line":0},"fn_name":null},{"line":64,"address":[5350204],"length":1,"stats":{"Line":0},"fn_name":null},{"line":65,"address":[5350252,5350490],"length":1,"stats":{"Line":0},"fn_name":null},{"line":66,"address":[5350566,5350696],"length":1,"stats":{"Line":0},"fn_name":null},{"line":70,"address":[5350420],"length":1,"stats":{"Line":0},"fn_name":null},{"line":71,"address":[5350431],"length":1,"stats":{"Line":0},"fn_name":null},{"line":72,"address":[5350442],"length":1,"stats":{"Line":0},"fn_name":null},{"line":73,"address":[5350454],"length":1,"stats":{"Line":0},"fn_name":null},{"line":74,"address":[5350740],"length":1,"stats":{"Line":0},"fn_name":null},{"line":77,"address":[5350866],"length":1,"stats":{"Line":0},"fn_name":null},{"line":83,"address":[5351082,5359829,5350894],"length":1,"stats":{"Line":0},"fn_name":null},{"line":85,"address":[5351177,5351053,5351237],"length":1,"stats":{"Line":0},"fn_name":null},{"line":86,"address":[5351397,5351269],"length":1,"stats":{"Line":0},"fn_name":null},{"line":87,"address":[5351668],"length":1,"stats":{"Line":0},"fn_name":null},{"line":90,"address":[5351833,5351705],"length":1,"stats":{"Line":0},"fn_name":null},{"line":92,"address":[5352128],"length":1,"stats":{"Line":0},"fn_name":null},{"line":93,"address":[5352164],"length":1,"stats":{"Line":0},"fn_name":null},{"line":95,"address":[5352252,5352200,5352581],"length":1,"stats":{"Line":0},"fn_name":null},{"line":96,"address":[5352257,5352209,5352342],"length":1,"stats":{"Line":0},"fn_name":null},{"line":97,"address":[5352510],"length":1,"stats":{"Line":0},"fn_name":null},{"line":100,"address":[5352591,5352607],"length":1,"stats":{"Line":0},"fn_name":null},{"line":101,"address":[5352600],"length":1,"stats":{"Line":0},"fn_name":null},{"line":106,"address":[5353123,5352611],"length":1,"stats":{"Line":0},"fn_name":null},{"line":107,"address":[5352671,5359850],"length":1,"stats":{"Line":0},"fn_name":null},{"line":109,"address":[5358779,5353128,5359671,5352689,5352793],"length":1,"stats":{"Line":0},"fn_name":null},{"line":111,"address":[5353349],"length":1,"stats":{"Line":0},"fn_name":null},{"line":112,"address":[5353494],"length":1,"stats":{"Line":0},"fn_name":null},{"line":120,"address":[5353575],"length":1,"stats":{"Line":0},"fn_name":null},{"line":121,"address":[5353698],"length":1,"stats":{"Line":0},"fn_name":null},{"line":122,"address":[5353758],"length":1,"stats":{"Line":0},"fn_name":null},{"line":123,"address":[5353785,5346960,5347398],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":124,"address":[5346983],"length":1,"stats":{"Line":0},"fn_name":null},{"line":125,"address":[5347015],"length":1,"stats":{"Line":0},"fn_name":null},{"line":126,"address":[5347042],"length":1,"stats":{"Line":0},"fn_name":null},{"line":127,"address":[5347083,5347281,5347141],"length":1,"stats":{"Line":0},"fn_name":null},{"line":128,"address":[5346775,5345952,5345985,5347125,5347151,5346624],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":129,"address":[5346601,5346036,5346344],"length":1,"stats":{"Line":0},"fn_name":null},{"line":130,"address":[5346219,5346162,5346098,5346608,5346762],"length":1,"stats":{"Line":0},"fn_name":null},{"line":132,"address":[5346114,5346425,5346349],"length":1,"stats":{"Line":0},"fn_name":null},{"line":133,"address":[5346586],"length":1,"stats":{"Line":0},"fn_name":null},{"line":137,"address":[5347288],"length":1,"stats":{"Line":0},"fn_name":null},{"line":142,"address":[5354178,5353934,5354115],"length":1,"stats":{"Line":0},"fn_name":null},{"line":143,"address":[5348708,5348606,5353996,5347601,5348736,5354125,5347568],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":144,"address":[5347658],"length":1,"stats":{"Line":0},"fn_name":null},{"line":145,"address":[5347716,5348728],"length":1,"stats":{"Line":0},"fn_name":null},{"line":146,"address":[5348542],"length":1,"stats":{"Line":0},"fn_name":null},{"line":147,"address":[5347884,5348677,5347753],"length":1,"stats":{"Line":0},"fn_name":null},{"line":148,"address":[5348025,5348141],"length":1,"stats":{"Line":0},"fn_name":null},{"line":149,"address":[5348408,5348302,5348695,5348552],"length":1,"stats":{"Line":0},"fn_name":null},{"line":154,"address":[5354188],"length":1,"stats":{"Line":0},"fn_name":null},{"line":155,"address":[5358636,5354419,5354254,5359692],"length":1,"stats":{"Line":0},"fn_name":null},{"line":156,"address":[5354702,5355427,5354735,5355096],"length":1,"stats":{"Line":0},"fn_name":null},{"line":157,"address":[5354623,5354712],"length":1,"stats":{"Line":0},"fn_name":null},{"line":158,"address":[5354877,5359713,5358614,5354756],"length":1,"stats":{"Line":0},"fn_name":null},{"line":159,"address":[5359734,5355200,5354677,5355109,5358592],"length":1,"stats":{"Line":0},"fn_name":null},{"line":162,"address":[5355467],"length":1,"stats":{"Line":0},"fn_name":null},{"line":165,"address":[5355536,5348864,5349575],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":166,"address":[5349082,5349300,5349534,5349191,5349412,5348894],"length":1,"stats":{"Line":0},"fn_name":null},{"line":170,"address":[5349032],"length":1,"stats":{"Line":0},"fn_name":null},{"line":173,"address":[5349040],"length":1,"stats":{"Line":0},"fn_name":null},{"line":177,"address":[5349149],"length":1,"stats":{"Line":0},"fn_name":null},{"line":179,"address":[5349258,5349356,5349641],"length":1,"stats":{"Line":0},"fn_name":null},{"line":180,"address":[5349445,5349614,5349372],"length":1,"stats":{"Line":0},"fn_name":null},{"line":181,"address":[5349461,5349559,5349590],"length":1,"stats":{"Line":0},"fn_name":null},{"line":185,"address":[5356589,5357628,5355640],"length":1,"stats":{"Line":0},"fn_name":null},{"line":186,"address":[5355657,5359871,5356226,5356594,5358511,5355815,5359755,5359396],"length":1,"stats":{"Line":0},"fn_name":null},{"line":190,"address":[5356771,5355771],"length":1,"stats":{"Line":0},"fn_name":null},{"line":191,"address":[5356790,5358937],"length":1,"stats":{"Line":0},"fn_name":null},{"line":192,"address":[5359045],"length":1,"stats":{"Line":0},"fn_name":null},{"line":195,"address":[5359063,5359908,5357633,5358456,5356854,5359391,5357265,5359776],"length":1,"stats":{"Line":0},"fn_name":null},{"line":199,"address":[5357983,5357802],"length":1,"stats":{"Line":0},"fn_name":null},{"line":200,"address":[5357930],"length":1,"stats":{"Line":0},"fn_name":null},{"line":203,"address":[5357892],"length":1,"stats":{"Line":0},"fn_name":null},{"line":212,"address":[5343264],"length":1,"stats":{"Line":0},"fn_name":"fmt"},{"line":213,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":214,"address":[5343292],"length":1,"stats":{"Line":0},"fn_name":null},{"line":215,"address":[5343309],"length":1,"stats":{"Line":0},"fn_name":null},{"line":216,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":218,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":227,"address":[5343552],"length":1,"stats":{"Line":0},"fn_name":"print_preamble_and_warnings"},{"line":228,"address":[5343572],"length":1,"stats":{"Line":0},"fn_name":null},{"line":254,"address":[5343622,5343669,5343872],"length":1,"stats":{"Line":0},"fn_name":null},{"line":255,"address":[5343631,5343674,5343728],"length":1,"stats":{"Line":0},"fn_name":null},{"line":258,"address":[5343942,5344145,5343879],"length":1,"stats":{"Line":0},"fn_name":null},{"line":259,"address":[5344001,5343904,5343947],"length":1,"stats":{"Line":0},"fn_name":null},{"line":262,"address":[5344448,5344245,5344152],"length":1,"stats":{"Line":0},"fn_name":null},{"line":266,"address":[5344250,5344304,5344207],"length":1,"stats":{"Line":0},"fn_name":null},{"line":269,"address":[5344751,5344455,5344548],"length":1,"stats":{"Line":0},"fn_name":null},{"line":270,"address":[5344607,5344553,5344510],"length":1,"stats":{"Line":0},"fn_name":null},{"line":273,"address":[5344758,5344981,5344867,5344810],"length":1,"stats":{"Line":0},"fn_name":null},{"line":274,"address":[5344789,5344845],"length":1,"stats":{"Line":0},"fn_name":null},{"line":278,"address":[5344878,5344987,5344970],"length":1,"stats":{"Line":0},"fn_name":null},{"line":283,"address":[5344972],"length":1,"stats":{"Line":0},"fn_name":null}],"covered":0,"coverable":98},{"path":["/","home","edward","Documents","repos","mangadex-home","src","metrics.rs"],"content":"use once_cell::sync::Lazy;\nuse prometheus::{register_int_counter, IntCounter};\n\nmacro_rules! init_counters {\n ($(($counter:ident, $ty:ty, $name:literal, $desc:literal),)*) =\u003e {\n $(\n pub static $counter: Lazy\u003c$ty\u003e = Lazy::new(|| {\n register_int_counter!($name, $desc).unwrap()\n });\n )*\n\n #[allow(clippy::shadow_unrelated)]\n pub fn init() {\n $(let _a = $counter.get();)*\n }\n };\n}\n\ninit_counters!(\n (\n CACHE_HIT_COUNTER,\n IntCounter,\n \"cache_hit\",\n \"The number of cache hits.\"\n ),\n (\n CACHE_MISS_COUNTER,\n IntCounter,\n \"cache_miss\",\n \"The number of cache misses.\"\n ),\n (\n REQUESTS_TOTAL_COUNTER,\n IntCounter,\n \"requests_total\",\n \"The total number of requests served.\"\n ),\n (\n REQUESTS_DATA_COUNTER,\n IntCounter,\n \"requests_data\",\n \"The number of requests served from the /data endpoint.\"\n ),\n (\n REQUESTS_DATA_SAVER_COUNTER,\n IntCounter,\n \"requests_data_saver\",\n \"The number of requests served from the /data-saver endpoint.\"\n ),\n (\n REQUESTS_OTHER_COUNTER,\n IntCounter,\n \"requests_other\",\n \"The total number of request not served by primary endpoints.\"\n ),\n);\n","traces":[{"line":7,"address":[5160256,5160864,5162556,5162688,5163296,5161340,5161472,5163772,5161948,5163164,5162080,5160732],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":8,"address":[5161355,5160614,5162703,5163311,5160271,5160747,5161963,5163179,5161487,5162571,5163046,5163787,5162095,5163654,5161830,5161222,5160879,5162438],"length":1,"stats":{"Line":0},"fn_name":null},{"line":13,"address":[5163888],"length":1,"stats":{"Line":0},"fn_name":"init"},{"line":14,"address":[5163892],"length":1,"stats":{"Line":0},"fn_name":null}],"covered":0,"coverable":4},{"path":["/","home","edward","Documents","repos","mangadex-home","src","ping.rs"],"content":"use std::sync::atomic::Ordering;\nuse std::{io::BufReader, sync::Arc};\n\nuse log::{debug, error, info, warn};\nuse rustls::internal::pemfile::{certs, rsa_private_keys};\nuse rustls::sign::{RSASigningKey, SigningKey};\nuse rustls::Certificate;\nuse serde::de::{MapAccess, Visitor};\nuse serde::{Deserialize, Serialize};\nuse serde_repr::Deserialize_repr;\nuse sodiumoxide::crypto::box_::PrecomputedKey;\nuse url::Url;\n\nuse crate::config::{ClientSecret, Config, UnstableOptions, VALIDATE_TOKENS};\nuse crate::state::{\n RwLockServerState, PREVIOUSLY_COMPROMISED, PREVIOUSLY_PAUSED, TLS_CERTS,\n TLS_PREVIOUSLY_CREATED, TLS_SIGNING_KEY,\n};\nuse crate::units::{BytesPerSecond, Mebibytes, Port};\nuse crate::CLIENT_API_VERSION;\n\npub const CONTROL_CENTER_PING_URL: \u0026str = \"https://api.mangadex.network/ping\";\n\n#[derive(Serialize)]\npub struct Request\u003c'a\u003e {\n secret: \u0026'a ClientSecret,\n port: Port,\n disk_space: Mebibytes,\n network_speed: BytesPerSecond,\n build_version: usize,\n tls_created_at: Option\u003cString\u003e,\n}\n\nimpl\u003c'a\u003e Request\u003c'a\u003e {\n fn from_config_and_state(secret: \u0026'a ClientSecret, config: \u0026Config) -\u003e Self {\n Self {\n secret,\n port: config.port,\n disk_space: config.disk_quota,\n network_speed: config.network_speed.into(),\n build_version: CLIENT_API_VERSION,\n tls_created_at: TLS_PREVIOUSLY_CREATED\n .get()\n .map(|v| v.load().as_ref().clone()),\n }\n }\n}\n\nimpl\u003c'a\u003e From\u003c(\u0026'a ClientSecret, \u0026Config)\u003e for Request\u003c'a\u003e {\n fn from((secret, config): (\u0026'a ClientSecret, \u0026Config)) -\u003e Self {\n Self {\n secret,\n port: config.port,\n disk_space: config.disk_quota,\n network_speed: config.network_speed.into(),\n build_version: CLIENT_API_VERSION,\n tls_created_at: None,\n }\n }\n}\n\n#[derive(Deserialize, Debug)]\n#[serde(untagged)]\npub enum Response {\n Ok(OkResponse),\n Error(ErrorResponse),\n}\n\n#[derive(Deserialize, Debug)]\npub struct OkResponse {\n pub image_server: Url,\n pub latest_build: usize,\n pub url: Url,\n pub token_key: Option\u003cString\u003e,\n pub compromised: bool,\n pub paused: bool,\n #[serde(default)]\n pub force_tokens: bool,\n pub tls: Option\u003cTls\u003e,\n}\n\n#[derive(Deserialize, Debug)]\npub struct ErrorResponse {\n pub error: String,\n pub status: ErrorCode,\n}\n\n#[derive(Deserialize_repr, Debug, Copy, Clone)]\n#[repr(u16)]\npub enum ErrorCode {\n MalformedJson = 400,\n InvalidSecret = 401,\n InvalidContentType = 415,\n}\n\npub struct Tls {\n pub created_at: String,\n pub priv_key: Arc\u003cBox\u003cdyn SigningKey\u003e\u003e,\n pub certs: Vec\u003cCertificate\u003e,\n}\n\nimpl\u003c'de\u003e Deserialize\u003c'de\u003e for Tls {\n fn deserialize\u003cD\u003e(deserializer: D) -\u003e Result\u003cSelf, D::Error\u003e\n where\n D: serde::Deserializer\u003c'de\u003e,\n {\n struct TlsVisitor;\n\n impl\u003c'de\u003e Visitor\u003c'de\u003e for TlsVisitor {\n type Value = Tls;\n\n fn expecting(\u0026self, formatter: \u0026mut std::fmt::Formatter) -\u003e std::fmt::Result {\n formatter.write_str(\"a tls struct\")\n }\n\n fn visit_map\u003cA\u003e(self, mut map: A) -\u003e Result\u003cSelf::Value, A::Error\u003e\n where\n A: MapAccess\u003c'de\u003e,\n {\n let mut created_at = None;\n let mut priv_key = None;\n let mut certificates = None;\n\n while let Some((key, value)) = map.next_entry::\u003c\u0026str, String\u003e()? {\n match key {\n \"created_at\" =\u003e created_at = Some(value.to_string()),\n \"private_key\" =\u003e {\n priv_key = rsa_private_keys(\u0026mut BufReader::new(value.as_bytes()))\n .ok()\n .and_then(|mut v| {\n v.pop().and_then(|key| RSASigningKey::new(\u0026key).ok())\n });\n }\n \"certificate\" =\u003e {\n certificates = certs(\u0026mut BufReader::new(value.as_bytes())).ok();\n }\n _ =\u003e (), // Ignore extra fields\n }\n }\n\n match (created_at, priv_key, certificates) {\n (Some(created_at), Some(priv_key), Some(certificates)) =\u003e Ok(Tls {\n created_at,\n priv_key: Arc::new(Box::new(priv_key)),\n certs: certificates,\n }),\n _ =\u003e Err(serde::de::Error::custom(\"Could not deserialize tls info\")),\n }\n }\n }\n\n deserializer.deserialize_map(TlsVisitor)\n }\n}\n\nimpl std::fmt::Debug for Tls {\n fn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n f.debug_struct(\"Tls\")\n .field(\"created_at\", \u0026self.created_at)\n .finish()\n }\n}\n\npub async fn update_server_state(\n secret: \u0026ClientSecret,\n cli: \u0026Config,\n data: \u0026mut Arc\u003cRwLockServerState\u003e,\n) {\n let req = Request::from_config_and_state(secret, cli);\n let client = reqwest::Client::new();\n let resp = client.post(CONTROL_CENTER_PING_URL).json(\u0026req).send().await;\n match resp {\n Ok(resp) =\u003e match resp.json::\u003cResponse\u003e().await {\n Ok(Response::Ok(resp)) =\u003e {\n debug!(\"got write guard for server state\");\n let mut write_guard = data.0.write();\n\n let image_server_changed = write_guard.image_server != resp.image_server;\n if !write_guard.url_overridden \u0026\u0026 image_server_changed {\n write_guard.image_server = resp.image_server;\n } else if image_server_changed {\n warn!(\"Ignoring new upstream url!\");\n }\n\n if let Some(key) = resp.token_key {\n if let Some(key) = base64::decode(\u0026key)\n .ok()\n .and_then(|k| PrecomputedKey::from_slice(\u0026k))\n {\n write_guard.precomputed_key = key;\n } else {\n error!(\"Failed to parse token key: got {}\", key);\n }\n }\n\n if !cli\n .unstable_options\n .contains(\u0026UnstableOptions::DisableTokenValidation)\n \u0026\u0026 VALIDATE_TOKENS.load(Ordering::Acquire) != resp.force_tokens\n {\n if resp.force_tokens {\n info!(\"Client received command to enforce token validity.\");\n } else {\n info!(\"Client received command to no longer enforce token validity\");\n }\n VALIDATE_TOKENS.store(resp.force_tokens, Ordering::Release);\n }\n\n if let Some(tls) = resp.tls {\n TLS_PREVIOUSLY_CREATED\n .get()\n .unwrap()\n .swap(Arc::new(tls.created_at));\n TLS_SIGNING_KEY.get().unwrap().swap(tls.priv_key);\n TLS_CERTS.get().unwrap().swap(Arc::new(tls.certs));\n }\n\n let previously_compromised = PREVIOUSLY_COMPROMISED.load(Ordering::Acquire);\n if resp.compromised != previously_compromised {\n PREVIOUSLY_COMPROMISED.store(resp.compromised, Ordering::Release);\n if resp.compromised {\n error!(\"Got compromised response from control center!\");\n } else if previously_compromised {\n info!(\"No longer compromised!\");\n }\n }\n\n let previously_paused = PREVIOUSLY_PAUSED.load(Ordering::Acquire);\n if resp.paused != previously_paused {\n PREVIOUSLY_PAUSED.store(resp.paused, Ordering::Release);\n if resp.paused {\n warn!(\"Control center has paused this node.\");\n } else {\n info!(\"Control center is no longer pausing this node.\");\n }\n }\n\n if resp.url != write_guard.url {\n info!(\"This client's URL has been updated to {}\", resp.url);\n }\n\n debug!(\"dropping write guard for server state\");\n }\n Ok(Response::Error(resp)) =\u003e {\n error!(\n \"Got an {} error from upstream: {}\",\n resp.status as u16, resp.error\n );\n }\n Err(e) =\u003e warn!(\"Got malformed response: {}\", e),\n },\n Err(e) =\u003e match e {\n e if e.is_timeout() =\u003e {\n error!(\"Response timed out to control server. Is MangaDex down?\");\n }\n e =\u003e warn!(\"Failed to send request: {}\", e),\n },\n }\n}\n","traces":[{"line":35,"address":[5142816],"length":1,"stats":{"Line":0},"fn_name":"from_config_and_state"},{"line":38,"address":[5142845],"length":1,"stats":{"Line":0},"fn_name":null},{"line":39,"address":[5142857],"length":1,"stats":{"Line":0},"fn_name":null},{"line":40,"address":[5142866],"length":1,"stats":{"Line":0},"fn_name":null},{"line":42,"address":[5142880],"length":1,"stats":{"Line":0},"fn_name":null},{"line":50,"address":[5143212,5143184],"length":1,"stats":{"Line":0},"fn_name":"from"},{"line":53,"address":[5143225],"length":1,"stats":{"Line":0},"fn_name":null},{"line":54,"address":[5143237],"length":1,"stats":{"Line":0},"fn_name":null},{"line":55,"address":[5143246],"length":1,"stats":{"Line":0},"fn_name":null},{"line":103,"address":[5143360],"length":1,"stats":{"Line":0},"fn_name":"deserialize\u003cserde::__private::de::content::ContentRefDeserializer\u003cserde_json::error::Error\u003e\u003e"},{"line":109,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":110,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":112,"address":[5143392],"length":1,"stats":{"Line":0},"fn_name":"expecting"},{"line":113,"address":[5143419],"length":1,"stats":{"Line":0},"fn_name":null},{"line":116,"address":[5143456,5145125],"length":1,"stats":{"Line":0},"fn_name":"visit_map\u003c\u0026mut serde::de::value::MapDeserializer\u003ccore::iter::adapters::map::Map\u003ccore::slice::iter::Iter\u003c(serde::__private::de::content::Content, serde::__private::de::content::Content)\u003e, closure-0\u003e, serde_json::error::Error\u003e\u003e"},{"line":120,"address":[5143484],"length":1,"stats":{"Line":0},"fn_name":null},{"line":121,"address":[5143520],"length":1,"stats":{"Line":0},"fn_name":null},{"line":122,"address":[5143532],"length":1,"stats":{"Line":0},"fn_name":null},{"line":124,"address":[5143785,5143560,5144387],"length":1,"stats":{"Line":0},"fn_name":null},{"line":125,"address":[5144366,5144226,5145238],"length":1,"stats":{"Line":0},"fn_name":null},{"line":126,"address":[5145140,5144011,5143859],"length":1,"stats":{"Line":0},"fn_name":null},{"line":127,"address":[5143904],"length":1,"stats":{"Line":0},"fn_name":null},{"line":128,"address":[5144080,5145248],"length":1,"stats":{"Line":0},"fn_name":null},{"line":129,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":130,"address":[5146736,5146656],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}\u003c\u0026mut serde::de::value::MapDeserializer\u003ccore::iter::adapters::map::Map\u003ccore::slice::iter::Iter\u003c(serde::__private::de::content::Content, serde::__private::de::content::Content)\u003e, closure-0\u003e, serde_json::error::Error\u003e\u003e"},{"line":131,"address":[5146544,5146553,5146668],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}\u003c\u0026mut serde::de::value::MapDeserializer\u003ccore::iter::adapters::map::Map\u003ccore::slice::iter::Iter\u003c(serde::__private::de::content::Content, serde::__private::de::content::Content)\u003e, closure-0\u003e, serde_json::error::Error\u003e\u003e"},{"line":134,"address":[5143953],"length":1,"stats":{"Line":0},"fn_name":null},{"line":135,"address":[5144239,5145035,5145292],"length":1,"stats":{"Line":0},"fn_name":null},{"line":141,"address":[5145395,5144892],"length":1,"stats":{"Line":0},"fn_name":null},{"line":142,"address":[5145627,5144754,5144422],"length":1,"stats":{"Line":0},"fn_name":null},{"line":143,"address":[5144583],"length":1,"stats":{"Line":0},"fn_name":null},{"line":144,"address":[5144672],"length":1,"stats":{"Line":0},"fn_name":null},{"line":145,"address":[5144706],"length":1,"stats":{"Line":0},"fn_name":null},{"line":147,"address":[5144392,5144904],"length":1,"stats":{"Line":0},"fn_name":null},{"line":152,"address":[5143377],"length":1,"stats":{"Line":0},"fn_name":null},{"line":157,"address":[5146768],"length":1,"stats":{"Line":0},"fn_name":"fmt"},{"line":158,"address":[5146800],"length":1,"stats":{"Line":0},"fn_name":null},{"line":159,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":164,"address":[5146928],"length":1,"stats":{"Line":0},"fn_name":"update_server_state"},{"line":169,"address":[5147391],"length":1,"stats":{"Line":0},"fn_name":null},{"line":170,"address":[5147414],"length":1,"stats":{"Line":0},"fn_name":null},{"line":171,"address":[5157425,5156167,5147475,5147647],"length":1,"stats":{"Line":0},"fn_name":null},{"line":172,"address":[5155853,5147979],"length":1,"stats":{"Line":0},"fn_name":null},{"line":173,"address":[5148183,5147901,5156134,5148462,5156692,5157446,5147989],"length":1,"stats":{"Line":0},"fn_name":null},{"line":174,"address":[5148425,5148526],"length":1,"stats":{"Line":0},"fn_name":null},{"line":175,"address":[5148708,5148580],"length":1,"stats":{"Line":0},"fn_name":null},{"line":176,"address":[5148884],"length":1,"stats":{"Line":0},"fn_name":null},{"line":178,"address":[5148956],"length":1,"stats":{"Line":0},"fn_name":null},{"line":179,"address":[5156285,5149034],"length":1,"stats":{"Line":0},"fn_name":null},{"line":180,"address":[5156228,5157137,5149274,5156071,5149115],"length":1,"stats":{"Line":0},"fn_name":null},{"line":181,"address":[5149260,5149339,5149597],"length":1,"stats":{"Line":0},"fn_name":null},{"line":182,"address":[5149296,5149344,5149429],"length":1,"stats":{"Line":0},"fn_name":null},{"line":185,"address":[5149599],"length":1,"stats":{"Line":0},"fn_name":null},{"line":186,"address":[5149815,5150318,5149751,5149670],"length":1,"stats":{"Line":0},"fn_name":null},{"line":188,"address":[5147051,5147024,5149743],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":190,"address":[5149855,5156034,5157043,5156298],"length":1,"stats":{"Line":0},"fn_name":null},{"line":192,"address":[5149958,5150043,5149769],"length":1,"stats":{"Line":0},"fn_name":null},{"line":196,"address":[5151170,5150522,5150417,5150331,5150499],"length":1,"stats":{"Line":0},"fn_name":null},{"line":199,"address":[5150378,5150490],"length":1,"stats":{"Line":0},"fn_name":null},{"line":201,"address":[5150510,5151130,5150872],"length":1,"stats":{"Line":0},"fn_name":null},{"line":202,"address":[5150619,5150527,5150704],"length":1,"stats":{"Line":0},"fn_name":null},{"line":204,"address":[5150573,5150877,5150962],"length":1,"stats":{"Line":0},"fn_name":null},{"line":206,"address":[5151132],"length":1,"stats":{"Line":0},"fn_name":null},{"line":209,"address":[5151172],"length":1,"stats":{"Line":0},"fn_name":null},{"line":210,"address":[5151439,5151299],"length":1,"stats":{"Line":0},"fn_name":null},{"line":213,"address":[5151357,5151481],"length":1,"stats":{"Line":0},"fn_name":null},{"line":214,"address":[5151488],"length":1,"stats":{"Line":0},"fn_name":null},{"line":215,"address":[5151623],"length":1,"stats":{"Line":0},"fn_name":null},{"line":218,"address":[5151836],"length":1,"stats":{"Line":0},"fn_name":null},{"line":219,"address":[5151892,5151943],"length":1,"stats":{"Line":0},"fn_name":null},{"line":220,"address":[5151905],"length":1,"stats":{"Line":0},"fn_name":null},{"line":221,"address":[5151948,5152274],"length":1,"stats":{"Line":0},"fn_name":null},{"line":222,"address":[5152021,5152106,5151958],"length":1,"stats":{"Line":0},"fn_name":null},{"line":223,"address":[5152580,5152008,5152322],"length":1,"stats":{"Line":0},"fn_name":null},{"line":224,"address":[5152327,5152279,5152412],"length":1,"stats":{"Line":0},"fn_name":null},{"line":228,"address":[5152582],"length":1,"stats":{"Line":0},"fn_name":null},{"line":229,"address":[5152689,5152638],"length":1,"stats":{"Line":0},"fn_name":null},{"line":230,"address":[5152651],"length":1,"stats":{"Line":0},"fn_name":null},{"line":231,"address":[5152694,5153307,5153049],"length":1,"stats":{"Line":0},"fn_name":null},{"line":232,"address":[5152881,5152704,5152796],"length":1,"stats":{"Line":0},"fn_name":null},{"line":234,"address":[5152750,5153139,5153054],"length":1,"stats":{"Line":0},"fn_name":null},{"line":238,"address":[5153812,5153309,5153447],"length":1,"stats":{"Line":0},"fn_name":null},{"line":239,"address":[5153537,5153452,5153404],"length":1,"stats":{"Line":0},"fn_name":null},{"line":242,"address":[5153942,5153814],"length":1,"stats":{"Line":0},"fn_name":null},{"line":244,"address":[5154144],"length":1,"stats":{"Line":0},"fn_name":null},{"line":245,"address":[5154304,5154176,5154422],"length":1,"stats":{"Line":0},"fn_name":null},{"line":247,"address":[5154398],"length":1,"stats":{"Line":0},"fn_name":null},{"line":250,"address":[5155971,5154832,5148464,5154756],"length":1,"stats":{"Line":0},"fn_name":null},{"line":252,"address":[5147920],"length":1,"stats":{"Line":0},"fn_name":null},{"line":253,"address":[5155111,5147948],"length":1,"stats":{"Line":0},"fn_name":null},{"line":254,"address":[5155141,5155327,5155251],"length":1,"stats":{"Line":0},"fn_name":null},{"line":256,"address":[5155505,5155920,5155581,5155184],"length":1,"stats":{"Line":0},"fn_name":null}],"covered":0,"coverable":92},{"path":["/","home","edward","Documents","repos","mangadex-home","src","routes.rs"],"content":"use std::sync::atomic::Ordering;\n\nuse actix_web::error::ErrorNotFound;\nuse actix_web::http::header::{CONTENT_LENGTH, CONTENT_TYPE, LAST_MODIFIED};\nuse actix_web::http::HeaderValue;\nuse actix_web::web::Path;\nuse actix_web::HttpResponseBuilder;\nuse actix_web::{get, web::Data, HttpRequest, HttpResponse, Responder};\nuse base64::DecodeError;\nuse bytes::Bytes;\nuse chrono::{DateTime, Utc};\nuse futures::Stream;\nuse log::{debug, error, info, trace};\nuse prometheus::{Encoder, TextEncoder};\nuse serde::Deserialize;\nuse sodiumoxide::crypto::box_::{open_precomputed, Nonce, PrecomputedKey, NONCEBYTES};\nuse thiserror::Error;\n\nuse crate::cache::{Cache, CacheKey, ImageMetadata, UpstreamError};\nuse crate::client::{FetchResult, DEFAULT_HEADERS, HTTP_CLIENT};\nuse crate::config::{OFFLINE_MODE, VALIDATE_TOKENS};\nuse crate::metrics::{\n CACHE_HIT_COUNTER, CACHE_MISS_COUNTER, REQUESTS_DATA_COUNTER, REQUESTS_DATA_SAVER_COUNTER,\n REQUESTS_OTHER_COUNTER, REQUESTS_TOTAL_COUNTER,\n};\nuse crate::state::RwLockServerState;\n\nconst BASE64_CONFIG: base64::Config = base64::Config::new(base64::CharacterSet::UrlSafe, false);\n\npub enum ServerResponse {\n TokenValidationError(TokenValidationError),\n HttpResponse(HttpResponse),\n}\n\nimpl Responder for ServerResponse {\n #[inline]\n fn respond_to(self, req: \u0026HttpRequest) -\u003e HttpResponse {\n match self {\n Self::TokenValidationError(e) =\u003e e.respond_to(req),\n Self::HttpResponse(resp) =\u003e {\n REQUESTS_TOTAL_COUNTER.inc();\n resp.respond_to(req)\n }\n }\n }\n}\n\n#[allow(clippy::unused_async)]\n#[get(\"/\")]\nasync fn index() -\u003e impl Responder {\n HttpResponse::Ok().body(include_str!(\"index.html\"))\n}\n\n#[allow(clippy::future_not_send)]\n#[get(\"/{token}/data/{chapter_hash}/{file_name}\")]\nasync fn token_data(\n state: Data\u003cRwLockServerState\u003e,\n cache: Data\u003cdyn Cache\u003e,\n path: Path\u003c(String, String, String)\u003e,\n) -\u003e impl Responder {\n REQUESTS_DATA_COUNTER.inc();\n let (token, chapter_hash, file_name) = path.into_inner();\n if VALIDATE_TOKENS.load(Ordering::Acquire) {\n if let Err(e) = validate_token(\u0026state.0.read().precomputed_key, token, \u0026chapter_hash) {\n return ServerResponse::TokenValidationError(e);\n }\n }\n fetch_image(state, cache, chapter_hash, file_name, false).await\n}\n\n#[allow(clippy::future_not_send)]\n#[get(\"/{token}/data-saver/{chapter_hash}/{file_name}\")]\nasync fn token_data_saver(\n state: Data\u003cRwLockServerState\u003e,\n cache: Data\u003cdyn Cache\u003e,\n path: Path\u003c(String, String, String)\u003e,\n) -\u003e impl Responder {\n REQUESTS_DATA_SAVER_COUNTER.inc();\n let (token, chapter_hash, file_name) = path.into_inner();\n if VALIDATE_TOKENS.load(Ordering::Acquire) {\n if let Err(e) = validate_token(\u0026state.0.read().precomputed_key, token, \u0026chapter_hash) {\n return ServerResponse::TokenValidationError(e);\n }\n }\n\n fetch_image(state, cache, chapter_hash, file_name, true).await\n}\n\n#[allow(clippy::future_not_send)]\npub async fn default(state: Data\u003cRwLockServerState\u003e, req: HttpRequest) -\u003e impl Responder {\n REQUESTS_OTHER_COUNTER.inc();\n let path = \u0026format!(\n \"{}{}\",\n state.0.read().image_server,\n req.path().chars().skip(1).collect::\u003cString\u003e()\n );\n\n if OFFLINE_MODE.load(Ordering::Acquire) {\n info!(\"Got unknown path in offline mode, returning 404: {}\", path);\n return ServerResponse::HttpResponse(\n ErrorNotFound(\"Path is not valid in offline mode\").into(),\n );\n }\n\n info!(\"Got unknown path, just proxying: {}\", path);\n\n let mut resp = match HTTP_CLIENT.inner().get(path).send().await {\n Ok(resp) =\u003e resp,\n Err(e) =\u003e {\n error!(\"{}\", e);\n return ServerResponse::HttpResponse(HttpResponse::BadGateway().finish());\n }\n };\n let content_type = resp.headers_mut().remove(CONTENT_TYPE);\n let mut resp_builder = HttpResponseBuilder::new(resp.status());\n let mut headers = DEFAULT_HEADERS.clone();\n if let Some(content_type) = content_type {\n headers.insert(CONTENT_TYPE, content_type);\n }\n // push_headers(\u0026mut resp_builder);\n\n let mut resp = resp_builder.body(resp.bytes().await.unwrap_or_default());\n *resp.headers_mut() = headers;\n ServerResponse::HttpResponse(resp)\n}\n\n#[allow(clippy::future_not_send, clippy::unused_async)]\n#[get(\"/metrics\")]\npub async fn metrics() -\u003e impl Responder {\n let metric_families = prometheus::gather();\n let mut buffer = Vec::new();\n TextEncoder::new()\n .encode(\u0026metric_families, \u0026mut buffer)\n .unwrap();\n String::from_utf8(buffer).unwrap()\n}\n\n#[derive(Error, Debug)]\npub enum TokenValidationError {\n #[error(\"Failed to decode base64 token.\")]\n DecodeError(#[from] DecodeError),\n #[error(\"Nonce was too short.\")]\n IncompleteNonce,\n #[error(\"Invalid nonce.\")]\n InvalidNonce,\n #[error(\"Decryption failed\")]\n DecryptionFailure,\n #[error(\"The token format was invalid.\")]\n InvalidToken,\n #[error(\"The token has expired.\")]\n TokenExpired,\n #[error(\"Invalid chapter hash.\")]\n InvalidChapterHash,\n}\n\nimpl Responder for TokenValidationError {\n #[inline]\n fn respond_to(self, _: \u0026HttpRequest) -\u003e HttpResponse {\n let mut resp = HttpResponse::Forbidden().finish();\n *resp.headers_mut() = DEFAULT_HEADERS.clone();\n resp\n }\n}\n\nfn validate_token(\n precomputed_key: \u0026PrecomputedKey,\n token: String,\n chapter_hash: \u0026str,\n) -\u003e Result\u003c(), TokenValidationError\u003e {\n #[derive(Deserialize)]\n struct Token\u003c'a\u003e {\n expires: DateTime\u003cUtc\u003e,\n hash: \u0026'a str,\n }\n\n let data = base64::decode_config(token, BASE64_CONFIG)?;\n if data.len() \u003c NONCEBYTES {\n return Err(TokenValidationError::IncompleteNonce);\n }\n\n let (nonce, encrypted) = data.split_at(NONCEBYTES);\n\n let nonce = Nonce::from_slice(nonce).ok_or(TokenValidationError::InvalidNonce)?;\n let decrypted = open_precomputed(encrypted, \u0026nonce, precomputed_key)\n .map_err(|_| TokenValidationError::DecryptionFailure)?;\n\n let parsed_token: Token =\n serde_json::from_slice(\u0026decrypted).map_err(|_| TokenValidationError::InvalidToken)?;\n\n if parsed_token.expires \u003c Utc::now() {\n return Err(TokenValidationError::TokenExpired);\n }\n\n if parsed_token.hash != chapter_hash {\n return Err(TokenValidationError::InvalidChapterHash);\n }\n\n debug!(\"Token validated!\");\n\n Ok(())\n}\n\n#[allow(clippy::future_not_send)]\nasync fn fetch_image(\n state: Data\u003cRwLockServerState\u003e,\n cache: Data\u003cdyn Cache\u003e,\n chapter_hash: String,\n file_name: String,\n is_data_saver: bool,\n) -\u003e ServerResponse {\n let key = CacheKey(chapter_hash, file_name, is_data_saver);\n\n match cache.get(\u0026key).await {\n Some(Ok((image, metadata))) =\u003e {\n CACHE_HIT_COUNTER.inc();\n return construct_response(image, \u0026metadata);\n }\n Some(Err(_)) =\u003e {\n return ServerResponse::HttpResponse(HttpResponse::BadGateway().finish());\n }\n None =\u003e (),\n }\n\n CACHE_MISS_COUNTER.inc();\n\n // If in offline mode, return early since there's nothing else we can do\n if OFFLINE_MODE.load(Ordering::Acquire) {\n return ServerResponse::HttpResponse(\n ErrorNotFound(\"Offline mode enabled and image not in cache\").into(),\n );\n }\n\n let url = if is_data_saver {\n format!(\n \"{}/data-saver/{}/{}\",\n state.0.read().image_server,\n \u0026key.0,\n \u0026key.1,\n )\n } else {\n format!(\"{}/data/{}/{}\", state.0.read().image_server, \u0026key.0, \u0026key.1)\n };\n\n match HTTP_CLIENT.fetch_and_cache(url, key, cache).await {\n FetchResult::ServiceUnavailable =\u003e {\n ServerResponse::HttpResponse(HttpResponse::ServiceUnavailable().finish())\n }\n FetchResult::InternalServerError =\u003e {\n ServerResponse::HttpResponse(HttpResponse::InternalServerError().finish())\n }\n FetchResult::Data(status, headers, data) =\u003e {\n let mut resp = HttpResponseBuilder::new(status);\n let mut resp = resp.body(data);\n *resp.headers_mut() = headers;\n ServerResponse::HttpResponse(resp)\n }\n FetchResult::Processing =\u003e panic!(\"Race condition found with fetch result\"),\n }\n}\n\n#[inline]\npub fn construct_response(\n data: impl Stream\u003cItem = Result\u003cBytes, UpstreamError\u003e\u003e + Unpin + 'static,\n metadata: \u0026ImageMetadata,\n) -\u003e ServerResponse {\n trace!(\"Constructing response\");\n\n let mut resp = HttpResponse::Ok();\n\n let mut headers = DEFAULT_HEADERS.clone();\n if let Some(content_type) = metadata.content_type {\n headers.insert(\n CONTENT_TYPE,\n HeaderValue::from_str(content_type.as_ref()).unwrap(),\n );\n }\n\n if let Some(content_length) = metadata.content_length {\n headers.insert(CONTENT_LENGTH, HeaderValue::from(content_length));\n }\n\n if let Some(last_modified) = metadata.last_modified {\n headers.insert(\n LAST_MODIFIED,\n HeaderValue::from_str(\u0026last_modified.to_rfc2822()).unwrap(),\n );\n }\n\n let mut ret = resp.streaming(data);\n *ret.headers_mut() = headers;\n\n ServerResponse::HttpResponse(ret)\n}\n","traces":[{"line":37,"address":[4371216,4371540],"length":1,"stats":{"Line":0},"fn_name":"respond_to"},{"line":38,"address":[4371343],"length":1,"stats":{"Line":0},"fn_name":null},{"line":39,"address":[4371251,4371360],"length":1,"stats":{"Line":0},"fn_name":null},{"line":40,"address":[4371274],"length":1,"stats":{"Line":0},"fn_name":null},{"line":41,"address":[4371321,4371401],"length":1,"stats":{"Line":0},"fn_name":null},{"line":42,"address":[4371418],"length":1,"stats":{"Line":0},"fn_name":null},{"line":49,"address":[4402835,4402816],"length":1,"stats":{"Line":0},"fn_name":"register"},{"line":50,"address":[4403210,4403168,4403384,4403124,4403120],"length":1,"stats":{"Line":0},"fn_name":"index"},{"line":51,"address":[4403255],"length":1,"stats":{"Line":0},"fn_name":null},{"line":55,"address":[4403555,4403536],"length":1,"stats":{"Line":0},"fn_name":"register"},{"line":56,"address":[4403840],"length":1,"stats":{"Line":0},"fn_name":"token_data"},{"line":61,"address":[4404228],"length":1,"stats":{"Line":0},"fn_name":null},{"line":62,"address":[4404270],"length":1,"stats":{"Line":0},"fn_name":null},{"line":63,"address":[4404554,4404481],"length":1,"stats":{"Line":0},"fn_name":null},{"line":64,"address":[4404564,4405826,4404890,4406102,4404535],"length":1,"stats":{"Line":0},"fn_name":null},{"line":65,"address":[4404906],"length":1,"stats":{"Line":0},"fn_name":null},{"line":68,"address":[4405188,4406229,4405732,4404976],"length":1,"stats":{"Line":0},"fn_name":null},{"line":72,"address":[4406624,4406643],"length":1,"stats":{"Line":0},"fn_name":"register"},{"line":73,"address":[4406928],"length":1,"stats":{"Line":0},"fn_name":"token_data_saver"},{"line":78,"address":[4407316],"length":1,"stats":{"Line":0},"fn_name":null},{"line":79,"address":[4407358],"length":1,"stats":{"Line":0},"fn_name":null},{"line":80,"address":[4407569,4407642],"length":1,"stats":{"Line":0},"fn_name":null},{"line":81,"address":[4407652,4408914,4409190,4407978,4407623],"length":1,"stats":{"Line":0},"fn_name":null},{"line":82,"address":[4407994],"length":1,"stats":{"Line":0},"fn_name":null},{"line":86,"address":[4409317,4408820,4408276,4408064],"length":1,"stats":{"Line":0},"fn_name":null},{"line":90,"address":[4375722,4371664,4371725,4371584,4376713,4375818,4371615,4376774,4376192],"length":1,"stats":{"Line":0},"fn_name":"default"},{"line":91,"address":[4371820],"length":1,"stats":{"Line":0},"fn_name":null},{"line":92,"address":[4372158,4376128],"length":1,"stats":{"Line":0},"fn_name":null},{"line":94,"address":[4371868],"length":1,"stats":{"Line":0},"fn_name":null},{"line":95,"address":[4372008],"length":1,"stats":{"Line":0},"fn_name":null},{"line":98,"address":[4372549],"length":1,"stats":{"Line":0},"fn_name":null},{"line":99,"address":[4372601,4372778,4372693],"length":1,"stats":{"Line":0},"fn_name":null},{"line":100,"address":[4373129],"length":1,"stats":{"Line":0},"fn_name":null},{"line":101,"address":[4373057],"length":1,"stats":{"Line":0},"fn_name":null},{"line":105,"address":[4373261,4373176,4372647],"length":1,"stats":{"Line":0},"fn_name":null},{"line":107,"address":[4376740,4376086,4374043,4373540,4373727],"length":1,"stats":{"Line":0},"fn_name":null},{"line":108,"address":[4374045,4373966],"length":1,"stats":{"Line":0},"fn_name":null},{"line":109,"address":[4373981],"length":1,"stats":{"Line":0},"fn_name":null},{"line":110,"address":[4374154,4373997,4374230],"length":1,"stats":{"Line":0},"fn_name":null},{"line":111,"address":[4374480,4376048],"length":1,"stats":{"Line":0},"fn_name":null},{"line":114,"address":[4374133,4374636],"length":1,"stats":{"Line":0},"fn_name":null},{"line":115,"address":[4374719],"length":1,"stats":{"Line":0},"fn_name":null},{"line":116,"address":[4374768],"length":1,"stats":{"Line":0},"fn_name":null},{"line":117,"address":[4375075,4374829],"length":1,"stats":{"Line":0},"fn_name":null},{"line":118,"address":[4374920],"length":1,"stats":{"Line":0},"fn_name":null},{"line":122,"address":[4376761,4375941,4375085,4375236],"length":1,"stats":{"Line":0},"fn_name":null},{"line":123,"address":[4376559,4375542,4375906,4376229],"length":1,"stats":{"Line":0},"fn_name":null},{"line":124,"address":[4376320],"length":1,"stats":{"Line":0},"fn_name":null},{"line":128,"address":[4409712,4409731],"length":1,"stats":{"Line":0},"fn_name":"register"},{"line":129,"address":[4410064,4410016,4410107,4410020,4410523],"length":1,"stats":{"Line":0},"fn_name":"metrics"},{"line":130,"address":[4410153],"length":1,"stats":{"Line":0},"fn_name":null},{"line":131,"address":[4410177],"length":1,"stats":{"Line":0},"fn_name":null},{"line":132,"address":[4410266,4410193],"length":1,"stats":{"Line":0},"fn_name":null},{"line":133,"address":[4410217],"length":1,"stats":{"Line":0},"fn_name":null},{"line":135,"address":[4410296],"length":1,"stats":{"Line":0},"fn_name":null},{"line":158,"address":[4377587,4377360],"length":1,"stats":{"Line":0},"fn_name":"respond_to"},{"line":159,"address":[4377576,4377415],"length":1,"stats":{"Line":0},"fn_name":null},{"line":160,"address":[4377467,4377606,4377669],"length":1,"stats":{"Line":0},"fn_name":null},{"line":161,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":165,"address":[4377808,4380242],"length":1,"stats":{"Line":0},"fn_name":"validate_token"},{"line":176,"address":[4378075,4377847],"length":1,"stats":{"Line":0},"fn_name":null},{"line":177,"address":[4378260,4378042],"length":1,"stats":{"Line":0},"fn_name":null},{"line":178,"address":[4378266],"length":1,"stats":{"Line":0},"fn_name":null},{"line":181,"address":[4378311],"length":1,"stats":{"Line":0},"fn_name":null},{"line":183,"address":[4378697,4380275,4378473],"length":1,"stats":{"Line":0},"fn_name":null},{"line":184,"address":[4378888,4378935,4379158,4379043,4378666],"length":1,"stats":{"Line":0},"fn_name":null},{"line":185,"address":[4378918,4380480,4380484,4379045,4380257],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":187,"address":[4380521,4379461,4378999,4380512,4380338,4379284],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":190,"address":[4379633,4379431],"length":1,"stats":{"Line":0},"fn_name":null},{"line":191,"address":[4379709],"length":1,"stats":{"Line":0},"fn_name":null},{"line":194,"address":[4379754],"length":1,"stats":{"Line":0},"fn_name":null},{"line":195,"address":[4379797],"length":1,"stats":{"Line":0},"fn_name":null},{"line":198,"address":[4379842,4379958],"length":1,"stats":{"Line":0},"fn_name":null},{"line":200,"address":[4380114],"length":1,"stats":{"Line":0},"fn_name":null},{"line":204,"address":[4380560],"length":1,"stats":{"Line":0},"fn_name":"fetch_image"},{"line":211,"address":[4381005],"length":1,"stats":{"Line":0},"fn_name":null},{"line":213,"address":[4381645,4385616,4386292,4381336,4381139],"length":1,"stats":{"Line":0},"fn_name":null},{"line":214,"address":[4381647,4381573,4381625],"length":1,"stats":{"Line":0},"fn_name":null},{"line":215,"address":[4381815],"length":1,"stats":{"Line":0},"fn_name":null},{"line":216,"address":[4381855],"length":1,"stats":{"Line":0},"fn_name":null},{"line":219,"address":[4382035,4385562],"length":1,"stats":{"Line":0},"fn_name":null},{"line":224,"address":[4381597,4382183],"length":1,"stats":{"Line":0},"fn_name":null},{"line":227,"address":[4382190],"length":1,"stats":{"Line":0},"fn_name":null},{"line":228,"address":[4382333],"length":1,"stats":{"Line":0},"fn_name":null},{"line":229,"address":[4382326,4382242],"length":1,"stats":{"Line":0},"fn_name":null},{"line":233,"address":[4383117,4382291],"length":1,"stats":{"Line":0},"fn_name":null},{"line":234,"address":[4382580,4385523],"length":1,"stats":{"Line":0},"fn_name":null},{"line":236,"address":[4382388,4382451],"length":1,"stats":{"Line":0},"fn_name":null},{"line":237,"address":[4382553],"length":1,"stats":{"Line":0},"fn_name":null},{"line":238,"address":[4382568],"length":1,"stats":{"Line":0},"fn_name":null},{"line":241,"address":[4382418,4383130,4385492],"length":1,"stats":{"Line":0},"fn_name":null},{"line":244,"address":[4383994,4383736,4385392,4384425,4386313],"length":1,"stats":{"Line":0},"fn_name":null},{"line":245,"address":[4384358],"length":1,"stats":{"Line":0},"fn_name":null},{"line":246,"address":[4384427],"length":1,"stats":{"Line":0},"fn_name":null},{"line":249,"address":[4384567],"length":1,"stats":{"Line":0},"fn_name":null},{"line":251,"address":[4384715],"length":1,"stats":{"Line":0},"fn_name":null},{"line":252,"address":[4384707,4384846],"length":1,"stats":{"Line":0},"fn_name":null},{"line":253,"address":[4384853],"length":1,"stats":{"Line":0},"fn_name":null},{"line":254,"address":[4385717,4385247,4386030,4384928],"length":1,"stats":{"Line":0},"fn_name":null},{"line":255,"address":[4385800],"length":1,"stats":{"Line":0},"fn_name":null},{"line":257,"address":[4384392],"length":1,"stats":{"Line":0},"fn_name":null},{"line":262,"address":[4388427,4387008],"length":1,"stats":{"Line":0},"fn_name":"construct_response\u003cmangadex_home::cache::CacheStream\u003e"},{"line":266,"address":[4387046,4387185],"length":1,"stats":{"Line":0},"fn_name":null},{"line":268,"address":[4387329],"length":1,"stats":{"Line":0},"fn_name":null},{"line":270,"address":[4387348],"length":1,"stats":{"Line":0},"fn_name":null},{"line":271,"address":[4387647,4387403],"length":1,"stats":{"Line":0},"fn_name":null},{"line":272,"address":[4387539],"length":1,"stats":{"Line":0},"fn_name":null},{"line":273,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":274,"address":[4387457],"length":1,"stats":{"Line":0},"fn_name":null},{"line":278,"address":[4387809,4387654],"length":1,"stats":{"Line":0},"fn_name":null},{"line":279,"address":[4387682],"length":1,"stats":{"Line":0},"fn_name":null},{"line":282,"address":[4387816,4388087],"length":1,"stats":{"Line":0},"fn_name":null},{"line":283,"address":[4387960],"length":1,"stats":{"Line":0},"fn_name":null},{"line":284,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":285,"address":[4387863],"length":1,"stats":{"Line":0},"fn_name":null},{"line":289,"address":[4388094],"length":1,"stats":{"Line":0},"fn_name":null},{"line":290,"address":[4388228,4388446,4388682],"length":1,"stats":{"Line":0},"fn_name":null},{"line":292,"address":[4388533],"length":1,"stats":{"Line":0},"fn_name":null}],"covered":0,"coverable":118},{"path":["/","home","edward","Documents","repos","mangadex-home","src","state.rs"],"content":"use std::str::FromStr;\nuse std::sync::atomic::{AtomicBool, Ordering};\n\nuse crate::config::{ClientSecret, Config, UnstableOptions, OFFLINE_MODE, VALIDATE_TOKENS};\nuse crate::ping::{Request, Response, CONTROL_CENTER_PING_URL};\nuse arc_swap::ArcSwap;\nuse log::{error, info, warn};\nuse once_cell::sync::OnceCell;\nuse parking_lot::RwLock;\nuse rustls::sign::{CertifiedKey, SigningKey};\nuse rustls::Certificate;\nuse rustls::{ClientHello, ResolvesServerCert};\nuse sodiumoxide::crypto::box_::{PrecomputedKey, PRECOMPUTEDKEYBYTES};\nuse thiserror::Error;\nuse url::Url;\n\npub struct ServerState {\n pub precomputed_key: PrecomputedKey,\n pub image_server: Url,\n pub url: Url,\n pub url_overridden: bool,\n}\n\npub static PREVIOUSLY_PAUSED: AtomicBool = AtomicBool::new(false);\npub static PREVIOUSLY_COMPROMISED: AtomicBool = AtomicBool::new(false);\n\npub static TLS_PREVIOUSLY_CREATED: OnceCell\u003cArcSwap\u003cString\u003e\u003e = OnceCell::new();\npub static TLS_SIGNING_KEY: OnceCell\u003cArcSwap\u003cBox\u003cdyn SigningKey\u003e\u003e\u003e = OnceCell::new();\npub static TLS_CERTS: OnceCell\u003cArcSwap\u003cVec\u003cCertificate\u003e\u003e\u003e = OnceCell::new();\n\n#[derive(Error, Debug)]\npub enum ServerInitError {\n #[error(transparent)]\n MalformedResponse(reqwest::Error),\n #[error(transparent)]\n Timeout(reqwest::Error),\n #[error(transparent)]\n SendFailure(reqwest::Error),\n #[error(\"Failed to parse token key\")]\n KeyParseError(String),\n #[error(\"Token key was not provided in initial request\")]\n MissingTokenKey,\n #[error(\"Got error response from control center\")]\n ErrorResponse,\n}\n\nimpl ServerState {\n pub async fn init(secret: \u0026ClientSecret, config: \u0026Config) -\u003e Result\u003cSelf, ServerInitError\u003e {\n let resp = reqwest::Client::new()\n .post(CONTROL_CENTER_PING_URL)\n .json(\u0026Request::from((secret, config)))\n .send()\n .await;\n\n match resp {\n Ok(resp) =\u003e match resp.json::\u003cResponse\u003e().await {\n Ok(Response::Ok(mut resp)) =\u003e {\n let key = resp\n .token_key\n .ok_or(ServerInitError::MissingTokenKey)\n .and_then(|key| {\n if let Some(key) = base64::decode(\u0026key)\n .ok()\n .and_then(|k| PrecomputedKey::from_slice(\u0026k))\n {\n Ok(key)\n } else {\n error!(\"Failed to parse token key: got {}\", key);\n Err(ServerInitError::KeyParseError(key))\n }\n })?;\n\n PREVIOUSLY_COMPROMISED.store(resp.compromised, Ordering::Release);\n if resp.compromised {\n error!(\"Got compromised response from control center!\");\n }\n\n PREVIOUSLY_PAUSED.store(resp.paused, Ordering::Release);\n if resp.paused {\n warn!(\"Control center has paused this node!\");\n }\n\n if let Some(ref override_url) = config.override_upstream {\n resp.image_server = override_url.clone();\n warn!(\"Upstream URL overridden to: {}\", resp.image_server);\n } else {\n }\n\n info!(\"This client's URL has been set to {}\", resp.url);\n\n if config\n .unstable_options\n .contains(\u0026UnstableOptions::DisableTokenValidation)\n {\n warn!(\"Token validation is explicitly disabled!\");\n } else {\n if resp.force_tokens {\n info!(\"This client will validate tokens.\");\n } else {\n info!(\"This client will not validate tokens.\");\n }\n VALIDATE_TOKENS.store(resp.force_tokens, Ordering::Release);\n }\n\n let tls = resp.tls.unwrap();\n std::mem::drop(\n TLS_PREVIOUSLY_CREATED.set(ArcSwap::from_pointee(tls.created_at)),\n );\n std::mem::drop(TLS_SIGNING_KEY.set(ArcSwap::new(tls.priv_key)));\n std::mem::drop(TLS_CERTS.set(ArcSwap::from_pointee(tls.certs)));\n\n Ok(Self {\n precomputed_key: key,\n image_server: resp.image_server,\n url: resp.url,\n url_overridden: config.override_upstream.is_some(),\n })\n }\n Ok(Response::Error(resp)) =\u003e {\n error!(\n \"Got an {} error from upstream: {}\",\n resp.status as u16, resp.error\n );\n Err(ServerInitError::ErrorResponse)\n }\n Err(e) =\u003e {\n error!(\"Got malformed response: {}. Is MangaDex@Home down?\", e);\n Err(ServerInitError::MalformedResponse(e))\n }\n },\n Err(e) =\u003e match e {\n e if e.is_timeout() =\u003e {\n error!(\"Response timed out to control server. Is MangaDex@Home down?\");\n Err(ServerInitError::Timeout(e))\n }\n e =\u003e {\n error!(\"Failed to send request: {}\", e);\n Err(ServerInitError::SendFailure(e))\n }\n },\n }\n }\n\n pub fn init_offline() -\u003e Self {\n assert!(OFFLINE_MODE.load(Ordering::Acquire));\n Self {\n precomputed_key: PrecomputedKey::from_slice(\u0026[41; PRECOMPUTEDKEYBYTES]).unwrap(),\n image_server: Url::from_file_path(\"/dev/null\").unwrap(),\n url: Url::from_str(\"http://localhost\").unwrap(),\n url_overridden: false,\n }\n }\n}\n\npub struct RwLockServerState(pub RwLock\u003cServerState\u003e);\n\npub struct DynamicServerCert;\n\nimpl ResolvesServerCert for DynamicServerCert {\n fn resolve(\u0026self, _: ClientHello) -\u003e Option\u003cCertifiedKey\u003e {\n // TODO: wait for actix-web to use a new version of rustls so we can\n // remove cloning the certs all the time\n Some(CertifiedKey {\n cert: TLS_CERTS.get().unwrap().load().as_ref().clone(),\n key: TLS_SIGNING_KEY.get().unwrap().load_full(),\n ocsp: None,\n sct_list: None,\n })\n }\n}\n","traces":[{"line":48,"address":[6199469,6198144,6199408,6207674,6208079,6208507,6198175],"length":1,"stats":{"Line":0},"fn_name":"init"},{"line":49,"address":[6140143,6199831,6199944,6199637,6207582,6208473],"length":1,"stats":{"Line":0},"fn_name":null},{"line":50,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":51,"address":[6199750,6207617],"length":1,"stats":{"Line":0},"fn_name":null},{"line":53,"address":[6140311,6200215,6140165,6207556,6207603,6207639],"length":1,"stats":{"Line":0},"fn_name":null},{"line":55,"address":[6200348,6207893],"length":1,"stats":{"Line":0},"fn_name":null},{"line":56,"address":[6200841,6200263,6207523,6208494,6200358,6200559,6206296,6140083,6205191],"length":1,"stats":{"Line":0},"fn_name":null},{"line":57,"address":[6200804,6200905],"length":1,"stats":{"Line":0},"fn_name":null},{"line":58,"address":[6201209,6201314,6201027,6200959,6201096],"length":1,"stats":{"Line":0},"fn_name":null},{"line":59,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":60,"address":[6200991],"length":1,"stats":{"Line":0},"fn_name":null},{"line":61,"address":[6198368,6199249],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":62,"address":[6198541,6198716,6198390,6198478],"length":1,"stats":{"Line":0},"fn_name":null},{"line":63,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":64,"address":[6198224,6198251,6198470],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":66,"address":[6198601],"length":1,"stats":{"Line":0},"fn_name":null},{"line":68,"address":[6198493,6198721,6198797],"length":1,"stats":{"Line":0},"fn_name":null},{"line":69,"address":[6199053],"length":1,"stats":{"Line":0},"fn_name":null},{"line":73,"address":[6201168],"length":1,"stats":{"Line":0},"fn_name":null},{"line":74,"address":[6201404,6201662,6201351],"length":1,"stats":{"Line":0},"fn_name":null},{"line":75,"address":[6201409,6201494,6201361],"length":1,"stats":{"Line":0},"fn_name":null},{"line":78,"address":[6201664],"length":1,"stats":{"Line":0},"fn_name":null},{"line":79,"address":[6201755,6202013,6201702],"length":1,"stats":{"Line":0},"fn_name":null},{"line":80,"address":[6201712,6201760,6201845],"length":1,"stats":{"Line":0},"fn_name":null},{"line":83,"address":[6202023,6202497],"length":1,"stats":{"Line":0},"fn_name":null},{"line":84,"address":[6202093,6207696],"length":1,"stats":{"Line":0},"fn_name":null},{"line":85,"address":[6202122,6207839,6202207],"length":1,"stats":{"Line":0},"fn_name":null},{"line":89,"address":[6202499,6202627],"length":1,"stats":{"Line":0},"fn_name":null},{"line":91,"address":[6203962,6203314,6202910],"length":1,"stats":{"Line":0},"fn_name":null},{"line":92,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":93,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":95,"address":[6203061,6203146,6202999],"length":1,"stats":{"Line":0},"fn_name":null},{"line":97,"address":[6203922,6203042,6203664],"length":1,"stats":{"Line":0},"fn_name":null},{"line":98,"address":[6203319,6203411,6203496],"length":1,"stats":{"Line":0},"fn_name":null},{"line":100,"address":[6203669,6203754,6203365],"length":1,"stats":{"Line":0},"fn_name":null},{"line":102,"address":[6203924],"length":1,"stats":{"Line":0},"fn_name":null},{"line":105,"address":[6203964],"length":1,"stats":{"Line":0},"fn_name":null},{"line":107,"address":[6204082],"length":1,"stats":{"Line":0},"fn_name":null},{"line":109,"address":[6204287],"length":1,"stats":{"Line":0},"fn_name":null},{"line":110,"address":[6204468],"length":1,"stats":{"Line":0},"fn_name":null},{"line":112,"address":[6204965],"length":1,"stats":{"Line":0},"fn_name":null},{"line":113,"address":[6204689],"length":1,"stats":{"Line":0},"fn_name":null},{"line":114,"address":[6204729],"length":1,"stats":{"Line":0},"fn_name":null},{"line":115,"address":[6204833],"length":1,"stats":{"Line":0},"fn_name":null},{"line":116,"address":[6204937],"length":1,"stats":{"Line":0},"fn_name":null},{"line":119,"address":[6205196],"length":1,"stats":{"Line":0},"fn_name":null},{"line":120,"address":[6205474,6205228,6205356],"length":1,"stats":{"Line":0},"fn_name":null},{"line":122,"address":[6205450],"length":1,"stats":{"Line":0},"fn_name":null},{"line":124,"address":[6205790],"length":1,"stats":{"Line":0},"fn_name":null},{"line":126,"address":[6200843],"length":1,"stats":{"Line":0},"fn_name":null},{"line":127,"address":[6200859,6205864,6205940],"length":1,"stats":{"Line":0},"fn_name":null},{"line":128,"address":[6206192],"length":1,"stats":{"Line":0},"fn_name":null},{"line":131,"address":[6200282,6206784],"length":1,"stats":{"Line":0},"fn_name":null},{"line":132,"address":[6206308,6200317],"length":1,"stats":{"Line":0},"fn_name":null},{"line":133,"address":[6206524,6206448,6206338],"length":1,"stats":{"Line":0},"fn_name":null},{"line":134,"address":[6206680],"length":1,"stats":{"Line":0},"fn_name":null},{"line":136,"address":[6206381],"length":1,"stats":{"Line":0},"fn_name":null},{"line":137,"address":[6206789,6206405,6206865],"length":1,"stats":{"Line":0},"fn_name":null},{"line":138,"address":[6207110],"length":1,"stats":{"Line":0},"fn_name":null},{"line":144,"address":[6209216,6209595],"length":1,"stats":{"Line":0},"fn_name":"init_offline"},{"line":145,"address":[6209232],"length":1,"stats":{"Line":0},"fn_name":null},{"line":147,"address":[6209300],"length":1,"stats":{"Line":0},"fn_name":null},{"line":148,"address":[6209345],"length":1,"stats":{"Line":0},"fn_name":null},{"line":149,"address":[6209399],"length":1,"stats":{"Line":0},"fn_name":null},{"line":160,"address":[6209664,6210121],"length":1,"stats":{"Line":0},"fn_name":"resolve"},{"line":163,"address":[6209922],"length":1,"stats":{"Line":0},"fn_name":null},{"line":164,"address":[6209689],"length":1,"stats":{"Line":0},"fn_name":null},{"line":165,"address":[6209821],"length":1,"stats":{"Line":0},"fn_name":null},{"line":166,"address":[6209898],"length":1,"stats":{"Line":0},"fn_name":null},{"line":167,"address":[6209910],"length":1,"stats":{"Line":0},"fn_name":null}],"covered":0,"coverable":70},{"path":["/","home","edward","Documents","repos","mangadex-home","src","stop.rs"],"content":"use log::{info, warn};\nuse reqwest::StatusCode;\nuse serde::Serialize;\n\nuse crate::config::ClientSecret;\n\nconst CONTROL_CENTER_STOP_URL: \u0026str = \"https://api.mangadex.network/ping\";\n\n#[derive(Serialize)]\nstruct StopRequest\u003c'a\u003e {\n secret: \u0026'a ClientSecret,\n}\n\npub async fn send_stop(secret: \u0026ClientSecret) {\n let request = StopRequest { secret };\n let client = reqwest::Client::new();\n match client\n .post(CONTROL_CENTER_STOP_URL)\n .json(\u0026request)\n .send()\n .await\n {\n Ok(resp) =\u003e {\n if resp.status() == StatusCode::OK {\n info!(\"Successfully sent stop message to control center.\");\n } else {\n warn!(\"Got weird response from server: {:?}\", resp.headers());\n }\n }\n Err(e) =\u003e warn!(\"Got error while sending stop message: {}\", e),\n }\n}\n","traces":[{"line":14,"address":[4530999,4530960,4532986,4533029,4530896,4530918],"length":1,"stats":{"Line":0},"fn_name":"send_stop"},{"line":15,"address":[4531073],"length":1,"stats":{"Line":0},"fn_name":null},{"line":16,"address":[4531077],"length":1,"stats":{"Line":0},"fn_name":null},{"line":17,"address":[4532908,4531138,4531621,4533016,4531305,4531197],"length":1,"stats":{"Line":0},"fn_name":null},{"line":19,"address":[4531177],"length":1,"stats":{"Line":0},"fn_name":null},{"line":23,"address":[4531623,4531544],"length":1,"stats":{"Line":0},"fn_name":null},{"line":24,"address":[4532417,4532053,4531663],"length":1,"stats":{"Line":0},"fn_name":null},{"line":25,"address":[4531735,4531821,4531897],"length":1,"stats":{"Line":0},"fn_name":null},{"line":27,"address":[4531778,4532058,4532134],"length":1,"stats":{"Line":0},"fn_name":null},{"line":30,"address":[4532441,4532850,4531559,4532517],"length":1,"stats":{"Line":0},"fn_name":null}],"covered":0,"coverable":10},{"path":["/","home","edward","Documents","repos","mangadex-home","src","units.rs"],"content":"use std::fmt::Display;\nuse std::num::{NonZeroU16, NonZeroU64, ParseIntError};\nuse std::str::FromStr;\n\nuse serde::{Deserialize, Serialize};\n\n/// Wrapper type for a port number.\n#[derive(Serialize, Deserialize, Debug, Clone, Copy)]\npub struct Port(NonZeroU16);\n\nimpl Port {\n pub const fn get(self) -\u003e u16 {\n self.0.get()\n }\n}\n\nimpl Default for Port {\n fn default() -\u003e Self {\n Self(unsafe { NonZeroU16::new_unchecked(443) })\n }\n}\n\nimpl FromStr for Port {\n type Err = \u003cNonZeroU16 as FromStr\u003e::Err;\n\n fn from_str(s: \u0026str) -\u003e Result\u003cSelf, Self::Err\u003e {\n NonZeroU16::from_str(s).map(Self)\n }\n}\n\nimpl Display for Port {\n fn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n self.0.fmt(f)\n }\n}\n\n#[derive(Copy, Clone, Serialize, Deserialize, Default, Debug, Hash, Eq, PartialEq)]\npub struct Mebibytes(usize);\n\nimpl FromStr for Mebibytes {\n type Err = ParseIntError;\n\n fn from_str(s: \u0026str) -\u003e Result\u003cSelf, Self::Err\u003e {\n s.parse::\u003cusize\u003e().map(Self)\n }\n}\n\npub struct Bytes(usize);\n\nimpl Bytes {\n pub const fn get(\u0026self) -\u003e usize {\n self.0\n }\n}\n\nimpl From\u003cMebibytes\u003e for Bytes {\n fn from(mib: Mebibytes) -\u003e Self {\n Self(mib.0 \u003c\u003c 20)\n }\n}\n\n#[derive(Copy, Clone, Deserialize, Debug, Hash, Eq, PartialEq)]\npub struct KilobitsPerSecond(NonZeroU64);\n\nimpl FromStr for KilobitsPerSecond {\n type Err = ParseIntError;\n\n fn from_str(s: \u0026str) -\u003e Result\u003cSelf, Self::Err\u003e {\n s.parse::\u003cNonZeroU64\u003e().map(Self)\n }\n}\n\n#[derive(Copy, Clone, Serialize, Debug, Hash, Eq, PartialEq)]\npub struct BytesPerSecond(NonZeroU64);\n\nimpl From\u003cKilobitsPerSecond\u003e for BytesPerSecond {\n fn from(kbps: KilobitsPerSecond) -\u003e Self {\n Self(unsafe { NonZeroU64::new_unchecked(kbps.0.get() * 125) })\n }\n}\n","traces":[{"line":12,"address":[4126240],"length":1,"stats":{"Line":0},"fn_name":"get"},{"line":13,"address":[4126249],"length":1,"stats":{"Line":0},"fn_name":null},{"line":18,"address":[4126272],"length":1,"stats":{"Line":1},"fn_name":"default"},{"line":19,"address":[4126273],"length":1,"stats":{"Line":1},"fn_name":null},{"line":26,"address":[4126320],"length":1,"stats":{"Line":0},"fn_name":"from_str"},{"line":27,"address":[4126334],"length":1,"stats":{"Line":0},"fn_name":null},{"line":32,"address":[4126384],"length":1,"stats":{"Line":0},"fn_name":"fmt"},{"line":33,"address":[4126398],"length":1,"stats":{"Line":0},"fn_name":null},{"line":43,"address":[4126432],"length":1,"stats":{"Line":0},"fn_name":"from_str"},{"line":44,"address":[4126446],"length":1,"stats":{"Line":0},"fn_name":null},{"line":51,"address":[4126560],"length":1,"stats":{"Line":0},"fn_name":"get"},{"line":52,"address":[4126565],"length":1,"stats":{"Line":0},"fn_name":null},{"line":57,"address":[4126576],"length":1,"stats":{"Line":0},"fn_name":"from"},{"line":58,"address":[4126585],"length":1,"stats":{"Line":0},"fn_name":null},{"line":68,"address":[4126624],"length":1,"stats":{"Line":0},"fn_name":"from_str"},{"line":69,"address":[4126638],"length":1,"stats":{"Line":0},"fn_name":null},{"line":77,"address":[4126736],"length":1,"stats":{"Line":0},"fn_name":"from"},{"line":78,"address":[4126813,4126745],"length":1,"stats":{"Line":0},"fn_name":null}],"covered":2,"coverable":18}]};
|
|
var previousData = {"files":[{"path":["/","home","edward","Documents","repos","mangadex-home","build.rs"],"content":"use std::error::Error;\n\nuse vergen::{vergen, Config, ShaKind};\n\nfn main() -\u003e Result\u003c(), Box\u003cdyn Error\u003e\u003e {\n // Initialize vergen stuff\n let mut config = Config::default();\n *config.git_mut().sha_kind_mut() = ShaKind::Short;\n vergen(config)?;\n\n Ok(())\n}\n","traces":[],"covered":0,"coverable":0},{"path":["/","home","edward","Documents","repos","mangadex-home","src","cache","compat.rs"],"content":"//! These structs have alternative deserialize and serializations\n//! implementations to assist reading from the official client file format.\n\nuse std::str::FromStr;\n\nuse chrono::{DateTime, FixedOffset};\nuse serde::de::{Unexpected, Visitor};\nuse serde::{Deserialize, Serialize};\n\nuse super::ImageContentType;\n\n#[derive(Copy, Clone, Deserialize)]\npub struct LegacyImageMetadata {\n pub(crate) content_type: Option\u003cLegacyImageContentType\u003e,\n pub(crate) size: Option\u003cu32\u003e,\n pub(crate) last_modified: Option\u003cLegacyDateTime\u003e,\n}\n\n#[derive(Copy, Clone, Serialize)]\npub struct LegacyDateTime(pub DateTime\u003cFixedOffset\u003e);\n\nimpl\u003c'de\u003e Deserialize\u003c'de\u003e for LegacyDateTime {\n fn deserialize\u003cD\u003e(deserializer: D) -\u003e Result\u003cSelf, D::Error\u003e\n where\n D: serde::Deserializer\u003c'de\u003e,\n {\n struct LegacyDateTimeVisitor;\n\n impl\u003c'de\u003e Visitor\u003c'de\u003e for LegacyDateTimeVisitor {\n type Value = LegacyDateTime;\n\n fn expecting(\u0026self, f: \u0026mut std::fmt::Formatter) -\u003e std::fmt::Result {\n write!(f, \"a valid image type\")\n }\n\n fn visit_str\u003cE\u003e(self, v: \u0026str) -\u003e Result\u003cSelf::Value, E\u003e\n where\n E: serde::de::Error,\n {\n DateTime::parse_from_rfc2822(v)\n .map(LegacyDateTime)\n .map_err(|_| E::invalid_value(Unexpected::Str(v), \u0026\"a valid image type\"))\n }\n }\n\n deserializer.deserialize_str(LegacyDateTimeVisitor)\n }\n}\n\n#[derive(Copy, Clone)]\npub struct LegacyImageContentType(pub ImageContentType);\n\nimpl\u003c'de\u003e Deserialize\u003c'de\u003e for LegacyImageContentType {\n fn deserialize\u003cD\u003e(deserializer: D) -\u003e Result\u003cSelf, D::Error\u003e\n where\n D: serde::Deserializer\u003c'de\u003e,\n {\n struct LegacyImageContentTypeVisitor;\n\n impl\u003c'de\u003e Visitor\u003c'de\u003e for LegacyImageContentTypeVisitor {\n type Value = LegacyImageContentType;\n\n fn expecting(\u0026self, f: \u0026mut std::fmt::Formatter) -\u003e std::fmt::Result {\n write!(f, \"a valid image type\")\n }\n\n fn visit_str\u003cE\u003e(self, v: \u0026str) -\u003e Result\u003cSelf::Value, E\u003e\n where\n E: serde::de::Error,\n {\n ImageContentType::from_str(v)\n .map(LegacyImageContentType)\n .map_err(|_| E::invalid_value(Unexpected::Str(v), \u0026\"a valid image type\"))\n }\n }\n\n deserializer.deserialize_str(LegacyImageContentTypeVisitor)\n }\n}\n","traces":[{"line":23,"address":[4324992],"length":1,"stats":{"Line":1},"fn_name":"deserialize\u003c\u0026mut serde_json::de::Deserializer\u003cserde_json::read::IoRead\u003cstd::fs::File\u003e\u003e\u003e"},{"line":29,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":30,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":32,"address":[4325024],"length":1,"stats":{"Line":0},"fn_name":"expecting"},{"line":33,"address":[4325043],"length":1,"stats":{"Line":0},"fn_name":null},{"line":36,"address":[4325120],"length":1,"stats":{"Line":1},"fn_name":"visit_str\u003cserde_json::error::Error\u003e"},{"line":40,"address":[4325144,4325195],"length":1,"stats":{"Line":2},"fn_name":null},{"line":41,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":42,"address":[4325185,4325232,4325248],"length":1,"stats":{"Line":1},"fn_name":"{{closure}}\u003cserde_json::error::Error\u003e"},{"line":46,"address":[4325009],"length":1,"stats":{"Line":1},"fn_name":null},{"line":54,"address":[4325312],"length":1,"stats":{"Line":1},"fn_name":"deserialize\u003c\u0026mut serde_json::de::Deserializer\u003cserde_json::read::IoRead\u003cstd::fs::File\u003e\u003e\u003e"},{"line":60,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":61,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":63,"address":[4325376],"length":1,"stats":{"Line":0},"fn_name":"expecting"},{"line":64,"address":[4325395],"length":1,"stats":{"Line":0},"fn_name":null},{"line":67,"address":[4325472],"length":1,"stats":{"Line":1},"fn_name":"visit_str\u003cserde_json::error::Error\u003e"},{"line":71,"address":[4325535,4325486],"length":1,"stats":{"Line":2},"fn_name":null},{"line":72,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":73,"address":[4325530,4325609,4325600],"length":1,"stats":{"Line":1},"fn_name":"{{closure}}\u003cserde_json::error::Error\u003e"},{"line":77,"address":[4325321],"length":1,"stats":{"Line":1},"fn_name":null}],"covered":10,"coverable":20},{"path":["/","home","edward","Documents","repos","mangadex-home","src","cache","disk.rs"],"content":"//! Low memory caching stuff\n\nuse std::convert::TryFrom;\nuse std::hint::unreachable_unchecked;\nuse std::os::unix::prelude::OsStrExt;\nuse std::path::{Path, PathBuf};\nuse std::str::FromStr;\nuse std::sync::atomic::{AtomicU64, Ordering};\nuse std::sync::Arc;\n\nuse async_trait::async_trait;\nuse futures::StreamExt;\nuse log::{debug, error, warn, LevelFilter};\nuse md5::digest::generic_array::GenericArray;\nuse md5::{Digest, Md5};\nuse sqlx::sqlite::SqliteConnectOptions;\nuse sqlx::{ConnectOptions, Sqlite, SqlitePool, Transaction};\nuse tokio::fs::remove_file;\nuse tokio::sync::mpsc::{channel, Receiver, Sender};\nuse tokio_stream::wrappers::ReceiverStream;\n\nuse crate::units::Bytes;\n\nuse super::{Cache, CacheError, CacheKey, CacheStream, CallbackCache, ImageMetadata};\n\npub struct DiskCache {\n disk_path: PathBuf,\n disk_cur_size: AtomicU64,\n db_update_channel_sender: Sender\u003cDbMessage\u003e,\n}\n\nenum DbMessage {\n Get(Arc\u003cPathBuf\u003e),\n Put(Arc\u003cPathBuf\u003e, u64),\n}\n\nimpl DiskCache {\n /// Constructs a new low memory cache at the provided path and capacity.\n /// This internally spawns a task that will wait for filesystem\n /// notifications when a file has been written.\n pub async fn new(disk_max_size: Bytes, disk_path: PathBuf) -\u003e Arc\u003cSelf\u003e {\n let (db_tx, db_rx) = channel(128);\n let db_pool = {\n let db_url = format!(\"sqlite:{}/metadata.sqlite\", disk_path.to_string_lossy());\n let mut options = SqliteConnectOptions::from_str(\u0026db_url)\n .unwrap()\n .create_if_missing(true);\n options.log_statements(LevelFilter::Trace);\n let db = SqlitePool::connect_with(options).await.unwrap();\n\n // Run db init\n sqlx::query_file!(\"./db_queries/init.sql\")\n .execute(\u0026mut db.acquire().await.unwrap())\n .await\n .unwrap();\n\n db\n };\n\n // This is intentional.\n #[allow(clippy::cast_sign_loss)]\n let disk_cur_size = {\n let mut conn = db_pool.acquire().await.unwrap();\n sqlx::query!(\"SELECT IFNULL(SUM(size), 0) AS size FROM Images\")\n .fetch_one(\u0026mut conn)\n .await\n .map(|record| record.size)\n .unwrap_or_default()\n .unwrap_or_default() as u64\n };\n\n let new_self = Arc::new(Self {\n disk_path,\n disk_cur_size: AtomicU64::new(disk_cur_size),\n db_update_channel_sender: db_tx,\n });\n\n // maybe this is better? in theory this sounds like it should be faster\n // but in practice I haven't seen any perf improvements\n\n // let ret = Arc::clone(\u0026new_self);\n // std::thread::spawn(move || {\n // tokio::runtime::Builder::new_current_thread()\n // .enable_all()\n // .build()\n // .unwrap()\n // .block_on(db_listener(\n // Arc::clone(\u0026new_self),\n // db_rx,\n // db_pool,\n // disk_max_size.get() as u64 / 20 * 19,\n // ))\n // });\n // ret\n\n tokio::spawn(db_listener(\n Arc::clone(\u0026new_self),\n db_rx,\n db_pool,\n disk_max_size.get() as u64 / 20 * 19,\n ));\n\n new_self\n }\n\n #[cfg(test)]\n fn in_memory() -\u003e (Self, Receiver\u003cDbMessage\u003e) {\n let (db_tx, db_rx) = channel(128);\n (\n Self {\n disk_path: PathBuf::new(),\n disk_cur_size: AtomicU64::new(0),\n db_update_channel_sender: db_tx,\n },\n db_rx,\n )\n }\n}\n\n/// Spawn a new task that will listen for updates to the db, pruning if the size\n/// becomes too large.\nasync fn db_listener(\n cache: Arc\u003cDiskCache\u003e,\n db_rx: Receiver\u003cDbMessage\u003e,\n db_pool: SqlitePool,\n max_on_disk_size: u64,\n) {\n let mut recv_stream = ReceiverStream::new(db_rx).ready_chunks(128);\n while let Some(messages) = recv_stream.next().await {\n let mut transaction = match db_pool.begin().await {\n Ok(transaction) =\u003e transaction,\n Err(e) =\u003e {\n error!(\"Failed to start a transaction to DB, cannot update DB. Disk cache may be losing track of files! {}\", e);\n continue;\n }\n };\n\n for message in messages {\n match message {\n DbMessage::Get(entry) =\u003e handle_db_get(\u0026entry, \u0026mut transaction).await,\n DbMessage::Put(entry, size) =\u003e {\n handle_db_put(\u0026entry, size, \u0026cache, \u0026mut transaction).await;\n }\n }\n }\n\n if let Err(e) = transaction.commit().await {\n error!(\n \"Failed to commit transaction to DB. Disk cache may be losing track of files! {}\",\n e\n );\n }\n\n let on_disk_size = (cache.disk_cur_size.load(Ordering::Acquire) + 4095) / 4096 * 4096;\n if on_disk_size \u003e= max_on_disk_size {\n let mut conn = match db_pool.acquire().await {\n Ok(conn) =\u003e conn,\n Err(e) =\u003e {\n error!(\n \"Failed to get a DB connection and cannot prune disk cache: {}\",\n e\n );\n continue;\n }\n };\n\n let items = {\n let request =\n sqlx::query!(\"select id, size from Images order by accessed asc limit 1000\")\n .fetch_all(\u0026mut conn)\n .await;\n match request {\n Ok(items) =\u003e items,\n Err(e) =\u003e {\n error!(\n \"Failed to fetch oldest images and cannot prune disk cache: {}\",\n e\n );\n continue;\n }\n }\n };\n\n let mut size_freed = 0;\n #[allow(clippy::cast_sign_loss)]\n for item in items {\n debug!(\"deleting file due to exceeding cache size\");\n size_freed += item.size as u64;\n tokio::spawn(async move {\n let key = item.id;\n if let Err(e) = remove_file(key.clone()).await {\n match e.kind() {\n std::io::ErrorKind::NotFound =\u003e {\n let hash = Md5Hash(*GenericArray::from_slice(key.as_bytes()));\n let path: PathBuf = hash.into();\n if let Err(e) = remove_file(\u0026path).await {\n warn!(\n \"Failed to delete file `{}` from cache: {}\",\n path.to_string_lossy(),\n e\n );\n }\n }\n _ =\u003e {\n warn!(\"Failed to delete file `{}` from cache: {}\", \u0026key, e);\n }\n }\n }\n });\n }\n\n cache.disk_cur_size.fetch_sub(size_freed, Ordering::Release);\n }\n }\n}\n\nasync fn handle_db_get(entry: \u0026Path, transaction: \u0026mut Transaction\u003c'_, Sqlite\u003e) {\n let hash = if let Ok(hash) = Md5Hash::try_from(entry) {\n hash\n } else {\n error!(\n \"Failed to derive hash from entry, dropping message: {}\",\n entry.to_string_lossy()\n );\n return;\n };\n\n let hash_str = hash.to_hex_string();\n let key = entry.as_os_str().to_str();\n let now = chrono::Utc::now();\n let query = sqlx::query!(\n \"update Images set accessed = ? where id = ? or id = ?\",\n now,\n key,\n hash_str\n )\n .execute(transaction)\n .await;\n if let Err(e) = query {\n warn!(\"Failed to update timestamp in db for {:?}: {}\", key, e);\n }\n}\n\nasync fn handle_db_put(\n entry: \u0026Path,\n size: u64,\n cache: \u0026DiskCache,\n transaction: \u0026mut Transaction\u003c'_, Sqlite\u003e,\n) {\n let key = entry.as_os_str().to_str();\n let now = chrono::Utc::now();\n\n // This is intentional.\n #[allow(clippy::cast_possible_wrap)]\n let casted_size = size as i64;\n let query = sqlx::query!(\n \"insert into Images (id, size, accessed) values (?, ?, ?) on conflict do nothing\",\n key,\n casted_size,\n now,\n )\n .execute(transaction)\n .await;\n\n if let Err(e) = query {\n warn!(\"Failed to add {:?} to db: {}\", key, e);\n }\n\n cache.disk_cur_size.fetch_add(size, Ordering::Release);\n}\n\n/// Represents a Md5 hash that can be converted to and from a path. This is used\n/// for compatibility with the official client, where the image id and on-disk\n/// path is determined by file path.\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\nstruct Md5Hash(GenericArray\u003cu8, \u003cMd5 as md5::Digest\u003e::OutputSize\u003e);\n\nimpl Md5Hash {\n fn to_hex_string(self) -\u003e String {\n format!(\"{:x}\", self.0)\n }\n}\n\nimpl TryFrom\u003c\u0026Path\u003e for Md5Hash {\n type Error = ();\n\n fn try_from(path: \u0026Path) -\u003e Result\u003cSelf, Self::Error\u003e {\n let mut iter = path.iter();\n let file_name = iter.next_back().ok_or(())?;\n let chapter_hash = iter.next_back().ok_or(())?;\n let is_data_saver = iter.next_back().ok_or(())? == \"saver\";\n let mut hasher = Md5::new();\n if is_data_saver {\n hasher.update(\"saver\");\n }\n hasher.update(chapter_hash.as_bytes());\n hasher.update(\".\");\n hasher.update(file_name.as_bytes());\n Ok(Self(hasher.finalize()))\n }\n}\n\nimpl From\u003cMd5Hash\u003e for PathBuf {\n fn from(hash: Md5Hash) -\u003e Self {\n let hex_value = dbg!(hash.to_hex_string());\n let path = hex_value[0..3]\n .chars()\n .rev()\n .map(|char| Self::from(char.to_string()))\n .reduce(|first, second| first.join(second));\n\n match path {\n Some(p) =\u003e p.join(hex_value),\n None =\u003e unsafe { unreachable_unchecked() }, // literally not possible\n }\n }\n}\n\n#[async_trait]\nimpl Cache for DiskCache {\n async fn get(\n \u0026self,\n key: \u0026CacheKey,\n ) -\u003e Option\u003cResult\u003c(CacheStream, ImageMetadata), CacheError\u003e\u003e {\n let channel = self.db_update_channel_sender.clone();\n\n let path = Arc::new(self.disk_path.clone().join(PathBuf::from(key)));\n let path_0 = Arc::clone(\u0026path);\n\n tokio::spawn(async move { channel.send(DbMessage::Get(path_0)).await });\n\n super::fs::read_file_from_path(\u0026path).await.map(|res| {\n let (inner, maybe_header, metadata) = res?;\n CacheStream::new(inner, maybe_header)\n .map(|stream| (stream, metadata))\n .map_err(|_| CacheError::DecryptionFailure)\n })\n }\n\n async fn put(\n \u0026self,\n key: CacheKey,\n image: bytes::Bytes,\n metadata: ImageMetadata,\n ) -\u003e Result\u003c(), CacheError\u003e {\n let channel = self.db_update_channel_sender.clone();\n\n let path = Arc::new(self.disk_path.clone().join(PathBuf::from(\u0026key)));\n let path_0 = Arc::clone(\u0026path);\n\n let db_callback = |size: u64| async move {\n std::mem::drop(channel.send(DbMessage::Put(path_0, size)).await);\n };\n\n super::fs::write_file(\u0026path, key, image, metadata, db_callback, None)\n .await\n .map_err(CacheError::from)\n }\n}\n\n#[async_trait]\nimpl CallbackCache for DiskCache {\n async fn put_with_on_completed_callback(\n \u0026self,\n key: CacheKey,\n image: bytes::Bytes,\n metadata: ImageMetadata,\n on_complete: Sender\u003c(CacheKey, bytes::Bytes, ImageMetadata, u64)\u003e,\n ) -\u003e Result\u003c(), CacheError\u003e {\n let channel = self.db_update_channel_sender.clone();\n\n let path = Arc::new(self.disk_path.clone().join(PathBuf::from(\u0026key)));\n let path_0 = Arc::clone(\u0026path);\n\n let db_callback = |size: u64| async move {\n // We don't care about the result of the send\n std::mem::drop(channel.send(DbMessage::Put(path_0, size)).await);\n };\n\n super::fs::write_file(\u0026path, key, image, metadata, db_callback, Some(on_complete))\n .await\n .map_err(CacheError::from)\n }\n}\n\n#[cfg(test)]\nmod db {\n use chrono::{DateTime, Utc};\n use sqlx::{Connection, Row, SqliteConnection};\n use std::error::Error;\n\n use super::*;\n\n #[tokio::test]\n async fn get() -\u003e Result\u003c(), Box\u003cdyn Error\u003e\u003e {\n let (cache, _) = DiskCache::in_memory();\n let path = PathBuf::from_str(\"a/b/c\")?;\n let mut conn = SqliteConnection::connect(\"sqlite::memory:\").await?;\n sqlx::query_file!(\"./db_queries/init.sql\")\n .execute(\u0026mut conn)\n .await?;\n\n // Add an entry\n let mut transaction = conn.begin().await?;\n handle_db_put(\u0026path, 10, \u0026cache, \u0026mut transaction).await;\n transaction.commit().await?;\n\n let time_fence = Utc::now();\n\n let mut transaction = conn.begin().await?;\n handle_db_get(\u0026path, \u0026mut transaction).await;\n transaction.commit().await?;\n\n let mut rows: Vec\u003c_\u003e = sqlx::query(\"select * from Images\")\n .fetch(\u0026mut conn)\n .collect()\n .await;\n assert_eq!(rows.len(), 1);\n let entry = rows.pop().unwrap()?;\n assert!(time_fence \u003c entry.get::\u003c'_, DateTime\u003cUtc\u003e, _\u003e(\"accessed\"));\n\n Ok(())\n }\n\n #[tokio::test]\n async fn put() -\u003e Result\u003c(), Box\u003cdyn Error\u003e\u003e {\n let (cache, _) = DiskCache::in_memory();\n let path = PathBuf::from_str(\"a/b/c\")?;\n let mut conn = SqliteConnection::connect(\"sqlite::memory:\").await?;\n sqlx::query_file!(\"./db_queries/init.sql\")\n .execute(\u0026mut conn)\n .await?;\n\n let mut transaction = conn.begin().await?;\n let transaction_time = Utc::now();\n handle_db_put(\u0026path, 10, \u0026cache, \u0026mut transaction).await;\n transaction.commit().await?;\n\n let mut rows: Vec\u003c_\u003e = sqlx::query(\"select * from Images\")\n .fetch(\u0026mut conn)\n .collect()\n .await;\n assert_eq!(rows.len(), 1);\n\n let entry = rows.pop().unwrap()?;\n assert_eq!(entry.get::\u003c'_, \u0026str, _\u003e(\"id\"), \"a/b/c\");\n assert_eq!(entry.get::\u003c'_, i64, _\u003e(\"size\"), 10);\n\n let accessed: DateTime\u003cUtc\u003e = entry.get(\"accessed\");\n assert!(transaction_time \u003c accessed);\n assert!(accessed \u003c Utc::now());\n\n assert_eq!(cache.disk_cur_size.load(Ordering::SeqCst), 10);\n\n Ok(())\n }\n}\n\n#[cfg(test)]\nmod md5_hash {\n use super::*;\n\n #[test]\n fn to_cache_path() {\n let hash = Md5Hash(\n *GenericArray::\u003c_, \u003cMd5 as md5::Digest\u003e::OutputSize\u003e::from_slice(\u0026[\n 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd,\n 0xef, 0xab,\n ]),\n );\n assert_eq!(\n PathBuf::from(hash).to_str(),\n Some(\"c/b/a/abcdefabcdefabcdefabcdefabcdefab\")\n )\n }\n\n #[test]\n fn from_data_path() {\n let mut expected_hasher = Md5::new();\n expected_hasher.update(\"foo.bar.png\");\n assert_eq!(\n Md5Hash::try_from(Path::new(\"data/foo/bar.png\")),\n Ok(Md5Hash(expected_hasher.finalize()))\n );\n }\n\n #[test]\n fn from_data_saver_path() {\n let mut expected_hasher = Md5::new();\n expected_hasher.update(\"saverfoo.bar.png\");\n assert_eq!(\n Md5Hash::try_from(Path::new(\"saver/foo/bar.png\")),\n Ok(Md5Hash(expected_hasher.finalize()))\n );\n }\n\n #[test]\n fn can_handle_long_paths() {\n assert_eq!(\n Md5Hash::try_from(Path::new(\"a/b/c/d/e/f/g/saver/foo/bar.png\")),\n Md5Hash::try_from(Path::new(\"saver/foo/bar.png\")),\n );\n }\n\n #[test]\n fn from_invalid_paths() {\n assert!(Md5Hash::try_from(Path::new(\"foo/bar.png\")).is_err());\n assert!(Md5Hash::try_from(Path::new(\"bar.png\")).is_err());\n assert!(Md5Hash::try_from(Path::new(\"\")).is_err());\n }\n}\n","traces":[{"line":41,"address":[5949327,5949856,5949909,5949296,5954232,5954434,5954576,5953667],"length":1,"stats":{"Line":0},"fn_name":"new"},{"line":42,"address":[5950066],"length":1,"stats":{"Line":0},"fn_name":null},{"line":43,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":44,"address":[5950141,5954118],"length":1,"stats":{"Line":0},"fn_name":null},{"line":45,"address":[5950435],"length":1,"stats":{"Line":0},"fn_name":null},{"line":48,"address":[5950580],"length":1,"stats":{"Line":0},"fn_name":null},{"line":49,"address":[5954074,5954479,5950834,5950623],"length":1,"stats":{"Line":0},"fn_name":null},{"line":52,"address":[5951187,5953942,5951663,5951764,5954521],"length":1,"stats":{"Line":0},"fn_name":null},{"line":53,"address":[5954500,5953985,5951276,5951377],"length":1,"stats":{"Line":0},"fn_name":null},{"line":54,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":57,"address":[5952097],"length":1,"stats":{"Line":0},"fn_name":null},{"line":62,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":63,"address":[5953896,5952243,5954542,5952145],"length":1,"stats":{"Line":0},"fn_name":null},{"line":64,"address":[5952519,5952647,5953807,5954563,5952730],"length":1,"stats":{"Line":0},"fn_name":null},{"line":65,"address":[5952624],"length":1,"stats":{"Line":0},"fn_name":null},{"line":66,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":67,"address":[5949840,5949824],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":68,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":69,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":72,"address":[5953218],"length":1,"stats":{"Line":0},"fn_name":null},{"line":73,"address":[5953124],"length":1,"stats":{"Line":0},"fn_name":null},{"line":74,"address":[5953155],"length":1,"stats":{"Line":0},"fn_name":null},{"line":75,"address":[5953207],"length":1,"stats":{"Line":0},"fn_name":null},{"line":96,"address":[5953568,5953481],"length":1,"stats":{"Line":0},"fn_name":null},{"line":97,"address":[5953318],"length":1,"stats":{"Line":0},"fn_name":null},{"line":98,"address":[5953362],"length":1,"stats":{"Line":0},"fn_name":null},{"line":99,"address":[5953389],"length":1,"stats":{"Line":0},"fn_name":null},{"line":100,"address":[5953416,5955204],"length":1,"stats":{"Line":0},"fn_name":null},{"line":103,"address":[5953638],"length":1,"stats":{"Line":0},"fn_name":null},{"line":107,"address":[5955264,5955525],"length":1,"stats":{"Line":1},"fn_name":"in_memory"},{"line":108,"address":[5955281],"length":1,"stats":{"Line":1},"fn_name":null},{"line":110,"address":[5955391],"length":1,"stats":{"Line":1},"fn_name":null},{"line":111,"address":[5955326],"length":1,"stats":{"Line":1},"fn_name":null},{"line":112,"address":[5955337],"length":1,"stats":{"Line":1},"fn_name":null},{"line":113,"address":[5955386],"length":1,"stats":{"Line":1},"fn_name":null},{"line":115,"address":[5955442],"length":1,"stats":{"Line":1},"fn_name":null},{"line":122,"address":[5955584],"length":1,"stats":{"Line":0},"fn_name":"db_listener"},{"line":128,"address":[5960243,5968080],"length":1,"stats":{"Line":0},"fn_name":null},{"line":129,"address":[5960653,5960304,5966937,5967923,5960395],"length":1,"stats":{"Line":0},"fn_name":null},{"line":130,"address":[5967944,5960682,5961120,5967473,5960780],"length":1,"stats":{"Line":0},"fn_name":null},{"line":131,"address":[5961011,5961122],"length":1,"stats":{"Line":0},"fn_name":null},{"line":132,"address":[5961026],"length":1,"stats":{"Line":0},"fn_name":null},{"line":133,"address":[5961074,5961377,5961292],"length":1,"stats":{"Line":0},"fn_name":null},{"line":138,"address":[5962882,5961682,5961229,5961828],"length":1,"stats":{"Line":0},"fn_name":null},{"line":139,"address":[5962068],"length":1,"stats":{"Line":0},"fn_name":null},{"line":140,"address":[5962279,5967965,5967380,5962078,5961978],"length":1,"stats":{"Line":0},"fn_name":null},{"line":141,"address":[5962000],"length":1,"stats":{"Line":0},"fn_name":null},{"line":142,"address":[5967986,5962693,5962464,5962047,5967338],"length":1,"stats":{"Line":0},"fn_name":null},{"line":147,"address":[5968007,5967304,5962895,5963042],"length":1,"stats":{"Line":0},"fn_name":null},{"line":148,"address":[5963391,5963519],"length":1,"stats":{"Line":0},"fn_name":null},{"line":154,"address":[5967600,5968620,5963824],"length":1,"stats":{"Line":0},"fn_name":null},{"line":155,"address":[5963976,5964009],"length":1,"stats":{"Line":0},"fn_name":null},{"line":156,"address":[5967263,5968028,5963990,5964022,5964093,5964433],"length":1,"stats":{"Line":0},"fn_name":null},{"line":157,"address":[5964324,5964435],"length":1,"stats":{"Line":0},"fn_name":null},{"line":158,"address":[5964339],"length":1,"stats":{"Line":0},"fn_name":null},{"line":159,"address":[5964633,5964387,5964548],"length":1,"stats":{"Line":0},"fn_name":null},{"line":168,"address":[5968049,5964538,5965051,5967198,5965137,5964930],"length":1,"stats":{"Line":0},"fn_name":null},{"line":170,"address":[5965028],"length":1,"stats":{"Line":0},"fn_name":null},{"line":172,"address":[5965517],"length":1,"stats":{"Line":0},"fn_name":null},{"line":173,"address":[5965408,5965519],"length":1,"stats":{"Line":0},"fn_name":null},{"line":174,"address":[5965423],"length":1,"stats":{"Line":0},"fn_name":null},{"line":175,"address":[5965653,5965738,5965471],"length":1,"stats":{"Line":0},"fn_name":null},{"line":184,"address":[5965583],"length":1,"stats":{"Line":0},"fn_name":null},{"line":186,"address":[5966178,5966035,5966817,5965595],"length":1,"stats":{"Line":0},"fn_name":null},{"line":187,"address":[5966338,5966457],"length":1,"stats":{"Line":0},"fn_name":null},{"line":188,"address":[5968932,5966616],"length":1,"stats":{"Line":0},"fn_name":null},{"line":189,"address":[5959330,5959588,5966661,5956631,5956592],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":190,"address":[5956691],"length":1,"stats":{"Line":0},"fn_name":null},{"line":191,"address":[5956841,5959554,5959271,5956725],"length":1,"stats":{"Line":0},"fn_name":null},{"line":192,"address":[5958951,5959357,5957138],"length":1,"stats":{"Line":0},"fn_name":null},{"line":193,"address":[5957168],"length":1,"stats":{"Line":0},"fn_name":null},{"line":194,"address":[5957236],"length":1,"stats":{"Line":0},"fn_name":null},{"line":195,"address":[5957346],"length":1,"stats":{"Line":0},"fn_name":null},{"line":196,"address":[5957397,5959132,5959575,5957490],"length":1,"stats":{"Line":0},"fn_name":null},{"line":197,"address":[5958048,5957788,5958092,5957916,5959042],"length":1,"stats":{"Line":0},"fn_name":null},{"line":199,"address":[5958077,5958015],"length":1,"stats":{"Line":0},"fn_name":null},{"line":205,"address":[5957182,5958479,5958555],"length":1,"stats":{"Line":0},"fn_name":null},{"line":212,"address":[5966830],"length":1,"stats":{"Line":0},"fn_name":null},{"line":217,"address":[5969072,5969111,5971739,5969014,5969816,5968976,5971696],"length":1,"stats":{"Line":4},"fn_name":"handle_db_get"},{"line":218,"address":[5969249,5969328],"length":1,"stats":{"Line":2},"fn_name":null},{"line":219,"address":[5969344],"length":1,"stats":{"Line":1},"fn_name":null},{"line":221,"address":[5969406,5969274,5969491,5969613,5971596],"length":1,"stats":{"Line":0},"fn_name":null},{"line":223,"address":[5969606],"length":1,"stats":{"Line":0},"fn_name":null},{"line":228,"address":[5969360],"length":1,"stats":{"Line":1},"fn_name":null},{"line":229,"address":[5969837],"length":1,"stats":{"Line":1},"fn_name":null},{"line":230,"address":[5969970],"length":1,"stats":{"Line":1},"fn_name":null},{"line":231,"address":[5970085,5971942,5971726,5970616,5971543],"length":1,"stats":{"Line":3},"fn_name":null},{"line":239,"address":[5970886],"length":1,"stats":{"Line":1},"fn_name":null},{"line":240,"address":[5971074,5970958],"length":1,"stats":{"Line":0},"fn_name":null},{"line":244,"address":[5972112],"length":1,"stats":{"Line":1},"fn_name":"handle_db_put"},{"line":250,"address":[5972393],"length":1,"stats":{"Line":1},"fn_name":null},{"line":251,"address":[5972526],"length":1,"stats":{"Line":1},"fn_name":null},{"line":255,"address":[5972641],"length":1,"stats":{"Line":1},"fn_name":null},{"line":256,"address":[5974120,5974319,5974485,5973180,5972664],"length":1,"stats":{"Line":3},"fn_name":null},{"line":265,"address":[5973450],"length":1,"stats":{"Line":1},"fn_name":null},{"line":266,"address":[5973522,5973638],"length":1,"stats":{"Line":0},"fn_name":null},{"line":269,"address":[5974045],"length":1,"stats":{"Line":1},"fn_name":null},{"line":279,"address":[5974656],"length":1,"stats":{"Line":1},"fn_name":"to_hex_string"},{"line":280,"address":[5974703],"length":1,"stats":{"Line":1},"fn_name":null},{"line":287,"address":[5974848],"length":1,"stats":{"Line":1},"fn_name":"try_from"},{"line":288,"address":[5974887],"length":1,"stats":{"Line":1},"fn_name":null},{"line":289,"address":[5974900,5975115],"length":1,"stats":{"Line":1},"fn_name":null},{"line":290,"address":[5975157,5975084,5975309],"length":1,"stats":{"Line":4},"fn_name":null},{"line":291,"address":[5975345,5975284],"length":1,"stats":{"Line":4},"fn_name":null},{"line":292,"address":[5975524],"length":1,"stats":{"Line":1},"fn_name":null},{"line":293,"address":[5975574,5975541],"length":1,"stats":{"Line":3},"fn_name":null},{"line":294,"address":[5975547],"length":1,"stats":{"Line":1},"fn_name":null},{"line":296,"address":[5975588],"length":1,"stats":{"Line":1},"fn_name":null},{"line":297,"address":[5975626],"length":1,"stats":{"Line":1},"fn_name":null},{"line":298,"address":[5975661],"length":1,"stats":{"Line":2},"fn_name":null},{"line":299,"address":[5975699],"length":1,"stats":{"Line":1},"fn_name":null},{"line":304,"address":[5141232,5142487],"length":1,"stats":{"Line":1},"fn_name":"from"},{"line":305,"address":[5142473,5141287],"length":1,"stats":{"Line":1},"fn_name":null},{"line":306,"address":[5142032],"length":1,"stats":{"Line":1},"fn_name":null},{"line":309,"address":[5975952,5975974],"length":1,"stats":{"Line":2},"fn_name":"{{closure}}"},{"line":310,"address":[5976052,5976016],"length":1,"stats":{"Line":2},"fn_name":"{{closure}}"},{"line":312,"address":[5142271],"length":1,"stats":{"Line":0},"fn_name":null},{"line":313,"address":[5142238,5142273,5142457],"length":1,"stats":{"Line":2},"fn_name":null},{"line":321,"address":[5976912],"length":1,"stats":{"Line":0},"fn_name":"get"},{"line":325,"address":[5979082],"length":1,"stats":{"Line":0},"fn_name":null},{"line":327,"address":[5980083,5979144],"length":1,"stats":{"Line":0},"fn_name":null},{"line":328,"address":[5979349],"length":1,"stats":{"Line":0},"fn_name":null},{"line":330,"address":[5977099,5977256,5977056,5979381],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":332,"address":[5979653,5978737,5979510,5978000,5980001,5980170],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":333,"address":[5978016,5978571],"length":1,"stats":{"Line":0},"fn_name":null},{"line":334,"address":[5978454,5978666],"length":1,"stats":{"Line":0},"fn_name":null},{"line":335,"address":[5977803,5977760,5978658],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":336,"address":[5977968,5977975],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":340,"address":[5980496],"length":1,"stats":{"Line":0},"fn_name":"put"},{"line":346,"address":[5981997],"length":1,"stats":{"Line":0},"fn_name":null},{"line":348,"address":[5982051,5983256],"length":1,"stats":{"Line":0},"fn_name":null},{"line":349,"address":[5982268],"length":1,"stats":{"Line":0},"fn_name":null},{"line":351,"address":[5982311,5981237,5980768,5981479,5980801,5981277,5981440],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":352,"address":[5981177,5981264,5980852,5980970],"length":1,"stats":{"Line":0},"fn_name":null},{"line":355,"address":[5982343,5983559,5983148,5982716],"length":1,"stats":{"Line":0},"fn_name":null},{"line":356,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":357,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":363,"address":[5983904],"length":1,"stats":{"Line":0},"fn_name":"put_with_on_completed_callback"},{"line":370,"address":[5985468],"length":1,"stats":{"Line":0},"fn_name":null},{"line":372,"address":[5985522,5986745],"length":1,"stats":{"Line":0},"fn_name":null},{"line":373,"address":[5985739],"length":1,"stats":{"Line":0},"fn_name":null},{"line":375,"address":[5984225,5984864,5984192,5984701,5985782,5984661,5984903],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":377,"address":[5984601,5984276,5984688,5984394],"length":1,"stats":{"Line":0},"fn_name":null},{"line":380,"address":[5986637,5985814,5987167,5986198],"length":1,"stats":{"Line":0},"fn_name":null},{"line":381,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":382,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":395,"address":[4126896,4127309,4126848,4127176,4127248,4127018,4134824,4134613,4126857],"length":1,"stats":{"Line":6},"fn_name":"{{closure}}"},{"line":396,"address":[4127372],"length":1,"stats":{"Line":1},"fn_name":null},{"line":397,"address":[4134479,4127478],"length":1,"stats":{"Line":2},"fn_name":null},{"line":398,"address":[4127678,4128077,4134423,4134643,4127549],"length":1,"stats":{"Line":3},"fn_name":null},{"line":399,"address":[4128373,4134390,4128067,4128294,4134664,4128816,4128479,4129039,4128888],"length":1,"stats":{"Line":6},"fn_name":null},{"line":400,"address":[4128369],"length":1,"stats":{"Line":1},"fn_name":null},{"line":401,"address":[4134380,4128791,4128890],"length":1,"stats":{"Line":1},"fn_name":null},{"line":404,"address":[4129189,4128848,4129627,4134340,4134685,4129129],"length":1,"stats":{"Line":4},"fn_name":null},{"line":405,"address":[4129591,4134323,4129977,4134706,4129868],"length":1,"stats":{"Line":4},"fn_name":null},{"line":406,"address":[4130655,4134284,4134727,4130143,4130293],"length":1,"stats":{"Line":3},"fn_name":null},{"line":408,"address":[4130904,4130605],"length":1,"stats":{"Line":2},"fn_name":null},{"line":410,"address":[4134241,4130940,4134748,4131499,4131061],"length":1,"stats":{"Line":3},"fn_name":null},{"line":411,"address":[4131463,4131841,4134224,4134769,4131740],"length":1,"stats":{"Line":4},"fn_name":null},{"line":412,"address":[4134790,4132007,4132157,4134185,4132495],"length":1,"stats":{"Line":3},"fn_name":null},{"line":415,"address":[4132720],"length":1,"stats":{"Line":1},"fn_name":null},{"line":418,"address":[4133067],"length":1,"stats":{"Line":1},"fn_name":null},{"line":419,"address":[4134140,4133269,4133510],"length":1,"stats":{"Line":1},"fn_name":null},{"line":420,"address":[4133453,4133731],"length":1,"stats":{"Line":2},"fn_name":null},{"line":422,"address":[4133842,4127062,4126909],"length":1,"stats":{"Line":3},"fn_name":null},{"line":426,"address":[4135616,4135568,4135738,4136029,4142268,4135968,4135896,4135577,4142416],"length":1,"stats":{"Line":6},"fn_name":"{{closure}}"},{"line":427,"address":[4136092],"length":1,"stats":{"Line":1},"fn_name":null},{"line":428,"address":[4136191,4142204],"length":1,"stats":{"Line":2},"fn_name":null},{"line":429,"address":[4142148,4136262,4136391,4136790,4142298],"length":1,"stats":{"Line":3},"fn_name":null},{"line":430,"address":[4137529,4137086,4142319,4137601,4136780,4137192,4137752,4142115,4137007],"length":1,"stats":{"Line":6},"fn_name":null},{"line":431,"address":[4137082],"length":1,"stats":{"Line":1},"fn_name":null},{"line":432,"address":[4137603,4137504,4142105],"length":1,"stats":{"Line":1},"fn_name":null},{"line":434,"address":[4142340,4137561,4138354,4142065,4137842,4137902],"length":1,"stats":{"Line":4},"fn_name":null},{"line":435,"address":[4138603,4138304],"length":1,"stats":{"Line":2},"fn_name":null},{"line":436,"address":[4138805,4142030,4138639,4142361],"length":1,"stats":{"Line":3},"fn_name":null},{"line":437,"address":[4139121,4141997,4139459,4138971,4142382],"length":1,"stats":{"Line":3},"fn_name":null},{"line":440,"address":[4139684],"length":1,"stats":{"Line":1},"fn_name":null},{"line":443,"address":[4140055],"length":1,"stats":{"Line":1},"fn_name":null},{"line":445,"address":[4141958,4140503,4140275],"length":1,"stats":{"Line":1},"fn_name":null},{"line":446,"address":[4140743,4140451],"length":1,"stats":{"Line":2},"fn_name":null},{"line":447,"address":[4140929],"length":1,"stats":{"Line":1},"fn_name":null},{"line":449,"address":[4141135],"length":1,"stats":{"Line":1},"fn_name":null},{"line":450,"address":[4141241],"length":1,"stats":{"Line":1},"fn_name":null},{"line":451,"address":[4141314],"length":1,"stats":{"Line":1},"fn_name":null},{"line":453,"address":[4141478],"length":1,"stats":{"Line":1},"fn_name":null},{"line":455,"address":[4135629,4135782,4141682],"length":1,"stats":{"Line":3},"fn_name":null},{"line":464,"address":[5158181,5158176,5158564,5158208],"length":1,"stats":{"Line":3},"fn_name":"{{closure}}"},{"line":466,"address":[5158215],"length":1,"stats":{"Line":1},"fn_name":null},{"line":471,"address":[5158394],"length":1,"stats":{"Line":1},"fn_name":null},{"line":472,"address":[5158260],"length":1,"stats":{"Line":1},"fn_name":null},{"line":478,"address":[5158613,5158608,5158640],"length":1,"stats":{"Line":3},"fn_name":"{{closure}}"},{"line":479,"address":[5158647],"length":1,"stats":{"Line":1},"fn_name":null},{"line":480,"address":[5158657],"length":1,"stats":{"Line":1},"fn_name":null},{"line":481,"address":[5158944],"length":1,"stats":{"Line":1},"fn_name":null},{"line":482,"address":[5158679],"length":1,"stats":{"Line":1},"fn_name":null},{"line":483,"address":[5158730],"length":1,"stats":{"Line":1},"fn_name":null},{"line":488,"address":[5159104,5159136,5159109],"length":1,"stats":{"Line":3},"fn_name":"{{closure}}"},{"line":489,"address":[5159143],"length":1,"stats":{"Line":1},"fn_name":null},{"line":490,"address":[5159153],"length":1,"stats":{"Line":1},"fn_name":null},{"line":491,"address":[5159440],"length":1,"stats":{"Line":1},"fn_name":null},{"line":492,"address":[5159175],"length":1,"stats":{"Line":1},"fn_name":null},{"line":493,"address":[5159226],"length":1,"stats":{"Line":1},"fn_name":null},{"line":498,"address":[5159632,5159605,5159600],"length":1,"stats":{"Line":3},"fn_name":"{{closure}}"},{"line":499,"address":[5159735],"length":1,"stats":{"Line":1},"fn_name":null},{"line":500,"address":[5159639],"length":1,"stats":{"Line":1},"fn_name":null},{"line":501,"address":[5159687],"length":1,"stats":{"Line":1},"fn_name":null},{"line":506,"address":[5159872,5159904,5159877],"length":1,"stats":{"Line":3},"fn_name":"{{closure}}"},{"line":507,"address":[5159911],"length":1,"stats":{"Line":1},"fn_name":null},{"line":508,"address":[5160015],"length":1,"stats":{"Line":1},"fn_name":null},{"line":509,"address":[5160119],"length":1,"stats":{"Line":1},"fn_name":null}],"covered":106,"coverable":210},{"path":["/","home","edward","Documents","repos","mangadex-home","src","cache","fs.rs"],"content":"//! This module contains two functions whose sole purpose is to allow a single\n//! producer multiple consumer (SPMC) system using the filesystem as an\n//! intermediate.\n//!\n//! Consider the scenario where two clients, A and B, request the same uncached\n//! file, one after the other. In a typical caching system, both requests would\n//! result in a cache miss, and both requests would then be proxied from\n//! upstream. But, we can do better. We know that by the time one request\n//! begins, there should be a file on disk for us to read from. Why require\n//! subsequent requests to read from upstream, when we can simply fetch one and\n//! read from the filesystem that we know will have the exact same data?\n//! Instead, we can just read from the filesystem and just inform all readers\n//! when the file is done. This is beneficial to both downstream and upstream as\n//! upstream no longer needs to process duplicate requests and sequential cache\n//! misses are treated as closer as a cache hit.\n\nuse std::error::Error;\nuse std::fmt::Display;\nuse std::io::{Seek, SeekFrom};\nuse std::path::Path;\nuse std::pin::Pin;\nuse std::task::{Context, Poll};\n\nuse actix_web::error::PayloadError;\nuse bytes::Bytes;\nuse futures::Future;\nuse log::{debug, warn};\nuse serde::{Deserialize, Serialize};\nuse sodiumoxide::crypto::secretstream::{\n Header, Pull, Push, Stream as SecretStream, Tag, HEADERBYTES,\n};\nuse tokio::fs::{create_dir_all, remove_file, File};\nuse tokio::io::{AsyncRead, AsyncReadExt, AsyncSeekExt, AsyncWrite, AsyncWriteExt, ReadBuf};\nuse tokio::sync::mpsc::Sender;\nuse tokio_util::codec::{BytesCodec, FramedRead};\n\nuse super::compat::LegacyImageMetadata;\nuse super::{CacheKey, ImageMetadata, InnerStream, ENCRYPTION_KEY};\n\n#[derive(Serialize, Deserialize)]\npub enum OnDiskMetadata {\n Encrypted(Header, ImageMetadata),\n Plaintext(ImageMetadata),\n}\n\n/// Attempts to lookup the file on disk, returning a byte stream if it exists.\n/// Note that this could return two types of streams, depending on if the file\n/// is in progress of being written to.\n#[inline]\npub(super) async fn read_file_from_path(\n path: \u0026Path,\n) -\u003e Option\u003cResult\u003c(InnerStream, Option\u003cHeader\u003e, ImageMetadata), std::io::Error\u003e\u003e {\n read_file(std::fs::File::open(path).ok()?).await\n}\n\nasync fn read_file(\n file: std::fs::File,\n) -\u003e Option\u003cResult\u003c(InnerStream, Option\u003cHeader\u003e, ImageMetadata), std::io::Error\u003e\u003e {\n let mut file_0 = file.try_clone().unwrap();\n let file_1 = file.try_clone().unwrap();\n\n // Try reading decrypted header first...\n let mut deserializer = serde_json::Deserializer::from_reader(file);\n let mut maybe_metadata = ImageMetadata::deserialize(\u0026mut deserializer);\n\n // Failed to parse normally, see if we have a legacy file format\n if maybe_metadata.is_err() {\n file_0.seek(SeekFrom::Start(2)).ok()?;\n let mut deserializer = serde_json::Deserializer::from_reader(file_0);\n maybe_metadata =\n LegacyImageMetadata::deserialize(\u0026mut deserializer).map(LegacyImageMetadata::into);\n }\n\n let parsed_metadata;\n let mut maybe_header = None;\n let mut reader: Option\u003cPin\u003cBox\u003cdyn AsyncRead + Send\u003e\u003e\u003e = None;\n if let Ok(metadata) = maybe_metadata {\n // image is decrypted\n if ENCRYPTION_KEY.get().is_some() {\n // invalidate cache since we're running in at-rest encryption and\n // the file wasn't encrypted.\n warn!(\"Found file but was not encrypted!\");\n return None;\n }\n\n reader = Some(Box::pin(File::from_std(file_1)));\n parsed_metadata = Some(metadata);\n debug!(\"Found not encrypted file\");\n } else {\n let mut file = File::from_std(file_1);\n file.seek(SeekFrom::Start(0)).await.ok()?;\n let file_0 = file.try_clone().await.unwrap();\n\n // image is encrypted or corrupt\n\n // If the encryption key was set, use the encrypted disk reader instead;\n // else, just directly read from file.\n if let Some(key) = ENCRYPTION_KEY.get() {\n let mut header_bytes = [0; HEADERBYTES];\n if let Err(e) = file.read_exact(\u0026mut header_bytes).await {\n warn!(\"Found file but failed reading header: {}\", e);\n return None;\n }\n\n let file_header = if let Some(header) = Header::from_slice(\u0026header_bytes) {\n header\n } else {\n warn!(\"Found file, but encrypted header was invalid. Assuming corrupted!\");\n return None;\n };\n\n let secret_stream = if let Ok(stream) = SecretStream::init_pull(\u0026file_header, key) {\n stream\n } else {\n warn!(\"Failed to init secret stream with key and header. Assuming corrupted!\");\n return None;\n };\n\n maybe_header = Some(file_header);\n\n reader = Some(Box::pin(EncryptedDiskReader::new(file, secret_stream)));\n }\n\n let mut deserializer = serde_json::Deserializer::from_reader(file_0.into_std().await);\n parsed_metadata = ImageMetadata::deserialize(\u0026mut deserializer).ok();\n\n if parsed_metadata.is_some() {\n debug!(\"Found encrypted file\");\n }\n }\n\n // parsed_metadata is either set or unset here. If it's set then we\n // successfully decoded the data; otherwise the file is garbage.\n\n if let Some(reader) = reader {\n let stream = InnerStream::Completed(FramedRead::new(reader, BytesCodec::new()));\n parsed_metadata.map(|metadata| Ok((stream, maybe_header, metadata)))\n } else {\n debug!(\"Reader was invalid, file is corrupt\");\n None\n }\n}\n\nstruct EncryptedDiskReader {\n file: Pin\u003cBox\u003cFile\u003e\u003e,\n stream: SecretStream\u003cPull\u003e,\n buf: Vec\u003cu8\u003e,\n}\n\nimpl EncryptedDiskReader {\n fn new(file: File, stream: SecretStream\u003cPull\u003e) -\u003e Self {\n Self {\n file: Box::pin(file),\n stream,\n buf: vec![],\n }\n }\n}\n\nimpl AsyncRead for EncryptedDiskReader {\n fn poll_read(\n mut self: Pin\u003c\u0026mut Self\u003e,\n cx: \u0026mut Context\u003c'_\u003e,\n buf: \u0026mut ReadBuf\u003c'_\u003e,\n ) -\u003e Poll\u003cstd::io::Result\u003c()\u003e\u003e {\n let cursor_start = buf.filled().len();\n\n let res = self.as_mut().file.as_mut().poll_read(cx, buf);\n if res.is_pending() {\n return Poll::Pending;\n }\n\n let cursor_new = buf.filled().len();\n\n // pull_to_vec internally calls vec.clear() and vec.reserve(). Generally\n // speaking we should be reading about the same amount of data each time\n // so we shouldn't experience too much of a slow down w.r.t resizing the\n // buffer...\n let new_self = Pin::into_inner(self);\n new_self\n .stream\n .pull_to_vec(\n \u0026buf.filled()[cursor_start..cursor_new],\n None,\n \u0026mut new_self.buf,\n )\n .unwrap();\n\n // data is strictly smaller than the encrypted stream, since you need to\n // encode tags as well, so this is always safe.\n\n // rewrite encrypted data into decrypted data\n let buffer = buf.filled_mut();\n for (old, new) in buffer[cursor_start..].iter_mut().zip(\u0026new_self.buf) {\n *old = *new;\n }\n buf.set_filled(cursor_start + new_self.buf.len());\n\n res\n }\n}\n\n/// Writes the metadata and input stream (in that order) to a file, returning a\n/// stream that reads from that file. Accepts a db callback function that is\n/// provided the number of bytes written, and an optional on-complete callback\n/// that is called with a completed cache entry.\npub(super) async fn write_file\u003cFut, DbCallback\u003e(\n path: \u0026Path,\n cache_key: CacheKey,\n bytes: Bytes,\n metadata: ImageMetadata,\n db_callback: DbCallback,\n on_complete: Option\u003cSender\u003c(CacheKey, Bytes, ImageMetadata, u64)\u003e\u003e,\n) -\u003e Result\u003c(), std::io::Error\u003e\nwhere\n Fut: 'static + Send + Sync + Future\u003cOutput = ()\u003e,\n DbCallback: 'static + Send + Sync + FnOnce(u64) -\u003e Fut,\n{\n let file = {\n let parent = path.parent().expect(\"The path to have a parent\");\n create_dir_all(parent).await?;\n let file = File::create(path).await?; // we need to make sure the file exists and is truncated.\n file\n };\n\n let metadata_string = serde_json::to_string(\u0026metadata).expect(\"serialization to work\");\n let metadata_size = metadata_string.len();\n\n let (mut writer, maybe_header): (Pin\u003cBox\u003cdyn AsyncWrite + Send\u003e\u003e, _) =\n if let Some((enc, header)) = ENCRYPTION_KEY\n .get()\n .map(|key| SecretStream::init_push(key).expect(\"Failed to init enc stream\"))\n {\n (Box::pin(EncryptedDiskWriter::new(file, enc)), Some(header))\n } else {\n (Box::pin(file), None)\n };\n\n let mut error = if let Some(header) = maybe_header {\n writer.write_all(header.as_ref()).await.err()\n } else {\n None\n };\n\n if error.is_none() {\n error = writer.write_all(metadata_string.as_bytes()).await.err();\n }\n if error.is_none() {\n error = writer.write_all(\u0026bytes).await.err();\n }\n\n if let Some(e) = error {\n // It's ok if the deleting the file fails, since we truncate on\n // create anyways, but it should be best effort.\n //\n // We don't care about the result of the call.\n std::mem::drop(remove_file(path).await);\n return Err(e);\n }\n\n writer.flush().await?;\n debug!(\"writing to file done\");\n\n let bytes_written = (metadata_size + bytes.len()) as u64;\n tokio::spawn(db_callback(bytes_written));\n\n if let Some(sender) = on_complete {\n tokio::spawn(async move {\n sender\n .send((cache_key, bytes, metadata, bytes_written))\n .await\n });\n }\n\n Ok(())\n}\n\nstruct EncryptedDiskWriter {\n file: Pin\u003cBox\u003cFile\u003e\u003e,\n stream: Option\u003cSecretStream\u003cPush\u003e\u003e,\n encryption_buffer: Vec\u003cu8\u003e,\n write_buffer: Vec\u003cu8\u003e,\n}\n\nimpl EncryptedDiskWriter {\n fn new(file: File, stream: SecretStream\u003cPush\u003e) -\u003e Self {\n Self {\n file: Box::pin(file),\n stream: Some(stream),\n encryption_buffer: vec![],\n write_buffer: vec![],\n }\n }\n}\n\nimpl AsyncWrite for EncryptedDiskWriter {\n fn poll_write(\n self: Pin\u003c\u0026mut Self\u003e,\n cx: \u0026mut Context\u003c'_\u003e,\n buf: \u0026[u8],\n ) -\u003e Poll\u003cResult\u003cusize, std::io::Error\u003e\u003e {\n let new_self = Pin::into_inner(self);\n {\n let encryption_buffer = \u0026mut new_self.encryption_buffer;\n if let Some(stream) = new_self.stream.as_mut() {\n stream\n .push_to_vec(buf, None, Tag::Message, encryption_buffer)\n .expect(\"Failed to write encrypted data to buffer\");\n }\n }\n\n new_self.write_buffer.extend(\u0026new_self.encryption_buffer);\n\n match new_self\n .file\n .as_mut()\n .poll_write(cx, \u0026new_self.write_buffer)\n {\n Poll::Ready(Ok(n)) =\u003e {\n new_self.write_buffer.drain(..n);\n // We buffered all the bytes that were provided to use.\n Poll::Ready(Ok(buf.len()))\n }\n poll =\u003e poll,\n }\n }\n\n fn poll_flush(\n mut self: Pin\u003c\u0026mut Self\u003e,\n cx: \u0026mut Context\u003c'_\u003e,\n ) -\u003e Poll\u003cResult\u003c(), std::io::Error\u003e\u003e {\n if self.as_ref().write_buffer.is_empty() {\n self.file.as_mut().poll_flush(cx)\n } else {\n let new_self = Pin::into_inner(self);\n let buffer = new_self.write_buffer.as_ref();\n match new_self.file.as_mut().poll_write(cx, buffer) {\n Poll::Ready(res) =\u003e {\n let n = res?;\n new_self.write_buffer.drain(..n);\n // We're immediately ready to do some more flushing!\n cx.waker().wake_by_ref();\n // Return pending here because we still need to flush the\n // file\n Poll::Pending\n }\n Poll::Pending =\u003e Poll::Pending,\n }\n }\n }\n\n fn poll_shutdown(\n mut self: Pin\u003c\u0026mut Self\u003e,\n cx: \u0026mut Context\u003c'_\u003e,\n ) -\u003e Poll\u003cResult\u003c(), std::io::Error\u003e\u003e {\n self.as_mut()\n .stream\n .take()\n .map(|stream| stream.finalize(None));\n self.file.as_mut().poll_shutdown(cx)\n }\n}\n\n/// Represents some upstream error.\n#[derive(Debug, PartialEq, Eq)]\npub struct UpstreamError;\n\nimpl Error for UpstreamError {}\n\nimpl Display for UpstreamError {\n fn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n write!(f, \"An upstream error occurred\")\n }\n}\n\nimpl From\u003cUpstreamError\u003e for actix_web::Error {\n #[inline]\n fn from(_: UpstreamError) -\u003e Self {\n PayloadError::Incomplete(None).into()\n }\n}\n\n#[cfg(test)]\nmod read_file {\n use crate::cache::{ImageContentType, ImageMetadata};\n\n use super::read_file;\n use bytes::Bytes;\n use chrono::DateTime;\n use futures::StreamExt;\n use std::io::{Seek, SeekFrom, Write};\n use tempfile::tempfile;\n\n #[tokio::test]\n async fn can_read() {\n let mut temp_file = tempfile().unwrap();\n temp_file\n .write_all(\n br#\"{\"content_type\":0,\"content_length\":708370,\"last_modified\":\"2021-04-13T04:37:41+00:00\"}abc\"#,\n )\n .unwrap();\n temp_file.seek(SeekFrom::Start(0)).unwrap();\n\n let (inner_stream, maybe_header, metadata) = read_file(temp_file).await.unwrap().unwrap();\n\n let foo: Vec\u003c_\u003e = inner_stream.collect().await;\n assert_eq!(foo, vec![Ok(Bytes::from(\"abc\"))]);\n assert!(maybe_header.is_none());\n assert_eq!(\n metadata,\n ImageMetadata {\n content_length: Some(708_370),\n content_type: Some(ImageContentType::Png),\n last_modified: Some(\n DateTime::parse_from_rfc3339(\"2021-04-13T04:37:41+00:00\").unwrap()\n )\n }\n );\n }\n}\n\n#[cfg(test)]\nmod read_file_compat {\n use crate::cache::{ImageContentType, ImageMetadata};\n\n use super::read_file;\n use bytes::Bytes;\n use chrono::DateTime;\n use futures::StreamExt;\n use std::io::{Seek, SeekFrom, Write};\n use tempfile::tempfile;\n\n #[tokio::test]\n async fn can_read_legacy() {\n let mut temp_file = tempfile().unwrap();\n temp_file\n .write_all(\n b\"\\x00\\x5b{\\\"content_type\\\":\\\"image/jpeg\\\",\\\"last_modified\\\":\\\"Sat, 10 Apr 2021 10:55:22 GMT\\\",\\\"size\\\":117888}abc\",\n )\n .unwrap();\n temp_file.seek(SeekFrom::Start(0)).unwrap();\n\n let (inner_stream, maybe_header, metadata) = read_file(temp_file).await.unwrap().unwrap();\n\n let foo: Vec\u003c_\u003e = inner_stream.collect().await;\n assert_eq!(foo, vec![Ok(Bytes::from(\"abc\"))]);\n assert!(maybe_header.is_none());\n assert_eq!(\n metadata,\n ImageMetadata {\n content_length: Some(117_888),\n content_type: Some(ImageContentType::Jpeg),\n last_modified: Some(\n DateTime::parse_from_rfc2822(\"Sat, 10 Apr 2021 10:55:22 GMT\").unwrap()\n )\n }\n );\n }\n}\n","traces":[{"line":50,"address":[4325680],"length":1,"stats":{"Line":0},"fn_name":"read_file_from_path"},{"line":53,"address":[4326359,4326404,4325885,4326054,4326111],"length":1,"stats":{"Line":0},"fn_name":null},{"line":56,"address":[4326576],"length":1,"stats":{"Line":1},"fn_name":"read_file"},{"line":59,"address":[4327189],"length":1,"stats":{"Line":1},"fn_name":null},{"line":60,"address":[4327293],"length":1,"stats":{"Line":1},"fn_name":null},{"line":63,"address":[4327379],"length":1,"stats":{"Line":1},"fn_name":null},{"line":64,"address":[4327407],"length":1,"stats":{"Line":1},"fn_name":null},{"line":67,"address":[4327429,4327537],"length":1,"stats":{"Line":2},"fn_name":null},{"line":68,"address":[4327729,4327550,4335462,4327470],"length":1,"stats":{"Line":2},"fn_name":null},{"line":69,"address":[4327701],"length":1,"stats":{"Line":1},"fn_name":null},{"line":70,"address":[4327814,4334492],"length":1,"stats":{"Line":1},"fn_name":null},{"line":71,"address":[4327776],"length":1,"stats":{"Line":1},"fn_name":null},{"line":75,"address":[4327836],"length":1,"stats":{"Line":1},"fn_name":null},{"line":76,"address":[4327843],"length":1,"stats":{"Line":1},"fn_name":null},{"line":77,"address":[4327915,4327858,4328840],"length":1,"stats":{"Line":3},"fn_name":null},{"line":79,"address":[4327939],"length":1,"stats":{"Line":1},"fn_name":null},{"line":82,"address":[4328192,4328107,4328017],"length":1,"stats":{"Line":0},"fn_name":null},{"line":83,"address":[4328360],"length":1,"stats":{"Line":0},"fn_name":null},{"line":86,"address":[4328532,4328071,4334589],"length":1,"stats":{"Line":2},"fn_name":null},{"line":87,"address":[4334664],"length":1,"stats":{"Line":1},"fn_name":null},{"line":88,"address":[4328654,4328569,4334760],"length":1,"stats":{"Line":2},"fn_name":null},{"line":90,"address":[4328853,4327875],"length":1,"stats":{"Line":0},"fn_name":null},{"line":91,"address":[4328860,4329018,4329391,4335467,4335368],"length":1,"stats":{"Line":0},"fn_name":null},{"line":92,"address":[4329501,4334258,4335389,4329368,4329430],"length":1,"stats":{"Line":0},"fn_name":null},{"line":98,"address":[4329926,4334889],"length":1,"stats":{"Line":0},"fn_name":null},{"line":99,"address":[4330021],"length":1,"stats":{"Line":0},"fn_name":null},{"line":100,"address":[4330052,4330411,4335410,4330155],"length":1,"stats":{"Line":0},"fn_name":null},{"line":101,"address":[4330427,4330555],"length":1,"stats":{"Line":0},"fn_name":null},{"line":102,"address":[4330830],"length":1,"stats":{"Line":0},"fn_name":null},{"line":105,"address":[4330872,4330975],"length":1,"stats":{"Line":0},"fn_name":null},{"line":106,"address":[4331007],"length":1,"stats":{"Line":0},"fn_name":null},{"line":108,"address":[4330921,4331161,4331076],"length":1,"stats":{"Line":0},"fn_name":null},{"line":109,"address":[4331329],"length":1,"stats":{"Line":0},"fn_name":null},{"line":112,"address":[4331039,4331430,4331346],"length":1,"stats":{"Line":0},"fn_name":null},{"line":113,"address":[4331494],"length":1,"stats":{"Line":0},"fn_name":null},{"line":115,"address":[4331946,4331376,4331870],"length":1,"stats":{"Line":0},"fn_name":null},{"line":116,"address":[4332105],"length":1,"stats":{"Line":0},"fn_name":null},{"line":119,"address":[4331558],"length":1,"stats":{"Line":0},"fn_name":null},{"line":121,"address":[4331660,4334814,4332253],"length":1,"stats":{"Line":0},"fn_name":null},{"line":124,"address":[4332298,4335431,4332513,4334214],"length":1,"stats":{"Line":0},"fn_name":null},{"line":125,"address":[4332712],"length":1,"stats":{"Line":0},"fn_name":null},{"line":127,"address":[4332845,4333082,4332782],"length":1,"stats":{"Line":0},"fn_name":null},{"line":128,"address":[4332926,4332850,4332805],"length":1,"stats":{"Line":0},"fn_name":null},{"line":135,"address":[4333809,4333150,4333223],"length":1,"stats":{"Line":2},"fn_name":null},{"line":136,"address":[4333254,4334161,4335097],"length":1,"stats":{"Line":1},"fn_name":null},{"line":137,"address":[4333420,4335472,4326667,4326640],"length":1,"stats":{"Line":4},"fn_name":"{{closure}}"},{"line":139,"address":[4333565,4333172,4333641],"length":1,"stats":{"Line":0},"fn_name":null},{"line":140,"address":[4333797],"length":1,"stats":{"Line":0},"fn_name":null},{"line":151,"address":[4336325,4336064],"length":1,"stats":{"Line":0},"fn_name":"new"},{"line":153,"address":[4336085,4336151],"length":1,"stats":{"Line":0},"fn_name":null},{"line":155,"address":[4336207],"length":1,"stats":{"Line":0},"fn_name":null},{"line":161,"address":[4336368,4337678],"length":1,"stats":{"Line":0},"fn_name":"poll_read"},{"line":166,"address":[4336434],"length":1,"stats":{"Line":0},"fn_name":null},{"line":168,"address":[4336546,4336652],"length":1,"stats":{"Line":0},"fn_name":null},{"line":169,"address":[4336717],"length":1,"stats":{"Line":0},"fn_name":null},{"line":170,"address":[4336746],"length":1,"stats":{"Line":0},"fn_name":null},{"line":173,"address":[4336781],"length":1,"stats":{"Line":0},"fn_name":null},{"line":179,"address":[4336864],"length":1,"stats":{"Line":0},"fn_name":null},{"line":180,"address":[4336912,4337075],"length":1,"stats":{"Line":0},"fn_name":null},{"line":181,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":183,"address":[4336921],"length":1,"stats":{"Line":0},"fn_name":null},{"line":184,"address":[4337059],"length":1,"stats":{"Line":0},"fn_name":null},{"line":185,"address":[4337071],"length":1,"stats":{"Line":0},"fn_name":null},{"line":193,"address":[4337138],"length":1,"stats":{"Line":0},"fn_name":null},{"line":194,"address":[4337557,4337487,4337195],"length":1,"stats":{"Line":0},"fn_name":null},{"line":195,"address":[4337553],"length":1,"stats":{"Line":0},"fn_name":null},{"line":197,"address":[4337575,4337715,4337464],"length":1,"stats":{"Line":0},"fn_name":null},{"line":199,"address":[4337608],"length":1,"stats":{"Line":0},"fn_name":null},{"line":207,"address":[4337760,4338080],"length":1,"stats":{"Line":0},"fn_name":"write_file\u003ccore::future::from_generator::GenFuture\u003cgenerator-0\u003e,closure-0\u003e"},{"line":220,"address":[4350630,4341062],"length":1,"stats":{"Line":0},"fn_name":null},{"line":221,"address":[4349598,4341213,4348730,4341299,4359166,4358298,4351266,4341698,4350781,4350867],"length":1,"stats":{"Line":0},"fn_name":null},{"line":222,"address":[4351523,4342819,4341666,4351594,4341955,4348706,4352387,4358274,4351234,4342026,4359187,4349619],"length":1,"stats":{"Line":0},"fn_name":null},{"line":223,"address":[4352243,4342675],"length":1,"stats":{"Line":0},"fn_name":null},{"line":226,"address":[4343076,4352644,4342794,4352362],"length":1,"stats":{"Line":0},"fn_name":null},{"line":227,"address":[4352693,4343125],"length":1,"stats":{"Line":0},"fn_name":null},{"line":229,"address":[4344302,4353870],"length":1,"stats":{"Line":0},"fn_name":null},{"line":232,"address":[4338419,4338499,4338400,4338480],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}\u003ccore::future::from_generator::GenFuture\u003cgenerator-0\u003e,closure-0\u003e"},{"line":234,"address":[4344002,4353209,4343641,4353570],"length":1,"stats":{"Line":0},"fn_name":null},{"line":236,"address":[4353769,4352844,4344201,4343276],"length":1,"stats":{"Line":0},"fn_name":null},{"line":239,"address":[4344434,4344394,4353962,4354002],"length":1,"stats":{"Line":0},"fn_name":null},{"line":240,"address":[4354055,4354236,4349640,4344668,4359208,4344487],"length":1,"stats":{"Line":0},"fn_name":null},{"line":242,"address":[4353988,4344420],"length":1,"stats":{"Line":0},"fn_name":null},{"line":245,"address":[4354608,4358580,4354705,4345040,4345137,4349012],"length":1,"stats":{"Line":0},"fn_name":null},{"line":246,"address":[4345084,4354848,4345174,4349661,4358493,4348925,4354742,4354652,4359229,4345280],"length":1,"stats":{"Line":0},"fn_name":null},{"line":248,"address":[4355294,4345726,4358680,4349112,4345629,4355197],"length":1,"stats":{"Line":0},"fn_name":null},{"line":249,"address":[4349025,4359250,4345869,4355437,4345763,4349682,4345673,4355331,4355241,4358593],"length":1,"stats":{"Line":0},"fn_name":null},{"line":252,"address":[4355859,4346291,4355786,4346218],"length":1,"stats":{"Line":0},"fn_name":null},{"line":257,"address":[4346312,4355880,4346424,4359271,4349703,4358164,4355992,4348596],"length":1,"stats":{"Line":0},"fn_name":null},{"line":258,"address":[4346708,4356276],"length":1,"stats":{"Line":0},"fn_name":null},{"line":261,"address":[4348583,4347206,4349724,4356359,4359292,4358151,4346791,4356774,4346844,4355823,4356412,4346255],"length":1,"stats":{"Line":0},"fn_name":null},{"line":262,"address":[4357075,4347431,4356999,4356731,4347507,4347163],"length":1,"stats":{"Line":0},"fn_name":null},{"line":264,"address":[4359683,4347671,4350115,4357239],"length":1,"stats":{"Line":0},"fn_name":null},{"line":265,"address":[4347755,4357323],"length":1,"stats":{"Line":0},"fn_name":null},{"line":267,"address":[4347877,4357445,4357747,4348179],"length":1,"stats":{"Line":0},"fn_name":null},{"line":268,"address":[4338603,4357510,4347942,4340576,4339648,4339488,4339448,4338560,4340536,4339691],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}\u003ccore::future::from_generator::GenFuture\u003cgenerator-0\u003e,closure-0\u003e"},{"line":269,"address":[4338978,4340462,4338909,4339997,4340066,4339475,4339374,4340563],"length":1,"stats":{"Line":0},"fn_name":null},{"line":270,"address":[4339742,4338654],"length":1,"stats":{"Line":0},"fn_name":null},{"line":275,"address":[4348189,4357757],"length":1,"stats":{"Line":0},"fn_name":null},{"line":286,"address":[4359872,4360266],"length":1,"stats":{"Line":0},"fn_name":"new"},{"line":288,"address":[4359959,4359893],"length":1,"stats":{"Line":0},"fn_name":null},{"line":289,"address":[4359968],"length":1,"stats":{"Line":0},"fn_name":null},{"line":290,"address":[4360079],"length":1,"stats":{"Line":0},"fn_name":null},{"line":291,"address":[4360098],"length":1,"stats":{"Line":0},"fn_name":null},{"line":297,"address":[4361110,4360336],"length":1,"stats":{"Line":0},"fn_name":"poll_write"},{"line":302,"address":[4360400],"length":1,"stats":{"Line":0},"fn_name":null},{"line":304,"address":[4360434],"length":1,"stats":{"Line":0},"fn_name":null},{"line":305,"address":[4360609,4360454],"length":1,"stats":{"Line":0},"fn_name":null},{"line":306,"address":[4360538],"length":1,"stats":{"Line":0},"fn_name":null},{"line":307,"address":[4360521],"length":1,"stats":{"Line":0},"fn_name":null},{"line":312,"address":[4360616],"length":1,"stats":{"Line":0},"fn_name":null},{"line":314,"address":[4360725,4360887],"length":1,"stats":{"Line":0},"fn_name":null},{"line":315,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":316,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":317,"address":[4360685],"length":1,"stats":{"Line":0},"fn_name":null},{"line":319,"address":[4360897,4360747],"length":1,"stats":{"Line":0},"fn_name":null},{"line":320,"address":[4360913],"length":1,"stats":{"Line":0},"fn_name":null},{"line":322,"address":[4360981],"length":1,"stats":{"Line":0},"fn_name":null},{"line":324,"address":[4360796],"length":1,"stats":{"Line":0},"fn_name":null},{"line":328,"address":[4361168],"length":1,"stats":{"Line":0},"fn_name":"poll_flush"},{"line":332,"address":[4361240,4362012],"length":1,"stats":{"Line":0},"fn_name":null},{"line":333,"address":[4361391,4361289],"length":1,"stats":{"Line":0},"fn_name":null},{"line":335,"address":[4361306],"length":1,"stats":{"Line":0},"fn_name":null},{"line":336,"address":[4361452],"length":1,"stats":{"Line":0},"fn_name":null},{"line":337,"address":[4361590,4361555],"length":1,"stats":{"Line":0},"fn_name":null},{"line":338,"address":[4361597,4361566],"length":1,"stats":{"Line":0},"fn_name":null},{"line":339,"address":[4361639,4361794],"length":1,"stats":{"Line":0},"fn_name":null},{"line":340,"address":[4361755,4361970],"length":1,"stats":{"Line":0},"fn_name":null},{"line":342,"address":[4361989],"length":1,"stats":{"Line":0},"fn_name":null},{"line":345,"address":[4362007],"length":1,"stats":{"Line":0},"fn_name":null},{"line":347,"address":[4361585],"length":1,"stats":{"Line":0},"fn_name":null},{"line":352,"address":[4362032],"length":1,"stats":{"Line":0},"fn_name":"poll_shutdown"},{"line":356,"address":[4362104],"length":1,"stats":{"Line":0},"fn_name":null},{"line":357,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":359,"address":[4362320,4362334,4362164],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":360,"address":[4362174,4362246],"length":1,"stats":{"Line":0},"fn_name":null},{"line":371,"address":[4362400],"length":1,"stats":{"Line":0},"fn_name":"fmt"},{"line":372,"address":[4362419],"length":1,"stats":{"Line":0},"fn_name":null},{"line":378,"address":[4362496],"length":1,"stats":{"Line":0},"fn_name":"from"},{"line":379,"address":[4362500],"length":1,"stats":{"Line":0},"fn_name":null},{"line":395,"address":[4143122,4145675,4143335,4142976,4143236,4142981,4143008,4145567,4143296],"length":1,"stats":{"Line":6},"fn_name":"{{closure}}"},{"line":396,"address":[4143395],"length":1,"stats":{"Line":1},"fn_name":null},{"line":397,"address":[4143498],"length":1,"stats":{"Line":1},"fn_name":null},{"line":402,"address":[4143633],"length":1,"stats":{"Line":1},"fn_name":null},{"line":404,"address":[4143818,4145641,4143723,4145544],"length":1,"stats":{"Line":2},"fn_name":null},{"line":406,"address":[4144206,4145662,4145499,4144368],"length":1,"stats":{"Line":3},"fn_name":null},{"line":407,"address":[4144572,4145448],"length":1,"stats":{"Line":1},"fn_name":null},{"line":408,"address":[4144895],"length":1,"stats":{"Line":1},"fn_name":null},{"line":409,"address":[4145233,4144965,4143166,4143015],"length":1,"stats":{"Line":4},"fn_name":null},{"line":411,"address":[4145161],"length":1,"stats":{"Line":1},"fn_name":null},{"line":412,"address":[4144974],"length":1,"stats":{"Line":1},"fn_name":null},{"line":413,"address":[4144996],"length":1,"stats":{"Line":1},"fn_name":null},{"line":414,"address":[4145134],"length":1,"stats":{"Line":1},"fn_name":null},{"line":415,"address":[4145018],"length":1,"stats":{"Line":1},"fn_name":null},{"line":434,"address":[4146000,4148667,4146288,4145973,4145968,4146114,4148559,4146327,4146228],"length":1,"stats":{"Line":6},"fn_name":"{{closure}}"},{"line":435,"address":[4146387],"length":1,"stats":{"Line":1},"fn_name":null},{"line":436,"address":[4146490],"length":1,"stats":{"Line":1},"fn_name":null},{"line":441,"address":[4146625],"length":1,"stats":{"Line":1},"fn_name":null},{"line":443,"address":[4148536,4148633,4146810,4146715],"length":1,"stats":{"Line":2},"fn_name":null},{"line":445,"address":[4148654,4148491,4147198,4147360],"length":1,"stats":{"Line":3},"fn_name":null},{"line":446,"address":[4147564,4148440],"length":1,"stats":{"Line":1},"fn_name":null},{"line":447,"address":[4147887],"length":1,"stats":{"Line":1},"fn_name":null},{"line":448,"address":[4148225,4146158,4147957,4146007],"length":1,"stats":{"Line":4},"fn_name":null},{"line":450,"address":[4148153],"length":1,"stats":{"Line":1},"fn_name":null},{"line":451,"address":[4147966],"length":1,"stats":{"Line":1},"fn_name":null},{"line":452,"address":[4147988],"length":1,"stats":{"Line":1},"fn_name":null},{"line":453,"address":[4148126],"length":1,"stats":{"Line":1},"fn_name":null},{"line":454,"address":[4148010],"length":1,"stats":{"Line":1},"fn_name":null}],"covered":48,"coverable":167},{"path":["/","home","edward","Documents","repos","mangadex-home","src","cache","mem.rs"],"content":"use std::sync::atomic::{AtomicU64, Ordering};\nuse std::sync::Arc;\n\nuse super::{\n Cache, CacheError, CacheKey, CacheStream, CallbackCache, ImageMetadata, InnerStream, MemStream,\n};\nuse async_trait::async_trait;\nuse bytes::Bytes;\nuse futures::FutureExt;\nuse lfu_cache::LfuCache;\nuse lru::LruCache;\nuse tokio::sync::mpsc::{channel, Sender};\nuse tokio::sync::Mutex;\n\ntype CacheValue = (Bytes, ImageMetadata, u64);\n\n/// Use LRU as the eviction strategy\npub type Lru = LruCache\u003cCacheKey, CacheValue\u003e;\n/// Use LFU as the eviction strategy\npub type Lfu = LfuCache\u003cCacheKey, CacheValue\u003e;\n\n/// Adapter trait for memory cache backends\npub trait InternalMemoryCache: Sync + Send {\n fn unbounded() -\u003e Self;\n fn get(\u0026mut self, key: \u0026CacheKey) -\u003e Option\u003c\u0026CacheValue\u003e;\n fn push(\u0026mut self, key: CacheKey, data: CacheValue);\n fn pop(\u0026mut self) -\u003e Option\u003c(CacheKey, CacheValue)\u003e;\n}\n\nimpl InternalMemoryCache for Lfu {\n #[inline]\n fn unbounded() -\u003e Self {\n Self::unbounded()\n }\n\n #[inline]\n fn get(\u0026mut self, key: \u0026CacheKey) -\u003e Option\u003c\u0026CacheValue\u003e {\n self.get(key)\n }\n\n #[inline]\n fn push(\u0026mut self, key: CacheKey, data: CacheValue) {\n self.insert(key, data);\n }\n\n #[inline]\n fn pop(\u0026mut self) -\u003e Option\u003c(CacheKey, CacheValue)\u003e {\n self.pop_lfu_key_value()\n }\n}\n\nimpl InternalMemoryCache for Lru {\n #[inline]\n fn unbounded() -\u003e Self {\n Self::unbounded()\n }\n\n #[inline]\n fn get(\u0026mut self, key: \u0026CacheKey) -\u003e Option\u003c\u0026CacheValue\u003e {\n self.get(key)\n }\n\n #[inline]\n fn push(\u0026mut self, key: CacheKey, data: CacheValue) {\n self.put(key, data);\n }\n\n #[inline]\n fn pop(\u0026mut self) -\u003e Option\u003c(CacheKey, CacheValue)\u003e {\n self.pop_lru()\n }\n}\n\n/// Memory accelerated disk cache. Uses the internal cache implementation in\n/// memory to speed up reads.\npub struct MemoryCache\u003cMemoryCacheImpl, ColdCache\u003e {\n inner: ColdCache,\n cur_mem_size: AtomicU64,\n mem_cache: Mutex\u003cMemoryCacheImpl\u003e,\n master_sender: Sender\u003c(CacheKey, Bytes, ImageMetadata, u64)\u003e,\n}\n\nimpl\u003cMemoryCacheImpl, ColdCache\u003e MemoryCache\u003cMemoryCacheImpl, ColdCache\u003e\nwhere\n MemoryCacheImpl: 'static + InternalMemoryCache,\n ColdCache: 'static + Cache,\n{\n pub async fn new(inner: ColdCache, max_mem_size: crate::units::Bytes) -\u003e Arc\u003cSelf\u003e {\n let (tx, mut rx) = channel(100);\n let new_self = Arc::new(Self {\n inner,\n cur_mem_size: AtomicU64::new(0),\n mem_cache: Mutex::new(MemoryCacheImpl::unbounded()),\n master_sender: tx,\n });\n\n let new_self_0 = Arc::clone(\u0026new_self);\n tokio::spawn(async move {\n let new_self = new_self_0;\n let max_mem_size = max_mem_size.get() / 20 * 19;\n while let Some((key, bytes, metadata, size)) = rx.recv().await {\n // Add to memory cache\n // We can add first because we constrain our memory usage to 95%\n new_self\n .cur_mem_size\n .fetch_add(size as u64, Ordering::Release);\n new_self\n .mem_cache\n .lock()\n .await\n .push(key, (bytes, metadata, size));\n\n // Pop if too large\n while new_self.cur_mem_size.load(Ordering::Acquire) \u003e= max_mem_size as u64 {\n let popped = new_self\n .mem_cache\n .lock()\n .await\n .pop()\n .map(|(key, (bytes, metadata, size))| (key, bytes, metadata, size));\n if let Some((_, _, _, size)) = popped {\n new_self\n .cur_mem_size\n .fetch_sub(size as u64, Ordering::Release);\n } else {\n break;\n }\n }\n }\n });\n\n new_self\n }\n}\n\n#[async_trait]\nimpl\u003cMemoryCacheImpl, ColdCache\u003e Cache for MemoryCache\u003cMemoryCacheImpl, ColdCache\u003e\nwhere\n MemoryCacheImpl: InternalMemoryCache,\n ColdCache: CallbackCache,\n{\n #[inline]\n async fn get(\n \u0026self,\n key: \u0026CacheKey,\n ) -\u003e Option\u003cResult\u003c(CacheStream, ImageMetadata), super::CacheError\u003e\u003e {\n match self.mem_cache.lock().now_or_never() {\n Some(mut mem_cache) =\u003e match mem_cache.get(key).map(|(bytes, metadata, _)| {\n Ok((InnerStream::Memory(MemStream(bytes.clone())), *metadata))\n }) {\n Some(v) =\u003e Some(v.and_then(|(inner, metadata)| {\n CacheStream::new(inner, None)\n .map(|v| (v, metadata))\n .map_err(|_| CacheError::DecryptionFailure)\n })),\n None =\u003e self.inner.get(key).await,\n },\n None =\u003e self.inner.get(key).await,\n }\n }\n\n #[inline]\n async fn put(\n \u0026self,\n key: CacheKey,\n image: Bytes,\n metadata: ImageMetadata,\n ) -\u003e Result\u003c(), super::CacheError\u003e {\n self.inner\n .put_with_on_completed_callback(key, image, metadata, self.master_sender.clone())\n .await\n }\n}\n","traces":[{"line":32,"address":[5343056],"length":1,"stats":{"Line":0},"fn_name":"unbounded"},{"line":33,"address":[5343064],"length":1,"stats":{"Line":0},"fn_name":null},{"line":37,"address":[5343088],"length":1,"stats":{"Line":0},"fn_name":"get"},{"line":38,"address":[5343102],"length":1,"stats":{"Line":0},"fn_name":null},{"line":42,"address":[5343120],"length":1,"stats":{"Line":0},"fn_name":"push"},{"line":43,"address":[5343145],"length":1,"stats":{"Line":0},"fn_name":null},{"line":47,"address":[5343232],"length":1,"stats":{"Line":0},"fn_name":"pop"},{"line":48,"address":[5343249],"length":1,"stats":{"Line":0},"fn_name":null},{"line":54,"address":[4126032],"length":1,"stats":{"Line":0},"fn_name":"unbounded"},{"line":55,"address":[4126040],"length":1,"stats":{"Line":0},"fn_name":null},{"line":59,"address":[4126064],"length":1,"stats":{"Line":0},"fn_name":"get"},{"line":60,"address":[4126078],"length":1,"stats":{"Line":0},"fn_name":null},{"line":64,"address":[4126096],"length":1,"stats":{"Line":0},"fn_name":"push"},{"line":65,"address":[4126121],"length":1,"stats":{"Line":0},"fn_name":null},{"line":69,"address":[4126208],"length":1,"stats":{"Line":0},"fn_name":"pop"},{"line":70,"address":[4126225],"length":1,"stats":{"Line":0},"fn_name":null},{"line":88,"address":[4369072,4362576,4370144,4371014,4369884,4362662,4362598,4370948,4369793,4370177,4369950,4369105,4370857,4362640],"length":1,"stats":{"Line":0},"fn_name":"new\u003clfu_cache::lfu::LfuCache\u003cmangadex_home::cache::CacheKey, (bytes::bytes::Bytes, mangadex_home::cache::ImageMetadata, u64)\u003e,alloc::sync::Arc\u003cmangadex_home::cache::disk::DiskCache\u003e\u003e"},{"line":89,"address":[4370291,4369219],"length":1,"stats":{"Line":0},"fn_name":null},{"line":90,"address":[4369398,4370470],"length":1,"stats":{"Line":0},"fn_name":null},{"line":91,"address":[4369278,4370350],"length":1,"stats":{"Line":0},"fn_name":null},{"line":92,"address":[4369303,4370375],"length":1,"stats":{"Line":0},"fn_name":null},{"line":93,"address":[4369355,4370427],"length":1,"stats":{"Line":0},"fn_name":null},{"line":94,"address":[4369385,4370457],"length":1,"stats":{"Line":0},"fn_name":null},{"line":97,"address":[4370670,4369604],"length":1,"stats":{"Line":0},"fn_name":null},{"line":98,"address":[4369637,4366343,4368723,4365870,4366304,4365955,4363536,4368638,4363575,4370703],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}\u003clru::LruCache\u003cmangadex_home::cache::CacheKey, (bytes::bytes::Bytes, mangadex_home::cache::ImageMetadata, u64), ahash::random_state::RandomState\u003e,alloc::sync::Arc\u003cmangadex_home::cache::disk::DiskCache\u003e\u003e"},{"line":99,"address":[4366406,4363638],"length":1,"stats":{"Line":0},"fn_name":null},{"line":100,"address":[4366427,4366058,4368826,4363659],"length":1,"stats":{"Line":0},"fn_name":null},{"line":101,"address":[4366896,4365543,4363756,4368311,4368668,4366657,4363889,4364128,4366524,4365900],"length":1,"stats":{"Line":0},"fn_name":null},{"line":104,"address":[4364301,4367069,4367018,4364250],"length":1,"stats":{"Line":0},"fn_name":null},{"line":105,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":106,"address":[4367054,4364286],"length":1,"stats":{"Line":0},"fn_name":null},{"line":107,"address":[4367649,4367092,4364881,4368432,4364443,4364324,4367211,4368689,4365921,4365664],"length":1,"stats":{"Line":0},"fn_name":null},{"line":108,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":109,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":110,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":111,"address":[4364652,4364896,4367664,4365641,4367420,4368409],"length":1,"stats":{"Line":0},"fn_name":null},{"line":114,"address":[4367679,4364911],"length":1,"stats":{"Line":0},"fn_name":null},{"line":115,"address":[4365002,4367770,4365121,4365942,4368395,4368710,4367889,4365627],"length":1,"stats":{"Line":0},"fn_name":null},{"line":116,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":117,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":118,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":119,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":120,"address":[4362721,4365604,4365368,4363137,4363120,4362704,4368372,4368136],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}\u003clru::LruCache\u003cmangadex_home::cache::CacheKey, (bytes::bytes::Bytes, mangadex_home::cache::ImageMetadata, u64), ahash::random_state::RandomState\u003e,alloc::sync::Arc\u003cmangadex_home::cache::disk::DiskCache\u003e\u003e"},{"line":121,"address":[4368196,4368143,4365428,4365375],"length":1,"stats":{"Line":0},"fn_name":null},{"line":122,"address":[4368256,4365488,4368216,4365448],"length":1,"stats":{"Line":0},"fn_name":null},{"line":123,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":124,"address":[4365480,4368248],"length":1,"stats":{"Line":0},"fn_name":null},{"line":126,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":132,"address":[4369772,4370836],"length":1,"stats":{"Line":0},"fn_name":null},{"line":143,"address":[4393648,4393504],"length":1,"stats":{"Line":0},"fn_name":"get\u003clru::LruCache\u003cmangadex_home::cache::CacheKey, (bytes::bytes::Bytes, mangadex_home::cache::ImageMetadata, u64), ahash::random_state::RandomState\u003e,alloc::sync::Arc\u003cmangadex_home::cache::disk::DiskCache\u003e\u003e"},{"line":147,"address":[4395727,4397787,4397631,4395883],"length":1,"stats":{"Line":0},"fn_name":null},{"line":148,"address":[4396303,4398207,4395893,4396045,4394203,4393819,4397723,4395819,4397797,4394176,4397949,4393792],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}\u003clru::LruCache\u003cmangadex_home::cache::CacheKey, (bytes::bytes::Bytes, mangadex_home::cache::ImageMetadata, u64), ahash::random_state::RandomState\u003e,alloc::sync::Arc\u003cmangadex_home::cache::disk::DiskCache\u003e\u003e"},{"line":149,"address":[4394231,4393847],"length":1,"stats":{"Line":0},"fn_name":null},{"line":151,"address":[4395983,4397887,4395040,4396055,4395269,4397959,4395248,4395061],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}\u003clfu_cache::lfu::LfuCache\u003cmangadex_home::cache::CacheKey, (bytes::bytes::Bytes, mangadex_home::cache::ImageMetadata, u64)\u003e,alloc::sync::Arc\u003cmangadex_home::cache::disk::DiskCache\u003e\u003e"},{"line":152,"address":[4395117,4395325,4395190,4395398],"length":1,"stats":{"Line":0},"fn_name":null},{"line":153,"address":[4394560,4394768,4394603,4395385,4395177,4394811],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}\u003clru::LruCache\u003cmangadex_home::cache::CacheKey, (bytes::bytes::Bytes, mangadex_home::cache::ImageMetadata, u64), ahash::random_state::RandomState\u003e,alloc::sync::Arc\u003cmangadex_home::cache::disk::DiskCache\u003e\u003e"},{"line":154,"address":[4395015,4395008,4394976,4394983],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}\u003clru::LruCache\u003cmangadex_home::cache::CacheKey, (bytes::bytes::Bytes, mangadex_home::cache::ImageMetadata, u64), ahash::random_state::RandomState\u003e,alloc::sync::Arc\u003cmangadex_home::cache::disk::DiskCache\u003e\u003e"},{"line":156,"address":[4398287,4396967,4396326,4398230,4396019,4398871,4397923,4397072,4398976,4396383],"length":1,"stats":{"Line":0},"fn_name":null},{"line":158,"address":[4398997,4397758,4398852,4396682,4398586,4398537,4396948,4396633,4397093,4395854],"length":1,"stats":{"Line":0},"fn_name":null},{"line":163,"address":[4399264,4399536],"length":1,"stats":{"Line":0},"fn_name":"put\u003clfu_cache::lfu::LfuCache\u003cmangadex_home::cache::CacheKey, (bytes::bytes::Bytes, mangadex_home::cache::ImageMetadata, u64)\u003e,alloc::sync::Arc\u003cmangadex_home::cache::disk::DiskCache\u003e\u003e"},{"line":169,"address":[4400518,4402500,4400472,4401834,4401928,4401974,4400823,4400378,4402279,4401044],"length":1,"stats":{"Line":0},"fn_name":null},{"line":170,"address":[4401912,4401658,4400456,4402293,4400837,4400902,4402358,4400202],"length":1,"stats":{"Line":0},"fn_name":null},{"line":171,"address":[],"length":0,"stats":{"Line":0},"fn_name":null}],"covered":0,"coverable":63},{"path":["/","home","edward","Documents","repos","mangadex-home","src","cache","mod.rs"],"content":"use std::fmt::Display;\nuse std::path::PathBuf;\nuse std::pin::Pin;\nuse std::str::FromStr;\nuse std::sync::Arc;\nuse std::task::{Context, Poll};\n\nuse actix_web::http::HeaderValue;\nuse async_trait::async_trait;\nuse bytes::{Bytes, BytesMut};\nuse chrono::{DateTime, FixedOffset};\nuse futures::{Stream, StreamExt};\nuse once_cell::sync::OnceCell;\nuse serde::{Deserialize, Serialize};\nuse serde_repr::{Deserialize_repr, Serialize_repr};\nuse sodiumoxide::crypto::secretstream::{Header, Key, Pull, Stream as SecretStream};\nuse thiserror::Error;\nuse tokio::io::AsyncRead;\nuse tokio::sync::mpsc::Sender;\nuse tokio_util::codec::{BytesCodec, FramedRead};\n\npub use disk::DiskCache;\npub use fs::UpstreamError;\npub use mem::MemoryCache;\n\nuse self::compat::LegacyImageMetadata;\n\npub static ENCRYPTION_KEY: OnceCell\u003cKey\u003e = OnceCell::new();\n\nmod compat;\nmod disk;\nmod fs;\npub mod mem;\n\n#[derive(PartialEq, Eq, Hash, Clone)]\npub struct CacheKey(pub String, pub String, pub bool);\n\nimpl Display for CacheKey {\n fn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n if self.2 {\n write!(f, \"saver/{}/{}\", self.0, self.1)\n } else {\n write!(f, \"data/{}/{}\", self.0, self.1)\n }\n }\n}\n\nimpl From\u003cCacheKey\u003e for PathBuf {\n #[inline]\n fn from(key: CacheKey) -\u003e Self {\n key.to_string().into()\n }\n}\n\nimpl From\u003c\u0026CacheKey\u003e for PathBuf {\n #[inline]\n fn from(key: \u0026CacheKey) -\u003e Self {\n key.to_string().into()\n }\n}\n\n#[derive(Clone)]\npub struct CachedImage(pub Bytes);\n\n#[derive(Copy, Clone, Serialize, Deserialize, Debug, PartialEq)]\npub struct ImageMetadata {\n pub content_type: Option\u003cImageContentType\u003e,\n pub content_length: Option\u003cu32\u003e,\n pub last_modified: Option\u003cDateTime\u003cFixedOffset\u003e\u003e,\n}\n\n// Confirmed by Ply to be these types: https://link.eddie.sh/ZXfk0\n#[derive(Copy, Clone, Serialize_repr, Deserialize_repr, Debug, PartialEq, Eq)]\n#[repr(u8)]\npub enum ImageContentType {\n Png = 0,\n Jpeg,\n Gif,\n}\n\npub struct InvalidContentType;\n\nimpl FromStr for ImageContentType {\n type Err = InvalidContentType;\n\n #[inline]\n fn from_str(s: \u0026str) -\u003e Result\u003cSelf, Self::Err\u003e {\n match s {\n \"image/png\" =\u003e Ok(Self::Png),\n \"image/jpeg\" =\u003e Ok(Self::Jpeg),\n \"image/gif\" =\u003e Ok(Self::Gif),\n _ =\u003e Err(InvalidContentType),\n }\n }\n}\n\nimpl AsRef\u003cstr\u003e for ImageContentType {\n #[inline]\n fn as_ref(\u0026self) -\u003e \u0026str {\n match self {\n Self::Png =\u003e \"image/png\",\n Self::Jpeg =\u003e \"image/jpeg\",\n Self::Gif =\u003e \"image/gif\",\n }\n }\n}\n\nimpl From\u003cLegacyImageMetadata\u003e for ImageMetadata {\n fn from(legacy: LegacyImageMetadata) -\u003e Self {\n Self {\n content_type: legacy.content_type.map(|v| v.0),\n content_length: legacy.size,\n last_modified: legacy.last_modified.map(|v| v.0),\n }\n }\n}\n\n#[derive(Debug)]\npub enum ImageRequestError {\n ContentType,\n ContentLength,\n LastModified,\n}\n\nimpl ImageMetadata {\n pub fn new(\n content_type: Option\u003cHeaderValue\u003e,\n content_length: Option\u003cHeaderValue\u003e,\n last_modified: Option\u003cHeaderValue\u003e,\n ) -\u003e Result\u003cSelf, ImageRequestError\u003e {\n Ok(Self {\n content_type: content_type\n .map(|v| match v.to_str() {\n Ok(v) =\u003e ImageContentType::from_str(v),\n Err(_) =\u003e Err(InvalidContentType),\n })\n .transpose()\n .map_err(|_| ImageRequestError::ContentType)?,\n content_length: content_length\n .map(|header_val| {\n header_val\n .to_str()\n .map_err(|_| ImageRequestError::ContentLength)?\n .parse()\n .map_err(|_| ImageRequestError::ContentLength)\n })\n .transpose()?,\n last_modified: last_modified\n .map(|header_val| {\n DateTime::parse_from_rfc2822(\n header_val\n .to_str()\n .map_err(|_| ImageRequestError::LastModified)?,\n )\n .map_err(|_| ImageRequestError::LastModified)\n })\n .transpose()?,\n })\n }\n}\n\n#[derive(Error, Debug)]\npub enum CacheError {\n #[error(transparent)]\n Io(#[from] std::io::Error),\n #[error(transparent)]\n Reqwest(#[from] reqwest::Error),\n #[error(transparent)]\n Upstream(#[from] UpstreamError),\n #[error(\"An error occurred while reading the decryption header\")]\n DecryptionFailure,\n}\n\n#[async_trait]\npub trait Cache: Send + Sync {\n async fn get(\u0026self, key: \u0026CacheKey)\n -\u003e Option\u003cResult\u003c(CacheStream, ImageMetadata), CacheError\u003e\u003e;\n\n async fn put(\n \u0026self,\n key: CacheKey,\n image: Bytes,\n metadata: ImageMetadata,\n ) -\u003e Result\u003c(), CacheError\u003e;\n}\n\n#[async_trait]\nimpl\u003cT: Cache\u003e Cache for Arc\u003cT\u003e {\n #[inline]\n async fn get(\n \u0026self,\n key: \u0026CacheKey,\n ) -\u003e Option\u003cResult\u003c(CacheStream, ImageMetadata), CacheError\u003e\u003e {\n self.as_ref().get(key).await\n }\n\n #[inline]\n async fn put(\n \u0026self,\n key: CacheKey,\n image: Bytes,\n metadata: ImageMetadata,\n ) -\u003e Result\u003c(), CacheError\u003e {\n self.as_ref().put(key, image, metadata).await\n }\n}\n\n#[async_trait]\npub trait CallbackCache: Cache {\n async fn put_with_on_completed_callback(\n \u0026self,\n key: CacheKey,\n image: Bytes,\n metadata: ImageMetadata,\n on_complete: Sender\u003c(CacheKey, Bytes, ImageMetadata, u64)\u003e,\n ) -\u003e Result\u003c(), CacheError\u003e;\n}\n\n#[async_trait]\nimpl\u003cT: CallbackCache\u003e CallbackCache for Arc\u003cT\u003e {\n #[inline]\n async fn put_with_on_completed_callback(\n \u0026self,\n key: CacheKey,\n image: Bytes,\n metadata: ImageMetadata,\n on_complete: Sender\u003c(CacheKey, Bytes, ImageMetadata, u64)\u003e,\n ) -\u003e Result\u003c(), CacheError\u003e {\n self.as_ref()\n .put_with_on_completed_callback(key, image, metadata, on_complete)\n .await\n }\n}\n\npub struct CacheStream {\n inner: InnerStream,\n decrypt: Option\u003cSecretStream\u003cPull\u003e\u003e,\n}\n\nimpl CacheStream {\n pub(self) fn new(inner: InnerStream, header: Option\u003cHeader\u003e) -\u003e Result\u003cSelf, ()\u003e {\n Ok(Self {\n inner,\n decrypt: header\n .and_then(|header| {\n ENCRYPTION_KEY\n .get()\n .map(|key| SecretStream::init_pull(\u0026header, key))\n })\n .transpose()?,\n })\n }\n}\n\nimpl Stream for CacheStream {\n type Item = CacheStreamItem;\n\n fn poll_next(mut self: Pin\u003c\u0026mut Self\u003e, cx: \u0026mut Context\u003c'_\u003e) -\u003e Poll\u003cOption\u003cSelf::Item\u003e\u003e {\n self.inner.poll_next_unpin(cx).map(|data| {\n // False positive (`data`): https://link.eddie.sh/r1fXX\n #[allow(clippy::option_if_let_else)]\n if let Some(keystream) = self.decrypt.as_mut() {\n data.map(|bytes_res| {\n bytes_res.and_then(|bytes| {\n keystream\n .pull(\u0026bytes, None)\n .map(|(data, _tag)| Bytes::from(data))\n .map_err(|_| UpstreamError)\n })\n })\n } else {\n data\n }\n })\n }\n}\n\npub(self) enum InnerStream {\n Memory(MemStream),\n Completed(FramedRead\u003cPin\u003cBox\u003cdyn AsyncRead + Send\u003e\u003e, BytesCodec\u003e),\n}\n\nimpl From\u003cCachedImage\u003e for InnerStream {\n fn from(image: CachedImage) -\u003e Self {\n Self::Memory(MemStream(image.0))\n }\n}\n\ntype CacheStreamItem = Result\u003cBytes, UpstreamError\u003e;\n\nimpl Stream for InnerStream {\n type Item = CacheStreamItem;\n\n fn poll_next(self: Pin\u003c\u0026mut Self\u003e, cx: \u0026mut Context\u003c'_\u003e) -\u003e Poll\u003cOption\u003cSelf::Item\u003e\u003e {\n match self.get_mut() {\n Self::Memory(stream) =\u003e stream.poll_next_unpin(cx),\n Self::Completed(stream) =\u003e stream\n .poll_next_unpin(cx)\n .map_ok(BytesMut::freeze)\n .map_err(|_| UpstreamError),\n }\n }\n}\n\npub struct MemStream(pub Bytes);\n\nimpl Stream for MemStream {\n type Item = CacheStreamItem;\n\n fn poll_next(mut self: Pin\u003c\u0026mut Self\u003e, _: \u0026mut Context\u003c'_\u003e) -\u003e Poll\u003cOption\u003cSelf::Item\u003e\u003e {\n let mut new_bytes = Bytes::new();\n std::mem::swap(\u0026mut self.0, \u0026mut new_bytes);\n if new_bytes.is_empty() {\n Poll::Ready(None)\n } else {\n Poll::Ready(Some(Ok(new_bytes)))\n }\n }\n}\n\n#[cfg(test)]\nmod tests {\n use super::*;\n\n #[test]\n fn metadata_size() {\n assert_eq!(std::mem::size_of::\u003cImageMetadata\u003e(), 32);\n }\n}\n","traces":[{"line":39,"address":[4955168],"length":1,"stats":{"Line":0},"fn_name":"fmt"},{"line":40,"address":[4955201],"length":1,"stats":{"Line":0},"fn_name":null},{"line":41,"address":[4955395,4955212],"length":1,"stats":{"Line":0},"fn_name":null},{"line":43,"address":[4955536,4955302],"length":1,"stats":{"Line":0},"fn_name":null},{"line":50,"address":[5142718,5142640],"length":1,"stats":{"Line":0},"fn_name":"from"},{"line":51,"address":[5142664],"length":1,"stats":{"Line":0},"fn_name":null},{"line":57,"address":[5142752],"length":1,"stats":{"Line":0},"fn_name":"from"},{"line":58,"address":[5142771],"length":1,"stats":{"Line":0},"fn_name":null},{"line":87,"address":[4955696],"length":1,"stats":{"Line":1},"fn_name":"from_str"},{"line":88,"address":[4955845,4955838,4955852],"length":1,"stats":{"Line":1},"fn_name":null},{"line":89,"address":[4955840,4955720],"length":1,"stats":{"Line":1},"fn_name":null},{"line":90,"address":[4955761,4955847],"length":1,"stats":{"Line":2},"fn_name":null},{"line":91,"address":[4955854,4955802],"length":1,"stats":{"Line":0},"fn_name":null},{"line":92,"address":[4955833],"length":1,"stats":{"Line":0},"fn_name":null},{"line":99,"address":[4955872],"length":1,"stats":{"Line":0},"fn_name":"as_ref"},{"line":100,"address":[4955924,4955949],"length":1,"stats":{"Line":0},"fn_name":null},{"line":101,"address":[4955928,4955881],"length":1,"stats":{"Line":0},"fn_name":null},{"line":102,"address":[4955951],"length":1,"stats":{"Line":0},"fn_name":null},{"line":103,"address":[4955903],"length":1,"stats":{"Line":0},"fn_name":null},{"line":109,"address":[4956000],"length":1,"stats":{"Line":1},"fn_name":"from"},{"line":111,"address":[4956168,4956019,4956160],"length":1,"stats":{"Line":3},"fn_name":"{{closure}}"},{"line":112,"address":[4956037],"length":1,"stats":{"Line":1},"fn_name":null},{"line":113,"address":[4956176,4956049,4956199],"length":1,"stats":{"Line":3},"fn_name":"{{closure}}"},{"line":126,"address":[4956224,4957383],"length":1,"stats":{"Line":0},"fn_name":"new"},{"line":131,"address":[4957173,4956254],"length":1,"stats":{"Line":0},"fn_name":null},{"line":132,"address":[4957465,4956597,4956555,4956286,4956471],"length":1,"stats":{"Line":0},"fn_name":null},{"line":133,"address":[4957545,4957536,4957683,4957615],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":134,"address":[4957699,4957619,4957592],"length":1,"stats":{"Line":0},"fn_name":null},{"line":135,"address":[4957610],"length":1,"stats":{"Line":0},"fn_name":null},{"line":137,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":138,"address":[4956557,4956450,4957728,4957732],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":139,"address":[4956985,4956489,4956841,4956943,4957467,4956615],"length":1,"stats":{"Line":0},"fn_name":null},{"line":140,"address":[4958158,4957824],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":141,"address":[4958056,4957915,4957836,4957994],"length":1,"stats":{"Line":0},"fn_name":null},{"line":142,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":143,"address":[4957902,4957760,4957764,4957962],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":144,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":145,"address":[4957803,4957792],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":147,"address":[4956820,4956945],"length":1,"stats":{"Line":0},"fn_name":null},{"line":148,"address":[4957285,4957327,4957014,4956877,4957472,4957081],"length":1,"stats":{"Line":0},"fn_name":null},{"line":149,"address":[4958537,4958272],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":150,"address":[4958407,4958495],"length":1,"stats":{"Line":0},"fn_name":null},{"line":151,"address":[4958462,4958423,4958381,4958302],"length":1,"stats":{"Line":0},"fn_name":null},{"line":152,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":153,"address":[4958425,4958212,4958208,4958368],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":155,"address":[4958240,4958251],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":157,"address":[4957287,4957044],"length":1,"stats":{"Line":0},"fn_name":null},{"line":190,"address":[4970128],"length":1,"stats":{"Line":0},"fn_name":"get\u003cmangadex_home::cache::disk::DiskCache\u003e"},{"line":194,"address":[4970855,4970589,4970893,4970480],"length":1,"stats":{"Line":0},"fn_name":null},{"line":198,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":204,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":222,"address":[4971040],"length":1,"stats":{"Line":0},"fn_name":"put_with_on_completed_callback\u003cmangadex_home::cache::disk::DiskCache\u003e"},{"line":229,"address":[4971776,4972727,4971982,4972399,4972066],"length":1,"stats":{"Line":0},"fn_name":null},{"line":230,"address":[4971801],"length":1,"stats":{"Line":0},"fn_name":null},{"line":231,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":241,"address":[4958576,4958944],"length":1,"stats":{"Line":0},"fn_name":"new"},{"line":242,"address":[4958808],"length":1,"stats":{"Line":0},"fn_name":null},{"line":243,"address":[4958596],"length":1,"stats":{"Line":0},"fn_name":null},{"line":244,"address":[4958643,4958880,4958898,4958756],"length":1,"stats":{"Line":0},"fn_name":null},{"line":245,"address":[4959040],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":246,"address":[4959096,4959059],"length":1,"stats":{"Line":0},"fn_name":null},{"line":247,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":248,"address":[4958992,4959091,4959013],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":250,"address":[4958735,4958882],"length":1,"stats":{"Line":0},"fn_name":null},{"line":258,"address":[4959120],"length":1,"stats":{"Line":0},"fn_name":"poll_next"},{"line":259,"address":[4959648,4959892,4959149],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":261,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":262,"address":[4959694,4959799],"length":1,"stats":{"Line":0},"fn_name":null},{"line":263,"address":[4959568,4959821,4959928],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":264,"address":[4959584,4959344,4959529],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":265,"address":[4959380,4959435],"length":1,"stats":{"Line":0},"fn_name":null},{"line":266,"address":[4959388],"length":1,"stats":{"Line":0},"fn_name":null},{"line":267,"address":[4959244,4959232],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":268,"address":[4959328],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":272,"address":[4959784],"length":1,"stats":{"Line":0},"fn_name":null},{"line":284,"address":[4960000],"length":1,"stats":{"Line":0},"fn_name":"from"},{"line":285,"address":[4960011],"length":1,"stats":{"Line":0},"fn_name":null},{"line":294,"address":[4960112],"length":1,"stats":{"Line":1},"fn_name":"poll_next"},{"line":295,"address":[4960204],"length":1,"stats":{"Line":0},"fn_name":null},{"line":296,"address":[4960161,4960220],"length":1,"stats":{"Line":1},"fn_name":null},{"line":297,"address":[4960180,4960239],"length":1,"stats":{"Line":2},"fn_name":null},{"line":298,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":299,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":300,"address":[4960321,4960288],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":310,"address":[4960336,4960747],"length":1,"stats":{"Line":0},"fn_name":"poll_next"},{"line":311,"address":[4960366],"length":1,"stats":{"Line":0},"fn_name":null},{"line":312,"address":[4960397],"length":1,"stats":{"Line":0},"fn_name":null},{"line":313,"address":[4960486,4960434],"length":1,"stats":{"Line":0},"fn_name":null},{"line":314,"address":[4960462],"length":1,"stats":{"Line":0},"fn_name":null},{"line":316,"address":[4960496],"length":1,"stats":{"Line":0},"fn_name":null},{"line":326,"address":[4402752,4402725,4402720],"length":1,"stats":{"Line":3},"fn_name":"{{closure}}"},{"line":327,"address":[4402756],"length":1,"stats":{"Line":1},"fn_name":null}],"covered":13,"coverable":92},{"path":["/","home","edward","Documents","repos","mangadex-home","src","client.rs"],"content":"use std::collections::HashMap;\nuse std::sync::Arc;\nuse std::time::Duration;\n\nuse actix_web::http::{HeaderMap, HeaderName, HeaderValue};\nuse actix_web::web::Data;\nuse bytes::Bytes;\nuse log::{debug, error, warn};\nuse once_cell::sync::Lazy;\nuse parking_lot::RwLock;\nuse reqwest::header::{\n ACCESS_CONTROL_ALLOW_ORIGIN, ACCESS_CONTROL_EXPOSE_HEADERS, CACHE_CONTROL, CONTENT_LENGTH,\n CONTENT_TYPE, LAST_MODIFIED, X_CONTENT_TYPE_OPTIONS,\n};\nuse reqwest::{Client, StatusCode};\nuse tokio::sync::watch::{channel, Receiver};\nuse tokio::sync::Notify;\n\nuse crate::cache::{Cache, CacheKey, ImageMetadata};\n\npub static HTTP_CLIENT: Lazy\u003cCachingClient\u003e = Lazy::new(|| CachingClient {\n inner: Client::builder()\n .pool_idle_timeout(Duration::from_secs(180))\n .https_only(true)\n .http2_prior_knowledge()\n .build()\n .expect(\"Client initialization to work\"),\n locks: RwLock::new(HashMap::new()),\n});\n\npub static DEFAULT_HEADERS: Lazy\u003cHeaderMap\u003e = Lazy::new(|| {\n let mut headers = HeaderMap::with_capacity(8);\n headers.insert(X_CONTENT_TYPE_OPTIONS, HeaderValue::from_static(\"nosniff\"));\n headers.insert(\n ACCESS_CONTROL_ALLOW_ORIGIN,\n HeaderValue::from_static(\"https://mangadex.org\"),\n );\n headers.insert(ACCESS_CONTROL_EXPOSE_HEADERS, HeaderValue::from_static(\"*\"));\n headers.insert(\n CACHE_CONTROL,\n HeaderValue::from_static(\"public, max-age=1209600\"),\n );\n headers.insert(\n HeaderName::from_static(\"timing-allow-origin\"),\n HeaderValue::from_static(\"https://mangadex.org\"),\n );\n headers\n});\n\npub struct CachingClient {\n inner: Client,\n locks: RwLock\u003cHashMap\u003cString, Receiver\u003cFetchResult\u003e\u003e\u003e,\n}\n\n#[derive(Clone, Debug)]\npub enum FetchResult {\n ServiceUnavailable,\n InternalServerError,\n Data(StatusCode, HeaderMap, Bytes),\n Processing,\n}\n\nimpl CachingClient {\n pub async fn fetch_and_cache(\n \u0026'static self,\n url: String,\n key: CacheKey,\n cache: Data\u003cdyn Cache\u003e,\n ) -\u003e FetchResult {\n if let Some(recv) = self.locks.read().get(\u0026url) {\n let mut recv = recv.clone();\n loop {\n if !matches!(*recv.borrow(), FetchResult::Processing) {\n break;\n }\n if recv.changed().await.is_err() {\n break;\n }\n }\n\n return recv.borrow().clone();\n }\n let url_0 = url.clone();\n\n let notify = Arc::new(Notify::new());\n let notify2 = Arc::clone(\u0026notify);\n\n tokio::spawn(async move {\n let (tx, rx) = channel(FetchResult::Processing);\n\n self.locks.write().insert(url.clone(), rx);\n notify.notify_one();\n let resp = self.inner.get(\u0026url).send().await;\n\n let resp = match resp {\n Ok(mut resp) =\u003e {\n let content_type = resp.headers().get(CONTENT_TYPE);\n\n let is_image = content_type\n .map(|v| String::from_utf8_lossy(v.as_ref()).contains(\"image/\"))\n .unwrap_or_default();\n\n if resp.status() != StatusCode::OK || !is_image {\n warn!(\"Got non-OK or non-image response code from upstream, proxying and not caching result.\");\n\n let mut headers = DEFAULT_HEADERS.clone();\n\n if let Some(content_type) = content_type {\n headers.insert(CONTENT_TYPE, content_type.clone());\n }\n\n FetchResult::Data(\n resp.status(),\n headers,\n resp.bytes().await.unwrap_or_default(),\n )\n } else {\n let (content_type, length, last_mod) = {\n let headers = resp.headers_mut();\n (\n headers.remove(CONTENT_TYPE),\n headers.remove(CONTENT_LENGTH),\n headers.remove(LAST_MODIFIED),\n )\n };\n\n let body = resp.bytes().await.unwrap();\n\n debug!(\"Inserting into cache\");\n\n let metadata = ImageMetadata::new(\n content_type.clone(),\n length.clone(),\n last_mod.clone(),\n )\n .unwrap();\n\n match cache.put(key, body.clone(), metadata).await {\n Ok(()) =\u003e {\n debug!(\"Done putting into cache\");\n\n let mut headers = DEFAULT_HEADERS.clone();\n if let Some(content_type) = content_type {\n headers.insert(CONTENT_TYPE, content_type);\n }\n\n if let Some(content_length) = length {\n headers.insert(CONTENT_LENGTH, content_length);\n }\n\n if let Some(last_modified) = last_mod {\n headers.insert(LAST_MODIFIED, last_modified);\n }\n\n FetchResult::Data(StatusCode::OK, headers, body)\n }\n Err(e) =\u003e {\n warn!(\"Failed to insert into cache: {}\", e);\n FetchResult::InternalServerError\n }\n }\n }\n }\n Err(e) =\u003e {\n error!(\"Failed to fetch image from server: {}\", e);\n FetchResult::ServiceUnavailable\n }\n };\n // This shouldn't happen\n tx.send(resp).unwrap();\n self.locks.write().remove(\u0026url);\n });\n\n notify2.notified().await;\n\n let mut recv = self.locks.read().get(\u0026url_0).unwrap().clone();\n loop {\n if !matches!(*recv.borrow(), FetchResult::Processing) {\n break;\n }\n if recv.changed().await.is_err() {\n break;\n }\n }\n let resp = recv.borrow().clone();\n resp\n }\n\n #[inline]\n pub const fn inner(\u0026self) -\u003e \u0026Client {\n \u0026self.inner\n }\n}\n","traces":[{"line":21,"address":[4514967,4515021,4514688],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":22,"address":[4514817,4514713,4514778],"length":1,"stats":{"Line":0},"fn_name":null},{"line":23,"address":[4514809,4514748,4515036],"length":1,"stats":{"Line":0},"fn_name":null},{"line":28,"address":[4514932],"length":1,"stats":{"Line":0},"fn_name":null},{"line":31,"address":[4515120,4515848],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":32,"address":[4515147],"length":1,"stats":{"Line":0},"fn_name":null},{"line":33,"address":[4515166],"length":1,"stats":{"Line":0},"fn_name":null},{"line":34,"address":[4515322],"length":1,"stats":{"Line":0},"fn_name":null},{"line":36,"address":[4515291],"length":1,"stats":{"Line":0},"fn_name":null},{"line":38,"address":[4515422],"length":1,"stats":{"Line":0},"fn_name":null},{"line":39,"address":[4515584],"length":1,"stats":{"Line":0},"fn_name":null},{"line":41,"address":[4515553],"length":1,"stats":{"Line":0},"fn_name":null},{"line":43,"address":[4515754],"length":1,"stats":{"Line":0},"fn_name":null},{"line":44,"address":[4515684],"length":1,"stats":{"Line":0},"fn_name":null},{"line":45,"address":[4515715],"length":1,"stats":{"Line":0},"fn_name":null},{"line":64,"address":[4515936],"length":1,"stats":{"Line":0},"fn_name":"fetch_and_cache"},{"line":70,"address":[4527088,4526883],"length":1,"stats":{"Line":0},"fn_name":null},{"line":71,"address":[4530331,4527104],"length":1,"stats":{"Line":0},"fn_name":null},{"line":72,"address":[4527638],"length":1,"stats":{"Line":0},"fn_name":null},{"line":73,"address":[4527158,4529955],"length":1,"stats":{"Line":0},"fn_name":null},{"line":74,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":76,"address":[4530234,4527419,4527318,4529941],"length":1,"stats":{"Line":0},"fn_name":null},{"line":77,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":81,"address":[4529918,4527651],"length":1,"stats":{"Line":0},"fn_name":null},{"line":83,"address":[4527767],"length":1,"stats":{"Line":0},"fn_name":null},{"line":85,"address":[4527792],"length":1,"stats":{"Line":0},"fn_name":null},{"line":86,"address":[4527893],"length":1,"stats":{"Line":0},"fn_name":null},{"line":88,"address":[4525631,4516304,4516349,4527962,4524686],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":89,"address":[4516412],"length":1,"stats":{"Line":0},"fn_name":null},{"line":91,"address":[4516584,4524631],"length":1,"stats":{"Line":0},"fn_name":null},{"line":92,"address":[4516828],"length":1,"stats":{"Line":0},"fn_name":null},{"line":93,"address":[4516877,4524613,4525555,4517005],"length":1,"stats":{"Line":0},"fn_name":null},{"line":95,"address":[4517339,4522978],"length":1,"stats":{"Line":0},"fn_name":null},{"line":96,"address":[4517259,4517349],"length":1,"stats":{"Line":0},"fn_name":null},{"line":97,"address":[4517400],"length":1,"stats":{"Line":0},"fn_name":null},{"line":99,"address":[4517517],"length":1,"stats":{"Line":0},"fn_name":null},{"line":100,"address":[4516121,4516112],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":101,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":103,"address":[4517595,4518980,4524930],"length":1,"stats":{"Line":0},"fn_name":null},{"line":104,"address":[4517802,4517887,4517720],"length":1,"stats":{"Line":0},"fn_name":null},{"line":106,"address":[4518055],"length":1,"stats":{"Line":0},"fn_name":null},{"line":108,"address":[4518303,4518110],"length":1,"stats":{"Line":0},"fn_name":null},{"line":109,"address":[4518165],"length":1,"stats":{"Line":0},"fn_name":null},{"line":112,"address":[4518862],"length":1,"stats":{"Line":0},"fn_name":null},{"line":113,"address":[4518313],"length":1,"stats":{"Line":0},"fn_name":null},{"line":114,"address":[4518383],"length":1,"stats":{"Line":0},"fn_name":null},{"line":115,"address":[4518589,4518439,4524488,4525576],"length":1,"stats":{"Line":0},"fn_name":null},{"line":118,"address":[4519385],"length":1,"stats":{"Line":0},"fn_name":null},{"line":119,"address":[4518993,4517774],"length":1,"stats":{"Line":0},"fn_name":null},{"line":121,"address":[4519001],"length":1,"stats":{"Line":0},"fn_name":null},{"line":122,"address":[4519081],"length":1,"stats":{"Line":0},"fn_name":null},{"line":123,"address":[4519161],"length":1,"stats":{"Line":0},"fn_name":null},{"line":127,"address":[4519541,4525597,4524337,4519691],"length":1,"stats":{"Line":0},"fn_name":null},{"line":129,"address":[4520113,4519985],"length":1,"stats":{"Line":0},"fn_name":null},{"line":131,"address":[4520399,4520470],"length":1,"stats":{"Line":0},"fn_name":null},{"line":132,"address":[4520289],"length":1,"stats":{"Line":0},"fn_name":null},{"line":133,"address":[4520331],"length":1,"stats":{"Line":0},"fn_name":null},{"line":134,"address":[4520373],"length":1,"stats":{"Line":0},"fn_name":null},{"line":136,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":138,"address":[4523978,4525618,4525324,4520894,4520508,4521204,4524115,4522533],"length":1,"stats":{"Line":0},"fn_name":null},{"line":139,"address":[4521104],"length":1,"stats":{"Line":0},"fn_name":null},{"line":140,"address":[4521206,4521334],"length":1,"stats":{"Line":0},"fn_name":null},{"line":142,"address":[4521496],"length":1,"stats":{"Line":0},"fn_name":null},{"line":143,"address":[4521546,4521796],"length":1,"stats":{"Line":0},"fn_name":null},{"line":144,"address":[4521640],"length":1,"stats":{"Line":0},"fn_name":null},{"line":147,"address":[4522056,4521806],"length":1,"stats":{"Line":0},"fn_name":null},{"line":148,"address":[4521900],"length":1,"stats":{"Line":0},"fn_name":null},{"line":151,"address":[4522066,4522316],"length":1,"stats":{"Line":0},"fn_name":null},{"line":152,"address":[4522160],"length":1,"stats":{"Line":0},"fn_name":null},{"line":155,"address":[4522318],"length":1,"stats":{"Line":0},"fn_name":null},{"line":157,"address":[4521126],"length":1,"stats":{"Line":0},"fn_name":null},{"line":158,"address":[4522614,4522538,4521158],"length":1,"stats":{"Line":0},"fn_name":null},{"line":159,"address":[4522864],"length":1,"stats":{"Line":0},"fn_name":null},{"line":164,"address":[4517278],"length":1,"stats":{"Line":0},"fn_name":null},{"line":165,"address":[4522983,4523059,4517293],"length":1,"stats":{"Line":0},"fn_name":null},{"line":166,"address":[4523309],"length":1,"stats":{"Line":0},"fn_name":null},{"line":170,"address":[4523346],"length":1,"stats":{"Line":0},"fn_name":null},{"line":171,"address":[4523515,4523719],"length":1,"stats":{"Line":0},"fn_name":null},{"line":174,"address":[4528372,4530255,4528241,4529831],"length":1,"stats":{"Line":0},"fn_name":null},{"line":176,"address":[4528544,4529808],"length":1,"stats":{"Line":0},"fn_name":null},{"line":177,"address":[4529195],"length":1,"stats":{"Line":0},"fn_name":null},{"line":178,"address":[4529773,4528757],"length":1,"stats":{"Line":0},"fn_name":null},{"line":179,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":181,"address":[4528997,4529759,4528899,4530276],"length":1,"stats":{"Line":0},"fn_name":null},{"line":182,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":185,"address":[4529736,4529208],"length":1,"stats":{"Line":0},"fn_name":null},{"line":186,"address":[4529309],"length":1,"stats":{"Line":0},"fn_name":null},{"line":190,"address":[4530880],"length":1,"stats":{"Line":0},"fn_name":"inner"},{"line":191,"address":[],"length":0,"stats":{"Line":0},"fn_name":null}],"covered":0,"coverable":89},{"path":["/","home","edward","Documents","repos","mangadex-home","src","config.rs"],"content":"use std::fmt::{Display, Formatter};\nuse std::fs::{File, OpenOptions};\nuse std::hint::unreachable_unchecked;\nuse std::io::{ErrorKind, Write};\nuse std::net::{IpAddr, Ipv4Addr, SocketAddr};\nuse std::num::NonZeroU16;\nuse std::path::{Path, PathBuf};\nuse std::str::FromStr;\nuse std::sync::atomic::{AtomicBool, Ordering};\n\nuse clap::{crate_authors, crate_description, crate_version, Clap};\nuse log::LevelFilter;\nuse serde::{Deserialize, Serialize};\nuse thiserror::Error;\nuse url::Url;\n\nuse crate::units::{KilobitsPerSecond, Mebibytes, Port};\n\n// Validate tokens is an atomic because it's faster than locking on rwlock.\npub static VALIDATE_TOKENS: AtomicBool = AtomicBool::new(false);\npub static OFFLINE_MODE: AtomicBool = AtomicBool::new(false);\n\n#[derive(Error, Debug)]\npub enum ConfigError {\n #[error(\"No config found. One has been created for you to modify.\")]\n NotInitialized,\n #[error(transparent)]\n Io(#[from] std::io::Error),\n #[error(transparent)]\n Parse(#[from] serde_yaml::Error),\n}\n\npub fn load_config() -\u003e Result\u003cConfig, ConfigError\u003e {\n // Load cli args first\n let cli_args: CliArgs = CliArgs::parse();\n\n // Load yaml file next\n let config_file: Result\u003cYamlArgs, _\u003e = {\n let config_path = cli_args\n .config_path\n .as_deref()\n .unwrap_or_else(|| Path::new(\"./settings.yaml\"));\n match File::open(config_path) {\n Ok(file) =\u003e serde_yaml::from_reader(file),\n Err(e) if e.kind() == ErrorKind::NotFound =\u003e {\n let mut file = OpenOptions::new()\n .write(true)\n .create_new(true)\n .open(config_path)\n .unwrap();\n\n let default_config = include_str!(\"../settings.sample.yaml\");\n file.write_all(default_config.as_bytes()).unwrap();\n\n return Err(ConfigError::NotInitialized);\n }\n Err(e) =\u003e return Err(e.into()),\n }\n };\n\n // generate config\n let config = Config::from_cli_and_file(cli_args, config_file?);\n\n // initialize globals\n OFFLINE_MODE.store(\n config\n .unstable_options\n .contains(\u0026UnstableOptions::OfflineMode),\n Ordering::Release,\n );\n\n Ok(config)\n}\n\n#[derive(Debug)]\n/// Represents a fully parsed config, from a variety of sources.\npub struct Config {\n pub cache_type: CacheType,\n pub cache_path: PathBuf,\n pub shutdown_timeout: NonZeroU16,\n pub log_level: LevelFilter,\n pub client_secret: ClientSecret,\n pub port: Port,\n pub bind_address: SocketAddr,\n pub external_address: Option\u003cSocketAddr\u003e,\n pub ephemeral_disk_encryption: bool,\n pub network_speed: KilobitsPerSecond,\n pub disk_quota: Mebibytes,\n pub memory_quota: Mebibytes,\n pub unstable_options: Vec\u003cUnstableOptions\u003e,\n pub override_upstream: Option\u003cUrl\u003e,\n pub enable_metrics: bool,\n}\n\nimpl Config {\n fn from_cli_and_file(cli_args: CliArgs, file_args: YamlArgs) -\u003e Self {\n let file_extended_options = file_args.extended_options.unwrap_or_default();\n\n let log_level = match (cli_args.quiet, cli_args.verbose) {\n (n, _) if n \u003e 2 =\u003e LevelFilter::Off,\n (2, _) =\u003e LevelFilter::Error,\n (1, _) =\u003e LevelFilter::Warn,\n // Use log level from file if no flags were provided to CLI\n (0, 0) =\u003e file_extended_options\n .logging_level\n .unwrap_or(LevelFilter::Info),\n (_, 1) =\u003e LevelFilter::Debug,\n (_, n) if n \u003e 1 =\u003e LevelFilter::Trace,\n // compiler can't figure it out\n _ =\u003e unsafe { unreachable_unchecked() },\n };\n\n let bind_port = cli_args\n .port\n .unwrap_or(file_args.server_settings.port)\n .get();\n\n // This needs to be outside because rust isn't smart enough yet to\n // realize a disjointed borrow of a moved value is ok. This will be\n // fixed in Rust 2021.\n let external_port = file_args\n .server_settings\n .external_port\n .map_or(bind_port, Port::get);\n\n Self {\n cache_type: cli_args\n .cache_type\n .or(file_extended_options.cache_type)\n .unwrap_or_default(),\n cache_path: cli_args\n .cache_path\n .or(file_extended_options.cache_path)\n .unwrap_or_else(|| PathBuf::from_str(\"./cache\").unwrap()),\n shutdown_timeout: file_args\n .server_settings\n .graceful_shutdown_wait_seconds\n .unwrap_or(unsafe { NonZeroU16::new_unchecked(60) }),\n log_level,\n // secret should never be in CLI\n client_secret: if let Ok(v) = std::env::var(\"CLIENT_SECRET\") {\n ClientSecret(v)\n } else {\n file_args.server_settings.secret\n },\n port: cli_args.port.unwrap_or(file_args.server_settings.port),\n bind_address: SocketAddr::new(\n file_args\n .server_settings\n .hostname\n .unwrap_or_else(|| IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0))),\n bind_port,\n ),\n external_address: file_args\n .server_settings\n .external_ip\n .map(|ip_addr| SocketAddr::new(ip_addr, external_port)),\n ephemeral_disk_encryption: cli_args.ephemeral_disk_encryption\n || file_extended_options\n .ephemeral_disk_encryption\n .unwrap_or_default(),\n network_speed: cli_args\n .network_speed\n .unwrap_or(file_args.server_settings.external_max_kilobits_per_second),\n disk_quota: cli_args\n .disk_quota\n .unwrap_or(file_args.max_cache_size_in_mebibytes),\n memory_quota: cli_args\n .memory_quota\n .or(file_extended_options.memory_quota)\n .unwrap_or_default(),\n enable_metrics: file_extended_options.enable_metrics.unwrap_or_default(),\n\n // Unstable options (and related) should never be in yaml config\n unstable_options: cli_args.unstable_options,\n override_upstream: cli_args.override_upstream,\n }\n }\n}\n\n// this intentionally does not implement display\n#[derive(Deserialize, Serialize, Clone)]\npub struct ClientSecret(String);\n\nimpl std::fmt::Debug for ClientSecret {\n fn fmt(\u0026self, f: \u0026mut Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n write!(f, \"[client secret]\")\n }\n}\n\n#[derive(Deserialize, Copy, Clone, Debug)]\n#[serde(rename_all = \"snake_case\")]\npub enum CacheType {\n OnDisk,\n Lru,\n Lfu,\n}\n\nimpl FromStr for CacheType {\n type Err = String;\n\n fn from_str(s: \u0026str) -\u003e Result\u003cSelf, Self::Err\u003e {\n match s {\n \"on_disk\" =\u003e Ok(Self::OnDisk),\n \"lru\" =\u003e Ok(Self::Lru),\n \"lfu\" =\u003e Ok(Self::Lfu),\n _ =\u003e Err(format!(\"Unknown option: {}\", s)),\n }\n }\n}\n\nimpl Default for CacheType {\n fn default() -\u003e Self {\n Self::OnDisk\n }\n}\n\n#[derive(Deserialize)]\nstruct YamlArgs {\n // Naming is legacy\n max_cache_size_in_mebibytes: Mebibytes,\n server_settings: YamlServerSettings,\n // This implementation custom options\n extended_options: Option\u003cYamlExtendedOptions\u003e,\n}\n\n// Naming is legacy\n#[derive(Deserialize)]\nstruct YamlServerSettings {\n secret: ClientSecret,\n #[serde(default)]\n port: Port,\n external_max_kilobits_per_second: KilobitsPerSecond,\n external_port: Option\u003cPort\u003e,\n graceful_shutdown_wait_seconds: Option\u003cNonZeroU16\u003e,\n hostname: Option\u003cIpAddr\u003e,\n external_ip: Option\u003cIpAddr\u003e,\n}\n\n#[derive(Deserialize, Default)]\nstruct YamlExtendedOptions {\n memory_quota: Option\u003cMebibytes\u003e,\n cache_type: Option\u003cCacheType\u003e,\n ephemeral_disk_encryption: Option\u003cbool\u003e,\n enable_metrics: Option\u003cbool\u003e,\n logging_level: Option\u003cLevelFilter\u003e,\n cache_path: Option\u003cPathBuf\u003e,\n}\n\n#[derive(Clap, Clone)]\n#[clap(version = crate_version!(), author = crate_authors!(), about = crate_description!())]\nstruct CliArgs {\n /// The port to listen on.\n #[clap(short, long)]\n pub port: Option\u003cPort\u003e,\n /// How large, in mebibytes, the in-memory cache should be. Note that this\n /// does not include runtime memory usage.\n #[clap(long)]\n pub memory_quota: Option\u003cMebibytes\u003e,\n /// How large, in mebibytes, the on-disk cache should be. Note that actual\n /// values may be larger for metadata information.\n #[clap(long)]\n pub disk_quota: Option\u003cMebibytes\u003e,\n /// Sets the location of the disk cache.\n #[clap(long)]\n pub cache_path: Option\u003cPathBuf\u003e,\n /// The network speed to advertise to Mangadex@Home control server.\n #[clap(long)]\n pub network_speed: Option\u003cKilobitsPerSecond\u003e,\n /// Changes verbosity. Default verbosity is INFO, while increasing counts of\n /// verbose flags increases the verbosity to DEBUG and TRACE, respectively.\n #[clap(short, long, parse(from_occurrences), conflicts_with = \"quiet\")]\n pub verbose: usize,\n /// Changes verbosity. Default verbosity is INFO, while increasing counts of\n /// quiet flags decreases the verbosity to WARN, ERROR, and no logs,\n /// respectively.\n #[clap(short, long, parse(from_occurrences), conflicts_with = \"verbose\")]\n pub quiet: usize,\n #[clap(short = 'Z', long)]\n pub unstable_options: Vec\u003cUnstableOptions\u003e,\n #[clap(long)]\n pub override_upstream: Option\u003cUrl\u003e,\n /// Enables ephemeral disk encryption. Items written to disk are first\n /// encrypted with a key generated at runtime. There are implications to\n /// performance, privacy, and usability with this flag enabled.\n #[clap(short, long)]\n pub ephemeral_disk_encryption: bool,\n #[clap(short, long)]\n pub config_path: Option\u003cPathBuf\u003e,\n #[clap(short = 't', long)]\n pub cache_type: Option\u003cCacheType\u003e,\n}\n\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\npub enum UnstableOptions {\n /// Overrides the upstream URL to fetch images from. Don't use this unless\n /// you know what you're dealing with.\n OverrideUpstream,\n\n /// Disables token validation. Don't use this unless you know the\n /// ramifications of this command.\n DisableTokenValidation,\n\n /// Tries to run without communication to MangaDex.\n OfflineMode,\n\n /// Serves HTTP in plaintext\n DisableTls,\n}\n\nimpl FromStr for UnstableOptions {\n type Err = String;\n\n fn from_str(s: \u0026str) -\u003e Result\u003cSelf, Self::Err\u003e {\n match s {\n \"override-upstream\" =\u003e Ok(Self::OverrideUpstream),\n \"disable-token-validation\" =\u003e Ok(Self::DisableTokenValidation),\n \"offline-mode\" =\u003e Ok(Self::OfflineMode),\n \"disable-tls\" =\u003e Ok(Self::DisableTls),\n _ =\u003e Err(format!(\"Unknown unstable option '{}'\", s)),\n }\n }\n}\n\nimpl Display for UnstableOptions {\n fn fmt(\u0026self, f: \u0026mut Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n match self {\n Self::OverrideUpstream =\u003e write!(f, \"override-upstream\"),\n Self::DisableTokenValidation =\u003e write!(f, \"disable-token-validation\"),\n Self::OfflineMode =\u003e write!(f, \"offline-mode\"),\n Self::DisableTls =\u003e write!(f, \"disable-tls\"),\n }\n }\n}\n\n#[cfg(test)]\nmod sample_yaml {\n use crate::config::YamlArgs;\n\n #[test]\n fn sample_yaml_parses() {\n assert!(serde_yaml::from_str::\u003cYamlArgs\u003e(include_str!(\"../settings.sample.yaml\")).is_ok());\n }\n}\n","traces":[{"line":33,"address":[4744180,4742464],"length":1,"stats":{"Line":0},"fn_name":"load_config"},{"line":35,"address":[4742487],"length":1,"stats":{"Line":0},"fn_name":null},{"line":39,"address":[4742540],"length":1,"stats":{"Line":0},"fn_name":null},{"line":42,"address":[4744548,4744544],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":43,"address":[4742718,4742684],"length":1,"stats":{"Line":0},"fn_name":null},{"line":44,"address":[4742720,4742699],"length":1,"stats":{"Line":0},"fn_name":null},{"line":45,"address":[4742892],"length":1,"stats":{"Line":0},"fn_name":null},{"line":46,"address":[4743123,4743006],"length":1,"stats":{"Line":0},"fn_name":null},{"line":52,"address":[4743296],"length":1,"stats":{"Line":0},"fn_name":null},{"line":53,"address":[4743399],"length":1,"stats":{"Line":0},"fn_name":null},{"line":55,"address":[4743518],"length":1,"stats":{"Line":0},"fn_name":null},{"line":57,"address":[4743591,4743036],"length":1,"stats":{"Line":0},"fn_name":null},{"line":62,"address":[4744039,4744133,4742765,4744210,4743639],"length":1,"stats":{"Line":0},"fn_name":null},{"line":65,"address":[4743922],"length":1,"stats":{"Line":0},"fn_name":null},{"line":66,"address":[4743849],"length":1,"stats":{"Line":0},"fn_name":null},{"line":69,"address":[4743914],"length":1,"stats":{"Line":0},"fn_name":null},{"line":72,"address":[4743950],"length":1,"stats":{"Line":0},"fn_name":null},{"line":96,"address":[4746720,4744592],"length":1,"stats":{"Line":0},"fn_name":"from_cli_and_file"},{"line":97,"address":[4744625],"length":1,"stats":{"Line":0},"fn_name":null},{"line":99,"address":[4744971,4744908,4744988,4744792,4745055,4745116],"length":1,"stats":{"Line":0},"fn_name":null},{"line":100,"address":[4744816,4744880],"length":1,"stats":{"Line":0},"fn_name":null},{"line":101,"address":[4744913],"length":1,"stats":{"Line":0},"fn_name":null},{"line":102,"address":[4744976],"length":1,"stats":{"Line":0},"fn_name":null},{"line":104,"address":[4747078,4744845,4744993,4745013],"length":1,"stats":{"Line":0},"fn_name":null},{"line":105,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":106,"address":[4745001],"length":1,"stats":{"Line":0},"fn_name":null},{"line":107,"address":[4744860,4745043],"length":1,"stats":{"Line":0},"fn_name":null},{"line":108,"address":[4745057],"length":1,"stats":{"Line":0},"fn_name":null},{"line":113,"address":[4745138],"length":1,"stats":{"Line":0},"fn_name":null},{"line":114,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":115,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":121,"address":[4745203],"length":1,"stats":{"Line":0},"fn_name":null},{"line":122,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":123,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":124,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":127,"address":[4745243],"length":1,"stats":{"Line":0},"fn_name":null},{"line":131,"address":[4745391,4745295],"length":1,"stats":{"Line":0},"fn_name":null},{"line":135,"address":[4745426,4745465],"length":1,"stats":{"Line":0},"fn_name":null},{"line":141,"address":[4745598,4745499],"length":1,"stats":{"Line":0},"fn_name":null},{"line":146,"address":[4745765],"length":1,"stats":{"Line":0},"fn_name":null},{"line":147,"address":[4745844],"length":1,"stats":{"Line":0},"fn_name":null},{"line":154,"address":[4745917,4745879],"length":1,"stats":{"Line":0},"fn_name":null},{"line":158,"address":[4746034,4745953],"length":1,"stats":{"Line":0},"fn_name":null},{"line":162,"address":[4746007,4746015],"length":1,"stats":{"Line":0},"fn_name":null},{"line":165,"address":[4746066,4746055],"length":1,"stats":{"Line":0},"fn_name":null},{"line":168,"address":[4746086,4746109],"length":1,"stats":{"Line":0},"fn_name":null},{"line":172,"address":[4746157],"length":1,"stats":{"Line":0},"fn_name":null},{"line":175,"address":[4746187],"length":1,"stats":{"Line":0},"fn_name":null},{"line":176,"address":[4746231],"length":1,"stats":{"Line":0},"fn_name":null},{"line":186,"address":[4747472],"length":1,"stats":{"Line":0},"fn_name":"fmt"},{"line":187,"address":[4747491],"length":1,"stats":{"Line":0},"fn_name":null},{"line":202,"address":[4747568],"length":1,"stats":{"Line":0},"fn_name":"from_str"},{"line":203,"address":[4747822,4747788,4747805],"length":1,"stats":{"Line":0},"fn_name":null},{"line":204,"address":[4747781,4747595],"length":1,"stats":{"Line":0},"fn_name":null},{"line":205,"address":[4747640,4747798],"length":1,"stats":{"Line":0},"fn_name":null},{"line":206,"address":[4747681,4747815],"length":1,"stats":{"Line":0},"fn_name":null},{"line":207,"address":[4747837,4747722],"length":1,"stats":{"Line":0},"fn_name":null},{"line":213,"address":[4747984],"length":1,"stats":{"Line":0},"fn_name":"default"},{"line":214,"address":[4747988],"length":1,"stats":{"Line":0},"fn_name":null},{"line":314,"address":[4748000],"length":1,"stats":{"Line":0},"fn_name":"from_str"},{"line":315,"address":[4748286,4748320,4748269,4748303],"length":1,"stats":{"Line":0},"fn_name":null},{"line":316,"address":[4748262,4748027],"length":1,"stats":{"Line":0},"fn_name":null},{"line":317,"address":[4748072,4748279],"length":1,"stats":{"Line":0},"fn_name":null},{"line":318,"address":[4748117,4748296],"length":1,"stats":{"Line":0},"fn_name":null},{"line":319,"address":[4748162,4748313],"length":1,"stats":{"Line":0},"fn_name":null},{"line":320,"address":[4748335,4748203],"length":1,"stats":{"Line":0},"fn_name":null},{"line":326,"address":[4748496],"length":1,"stats":{"Line":0},"fn_name":"fmt"},{"line":327,"address":[4748594],"length":1,"stats":{"Line":0},"fn_name":null},{"line":328,"address":[4748596,4748523],"length":1,"stats":{"Line":0},"fn_name":null},{"line":329,"address":[4748656],"length":1,"stats":{"Line":0},"fn_name":null},{"line":330,"address":[4748713],"length":1,"stats":{"Line":0},"fn_name":null},{"line":331,"address":[4748552,4748774],"length":1,"stats":{"Line":0},"fn_name":null},{"line":341,"address":[6338144,6338269,6338112,6338117],"length":1,"stats":{"Line":3},"fn_name":"{{closure}}"},{"line":342,"address":[6338259,6338151],"length":1,"stats":{"Line":1},"fn_name":null}],"covered":2,"coverable":74},{"path":["/","home","edward","Documents","repos","mangadex-home","src","main.rs"],"content":"#![warn(clippy::pedantic, clippy::nursery)]\n// We're end users, so these is ok\n#![allow(clippy::module_name_repetitions)]\n\nuse std::env::VarError;\nuse std::error::Error;\nuse std::fmt::Display;\nuse std::num::ParseIntError;\nuse std::sync::atomic::{AtomicBool, Ordering};\nuse std::sync::Arc;\nuse std::time::Duration;\n\nuse actix_web::rt::{spawn, time, System};\nuse actix_web::web::{self, Data};\nuse actix_web::{App, HttpResponse, HttpServer};\nuse cache::{Cache, DiskCache};\nuse config::Config;\nuse log::{debug, error, info, warn};\nuse parking_lot::RwLock;\nuse rustls::{NoClientAuth, ServerConfig};\nuse simple_logger::SimpleLogger;\nuse sodiumoxide::crypto::secretstream::gen_key;\nuse state::{RwLockServerState, ServerState};\nuse stop::send_stop;\nuse thiserror::Error;\n\nuse crate::cache::mem::{Lfu, Lru};\nuse crate::cache::{MemoryCache, ENCRYPTION_KEY};\nuse crate::config::{CacheType, UnstableOptions, OFFLINE_MODE};\nuse crate::state::DynamicServerCert;\n\nmod cache;\nmod client;\nmod config;\nmod metrics;\nmod ping;\nmod routes;\nmod state;\nmod stop;\nmod units;\n\nconst CLIENT_API_VERSION: usize = 31;\n\n#[derive(Error, Debug)]\nenum ServerError {\n #[error(\"There was a failure parsing config\")]\n Config(#[from] VarError),\n #[error(\"Failed to parse an int\")]\n ParseInt(#[from] ParseIntError),\n}\n\n#[actix_web::main]\nasync fn main() -\u003e Result\u003c(), Box\u003cdyn Error\u003e\u003e {\n sodiumoxide::init().expect(\"Failed to initialize crypto\");\n // It's ok to fail early here, it would imply we have a invalid config.\n dotenv::dotenv().ok();\n\n //\n // Config loading\n //\n\n let config = match config::load_config() {\n Ok(c) =\u003e c,\n Err(e) =\u003e {\n eprintln!(\"{}\", e);\n return Err(Box::new(e) as Box\u003c_\u003e);\n }\n };\n\n let memory_quota = config.memory_quota;\n let disk_quota = config.disk_quota;\n let cache_type = config.cache_type;\n let cache_path = config.cache_path.clone();\n let disable_tls = config\n .unstable_options\n .contains(\u0026UnstableOptions::DisableTls);\n let bind_address = config.bind_address;\n\n //\n // Logging and warnings\n //\n\n SimpleLogger::new().with_level(config.log_level).init()?;\n\n if let Err(e) = print_preamble_and_warnings(\u0026config) {\n error!(\"{}\", e);\n return Err(e);\n }\n\n debug!(\"{:?}\", \u0026config);\n\n let client_secret = config.client_secret.clone();\n let client_secret_1 = config.client_secret.clone();\n\n if config.ephemeral_disk_encryption {\n info!(\"Running with at-rest encryption!\");\n ENCRYPTION_KEY.set(gen_key()).unwrap();\n }\n\n if config.enable_metrics {\n metrics::init();\n }\n\n // HTTP Server init\n\n let server = if OFFLINE_MODE.load(Ordering::Acquire) {\n ServerState::init_offline()\n } else {\n ServerState::init(\u0026client_secret, \u0026config).await?\n };\n let data_0 = Arc::new(RwLockServerState(RwLock::new(server)));\n let data_1 = Arc::clone(\u0026data_0);\n\n //\n // At this point, the server is ready to start, and starts the necessary\n // threads.\n //\n\n // Set ctrl+c to send a stop message\n let running = Arc::new(AtomicBool::new(true));\n let running_1 = running.clone();\n let system = System::current();\n ctrlc::set_handler(move || {\n let system = \u0026system;\n let client_secret = client_secret.clone();\n let running_2 = Arc::clone(\u0026running_1);\n if !OFFLINE_MODE.load(Ordering::Acquire) {\n System::new().block_on(async move {\n if running_2.load(Ordering::SeqCst) {\n send_stop(\u0026client_secret).await;\n } else {\n warn!(\"Got second Ctrl-C, forcefully exiting\");\n system.stop();\n }\n });\n }\n running_1.store(false, Ordering::SeqCst);\n })\n .expect(\"Error setting Ctrl-C handler\");\n\n // Spawn ping task\n if !OFFLINE_MODE.load(Ordering::Acquire) {\n spawn(async move {\n let mut interval = time::interval(Duration::from_secs(90));\n let mut data = Arc::clone(\u0026data_0);\n loop {\n interval.tick().await;\n debug!(\"Sending ping!\");\n ping::update_server_state(\u0026client_secret_1, \u0026config, \u0026mut data).await;\n }\n });\n }\n\n let memory_max_size = memory_quota.into();\n let cache = DiskCache::new(disk_quota.into(), cache_path.clone()).await;\n let cache: Arc\u003cdyn Cache\u003e = match cache_type {\n CacheType::OnDisk =\u003e cache,\n CacheType::Lru =\u003e MemoryCache::\u003cLfu, _\u003e::new(cache, memory_max_size).await,\n CacheType::Lfu =\u003e MemoryCache::\u003cLru, _\u003e::new(cache, memory_max_size).await,\n };\n\n let cache_0 = Arc::clone(\u0026cache);\n\n // Start HTTPS server\n let server = HttpServer::new(move || {\n App::new()\n .service(routes::index)\n .service(routes::token_data)\n .service(routes::token_data_saver)\n .service(routes::metrics)\n .route(\n \"/data/{tail:.*}\",\n web::get().to(HttpResponse::UnavailableForLegalReasons),\n )\n .route(\n \"/data-saver/{tail:.*}\",\n web::get().to(HttpResponse::UnavailableForLegalReasons),\n )\n .route(\"{tail:.*}\", web::get().to(routes::default))\n .app_data(Data::from(Arc::clone(\u0026data_1)))\n .app_data(Data::from(Arc::clone(\u0026cache_0)))\n })\n .shutdown_timeout(60);\n\n if disable_tls {\n server.bind(bind_address)?.run().await?;\n } else {\n // Rustls only supports TLS 1.2 and 1.3.\n let tls_config = {\n let mut tls_config = ServerConfig::new(NoClientAuth::new());\n tls_config.cert_resolver = Arc::new(DynamicServerCert);\n tls_config\n };\n\n server.bind_rustls(bind_address, tls_config)?.run().await?;\n }\n\n // Waiting for us to finish sending stop message\n while running.load(Ordering::SeqCst) {\n std::thread::sleep(Duration::from_millis(250));\n }\n\n Ok(())\n}\n\n#[derive(Debug)]\nenum InvalidCombination {\n MissingUnstableOption(\u0026'static str, UnstableOptions),\n}\n\nimpl Display for InvalidCombination {\n fn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n match self {\n InvalidCombination::MissingUnstableOption(opt, arg) =\u003e {\n write!(\n f,\n \"The option '{}' requires the unstable option '-Z {}'\",\n opt, arg\n )\n }\n }\n }\n}\n\nimpl Error for InvalidCombination {}\n\nfn print_preamble_and_warnings(args: \u0026Config) -\u003e Result\u003c(), Box\u003cdyn Error\u003e\u003e {\n println!(concat!(\n env!(\"CARGO_PKG_NAME\"),\n \" \",\n env!(\"CARGO_PKG_VERSION\"),\n \" (\",\n env!(\"VERGEN_GIT_SHA_SHORT\"),\n \")\",\n \" Copyright (C) 2021 \",\n env!(\"CARGO_PKG_AUTHORS\"),\n \"\\n\\n\",\n env!(\"CARGO_PKG_NAME\"),\n \" is free software: you can redistribute it and/or modify\\n\\\n it under the terms of the GNU General Public License as published by\\n\\\n the Free Software Foundation, either version 3 of the License, or\\n\\\n (at your option) any later version.\\n\\n\",\n env!(\"CARGO_PKG_NAME\"),\n \" is distributed in the hope that it will be useful,\\n\\\n but WITHOUT ANY WARRANTY; without even the implied warranty of\\n\\\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\\n\\\n GNU General Public License for more details.\\n\\n\\\n You should have received a copy of the GNU General Public License\\n\\\n along with \",\n env!(\"CARGO_PKG_NAME\"),\n \". If not, see \u003chttps://www.gnu.org/licenses/\u003e.\\n\"\n ));\n\n if args.ephemeral_disk_encryption {\n error!(\"Encrypted files are _very_ broken; caveat emptor!\");\n }\n\n if !args.unstable_options.is_empty() {\n warn!(\"Unstable options are enabled. These options should not be used in production!\");\n }\n\n if args\n .unstable_options\n .contains(\u0026UnstableOptions::OfflineMode)\n {\n warn!(\"Running in offline mode. No communication to MangaDex will be made!\");\n }\n\n if args.unstable_options.contains(\u0026UnstableOptions::DisableTls) {\n warn!(\"Serving insecure traffic! You better be running this for development only.\");\n }\n\n if args.override_upstream.is_some()\n \u0026\u0026 !args\n .unstable_options\n .contains(\u0026UnstableOptions::OverrideUpstream)\n {\n Err(Box::new(InvalidCombination::MissingUnstableOption(\n \"override-upstream\",\n UnstableOptions::OverrideUpstream,\n )))\n } else {\n Ok(())\n }\n}\n","traces":[{"line":52,"address":[5358153,5345797,5359641,5359929,5359789,5349925,5349856,5358413],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":53,"address":[5345776],"length":1,"stats":{"Line":0},"fn_name":"main"},{"line":54,"address":[5349988],"length":1,"stats":{"Line":0},"fn_name":null},{"line":56,"address":[5350109],"length":1,"stats":{"Line":0},"fn_name":null},{"line":62,"address":[5350182,5350315],"length":1,"stats":{"Line":0},"fn_name":null},{"line":63,"address":[5350189,5350317],"length":1,"stats":{"Line":0},"fn_name":null},{"line":64,"address":[5350204],"length":1,"stats":{"Line":0},"fn_name":null},{"line":65,"address":[5350252,5350490],"length":1,"stats":{"Line":0},"fn_name":null},{"line":66,"address":[5350696,5350566],"length":1,"stats":{"Line":0},"fn_name":null},{"line":70,"address":[5350420],"length":1,"stats":{"Line":0},"fn_name":null},{"line":71,"address":[5350431],"length":1,"stats":{"Line":0},"fn_name":null},{"line":72,"address":[5350442],"length":1,"stats":{"Line":0},"fn_name":null},{"line":73,"address":[5350454],"length":1,"stats":{"Line":0},"fn_name":null},{"line":74,"address":[5350740],"length":1,"stats":{"Line":0},"fn_name":null},{"line":77,"address":[5350866],"length":1,"stats":{"Line":0},"fn_name":null},{"line":83,"address":[5351082,5359829,5350894],"length":1,"stats":{"Line":0},"fn_name":null},{"line":85,"address":[5351177,5351237,5351053],"length":1,"stats":{"Line":0},"fn_name":null},{"line":86,"address":[5351269,5351397],"length":1,"stats":{"Line":0},"fn_name":null},{"line":87,"address":[5351668],"length":1,"stats":{"Line":0},"fn_name":null},{"line":90,"address":[5351705,5351833],"length":1,"stats":{"Line":0},"fn_name":null},{"line":92,"address":[5352128],"length":1,"stats":{"Line":0},"fn_name":null},{"line":93,"address":[5352164],"length":1,"stats":{"Line":0},"fn_name":null},{"line":95,"address":[5352252,5352581,5352200],"length":1,"stats":{"Line":0},"fn_name":null},{"line":96,"address":[5352209,5352257,5352342],"length":1,"stats":{"Line":0},"fn_name":null},{"line":97,"address":[5352510],"length":1,"stats":{"Line":0},"fn_name":null},{"line":100,"address":[5352607,5352591],"length":1,"stats":{"Line":0},"fn_name":null},{"line":101,"address":[5352600],"length":1,"stats":{"Line":0},"fn_name":null},{"line":106,"address":[5353123,5352611],"length":1,"stats":{"Line":0},"fn_name":null},{"line":107,"address":[5352671,5359850],"length":1,"stats":{"Line":0},"fn_name":null},{"line":109,"address":[5359671,5352793,5353128,5352689,5358779],"length":1,"stats":{"Line":0},"fn_name":null},{"line":111,"address":[5353349],"length":1,"stats":{"Line":0},"fn_name":null},{"line":112,"address":[5353494],"length":1,"stats":{"Line":0},"fn_name":null},{"line":120,"address":[5353575],"length":1,"stats":{"Line":0},"fn_name":null},{"line":121,"address":[5353698],"length":1,"stats":{"Line":0},"fn_name":null},{"line":122,"address":[5353758],"length":1,"stats":{"Line":0},"fn_name":null},{"line":123,"address":[5353785,5346960,5347398],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":124,"address":[5346983],"length":1,"stats":{"Line":0},"fn_name":null},{"line":125,"address":[5347015],"length":1,"stats":{"Line":0},"fn_name":null},{"line":126,"address":[5347042],"length":1,"stats":{"Line":0},"fn_name":null},{"line":127,"address":[5347141,5347281,5347083],"length":1,"stats":{"Line":0},"fn_name":null},{"line":128,"address":[5347151,5345952,5345985,5346775,5346624,5347125],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":129,"address":[5346036,5346601,5346344],"length":1,"stats":{"Line":0},"fn_name":null},{"line":130,"address":[5346219,5346098,5346762,5346608,5346162],"length":1,"stats":{"Line":0},"fn_name":null},{"line":132,"address":[5346114,5346349,5346425],"length":1,"stats":{"Line":0},"fn_name":null},{"line":133,"address":[5346586],"length":1,"stats":{"Line":0},"fn_name":null},{"line":137,"address":[5347288],"length":1,"stats":{"Line":0},"fn_name":null},{"line":142,"address":[5354115,5353934,5354178],"length":1,"stats":{"Line":0},"fn_name":null},{"line":143,"address":[5354125,5348606,5348708,5353996,5348736,5347568,5347601],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":144,"address":[5347658],"length":1,"stats":{"Line":0},"fn_name":null},{"line":145,"address":[5347716,5348728],"length":1,"stats":{"Line":0},"fn_name":null},{"line":146,"address":[5348542],"length":1,"stats":{"Line":0},"fn_name":null},{"line":147,"address":[5347753,5348677,5347884],"length":1,"stats":{"Line":0},"fn_name":null},{"line":148,"address":[5348025,5348141],"length":1,"stats":{"Line":0},"fn_name":null},{"line":149,"address":[5348695,5348302,5348408,5348552],"length":1,"stats":{"Line":0},"fn_name":null},{"line":154,"address":[5354188],"length":1,"stats":{"Line":0},"fn_name":null},{"line":155,"address":[5354254,5354419,5359692,5358636],"length":1,"stats":{"Line":0},"fn_name":null},{"line":156,"address":[5354735,5354702,5355096,5355427],"length":1,"stats":{"Line":0},"fn_name":null},{"line":157,"address":[5354623,5354712],"length":1,"stats":{"Line":0},"fn_name":null},{"line":158,"address":[5354877,5358614,5359713,5354756],"length":1,"stats":{"Line":0},"fn_name":null},{"line":159,"address":[5358592,5355109,5359734,5354677,5355200],"length":1,"stats":{"Line":0},"fn_name":null},{"line":162,"address":[5355467],"length":1,"stats":{"Line":0},"fn_name":null},{"line":165,"address":[5355536,5349575,5348864],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":166,"address":[5349412,5348894,5349191,5349300,5349082,5349534],"length":1,"stats":{"Line":0},"fn_name":null},{"line":170,"address":[5349032],"length":1,"stats":{"Line":0},"fn_name":null},{"line":173,"address":[5349040],"length":1,"stats":{"Line":0},"fn_name":null},{"line":177,"address":[5349149],"length":1,"stats":{"Line":0},"fn_name":null},{"line":179,"address":[5349641,5349258,5349356],"length":1,"stats":{"Line":0},"fn_name":null},{"line":180,"address":[5349614,5349445,5349372],"length":1,"stats":{"Line":0},"fn_name":null},{"line":181,"address":[5349559,5349590,5349461],"length":1,"stats":{"Line":0},"fn_name":null},{"line":185,"address":[5355640,5357628,5356589],"length":1,"stats":{"Line":0},"fn_name":null},{"line":186,"address":[5359396,5355815,5356594,5358511,5356226,5359755,5359871,5355657],"length":1,"stats":{"Line":0},"fn_name":null},{"line":190,"address":[5355771,5356771],"length":1,"stats":{"Line":0},"fn_name":null},{"line":191,"address":[5356790,5358937],"length":1,"stats":{"Line":0},"fn_name":null},{"line":192,"address":[5359045],"length":1,"stats":{"Line":0},"fn_name":null},{"line":195,"address":[5357633,5359908,5358456,5356854,5359391,5359063,5359776,5357265],"length":1,"stats":{"Line":0},"fn_name":null},{"line":199,"address":[5357983,5357802],"length":1,"stats":{"Line":0},"fn_name":null},{"line":200,"address":[5357930],"length":1,"stats":{"Line":0},"fn_name":null},{"line":203,"address":[5357892],"length":1,"stats":{"Line":0},"fn_name":null},{"line":212,"address":[5343264],"length":1,"stats":{"Line":0},"fn_name":"fmt"},{"line":213,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":214,"address":[5343292],"length":1,"stats":{"Line":0},"fn_name":null},{"line":215,"address":[5343309],"length":1,"stats":{"Line":0},"fn_name":null},{"line":216,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":218,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":227,"address":[5343552],"length":1,"stats":{"Line":0},"fn_name":"print_preamble_and_warnings"},{"line":228,"address":[5343572],"length":1,"stats":{"Line":0},"fn_name":null},{"line":254,"address":[5343622,5343669,5343872],"length":1,"stats":{"Line":0},"fn_name":null},{"line":255,"address":[5343631,5343728,5343674],"length":1,"stats":{"Line":0},"fn_name":null},{"line":258,"address":[5344145,5343879,5343942],"length":1,"stats":{"Line":0},"fn_name":null},{"line":259,"address":[5343947,5343904,5344001],"length":1,"stats":{"Line":0},"fn_name":null},{"line":262,"address":[5344152,5344245,5344448],"length":1,"stats":{"Line":0},"fn_name":null},{"line":266,"address":[5344250,5344304,5344207],"length":1,"stats":{"Line":0},"fn_name":null},{"line":269,"address":[5344548,5344751,5344455],"length":1,"stats":{"Line":0},"fn_name":null},{"line":270,"address":[5344553,5344510,5344607],"length":1,"stats":{"Line":0},"fn_name":null},{"line":273,"address":[5344867,5344981,5344810,5344758],"length":1,"stats":{"Line":0},"fn_name":null},{"line":274,"address":[5344845,5344789],"length":1,"stats":{"Line":0},"fn_name":null},{"line":278,"address":[5344878,5344970,5344987],"length":1,"stats":{"Line":0},"fn_name":null},{"line":283,"address":[5344972],"length":1,"stats":{"Line":0},"fn_name":null}],"covered":0,"coverable":98},{"path":["/","home","edward","Documents","repos","mangadex-home","src","metrics.rs"],"content":"use once_cell::sync::Lazy;\nuse prometheus::{register_int_counter, IntCounter};\n\nmacro_rules! init_counters {\n ($(($counter:ident, $ty:ty, $name:literal, $desc:literal),)*) =\u003e {\n $(\n pub static $counter: Lazy\u003c$ty\u003e = Lazy::new(|| {\n register_int_counter!($name, $desc).unwrap()\n });\n )*\n\n #[allow(clippy::shadow_unrelated)]\n pub fn init() {\n $(let _a = $counter.get();)*\n }\n };\n}\n\ninit_counters!(\n (\n CACHE_HIT_COUNTER,\n IntCounter,\n \"cache_hit\",\n \"The number of cache hits.\"\n ),\n (\n CACHE_MISS_COUNTER,\n IntCounter,\n \"cache_miss\",\n \"The number of cache misses.\"\n ),\n (\n REQUESTS_TOTAL_COUNTER,\n IntCounter,\n \"requests_total\",\n \"The total number of requests served.\"\n ),\n (\n REQUESTS_DATA_COUNTER,\n IntCounter,\n \"requests_data\",\n \"The number of requests served from the /data endpoint.\"\n ),\n (\n REQUESTS_DATA_SAVER_COUNTER,\n IntCounter,\n \"requests_data_saver\",\n \"The number of requests served from the /data-saver endpoint.\"\n ),\n (\n REQUESTS_OTHER_COUNTER,\n IntCounter,\n \"requests_other\",\n \"The total number of request not served by primary endpoints.\"\n ),\n);\n","traces":[{"line":7,"address":[5162080,5161948,5160256,5161340,5162688,5160864,5162556,5163296,5163164,5163772,5160732,5161472],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":8,"address":[5162438,5161963,5162095,5161487,5161222,5160271,5163179,5160747,5163046,5163654,5160614,5161355,5162703,5161830,5160879,5162571,5163787,5163311],"length":1,"stats":{"Line":0},"fn_name":null},{"line":13,"address":[5163888],"length":1,"stats":{"Line":0},"fn_name":"init"},{"line":14,"address":[5163892],"length":1,"stats":{"Line":0},"fn_name":null}],"covered":0,"coverable":4},{"path":["/","home","edward","Documents","repos","mangadex-home","src","ping.rs"],"content":"use std::sync::atomic::Ordering;\nuse std::{io::BufReader, sync::Arc};\n\nuse log::{debug, error, info, warn};\nuse rustls::internal::pemfile::{certs, rsa_private_keys};\nuse rustls::sign::{RSASigningKey, SigningKey};\nuse rustls::Certificate;\nuse serde::de::{MapAccess, Visitor};\nuse serde::{Deserialize, Serialize};\nuse serde_repr::Deserialize_repr;\nuse sodiumoxide::crypto::box_::PrecomputedKey;\nuse url::Url;\n\nuse crate::config::{ClientSecret, Config, UnstableOptions, VALIDATE_TOKENS};\nuse crate::state::{\n RwLockServerState, PREVIOUSLY_COMPROMISED, PREVIOUSLY_PAUSED, TLS_CERTS,\n TLS_PREVIOUSLY_CREATED, TLS_SIGNING_KEY,\n};\nuse crate::units::{BytesPerSecond, Mebibytes, Port};\nuse crate::CLIENT_API_VERSION;\n\npub const CONTROL_CENTER_PING_URL: \u0026str = \"https://api.mangadex.network/ping\";\n\n#[derive(Serialize)]\npub struct Request\u003c'a\u003e {\n secret: \u0026'a ClientSecret,\n port: Port,\n disk_space: Mebibytes,\n network_speed: BytesPerSecond,\n build_version: usize,\n tls_created_at: Option\u003cString\u003e,\n}\n\nimpl\u003c'a\u003e Request\u003c'a\u003e {\n fn from_config_and_state(secret: \u0026'a ClientSecret, config: \u0026Config) -\u003e Self {\n Self {\n secret,\n port: config.port,\n disk_space: config.disk_quota,\n network_speed: config.network_speed.into(),\n build_version: CLIENT_API_VERSION,\n tls_created_at: TLS_PREVIOUSLY_CREATED\n .get()\n .map(|v| v.load().as_ref().clone()),\n }\n }\n}\n\nimpl\u003c'a\u003e From\u003c(\u0026'a ClientSecret, \u0026Config)\u003e for Request\u003c'a\u003e {\n fn from((secret, config): (\u0026'a ClientSecret, \u0026Config)) -\u003e Self {\n Self {\n secret,\n port: config.port,\n disk_space: config.disk_quota,\n network_speed: config.network_speed.into(),\n build_version: CLIENT_API_VERSION,\n tls_created_at: None,\n }\n }\n}\n\n#[derive(Deserialize, Debug)]\n#[serde(untagged)]\npub enum Response {\n Ok(OkResponse),\n Error(ErrorResponse),\n}\n\n#[derive(Deserialize, Debug)]\npub struct OkResponse {\n pub image_server: Url,\n pub latest_build: usize,\n pub url: Url,\n pub token_key: Option\u003cString\u003e,\n pub compromised: bool,\n pub paused: bool,\n #[serde(default)]\n pub force_tokens: bool,\n pub tls: Option\u003cTls\u003e,\n}\n\n#[derive(Deserialize, Debug)]\npub struct ErrorResponse {\n pub error: String,\n pub status: ErrorCode,\n}\n\n#[derive(Deserialize_repr, Debug, Copy, Clone)]\n#[repr(u16)]\npub enum ErrorCode {\n MalformedJson = 400,\n InvalidSecret = 401,\n InvalidContentType = 415,\n}\n\npub struct Tls {\n pub created_at: String,\n pub priv_key: Arc\u003cBox\u003cdyn SigningKey\u003e\u003e,\n pub certs: Vec\u003cCertificate\u003e,\n}\n\nimpl\u003c'de\u003e Deserialize\u003c'de\u003e for Tls {\n fn deserialize\u003cD\u003e(deserializer: D) -\u003e Result\u003cSelf, D::Error\u003e\n where\n D: serde::Deserializer\u003c'de\u003e,\n {\n struct TlsVisitor;\n\n impl\u003c'de\u003e Visitor\u003c'de\u003e for TlsVisitor {\n type Value = Tls;\n\n fn expecting(\u0026self, formatter: \u0026mut std::fmt::Formatter) -\u003e std::fmt::Result {\n formatter.write_str(\"a tls struct\")\n }\n\n fn visit_map\u003cA\u003e(self, mut map: A) -\u003e Result\u003cSelf::Value, A::Error\u003e\n where\n A: MapAccess\u003c'de\u003e,\n {\n let mut created_at = None;\n let mut priv_key = None;\n let mut certificates = None;\n\n while let Some((key, value)) = map.next_entry::\u003c\u0026str, String\u003e()? {\n match key {\n \"created_at\" =\u003e created_at = Some(value.to_string()),\n \"private_key\" =\u003e {\n priv_key = rsa_private_keys(\u0026mut BufReader::new(value.as_bytes()))\n .ok()\n .and_then(|mut v| {\n v.pop().and_then(|key| RSASigningKey::new(\u0026key).ok())\n });\n }\n \"certificate\" =\u003e {\n certificates = certs(\u0026mut BufReader::new(value.as_bytes())).ok();\n }\n _ =\u003e (), // Ignore extra fields\n }\n }\n\n match (created_at, priv_key, certificates) {\n (Some(created_at), Some(priv_key), Some(certificates)) =\u003e Ok(Tls {\n created_at,\n priv_key: Arc::new(Box::new(priv_key)),\n certs: certificates,\n }),\n _ =\u003e Err(serde::de::Error::custom(\"Could not deserialize tls info\")),\n }\n }\n }\n\n deserializer.deserialize_map(TlsVisitor)\n }\n}\n\nimpl std::fmt::Debug for Tls {\n fn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n f.debug_struct(\"Tls\")\n .field(\"created_at\", \u0026self.created_at)\n .finish()\n }\n}\n\npub async fn update_server_state(\n secret: \u0026ClientSecret,\n cli: \u0026Config,\n data: \u0026mut Arc\u003cRwLockServerState\u003e,\n) {\n let req = Request::from_config_and_state(secret, cli);\n let client = reqwest::Client::new();\n let resp = client.post(CONTROL_CENTER_PING_URL).json(\u0026req).send().await;\n match resp {\n Ok(resp) =\u003e match resp.json::\u003cResponse\u003e().await {\n Ok(Response::Ok(resp)) =\u003e {\n debug!(\"got write guard for server state\");\n let mut write_guard = data.0.write();\n\n let image_server_changed = write_guard.image_server != resp.image_server;\n if !write_guard.url_overridden \u0026\u0026 image_server_changed {\n write_guard.image_server = resp.image_server;\n } else if image_server_changed {\n warn!(\"Ignoring new upstream url!\");\n }\n\n if let Some(key) = resp.token_key {\n if let Some(key) = base64::decode(\u0026key)\n .ok()\n .and_then(|k| PrecomputedKey::from_slice(\u0026k))\n {\n write_guard.precomputed_key = key;\n } else {\n error!(\"Failed to parse token key: got {}\", key);\n }\n }\n\n if !cli\n .unstable_options\n .contains(\u0026UnstableOptions::DisableTokenValidation)\n \u0026\u0026 VALIDATE_TOKENS.load(Ordering::Acquire) != resp.force_tokens\n {\n if resp.force_tokens {\n info!(\"Client received command to enforce token validity.\");\n } else {\n info!(\"Client received command to no longer enforce token validity\");\n }\n VALIDATE_TOKENS.store(resp.force_tokens, Ordering::Release);\n }\n\n if let Some(tls) = resp.tls {\n TLS_PREVIOUSLY_CREATED\n .get()\n .unwrap()\n .swap(Arc::new(tls.created_at));\n TLS_SIGNING_KEY.get().unwrap().swap(tls.priv_key);\n TLS_CERTS.get().unwrap().swap(Arc::new(tls.certs));\n }\n\n let previously_compromised = PREVIOUSLY_COMPROMISED.load(Ordering::Acquire);\n if resp.compromised != previously_compromised {\n PREVIOUSLY_COMPROMISED.store(resp.compromised, Ordering::Release);\n if resp.compromised {\n error!(\"Got compromised response from control center!\");\n } else if previously_compromised {\n info!(\"No longer compromised!\");\n }\n }\n\n let previously_paused = PREVIOUSLY_PAUSED.load(Ordering::Acquire);\n if resp.paused != previously_paused {\n PREVIOUSLY_PAUSED.store(resp.paused, Ordering::Release);\n if resp.paused {\n warn!(\"Control center has paused this node.\");\n } else {\n info!(\"Control center is no longer pausing this node.\");\n }\n }\n\n if resp.url != write_guard.url {\n info!(\"This client's URL has been updated to {}\", resp.url);\n }\n\n debug!(\"dropping write guard for server state\");\n }\n Ok(Response::Error(resp)) =\u003e {\n error!(\n \"Got an {} error from upstream: {}\",\n resp.status as u16, resp.error\n );\n }\n Err(e) =\u003e warn!(\"Got malformed response: {}\", e),\n },\n Err(e) =\u003e match e {\n e if e.is_timeout() =\u003e {\n error!(\"Response timed out to control server. Is MangaDex down?\");\n }\n e =\u003e warn!(\"Failed to send request: {}\", e),\n },\n }\n}\n","traces":[{"line":35,"address":[5142816],"length":1,"stats":{"Line":0},"fn_name":"from_config_and_state"},{"line":38,"address":[5142845],"length":1,"stats":{"Line":0},"fn_name":null},{"line":39,"address":[5142857],"length":1,"stats":{"Line":0},"fn_name":null},{"line":40,"address":[5142866],"length":1,"stats":{"Line":0},"fn_name":null},{"line":42,"address":[5142880],"length":1,"stats":{"Line":0},"fn_name":null},{"line":50,"address":[5143184,5143212],"length":1,"stats":{"Line":0},"fn_name":"from"},{"line":53,"address":[5143225],"length":1,"stats":{"Line":0},"fn_name":null},{"line":54,"address":[5143237],"length":1,"stats":{"Line":0},"fn_name":null},{"line":55,"address":[5143246],"length":1,"stats":{"Line":0},"fn_name":null},{"line":103,"address":[5143360],"length":1,"stats":{"Line":0},"fn_name":"deserialize\u003cserde::__private::de::content::ContentRefDeserializer\u003cserde_json::error::Error\u003e\u003e"},{"line":109,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":110,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":112,"address":[5143392],"length":1,"stats":{"Line":0},"fn_name":"expecting"},{"line":113,"address":[5143419],"length":1,"stats":{"Line":0},"fn_name":null},{"line":116,"address":[5143456,5145125],"length":1,"stats":{"Line":0},"fn_name":"visit_map\u003c\u0026mut serde::de::value::MapDeserializer\u003ccore::iter::adapters::map::Map\u003ccore::slice::iter::Iter\u003c(serde::__private::de::content::Content, serde::__private::de::content::Content)\u003e, closure-0\u003e, serde_json::error::Error\u003e\u003e"},{"line":120,"address":[5143484],"length":1,"stats":{"Line":0},"fn_name":null},{"line":121,"address":[5143520],"length":1,"stats":{"Line":0},"fn_name":null},{"line":122,"address":[5143532],"length":1,"stats":{"Line":0},"fn_name":null},{"line":124,"address":[5143560,5143785,5144387],"length":1,"stats":{"Line":0},"fn_name":null},{"line":125,"address":[5144366,5144226,5145238],"length":1,"stats":{"Line":0},"fn_name":null},{"line":126,"address":[5144011,5145140,5143859],"length":1,"stats":{"Line":0},"fn_name":null},{"line":127,"address":[5143904],"length":1,"stats":{"Line":0},"fn_name":null},{"line":128,"address":[5144080,5145248],"length":1,"stats":{"Line":0},"fn_name":null},{"line":129,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":130,"address":[5146656,5146736],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}\u003c\u0026mut serde::de::value::MapDeserializer\u003ccore::iter::adapters::map::Map\u003ccore::slice::iter::Iter\u003c(serde::__private::de::content::Content, serde::__private::de::content::Content)\u003e, closure-0\u003e, serde_json::error::Error\u003e\u003e"},{"line":131,"address":[5146544,5146668,5146553],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}\u003c\u0026mut serde::de::value::MapDeserializer\u003ccore::iter::adapters::map::Map\u003ccore::slice::iter::Iter\u003c(serde::__private::de::content::Content, serde::__private::de::content::Content)\u003e, closure-0\u003e, serde_json::error::Error\u003e\u003e"},{"line":134,"address":[5143953],"length":1,"stats":{"Line":0},"fn_name":null},{"line":135,"address":[5145292,5145035,5144239],"length":1,"stats":{"Line":0},"fn_name":null},{"line":141,"address":[5145395,5144892],"length":1,"stats":{"Line":0},"fn_name":null},{"line":142,"address":[5144422,5144754,5145627],"length":1,"stats":{"Line":0},"fn_name":null},{"line":143,"address":[5144583],"length":1,"stats":{"Line":0},"fn_name":null},{"line":144,"address":[5144672],"length":1,"stats":{"Line":0},"fn_name":null},{"line":145,"address":[5144706],"length":1,"stats":{"Line":0},"fn_name":null},{"line":147,"address":[5144392,5144904],"length":1,"stats":{"Line":0},"fn_name":null},{"line":152,"address":[5143377],"length":1,"stats":{"Line":0},"fn_name":null},{"line":157,"address":[5146768],"length":1,"stats":{"Line":0},"fn_name":"fmt"},{"line":158,"address":[5146800],"length":1,"stats":{"Line":0},"fn_name":null},{"line":159,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":164,"address":[5146928],"length":1,"stats":{"Line":0},"fn_name":"update_server_state"},{"line":169,"address":[5147391],"length":1,"stats":{"Line":0},"fn_name":null},{"line":170,"address":[5147414],"length":1,"stats":{"Line":0},"fn_name":null},{"line":171,"address":[5147647,5156167,5147475,5157425],"length":1,"stats":{"Line":0},"fn_name":null},{"line":172,"address":[5155853,5147979],"length":1,"stats":{"Line":0},"fn_name":null},{"line":173,"address":[5147989,5148462,5156134,5156692,5157446,5148183,5147901],"length":1,"stats":{"Line":0},"fn_name":null},{"line":174,"address":[5148526,5148425],"length":1,"stats":{"Line":0},"fn_name":null},{"line":175,"address":[5148708,5148580],"length":1,"stats":{"Line":0},"fn_name":null},{"line":176,"address":[5148884],"length":1,"stats":{"Line":0},"fn_name":null},{"line":178,"address":[5148956],"length":1,"stats":{"Line":0},"fn_name":null},{"line":179,"address":[5149034,5156285],"length":1,"stats":{"Line":0},"fn_name":null},{"line":180,"address":[5149274,5157137,5149115,5156071,5156228],"length":1,"stats":{"Line":0},"fn_name":null},{"line":181,"address":[5149597,5149260,5149339],"length":1,"stats":{"Line":0},"fn_name":null},{"line":182,"address":[5149296,5149344,5149429],"length":1,"stats":{"Line":0},"fn_name":null},{"line":185,"address":[5149599],"length":1,"stats":{"Line":0},"fn_name":null},{"line":186,"address":[5150318,5149670,5149815,5149751],"length":1,"stats":{"Line":0},"fn_name":null},{"line":188,"address":[5149743,5147051,5147024],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":190,"address":[5157043,5156034,5156298,5149855],"length":1,"stats":{"Line":0},"fn_name":null},{"line":192,"address":[5149958,5149769,5150043],"length":1,"stats":{"Line":0},"fn_name":null},{"line":196,"address":[5150417,5150331,5150499,5151170,5150522],"length":1,"stats":{"Line":0},"fn_name":null},{"line":199,"address":[5150378,5150490],"length":1,"stats":{"Line":0},"fn_name":null},{"line":201,"address":[5151130,5150510,5150872],"length":1,"stats":{"Line":0},"fn_name":null},{"line":202,"address":[5150619,5150527,5150704],"length":1,"stats":{"Line":0},"fn_name":null},{"line":204,"address":[5150877,5150962,5150573],"length":1,"stats":{"Line":0},"fn_name":null},{"line":206,"address":[5151132],"length":1,"stats":{"Line":0},"fn_name":null},{"line":209,"address":[5151172],"length":1,"stats":{"Line":0},"fn_name":null},{"line":210,"address":[5151439,5151299],"length":1,"stats":{"Line":0},"fn_name":null},{"line":213,"address":[5151481,5151357],"length":1,"stats":{"Line":0},"fn_name":null},{"line":214,"address":[5151488],"length":1,"stats":{"Line":0},"fn_name":null},{"line":215,"address":[5151623],"length":1,"stats":{"Line":0},"fn_name":null},{"line":218,"address":[5151836],"length":1,"stats":{"Line":0},"fn_name":null},{"line":219,"address":[5151943,5151892],"length":1,"stats":{"Line":0},"fn_name":null},{"line":220,"address":[5151905],"length":1,"stats":{"Line":0},"fn_name":null},{"line":221,"address":[5151948,5152274],"length":1,"stats":{"Line":0},"fn_name":null},{"line":222,"address":[5152021,5151958,5152106],"length":1,"stats":{"Line":0},"fn_name":null},{"line":223,"address":[5152008,5152580,5152322],"length":1,"stats":{"Line":0},"fn_name":null},{"line":224,"address":[5152279,5152412,5152327],"length":1,"stats":{"Line":0},"fn_name":null},{"line":228,"address":[5152582],"length":1,"stats":{"Line":0},"fn_name":null},{"line":229,"address":[5152638,5152689],"length":1,"stats":{"Line":0},"fn_name":null},{"line":230,"address":[5152651],"length":1,"stats":{"Line":0},"fn_name":null},{"line":231,"address":[5153307,5152694,5153049],"length":1,"stats":{"Line":0},"fn_name":null},{"line":232,"address":[5152881,5152704,5152796],"length":1,"stats":{"Line":0},"fn_name":null},{"line":234,"address":[5152750,5153054,5153139],"length":1,"stats":{"Line":0},"fn_name":null},{"line":238,"address":[5153309,5153447,5153812],"length":1,"stats":{"Line":0},"fn_name":null},{"line":239,"address":[5153537,5153404,5153452],"length":1,"stats":{"Line":0},"fn_name":null},{"line":242,"address":[5153942,5153814],"length":1,"stats":{"Line":0},"fn_name":null},{"line":244,"address":[5154144],"length":1,"stats":{"Line":0},"fn_name":null},{"line":245,"address":[5154304,5154176,5154422],"length":1,"stats":{"Line":0},"fn_name":null},{"line":247,"address":[5154398],"length":1,"stats":{"Line":0},"fn_name":null},{"line":250,"address":[5148464,5154756,5154832,5155971],"length":1,"stats":{"Line":0},"fn_name":null},{"line":252,"address":[5147920],"length":1,"stats":{"Line":0},"fn_name":null},{"line":253,"address":[5155111,5147948],"length":1,"stats":{"Line":0},"fn_name":null},{"line":254,"address":[5155251,5155327,5155141],"length":1,"stats":{"Line":0},"fn_name":null},{"line":256,"address":[5155581,5155920,5155184,5155505],"length":1,"stats":{"Line":0},"fn_name":null}],"covered":0,"coverable":92},{"path":["/","home","edward","Documents","repos","mangadex-home","src","routes.rs"],"content":"use std::sync::atomic::Ordering;\n\nuse actix_web::error::ErrorNotFound;\nuse actix_web::http::header::{CONTENT_LENGTH, CONTENT_TYPE, LAST_MODIFIED};\nuse actix_web::http::HeaderValue;\nuse actix_web::web::Path;\nuse actix_web::HttpResponseBuilder;\nuse actix_web::{get, web::Data, HttpRequest, HttpResponse, Responder};\nuse base64::DecodeError;\nuse bytes::Bytes;\nuse chrono::{DateTime, Utc};\nuse futures::Stream;\nuse log::{debug, error, info, trace};\nuse prometheus::{Encoder, TextEncoder};\nuse serde::Deserialize;\nuse sodiumoxide::crypto::box_::{open_precomputed, Nonce, PrecomputedKey, NONCEBYTES};\nuse thiserror::Error;\n\nuse crate::cache::{Cache, CacheKey, ImageMetadata, UpstreamError};\nuse crate::client::{FetchResult, DEFAULT_HEADERS, HTTP_CLIENT};\nuse crate::config::{OFFLINE_MODE, VALIDATE_TOKENS};\nuse crate::metrics::{\n CACHE_HIT_COUNTER, CACHE_MISS_COUNTER, REQUESTS_DATA_COUNTER, REQUESTS_DATA_SAVER_COUNTER,\n REQUESTS_OTHER_COUNTER, REQUESTS_TOTAL_COUNTER,\n};\nuse crate::state::RwLockServerState;\n\nconst BASE64_CONFIG: base64::Config = base64::Config::new(base64::CharacterSet::UrlSafe, false);\n\npub enum ServerResponse {\n TokenValidationError(TokenValidationError),\n HttpResponse(HttpResponse),\n}\n\nimpl Responder for ServerResponse {\n #[inline]\n fn respond_to(self, req: \u0026HttpRequest) -\u003e HttpResponse {\n match self {\n Self::TokenValidationError(e) =\u003e e.respond_to(req),\n Self::HttpResponse(resp) =\u003e {\n REQUESTS_TOTAL_COUNTER.inc();\n resp.respond_to(req)\n }\n }\n }\n}\n\n#[allow(clippy::unused_async)]\n#[get(\"/\")]\nasync fn index() -\u003e impl Responder {\n HttpResponse::Ok().body(include_str!(\"index.html\"))\n}\n\n#[allow(clippy::future_not_send)]\n#[get(\"/{token}/data/{chapter_hash}/{file_name}\")]\nasync fn token_data(\n state: Data\u003cRwLockServerState\u003e,\n cache: Data\u003cdyn Cache\u003e,\n path: Path\u003c(String, String, String)\u003e,\n) -\u003e impl Responder {\n REQUESTS_DATA_COUNTER.inc();\n let (token, chapter_hash, file_name) = path.into_inner();\n if VALIDATE_TOKENS.load(Ordering::Acquire) {\n if let Err(e) = validate_token(\u0026state.0.read().precomputed_key, token, \u0026chapter_hash) {\n return ServerResponse::TokenValidationError(e);\n }\n }\n fetch_image(state, cache, chapter_hash, file_name, false).await\n}\n\n#[allow(clippy::future_not_send)]\n#[get(\"/{token}/data-saver/{chapter_hash}/{file_name}\")]\nasync fn token_data_saver(\n state: Data\u003cRwLockServerState\u003e,\n cache: Data\u003cdyn Cache\u003e,\n path: Path\u003c(String, String, String)\u003e,\n) -\u003e impl Responder {\n REQUESTS_DATA_SAVER_COUNTER.inc();\n let (token, chapter_hash, file_name) = path.into_inner();\n if VALIDATE_TOKENS.load(Ordering::Acquire) {\n if let Err(e) = validate_token(\u0026state.0.read().precomputed_key, token, \u0026chapter_hash) {\n return ServerResponse::TokenValidationError(e);\n }\n }\n\n fetch_image(state, cache, chapter_hash, file_name, true).await\n}\n\n#[allow(clippy::future_not_send)]\npub async fn default(state: Data\u003cRwLockServerState\u003e, req: HttpRequest) -\u003e impl Responder {\n REQUESTS_OTHER_COUNTER.inc();\n let path = \u0026format!(\n \"{}{}\",\n state.0.read().image_server,\n req.path().chars().skip(1).collect::\u003cString\u003e()\n );\n\n if OFFLINE_MODE.load(Ordering::Acquire) {\n info!(\"Got unknown path in offline mode, returning 404: {}\", path);\n return ServerResponse::HttpResponse(\n ErrorNotFound(\"Path is not valid in offline mode\").into(),\n );\n }\n\n info!(\"Got unknown path, just proxying: {}\", path);\n\n let mut resp = match HTTP_CLIENT.inner().get(path).send().await {\n Ok(resp) =\u003e resp,\n Err(e) =\u003e {\n error!(\"{}\", e);\n return ServerResponse::HttpResponse(HttpResponse::BadGateway().finish());\n }\n };\n let content_type = resp.headers_mut().remove(CONTENT_TYPE);\n let mut resp_builder = HttpResponseBuilder::new(resp.status());\n let mut headers = DEFAULT_HEADERS.clone();\n if let Some(content_type) = content_type {\n headers.insert(CONTENT_TYPE, content_type);\n }\n // push_headers(\u0026mut resp_builder);\n\n let mut resp = resp_builder.body(resp.bytes().await.unwrap_or_default());\n *resp.headers_mut() = headers;\n ServerResponse::HttpResponse(resp)\n}\n\n#[allow(clippy::future_not_send, clippy::unused_async)]\n#[get(\"/metrics\")]\npub async fn metrics() -\u003e impl Responder {\n let metric_families = prometheus::gather();\n let mut buffer = Vec::new();\n TextEncoder::new()\n .encode(\u0026metric_families, \u0026mut buffer)\n .unwrap();\n String::from_utf8(buffer).unwrap()\n}\n\n#[derive(Error, Debug)]\npub enum TokenValidationError {\n #[error(\"Failed to decode base64 token.\")]\n DecodeError(#[from] DecodeError),\n #[error(\"Nonce was too short.\")]\n IncompleteNonce,\n #[error(\"Invalid nonce.\")]\n InvalidNonce,\n #[error(\"Decryption failed\")]\n DecryptionFailure,\n #[error(\"The token format was invalid.\")]\n InvalidToken,\n #[error(\"The token has expired.\")]\n TokenExpired,\n #[error(\"Invalid chapter hash.\")]\n InvalidChapterHash,\n}\n\nimpl Responder for TokenValidationError {\n #[inline]\n fn respond_to(self, _: \u0026HttpRequest) -\u003e HttpResponse {\n let mut resp = HttpResponse::Forbidden().finish();\n *resp.headers_mut() = DEFAULT_HEADERS.clone();\n resp\n }\n}\n\nfn validate_token(\n precomputed_key: \u0026PrecomputedKey,\n token: String,\n chapter_hash: \u0026str,\n) -\u003e Result\u003c(), TokenValidationError\u003e {\n #[derive(Deserialize)]\n struct Token\u003c'a\u003e {\n expires: DateTime\u003cUtc\u003e,\n hash: \u0026'a str,\n }\n\n let data = base64::decode_config(token, BASE64_CONFIG)?;\n if data.len() \u003c NONCEBYTES {\n return Err(TokenValidationError::IncompleteNonce);\n }\n\n let (nonce, encrypted) = data.split_at(NONCEBYTES);\n\n let nonce = Nonce::from_slice(nonce).ok_or(TokenValidationError::InvalidNonce)?;\n let decrypted = open_precomputed(encrypted, \u0026nonce, precomputed_key)\n .map_err(|_| TokenValidationError::DecryptionFailure)?;\n\n let parsed_token: Token =\n serde_json::from_slice(\u0026decrypted).map_err(|_| TokenValidationError::InvalidToken)?;\n\n if parsed_token.expires \u003c Utc::now() {\n return Err(TokenValidationError::TokenExpired);\n }\n\n if parsed_token.hash != chapter_hash {\n return Err(TokenValidationError::InvalidChapterHash);\n }\n\n debug!(\"Token validated!\");\n\n Ok(())\n}\n\n#[allow(clippy::future_not_send)]\nasync fn fetch_image(\n state: Data\u003cRwLockServerState\u003e,\n cache: Data\u003cdyn Cache\u003e,\n chapter_hash: String,\n file_name: String,\n is_data_saver: bool,\n) -\u003e ServerResponse {\n let key = CacheKey(chapter_hash, file_name, is_data_saver);\n\n match cache.get(\u0026key).await {\n Some(Ok((image, metadata))) =\u003e {\n CACHE_HIT_COUNTER.inc();\n return construct_response(image, \u0026metadata);\n }\n Some(Err(_)) =\u003e {\n return ServerResponse::HttpResponse(HttpResponse::BadGateway().finish());\n }\n None =\u003e (),\n }\n\n CACHE_MISS_COUNTER.inc();\n\n // If in offline mode, return early since there's nothing else we can do\n if OFFLINE_MODE.load(Ordering::Acquire) {\n return ServerResponse::HttpResponse(\n ErrorNotFound(\"Offline mode enabled and image not in cache\").into(),\n );\n }\n\n let url = if is_data_saver {\n format!(\n \"{}/data-saver/{}/{}\",\n state.0.read().image_server,\n \u0026key.0,\n \u0026key.1,\n )\n } else {\n format!(\"{}/data/{}/{}\", state.0.read().image_server, \u0026key.0, \u0026key.1)\n };\n\n match HTTP_CLIENT.fetch_and_cache(url, key, cache).await {\n FetchResult::ServiceUnavailable =\u003e {\n ServerResponse::HttpResponse(HttpResponse::ServiceUnavailable().finish())\n }\n FetchResult::InternalServerError =\u003e {\n ServerResponse::HttpResponse(HttpResponse::InternalServerError().finish())\n }\n FetchResult::Data(status, headers, data) =\u003e {\n let mut resp = HttpResponseBuilder::new(status);\n let mut resp = resp.body(data);\n *resp.headers_mut() = headers;\n ServerResponse::HttpResponse(resp)\n }\n FetchResult::Processing =\u003e panic!(\"Race condition found with fetch result\"),\n }\n}\n\n#[inline]\npub fn construct_response(\n data: impl Stream\u003cItem = Result\u003cBytes, UpstreamError\u003e\u003e + Unpin + 'static,\n metadata: \u0026ImageMetadata,\n) -\u003e ServerResponse {\n trace!(\"Constructing response\");\n\n let mut resp = HttpResponse::Ok();\n\n let mut headers = DEFAULT_HEADERS.clone();\n if let Some(content_type) = metadata.content_type {\n headers.insert(\n CONTENT_TYPE,\n HeaderValue::from_str(content_type.as_ref()).unwrap(),\n );\n }\n\n if let Some(content_length) = metadata.content_length {\n headers.insert(CONTENT_LENGTH, HeaderValue::from(content_length));\n }\n\n if let Some(last_modified) = metadata.last_modified {\n headers.insert(\n LAST_MODIFIED,\n HeaderValue::from_str(\u0026last_modified.to_rfc2822()).unwrap(),\n );\n }\n\n let mut ret = resp.streaming(data);\n *ret.headers_mut() = headers;\n\n ServerResponse::HttpResponse(ret)\n}\n","traces":[{"line":37,"address":[4371216,4371540],"length":1,"stats":{"Line":0},"fn_name":"respond_to"},{"line":38,"address":[4371343],"length":1,"stats":{"Line":0},"fn_name":null},{"line":39,"address":[4371251,4371360],"length":1,"stats":{"Line":0},"fn_name":null},{"line":40,"address":[4371274],"length":1,"stats":{"Line":0},"fn_name":null},{"line":41,"address":[4371401,4371321],"length":1,"stats":{"Line":0},"fn_name":null},{"line":42,"address":[4371418],"length":1,"stats":{"Line":0},"fn_name":null},{"line":49,"address":[4402816,4402835],"length":1,"stats":{"Line":0},"fn_name":"register"},{"line":50,"address":[4403210,4403168,4403124,4403384,4403120],"length":1,"stats":{"Line":0},"fn_name":"index"},{"line":51,"address":[4403255],"length":1,"stats":{"Line":0},"fn_name":null},{"line":55,"address":[4403536,4403555],"length":1,"stats":{"Line":0},"fn_name":"register"},{"line":56,"address":[4403840],"length":1,"stats":{"Line":0},"fn_name":"token_data"},{"line":61,"address":[4404228],"length":1,"stats":{"Line":0},"fn_name":null},{"line":62,"address":[4404270],"length":1,"stats":{"Line":0},"fn_name":null},{"line":63,"address":[4404554,4404481],"length":1,"stats":{"Line":0},"fn_name":null},{"line":64,"address":[4404890,4404535,4404564,4405826,4406102],"length":1,"stats":{"Line":0},"fn_name":null},{"line":65,"address":[4404906],"length":1,"stats":{"Line":0},"fn_name":null},{"line":68,"address":[4405188,4406229,4404976,4405732],"length":1,"stats":{"Line":0},"fn_name":null},{"line":72,"address":[4406624,4406643],"length":1,"stats":{"Line":0},"fn_name":"register"},{"line":73,"address":[4406928],"length":1,"stats":{"Line":0},"fn_name":"token_data_saver"},{"line":78,"address":[4407316],"length":1,"stats":{"Line":0},"fn_name":null},{"line":79,"address":[4407358],"length":1,"stats":{"Line":0},"fn_name":null},{"line":80,"address":[4407642,4407569],"length":1,"stats":{"Line":0},"fn_name":null},{"line":81,"address":[4407623,4408914,4409190,4407652,4407978],"length":1,"stats":{"Line":0},"fn_name":null},{"line":82,"address":[4407994],"length":1,"stats":{"Line":0},"fn_name":null},{"line":86,"address":[4408276,4409317,4408064,4408820],"length":1,"stats":{"Line":0},"fn_name":null},{"line":90,"address":[4371725,4371664,4375818,4376192,4371615,4371584,4376774,4375722,4376713],"length":1,"stats":{"Line":0},"fn_name":"default"},{"line":91,"address":[4371820],"length":1,"stats":{"Line":0},"fn_name":null},{"line":92,"address":[4372158,4376128],"length":1,"stats":{"Line":0},"fn_name":null},{"line":94,"address":[4371868],"length":1,"stats":{"Line":0},"fn_name":null},{"line":95,"address":[4372008],"length":1,"stats":{"Line":0},"fn_name":null},{"line":98,"address":[4372549],"length":1,"stats":{"Line":0},"fn_name":null},{"line":99,"address":[4372778,4372601,4372693],"length":1,"stats":{"Line":0},"fn_name":null},{"line":100,"address":[4373129],"length":1,"stats":{"Line":0},"fn_name":null},{"line":101,"address":[4373057],"length":1,"stats":{"Line":0},"fn_name":null},{"line":105,"address":[4372647,4373261,4373176],"length":1,"stats":{"Line":0},"fn_name":null},{"line":107,"address":[4376740,4376086,4373727,4374043,4373540],"length":1,"stats":{"Line":0},"fn_name":null},{"line":108,"address":[4373966,4374045],"length":1,"stats":{"Line":0},"fn_name":null},{"line":109,"address":[4373981],"length":1,"stats":{"Line":0},"fn_name":null},{"line":110,"address":[4374230,4373997,4374154],"length":1,"stats":{"Line":0},"fn_name":null},{"line":111,"address":[4374480,4376048],"length":1,"stats":{"Line":0},"fn_name":null},{"line":114,"address":[4374133,4374636],"length":1,"stats":{"Line":0},"fn_name":null},{"line":115,"address":[4374719],"length":1,"stats":{"Line":0},"fn_name":null},{"line":116,"address":[4374768],"length":1,"stats":{"Line":0},"fn_name":null},{"line":117,"address":[4375075,4374829],"length":1,"stats":{"Line":0},"fn_name":null},{"line":118,"address":[4374920],"length":1,"stats":{"Line":0},"fn_name":null},{"line":122,"address":[4375236,4376761,4375085,4375941],"length":1,"stats":{"Line":0},"fn_name":null},{"line":123,"address":[4376559,4375542,4375906,4376229],"length":1,"stats":{"Line":0},"fn_name":null},{"line":124,"address":[4376320],"length":1,"stats":{"Line":0},"fn_name":null},{"line":128,"address":[4409731,4409712],"length":1,"stats":{"Line":0},"fn_name":"register"},{"line":129,"address":[4410107,4410064,4410016,4410523,4410020],"length":1,"stats":{"Line":0},"fn_name":"metrics"},{"line":130,"address":[4410153],"length":1,"stats":{"Line":0},"fn_name":null},{"line":131,"address":[4410177],"length":1,"stats":{"Line":0},"fn_name":null},{"line":132,"address":[4410266,4410193],"length":1,"stats":{"Line":0},"fn_name":null},{"line":133,"address":[4410217],"length":1,"stats":{"Line":0},"fn_name":null},{"line":135,"address":[4410296],"length":1,"stats":{"Line":0},"fn_name":null},{"line":158,"address":[4377587,4377360],"length":1,"stats":{"Line":0},"fn_name":"respond_to"},{"line":159,"address":[4377415,4377576],"length":1,"stats":{"Line":0},"fn_name":null},{"line":160,"address":[4377606,4377669,4377467],"length":1,"stats":{"Line":0},"fn_name":null},{"line":161,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":165,"address":[4377808,4380242],"length":1,"stats":{"Line":0},"fn_name":"validate_token"},{"line":176,"address":[4378075,4377847],"length":1,"stats":{"Line":0},"fn_name":null},{"line":177,"address":[4378042,4378260],"length":1,"stats":{"Line":0},"fn_name":null},{"line":178,"address":[4378266],"length":1,"stats":{"Line":0},"fn_name":null},{"line":181,"address":[4378311],"length":1,"stats":{"Line":0},"fn_name":null},{"line":183,"address":[4378697,4380275,4378473],"length":1,"stats":{"Line":0},"fn_name":null},{"line":184,"address":[4378888,4379043,4379158,4378666,4378935],"length":1,"stats":{"Line":0},"fn_name":null},{"line":185,"address":[4380257,4379045,4378918,4380480,4380484],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":187,"address":[4380338,4380521,4379461,4380512,4379284,4378999],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":190,"address":[4379633,4379431],"length":1,"stats":{"Line":0},"fn_name":null},{"line":191,"address":[4379709],"length":1,"stats":{"Line":0},"fn_name":null},{"line":194,"address":[4379754],"length":1,"stats":{"Line":0},"fn_name":null},{"line":195,"address":[4379797],"length":1,"stats":{"Line":0},"fn_name":null},{"line":198,"address":[4379842,4379958],"length":1,"stats":{"Line":0},"fn_name":null},{"line":200,"address":[4380114],"length":1,"stats":{"Line":0},"fn_name":null},{"line":204,"address":[4380560],"length":1,"stats":{"Line":0},"fn_name":"fetch_image"},{"line":211,"address":[4381005],"length":1,"stats":{"Line":0},"fn_name":null},{"line":213,"address":[4385616,4386292,4381645,4381139,4381336],"length":1,"stats":{"Line":0},"fn_name":null},{"line":214,"address":[4381573,4381647,4381625],"length":1,"stats":{"Line":0},"fn_name":null},{"line":215,"address":[4381815],"length":1,"stats":{"Line":0},"fn_name":null},{"line":216,"address":[4381855],"length":1,"stats":{"Line":0},"fn_name":null},{"line":219,"address":[4382035,4385562],"length":1,"stats":{"Line":0},"fn_name":null},{"line":224,"address":[4382183,4381597],"length":1,"stats":{"Line":0},"fn_name":null},{"line":227,"address":[4382190],"length":1,"stats":{"Line":0},"fn_name":null},{"line":228,"address":[4382333],"length":1,"stats":{"Line":0},"fn_name":null},{"line":229,"address":[4382326,4382242],"length":1,"stats":{"Line":0},"fn_name":null},{"line":233,"address":[4383117,4382291],"length":1,"stats":{"Line":0},"fn_name":null},{"line":234,"address":[4382580,4385523],"length":1,"stats":{"Line":0},"fn_name":null},{"line":236,"address":[4382388,4382451],"length":1,"stats":{"Line":0},"fn_name":null},{"line":237,"address":[4382553],"length":1,"stats":{"Line":0},"fn_name":null},{"line":238,"address":[4382568],"length":1,"stats":{"Line":0},"fn_name":null},{"line":241,"address":[4383130,4385492,4382418],"length":1,"stats":{"Line":0},"fn_name":null},{"line":244,"address":[4384425,4383736,4383994,4386313,4385392],"length":1,"stats":{"Line":0},"fn_name":null},{"line":245,"address":[4384358],"length":1,"stats":{"Line":0},"fn_name":null},{"line":246,"address":[4384427],"length":1,"stats":{"Line":0},"fn_name":null},{"line":249,"address":[4384567],"length":1,"stats":{"Line":0},"fn_name":null},{"line":251,"address":[4384715],"length":1,"stats":{"Line":0},"fn_name":null},{"line":252,"address":[4384846,4384707],"length":1,"stats":{"Line":0},"fn_name":null},{"line":253,"address":[4384853],"length":1,"stats":{"Line":0},"fn_name":null},{"line":254,"address":[4385247,4384928,4385717,4386030],"length":1,"stats":{"Line":0},"fn_name":null},{"line":255,"address":[4385800],"length":1,"stats":{"Line":0},"fn_name":null},{"line":257,"address":[4384392],"length":1,"stats":{"Line":0},"fn_name":null},{"line":262,"address":[4387008,4388427],"length":1,"stats":{"Line":0},"fn_name":"construct_response\u003cmangadex_home::cache::CacheStream\u003e"},{"line":266,"address":[4387046,4387185],"length":1,"stats":{"Line":0},"fn_name":null},{"line":268,"address":[4387329],"length":1,"stats":{"Line":0},"fn_name":null},{"line":270,"address":[4387348],"length":1,"stats":{"Line":0},"fn_name":null},{"line":271,"address":[4387647,4387403],"length":1,"stats":{"Line":0},"fn_name":null},{"line":272,"address":[4387539],"length":1,"stats":{"Line":0},"fn_name":null},{"line":273,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":274,"address":[4387457],"length":1,"stats":{"Line":0},"fn_name":null},{"line":278,"address":[4387654,4387809],"length":1,"stats":{"Line":0},"fn_name":null},{"line":279,"address":[4387682],"length":1,"stats":{"Line":0},"fn_name":null},{"line":282,"address":[4387816,4388087],"length":1,"stats":{"Line":0},"fn_name":null},{"line":283,"address":[4387960],"length":1,"stats":{"Line":0},"fn_name":null},{"line":284,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":285,"address":[4387863],"length":1,"stats":{"Line":0},"fn_name":null},{"line":289,"address":[4388094],"length":1,"stats":{"Line":0},"fn_name":null},{"line":290,"address":[4388682,4388228,4388446],"length":1,"stats":{"Line":0},"fn_name":null},{"line":292,"address":[4388533],"length":1,"stats":{"Line":0},"fn_name":null}],"covered":0,"coverable":118},{"path":["/","home","edward","Documents","repos","mangadex-home","src","state.rs"],"content":"use std::str::FromStr;\nuse std::sync::atomic::{AtomicBool, Ordering};\n\nuse crate::config::{ClientSecret, Config, UnstableOptions, OFFLINE_MODE, VALIDATE_TOKENS};\nuse crate::ping::{Request, Response, CONTROL_CENTER_PING_URL};\nuse arc_swap::ArcSwap;\nuse log::{error, info, warn};\nuse once_cell::sync::OnceCell;\nuse parking_lot::RwLock;\nuse rustls::sign::{CertifiedKey, SigningKey};\nuse rustls::Certificate;\nuse rustls::{ClientHello, ResolvesServerCert};\nuse sodiumoxide::crypto::box_::{PrecomputedKey, PRECOMPUTEDKEYBYTES};\nuse thiserror::Error;\nuse url::Url;\n\npub struct ServerState {\n pub precomputed_key: PrecomputedKey,\n pub image_server: Url,\n pub url: Url,\n pub url_overridden: bool,\n}\n\npub static PREVIOUSLY_PAUSED: AtomicBool = AtomicBool::new(false);\npub static PREVIOUSLY_COMPROMISED: AtomicBool = AtomicBool::new(false);\n\npub static TLS_PREVIOUSLY_CREATED: OnceCell\u003cArcSwap\u003cString\u003e\u003e = OnceCell::new();\npub static TLS_SIGNING_KEY: OnceCell\u003cArcSwap\u003cBox\u003cdyn SigningKey\u003e\u003e\u003e = OnceCell::new();\npub static TLS_CERTS: OnceCell\u003cArcSwap\u003cVec\u003cCertificate\u003e\u003e\u003e = OnceCell::new();\n\n#[derive(Error, Debug)]\npub enum ServerInitError {\n #[error(transparent)]\n MalformedResponse(reqwest::Error),\n #[error(transparent)]\n Timeout(reqwest::Error),\n #[error(transparent)]\n SendFailure(reqwest::Error),\n #[error(\"Failed to parse token key\")]\n KeyParseError(String),\n #[error(\"Token key was not provided in initial request\")]\n MissingTokenKey,\n #[error(\"Got error response from control center\")]\n ErrorResponse,\n}\n\nimpl ServerState {\n pub async fn init(secret: \u0026ClientSecret, config: \u0026Config) -\u003e Result\u003cSelf, ServerInitError\u003e {\n let resp = reqwest::Client::new()\n .post(CONTROL_CENTER_PING_URL)\n .json(\u0026Request::from((secret, config)))\n .send()\n .await;\n\n match resp {\n Ok(resp) =\u003e match resp.json::\u003cResponse\u003e().await {\n Ok(Response::Ok(mut resp)) =\u003e {\n let key = resp\n .token_key\n .ok_or(ServerInitError::MissingTokenKey)\n .and_then(|key| {\n if let Some(key) = base64::decode(\u0026key)\n .ok()\n .and_then(|k| PrecomputedKey::from_slice(\u0026k))\n {\n Ok(key)\n } else {\n error!(\"Failed to parse token key: got {}\", key);\n Err(ServerInitError::KeyParseError(key))\n }\n })?;\n\n PREVIOUSLY_COMPROMISED.store(resp.compromised, Ordering::Release);\n if resp.compromised {\n error!(\"Got compromised response from control center!\");\n }\n\n PREVIOUSLY_PAUSED.store(resp.paused, Ordering::Release);\n if resp.paused {\n warn!(\"Control center has paused this node!\");\n }\n\n if let Some(ref override_url) = config.override_upstream {\n resp.image_server = override_url.clone();\n warn!(\"Upstream URL overridden to: {}\", resp.image_server);\n } else {\n }\n\n info!(\"This client's URL has been set to {}\", resp.url);\n\n if config\n .unstable_options\n .contains(\u0026UnstableOptions::DisableTokenValidation)\n {\n warn!(\"Token validation is explicitly disabled!\");\n } else {\n if resp.force_tokens {\n info!(\"This client will validate tokens.\");\n } else {\n info!(\"This client will not validate tokens.\");\n }\n VALIDATE_TOKENS.store(resp.force_tokens, Ordering::Release);\n }\n\n let tls = resp.tls.unwrap();\n std::mem::drop(\n TLS_PREVIOUSLY_CREATED.set(ArcSwap::from_pointee(tls.created_at)),\n );\n std::mem::drop(TLS_SIGNING_KEY.set(ArcSwap::new(tls.priv_key)));\n std::mem::drop(TLS_CERTS.set(ArcSwap::from_pointee(tls.certs)));\n\n Ok(Self {\n precomputed_key: key,\n image_server: resp.image_server,\n url: resp.url,\n url_overridden: config.override_upstream.is_some(),\n })\n }\n Ok(Response::Error(resp)) =\u003e {\n error!(\n \"Got an {} error from upstream: {}\",\n resp.status as u16, resp.error\n );\n Err(ServerInitError::ErrorResponse)\n }\n Err(e) =\u003e {\n error!(\"Got malformed response: {}. Is MangaDex@Home down?\", e);\n Err(ServerInitError::MalformedResponse(e))\n }\n },\n Err(e) =\u003e match e {\n e if e.is_timeout() =\u003e {\n error!(\"Response timed out to control server. Is MangaDex@Home down?\");\n Err(ServerInitError::Timeout(e))\n }\n e =\u003e {\n error!(\"Failed to send request: {}\", e);\n Err(ServerInitError::SendFailure(e))\n }\n },\n }\n }\n\n pub fn init_offline() -\u003e Self {\n assert!(OFFLINE_MODE.load(Ordering::Acquire));\n Self {\n precomputed_key: PrecomputedKey::from_slice(\u0026[41; PRECOMPUTEDKEYBYTES]).unwrap(),\n image_server: Url::from_file_path(\"/dev/null\").unwrap(),\n url: Url::from_str(\"http://localhost\").unwrap(),\n url_overridden: false,\n }\n }\n}\n\npub struct RwLockServerState(pub RwLock\u003cServerState\u003e);\n\npub struct DynamicServerCert;\n\nimpl ResolvesServerCert for DynamicServerCert {\n fn resolve(\u0026self, _: ClientHello) -\u003e Option\u003cCertifiedKey\u003e {\n // TODO: wait for actix-web to use a new version of rustls so we can\n // remove cloning the certs all the time\n Some(CertifiedKey {\n cert: TLS_CERTS.get().unwrap().load().as_ref().clone(),\n key: TLS_SIGNING_KEY.get().unwrap().load_full(),\n ocsp: None,\n sct_list: None,\n })\n }\n}\n","traces":[{"line":48,"address":[6207674,6198175,6199408,6208079,6199469,6198144,6208507],"length":1,"stats":{"Line":0},"fn_name":"init"},{"line":49,"address":[6199637,6140143,6199831,6199944,6207582,6208473],"length":1,"stats":{"Line":0},"fn_name":null},{"line":50,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":51,"address":[6207617,6199750],"length":1,"stats":{"Line":0},"fn_name":null},{"line":53,"address":[6140165,6207639,6207603,6200215,6207556,6140311],"length":1,"stats":{"Line":0},"fn_name":null},{"line":55,"address":[6207893,6200348],"length":1,"stats":{"Line":0},"fn_name":null},{"line":56,"address":[6200263,6140083,6200358,6206296,6200559,6207523,6200841,6208494,6205191],"length":1,"stats":{"Line":0},"fn_name":null},{"line":57,"address":[6200804,6200905],"length":1,"stats":{"Line":0},"fn_name":null},{"line":58,"address":[6201314,6201096,6201027,6200959,6201209],"length":1,"stats":{"Line":0},"fn_name":null},{"line":59,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":60,"address":[6200991],"length":1,"stats":{"Line":0},"fn_name":null},{"line":61,"address":[6198368,6199249],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":62,"address":[6198716,6198541,6198478,6198390],"length":1,"stats":{"Line":0},"fn_name":null},{"line":63,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":64,"address":[6198251,6198224,6198470],"length":1,"stats":{"Line":0},"fn_name":"{{closure}}"},{"line":66,"address":[6198601],"length":1,"stats":{"Line":0},"fn_name":null},{"line":68,"address":[6198797,6198493,6198721],"length":1,"stats":{"Line":0},"fn_name":null},{"line":69,"address":[6199053],"length":1,"stats":{"Line":0},"fn_name":null},{"line":73,"address":[6201168],"length":1,"stats":{"Line":0},"fn_name":null},{"line":74,"address":[6201662,6201351,6201404],"length":1,"stats":{"Line":0},"fn_name":null},{"line":75,"address":[6201494,6201409,6201361],"length":1,"stats":{"Line":0},"fn_name":null},{"line":78,"address":[6201664],"length":1,"stats":{"Line":0},"fn_name":null},{"line":79,"address":[6201702,6202013,6201755],"length":1,"stats":{"Line":0},"fn_name":null},{"line":80,"address":[6201760,6201712,6201845],"length":1,"stats":{"Line":0},"fn_name":null},{"line":83,"address":[6202497,6202023],"length":1,"stats":{"Line":0},"fn_name":null},{"line":84,"address":[6202093,6207696],"length":1,"stats":{"Line":0},"fn_name":null},{"line":85,"address":[6202207,6207839,6202122],"length":1,"stats":{"Line":0},"fn_name":null},{"line":89,"address":[6202627,6202499],"length":1,"stats":{"Line":0},"fn_name":null},{"line":91,"address":[6203962,6202910,6203314],"length":1,"stats":{"Line":0},"fn_name":null},{"line":92,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":93,"address":[],"length":0,"stats":{"Line":0},"fn_name":null},{"line":95,"address":[6202999,6203146,6203061],"length":1,"stats":{"Line":0},"fn_name":null},{"line":97,"address":[6203042,6203922,6203664],"length":1,"stats":{"Line":0},"fn_name":null},{"line":98,"address":[6203411,6203319,6203496],"length":1,"stats":{"Line":0},"fn_name":null},{"line":100,"address":[6203669,6203754,6203365],"length":1,"stats":{"Line":0},"fn_name":null},{"line":102,"address":[6203924],"length":1,"stats":{"Line":0},"fn_name":null},{"line":105,"address":[6203964],"length":1,"stats":{"Line":0},"fn_name":null},{"line":107,"address":[6204082],"length":1,"stats":{"Line":0},"fn_name":null},{"line":109,"address":[6204287],"length":1,"stats":{"Line":0},"fn_name":null},{"line":110,"address":[6204468],"length":1,"stats":{"Line":0},"fn_name":null},{"line":112,"address":[6204965],"length":1,"stats":{"Line":0},"fn_name":null},{"line":113,"address":[6204689],"length":1,"stats":{"Line":0},"fn_name":null},{"line":114,"address":[6204729],"length":1,"stats":{"Line":0},"fn_name":null},{"line":115,"address":[6204833],"length":1,"stats":{"Line":0},"fn_name":null},{"line":116,"address":[6204937],"length":1,"stats":{"Line":0},"fn_name":null},{"line":119,"address":[6205196],"length":1,"stats":{"Line":0},"fn_name":null},{"line":120,"address":[6205356,6205474,6205228],"length":1,"stats":{"Line":0},"fn_name":null},{"line":122,"address":[6205450],"length":1,"stats":{"Line":0},"fn_name":null},{"line":124,"address":[6205790],"length":1,"stats":{"Line":0},"fn_name":null},{"line":126,"address":[6200843],"length":1,"stats":{"Line":0},"fn_name":null},{"line":127,"address":[6205940,6200859,6205864],"length":1,"stats":{"Line":0},"fn_name":null},{"line":128,"address":[6206192],"length":1,"stats":{"Line":0},"fn_name":null},{"line":131,"address":[6200282,6206784],"length":1,"stats":{"Line":0},"fn_name":null},{"line":132,"address":[6200317,6206308],"length":1,"stats":{"Line":0},"fn_name":null},{"line":133,"address":[6206524,6206448,6206338],"length":1,"stats":{"Line":0},"fn_name":null},{"line":134,"address":[6206680],"length":1,"stats":{"Line":0},"fn_name":null},{"line":136,"address":[6206381],"length":1,"stats":{"Line":0},"fn_name":null},{"line":137,"address":[6206865,6206789,6206405],"length":1,"stats":{"Line":0},"fn_name":null},{"line":138,"address":[6207110],"length":1,"stats":{"Line":0},"fn_name":null},{"line":144,"address":[6209595,6209216],"length":1,"stats":{"Line":0},"fn_name":"init_offline"},{"line":145,"address":[6209232],"length":1,"stats":{"Line":0},"fn_name":null},{"line":147,"address":[6209300],"length":1,"stats":{"Line":0},"fn_name":null},{"line":148,"address":[6209345],"length":1,"stats":{"Line":0},"fn_name":null},{"line":149,"address":[6209399],"length":1,"stats":{"Line":0},"fn_name":null},{"line":160,"address":[6209664,6210121],"length":1,"stats":{"Line":0},"fn_name":"resolve"},{"line":163,"address":[6209922],"length":1,"stats":{"Line":0},"fn_name":null},{"line":164,"address":[6209689],"length":1,"stats":{"Line":0},"fn_name":null},{"line":165,"address":[6209821],"length":1,"stats":{"Line":0},"fn_name":null},{"line":166,"address":[6209898],"length":1,"stats":{"Line":0},"fn_name":null},{"line":167,"address":[6209910],"length":1,"stats":{"Line":0},"fn_name":null}],"covered":0,"coverable":70},{"path":["/","home","edward","Documents","repos","mangadex-home","src","stop.rs"],"content":"use log::{info, warn};\nuse reqwest::StatusCode;\nuse serde::Serialize;\n\nuse crate::config::ClientSecret;\n\nconst CONTROL_CENTER_STOP_URL: \u0026str = \"https://api.mangadex.network/ping\";\n\n#[derive(Serialize)]\nstruct StopRequest\u003c'a\u003e {\n secret: \u0026'a ClientSecret,\n}\n\npub async fn send_stop(secret: \u0026ClientSecret) {\n let request = StopRequest { secret };\n let client = reqwest::Client::new();\n match client\n .post(CONTROL_CENTER_STOP_URL)\n .json(\u0026request)\n .send()\n .await\n {\n Ok(resp) =\u003e {\n if resp.status() == StatusCode::OK {\n info!(\"Successfully sent stop message to control center.\");\n } else {\n warn!(\"Got weird response from server: {:?}\", resp.headers());\n }\n }\n Err(e) =\u003e warn!(\"Got error while sending stop message: {}\", e),\n }\n}\n","traces":[{"line":14,"address":[4532986,4533029,4530999,4530918,4530896,4530960],"length":1,"stats":{"Line":0},"fn_name":"send_stop"},{"line":15,"address":[4531073],"length":1,"stats":{"Line":0},"fn_name":null},{"line":16,"address":[4531077],"length":1,"stats":{"Line":0},"fn_name":null},{"line":17,"address":[4531305,4533016,4531621,4531138,4531197,4532908],"length":1,"stats":{"Line":0},"fn_name":null},{"line":19,"address":[4531177],"length":1,"stats":{"Line":0},"fn_name":null},{"line":23,"address":[4531623,4531544],"length":1,"stats":{"Line":0},"fn_name":null},{"line":24,"address":[4531663,4532053,4532417],"length":1,"stats":{"Line":0},"fn_name":null},{"line":25,"address":[4531735,4531897,4531821],"length":1,"stats":{"Line":0},"fn_name":null},{"line":27,"address":[4532134,4531778,4532058],"length":1,"stats":{"Line":0},"fn_name":null},{"line":30,"address":[4532441,4532850,4531559,4532517],"length":1,"stats":{"Line":0},"fn_name":null}],"covered":0,"coverable":10},{"path":["/","home","edward","Documents","repos","mangadex-home","src","units.rs"],"content":"use std::fmt::Display;\nuse std::num::{NonZeroU16, NonZeroU64, ParseIntError};\nuse std::str::FromStr;\n\nuse serde::{Deserialize, Serialize};\n\n/// Wrapper type for a port number.\n#[derive(Serialize, Deserialize, Debug, Clone, Copy)]\npub struct Port(NonZeroU16);\n\nimpl Port {\n pub const fn get(self) -\u003e u16 {\n self.0.get()\n }\n}\n\nimpl Default for Port {\n fn default() -\u003e Self {\n Self(unsafe { NonZeroU16::new_unchecked(443) })\n }\n}\n\nimpl FromStr for Port {\n type Err = \u003cNonZeroU16 as FromStr\u003e::Err;\n\n fn from_str(s: \u0026str) -\u003e Result\u003cSelf, Self::Err\u003e {\n NonZeroU16::from_str(s).map(Self)\n }\n}\n\nimpl Display for Port {\n fn fmt(\u0026self, f: \u0026mut std::fmt::Formatter\u003c'_\u003e) -\u003e std::fmt::Result {\n self.0.fmt(f)\n }\n}\n\n#[derive(Copy, Clone, Serialize, Deserialize, Default, Debug, Hash, Eq, PartialEq)]\npub struct Mebibytes(usize);\n\nimpl FromStr for Mebibytes {\n type Err = ParseIntError;\n\n fn from_str(s: \u0026str) -\u003e Result\u003cSelf, Self::Err\u003e {\n s.parse::\u003cusize\u003e().map(Self)\n }\n}\n\npub struct Bytes(usize);\n\nimpl Bytes {\n pub const fn get(\u0026self) -\u003e usize {\n self.0\n }\n}\n\nimpl From\u003cMebibytes\u003e for Bytes {\n fn from(mib: Mebibytes) -\u003e Self {\n Self(mib.0 \u003c\u003c 20)\n }\n}\n\n#[derive(Copy, Clone, Deserialize, Debug, Hash, Eq, PartialEq)]\npub struct KilobitsPerSecond(NonZeroU64);\n\nimpl FromStr for KilobitsPerSecond {\n type Err = ParseIntError;\n\n fn from_str(s: \u0026str) -\u003e Result\u003cSelf, Self::Err\u003e {\n s.parse::\u003cNonZeroU64\u003e().map(Self)\n }\n}\n\n#[derive(Copy, Clone, Serialize, Debug, Hash, Eq, PartialEq)]\npub struct BytesPerSecond(NonZeroU64);\n\nimpl From\u003cKilobitsPerSecond\u003e for BytesPerSecond {\n fn from(kbps: KilobitsPerSecond) -\u003e Self {\n Self(unsafe { NonZeroU64::new_unchecked(kbps.0.get() * 125) })\n }\n}\n","traces":[{"line":12,"address":[4126240],"length":1,"stats":{"Line":0},"fn_name":"get"},{"line":13,"address":[4126249],"length":1,"stats":{"Line":0},"fn_name":null},{"line":18,"address":[4126272],"length":1,"stats":{"Line":1},"fn_name":"default"},{"line":19,"address":[4126273],"length":1,"stats":{"Line":1},"fn_name":null},{"line":26,"address":[4126320],"length":1,"stats":{"Line":0},"fn_name":"from_str"},{"line":27,"address":[4126334],"length":1,"stats":{"Line":0},"fn_name":null},{"line":32,"address":[4126384],"length":1,"stats":{"Line":0},"fn_name":"fmt"},{"line":33,"address":[4126398],"length":1,"stats":{"Line":0},"fn_name":null},{"line":43,"address":[4126432],"length":1,"stats":{"Line":0},"fn_name":"from_str"},{"line":44,"address":[4126446],"length":1,"stats":{"Line":0},"fn_name":null},{"line":51,"address":[4126560],"length":1,"stats":{"Line":0},"fn_name":"get"},{"line":52,"address":[4126565],"length":1,"stats":{"Line":0},"fn_name":null},{"line":57,"address":[4126576],"length":1,"stats":{"Line":0},"fn_name":"from"},{"line":58,"address":[4126585],"length":1,"stats":{"Line":0},"fn_name":null},{"line":68,"address":[4126624],"length":1,"stats":{"Line":0},"fn_name":"from_str"},{"line":69,"address":[4126638],"length":1,"stats":{"Line":0},"fn_name":null},{"line":77,"address":[4126736],"length":1,"stats":{"Line":0},"fn_name":"from"},{"line":78,"address":[4126745,4126813],"length":1,"stats":{"Line":0},"fn_name":null}],"covered":2,"coverable":18}]};
|
|
</script>
|
|
<script crossorigin>/** @license React v16.13.1
|
|
* react.production.min.js
|
|
*
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*/
|
|
'use strict';(function(d,r){"object"===typeof exports&&"undefined"!==typeof module?r(exports):"function"===typeof define&&define.amd?define(["exports"],r):(d=d||self,r(d.React={}))})(this,function(d){function r(a){for(var b="https://reactjs.org/docs/error-decoder.html?invariant="+a,c=1;c<arguments.length;c++)b+="&args[]="+encodeURIComponent(arguments[c]);return"Minified React error #"+a+"; visit "+b+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings."}
|
|
function w(a,b,c){this.props=a;this.context=b;this.refs=ba;this.updater=c||ca}function da(){}function L(a,b,c){this.props=a;this.context=b;this.refs=ba;this.updater=c||ca}function ea(a,b,c){var g,e={},fa=null,d=null;if(null!=b)for(g in void 0!==b.ref&&(d=b.ref),void 0!==b.key&&(fa=""+b.key),b)ha.call(b,g)&&!ia.hasOwnProperty(g)&&(e[g]=b[g]);var h=arguments.length-2;if(1===h)e.children=c;else if(1<h){for(var k=Array(h),f=0;f<h;f++)k[f]=arguments[f+2];e.children=k}if(a&&a.defaultProps)for(g in h=a.defaultProps,
|
|
h)void 0===e[g]&&(e[g]=h[g]);return{$$typeof:x,type:a,key:fa,ref:d,props:e,_owner:M.current}}function va(a,b){return{$$typeof:x,type:a.type,key:b,ref:a.ref,props:a.props,_owner:a._owner}}function N(a){return"object"===typeof a&&null!==a&&a.$$typeof===x}function wa(a){var b={"=":"=0",":":"=2"};return"$"+(""+a).replace(/[=:]/g,function(a){return b[a]})}function ja(a,b,c,g){if(C.length){var e=C.pop();e.result=a;e.keyPrefix=b;e.func=c;e.context=g;e.count=0;return e}return{result:a,keyPrefix:b,func:c,
|
|
context:g,count:0}}function ka(a){a.result=null;a.keyPrefix=null;a.func=null;a.context=null;a.count=0;10>C.length&&C.push(a)}function O(a,b,c,g){var e=typeof a;if("undefined"===e||"boolean"===e)a=null;var d=!1;if(null===a)d=!0;else switch(e){case "string":case "number":d=!0;break;case "object":switch(a.$$typeof){case x:case xa:d=!0}}if(d)return c(g,a,""===b?"."+P(a,0):b),1;d=0;b=""===b?".":b+":";if(Array.isArray(a))for(var f=0;f<a.length;f++){e=a[f];var h=b+P(e,f);d+=O(e,h,c,g)}else if(null===a||
|
|
"object"!==typeof a?h=null:(h=la&&a[la]||a["@@iterator"],h="function"===typeof h?h:null),"function"===typeof h)for(a=h.call(a),f=0;!(e=a.next()).done;)e=e.value,h=b+P(e,f++),d+=O(e,h,c,g);else if("object"===e)throw c=""+a,Error(r(31,"[object Object]"===c?"object with keys {"+Object.keys(a).join(", ")+"}":c,""));return d}function Q(a,b,c){return null==a?0:O(a,"",b,c)}function P(a,b){return"object"===typeof a&&null!==a&&null!=a.key?wa(a.key):b.toString(36)}function ya(a,b,c){a.func.call(a.context,b,
|
|
a.count++)}function za(a,b,c){var g=a.result,e=a.keyPrefix;a=a.func.call(a.context,b,a.count++);Array.isArray(a)?R(a,g,c,function(a){return a}):null!=a&&(N(a)&&(a=va(a,e+(!a.key||b&&b.key===a.key?"":(""+a.key).replace(ma,"$&/")+"/")+c)),g.push(a))}function R(a,b,c,g,e){var d="";null!=c&&(d=(""+c).replace(ma,"$&/")+"/");b=ja(b,d,g,e);Q(a,za,b);ka(b)}function t(){var a=na.current;if(null===a)throw Error(r(321));return a}function S(a,b){var c=a.length;a.push(b);a:for(;;){var g=c-1>>>1,e=a[g];if(void 0!==
|
|
e&&0<D(e,b))a[g]=b,a[c]=e,c=g;else break a}}function n(a){a=a[0];return void 0===a?null:a}function E(a){var b=a[0];if(void 0!==b){var c=a.pop();if(c!==b){a[0]=c;a:for(var g=0,e=a.length;g<e;){var d=2*(g+1)-1,f=a[d],h=d+1,k=a[h];if(void 0!==f&&0>D(f,c))void 0!==k&&0>D(k,f)?(a[g]=k,a[h]=c,g=h):(a[g]=f,a[d]=c,g=d);else if(void 0!==k&&0>D(k,c))a[g]=k,a[h]=c,g=h;else break a}}return b}return null}function D(a,b){var c=a.sortIndex-b.sortIndex;return 0!==c?c:a.id-b.id}function F(a){for(var b=n(u);null!==
|
|
b;){if(null===b.callback)E(u);else if(b.startTime<=a)E(u),b.sortIndex=b.expirationTime,S(p,b);else break;b=n(u)}}function T(a){y=!1;F(a);if(!v)if(null!==n(p))v=!0,z(U);else{var b=n(u);null!==b&&G(T,b.startTime-a)}}function U(a,b){v=!1;y&&(y=!1,V());H=!0;var c=m;try{F(b);for(l=n(p);null!==l&&(!(l.expirationTime>b)||a&&!W());){var g=l.callback;if(null!==g){l.callback=null;m=l.priorityLevel;var e=g(l.expirationTime<=b);b=q();"function"===typeof e?l.callback=e:l===n(p)&&E(p);F(b)}else E(p);l=n(p)}if(null!==
|
|
l)var d=!0;else{var f=n(u);null!==f&&G(T,f.startTime-b);d=!1}return d}finally{l=null,m=c,H=!1}}function oa(a){switch(a){case 1:return-1;case 2:return 250;case 5:return 1073741823;case 4:return 1E4;default:return 5E3}}var f="function"===typeof Symbol&&Symbol.for,x=f?Symbol.for("react.element"):60103,xa=f?Symbol.for("react.portal"):60106,Aa=f?Symbol.for("react.fragment"):60107,Ba=f?Symbol.for("react.strict_mode"):60108,Ca=f?Symbol.for("react.profiler"):60114,Da=f?Symbol.for("react.provider"):60109,
|
|
Ea=f?Symbol.for("react.context"):60110,Fa=f?Symbol.for("react.forward_ref"):60112,Ga=f?Symbol.for("react.suspense"):60113,Ha=f?Symbol.for("react.memo"):60115,Ia=f?Symbol.for("react.lazy"):60116,la="function"===typeof Symbol&&Symbol.iterator,pa=Object.getOwnPropertySymbols,Ja=Object.prototype.hasOwnProperty,Ka=Object.prototype.propertyIsEnumerable,I=function(){try{if(!Object.assign)return!1;var a=new String("abc");a[5]="de";if("5"===Object.getOwnPropertyNames(a)[0])return!1;var b={};for(a=0;10>a;a++)b["_"+
|
|
String.fromCharCode(a)]=a;if("0123456789"!==Object.getOwnPropertyNames(b).map(function(a){return b[a]}).join(""))return!1;var c={};"abcdefghijklmnopqrst".split("").forEach(function(a){c[a]=a});return"abcdefghijklmnopqrst"!==Object.keys(Object.assign({},c)).join("")?!1:!0}catch(g){return!1}}()?Object.assign:function(a,b){if(null===a||void 0===a)throw new TypeError("Object.assign cannot be called with null or undefined");var c=Object(a);for(var g,e=1;e<arguments.length;e++){var d=Object(arguments[e]);
|
|
for(var f in d)Ja.call(d,f)&&(c[f]=d[f]);if(pa){g=pa(d);for(var h=0;h<g.length;h++)Ka.call(d,g[h])&&(c[g[h]]=d[g[h]])}}return c},ca={isMounted:function(a){return!1},enqueueForceUpdate:function(a,b,c){},enqueueReplaceState:function(a,b,c,d){},enqueueSetState:function(a,b,c,d){}},ba={};w.prototype.isReactComponent={};w.prototype.setState=function(a,b){if("object"!==typeof a&&"function"!==typeof a&&null!=a)throw Error(r(85));this.updater.enqueueSetState(this,a,b,"setState")};w.prototype.forceUpdate=
|
|
function(a){this.updater.enqueueForceUpdate(this,a,"forceUpdate")};da.prototype=w.prototype;f=L.prototype=new da;f.constructor=L;I(f,w.prototype);f.isPureReactComponent=!0;var M={current:null},ha=Object.prototype.hasOwnProperty,ia={key:!0,ref:!0,__self:!0,__source:!0},ma=/\/+/g,C=[],na={current:null},X;if("undefined"===typeof window||"function"!==typeof MessageChannel){var A=null,qa=null,ra=function(){if(null!==A)try{var a=q();A(!0,a);A=null}catch(b){throw setTimeout(ra,0),b;}},La=Date.now();var q=
|
|
function(){return Date.now()-La};var z=function(a){null!==A?setTimeout(z,0,a):(A=a,setTimeout(ra,0))};var G=function(a,b){qa=setTimeout(a,b)};var V=function(){clearTimeout(qa)};var W=function(){return!1};f=X=function(){}}else{var Y=window.performance,sa=window.Date,Ma=window.setTimeout,Na=window.clearTimeout;"undefined"!==typeof console&&(f=window.cancelAnimationFrame,"function"!==typeof window.requestAnimationFrame&&console.error("This browser doesn't support requestAnimationFrame. Make sure that you load a polyfill in older browsers. https://fb.me/react-polyfills"),
|
|
"function"!==typeof f&&console.error("This browser doesn't support cancelAnimationFrame. Make sure that you load a polyfill in older browsers. https://fb.me/react-polyfills"));if("object"===typeof Y&&"function"===typeof Y.now)q=function(){return Y.now()};else{var Oa=sa.now();q=function(){return sa.now()-Oa}}var J=!1,K=null,Z=-1,ta=5,ua=0;W=function(){return q()>=ua};f=function(){};X=function(a){0>a||125<a?console.error("forceFrameRate takes a positive int between 0 and 125, forcing framerates higher than 125 fps is not unsupported"):
|
|
ta=0<a?Math.floor(1E3/a):5};var B=new MessageChannel,aa=B.port2;B.port1.onmessage=function(){if(null!==K){var a=q();ua=a+ta;try{K(!0,a)?aa.postMessage(null):(J=!1,K=null)}catch(b){throw aa.postMessage(null),b;}}else J=!1};z=function(a){K=a;J||(J=!0,aa.postMessage(null))};G=function(a,b){Z=Ma(function(){a(q())},b)};V=function(){Na(Z);Z=-1}}var p=[],u=[],Pa=1,l=null,m=3,H=!1,v=!1,y=!1,Qa=0;B={ReactCurrentDispatcher:na,ReactCurrentOwner:M,IsSomeRendererActing:{current:!1},assign:I};I(B,{Scheduler:{__proto__:null,
|
|
unstable_ImmediatePriority:1,unstable_UserBlockingPriority:2,unstable_NormalPriority:3,unstable_IdlePriority:5,unstable_LowPriority:4,unstable_runWithPriority:function(a,b){switch(a){case 1:case 2:case 3:case 4:case 5:break;default:a=3}var c=m;m=a;try{return b()}finally{m=c}},unstable_next:function(a){switch(m){case 1:case 2:case 3:var b=3;break;default:b=m}var c=m;m=b;try{return a()}finally{m=c}},unstable_scheduleCallback:function(a,b,c){var d=q();if("object"===typeof c&&null!==c){var e=c.delay;
|
|
e="number"===typeof e&&0<e?d+e:d;c="number"===typeof c.timeout?c.timeout:oa(a)}else c=oa(a),e=d;c=e+c;a={id:Pa++,callback:b,priorityLevel:a,startTime:e,expirationTime:c,sortIndex:-1};e>d?(a.sortIndex=e,S(u,a),null===n(p)&&a===n(u)&&(y?V():y=!0,G(T,e-d))):(a.sortIndex=c,S(p,a),v||H||(v=!0,z(U)));return a},unstable_cancelCallback:function(a){a.callback=null},unstable_wrapCallback:function(a){var b=m;return function(){var c=m;m=b;try{return a.apply(this,arguments)}finally{m=c}}},unstable_getCurrentPriorityLevel:function(){return m},
|
|
unstable_shouldYield:function(){var a=q();F(a);var b=n(p);return b!==l&&null!==l&&null!==b&&null!==b.callback&&b.startTime<=a&&b.expirationTime<l.expirationTime||W()},unstable_requestPaint:f,unstable_continueExecution:function(){v||H||(v=!0,z(U))},unstable_pauseExecution:function(){},unstable_getFirstCallbackNode:function(){return n(p)},get unstable_now(){return q},get unstable_forceFrameRate(){return X},unstable_Profiling:null},SchedulerTracing:{__proto__:null,__interactionsRef:null,__subscriberRef:null,
|
|
unstable_clear:function(a){return a()},unstable_getCurrent:function(){return null},unstable_getThreadID:function(){return++Qa},unstable_trace:function(a,b,c){return c()},unstable_wrap:function(a){return a},unstable_subscribe:function(a){},unstable_unsubscribe:function(a){}}});d.Children={map:function(a,b,c){if(null==a)return a;var d=[];R(a,d,null,b,c);return d},forEach:function(a,b,c){if(null==a)return a;b=ja(null,null,b,c);Q(a,ya,b);ka(b)},count:function(a){return Q(a,function(){return null},null)},
|
|
toArray:function(a){var b=[];R(a,b,null,function(a){return a});return b},only:function(a){if(!N(a))throw Error(r(143));return a}};d.Component=w;d.Fragment=Aa;d.Profiler=Ca;d.PureComponent=L;d.StrictMode=Ba;d.Suspense=Ga;d.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED=B;d.cloneElement=function(a,b,c){if(null===a||void 0===a)throw Error(r(267,a));var d=I({},a.props),e=a.key,f=a.ref,m=a._owner;if(null!=b){void 0!==b.ref&&(f=b.ref,m=M.current);void 0!==b.key&&(e=""+b.key);if(a.type&&a.type.defaultProps)var h=
|
|
a.type.defaultProps;for(k in b)ha.call(b,k)&&!ia.hasOwnProperty(k)&&(d[k]=void 0===b[k]&&void 0!==h?h[k]:b[k])}var k=arguments.length-2;if(1===k)d.children=c;else if(1<k){h=Array(k);for(var l=0;l<k;l++)h[l]=arguments[l+2];d.children=h}return{$$typeof:x,type:a.type,key:e,ref:f,props:d,_owner:m}};d.createContext=function(a,b){void 0===b&&(b=null);a={$$typeof:Ea,_calculateChangedBits:b,_currentValue:a,_currentValue2:a,_threadCount:0,Provider:null,Consumer:null};a.Provider={$$typeof:Da,_context:a};return a.Consumer=
|
|
a};d.createElement=ea;d.createFactory=function(a){var b=ea.bind(null,a);b.type=a;return b};d.createRef=function(){return{current:null}};d.forwardRef=function(a){return{$$typeof:Fa,render:a}};d.isValidElement=N;d.lazy=function(a){return{$$typeof:Ia,_ctor:a,_status:-1,_result:null}};d.memo=function(a,b){return{$$typeof:Ha,type:a,compare:void 0===b?null:b}};d.useCallback=function(a,b){return t().useCallback(a,b)};d.useContext=function(a,b){return t().useContext(a,b)};d.useDebugValue=function(a,b){};
|
|
d.useEffect=function(a,b){return t().useEffect(a,b)};d.useImperativeHandle=function(a,b,c){return t().useImperativeHandle(a,b,c)};d.useLayoutEffect=function(a,b){return t().useLayoutEffect(a,b)};d.useMemo=function(a,b){return t().useMemo(a,b)};d.useReducer=function(a,b,c){return t().useReducer(a,b,c)};d.useRef=function(a){return t().useRef(a)};d.useState=function(a){return t().useState(a)};d.version="16.13.1"});
|
|
</script>
|
|
<script crossorigin>/** @license React v16.13.1
|
|
* react-dom.production.min.js
|
|
*
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*/
|
|
/*
|
|
Modernizr 3.0.0pre (Custom Build) | MIT
|
|
*/
|
|
'use strict';(function(I,ea){"object"===typeof exports&&"undefined"!==typeof module?ea(exports,require("react")):"function"===typeof define&&define.amd?define(["exports","react"],ea):(I=I||self,ea(I.ReactDOM={},I.React))})(this,function(I,ea){function k(a){for(var b="https://reactjs.org/docs/error-decoder.html?invariant="+a,c=1;c<arguments.length;c++)b+="&args[]="+encodeURIComponent(arguments[c]);return"Minified React error #"+a+"; visit "+b+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings."}
|
|
function ji(a,b,c,d,e,f,g,h,m){yb=!1;gc=null;ki.apply(li,arguments)}function mi(a,b,c,d,e,f,g,h,m){ji.apply(this,arguments);if(yb){if(yb){var n=gc;yb=!1;gc=null}else throw Error(k(198));hc||(hc=!0,pd=n)}}function lf(a,b,c){var d=a.type||"unknown-event";a.currentTarget=mf(c);mi(d,b,void 0,a);a.currentTarget=null}function nf(){if(ic)for(var a in cb){var b=cb[a],c=ic.indexOf(a);if(!(-1<c))throw Error(k(96,a));if(!jc[c]){if(!b.extractEvents)throw Error(k(97,a));jc[c]=b;c=b.eventTypes;for(var d in c){var e=
|
|
void 0;var f=c[d],g=b,h=d;if(qd.hasOwnProperty(h))throw Error(k(99,h));qd[h]=f;var m=f.phasedRegistrationNames;if(m){for(e in m)m.hasOwnProperty(e)&&of(m[e],g,h);e=!0}else f.registrationName?(of(f.registrationName,g,h),e=!0):e=!1;if(!e)throw Error(k(98,d,a));}}}}function of(a,b,c){if(db[a])throw Error(k(100,a));db[a]=b;rd[a]=b.eventTypes[c].dependencies}function pf(a){var b=!1,c;for(c in a)if(a.hasOwnProperty(c)){var d=a[c];if(!cb.hasOwnProperty(c)||cb[c]!==d){if(cb[c])throw Error(k(102,c));cb[c]=
|
|
d;b=!0}}b&&nf()}function qf(a){if(a=rf(a)){if("function"!==typeof sd)throw Error(k(280));var b=a.stateNode;b&&(b=td(b),sd(a.stateNode,a.type,b))}}function sf(a){eb?fb?fb.push(a):fb=[a]:eb=a}function tf(){if(eb){var a=eb,b=fb;fb=eb=null;qf(a);if(b)for(a=0;a<b.length;a++)qf(b[a])}}function ud(){if(null!==eb||null!==fb)vd(),tf()}function uf(a,b,c){if(wd)return a(b,c);wd=!0;try{return vf(a,b,c)}finally{wd=!1,ud()}}function ni(a){if(wf.call(xf,a))return!0;if(wf.call(yf,a))return!1;if(oi.test(a))return xf[a]=
|
|
!0;yf[a]=!0;return!1}function pi(a,b,c,d){if(null!==c&&0===c.type)return!1;switch(typeof b){case "function":case "symbol":return!0;case "boolean":if(d)return!1;if(null!==c)return!c.acceptsBooleans;a=a.toLowerCase().slice(0,5);return"data-"!==a&&"aria-"!==a;default:return!1}}function qi(a,b,c,d){if(null===b||"undefined"===typeof b||pi(a,b,c,d))return!0;if(d)return!1;if(null!==c)switch(c.type){case 3:return!b;case 4:return!1===b;case 5:return isNaN(b);case 6:return isNaN(b)||1>b}return!1}function L(a,
|
|
b,c,d,e,f){this.acceptsBooleans=2===b||3===b||4===b;this.attributeName=d;this.attributeNamespace=e;this.mustUseProperty=c;this.propertyName=a;this.type=b;this.sanitizeURL=f}function xd(a,b,c,d){var e=E.hasOwnProperty(b)?E[b]:null;var f=null!==e?0===e.type:d?!1:!(2<b.length)||"o"!==b[0]&&"O"!==b[0]||"n"!==b[1]&&"N"!==b[1]?!1:!0;f||(qi(b,c,e,d)&&(c=null),d||null===e?ni(b)&&(null===c?a.removeAttribute(b):a.setAttribute(b,""+c)):e.mustUseProperty?a[e.propertyName]=null===c?3===e.type?!1:"":c:(b=e.attributeName,
|
|
d=e.attributeNamespace,null===c?a.removeAttribute(b):(e=e.type,c=3===e||4===e&&!0===c?"":""+c,d?a.setAttributeNS(d,b,c):a.setAttribute(b,c))))}function zb(a){if(null===a||"object"!==typeof a)return null;a=zf&&a[zf]||a["@@iterator"];return"function"===typeof a?a:null}function ri(a){if(-1===a._status){a._status=0;var b=a._ctor;b=b();a._result=b;b.then(function(b){0===a._status&&(b=b.default,a._status=1,a._result=b)},function(b){0===a._status&&(a._status=2,a._result=b)})}}function na(a){if(null==a)return null;
|
|
if("function"===typeof a)return a.displayName||a.name||null;if("string"===typeof a)return a;switch(a){case Ma:return"Fragment";case gb:return"Portal";case kc:return"Profiler";case Af:return"StrictMode";case lc:return"Suspense";case yd:return"SuspenseList"}if("object"===typeof a)switch(a.$$typeof){case Bf:return"Context.Consumer";case Cf:return"Context.Provider";case zd:var b=a.render;b=b.displayName||b.name||"";return a.displayName||(""!==b?"ForwardRef("+b+")":"ForwardRef");case Ad:return na(a.type);
|
|
case Df:return na(a.render);case Ef:if(a=1===a._status?a._result:null)return na(a)}return null}function Bd(a){var b="";do{a:switch(a.tag){case 3:case 4:case 6:case 7:case 10:case 9:var c="";break a;default:var d=a._debugOwner,e=a._debugSource,f=na(a.type);c=null;d&&(c=na(d.type));d=f;f="";e?f=" (at "+e.fileName.replace(si,"")+":"+e.lineNumber+")":c&&(f=" (created by "+c+")");c="\n in "+(d||"Unknown")+f}b+=c;a=a.return}while(a);return b}function va(a){switch(typeof a){case "boolean":case "number":case "object":case "string":case "undefined":return a;
|
|
default:return""}}function Ff(a){var b=a.type;return(a=a.nodeName)&&"input"===a.toLowerCase()&&("checkbox"===b||"radio"===b)}function ti(a){var b=Ff(a)?"checked":"value",c=Object.getOwnPropertyDescriptor(a.constructor.prototype,b),d=""+a[b];if(!a.hasOwnProperty(b)&&"undefined"!==typeof c&&"function"===typeof c.get&&"function"===typeof c.set){var e=c.get,f=c.set;Object.defineProperty(a,b,{configurable:!0,get:function(){return e.call(this)},set:function(a){d=""+a;f.call(this,a)}});Object.defineProperty(a,
|
|
b,{enumerable:c.enumerable});return{getValue:function(){return d},setValue:function(a){d=""+a},stopTracking:function(){a._valueTracker=null;delete a[b]}}}}function mc(a){a._valueTracker||(a._valueTracker=ti(a))}function Gf(a){if(!a)return!1;var b=a._valueTracker;if(!b)return!0;var c=b.getValue();var d="";a&&(d=Ff(a)?a.checked?"true":"false":a.value);a=d;return a!==c?(b.setValue(a),!0):!1}function Cd(a,b){var c=b.checked;return M({},b,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:null!=
|
|
c?c:a._wrapperState.initialChecked})}function Hf(a,b){var c=null==b.defaultValue?"":b.defaultValue,d=null!=b.checked?b.checked:b.defaultChecked;c=va(null!=b.value?b.value:c);a._wrapperState={initialChecked:d,initialValue:c,controlled:"checkbox"===b.type||"radio"===b.type?null!=b.checked:null!=b.value}}function If(a,b){b=b.checked;null!=b&&xd(a,"checked",b,!1)}function Dd(a,b){If(a,b);var c=va(b.value),d=b.type;if(null!=c)if("number"===d){if(0===c&&""===a.value||a.value!=c)a.value=""+c}else a.value!==
|
|
""+c&&(a.value=""+c);else if("submit"===d||"reset"===d){a.removeAttribute("value");return}b.hasOwnProperty("value")?Ed(a,b.type,c):b.hasOwnProperty("defaultValue")&&Ed(a,b.type,va(b.defaultValue));null==b.checked&&null!=b.defaultChecked&&(a.defaultChecked=!!b.defaultChecked)}function Jf(a,b,c){if(b.hasOwnProperty("value")||b.hasOwnProperty("defaultValue")){var d=b.type;if(!("submit"!==d&&"reset"!==d||void 0!==b.value&&null!==b.value))return;b=""+a._wrapperState.initialValue;c||b===a.value||(a.value=
|
|
b);a.defaultValue=b}c=a.name;""!==c&&(a.name="");a.defaultChecked=!!a._wrapperState.initialChecked;""!==c&&(a.name=c)}function Ed(a,b,c){if("number"!==b||a.ownerDocument.activeElement!==a)null==c?a.defaultValue=""+a._wrapperState.initialValue:a.defaultValue!==""+c&&(a.defaultValue=""+c)}function ui(a){var b="";ea.Children.forEach(a,function(a){null!=a&&(b+=a)});return b}function Fd(a,b){a=M({children:void 0},b);if(b=ui(b.children))a.children=b;return a}function hb(a,b,c,d){a=a.options;if(b){b={};
|
|
for(var e=0;e<c.length;e++)b["$"+c[e]]=!0;for(c=0;c<a.length;c++)e=b.hasOwnProperty("$"+a[c].value),a[c].selected!==e&&(a[c].selected=e),e&&d&&(a[c].defaultSelected=!0)}else{c=""+va(c);b=null;for(e=0;e<a.length;e++){if(a[e].value===c){a[e].selected=!0;d&&(a[e].defaultSelected=!0);return}null!==b||a[e].disabled||(b=a[e])}null!==b&&(b.selected=!0)}}function Gd(a,b){if(null!=b.dangerouslySetInnerHTML)throw Error(k(91));return M({},b,{value:void 0,defaultValue:void 0,children:""+a._wrapperState.initialValue})}
|
|
function Kf(a,b){var c=b.value;if(null==c){c=b.children;b=b.defaultValue;if(null!=c){if(null!=b)throw Error(k(92));if(Array.isArray(c)){if(!(1>=c.length))throw Error(k(93));c=c[0]}b=c}null==b&&(b="");c=b}a._wrapperState={initialValue:va(c)}}function Lf(a,b){var c=va(b.value),d=va(b.defaultValue);null!=c&&(c=""+c,c!==a.value&&(a.value=c),null==b.defaultValue&&a.defaultValue!==c&&(a.defaultValue=c));null!=d&&(a.defaultValue=""+d)}function Mf(a,b){b=a.textContent;b===a._wrapperState.initialValue&&""!==
|
|
b&&null!==b&&(a.value=b)}function Nf(a){switch(a){case "svg":return"http://www.w3.org/2000/svg";case "math":return"http://www.w3.org/1998/Math/MathML";default:return"http://www.w3.org/1999/xhtml"}}function Hd(a,b){return null==a||"http://www.w3.org/1999/xhtml"===a?Nf(b):"http://www.w3.org/2000/svg"===a&&"foreignObject"===b?"http://www.w3.org/1999/xhtml":a}function nc(a,b){var c={};c[a.toLowerCase()]=b.toLowerCase();c["Webkit"+a]="webkit"+b;c["Moz"+a]="moz"+b;return c}function oc(a){if(Id[a])return Id[a];
|
|
if(!ib[a])return a;var b=ib[a],c;for(c in b)if(b.hasOwnProperty(c)&&c in Of)return Id[a]=b[c];return a}function Jd(a){var b=Pf.get(a);void 0===b&&(b=new Map,Pf.set(a,b));return b}function Na(a){var b=a,c=a;if(a.alternate)for(;b.return;)b=b.return;else{a=b;do b=a,0!==(b.effectTag&1026)&&(c=b.return),a=b.return;while(a)}return 3===b.tag?c:null}function Qf(a){if(13===a.tag){var b=a.memoizedState;null===b&&(a=a.alternate,null!==a&&(b=a.memoizedState));if(null!==b)return b.dehydrated}return null}function Rf(a){if(Na(a)!==
|
|
a)throw Error(k(188));}function vi(a){var b=a.alternate;if(!b){b=Na(a);if(null===b)throw Error(k(188));return b!==a?null:a}for(var c=a,d=b;;){var e=c.return;if(null===e)break;var f=e.alternate;if(null===f){d=e.return;if(null!==d){c=d;continue}break}if(e.child===f.child){for(f=e.child;f;){if(f===c)return Rf(e),a;if(f===d)return Rf(e),b;f=f.sibling}throw Error(k(188));}if(c.return!==d.return)c=e,d=f;else{for(var g=!1,h=e.child;h;){if(h===c){g=!0;c=e;d=f;break}if(h===d){g=!0;d=e;c=f;break}h=h.sibling}if(!g){for(h=
|
|
f.child;h;){if(h===c){g=!0;c=f;d=e;break}if(h===d){g=!0;d=f;c=e;break}h=h.sibling}if(!g)throw Error(k(189));}}if(c.alternate!==d)throw Error(k(190));}if(3!==c.tag)throw Error(k(188));return c.stateNode.current===c?a:b}function Sf(a){a=vi(a);if(!a)return null;for(var b=a;;){if(5===b.tag||6===b.tag)return b;if(b.child)b.child.return=b,b=b.child;else{if(b===a)break;for(;!b.sibling;){if(!b.return||b.return===a)return null;b=b.return}b.sibling.return=b.return;b=b.sibling}}return null}function jb(a,b){if(null==
|
|
b)throw Error(k(30));if(null==a)return b;if(Array.isArray(a)){if(Array.isArray(b))return a.push.apply(a,b),a;a.push(b);return a}return Array.isArray(b)?[a].concat(b):[a,b]}function Kd(a,b,c){Array.isArray(a)?a.forEach(b,c):a&&b.call(c,a)}function pc(a){null!==a&&(Ab=jb(Ab,a));a=Ab;Ab=null;if(a){Kd(a,wi);if(Ab)throw Error(k(95));if(hc)throw a=pd,hc=!1,pd=null,a;}}function Ld(a){a=a.target||a.srcElement||window;a.correspondingUseElement&&(a=a.correspondingUseElement);return 3===a.nodeType?a.parentNode:
|
|
a}function Tf(a){if(!wa)return!1;a="on"+a;var b=a in document;b||(b=document.createElement("div"),b.setAttribute(a,"return;"),b="function"===typeof b[a]);return b}function Uf(a){a.topLevelType=null;a.nativeEvent=null;a.targetInst=null;a.ancestors.length=0;10>qc.length&&qc.push(a)}function Vf(a,b,c,d){if(qc.length){var e=qc.pop();e.topLevelType=a;e.eventSystemFlags=d;e.nativeEvent=b;e.targetInst=c;return e}return{topLevelType:a,eventSystemFlags:d,nativeEvent:b,targetInst:c,ancestors:[]}}function Wf(a){var b=
|
|
a.targetInst,c=b;do{if(!c){a.ancestors.push(c);break}var d=c;if(3===d.tag)d=d.stateNode.containerInfo;else{for(;d.return;)d=d.return;d=3!==d.tag?null:d.stateNode.containerInfo}if(!d)break;b=c.tag;5!==b&&6!==b||a.ancestors.push(c);c=Bb(d)}while(c);for(c=0;c<a.ancestors.length;c++){b=a.ancestors[c];var e=Ld(a.nativeEvent);d=a.topLevelType;var f=a.nativeEvent,g=a.eventSystemFlags;0===c&&(g|=64);for(var h=null,m=0;m<jc.length;m++){var n=jc[m];n&&(n=n.extractEvents(d,b,f,e,g))&&(h=jb(h,n))}pc(h)}}function Md(a,
|
|
b,c){if(!c.has(a)){switch(a){case "scroll":Cb(b,"scroll",!0);break;case "focus":case "blur":Cb(b,"focus",!0);Cb(b,"blur",!0);c.set("blur",null);c.set("focus",null);break;case "cancel":case "close":Tf(a)&&Cb(b,a,!0);break;case "invalid":case "submit":case "reset":break;default:-1===Db.indexOf(a)&&w(a,b)}c.set(a,null)}}function xi(a,b){var c=Jd(b);Nd.forEach(function(a){Md(a,b,c)});yi.forEach(function(a){Md(a,b,c)})}function Od(a,b,c,d,e){return{blockedOn:a,topLevelType:b,eventSystemFlags:c|32,nativeEvent:e,
|
|
container:d}}function Xf(a,b){switch(a){case "focus":case "blur":xa=null;break;case "dragenter":case "dragleave":ya=null;break;case "mouseover":case "mouseout":za=null;break;case "pointerover":case "pointerout":Eb.delete(b.pointerId);break;case "gotpointercapture":case "lostpointercapture":Fb.delete(b.pointerId)}}function Gb(a,b,c,d,e,f){if(null===a||a.nativeEvent!==f)return a=Od(b,c,d,e,f),null!==b&&(b=Hb(b),null!==b&&Yf(b)),a;a.eventSystemFlags|=d;return a}function zi(a,b,c,d,e){switch(b){case "focus":return xa=
|
|
Gb(xa,a,b,c,d,e),!0;case "dragenter":return ya=Gb(ya,a,b,c,d,e),!0;case "mouseover":return za=Gb(za,a,b,c,d,e),!0;case "pointerover":var f=e.pointerId;Eb.set(f,Gb(Eb.get(f)||null,a,b,c,d,e));return!0;case "gotpointercapture":return f=e.pointerId,Fb.set(f,Gb(Fb.get(f)||null,a,b,c,d,e)),!0}return!1}function Ai(a){var b=Bb(a.target);if(null!==b){var c=Na(b);if(null!==c)if(b=c.tag,13===b){if(b=Qf(c),null!==b){a.blockedOn=b;Pd(a.priority,function(){Bi(c)});return}}else if(3===b&&c.stateNode.hydrate){a.blockedOn=
|
|
3===c.tag?c.stateNode.containerInfo:null;return}}a.blockedOn=null}function rc(a){if(null!==a.blockedOn)return!1;var b=Qd(a.topLevelType,a.eventSystemFlags,a.container,a.nativeEvent);if(null!==b){var c=Hb(b);null!==c&&Yf(c);a.blockedOn=b;return!1}return!0}function Zf(a,b,c){rc(a)&&c.delete(b)}function Ci(){for(Rd=!1;0<fa.length;){var a=fa[0];if(null!==a.blockedOn){a=Hb(a.blockedOn);null!==a&&Di(a);break}var b=Qd(a.topLevelType,a.eventSystemFlags,a.container,a.nativeEvent);null!==b?a.blockedOn=b:fa.shift()}null!==
|
|
xa&&rc(xa)&&(xa=null);null!==ya&&rc(ya)&&(ya=null);null!==za&&rc(za)&&(za=null);Eb.forEach(Zf);Fb.forEach(Zf)}function Ib(a,b){a.blockedOn===b&&(a.blockedOn=null,Rd||(Rd=!0,$f(ag,Ci)))}function bg(a){if(0<fa.length){Ib(fa[0],a);for(var b=1;b<fa.length;b++){var c=fa[b];c.blockedOn===a&&(c.blockedOn=null)}}null!==xa&&Ib(xa,a);null!==ya&&Ib(ya,a);null!==za&&Ib(za,a);b=function(b){return Ib(b,a)};Eb.forEach(b);Fb.forEach(b);for(b=0;b<Jb.length;b++)c=Jb[b],c.blockedOn===a&&(c.blockedOn=null);for(;0<Jb.length&&
|
|
(b=Jb[0],null===b.blockedOn);)Ai(b),null===b.blockedOn&&Jb.shift()}function Sd(a,b){for(var c=0;c<a.length;c+=2){var d=a[c],e=a[c+1],f="on"+(e[0].toUpperCase()+e.slice(1));f={phasedRegistrationNames:{bubbled:f,captured:f+"Capture"},dependencies:[d],eventPriority:b};Td.set(d,b);cg.set(d,f);dg[e]=f}}function w(a,b){Cb(b,a,!1)}function Cb(a,b,c){var d=Td.get(b);switch(void 0===d?2:d){case 0:d=Ei.bind(null,b,1,a);break;case 1:d=Fi.bind(null,b,1,a);break;default:d=sc.bind(null,b,1,a)}c?a.addEventListener(b,
|
|
d,!0):a.addEventListener(b,d,!1)}function Ei(a,b,c,d){Oa||vd();var e=sc,f=Oa;Oa=!0;try{eg(e,a,b,c,d)}finally{(Oa=f)||ud()}}function Fi(a,b,c,d){Gi(Hi,sc.bind(null,a,b,c,d))}function sc(a,b,c,d){if(tc)if(0<fa.length&&-1<Nd.indexOf(a))a=Od(null,a,b,c,d),fa.push(a);else{var e=Qd(a,b,c,d);if(null===e)Xf(a,d);else if(-1<Nd.indexOf(a))a=Od(e,a,b,c,d),fa.push(a);else if(!zi(e,a,b,c,d)){Xf(a,d);a=Vf(a,d,null,b);try{uf(Wf,a)}finally{Uf(a)}}}}function Qd(a,b,c,d){c=Ld(d);c=Bb(c);if(null!==c){var e=Na(c);if(null===
|
|
e)c=null;else{var f=e.tag;if(13===f){c=Qf(e);if(null!==c)return c;c=null}else if(3===f){if(e.stateNode.hydrate)return 3===e.tag?e.stateNode.containerInfo:null;c=null}else e!==c&&(c=null)}}a=Vf(a,d,c,b);try{uf(Wf,a)}finally{Uf(a)}return null}function fg(a,b,c){return null==b||"boolean"===typeof b||""===b?"":c||"number"!==typeof b||0===b||Kb.hasOwnProperty(a)&&Kb[a]?(""+b).trim():b+"px"}function gg(a,b){a=a.style;for(var c in b)if(b.hasOwnProperty(c)){var d=0===c.indexOf("--"),e=fg(c,b[c],d);"float"===
|
|
c&&(c="cssFloat");d?a.setProperty(c,e):a[c]=e}}function Ud(a,b){if(b){if(Ii[a]&&(null!=b.children||null!=b.dangerouslySetInnerHTML))throw Error(k(137,a,""));if(null!=b.dangerouslySetInnerHTML){if(null!=b.children)throw Error(k(60));if(!("object"===typeof b.dangerouslySetInnerHTML&&"__html"in b.dangerouslySetInnerHTML))throw Error(k(61));}if(null!=b.style&&"object"!==typeof b.style)throw Error(k(62,""));}}function Vd(a,b){if(-1===a.indexOf("-"))return"string"===typeof b.is;switch(a){case "annotation-xml":case "color-profile":case "font-face":case "font-face-src":case "font-face-uri":case "font-face-format":case "font-face-name":case "missing-glyph":return!1;
|
|
default:return!0}}function oa(a,b){a=9===a.nodeType||11===a.nodeType?a:a.ownerDocument;var c=Jd(a);b=rd[b];for(var d=0;d<b.length;d++)Md(b[d],a,c)}function uc(){}function Wd(a){a=a||("undefined"!==typeof document?document:void 0);if("undefined"===typeof a)return null;try{return a.activeElement||a.body}catch(b){return a.body}}function hg(a){for(;a&&a.firstChild;)a=a.firstChild;return a}function ig(a,b){var c=hg(a);a=0;for(var d;c;){if(3===c.nodeType){d=a+c.textContent.length;if(a<=b&&d>=b)return{node:c,
|
|
offset:b-a};a=d}a:{for(;c;){if(c.nextSibling){c=c.nextSibling;break a}c=c.parentNode}c=void 0}c=hg(c)}}function jg(a,b){return a&&b?a===b?!0:a&&3===a.nodeType?!1:b&&3===b.nodeType?jg(a,b.parentNode):"contains"in a?a.contains(b):a.compareDocumentPosition?!!(a.compareDocumentPosition(b)&16):!1:!1}function kg(){for(var a=window,b=Wd();b instanceof a.HTMLIFrameElement;){try{var c="string"===typeof b.contentWindow.location.href}catch(d){c=!1}if(c)a=b.contentWindow;else break;b=Wd(a.document)}return b}
|
|
function Xd(a){var b=a&&a.nodeName&&a.nodeName.toLowerCase();return b&&("input"===b&&("text"===a.type||"search"===a.type||"tel"===a.type||"url"===a.type||"password"===a.type)||"textarea"===b||"true"===a.contentEditable)}function lg(a,b){switch(a){case "button":case "input":case "select":case "textarea":return!!b.autoFocus}return!1}function Yd(a,b){return"textarea"===a||"option"===a||"noscript"===a||"string"===typeof b.children||"number"===typeof b.children||"object"===typeof b.dangerouslySetInnerHTML&&
|
|
null!==b.dangerouslySetInnerHTML&&null!=b.dangerouslySetInnerHTML.__html}function kb(a){for(;null!=a;a=a.nextSibling){var b=a.nodeType;if(1===b||3===b)break}return a}function mg(a){a=a.previousSibling;for(var b=0;a;){if(8===a.nodeType){var c=a.data;if(c===ng||c===Zd||c===$d){if(0===b)return a;b--}else c===og&&b++}a=a.previousSibling}return null}function Bb(a){var b=a[Aa];if(b)return b;for(var c=a.parentNode;c;){if(b=c[Lb]||c[Aa]){c=b.alternate;if(null!==b.child||null!==c&&null!==c.child)for(a=mg(a);null!==
|
|
a;){if(c=a[Aa])return c;a=mg(a)}return b}a=c;c=a.parentNode}return null}function Hb(a){a=a[Aa]||a[Lb];return!a||5!==a.tag&&6!==a.tag&&13!==a.tag&&3!==a.tag?null:a}function Pa(a){if(5===a.tag||6===a.tag)return a.stateNode;throw Error(k(33));}function ae(a){return a[vc]||null}function pa(a){do a=a.return;while(a&&5!==a.tag);return a?a:null}function pg(a,b){var c=a.stateNode;if(!c)return null;var d=td(c);if(!d)return null;c=d[b];a:switch(b){case "onClick":case "onClickCapture":case "onDoubleClick":case "onDoubleClickCapture":case "onMouseDown":case "onMouseDownCapture":case "onMouseMove":case "onMouseMoveCapture":case "onMouseUp":case "onMouseUpCapture":case "onMouseEnter":(d=
|
|
!d.disabled)||(a=a.type,d=!("button"===a||"input"===a||"select"===a||"textarea"===a));a=!d;break a;default:a=!1}if(a)return null;if(c&&"function"!==typeof c)throw Error(k(231,b,typeof c));return c}function qg(a,b,c){if(b=pg(a,c.dispatchConfig.phasedRegistrationNames[b]))c._dispatchListeners=jb(c._dispatchListeners,b),c._dispatchInstances=jb(c._dispatchInstances,a)}function Ji(a){if(a&&a.dispatchConfig.phasedRegistrationNames){for(var b=a._targetInst,c=[];b;)c.push(b),b=pa(b);for(b=c.length;0<b--;)qg(c[b],
|
|
"captured",a);for(b=0;b<c.length;b++)qg(c[b],"bubbled",a)}}function be(a,b,c){a&&c&&c.dispatchConfig.registrationName&&(b=pg(a,c.dispatchConfig.registrationName))&&(c._dispatchListeners=jb(c._dispatchListeners,b),c._dispatchInstances=jb(c._dispatchInstances,a))}function Ki(a){a&&a.dispatchConfig.registrationName&&be(a._targetInst,null,a)}function lb(a){Kd(a,Ji)}function rg(){if(wc)return wc;var a,b=ce,c=b.length,d,e="value"in Ba?Ba.value:Ba.textContent,f=e.length;for(a=0;a<c&&b[a]===e[a];a++);var g=
|
|
c-a;for(d=1;d<=g&&b[c-d]===e[f-d];d++);return wc=e.slice(a,1<d?1-d:void 0)}function xc(){return!0}function yc(){return!1}function R(a,b,c,d){this.dispatchConfig=a;this._targetInst=b;this.nativeEvent=c;a=this.constructor.Interface;for(var e in a)a.hasOwnProperty(e)&&((b=a[e])?this[e]=b(c):"target"===e?this.target=d:this[e]=c[e]);this.isDefaultPrevented=(null!=c.defaultPrevented?c.defaultPrevented:!1===c.returnValue)?xc:yc;this.isPropagationStopped=yc;return this}function Li(a,b,c,d){if(this.eventPool.length){var e=
|
|
this.eventPool.pop();this.call(e,a,b,c,d);return e}return new this(a,b,c,d)}function Mi(a){if(!(a instanceof this))throw Error(k(279));a.destructor();10>this.eventPool.length&&this.eventPool.push(a)}function sg(a){a.eventPool=[];a.getPooled=Li;a.release=Mi}function tg(a,b){switch(a){case "keyup":return-1!==Ni.indexOf(b.keyCode);case "keydown":return 229!==b.keyCode;case "keypress":case "mousedown":case "blur":return!0;default:return!1}}function ug(a){a=a.detail;return"object"===typeof a&&"data"in
|
|
a?a.data:null}function Oi(a,b){switch(a){case "compositionend":return ug(b);case "keypress":if(32!==b.which)return null;vg=!0;return wg;case "textInput":return a=b.data,a===wg&&vg?null:a;default:return null}}function Pi(a,b){if(mb)return"compositionend"===a||!de&&tg(a,b)?(a=rg(),wc=ce=Ba=null,mb=!1,a):null;switch(a){case "paste":return null;case "keypress":if(!(b.ctrlKey||b.altKey||b.metaKey)||b.ctrlKey&&b.altKey){if(b.char&&1<b.char.length)return b.char;if(b.which)return String.fromCharCode(b.which)}return null;
|
|
case "compositionend":return xg&&"ko"!==b.locale?null:b.data;default:return null}}function yg(a){var b=a&&a.nodeName&&a.nodeName.toLowerCase();return"input"===b?!!Qi[a.type]:"textarea"===b?!0:!1}function zg(a,b,c){a=R.getPooled(Ag.change,a,b,c);a.type="change";sf(c);lb(a);return a}function Ri(a){pc(a)}function zc(a){var b=Pa(a);if(Gf(b))return a}function Si(a,b){if("change"===a)return b}function Bg(){Mb&&(Mb.detachEvent("onpropertychange",Cg),Nb=Mb=null)}function Cg(a){if("value"===a.propertyName&&
|
|
zc(Nb))if(a=zg(Nb,a,Ld(a)),Oa)pc(a);else{Oa=!0;try{ee(Ri,a)}finally{Oa=!1,ud()}}}function Ti(a,b,c){"focus"===a?(Bg(),Mb=b,Nb=c,Mb.attachEvent("onpropertychange",Cg)):"blur"===a&&Bg()}function Ui(a,b){if("selectionchange"===a||"keyup"===a||"keydown"===a)return zc(Nb)}function Vi(a,b){if("click"===a)return zc(b)}function Wi(a,b){if("input"===a||"change"===a)return zc(b)}function Xi(a){var b=this.nativeEvent;return b.getModifierState?b.getModifierState(a):(a=Yi[a])?!!b[a]:!1}function fe(a){return Xi}
|
|
function Zi(a,b){return a===b&&(0!==a||1/a===1/b)||a!==a&&b!==b}function Ob(a,b){if(Qa(a,b))return!0;if("object"!==typeof a||null===a||"object"!==typeof b||null===b)return!1;var c=Object.keys(a),d=Object.keys(b);if(c.length!==d.length)return!1;for(d=0;d<c.length;d++)if(!$i.call(b,c[d])||!Qa(a[c[d]],b[c[d]]))return!1;return!0}function Dg(a,b){var c=b.window===b?b.document:9===b.nodeType?b:b.ownerDocument;if(ge||null==nb||nb!==Wd(c))return null;c=nb;"selectionStart"in c&&Xd(c)?c={start:c.selectionStart,
|
|
end:c.selectionEnd}:(c=(c.ownerDocument&&c.ownerDocument.defaultView||window).getSelection(),c={anchorNode:c.anchorNode,anchorOffset:c.anchorOffset,focusNode:c.focusNode,focusOffset:c.focusOffset});return Pb&&Ob(Pb,c)?null:(Pb=c,a=R.getPooled(Eg.select,he,a,b),a.type="select",a.target=nb,lb(a),a)}function Ac(a){var b=a.keyCode;"charCode"in a?(a=a.charCode,0===a&&13===b&&(a=13)):a=b;10===a&&(a=13);return 32<=a||13===a?a:0}function q(a,b){0>ob||(a.current=ie[ob],ie[ob]=null,ob--)}function y(a,b,c){ob++;
|
|
ie[ob]=a.current;a.current=b}function pb(a,b){var c=a.type.contextTypes;if(!c)return Ca;var d=a.stateNode;if(d&&d.__reactInternalMemoizedUnmaskedChildContext===b)return d.__reactInternalMemoizedMaskedChildContext;var e={},f;for(f in c)e[f]=b[f];d&&(a=a.stateNode,a.__reactInternalMemoizedUnmaskedChildContext=b,a.__reactInternalMemoizedMaskedChildContext=e);return e}function N(a){a=a.childContextTypes;return null!==a&&void 0!==a}function Fg(a,b,c){if(B.current!==Ca)throw Error(k(168));y(B,b);y(G,c)}
|
|
function Gg(a,b,c){var d=a.stateNode;a=b.childContextTypes;if("function"!==typeof d.getChildContext)return c;d=d.getChildContext();for(var e in d)if(!(e in a))throw Error(k(108,na(b)||"Unknown",e));return M({},c,{},d)}function Bc(a){a=(a=a.stateNode)&&a.__reactInternalMemoizedMergedChildContext||Ca;Ra=B.current;y(B,a);y(G,G.current);return!0}function Hg(a,b,c){var d=a.stateNode;if(!d)throw Error(k(169));c?(a=Gg(a,b,Ra),d.__reactInternalMemoizedMergedChildContext=a,q(G),q(B),y(B,a)):q(G);y(G,c)}function Cc(){switch(aj()){case Dc:return 99;
|
|
case Ig:return 98;case Jg:return 97;case Kg:return 96;case Lg:return 95;default:throw Error(k(332));}}function Mg(a){switch(a){case 99:return Dc;case 98:return Ig;case 97:return Jg;case 96:return Kg;case 95:return Lg;default:throw Error(k(332));}}function Da(a,b){a=Mg(a);return bj(a,b)}function Ng(a,b,c){a=Mg(a);return je(a,b,c)}function Og(a){null===qa?(qa=[a],Ec=je(Dc,Pg)):qa.push(a);return Qg}function ha(){if(null!==Ec){var a=Ec;Ec=null;Rg(a)}Pg()}function Pg(){if(!ke&&null!==qa){ke=!0;var a=0;
|
|
try{var b=qa;Da(99,function(){for(;a<b.length;a++){var c=b[a];do c=c(!0);while(null!==c)}});qa=null}catch(c){throw null!==qa&&(qa=qa.slice(a+1)),je(Dc,ha),c;}finally{ke=!1}}}function Fc(a,b,c){c/=10;return 1073741821-(((1073741821-a+b/10)/c|0)+1)*c}function aa(a,b){if(a&&a.defaultProps){b=M({},b);a=a.defaultProps;for(var c in a)void 0===b[c]&&(b[c]=a[c])}return b}function le(){Gc=qb=Hc=null}function me(a){var b=Ic.current;q(Ic);a.type._context._currentValue=b}function Sg(a,b){for(;null!==a;){var c=
|
|
a.alternate;if(a.childExpirationTime<b)a.childExpirationTime=b,null!==c&&c.childExpirationTime<b&&(c.childExpirationTime=b);else if(null!==c&&c.childExpirationTime<b)c.childExpirationTime=b;else break;a=a.return}}function rb(a,b){Hc=a;Gc=qb=null;a=a.dependencies;null!==a&&null!==a.firstContext&&(a.expirationTime>=b&&(ia=!0),a.firstContext=null)}function W(a,b){if(Gc!==a&&!1!==b&&0!==b){if("number"!==typeof b||1073741823===b)Gc=a,b=1073741823;b={context:a,observedBits:b,next:null};if(null===qb){if(null===
|
|
Hc)throw Error(k(308));qb=b;Hc.dependencies={expirationTime:0,firstContext:b,responders:null}}else qb=qb.next=b}return a._currentValue}function ne(a){a.updateQueue={baseState:a.memoizedState,baseQueue:null,shared:{pending:null},effects:null}}function oe(a,b){a=a.updateQueue;b.updateQueue===a&&(b.updateQueue={baseState:a.baseState,baseQueue:a.baseQueue,shared:a.shared,effects:a.effects})}function Ea(a,b){a={expirationTime:a,suspenseConfig:b,tag:Tg,payload:null,callback:null,next:null};return a.next=
|
|
a}function Fa(a,b){a=a.updateQueue;if(null!==a){a=a.shared;var c=a.pending;null===c?b.next=b:(b.next=c.next,c.next=b);a.pending=b}}function Ug(a,b){var c=a.alternate;null!==c&&oe(c,a);a=a.updateQueue;c=a.baseQueue;null===c?(a.baseQueue=b.next=b,b.next=b):(b.next=c.next,c.next=b)}function Qb(a,b,c,d){var e=a.updateQueue;Ga=!1;var f=e.baseQueue,g=e.shared.pending;if(null!==g){if(null!==f){var h=f.next;f.next=g.next;g.next=h}f=g;e.shared.pending=null;h=a.alternate;null!==h&&(h=h.updateQueue,null!==h&&
|
|
(h.baseQueue=g))}if(null!==f){h=f.next;var m=e.baseState,n=0,k=null,ba=null,l=null;if(null!==h){var p=h;do{g=p.expirationTime;if(g<d){var t={expirationTime:p.expirationTime,suspenseConfig:p.suspenseConfig,tag:p.tag,payload:p.payload,callback:p.callback,next:null};null===l?(ba=l=t,k=m):l=l.next=t;g>n&&(n=g)}else{null!==l&&(l=l.next={expirationTime:1073741823,suspenseConfig:p.suspenseConfig,tag:p.tag,payload:p.payload,callback:p.callback,next:null});Vg(g,p.suspenseConfig);a:{var q=a,r=p;g=b;t=c;switch(r.tag){case 1:q=
|
|
r.payload;if("function"===typeof q){m=q.call(t,m,g);break a}m=q;break a;case 3:q.effectTag=q.effectTag&-4097|64;case Tg:q=r.payload;g="function"===typeof q?q.call(t,m,g):q;if(null===g||void 0===g)break a;m=M({},m,g);break a;case Jc:Ga=!0}}null!==p.callback&&(a.effectTag|=32,g=e.effects,null===g?e.effects=[p]:g.push(p))}p=p.next;if(null===p||p===h)if(g=e.shared.pending,null===g)break;else p=f.next=g.next,g.next=h,e.baseQueue=f=g,e.shared.pending=null}while(1)}null===l?k=m:l.next=ba;e.baseState=k;e.baseQueue=
|
|
l;Kc(n);a.expirationTime=n;a.memoizedState=m}}function Wg(a,b,c){a=b.effects;b.effects=null;if(null!==a)for(b=0;b<a.length;b++){var d=a[b],e=d.callback;if(null!==e){d.callback=null;d=e;e=c;if("function"!==typeof d)throw Error(k(191,d));d.call(e)}}}function Lc(a,b,c,d){b=a.memoizedState;c=c(d,b);c=null===c||void 0===c?b:M({},b,c);a.memoizedState=c;0===a.expirationTime&&(a.updateQueue.baseState=c)}function Xg(a,b,c,d,e,f,g){a=a.stateNode;return"function"===typeof a.shouldComponentUpdate?a.shouldComponentUpdate(d,
|
|
f,g):b.prototype&&b.prototype.isPureReactComponent?!Ob(c,d)||!Ob(e,f):!0}function Yg(a,b,c){var d=!1,e=Ca;var f=b.contextType;"object"===typeof f&&null!==f?f=W(f):(e=N(b)?Ra:B.current,d=b.contextTypes,f=(d=null!==d&&void 0!==d)?pb(a,e):Ca);b=new b(c,f);a.memoizedState=null!==b.state&&void 0!==b.state?b.state:null;b.updater=Mc;a.stateNode=b;b._reactInternalFiber=a;d&&(a=a.stateNode,a.__reactInternalMemoizedUnmaskedChildContext=e,a.__reactInternalMemoizedMaskedChildContext=f);return b}function Zg(a,
|
|
b,c,d){a=b.state;"function"===typeof b.componentWillReceiveProps&&b.componentWillReceiveProps(c,d);"function"===typeof b.UNSAFE_componentWillReceiveProps&&b.UNSAFE_componentWillReceiveProps(c,d);b.state!==a&&Mc.enqueueReplaceState(b,b.state,null)}function pe(a,b,c,d){var e=a.stateNode;e.props=c;e.state=a.memoizedState;e.refs=$g;ne(a);var f=b.contextType;"object"===typeof f&&null!==f?e.context=W(f):(f=N(b)?Ra:B.current,e.context=pb(a,f));Qb(a,c,e,d);e.state=a.memoizedState;f=b.getDerivedStateFromProps;
|
|
"function"===typeof f&&(Lc(a,b,f,c),e.state=a.memoizedState);"function"===typeof b.getDerivedStateFromProps||"function"===typeof e.getSnapshotBeforeUpdate||"function"!==typeof e.UNSAFE_componentWillMount&&"function"!==typeof e.componentWillMount||(b=e.state,"function"===typeof e.componentWillMount&&e.componentWillMount(),"function"===typeof e.UNSAFE_componentWillMount&&e.UNSAFE_componentWillMount(),b!==e.state&&Mc.enqueueReplaceState(e,e.state,null),Qb(a,c,e,d),e.state=a.memoizedState);"function"===
|
|
typeof e.componentDidMount&&(a.effectTag|=4)}function Rb(a,b,c){a=c.ref;if(null!==a&&"function"!==typeof a&&"object"!==typeof a){if(c._owner){c=c._owner;if(c){if(1!==c.tag)throw Error(k(309));var d=c.stateNode}if(!d)throw Error(k(147,a));var e=""+a;if(null!==b&&null!==b.ref&&"function"===typeof b.ref&&b.ref._stringRef===e)return b.ref;b=function(a){var b=d.refs;b===$g&&(b=d.refs={});null===a?delete b[e]:b[e]=a};b._stringRef=e;return b}if("string"!==typeof a)throw Error(k(284));if(!c._owner)throw Error(k(290,
|
|
a));}return a}function Nc(a,b){if("textarea"!==a.type)throw Error(k(31,"[object Object]"===Object.prototype.toString.call(b)?"object with keys {"+Object.keys(b).join(", ")+"}":b,""));}function ah(a){function b(b,c){if(a){var d=b.lastEffect;null!==d?(d.nextEffect=c,b.lastEffect=c):b.firstEffect=b.lastEffect=c;c.nextEffect=null;c.effectTag=8}}function c(c,d){if(!a)return null;for(;null!==d;)b(c,d),d=d.sibling;return null}function d(a,b){for(a=new Map;null!==b;)null!==b.key?a.set(b.key,b):a.set(b.index,
|
|
b),b=b.sibling;return a}function e(a,b){a=Sa(a,b);a.index=0;a.sibling=null;return a}function f(b,c,d){b.index=d;if(!a)return c;d=b.alternate;if(null!==d)return d=d.index,d<c?(b.effectTag=2,c):d;b.effectTag=2;return c}function g(b){a&&null===b.alternate&&(b.effectTag=2);return b}function h(a,b,c,d){if(null===b||6!==b.tag)return b=qe(c,a.mode,d),b.return=a,b;b=e(b,c);b.return=a;return b}function m(a,b,c,d){if(null!==b&&b.elementType===c.type)return d=e(b,c.props),d.ref=Rb(a,b,c),d.return=a,d;d=Oc(c.type,
|
|
c.key,c.props,null,a.mode,d);d.ref=Rb(a,b,c);d.return=a;return d}function n(a,b,c,d){if(null===b||4!==b.tag||b.stateNode.containerInfo!==c.containerInfo||b.stateNode.implementation!==c.implementation)return b=re(c,a.mode,d),b.return=a,b;b=e(b,c.children||[]);b.return=a;return b}function l(a,b,c,d,f){if(null===b||7!==b.tag)return b=Ha(c,a.mode,d,f),b.return=a,b;b=e(b,c);b.return=a;return b}function ba(a,b,c){if("string"===typeof b||"number"===typeof b)return b=qe(""+b,a.mode,c),b.return=a,b;if("object"===
|
|
typeof b&&null!==b){switch(b.$$typeof){case Pc:return c=Oc(b.type,b.key,b.props,null,a.mode,c),c.ref=Rb(a,null,b),c.return=a,c;case gb:return b=re(b,a.mode,c),b.return=a,b}if(Qc(b)||zb(b))return b=Ha(b,a.mode,c,null),b.return=a,b;Nc(a,b)}return null}function p(a,b,c,d){var e=null!==b?b.key:null;if("string"===typeof c||"number"===typeof c)return null!==e?null:h(a,b,""+c,d);if("object"===typeof c&&null!==c){switch(c.$$typeof){case Pc:return c.key===e?c.type===Ma?l(a,b,c.props.children,d,e):m(a,b,c,
|
|
d):null;case gb:return c.key===e?n(a,b,c,d):null}if(Qc(c)||zb(c))return null!==e?null:l(a,b,c,d,null);Nc(a,c)}return null}function t(a,b,c,d,e){if("string"===typeof d||"number"===typeof d)return a=a.get(c)||null,h(b,a,""+d,e);if("object"===typeof d&&null!==d){switch(d.$$typeof){case Pc:return a=a.get(null===d.key?c:d.key)||null,d.type===Ma?l(b,a,d.props.children,e,d.key):m(b,a,d,e);case gb:return a=a.get(null===d.key?c:d.key)||null,n(b,a,d,e)}if(Qc(d)||zb(d))return a=a.get(c)||null,l(b,a,d,e,null);
|
|
Nc(b,d)}return null}function q(e,g,h,m){for(var n=null,k=null,l=g,r=g=0,C=null;null!==l&&r<h.length;r++){l.index>r?(C=l,l=null):C=l.sibling;var O=p(e,l,h[r],m);if(null===O){null===l&&(l=C);break}a&&l&&null===O.alternate&&b(e,l);g=f(O,g,r);null===k?n=O:k.sibling=O;k=O;l=C}if(r===h.length)return c(e,l),n;if(null===l){for(;r<h.length;r++)l=ba(e,h[r],m),null!==l&&(g=f(l,g,r),null===k?n=l:k.sibling=l,k=l);return n}for(l=d(e,l);r<h.length;r++)C=t(l,e,r,h[r],m),null!==C&&(a&&null!==C.alternate&&l.delete(null===
|
|
C.key?r:C.key),g=f(C,g,r),null===k?n=C:k.sibling=C,k=C);a&&l.forEach(function(a){return b(e,a)});return n}function w(e,g,h,n){var m=zb(h);if("function"!==typeof m)throw Error(k(150));h=m.call(h);if(null==h)throw Error(k(151));for(var l=m=null,r=g,C=g=0,O=null,v=h.next();null!==r&&!v.done;C++,v=h.next()){r.index>C?(O=r,r=null):O=r.sibling;var q=p(e,r,v.value,n);if(null===q){null===r&&(r=O);break}a&&r&&null===q.alternate&&b(e,r);g=f(q,g,C);null===l?m=q:l.sibling=q;l=q;r=O}if(v.done)return c(e,r),m;
|
|
if(null===r){for(;!v.done;C++,v=h.next())v=ba(e,v.value,n),null!==v&&(g=f(v,g,C),null===l?m=v:l.sibling=v,l=v);return m}for(r=d(e,r);!v.done;C++,v=h.next())v=t(r,e,C,v.value,n),null!==v&&(a&&null!==v.alternate&&r.delete(null===v.key?C:v.key),g=f(v,g,C),null===l?m=v:l.sibling=v,l=v);a&&r.forEach(function(a){return b(e,a)});return m}return function(a,d,f,h){var m="object"===typeof f&&null!==f&&f.type===Ma&&null===f.key;m&&(f=f.props.children);var n="object"===typeof f&&null!==f;if(n)switch(f.$$typeof){case Pc:a:{n=
|
|
f.key;for(m=d;null!==m;){if(m.key===n){switch(m.tag){case 7:if(f.type===Ma){c(a,m.sibling);d=e(m,f.props.children);d.return=a;a=d;break a}break;default:if(m.elementType===f.type){c(a,m.sibling);d=e(m,f.props);d.ref=Rb(a,m,f);d.return=a;a=d;break a}}c(a,m);break}else b(a,m);m=m.sibling}f.type===Ma?(d=Ha(f.props.children,a.mode,h,f.key),d.return=a,a=d):(h=Oc(f.type,f.key,f.props,null,a.mode,h),h.ref=Rb(a,d,f),h.return=a,a=h)}return g(a);case gb:a:{for(m=f.key;null!==d;){if(d.key===m)if(4===d.tag&&d.stateNode.containerInfo===
|
|
f.containerInfo&&d.stateNode.implementation===f.implementation){c(a,d.sibling);d=e(d,f.children||[]);d.return=a;a=d;break a}else{c(a,d);break}else b(a,d);d=d.sibling}d=re(f,a.mode,h);d.return=a;a=d}return g(a)}if("string"===typeof f||"number"===typeof f)return f=""+f,null!==d&&6===d.tag?(c(a,d.sibling),d=e(d,f),d.return=a,a=d):(c(a,d),d=qe(f,a.mode,h),d.return=a,a=d),g(a);if(Qc(f))return q(a,d,f,h);if(zb(f))return w(a,d,f,h);n&&Nc(a,f);if("undefined"===typeof f&&!m)switch(a.tag){case 1:case 0:throw a=
|
|
a.type,Error(k(152,a.displayName||a.name||"Component"));}return c(a,d)}}function Ta(a){if(a===Sb)throw Error(k(174));return a}function se(a,b){y(Tb,b);y(Ub,a);y(ja,Sb);a=b.nodeType;switch(a){case 9:case 11:b=(b=b.documentElement)?b.namespaceURI:Hd(null,"");break;default:a=8===a?b.parentNode:b,b=a.namespaceURI||null,a=a.tagName,b=Hd(b,a)}q(ja);y(ja,b)}function tb(a){q(ja);q(Ub);q(Tb)}function bh(a){Ta(Tb.current);var b=Ta(ja.current);var c=Hd(b,a.type);b!==c&&(y(Ub,a),y(ja,c))}function te(a){Ub.current===
|
|
a&&(q(ja),q(Ub))}function Rc(a){for(var b=a;null!==b;){if(13===b.tag){var c=b.memoizedState;if(null!==c&&(c=c.dehydrated,null===c||c.data===$d||c.data===Zd))return b}else if(19===b.tag&&void 0!==b.memoizedProps.revealOrder){if(0!==(b.effectTag&64))return b}else if(null!==b.child){b.child.return=b;b=b.child;continue}if(b===a)break;for(;null===b.sibling;){if(null===b.return||b.return===a)return null;b=b.return}b.sibling.return=b.return;b=b.sibling}return null}function ue(a,b){return{responder:a,props:b}}
|
|
function S(){throw Error(k(321));}function ve(a,b){if(null===b)return!1;for(var c=0;c<b.length&&c<a.length;c++)if(!Qa(a[c],b[c]))return!1;return!0}function we(a,b,c,d,e,f){Ia=f;z=b;b.memoizedState=null;b.updateQueue=null;b.expirationTime=0;Sc.current=null===a||null===a.memoizedState?dj:ej;a=c(d,e);if(b.expirationTime===Ia){f=0;do{b.expirationTime=0;if(!(25>f))throw Error(k(301));f+=1;J=K=null;b.updateQueue=null;Sc.current=fj;a=c(d,e)}while(b.expirationTime===Ia)}Sc.current=Tc;b=null!==K&&null!==K.next;
|
|
Ia=0;J=K=z=null;Uc=!1;if(b)throw Error(k(300));return a}function ub(){var a={memoizedState:null,baseState:null,baseQueue:null,queue:null,next:null};null===J?z.memoizedState=J=a:J=J.next=a;return J}function vb(){if(null===K){var a=z.alternate;a=null!==a?a.memoizedState:null}else a=K.next;var b=null===J?z.memoizedState:J.next;if(null!==b)J=b,K=a;else{if(null===a)throw Error(k(310));K=a;a={memoizedState:K.memoizedState,baseState:K.baseState,baseQueue:K.baseQueue,queue:K.queue,next:null};null===J?z.memoizedState=
|
|
J=a:J=J.next=a}return J}function Ua(a,b){return"function"===typeof b?b(a):b}function Vc(a,b,c){b=vb();c=b.queue;if(null===c)throw Error(k(311));c.lastRenderedReducer=a;var d=K,e=d.baseQueue,f=c.pending;if(null!==f){if(null!==e){var g=e.next;e.next=f.next;f.next=g}d.baseQueue=e=f;c.pending=null}if(null!==e){e=e.next;d=d.baseState;var h=g=f=null,m=e;do{var n=m.expirationTime;if(n<Ia){var l={expirationTime:m.expirationTime,suspenseConfig:m.suspenseConfig,action:m.action,eagerReducer:m.eagerReducer,eagerState:m.eagerState,
|
|
next:null};null===h?(g=h=l,f=d):h=h.next=l;n>z.expirationTime&&(z.expirationTime=n,Kc(n))}else null!==h&&(h=h.next={expirationTime:1073741823,suspenseConfig:m.suspenseConfig,action:m.action,eagerReducer:m.eagerReducer,eagerState:m.eagerState,next:null}),Vg(n,m.suspenseConfig),d=m.eagerReducer===a?m.eagerState:a(d,m.action);m=m.next}while(null!==m&&m!==e);null===h?f=d:h.next=g;Qa(d,b.memoizedState)||(ia=!0);b.memoizedState=d;b.baseState=f;b.baseQueue=h;c.lastRenderedState=d}return[b.memoizedState,
|
|
c.dispatch]}function Wc(a,b,c){b=vb();c=b.queue;if(null===c)throw Error(k(311));c.lastRenderedReducer=a;var d=c.dispatch,e=c.pending,f=b.memoizedState;if(null!==e){c.pending=null;var g=e=e.next;do f=a(f,g.action),g=g.next;while(g!==e);Qa(f,b.memoizedState)||(ia=!0);b.memoizedState=f;null===b.baseQueue&&(b.baseState=f);c.lastRenderedState=f}return[f,d]}function xe(a){var b=ub();"function"===typeof a&&(a=a());b.memoizedState=b.baseState=a;a=b.queue={pending:null,dispatch:null,lastRenderedReducer:Ua,
|
|
lastRenderedState:a};a=a.dispatch=ch.bind(null,z,a);return[b.memoizedState,a]}function ye(a,b,c,d){a={tag:a,create:b,destroy:c,deps:d,next:null};b=z.updateQueue;null===b?(b={lastEffect:null},z.updateQueue=b,b.lastEffect=a.next=a):(c=b.lastEffect,null===c?b.lastEffect=a.next=a:(d=c.next,c.next=a,a.next=d,b.lastEffect=a));return a}function dh(a){return vb().memoizedState}function ze(a,b,c,d){var e=ub();z.effectTag|=a;e.memoizedState=ye(1|b,c,void 0,void 0===d?null:d)}function Ae(a,b,c,d){var e=vb();
|
|
d=void 0===d?null:d;var f=void 0;if(null!==K){var g=K.memoizedState;f=g.destroy;if(null!==d&&ve(d,g.deps)){ye(b,c,f,d);return}}z.effectTag|=a;e.memoizedState=ye(1|b,c,f,d)}function eh(a,b){return ze(516,4,a,b)}function Xc(a,b){return Ae(516,4,a,b)}function fh(a,b){return Ae(4,2,a,b)}function gh(a,b){if("function"===typeof b)return a=a(),b(a),function(){b(null)};if(null!==b&&void 0!==b)return a=a(),b.current=a,function(){b.current=null}}function hh(a,b,c){c=null!==c&&void 0!==c?c.concat([a]):null;
|
|
return Ae(4,2,gh.bind(null,b,a),c)}function Be(a,b){}function ih(a,b){ub().memoizedState=[a,void 0===b?null:b];return a}function Yc(a,b){var c=vb();b=void 0===b?null:b;var d=c.memoizedState;if(null!==d&&null!==b&&ve(b,d[1]))return d[0];c.memoizedState=[a,b];return a}function jh(a,b){var c=vb();b=void 0===b?null:b;var d=c.memoizedState;if(null!==d&&null!==b&&ve(b,d[1]))return d[0];a=a();c.memoizedState=[a,b];return a}function Ce(a,b,c){var d=Cc();Da(98>d?98:d,function(){a(!0)});Da(97<d?97:d,function(){var d=
|
|
X.suspense;X.suspense=void 0===b?null:b;try{a(!1),c()}finally{X.suspense=d}})}function ch(a,b,c){var d=ka(),e=Vb.suspense;d=Va(d,a,e);e={expirationTime:d,suspenseConfig:e,action:c,eagerReducer:null,eagerState:null,next:null};var f=b.pending;null===f?e.next=e:(e.next=f.next,f.next=e);b.pending=e;f=a.alternate;if(a===z||null!==f&&f===z)Uc=!0,e.expirationTime=Ia,z.expirationTime=Ia;else{if(0===a.expirationTime&&(null===f||0===f.expirationTime)&&(f=b.lastRenderedReducer,null!==f))try{var g=b.lastRenderedState,
|
|
h=f(g,c);e.eagerReducer=f;e.eagerState=h;if(Qa(h,g))return}catch(m){}finally{}Ja(a,d)}}function kh(a,b){var c=la(5,null,null,0);c.elementType="DELETED";c.type="DELETED";c.stateNode=b;c.return=a;c.effectTag=8;null!==a.lastEffect?(a.lastEffect.nextEffect=c,a.lastEffect=c):a.firstEffect=a.lastEffect=c}function lh(a,b){switch(a.tag){case 5:var c=a.type;b=1!==b.nodeType||c.toLowerCase()!==b.nodeName.toLowerCase()?null:b;return null!==b?(a.stateNode=b,!0):!1;case 6:return b=""===a.pendingProps||3!==b.nodeType?
|
|
null:b,null!==b?(a.stateNode=b,!0):!1;case 13:return!1;default:return!1}}function De(a){if(Wa){var b=Ka;if(b){var c=b;if(!lh(a,b)){b=kb(c.nextSibling);if(!b||!lh(a,b)){a.effectTag=a.effectTag&-1025|2;Wa=!1;ra=a;return}kh(ra,c)}ra=a;Ka=kb(b.firstChild)}else a.effectTag=a.effectTag&-1025|2,Wa=!1,ra=a}}function mh(a){for(a=a.return;null!==a&&5!==a.tag&&3!==a.tag&&13!==a.tag;)a=a.return;ra=a}function Zc(a){if(a!==ra)return!1;if(!Wa)return mh(a),Wa=!0,!1;var b=a.type;if(5!==a.tag||"head"!==b&&"body"!==
|
|
b&&!Yd(b,a.memoizedProps))for(b=Ka;b;)kh(a,b),b=kb(b.nextSibling);mh(a);if(13===a.tag){a=a.memoizedState;a=null!==a?a.dehydrated:null;if(!a)throw Error(k(317));a:{a=a.nextSibling;for(b=0;a;){if(8===a.nodeType){var c=a.data;if(c===og){if(0===b){Ka=kb(a.nextSibling);break a}b--}else c!==ng&&c!==Zd&&c!==$d||b++}a=a.nextSibling}Ka=null}}else Ka=ra?kb(a.stateNode.nextSibling):null;return!0}function Ee(){Ka=ra=null;Wa=!1}function T(a,b,c,d){b.child=null===a?Fe(b,null,c,d):wb(b,a.child,c,d)}function nh(a,
|
|
b,c,d,e){c=c.render;var f=b.ref;rb(b,e);d=we(a,b,c,d,f,e);if(null!==a&&!ia)return b.updateQueue=a.updateQueue,b.effectTag&=-517,a.expirationTime<=e&&(a.expirationTime=0),sa(a,b,e);b.effectTag|=1;T(a,b,d,e);return b.child}function oh(a,b,c,d,e,f){if(null===a){var g=c.type;if("function"===typeof g&&!Ge(g)&&void 0===g.defaultProps&&null===c.compare&&void 0===c.defaultProps)return b.tag=15,b.type=g,ph(a,b,g,d,e,f);a=Oc(c.type,null,d,null,b.mode,f);a.ref=b.ref;a.return=b;return b.child=a}g=a.child;if(e<
|
|
f&&(e=g.memoizedProps,c=c.compare,c=null!==c?c:Ob,c(e,d)&&a.ref===b.ref))return sa(a,b,f);b.effectTag|=1;a=Sa(g,d);a.ref=b.ref;a.return=b;return b.child=a}function ph(a,b,c,d,e,f){return null!==a&&Ob(a.memoizedProps,d)&&a.ref===b.ref&&(ia=!1,e<f)?(b.expirationTime=a.expirationTime,sa(a,b,f)):He(a,b,c,d,f)}function qh(a,b){var c=b.ref;if(null===a&&null!==c||null!==a&&a.ref!==c)b.effectTag|=128}function He(a,b,c,d,e){var f=N(c)?Ra:B.current;f=pb(b,f);rb(b,e);c=we(a,b,c,d,f,e);if(null!==a&&!ia)return b.updateQueue=
|
|
a.updateQueue,b.effectTag&=-517,a.expirationTime<=e&&(a.expirationTime=0),sa(a,b,e);b.effectTag|=1;T(a,b,c,e);return b.child}function rh(a,b,c,d,e){if(N(c)){var f=!0;Bc(b)}else f=!1;rb(b,e);if(null===b.stateNode)null!==a&&(a.alternate=null,b.alternate=null,b.effectTag|=2),Yg(b,c,d),pe(b,c,d,e),d=!0;else if(null===a){var g=b.stateNode,h=b.memoizedProps;g.props=h;var m=g.context,n=c.contextType;"object"===typeof n&&null!==n?n=W(n):(n=N(c)?Ra:B.current,n=pb(b,n));var l=c.getDerivedStateFromProps,k="function"===
|
|
typeof l||"function"===typeof g.getSnapshotBeforeUpdate;k||"function"!==typeof g.UNSAFE_componentWillReceiveProps&&"function"!==typeof g.componentWillReceiveProps||(h!==d||m!==n)&&Zg(b,g,d,n);Ga=!1;var p=b.memoizedState;g.state=p;Qb(b,d,g,e);m=b.memoizedState;h!==d||p!==m||G.current||Ga?("function"===typeof l&&(Lc(b,c,l,d),m=b.memoizedState),(h=Ga||Xg(b,c,h,d,p,m,n))?(k||"function"!==typeof g.UNSAFE_componentWillMount&&"function"!==typeof g.componentWillMount||("function"===typeof g.componentWillMount&&
|
|
g.componentWillMount(),"function"===typeof g.UNSAFE_componentWillMount&&g.UNSAFE_componentWillMount()),"function"===typeof g.componentDidMount&&(b.effectTag|=4)):("function"===typeof g.componentDidMount&&(b.effectTag|=4),b.memoizedProps=d,b.memoizedState=m),g.props=d,g.state=m,g.context=n,d=h):("function"===typeof g.componentDidMount&&(b.effectTag|=4),d=!1)}else g=b.stateNode,oe(a,b),h=b.memoizedProps,g.props=b.type===b.elementType?h:aa(b.type,h),m=g.context,n=c.contextType,"object"===typeof n&&null!==
|
|
n?n=W(n):(n=N(c)?Ra:B.current,n=pb(b,n)),l=c.getDerivedStateFromProps,(k="function"===typeof l||"function"===typeof g.getSnapshotBeforeUpdate)||"function"!==typeof g.UNSAFE_componentWillReceiveProps&&"function"!==typeof g.componentWillReceiveProps||(h!==d||m!==n)&&Zg(b,g,d,n),Ga=!1,m=b.memoizedState,g.state=m,Qb(b,d,g,e),p=b.memoizedState,h!==d||m!==p||G.current||Ga?("function"===typeof l&&(Lc(b,c,l,d),p=b.memoizedState),(l=Ga||Xg(b,c,h,d,m,p,n))?(k||"function"!==typeof g.UNSAFE_componentWillUpdate&&
|
|
"function"!==typeof g.componentWillUpdate||("function"===typeof g.componentWillUpdate&&g.componentWillUpdate(d,p,n),"function"===typeof g.UNSAFE_componentWillUpdate&&g.UNSAFE_componentWillUpdate(d,p,n)),"function"===typeof g.componentDidUpdate&&(b.effectTag|=4),"function"===typeof g.getSnapshotBeforeUpdate&&(b.effectTag|=256)):("function"!==typeof g.componentDidUpdate||h===a.memoizedProps&&m===a.memoizedState||(b.effectTag|=4),"function"!==typeof g.getSnapshotBeforeUpdate||h===a.memoizedProps&&m===
|
|
a.memoizedState||(b.effectTag|=256),b.memoizedProps=d,b.memoizedState=p),g.props=d,g.state=p,g.context=n,d=l):("function"!==typeof g.componentDidUpdate||h===a.memoizedProps&&m===a.memoizedState||(b.effectTag|=4),"function"!==typeof g.getSnapshotBeforeUpdate||h===a.memoizedProps&&m===a.memoizedState||(b.effectTag|=256),d=!1);return Ie(a,b,c,d,f,e)}function Ie(a,b,c,d,e,f){qh(a,b);var g=0!==(b.effectTag&64);if(!d&&!g)return e&&Hg(b,c,!1),sa(a,b,f);d=b.stateNode;gj.current=b;var h=g&&"function"!==typeof c.getDerivedStateFromError?
|
|
null:d.render();b.effectTag|=1;null!==a&&g?(b.child=wb(b,a.child,null,f),b.child=wb(b,null,h,f)):T(a,b,h,f);b.memoizedState=d.state;e&&Hg(b,c,!0);return b.child}function sh(a){var b=a.stateNode;b.pendingContext?Fg(a,b.pendingContext,b.pendingContext!==b.context):b.context&&Fg(a,b.context,!1);se(a,b.containerInfo)}function th(a,b,c){var d=b.mode,e=b.pendingProps,f=D.current,g=!1,h;(h=0!==(b.effectTag&64))||(h=0!==(f&2)&&(null===a||null!==a.memoizedState));h?(g=!0,b.effectTag&=-65):null!==a&&null===
|
|
a.memoizedState||void 0===e.fallback||!0===e.unstable_avoidThisFallback||(f|=1);y(D,f&1);if(null===a){void 0!==e.fallback&&De(b);if(g){g=e.fallback;e=Ha(null,d,0,null);e.return=b;if(0===(b.mode&2))for(a=null!==b.memoizedState?b.child.child:b.child,e.child=a;null!==a;)a.return=e,a=a.sibling;c=Ha(g,d,c,null);c.return=b;e.sibling=c;b.memoizedState=Je;b.child=e;return c}d=e.children;b.memoizedState=null;return b.child=Fe(b,null,d,c)}if(null!==a.memoizedState){a=a.child;d=a.sibling;if(g){e=e.fallback;
|
|
c=Sa(a,a.pendingProps);c.return=b;if(0===(b.mode&2)&&(g=null!==b.memoizedState?b.child.child:b.child,g!==a.child))for(c.child=g;null!==g;)g.return=c,g=g.sibling;d=Sa(d,e);d.return=b;c.sibling=d;c.childExpirationTime=0;b.memoizedState=Je;b.child=c;return d}c=wb(b,a.child,e.children,c);b.memoizedState=null;return b.child=c}a=a.child;if(g){g=e.fallback;e=Ha(null,d,0,null);e.return=b;e.child=a;null!==a&&(a.return=e);if(0===(b.mode&2))for(a=null!==b.memoizedState?b.child.child:b.child,e.child=a;null!==
|
|
a;)a.return=e,a=a.sibling;c=Ha(g,d,c,null);c.return=b;e.sibling=c;c.effectTag|=2;e.childExpirationTime=0;b.memoizedState=Je;b.child=e;return c}b.memoizedState=null;return b.child=wb(b,a,e.children,c)}function uh(a,b){a.expirationTime<b&&(a.expirationTime=b);var c=a.alternate;null!==c&&c.expirationTime<b&&(c.expirationTime=b);Sg(a.return,b)}function Ke(a,b,c,d,e,f){var g=a.memoizedState;null===g?a.memoizedState={isBackwards:b,rendering:null,renderingStartTime:0,last:d,tail:c,tailExpiration:0,tailMode:e,
|
|
lastEffect:f}:(g.isBackwards=b,g.rendering=null,g.renderingStartTime=0,g.last=d,g.tail=c,g.tailExpiration=0,g.tailMode=e,g.lastEffect=f)}function vh(a,b,c){var d=b.pendingProps,e=d.revealOrder,f=d.tail;T(a,b,d.children,c);d=D.current;if(0!==(d&2))d=d&1|2,b.effectTag|=64;else{if(null!==a&&0!==(a.effectTag&64))a:for(a=b.child;null!==a;){if(13===a.tag)null!==a.memoizedState&&uh(a,c);else if(19===a.tag)uh(a,c);else if(null!==a.child){a.child.return=a;a=a.child;continue}if(a===b)break a;for(;null===a.sibling;){if(null===
|
|
a.return||a.return===b)break a;a=a.return}a.sibling.return=a.return;a=a.sibling}d&=1}y(D,d);if(0===(b.mode&2))b.memoizedState=null;else switch(e){case "forwards":c=b.child;for(e=null;null!==c;)a=c.alternate,null!==a&&null===Rc(a)&&(e=c),c=c.sibling;c=e;null===c?(e=b.child,b.child=null):(e=c.sibling,c.sibling=null);Ke(b,!1,e,c,f,b.lastEffect);break;case "backwards":c=null;e=b.child;for(b.child=null;null!==e;){a=e.alternate;if(null!==a&&null===Rc(a)){b.child=e;break}a=e.sibling;e.sibling=c;c=e;e=a}Ke(b,
|
|
!0,c,null,f,b.lastEffect);break;case "together":Ke(b,!1,null,null,void 0,b.lastEffect);break;default:b.memoizedState=null}return b.child}function sa(a,b,c){null!==a&&(b.dependencies=a.dependencies);var d=b.expirationTime;0!==d&&Kc(d);if(b.childExpirationTime<c)return null;if(null!==a&&b.child!==a.child)throw Error(k(153));if(null!==b.child){a=b.child;c=Sa(a,a.pendingProps);b.child=c;for(c.return=b;null!==a.sibling;)a=a.sibling,c=c.sibling=Sa(a,a.pendingProps),c.return=b;c.sibling=null}return b.child}
|
|
function $c(a,b){switch(a.tailMode){case "hidden":b=a.tail;for(var c=null;null!==b;)null!==b.alternate&&(c=b),b=b.sibling;null===c?a.tail=null:c.sibling=null;break;case "collapsed":c=a.tail;for(var d=null;null!==c;)null!==c.alternate&&(d=c),c=c.sibling;null===d?b||null===a.tail?a.tail=null:a.tail.sibling=null:d.sibling=null}}function hj(a,b,c){var d=b.pendingProps;switch(b.tag){case 2:case 16:case 15:case 0:case 11:case 7:case 8:case 12:case 9:case 14:return null;case 1:return N(b.type)&&(q(G),q(B)),
|
|
null;case 3:return tb(),q(G),q(B),c=b.stateNode,c.pendingContext&&(c.context=c.pendingContext,c.pendingContext=null),null!==a&&null!==a.child||!Zc(b)||(b.effectTag|=4),wh(b),null;case 5:te(b);c=Ta(Tb.current);var e=b.type;if(null!==a&&null!=b.stateNode)ij(a,b,e,d,c),a.ref!==b.ref&&(b.effectTag|=128);else{if(!d){if(null===b.stateNode)throw Error(k(166));return null}a=Ta(ja.current);if(Zc(b)){d=b.stateNode;e=b.type;var f=b.memoizedProps;d[Aa]=b;d[vc]=f;switch(e){case "iframe":case "object":case "embed":w("load",
|
|
d);break;case "video":case "audio":for(a=0;a<Db.length;a++)w(Db[a],d);break;case "source":w("error",d);break;case "img":case "image":case "link":w("error",d);w("load",d);break;case "form":w("reset",d);w("submit",d);break;case "details":w("toggle",d);break;case "input":Hf(d,f);w("invalid",d);oa(c,"onChange");break;case "select":d._wrapperState={wasMultiple:!!f.multiple};w("invalid",d);oa(c,"onChange");break;case "textarea":Kf(d,f),w("invalid",d),oa(c,"onChange")}Ud(e,f);a=null;for(var g in f)if(f.hasOwnProperty(g)){var h=
|
|
f[g];"children"===g?"string"===typeof h?d.textContent!==h&&(a=["children",h]):"number"===typeof h&&d.textContent!==""+h&&(a=["children",""+h]):db.hasOwnProperty(g)&&null!=h&&oa(c,g)}switch(e){case "input":mc(d);Jf(d,f,!0);break;case "textarea":mc(d);Mf(d);break;case "select":case "option":break;default:"function"===typeof f.onClick&&(d.onclick=uc)}c=a;b.updateQueue=c;null!==c&&(b.effectTag|=4)}else{g=9===c.nodeType?c:c.ownerDocument;"http://www.w3.org/1999/xhtml"===a&&(a=Nf(e));"http://www.w3.org/1999/xhtml"===
|
|
a?"script"===e?(a=g.createElement("div"),a.innerHTML="<script>\x3c/script>",a=a.removeChild(a.firstChild)):"string"===typeof d.is?a=g.createElement(e,{is:d.is}):(a=g.createElement(e),"select"===e&&(g=a,d.multiple?g.multiple=!0:d.size&&(g.size=d.size))):a=g.createElementNS(a,e);a[Aa]=b;a[vc]=d;jj(a,b,!1,!1);b.stateNode=a;g=Vd(e,d);switch(e){case "iframe":case "object":case "embed":w("load",a);h=d;break;case "video":case "audio":for(h=0;h<Db.length;h++)w(Db[h],a);h=d;break;case "source":w("error",a);
|
|
h=d;break;case "img":case "image":case "link":w("error",a);w("load",a);h=d;break;case "form":w("reset",a);w("submit",a);h=d;break;case "details":w("toggle",a);h=d;break;case "input":Hf(a,d);h=Cd(a,d);w("invalid",a);oa(c,"onChange");break;case "option":h=Fd(a,d);break;case "select":a._wrapperState={wasMultiple:!!d.multiple};h=M({},d,{value:void 0});w("invalid",a);oa(c,"onChange");break;case "textarea":Kf(a,d);h=Gd(a,d);w("invalid",a);oa(c,"onChange");break;default:h=d}Ud(e,h);var m=h;for(f in m)if(m.hasOwnProperty(f)){var n=
|
|
m[f];"style"===f?gg(a,n):"dangerouslySetInnerHTML"===f?(n=n?n.__html:void 0,null!=n&&xh(a,n)):"children"===f?"string"===typeof n?("textarea"!==e||""!==n)&&Wb(a,n):"number"===typeof n&&Wb(a,""+n):"suppressContentEditableWarning"!==f&&"suppressHydrationWarning"!==f&&"autoFocus"!==f&&(db.hasOwnProperty(f)?null!=n&&oa(c,f):null!=n&&xd(a,f,n,g))}switch(e){case "input":mc(a);Jf(a,d,!1);break;case "textarea":mc(a);Mf(a);break;case "option":null!=d.value&&a.setAttribute("value",""+va(d.value));break;case "select":a.multiple=
|
|
!!d.multiple;c=d.value;null!=c?hb(a,!!d.multiple,c,!1):null!=d.defaultValue&&hb(a,!!d.multiple,d.defaultValue,!0);break;default:"function"===typeof h.onClick&&(a.onclick=uc)}lg(e,d)&&(b.effectTag|=4)}null!==b.ref&&(b.effectTag|=128)}return null;case 6:if(a&&null!=b.stateNode)kj(a,b,a.memoizedProps,d);else{if("string"!==typeof d&&null===b.stateNode)throw Error(k(166));c=Ta(Tb.current);Ta(ja.current);Zc(b)?(c=b.stateNode,d=b.memoizedProps,c[Aa]=b,c.nodeValue!==d&&(b.effectTag|=4)):(c=(9===c.nodeType?
|
|
c:c.ownerDocument).createTextNode(d),c[Aa]=b,b.stateNode=c)}return null;case 13:q(D);d=b.memoizedState;if(0!==(b.effectTag&64))return b.expirationTime=c,b;c=null!==d;d=!1;null===a?void 0!==b.memoizedProps.fallback&&Zc(b):(e=a.memoizedState,d=null!==e,c||null===e||(e=a.child.sibling,null!==e&&(f=b.firstEffect,null!==f?(b.firstEffect=e,e.nextEffect=f):(b.firstEffect=b.lastEffect=e,e.nextEffect=null),e.effectTag=8)));if(c&&!d&&0!==(b.mode&2))if(null===a&&!0!==b.memoizedProps.unstable_avoidThisFallback||
|
|
0!==(D.current&1))F===Xa&&(F=ad);else{if(F===Xa||F===ad)F=bd;0!==Xb&&null!==U&&(Ya(U,P),yh(U,Xb))}if(c||d)b.effectTag|=4;return null;case 4:return tb(),wh(b),null;case 10:return me(b),null;case 17:return N(b.type)&&(q(G),q(B)),null;case 19:q(D);d=b.memoizedState;if(null===d)return null;e=0!==(b.effectTag&64);f=d.rendering;if(null===f)if(e)$c(d,!1);else{if(F!==Xa||null!==a&&0!==(a.effectTag&64))for(f=b.child;null!==f;){a=Rc(f);if(null!==a){b.effectTag|=64;$c(d,!1);e=a.updateQueue;null!==e&&(b.updateQueue=
|
|
e,b.effectTag|=4);null===d.lastEffect&&(b.firstEffect=null);b.lastEffect=d.lastEffect;for(d=b.child;null!==d;)e=d,f=c,e.effectTag&=2,e.nextEffect=null,e.firstEffect=null,e.lastEffect=null,a=e.alternate,null===a?(e.childExpirationTime=0,e.expirationTime=f,e.child=null,e.memoizedProps=null,e.memoizedState=null,e.updateQueue=null,e.dependencies=null):(e.childExpirationTime=a.childExpirationTime,e.expirationTime=a.expirationTime,e.child=a.child,e.memoizedProps=a.memoizedProps,e.memoizedState=a.memoizedState,
|
|
e.updateQueue=a.updateQueue,f=a.dependencies,e.dependencies=null===f?null:{expirationTime:f.expirationTime,firstContext:f.firstContext,responders:f.responders}),d=d.sibling;y(D,D.current&1|2);return b.child}f=f.sibling}}else{if(!e)if(a=Rc(f),null!==a){if(b.effectTag|=64,e=!0,c=a.updateQueue,null!==c&&(b.updateQueue=c,b.effectTag|=4),$c(d,!0),null===d.tail&&"hidden"===d.tailMode&&!f.alternate)return b=b.lastEffect=d.lastEffect,null!==b&&(b.nextEffect=null),null}else 2*Y()-d.renderingStartTime>d.tailExpiration&&
|
|
1<c&&(b.effectTag|=64,e=!0,$c(d,!1),b.expirationTime=b.childExpirationTime=c-1);d.isBackwards?(f.sibling=b.child,b.child=f):(c=d.last,null!==c?c.sibling=f:b.child=f,d.last=f)}return null!==d.tail?(0===d.tailExpiration&&(d.tailExpiration=Y()+500),c=d.tail,d.rendering=c,d.tail=c.sibling,d.lastEffect=b.lastEffect,d.renderingStartTime=Y(),c.sibling=null,b=D.current,y(D,e?b&1|2:b&1),c):null}throw Error(k(156,b.tag));}function lj(a,b){switch(a.tag){case 1:return N(a.type)&&(q(G),q(B)),b=a.effectTag,b&4096?
|
|
(a.effectTag=b&-4097|64,a):null;case 3:tb();q(G);q(B);b=a.effectTag;if(0!==(b&64))throw Error(k(285));a.effectTag=b&-4097|64;return a;case 5:return te(a),null;case 13:return q(D),b=a.effectTag,b&4096?(a.effectTag=b&-4097|64,a):null;case 19:return q(D),null;case 4:return tb(),null;case 10:return me(a),null;default:return null}}function Le(a,b){return{value:a,source:b,stack:Bd(b)}}function Me(a,b){var c=b.source,d=b.stack;null===d&&null!==c&&(d=Bd(c));null!==c&&na(c.type);b=b.value;null!==a&&1===a.tag&&
|
|
na(a.type);try{console.error(b)}catch(e){setTimeout(function(){throw e;})}}function mj(a,b){try{b.props=a.memoizedProps,b.state=a.memoizedState,b.componentWillUnmount()}catch(c){Za(a,c)}}function zh(a){var b=a.ref;if(null!==b)if("function"===typeof b)try{b(null)}catch(c){Za(a,c)}else b.current=null}function nj(a,b){switch(b.tag){case 0:case 11:case 15:case 22:return;case 1:if(b.effectTag&256&&null!==a){var c=a.memoizedProps,d=a.memoizedState;a=b.stateNode;b=a.getSnapshotBeforeUpdate(b.elementType===
|
|
b.type?c:aa(b.type,c),d);a.__reactInternalSnapshotBeforeUpdate=b}return;case 3:case 5:case 6:case 4:case 17:return}throw Error(k(163));}function Ah(a,b){b=b.updateQueue;b=null!==b?b.lastEffect:null;if(null!==b){var c=b=b.next;do{if((c.tag&a)===a){var d=c.destroy;c.destroy=void 0;void 0!==d&&d()}c=c.next}while(c!==b)}}function Bh(a,b){b=b.updateQueue;b=null!==b?b.lastEffect:null;if(null!==b){var c=b=b.next;do{if((c.tag&a)===a){var d=c.create;c.destroy=d()}c=c.next}while(c!==b)}}function oj(a,b,c,d){switch(c.tag){case 0:case 11:case 15:case 22:Bh(3,
|
|
c);return;case 1:a=c.stateNode;c.effectTag&4&&(null===b?a.componentDidMount():(d=c.elementType===c.type?b.memoizedProps:aa(c.type,b.memoizedProps),a.componentDidUpdate(d,b.memoizedState,a.__reactInternalSnapshotBeforeUpdate)));b=c.updateQueue;null!==b&&Wg(c,b,a);return;case 3:b=c.updateQueue;if(null!==b){a=null;if(null!==c.child)switch(c.child.tag){case 5:a=c.child.stateNode;break;case 1:a=c.child.stateNode}Wg(c,b,a)}return;case 5:a=c.stateNode;null===b&&c.effectTag&4&&lg(c.type,c.memoizedProps)&&
|
|
a.focus();return;case 6:return;case 4:return;case 12:return;case 13:null===c.memoizedState&&(c=c.alternate,null!==c&&(c=c.memoizedState,null!==c&&(c=c.dehydrated,null!==c&&bg(c))));return;case 19:case 17:case 20:case 21:return}throw Error(k(163));}function Ch(a,b,c){"function"===typeof Ne&&Ne(b);switch(b.tag){case 0:case 11:case 14:case 15:case 22:a=b.updateQueue;if(null!==a&&(a=a.lastEffect,null!==a)){var d=a.next;Da(97<c?97:c,function(){var a=d;do{var c=a.destroy;if(void 0!==c){var g=b;try{c()}catch(h){Za(g,
|
|
h)}}a=a.next}while(a!==d)})}break;case 1:zh(b);c=b.stateNode;"function"===typeof c.componentWillUnmount&&mj(b,c);break;case 5:zh(b);break;case 4:Dh(a,b,c)}}function Eh(a){var b=a.alternate;a.return=null;a.child=null;a.memoizedState=null;a.updateQueue=null;a.dependencies=null;a.alternate=null;a.firstEffect=null;a.lastEffect=null;a.pendingProps=null;a.memoizedProps=null;a.stateNode=null;null!==b&&Eh(b)}function Fh(a){return 5===a.tag||3===a.tag||4===a.tag}function Gh(a){a:{for(var b=a.return;null!==
|
|
b;){if(Fh(b)){var c=b;break a}b=b.return}throw Error(k(160));}b=c.stateNode;switch(c.tag){case 5:var d=!1;break;case 3:b=b.containerInfo;d=!0;break;case 4:b=b.containerInfo;d=!0;break;default:throw Error(k(161));}c.effectTag&16&&(Wb(b,""),c.effectTag&=-17);a:b:for(c=a;;){for(;null===c.sibling;){if(null===c.return||Fh(c.return)){c=null;break a}c=c.return}c.sibling.return=c.return;for(c=c.sibling;5!==c.tag&&6!==c.tag&&18!==c.tag;){if(c.effectTag&2)continue b;if(null===c.child||4===c.tag)continue b;
|
|
else c.child.return=c,c=c.child}if(!(c.effectTag&2)){c=c.stateNode;break a}}d?Oe(a,c,b):Pe(a,c,b)}function Oe(a,b,c){var d=a.tag,e=5===d||6===d;if(e)a=e?a.stateNode:a.stateNode.instance,b?8===c.nodeType?c.parentNode.insertBefore(a,b):c.insertBefore(a,b):(8===c.nodeType?(b=c.parentNode,b.insertBefore(a,c)):(b=c,b.appendChild(a)),c=c._reactRootContainer,null!==c&&void 0!==c||null!==b.onclick||(b.onclick=uc));else if(4!==d&&(a=a.child,null!==a))for(Oe(a,b,c),a=a.sibling;null!==a;)Oe(a,b,c),a=a.sibling}
|
|
function Pe(a,b,c){var d=a.tag,e=5===d||6===d;if(e)a=e?a.stateNode:a.stateNode.instance,b?c.insertBefore(a,b):c.appendChild(a);else if(4!==d&&(a=a.child,null!==a))for(Pe(a,b,c),a=a.sibling;null!==a;)Pe(a,b,c),a=a.sibling}function Dh(a,b,c){for(var d=b,e=!1,f,g;;){if(!e){e=d.return;a:for(;;){if(null===e)throw Error(k(160));f=e.stateNode;switch(e.tag){case 5:g=!1;break a;case 3:f=f.containerInfo;g=!0;break a;case 4:f=f.containerInfo;g=!0;break a}e=e.return}e=!0}if(5===d.tag||6===d.tag){a:for(var h=
|
|
a,m=d,n=c,l=m;;)if(Ch(h,l,n),null!==l.child&&4!==l.tag)l.child.return=l,l=l.child;else{if(l===m)break a;for(;null===l.sibling;){if(null===l.return||l.return===m)break a;l=l.return}l.sibling.return=l.return;l=l.sibling}g?(h=f,m=d.stateNode,8===h.nodeType?h.parentNode.removeChild(m):h.removeChild(m)):f.removeChild(d.stateNode)}else if(4===d.tag){if(null!==d.child){f=d.stateNode.containerInfo;g=!0;d.child.return=d;d=d.child;continue}}else if(Ch(a,d,c),null!==d.child){d.child.return=d;d=d.child;continue}if(d===
|
|
b)break;for(;null===d.sibling;){if(null===d.return||d.return===b)return;d=d.return;4===d.tag&&(e=!1)}d.sibling.return=d.return;d=d.sibling}}function Qe(a,b){switch(b.tag){case 0:case 11:case 14:case 15:case 22:Ah(3,b);return;case 1:return;case 5:var c=b.stateNode;if(null!=c){var d=b.memoizedProps,e=null!==a?a.memoizedProps:d;a=b.type;var f=b.updateQueue;b.updateQueue=null;if(null!==f){c[vc]=d;"input"===a&&"radio"===d.type&&null!=d.name&&If(c,d);Vd(a,e);b=Vd(a,d);for(e=0;e<f.length;e+=2){var g=f[e],
|
|
h=f[e+1];"style"===g?gg(c,h):"dangerouslySetInnerHTML"===g?xh(c,h):"children"===g?Wb(c,h):xd(c,g,h,b)}switch(a){case "input":Dd(c,d);break;case "textarea":Lf(c,d);break;case "select":b=c._wrapperState.wasMultiple,c._wrapperState.wasMultiple=!!d.multiple,a=d.value,null!=a?hb(c,!!d.multiple,a,!1):b!==!!d.multiple&&(null!=d.defaultValue?hb(c,!!d.multiple,d.defaultValue,!0):hb(c,!!d.multiple,d.multiple?[]:"",!1))}}}return;case 6:if(null===b.stateNode)throw Error(k(162));b.stateNode.nodeValue=b.memoizedProps;
|
|
return;case 3:b=b.stateNode;b.hydrate&&(b.hydrate=!1,bg(b.containerInfo));return;case 12:return;case 13:c=b;null===b.memoizedState?d=!1:(d=!0,c=b.child,Re=Y());if(null!==c)a:for(a=c;;){if(5===a.tag)f=a.stateNode,d?(f=f.style,"function"===typeof f.setProperty?f.setProperty("display","none","important"):f.display="none"):(f=a.stateNode,e=a.memoizedProps.style,e=void 0!==e&&null!==e&&e.hasOwnProperty("display")?e.display:null,f.style.display=fg("display",e));else if(6===a.tag)a.stateNode.nodeValue=d?
|
|
"":a.memoizedProps;else if(13===a.tag&&null!==a.memoizedState&&null===a.memoizedState.dehydrated){f=a.child.sibling;f.return=a;a=f;continue}else if(null!==a.child){a.child.return=a;a=a.child;continue}if(a===c)break;for(;null===a.sibling;){if(null===a.return||a.return===c)break a;a=a.return}a.sibling.return=a.return;a=a.sibling}Hh(b);return;case 19:Hh(b);return;case 17:return}throw Error(k(163));}function Hh(a){var b=a.updateQueue;if(null!==b){a.updateQueue=null;var c=a.stateNode;null===c&&(c=a.stateNode=
|
|
new pj);b.forEach(function(b){var d=qj.bind(null,a,b);c.has(b)||(c.add(b),b.then(d,d))})}}function Ih(a,b,c){c=Ea(c,null);c.tag=3;c.payload={element:null};var d=b.value;c.callback=function(){cd||(cd=!0,Se=d);Me(a,b)};return c}function Jh(a,b,c){c=Ea(c,null);c.tag=3;var d=a.type.getDerivedStateFromError;if("function"===typeof d){var e=b.value;c.payload=function(){Me(a,b);return d(e)}}var f=a.stateNode;null!==f&&"function"===typeof f.componentDidCatch&&(c.callback=function(){"function"!==typeof d&&
|
|
(null===La?La=new Set([this]):La.add(this),Me(a,b));var c=b.stack;this.componentDidCatch(b.value,{componentStack:null!==c?c:""})});return c}function ka(){return(p&(ca|ma))!==H?1073741821-(Y()/10|0):0!==dd?dd:dd=1073741821-(Y()/10|0)}function Va(a,b,c){b=b.mode;if(0===(b&2))return 1073741823;var d=Cc();if(0===(b&4))return 99===d?1073741823:1073741822;if((p&ca)!==H)return P;if(null!==c)a=Fc(a,c.timeoutMs|0||5E3,250);else switch(d){case 99:a=1073741823;break;case 98:a=Fc(a,150,100);break;case 97:case 96:a=
|
|
Fc(a,5E3,250);break;case 95:a=2;break;default:throw Error(k(326));}null!==U&&a===P&&--a;return a}function ed(a,b){a.expirationTime<b&&(a.expirationTime=b);var c=a.alternate;null!==c&&c.expirationTime<b&&(c.expirationTime=b);var d=a.return,e=null;if(null===d&&3===a.tag)e=a.stateNode;else for(;null!==d;){c=d.alternate;d.childExpirationTime<b&&(d.childExpirationTime=b);null!==c&&c.childExpirationTime<b&&(c.childExpirationTime=b);if(null===d.return&&3===d.tag){e=d.stateNode;break}d=d.return}null!==e&&
|
|
(U===e&&(Kc(b),F===bd&&Ya(e,P)),yh(e,b));return e}function fd(a){var b=a.lastExpiredTime;if(0!==b)return b;b=a.firstPendingTime;if(!Kh(a,b))return b;var c=a.lastPingedTime;a=a.nextKnownPendingLevel;a=c>a?c:a;return 2>=a&&b!==a?0:a}function V(a){if(0!==a.lastExpiredTime)a.callbackExpirationTime=1073741823,a.callbackPriority=99,a.callbackNode=Og(Te.bind(null,a));else{var b=fd(a),c=a.callbackNode;if(0===b)null!==c&&(a.callbackNode=null,a.callbackExpirationTime=0,a.callbackPriority=90);else{var d=ka();
|
|
1073741823===b?d=99:1===b||2===b?d=95:(d=10*(1073741821-b)-10*(1073741821-d),d=0>=d?99:250>=d?98:5250>=d?97:95);if(null!==c){var e=a.callbackPriority;if(a.callbackExpirationTime===b&&e>=d)return;c!==Qg&&Rg(c)}a.callbackExpirationTime=b;a.callbackPriority=d;b=1073741823===b?Og(Te.bind(null,a)):Ng(d,Lh.bind(null,a),{timeout:10*(1073741821-b)-Y()});a.callbackNode=b}}}function Lh(a,b){dd=0;if(b)return b=ka(),Ue(a,b),V(a),null;var c=fd(a);if(0!==c){b=a.callbackNode;if((p&(ca|ma))!==H)throw Error(k(327));
|
|
xb();a===U&&c===P||$a(a,c);if(null!==t){var d=p;p|=ca;var e=Mh();do try{rj();break}catch(h){Nh(a,h)}while(1);le();p=d;gd.current=e;if(F===hd)throw b=id,$a(a,c),Ya(a,c),V(a),b;if(null===t)switch(e=a.finishedWork=a.current.alternate,a.finishedExpirationTime=c,d=F,U=null,d){case Xa:case hd:throw Error(k(345));case Oh:Ue(a,2<c?2:c);break;case ad:Ya(a,c);d=a.lastSuspendedTime;c===d&&(a.nextKnownPendingLevel=Ve(e));if(1073741823===ta&&(e=Re+Ph-Y(),10<e)){if(jd){var f=a.lastPingedTime;if(0===f||f>=c){a.lastPingedTime=
|
|
c;$a(a,c);break}}f=fd(a);if(0!==f&&f!==c)break;if(0!==d&&d!==c){a.lastPingedTime=d;break}a.timeoutHandle=We(ab.bind(null,a),e);break}ab(a);break;case bd:Ya(a,c);d=a.lastSuspendedTime;c===d&&(a.nextKnownPendingLevel=Ve(e));if(jd&&(e=a.lastPingedTime,0===e||e>=c)){a.lastPingedTime=c;$a(a,c);break}e=fd(a);if(0!==e&&e!==c)break;if(0!==d&&d!==c){a.lastPingedTime=d;break}1073741823!==Yb?d=10*(1073741821-Yb)-Y():1073741823===ta?d=0:(d=10*(1073741821-ta)-5E3,e=Y(),c=10*(1073741821-c)-e,d=e-d,0>d&&(d=0),d=
|
|
(120>d?120:480>d?480:1080>d?1080:1920>d?1920:3E3>d?3E3:4320>d?4320:1960*sj(d/1960))-d,c<d&&(d=c));if(10<d){a.timeoutHandle=We(ab.bind(null,a),d);break}ab(a);break;case Xe:if(1073741823!==ta&&null!==kd){f=ta;var g=kd;d=g.busyMinDurationMs|0;0>=d?d=0:(e=g.busyDelayMs|0,f=Y()-(10*(1073741821-f)-(g.timeoutMs|0||5E3)),d=f<=e?0:e+d-f);if(10<d){Ya(a,c);a.timeoutHandle=We(ab.bind(null,a),d);break}}ab(a);break;default:throw Error(k(329));}V(a);if(a.callbackNode===b)return Lh.bind(null,a)}}return null}function Te(a){var b=
|
|
a.lastExpiredTime;b=0!==b?b:1073741823;if((p&(ca|ma))!==H)throw Error(k(327));xb();a===U&&b===P||$a(a,b);if(null!==t){var c=p;p|=ca;var d=Mh();do try{tj();break}catch(e){Nh(a,e)}while(1);le();p=c;gd.current=d;if(F===hd)throw c=id,$a(a,b),Ya(a,b),V(a),c;if(null!==t)throw Error(k(261));a.finishedWork=a.current.alternate;a.finishedExpirationTime=b;U=null;ab(a);V(a)}return null}function uj(){if(null!==bb){var a=bb;bb=null;a.forEach(function(a,c){Ue(c,a);V(c)});ha()}}function Qh(a,b){var c=p;p|=1;try{return a(b)}finally{p=
|
|
c,p===H&&ha()}}function Rh(a,b){var c=p;p&=-2;p|=Ye;try{return a(b)}finally{p=c,p===H&&ha()}}function $a(a,b){a.finishedWork=null;a.finishedExpirationTime=0;var c=a.timeoutHandle;-1!==c&&(a.timeoutHandle=-1,vj(c));if(null!==t)for(c=t.return;null!==c;){var d=c;switch(d.tag){case 1:d=d.type.childContextTypes;null!==d&&void 0!==d&&(q(G),q(B));break;case 3:tb();q(G);q(B);break;case 5:te(d);break;case 4:tb();break;case 13:q(D);break;case 19:q(D);break;case 10:me(d)}c=c.return}U=a;t=Sa(a.current,null);
|
|
P=b;F=Xa;id=null;Yb=ta=1073741823;kd=null;Xb=0;jd=!1}function Nh(a,b){do{try{le();Sc.current=Tc;if(Uc)for(var c=z.memoizedState;null!==c;){var d=c.queue;null!==d&&(d.pending=null);c=c.next}Ia=0;J=K=z=null;Uc=!1;if(null===t||null===t.return)return F=hd,id=b,t=null;a:{var e=a,f=t.return,g=t,h=b;b=P;g.effectTag|=2048;g.firstEffect=g.lastEffect=null;if(null!==h&&"object"===typeof h&&"function"===typeof h.then){var m=h;if(0===(g.mode&2)){var n=g.alternate;n?(g.updateQueue=n.updateQueue,g.memoizedState=
|
|
n.memoizedState,g.expirationTime=n.expirationTime):(g.updateQueue=null,g.memoizedState=null)}var l=0!==(D.current&1),k=f;do{var p;if(p=13===k.tag){var q=k.memoizedState;if(null!==q)p=null!==q.dehydrated?!0:!1;else{var w=k.memoizedProps;p=void 0===w.fallback?!1:!0!==w.unstable_avoidThisFallback?!0:l?!1:!0}}if(p){var y=k.updateQueue;if(null===y){var r=new Set;r.add(m);k.updateQueue=r}else y.add(m);if(0===(k.mode&2)){k.effectTag|=64;g.effectTag&=-2981;if(1===g.tag)if(null===g.alternate)g.tag=17;else{var O=
|
|
Ea(1073741823,null);O.tag=Jc;Fa(g,O)}g.expirationTime=1073741823;break a}h=void 0;g=b;var v=e.pingCache;null===v?(v=e.pingCache=new wj,h=new Set,v.set(m,h)):(h=v.get(m),void 0===h&&(h=new Set,v.set(m,h)));if(!h.has(g)){h.add(g);var x=xj.bind(null,e,m,g);m.then(x,x)}k.effectTag|=4096;k.expirationTime=b;break a}k=k.return}while(null!==k);h=Error((na(g.type)||"A React component")+" suspended while rendering, but no fallback UI was specified.\n\nAdd a <Suspense fallback=...> component higher in the tree to provide a loading indicator or placeholder to display."+
|
|
Bd(g))}F!==Xe&&(F=Oh);h=Le(h,g);k=f;do{switch(k.tag){case 3:m=h;k.effectTag|=4096;k.expirationTime=b;var A=Ih(k,m,b);Ug(k,A);break a;case 1:m=h;var u=k.type,B=k.stateNode;if(0===(k.effectTag&64)&&("function"===typeof u.getDerivedStateFromError||null!==B&&"function"===typeof B.componentDidCatch&&(null===La||!La.has(B)))){k.effectTag|=4096;k.expirationTime=b;var H=Jh(k,m,b);Ug(k,H);break a}}k=k.return}while(null!==k)}t=Sh(t)}catch(cj){b=cj;continue}break}while(1)}function Mh(a){a=gd.current;gd.current=
|
|
Tc;return null===a?Tc:a}function Vg(a,b){a<ta&&2<a&&(ta=a);null!==b&&a<Yb&&2<a&&(Yb=a,kd=b)}function Kc(a){a>Xb&&(Xb=a)}function tj(){for(;null!==t;)t=Th(t)}function rj(){for(;null!==t&&!yj();)t=Th(t)}function Th(a){var b=zj(a.alternate,a,P);a.memoizedProps=a.pendingProps;null===b&&(b=Sh(a));Uh.current=null;return b}function Sh(a){t=a;do{var b=t.alternate;a=t.return;if(0===(t.effectTag&2048)){b=hj(b,t,P);if(1===P||1!==t.childExpirationTime){for(var c=0,d=t.child;null!==d;){var e=d.expirationTime,
|
|
f=d.childExpirationTime;e>c&&(c=e);f>c&&(c=f);d=d.sibling}t.childExpirationTime=c}if(null!==b)return b;null!==a&&0===(a.effectTag&2048)&&(null===a.firstEffect&&(a.firstEffect=t.firstEffect),null!==t.lastEffect&&(null!==a.lastEffect&&(a.lastEffect.nextEffect=t.firstEffect),a.lastEffect=t.lastEffect),1<t.effectTag&&(null!==a.lastEffect?a.lastEffect.nextEffect=t:a.firstEffect=t,a.lastEffect=t))}else{b=lj(t);if(null!==b)return b.effectTag&=2047,b;null!==a&&(a.firstEffect=a.lastEffect=null,a.effectTag|=
|
|
2048)}b=t.sibling;if(null!==b)return b;t=a}while(null!==t);F===Xa&&(F=Xe);return null}function Ve(a){var b=a.expirationTime;a=a.childExpirationTime;return b>a?b:a}function ab(a){var b=Cc();Da(99,Aj.bind(null,a,b));return null}function Aj(a,b){do xb();while(null!==Zb);if((p&(ca|ma))!==H)throw Error(k(327));var c=a.finishedWork,d=a.finishedExpirationTime;if(null===c)return null;a.finishedWork=null;a.finishedExpirationTime=0;if(c===a.current)throw Error(k(177));a.callbackNode=null;a.callbackExpirationTime=
|
|
0;a.callbackPriority=90;a.nextKnownPendingLevel=0;var e=Ve(c);a.firstPendingTime=e;d<=a.lastSuspendedTime?a.firstSuspendedTime=a.lastSuspendedTime=a.nextKnownPendingLevel=0:d<=a.firstSuspendedTime&&(a.firstSuspendedTime=d-1);d<=a.lastPingedTime&&(a.lastPingedTime=0);d<=a.lastExpiredTime&&(a.lastExpiredTime=0);a===U&&(t=U=null,P=0);1<c.effectTag?null!==c.lastEffect?(c.lastEffect.nextEffect=c,e=c.firstEffect):e=c:e=c.firstEffect;if(null!==e){var f=p;p|=ma;Uh.current=null;Ze=tc;var g=kg();if(Xd(g)){if("selectionStart"in
|
|
g)var h={start:g.selectionStart,end:g.selectionEnd};else a:{h=(h=g.ownerDocument)&&h.defaultView||window;var m=h.getSelection&&h.getSelection();if(m&&0!==m.rangeCount){h=m.anchorNode;var n=m.anchorOffset,q=m.focusNode;m=m.focusOffset;try{h.nodeType,q.nodeType}catch(sb){h=null;break a}var ba=0,w=-1,y=-1,B=0,D=0,r=g,z=null;b:for(;;){for(var v;;){r!==h||0!==n&&3!==r.nodeType||(w=ba+n);r!==q||0!==m&&3!==r.nodeType||(y=ba+m);3===r.nodeType&&(ba+=r.nodeValue.length);if(null===(v=r.firstChild))break;z=r;
|
|
r=v}for(;;){if(r===g)break b;z===h&&++B===n&&(w=ba);z===q&&++D===m&&(y=ba);if(null!==(v=r.nextSibling))break;r=z;z=r.parentNode}r=v}h=-1===w||-1===y?null:{start:w,end:y}}else h=null}h=h||{start:0,end:0}}else h=null;$e={activeElementDetached:null,focusedElem:g,selectionRange:h};tc=!1;l=e;do try{Bj()}catch(sb){if(null===l)throw Error(k(330));Za(l,sb);l=l.nextEffect}while(null!==l);l=e;do try{for(g=a,h=b;null!==l;){var x=l.effectTag;x&16&&Wb(l.stateNode,"");if(x&128){var A=l.alternate;if(null!==A){var u=
|
|
A.ref;null!==u&&("function"===typeof u?u(null):u.current=null)}}switch(x&1038){case 2:Gh(l);l.effectTag&=-3;break;case 6:Gh(l);l.effectTag&=-3;Qe(l.alternate,l);break;case 1024:l.effectTag&=-1025;break;case 1028:l.effectTag&=-1025;Qe(l.alternate,l);break;case 4:Qe(l.alternate,l);break;case 8:n=l,Dh(g,n,h),Eh(n)}l=l.nextEffect}}catch(sb){if(null===l)throw Error(k(330));Za(l,sb);l=l.nextEffect}while(null!==l);u=$e;A=kg();x=u.focusedElem;h=u.selectionRange;if(A!==x&&x&&x.ownerDocument&&jg(x.ownerDocument.documentElement,
|
|
x)){null!==h&&Xd(x)&&(A=h.start,u=h.end,void 0===u&&(u=A),"selectionStart"in x?(x.selectionStart=A,x.selectionEnd=Math.min(u,x.value.length)):(u=(A=x.ownerDocument||document)&&A.defaultView||window,u.getSelection&&(u=u.getSelection(),n=x.textContent.length,g=Math.min(h.start,n),h=void 0===h.end?g:Math.min(h.end,n),!u.extend&&g>h&&(n=h,h=g,g=n),n=ig(x,g),q=ig(x,h),n&&q&&(1!==u.rangeCount||u.anchorNode!==n.node||u.anchorOffset!==n.offset||u.focusNode!==q.node||u.focusOffset!==q.offset)&&(A=A.createRange(),
|
|
A.setStart(n.node,n.offset),u.removeAllRanges(),g>h?(u.addRange(A),u.extend(q.node,q.offset)):(A.setEnd(q.node,q.offset),u.addRange(A))))));A=[];for(u=x;u=u.parentNode;)1===u.nodeType&&A.push({element:u,left:u.scrollLeft,top:u.scrollTop});"function"===typeof x.focus&&x.focus();for(x=0;x<A.length;x++)u=A[x],u.element.scrollLeft=u.left,u.element.scrollTop=u.top}tc=!!Ze;$e=Ze=null;a.current=c;l=e;do try{for(x=a;null!==l;){var F=l.effectTag;F&36&&oj(x,l.alternate,l);if(F&128){A=void 0;var E=l.ref;if(null!==
|
|
E){var G=l.stateNode;switch(l.tag){case 5:A=G;break;default:A=G}"function"===typeof E?E(A):E.current=A}}l=l.nextEffect}}catch(sb){if(null===l)throw Error(k(330));Za(l,sb);l=l.nextEffect}while(null!==l);l=null;Cj();p=f}else a.current=c;if(ld)ld=!1,Zb=a,$b=b;else for(l=e;null!==l;)b=l.nextEffect,l.nextEffect=null,l=b;b=a.firstPendingTime;0===b&&(La=null);1073741823===b?a===af?ac++:(ac=0,af=a):ac=0;"function"===typeof bf&&bf(c.stateNode,d);V(a);if(cd)throw cd=!1,a=Se,Se=null,a;if((p&Ye)!==H)return null;
|
|
ha();return null}function Bj(){for(;null!==l;){var a=l.effectTag;0!==(a&256)&&nj(l.alternate,l);0===(a&512)||ld||(ld=!0,Ng(97,function(){xb();return null}));l=l.nextEffect}}function xb(){if(90!==$b){var a=97<$b?97:$b;$b=90;return Da(a,Dj)}}function Dj(){if(null===Zb)return!1;var a=Zb;Zb=null;if((p&(ca|ma))!==H)throw Error(k(331));var b=p;p|=ma;for(a=a.current.firstEffect;null!==a;){try{var c=a;if(0!==(c.effectTag&512))switch(c.tag){case 0:case 11:case 15:case 22:Ah(5,c),Bh(5,c)}}catch(d){if(null===
|
|
a)throw Error(k(330));Za(a,d)}c=a.nextEffect;a.nextEffect=null;a=c}p=b;ha();return!0}function Vh(a,b,c){b=Le(c,b);b=Ih(a,b,1073741823);Fa(a,b);a=ed(a,1073741823);null!==a&&V(a)}function Za(a,b){if(3===a.tag)Vh(a,a,b);else for(var c=a.return;null!==c;){if(3===c.tag){Vh(c,a,b);break}else if(1===c.tag){var d=c.stateNode;if("function"===typeof c.type.getDerivedStateFromError||"function"===typeof d.componentDidCatch&&(null===La||!La.has(d))){a=Le(b,a);a=Jh(c,a,1073741823);Fa(c,a);c=ed(c,1073741823);null!==
|
|
c&&V(c);break}}c=c.return}}function xj(a,b,c){var d=a.pingCache;null!==d&&d.delete(b);U===a&&P===c?F===bd||F===ad&&1073741823===ta&&Y()-Re<Ph?$a(a,P):jd=!0:Kh(a,c)&&(b=a.lastPingedTime,0!==b&&b<c||(a.lastPingedTime=c,V(a)))}function qj(a,b){var c=a.stateNode;null!==c&&c.delete(b);b=0;0===b&&(b=ka(),b=Va(b,a,null));a=ed(a,b);null!==a&&V(a)}function Ej(a){if("undefined"===typeof __REACT_DEVTOOLS_GLOBAL_HOOK__)return!1;var b=__REACT_DEVTOOLS_GLOBAL_HOOK__;if(b.isDisabled||!b.supportsFiber)return!0;try{var c=
|
|
b.inject(a);bf=function(a,e){try{b.onCommitFiberRoot(c,a,void 0,64===(a.current.effectTag&64))}catch(f){}};Ne=function(a){try{b.onCommitFiberUnmount(c,a)}catch(e){}}}catch(d){}return!0}function Fj(a,b,c,d){this.tag=a;this.key=c;this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null;this.index=0;this.ref=null;this.pendingProps=b;this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null;this.mode=d;this.effectTag=0;this.lastEffect=this.firstEffect=this.nextEffect=
|
|
null;this.childExpirationTime=this.expirationTime=0;this.alternate=null}function Ge(a){a=a.prototype;return!(!a||!a.isReactComponent)}function Gj(a){if("function"===typeof a)return Ge(a)?1:0;if(void 0!==a&&null!==a){a=a.$$typeof;if(a===zd)return 11;if(a===Ad)return 14}return 2}function Sa(a,b){var c=a.alternate;null===c?(c=la(a.tag,b,a.key,a.mode),c.elementType=a.elementType,c.type=a.type,c.stateNode=a.stateNode,c.alternate=a,a.alternate=c):(c.pendingProps=b,c.effectTag=0,c.nextEffect=null,c.firstEffect=
|
|
null,c.lastEffect=null);c.childExpirationTime=a.childExpirationTime;c.expirationTime=a.expirationTime;c.child=a.child;c.memoizedProps=a.memoizedProps;c.memoizedState=a.memoizedState;c.updateQueue=a.updateQueue;b=a.dependencies;c.dependencies=null===b?null:{expirationTime:b.expirationTime,firstContext:b.firstContext,responders:b.responders};c.sibling=a.sibling;c.index=a.index;c.ref=a.ref;return c}function Oc(a,b,c,d,e,f){var g=2;d=a;if("function"===typeof a)Ge(a)&&(g=1);else if("string"===typeof a)g=
|
|
5;else a:switch(a){case Ma:return Ha(c.children,e,f,b);case Hj:g=8;e|=7;break;case Af:g=8;e|=1;break;case kc:return a=la(12,c,b,e|8),a.elementType=kc,a.type=kc,a.expirationTime=f,a;case lc:return a=la(13,c,b,e),a.type=lc,a.elementType=lc,a.expirationTime=f,a;case yd:return a=la(19,c,b,e),a.elementType=yd,a.expirationTime=f,a;default:if("object"===typeof a&&null!==a)switch(a.$$typeof){case Cf:g=10;break a;case Bf:g=9;break a;case zd:g=11;break a;case Ad:g=14;break a;case Ef:g=16;d=null;break a;case Df:g=
|
|
22;break a}throw Error(k(130,null==a?a:typeof a,""));}b=la(g,c,b,e);b.elementType=a;b.type=d;b.expirationTime=f;return b}function Ha(a,b,c,d){a=la(7,a,d,b);a.expirationTime=c;return a}function qe(a,b,c){a=la(6,a,null,b);a.expirationTime=c;return a}function re(a,b,c){b=la(4,null!==a.children?a.children:[],a.key,b);b.expirationTime=c;b.stateNode={containerInfo:a.containerInfo,pendingChildren:null,implementation:a.implementation};return b}function Ij(a,b,c){this.tag=b;this.current=null;this.containerInfo=
|
|
a;this.pingCache=this.pendingChildren=null;this.finishedExpirationTime=0;this.finishedWork=null;this.timeoutHandle=-1;this.pendingContext=this.context=null;this.hydrate=c;this.callbackNode=null;this.callbackPriority=90;this.lastExpiredTime=this.lastPingedTime=this.nextKnownPendingLevel=this.lastSuspendedTime=this.firstSuspendedTime=this.firstPendingTime=0}function Kh(a,b){var c=a.firstSuspendedTime;a=a.lastSuspendedTime;return 0!==c&&c>=b&&a<=b}function Ya(a,b){var c=a.firstSuspendedTime,d=a.lastSuspendedTime;
|
|
c<b&&(a.firstSuspendedTime=b);if(d>b||0===c)a.lastSuspendedTime=b;b<=a.lastPingedTime&&(a.lastPingedTime=0);b<=a.lastExpiredTime&&(a.lastExpiredTime=0)}function yh(a,b){b>a.firstPendingTime&&(a.firstPendingTime=b);var c=a.firstSuspendedTime;0!==c&&(b>=c?a.firstSuspendedTime=a.lastSuspendedTime=a.nextKnownPendingLevel=0:b>=a.lastSuspendedTime&&(a.lastSuspendedTime=b+1),b>a.nextKnownPendingLevel&&(a.nextKnownPendingLevel=b))}function Ue(a,b){var c=a.lastExpiredTime;if(0===c||c>b)a.lastExpiredTime=b}
|
|
function md(a,b,c,d){var e=b.current,f=ka(),g=Vb.suspense;f=Va(f,e,g);a:if(c){c=c._reactInternalFiber;b:{if(Na(c)!==c||1!==c.tag)throw Error(k(170));var h=c;do{switch(h.tag){case 3:h=h.stateNode.context;break b;case 1:if(N(h.type)){h=h.stateNode.__reactInternalMemoizedMergedChildContext;break b}}h=h.return}while(null!==h);throw Error(k(171));}if(1===c.tag){var m=c.type;if(N(m)){c=Gg(c,m,h);break a}}c=h}else c=Ca;null===b.context?b.context=c:b.pendingContext=c;b=Ea(f,g);b.payload={element:a};d=void 0===
|
|
d?null:d;null!==d&&(b.callback=d);Fa(e,b);Ja(e,f);return f}function cf(a){a=a.current;if(!a.child)return null;switch(a.child.tag){case 5:return a.child.stateNode;default:return a.child.stateNode}}function Wh(a,b){a=a.memoizedState;null!==a&&null!==a.dehydrated&&a.retryTime<b&&(a.retryTime=b)}function df(a,b){Wh(a,b);(a=a.alternate)&&Wh(a,b)}function ef(a,b,c){c=null!=c&&!0===c.hydrate;var d=new Ij(a,b,c),e=la(3,null,null,2===b?7:1===b?3:0);d.current=e;e.stateNode=d;ne(e);a[Lb]=d.current;c&&0!==b&&
|
|
xi(a,9===a.nodeType?a:a.ownerDocument);this._internalRoot=d}function bc(a){return!(!a||1!==a.nodeType&&9!==a.nodeType&&11!==a.nodeType&&(8!==a.nodeType||" react-mount-point-unstable "!==a.nodeValue))}function Jj(a,b){b||(b=a?9===a.nodeType?a.documentElement:a.firstChild:null,b=!(!b||1!==b.nodeType||!b.hasAttribute("data-reactroot")));if(!b)for(var c;c=a.lastChild;)a.removeChild(c);return new ef(a,0,b?{hydrate:!0}:void 0)}function nd(a,b,c,d,e){var f=c._reactRootContainer;if(f){var g=f._internalRoot;
|
|
if("function"===typeof e){var h=e;e=function(){var a=cf(g);h.call(a)}}md(b,g,a,e)}else{f=c._reactRootContainer=Jj(c,d);g=f._internalRoot;if("function"===typeof e){var m=e;e=function(){var a=cf(g);m.call(a)}}Rh(function(){md(b,g,a,e)})}return cf(g)}function Kj(a,b,c){var d=3<arguments.length&&void 0!==arguments[3]?arguments[3]:null;return{$$typeof:gb,key:null==d?null:""+d,children:a,containerInfo:b,implementation:c}}function Xh(a,b){var c=2<arguments.length&&void 0!==arguments[2]?arguments[2]:null;
|
|
if(!bc(b))throw Error(k(200));return Kj(a,b,null,c)}if(!ea)throw Error(k(227));var ki=function(a,b,c,d,e,f,g,h,m){var n=Array.prototype.slice.call(arguments,3);try{b.apply(c,n)}catch(C){this.onError(C)}},yb=!1,gc=null,hc=!1,pd=null,li={onError:function(a){yb=!0;gc=a}},td=null,rf=null,mf=null,ic=null,cb={},jc=[],qd={},db={},rd={},wa=!("undefined"===typeof window||"undefined"===typeof window.document||"undefined"===typeof window.document.createElement),M=ea.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.assign,
|
|
sd=null,eb=null,fb=null,ee=function(a,b){return a(b)},eg=function(a,b,c,d,e){return a(b,c,d,e)},vd=function(){},vf=ee,Oa=!1,wd=!1,Z=ea.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.Scheduler,Lj=Z.unstable_cancelCallback,ff=Z.unstable_now,$f=Z.unstable_scheduleCallback,Mj=Z.unstable_shouldYield,Yh=Z.unstable_requestPaint,Pd=Z.unstable_runWithPriority,Nj=Z.unstable_getCurrentPriorityLevel,Oj=Z.unstable_ImmediatePriority,Zh=Z.unstable_UserBlockingPriority,ag=Z.unstable_NormalPriority,Pj=Z.unstable_LowPriority,
|
|
Qj=Z.unstable_IdlePriority,oi=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,wf=Object.prototype.hasOwnProperty,yf={},xf={},E={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(a){E[a]=
|
|
new L(a,0,!1,a,null,!1)});[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(a){var b=a[0];E[b]=new L(b,1,!1,a[1],null,!1)});["contentEditable","draggable","spellCheck","value"].forEach(function(a){E[a]=new L(a,2,!1,a.toLowerCase(),null,!1)});["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(a){E[a]=new L(a,2,!1,a,null,!1)});"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(a){E[a]=
|
|
new L(a,3,!1,a.toLowerCase(),null,!1)});["checked","multiple","muted","selected"].forEach(function(a){E[a]=new L(a,3,!0,a,null,!1)});["capture","download"].forEach(function(a){E[a]=new L(a,4,!1,a,null,!1)});["cols","rows","size","span"].forEach(function(a){E[a]=new L(a,6,!1,a,null,!1)});["rowSpan","start"].forEach(function(a){E[a]=new L(a,5,!1,a.toLowerCase(),null,!1)});var gf=/[\-:]([a-z])/g,hf=function(a){return a[1].toUpperCase()};"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(a){var b=
|
|
a.replace(gf,hf);E[b]=new L(b,1,!1,a,null,!1)});"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(a){var b=a.replace(gf,hf);E[b]=new L(b,1,!1,a,"http://www.w3.org/1999/xlink",!1)});["xml:base","xml:lang","xml:space"].forEach(function(a){var b=a.replace(gf,hf);E[b]=new L(b,1,!1,a,"http://www.w3.org/XML/1998/namespace",!1)});["tabIndex","crossOrigin"].forEach(function(a){E[a]=new L(a,1,!1,a.toLowerCase(),null,!1)});E.xlinkHref=new L("xlinkHref",1,
|
|
!1,"xlink:href","http://www.w3.org/1999/xlink",!0);["src","href","action","formAction"].forEach(function(a){E[a]=new L(a,1,!1,a.toLowerCase(),null,!0)});var da=ea.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;da.hasOwnProperty("ReactCurrentDispatcher")||(da.ReactCurrentDispatcher={current:null});da.hasOwnProperty("ReactCurrentBatchConfig")||(da.ReactCurrentBatchConfig={suspense:null});var si=/^(.*)[\\\/]/,Q="function"===typeof Symbol&&Symbol.for,Pc=Q?Symbol.for("react.element"):60103,gb=Q?Symbol.for("react.portal"):
|
|
60106,Ma=Q?Symbol.for("react.fragment"):60107,Af=Q?Symbol.for("react.strict_mode"):60108,kc=Q?Symbol.for("react.profiler"):60114,Cf=Q?Symbol.for("react.provider"):60109,Bf=Q?Symbol.for("react.context"):60110,Hj=Q?Symbol.for("react.concurrent_mode"):60111,zd=Q?Symbol.for("react.forward_ref"):60112,lc=Q?Symbol.for("react.suspense"):60113,yd=Q?Symbol.for("react.suspense_list"):60120,Ad=Q?Symbol.for("react.memo"):60115,Ef=Q?Symbol.for("react.lazy"):60116,Df=Q?Symbol.for("react.block"):60121,zf="function"===
|
|
typeof Symbol&&Symbol.iterator,od,xh=function(a){return"undefined"!==typeof MSApp&&MSApp.execUnsafeLocalFunction?function(b,c,d,e){MSApp.execUnsafeLocalFunction(function(){return a(b,c,d,e)})}:a}(function(a,b){if("http://www.w3.org/2000/svg"!==a.namespaceURI||"innerHTML"in a)a.innerHTML=b;else{od=od||document.createElement("div");od.innerHTML="<svg>"+b.valueOf().toString()+"</svg>";for(b=od.firstChild;a.firstChild;)a.removeChild(a.firstChild);for(;b.firstChild;)a.appendChild(b.firstChild)}}),Wb=function(a,
|
|
b){if(b){var c=a.firstChild;if(c&&c===a.lastChild&&3===c.nodeType){c.nodeValue=b;return}}a.textContent=b},ib={animationend:nc("Animation","AnimationEnd"),animationiteration:nc("Animation","AnimationIteration"),animationstart:nc("Animation","AnimationStart"),transitionend:nc("Transition","TransitionEnd")},Id={},Of={};wa&&(Of=document.createElement("div").style,"AnimationEvent"in window||(delete ib.animationend.animation,delete ib.animationiteration.animation,delete ib.animationstart.animation),"TransitionEvent"in
|
|
window||delete ib.transitionend.transition);var $h=oc("animationend"),ai=oc("animationiteration"),bi=oc("animationstart"),ci=oc("transitionend"),Db="abort canplay canplaythrough durationchange emptied encrypted ended error loadeddata loadedmetadata loadstart pause play playing progress ratechange seeked seeking stalled suspend timeupdate volumechange waiting".split(" "),Pf=new ("function"===typeof WeakMap?WeakMap:Map),Ab=null,wi=function(a){if(a){var b=a._dispatchListeners,c=a._dispatchInstances;
|
|
if(Array.isArray(b))for(var d=0;d<b.length&&!a.isPropagationStopped();d++)lf(a,b[d],c[d]);else b&&lf(a,b,c);a._dispatchListeners=null;a._dispatchInstances=null;a.isPersistent()||a.constructor.release(a)}},qc=[],Rd=!1,fa=[],xa=null,ya=null,za=null,Eb=new Map,Fb=new Map,Jb=[],Nd="mousedown mouseup touchcancel touchend touchstart auxclick dblclick pointercancel pointerdown pointerup dragend dragstart drop compositionend compositionstart keydown keypress keyup input textInput close cancel copy cut paste click change contextmenu reset submit".split(" "),
|
|
yi="focus blur dragenter dragleave mouseover mouseout pointerover pointerout gotpointercapture lostpointercapture".split(" "),dg={},cg=new Map,Td=new Map,Rj=["abort","abort",$h,"animationEnd",ai,"animationIteration",bi,"animationStart","canplay","canPlay","canplaythrough","canPlayThrough","durationchange","durationChange","emptied","emptied","encrypted","encrypted","ended","ended","error","error","gotpointercapture","gotPointerCapture","load","load","loadeddata","loadedData","loadedmetadata","loadedMetadata",
|
|
"loadstart","loadStart","lostpointercapture","lostPointerCapture","playing","playing","progress","progress","seeking","seeking","stalled","stalled","suspend","suspend","timeupdate","timeUpdate",ci,"transitionEnd","waiting","waiting"];Sd("blur blur cancel cancel click click close close contextmenu contextMenu copy copy cut cut auxclick auxClick dblclick doubleClick dragend dragEnd dragstart dragStart drop drop focus focus input input invalid invalid keydown keyDown keypress keyPress keyup keyUp mousedown mouseDown mouseup mouseUp paste paste pause pause play play pointercancel pointerCancel pointerdown pointerDown pointerup pointerUp ratechange rateChange reset reset seeked seeked submit submit touchcancel touchCancel touchend touchEnd touchstart touchStart volumechange volumeChange".split(" "),
|
|
0);Sd("drag drag dragenter dragEnter dragexit dragExit dragleave dragLeave dragover dragOver mousemove mouseMove mouseout mouseOut mouseover mouseOver pointermove pointerMove pointerout pointerOut pointerover pointerOver scroll scroll toggle toggle touchmove touchMove wheel wheel".split(" "),1);Sd(Rj,2);(function(a,b){for(var c=0;c<a.length;c++)Td.set(a[c],b)})("change selectionchange textInput compositionstart compositionend compositionupdate".split(" "),0);var Hi=Zh,Gi=Pd,tc=!0,Kb={animationIterationCount:!0,
|
|
borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,
|
|
strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},Sj=["Webkit","ms","Moz","O"];Object.keys(Kb).forEach(function(a){Sj.forEach(function(b){b=b+a.charAt(0).toUpperCase()+a.substring(1);Kb[b]=Kb[a]})});var Ii=M({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0}),ng="$",og="/$",$d="$?",Zd="$!",Ze=null,$e=null,We="function"===typeof setTimeout?setTimeout:void 0,vj="function"===
|
|
typeof clearTimeout?clearTimeout:void 0,jf=Math.random().toString(36).slice(2),Aa="__reactInternalInstance$"+jf,vc="__reactEventHandlers$"+jf,Lb="__reactContainere$"+jf,Ba=null,ce=null,wc=null;M(R.prototype,{preventDefault:function(){this.defaultPrevented=!0;var a=this.nativeEvent;a&&(a.preventDefault?a.preventDefault():"unknown"!==typeof a.returnValue&&(a.returnValue=!1),this.isDefaultPrevented=xc)},stopPropagation:function(){var a=this.nativeEvent;a&&(a.stopPropagation?a.stopPropagation():"unknown"!==
|
|
typeof a.cancelBubble&&(a.cancelBubble=!0),this.isPropagationStopped=xc)},persist:function(){this.isPersistent=xc},isPersistent:yc,destructor:function(){var a=this.constructor.Interface,b;for(b in a)this[b]=null;this.nativeEvent=this._targetInst=this.dispatchConfig=null;this.isPropagationStopped=this.isDefaultPrevented=yc;this._dispatchInstances=this._dispatchListeners=null}});R.Interface={type:null,target:null,currentTarget:function(){return null},eventPhase:null,bubbles:null,cancelable:null,timeStamp:function(a){return a.timeStamp||
|
|
Date.now()},defaultPrevented:null,isTrusted:null};R.extend=function(a){function b(){return c.apply(this,arguments)}var c=this,d=function(){};d.prototype=c.prototype;d=new d;M(d,b.prototype);b.prototype=d;b.prototype.constructor=b;b.Interface=M({},c.Interface,a);b.extend=c.extend;sg(b);return b};sg(R);var Tj=R.extend({data:null}),Uj=R.extend({data:null}),Ni=[9,13,27,32],de=wa&&"CompositionEvent"in window,cc=null;wa&&"documentMode"in document&&(cc=document.documentMode);var Vj=wa&&"TextEvent"in window&&
|
|
!cc,xg=wa&&(!de||cc&&8<cc&&11>=cc),wg=String.fromCharCode(32),ua={beforeInput:{phasedRegistrationNames:{bubbled:"onBeforeInput",captured:"onBeforeInputCapture"},dependencies:["compositionend","keypress","textInput","paste"]},compositionEnd:{phasedRegistrationNames:{bubbled:"onCompositionEnd",captured:"onCompositionEndCapture"},dependencies:"blur compositionend keydown keypress keyup mousedown".split(" ")},compositionStart:{phasedRegistrationNames:{bubbled:"onCompositionStart",captured:"onCompositionStartCapture"},
|
|
dependencies:"blur compositionstart keydown keypress keyup mousedown".split(" ")},compositionUpdate:{phasedRegistrationNames:{bubbled:"onCompositionUpdate",captured:"onCompositionUpdateCapture"},dependencies:"blur compositionupdate keydown keypress keyup mousedown".split(" ")}},vg=!1,mb=!1,Wj={eventTypes:ua,extractEvents:function(a,b,c,d,e){var f;if(de)b:{switch(a){case "compositionstart":var g=ua.compositionStart;break b;case "compositionend":g=ua.compositionEnd;break b;case "compositionupdate":g=
|
|
ua.compositionUpdate;break b}g=void 0}else mb?tg(a,c)&&(g=ua.compositionEnd):"keydown"===a&&229===c.keyCode&&(g=ua.compositionStart);g?(xg&&"ko"!==c.locale&&(mb||g!==ua.compositionStart?g===ua.compositionEnd&&mb&&(f=rg()):(Ba=d,ce="value"in Ba?Ba.value:Ba.textContent,mb=!0)),e=Tj.getPooled(g,b,c,d),f?e.data=f:(f=ug(c),null!==f&&(e.data=f)),lb(e),f=e):f=null;(a=Vj?Oi(a,c):Pi(a,c))?(b=Uj.getPooled(ua.beforeInput,b,c,d),b.data=a,lb(b)):b=null;return null===f?b:null===b?f:[f,b]}},Qi={color:!0,date:!0,
|
|
datetime:!0,"datetime-local":!0,email:!0,month:!0,number:!0,password:!0,range:!0,search:!0,tel:!0,text:!0,time:!0,url:!0,week:!0},Ag={change:{phasedRegistrationNames:{bubbled:"onChange",captured:"onChangeCapture"},dependencies:"blur change click focus input keydown keyup selectionchange".split(" ")}},Mb=null,Nb=null,kf=!1;wa&&(kf=Tf("input")&&(!document.documentMode||9<document.documentMode));var Xj={eventTypes:Ag,_isInputEventSupported:kf,extractEvents:function(a,b,c,d,e){e=b?Pa(b):window;var f=
|
|
e.nodeName&&e.nodeName.toLowerCase();if("select"===f||"input"===f&&"file"===e.type)var g=Si;else if(yg(e))if(kf)g=Wi;else{g=Ui;var h=Ti}else(f=e.nodeName)&&"input"===f.toLowerCase()&&("checkbox"===e.type||"radio"===e.type)&&(g=Vi);if(g&&(g=g(a,b)))return zg(g,c,d);h&&h(a,e,b);"blur"===a&&(a=e._wrapperState)&&a.controlled&&"number"===e.type&&Ed(e,"number",e.value)}},dc=R.extend({view:null,detail:null}),Yi={Alt:"altKey",Control:"ctrlKey",Meta:"metaKey",Shift:"shiftKey"},di=0,ei=0,fi=!1,gi=!1,ec=dc.extend({screenX:null,
|
|
screenY:null,clientX:null,clientY:null,pageX:null,pageY:null,ctrlKey:null,shiftKey:null,altKey:null,metaKey:null,getModifierState:fe,button:null,buttons:null,relatedTarget:function(a){return a.relatedTarget||(a.fromElement===a.srcElement?a.toElement:a.fromElement)},movementX:function(a){if("movementX"in a)return a.movementX;var b=di;di=a.screenX;return fi?"mousemove"===a.type?a.screenX-b:0:(fi=!0,0)},movementY:function(a){if("movementY"in a)return a.movementY;var b=ei;ei=a.screenY;return gi?"mousemove"===
|
|
a.type?a.screenY-b:0:(gi=!0,0)}}),hi=ec.extend({pointerId:null,width:null,height:null,pressure:null,tangentialPressure:null,tiltX:null,tiltY:null,twist:null,pointerType:null,isPrimary:null}),fc={mouseEnter:{registrationName:"onMouseEnter",dependencies:["mouseout","mouseover"]},mouseLeave:{registrationName:"onMouseLeave",dependencies:["mouseout","mouseover"]},pointerEnter:{registrationName:"onPointerEnter",dependencies:["pointerout","pointerover"]},pointerLeave:{registrationName:"onPointerLeave",dependencies:["pointerout",
|
|
"pointerover"]}},Yj={eventTypes:fc,extractEvents:function(a,b,c,d,e){var f="mouseover"===a||"pointerover"===a,g="mouseout"===a||"pointerout"===a;if(f&&0===(e&32)&&(c.relatedTarget||c.fromElement)||!g&&!f)return null;f=d.window===d?d:(f=d.ownerDocument)?f.defaultView||f.parentWindow:window;if(g){if(g=b,b=(b=c.relatedTarget||c.toElement)?Bb(b):null,null!==b){var h=Na(b);if(b!==h||5!==b.tag&&6!==b.tag)b=null}}else g=null;if(g===b)return null;if("mouseout"===a||"mouseover"===a){var m=ec;var n=fc.mouseLeave;
|
|
var l=fc.mouseEnter;var k="mouse"}else if("pointerout"===a||"pointerover"===a)m=hi,n=fc.pointerLeave,l=fc.pointerEnter,k="pointer";a=null==g?f:Pa(g);f=null==b?f:Pa(b);n=m.getPooled(n,g,c,d);n.type=k+"leave";n.target=a;n.relatedTarget=f;c=m.getPooled(l,b,c,d);c.type=k+"enter";c.target=f;c.relatedTarget=a;d=g;k=b;if(d&&k)a:{m=d;l=k;g=0;for(a=m;a;a=pa(a))g++;a=0;for(b=l;b;b=pa(b))a++;for(;0<g-a;)m=pa(m),g--;for(;0<a-g;)l=pa(l),a--;for(;g--;){if(m===l||m===l.alternate)break a;m=pa(m);l=pa(l)}m=null}else m=
|
|
null;l=m;for(m=[];d&&d!==l;){g=d.alternate;if(null!==g&&g===l)break;m.push(d);d=pa(d)}for(d=[];k&&k!==l;){g=k.alternate;if(null!==g&&g===l)break;d.push(k);k=pa(k)}for(k=0;k<m.length;k++)be(m[k],"bubbled",n);for(k=d.length;0<k--;)be(d[k],"captured",c);return 0===(e&64)?[n]:[n,c]}},Qa="function"===typeof Object.is?Object.is:Zi,$i=Object.prototype.hasOwnProperty,Zj=wa&&"documentMode"in document&&11>=document.documentMode,Eg={select:{phasedRegistrationNames:{bubbled:"onSelect",captured:"onSelectCapture"},
|
|
dependencies:"blur contextmenu dragend focus keydown keyup mousedown mouseup selectionchange".split(" ")}},nb=null,he=null,Pb=null,ge=!1,ak={eventTypes:Eg,extractEvents:function(a,b,c,d,e,f){e=f||(d.window===d?d.document:9===d.nodeType?d:d.ownerDocument);if(!(f=!e)){a:{e=Jd(e);f=rd.onSelect;for(var g=0;g<f.length;g++)if(!e.has(f[g])){e=!1;break a}e=!0}f=!e}if(f)return null;e=b?Pa(b):window;switch(a){case "focus":if(yg(e)||"true"===e.contentEditable)nb=e,he=b,Pb=null;break;case "blur":Pb=he=nb=null;
|
|
break;case "mousedown":ge=!0;break;case "contextmenu":case "mouseup":case "dragend":return ge=!1,Dg(c,d);case "selectionchange":if(Zj)break;case "keydown":case "keyup":return Dg(c,d)}return null}},bk=R.extend({animationName:null,elapsedTime:null,pseudoElement:null}),ck=R.extend({clipboardData:function(a){return"clipboardData"in a?a.clipboardData:window.clipboardData}}),dk=dc.extend({relatedTarget:null}),ek={Esc:"Escape",Spacebar:" ",Left:"ArrowLeft",Up:"ArrowUp",Right:"ArrowRight",Down:"ArrowDown",
|
|
Del:"Delete",Win:"OS",Menu:"ContextMenu",Apps:"ContextMenu",Scroll:"ScrollLock",MozPrintableKey:"Unidentified"},fk={8:"Backspace",9:"Tab",12:"Clear",13:"Enter",16:"Shift",17:"Control",18:"Alt",19:"Pause",20:"CapsLock",27:"Escape",32:" ",33:"PageUp",34:"PageDown",35:"End",36:"Home",37:"ArrowLeft",38:"ArrowUp",39:"ArrowRight",40:"ArrowDown",45:"Insert",46:"Delete",112:"F1",113:"F2",114:"F3",115:"F4",116:"F5",117:"F6",118:"F7",119:"F8",120:"F9",121:"F10",122:"F11",123:"F12",144:"NumLock",145:"ScrollLock",
|
|
224:"Meta"},gk=dc.extend({key:function(a){if(a.key){var b=ek[a.key]||a.key;if("Unidentified"!==b)return b}return"keypress"===a.type?(a=Ac(a),13===a?"Enter":String.fromCharCode(a)):"keydown"===a.type||"keyup"===a.type?fk[a.keyCode]||"Unidentified":""},location:null,ctrlKey:null,shiftKey:null,altKey:null,metaKey:null,repeat:null,locale:null,getModifierState:fe,charCode:function(a){return"keypress"===a.type?Ac(a):0},keyCode:function(a){return"keydown"===a.type||"keyup"===a.type?a.keyCode:0},which:function(a){return"keypress"===
|
|
a.type?Ac(a):"keydown"===a.type||"keyup"===a.type?a.keyCode:0}}),hk=ec.extend({dataTransfer:null}),ik=dc.extend({touches:null,targetTouches:null,changedTouches:null,altKey:null,metaKey:null,ctrlKey:null,shiftKey:null,getModifierState:fe}),jk=R.extend({propertyName:null,elapsedTime:null,pseudoElement:null}),kk=ec.extend({deltaX:function(a){return"deltaX"in a?a.deltaX:"wheelDeltaX"in a?-a.wheelDeltaX:0},deltaY:function(a){return"deltaY"in a?a.deltaY:"wheelDeltaY"in a?-a.wheelDeltaY:"wheelDelta"in a?
|
|
-a.wheelDelta:0},deltaZ:null,deltaMode:null}),lk={eventTypes:dg,extractEvents:function(a,b,c,d,e){e=cg.get(a);if(!e)return null;switch(a){case "keypress":if(0===Ac(c))return null;case "keydown":case "keyup":a=gk;break;case "blur":case "focus":a=dk;break;case "click":if(2===c.button)return null;case "auxclick":case "dblclick":case "mousedown":case "mousemove":case "mouseup":case "mouseout":case "mouseover":case "contextmenu":a=ec;break;case "drag":case "dragend":case "dragenter":case "dragexit":case "dragleave":case "dragover":case "dragstart":case "drop":a=
|
|
hk;break;case "touchcancel":case "touchend":case "touchmove":case "touchstart":a=ik;break;case $h:case ai:case bi:a=bk;break;case ci:a=jk;break;case "scroll":a=dc;break;case "wheel":a=kk;break;case "copy":case "cut":case "paste":a=ck;break;case "gotpointercapture":case "lostpointercapture":case "pointercancel":case "pointerdown":case "pointermove":case "pointerout":case "pointerover":case "pointerup":a=hi;break;default:a=R}b=a.getPooled(e,b,c,d);lb(b);return b}};(function(a){if(ic)throw Error(k(101));
|
|
ic=Array.prototype.slice.call(a);nf()})("ResponderEventPlugin SimpleEventPlugin EnterLeaveEventPlugin ChangeEventPlugin SelectEventPlugin BeforeInputEventPlugin".split(" "));(function(a,b,c){td=a;rf=b;mf=c})(ae,Hb,Pa);pf({SimpleEventPlugin:lk,EnterLeaveEventPlugin:Yj,ChangeEventPlugin:Xj,SelectEventPlugin:ak,BeforeInputEventPlugin:Wj});var ie=[],ob=-1,Ca={},B={current:Ca},G={current:!1},Ra=Ca,bj=Pd,je=$f,Rg=Lj,aj=Nj,Dc=Oj,Ig=Zh,Jg=ag,Kg=Pj,Lg=Qj,Qg={},yj=Mj,Cj=void 0!==Yh?Yh:function(){},qa=null,
|
|
Ec=null,ke=!1,ii=ff(),Y=1E4>ii?ff:function(){return ff()-ii},Ic={current:null},Hc=null,qb=null,Gc=null,Tg=0,Jc=2,Ga=!1,Vb=da.ReactCurrentBatchConfig,$g=(new ea.Component).refs,Mc={isMounted:function(a){return(a=a._reactInternalFiber)?Na(a)===a:!1},enqueueSetState:function(a,b,c){a=a._reactInternalFiber;var d=ka(),e=Vb.suspense;d=Va(d,a,e);e=Ea(d,e);e.payload=b;void 0!==c&&null!==c&&(e.callback=c);Fa(a,e);Ja(a,d)},enqueueReplaceState:function(a,b,c){a=a._reactInternalFiber;var d=ka(),e=Vb.suspense;
|
|
d=Va(d,a,e);e=Ea(d,e);e.tag=1;e.payload=b;void 0!==c&&null!==c&&(e.callback=c);Fa(a,e);Ja(a,d)},enqueueForceUpdate:function(a,b){a=a._reactInternalFiber;var c=ka(),d=Vb.suspense;c=Va(c,a,d);d=Ea(c,d);d.tag=Jc;void 0!==b&&null!==b&&(d.callback=b);Fa(a,d);Ja(a,c)}},Qc=Array.isArray,wb=ah(!0),Fe=ah(!1),Sb={},ja={current:Sb},Ub={current:Sb},Tb={current:Sb},D={current:0},Sc=da.ReactCurrentDispatcher,X=da.ReactCurrentBatchConfig,Ia=0,z=null,K=null,J=null,Uc=!1,Tc={readContext:W,useCallback:S,useContext:S,
|
|
useEffect:S,useImperativeHandle:S,useLayoutEffect:S,useMemo:S,useReducer:S,useRef:S,useState:S,useDebugValue:S,useResponder:S,useDeferredValue:S,useTransition:S},dj={readContext:W,useCallback:ih,useContext:W,useEffect:eh,useImperativeHandle:function(a,b,c){c=null!==c&&void 0!==c?c.concat([a]):null;return ze(4,2,gh.bind(null,b,a),c)},useLayoutEffect:function(a,b){return ze(4,2,a,b)},useMemo:function(a,b){var c=ub();b=void 0===b?null:b;a=a();c.memoizedState=[a,b];return a},useReducer:function(a,b,c){var d=
|
|
ub();b=void 0!==c?c(b):b;d.memoizedState=d.baseState=b;a=d.queue={pending:null,dispatch:null,lastRenderedReducer:a,lastRenderedState:b};a=a.dispatch=ch.bind(null,z,a);return[d.memoizedState,a]},useRef:function(a){var b=ub();a={current:a};return b.memoizedState=a},useState:xe,useDebugValue:Be,useResponder:ue,useDeferredValue:function(a,b){var c=xe(a),d=c[0],e=c[1];eh(function(){var c=X.suspense;X.suspense=void 0===b?null:b;try{e(a)}finally{X.suspense=c}},[a,b]);return d},useTransition:function(a){var b=
|
|
xe(!1),c=b[0];b=b[1];return[ih(Ce.bind(null,b,a),[b,a]),c]}},ej={readContext:W,useCallback:Yc,useContext:W,useEffect:Xc,useImperativeHandle:hh,useLayoutEffect:fh,useMemo:jh,useReducer:Vc,useRef:dh,useState:function(a){return Vc(Ua)},useDebugValue:Be,useResponder:ue,useDeferredValue:function(a,b){var c=Vc(Ua),d=c[0],e=c[1];Xc(function(){var c=X.suspense;X.suspense=void 0===b?null:b;try{e(a)}finally{X.suspense=c}},[a,b]);return d},useTransition:function(a){var b=Vc(Ua),c=b[0];b=b[1];return[Yc(Ce.bind(null,
|
|
b,a),[b,a]),c]}},fj={readContext:W,useCallback:Yc,useContext:W,useEffect:Xc,useImperativeHandle:hh,useLayoutEffect:fh,useMemo:jh,useReducer:Wc,useRef:dh,useState:function(a){return Wc(Ua)},useDebugValue:Be,useResponder:ue,useDeferredValue:function(a,b){var c=Wc(Ua),d=c[0],e=c[1];Xc(function(){var c=X.suspense;X.suspense=void 0===b?null:b;try{e(a)}finally{X.suspense=c}},[a,b]);return d},useTransition:function(a){var b=Wc(Ua),c=b[0];b=b[1];return[Yc(Ce.bind(null,b,a),[b,a]),c]}},ra=null,Ka=null,Wa=
|
|
!1,gj=da.ReactCurrentOwner,ia=!1,Je={dehydrated:null,retryTime:0};var jj=function(a,b,c,d){for(c=b.child;null!==c;){if(5===c.tag||6===c.tag)a.appendChild(c.stateNode);else if(4!==c.tag&&null!==c.child){c.child.return=c;c=c.child;continue}if(c===b)break;for(;null===c.sibling;){if(null===c.return||c.return===b)return;c=c.return}c.sibling.return=c.return;c=c.sibling}};var wh=function(a){};var ij=function(a,b,c,d,e){var f=a.memoizedProps;if(f!==d){var g=b.stateNode;Ta(ja.current);a=null;switch(c){case "input":f=
|
|
Cd(g,f);d=Cd(g,d);a=[];break;case "option":f=Fd(g,f);d=Fd(g,d);a=[];break;case "select":f=M({},f,{value:void 0});d=M({},d,{value:void 0});a=[];break;case "textarea":f=Gd(g,f);d=Gd(g,d);a=[];break;default:"function"!==typeof f.onClick&&"function"===typeof d.onClick&&(g.onclick=uc)}Ud(c,d);var h,m;c=null;for(h in f)if(!d.hasOwnProperty(h)&&f.hasOwnProperty(h)&&null!=f[h])if("style"===h)for(m in g=f[h],g)g.hasOwnProperty(m)&&(c||(c={}),c[m]="");else"dangerouslySetInnerHTML"!==h&&"children"!==h&&"suppressContentEditableWarning"!==
|
|
h&&"suppressHydrationWarning"!==h&&"autoFocus"!==h&&(db.hasOwnProperty(h)?a||(a=[]):(a=a||[]).push(h,null));for(h in d){var k=d[h];g=null!=f?f[h]:void 0;if(d.hasOwnProperty(h)&&k!==g&&(null!=k||null!=g))if("style"===h)if(g){for(m in g)!g.hasOwnProperty(m)||k&&k.hasOwnProperty(m)||(c||(c={}),c[m]="");for(m in k)k.hasOwnProperty(m)&&g[m]!==k[m]&&(c||(c={}),c[m]=k[m])}else c||(a||(a=[]),a.push(h,c)),c=k;else"dangerouslySetInnerHTML"===h?(k=k?k.__html:void 0,g=g?g.__html:void 0,null!=k&&g!==k&&(a=a||
|
|
[]).push(h,k)):"children"===h?g===k||"string"!==typeof k&&"number"!==typeof k||(a=a||[]).push(h,""+k):"suppressContentEditableWarning"!==h&&"suppressHydrationWarning"!==h&&(db.hasOwnProperty(h)?(null!=k&&oa(e,h),a||g===k||(a=[])):(a=a||[]).push(h,k))}c&&(a=a||[]).push("style",c);e=a;if(b.updateQueue=e)b.effectTag|=4}};var kj=function(a,b,c,d){c!==d&&(b.effectTag|=4)};var pj="function"===typeof WeakSet?WeakSet:Set,wj="function"===typeof WeakMap?WeakMap:Map,sj=Math.ceil,gd=da.ReactCurrentDispatcher,
|
|
Uh=da.ReactCurrentOwner,H=0,Ye=8,ca=16,ma=32,Xa=0,hd=1,Oh=2,ad=3,bd=4,Xe=5,p=H,U=null,t=null,P=0,F=Xa,id=null,ta=1073741823,Yb=1073741823,kd=null,Xb=0,jd=!1,Re=0,Ph=500,l=null,cd=!1,Se=null,La=null,ld=!1,Zb=null,$b=90,bb=null,ac=0,af=null,dd=0,Ja=function(a,b){if(50<ac)throw ac=0,af=null,Error(k(185));a=ed(a,b);if(null!==a){var c=Cc();1073741823===b?(p&Ye)!==H&&(p&(ca|ma))===H?Te(a):(V(a),p===H&&ha()):V(a);(p&4)===H||98!==c&&99!==c||(null===bb?bb=new Map([[a,b]]):(c=bb.get(a),(void 0===c||c>b)&&bb.set(a,
|
|
b)))}};var zj=function(a,b,c){var d=b.expirationTime;if(null!==a){var e=b.pendingProps;if(a.memoizedProps!==e||G.current)ia=!0;else{if(d<c){ia=!1;switch(b.tag){case 3:sh(b);Ee();break;case 5:bh(b);if(b.mode&4&&1!==c&&e.hidden)return b.expirationTime=b.childExpirationTime=1,null;break;case 1:N(b.type)&&Bc(b);break;case 4:se(b,b.stateNode.containerInfo);break;case 10:d=b.memoizedProps.value;e=b.type._context;y(Ic,e._currentValue);e._currentValue=d;break;case 13:if(null!==b.memoizedState){d=b.child.childExpirationTime;
|
|
if(0!==d&&d>=c)return th(a,b,c);y(D,D.current&1);b=sa(a,b,c);return null!==b?b.sibling:null}y(D,D.current&1);break;case 19:d=b.childExpirationTime>=c;if(0!==(a.effectTag&64)){if(d)return vh(a,b,c);b.effectTag|=64}e=b.memoizedState;null!==e&&(e.rendering=null,e.tail=null);y(D,D.current);if(!d)return null}return sa(a,b,c)}ia=!1}}else ia=!1;b.expirationTime=0;switch(b.tag){case 2:d=b.type;null!==a&&(a.alternate=null,b.alternate=null,b.effectTag|=2);a=b.pendingProps;e=pb(b,B.current);rb(b,c);e=we(null,
|
|
b,d,a,e,c);b.effectTag|=1;if("object"===typeof e&&null!==e&&"function"===typeof e.render&&void 0===e.$$typeof){b.tag=1;b.memoizedState=null;b.updateQueue=null;if(N(d)){var f=!0;Bc(b)}else f=!1;b.memoizedState=null!==e.state&&void 0!==e.state?e.state:null;ne(b);var g=d.getDerivedStateFromProps;"function"===typeof g&&Lc(b,d,g,a);e.updater=Mc;b.stateNode=e;e._reactInternalFiber=b;pe(b,d,a,c);b=Ie(null,b,d,!0,f,c)}else b.tag=0,T(null,b,e,c),b=b.child;return b;case 16:a:{e=b.elementType;null!==a&&(a.alternate=
|
|
null,b.alternate=null,b.effectTag|=2);a=b.pendingProps;ri(e);if(1!==e._status)throw e._result;e=e._result;b.type=e;f=b.tag=Gj(e);a=aa(e,a);switch(f){case 0:b=He(null,b,e,a,c);break a;case 1:b=rh(null,b,e,a,c);break a;case 11:b=nh(null,b,e,a,c);break a;case 14:b=oh(null,b,e,aa(e.type,a),d,c);break a}throw Error(k(306,e,""));}return b;case 0:return d=b.type,e=b.pendingProps,e=b.elementType===d?e:aa(d,e),He(a,b,d,e,c);case 1:return d=b.type,e=b.pendingProps,e=b.elementType===d?e:aa(d,e),rh(a,b,d,e,c);
|
|
case 3:sh(b);d=b.updateQueue;if(null===a||null===d)throw Error(k(282));d=b.pendingProps;e=b.memoizedState;e=null!==e?e.element:null;oe(a,b);Qb(b,d,null,c);d=b.memoizedState.element;if(d===e)Ee(),b=sa(a,b,c);else{if(e=b.stateNode.hydrate)Ka=kb(b.stateNode.containerInfo.firstChild),ra=b,e=Wa=!0;if(e)for(c=Fe(b,null,d,c),b.child=c;c;)c.effectTag=c.effectTag&-3|1024,c=c.sibling;else T(a,b,d,c),Ee();b=b.child}return b;case 5:return bh(b),null===a&&De(b),d=b.type,e=b.pendingProps,f=null!==a?a.memoizedProps:
|
|
null,g=e.children,Yd(d,e)?g=null:null!==f&&Yd(d,f)&&(b.effectTag|=16),qh(a,b),b.mode&4&&1!==c&&e.hidden?(b.expirationTime=b.childExpirationTime=1,b=null):(T(a,b,g,c),b=b.child),b;case 6:return null===a&&De(b),null;case 13:return th(a,b,c);case 4:return se(b,b.stateNode.containerInfo),d=b.pendingProps,null===a?b.child=wb(b,null,d,c):T(a,b,d,c),b.child;case 11:return d=b.type,e=b.pendingProps,e=b.elementType===d?e:aa(d,e),nh(a,b,d,e,c);case 7:return T(a,b,b.pendingProps,c),b.child;case 8:return T(a,
|
|
b,b.pendingProps.children,c),b.child;case 12:return T(a,b,b.pendingProps.children,c),b.child;case 10:a:{d=b.type._context;e=b.pendingProps;g=b.memoizedProps;f=e.value;var h=b.type._context;y(Ic,h._currentValue);h._currentValue=f;if(null!==g)if(h=g.value,f=Qa(h,f)?0:("function"===typeof d._calculateChangedBits?d._calculateChangedBits(h,f):1073741823)|0,0===f){if(g.children===e.children&&!G.current){b=sa(a,b,c);break a}}else for(h=b.child,null!==h&&(h.return=b);null!==h;){var m=h.dependencies;if(null!==
|
|
m){g=h.child;for(var l=m.firstContext;null!==l;){if(l.context===d&&0!==(l.observedBits&f)){1===h.tag&&(l=Ea(c,null),l.tag=Jc,Fa(h,l));h.expirationTime<c&&(h.expirationTime=c);l=h.alternate;null!==l&&l.expirationTime<c&&(l.expirationTime=c);Sg(h.return,c);m.expirationTime<c&&(m.expirationTime=c);break}l=l.next}}else g=10===h.tag?h.type===b.type?null:h.child:h.child;if(null!==g)g.return=h;else for(g=h;null!==g;){if(g===b){g=null;break}h=g.sibling;if(null!==h){h.return=g.return;g=h;break}g=g.return}h=
|
|
g}T(a,b,e.children,c);b=b.child}return b;case 9:return e=b.type,f=b.pendingProps,d=f.children,rb(b,c),e=W(e,f.unstable_observedBits),d=d(e),b.effectTag|=1,T(a,b,d,c),b.child;case 14:return e=b.type,f=aa(e,b.pendingProps),f=aa(e.type,f),oh(a,b,e,f,d,c);case 15:return ph(a,b,b.type,b.pendingProps,d,c);case 17:return d=b.type,e=b.pendingProps,e=b.elementType===d?e:aa(d,e),null!==a&&(a.alternate=null,b.alternate=null,b.effectTag|=2),b.tag=1,N(d)?(a=!0,Bc(b)):a=!1,rb(b,c),Yg(b,d,e),pe(b,d,e,c),Ie(null,
|
|
b,d,!0,a,c);case 19:return vh(a,b,c)}throw Error(k(156,b.tag));};var bf=null,Ne=null,la=function(a,b,c,d){return new Fj(a,b,c,d)};ef.prototype.render=function(a){md(a,this._internalRoot,null,null)};ef.prototype.unmount=function(){var a=this._internalRoot,b=a.containerInfo;md(null,a,null,function(){b[Lb]=null})};var Di=function(a){if(13===a.tag){var b=Fc(ka(),150,100);Ja(a,b);df(a,b)}};var Yf=function(a){13===a.tag&&(Ja(a,3),df(a,3))};var Bi=function(a){if(13===a.tag){var b=ka();b=Va(b,a,null);Ja(a,
|
|
b);df(a,b)}};sd=function(a,b,c){switch(b){case "input":Dd(a,c);b=c.name;if("radio"===c.type&&null!=b){for(c=a;c.parentNode;)c=c.parentNode;c=c.querySelectorAll("input[name="+JSON.stringify(""+b)+'][type="radio"]');for(b=0;b<c.length;b++){var d=c[b];if(d!==a&&d.form===a.form){var e=ae(d);if(!e)throw Error(k(90));Gf(d);Dd(d,e)}}}break;case "textarea":Lf(a,c);break;case "select":b=c.value,null!=b&&hb(a,!!c.multiple,b,!1)}};(function(a,b,c,d){ee=a;eg=b;vd=c;vf=d})(Qh,function(a,b,c,d,e){var f=p;p|=4;
|
|
try{return Da(98,a.bind(null,b,c,d,e))}finally{p=f,p===H&&ha()}},function(){(p&(1|ca|ma))===H&&(uj(),xb())},function(a,b){var c=p;p|=2;try{return a(b)}finally{p=c,p===H&&ha()}});var mk={Events:[Hb,Pa,ae,pf,qd,lb,function(a){Kd(a,Ki)},sf,tf,sc,pc,xb,{current:!1}]};(function(a){var b=a.findFiberByHostInstance;return Ej(M({},a,{overrideHookState:null,overrideProps:null,setSuspenseHandler:null,scheduleUpdate:null,currentDispatcherRef:da.ReactCurrentDispatcher,findHostInstanceByFiber:function(a){a=Sf(a);
|
|
return null===a?null:a.stateNode},findFiberByHostInstance:function(a){return b?b(a):null},findHostInstancesForRefresh:null,scheduleRefresh:null,scheduleRoot:null,setRefreshHandler:null,getCurrentFiber:null}))})({findFiberByHostInstance:Bb,bundleType:0,version:"16.13.1",rendererPackageName:"react-dom"});I.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED=mk;I.createPortal=Xh;I.findDOMNode=function(a){if(null==a)return null;if(1===a.nodeType)return a;var b=a._reactInternalFiber;if(void 0===
|
|
b){if("function"===typeof a.render)throw Error(k(188));throw Error(k(268,Object.keys(a)));}a=Sf(b);a=null===a?null:a.stateNode;return a};I.flushSync=function(a,b){if((p&(ca|ma))!==H)throw Error(k(187));var c=p;p|=1;try{return Da(99,a.bind(null,b))}finally{p=c,ha()}};I.hydrate=function(a,b,c){if(!bc(b))throw Error(k(200));return nd(null,a,b,!0,c)};I.render=function(a,b,c){if(!bc(b))throw Error(k(200));return nd(null,a,b,!1,c)};I.unmountComponentAtNode=function(a){if(!bc(a))throw Error(k(40));return a._reactRootContainer?
|
|
(Rh(function(){nd(null,null,a,!1,function(){a._reactRootContainer=null;a[Lb]=null})}),!0):!1};I.unstable_batchedUpdates=Qh;I.unstable_createPortal=function(a,b){return Xh(a,b,2<arguments.length&&void 0!==arguments[2]?arguments[2]:null)};I.unstable_renderSubtreeIntoContainer=function(a,b,c,d){if(!bc(c))throw Error(k(200));if(null==a||void 0===a._reactInternalFiber)throw Error(k(38));return nd(a,b,c,!1,d)};I.version="16.13.1"});
|
|
</script>
|
|
<script>const e = React.createElement;
|
|
|
|
function pathToString(path) {
|
|
if (path[0] === '/') {
|
|
return '/' + path.slice(1).join('/');
|
|
} else {
|
|
return path.join('/');
|
|
}
|
|
}
|
|
|
|
function findCommonPath(files) {
|
|
if (!files || !files.length) {
|
|
return [];
|
|
}
|
|
|
|
function isPrefix(arr, prefix) {
|
|
if (arr.length < prefix.length) {
|
|
return false;
|
|
}
|
|
for (let i = prefix.length - 1; i >= 0; --i) {
|
|
if (arr[i] !== prefix[i]) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
let commonPath = files[0].path.slice(0, -1);
|
|
while (commonPath.length) {
|
|
if (files.every(file => isPrefix(file.path, commonPath))) {
|
|
break;
|
|
}
|
|
commonPath.pop();
|
|
}
|
|
return commonPath;
|
|
}
|
|
|
|
function findFolders(files) {
|
|
if (!files || !files.length) {
|
|
return [];
|
|
}
|
|
|
|
let folders = files.filter(file => file.path.length > 1).map(file => file.path[0]);
|
|
folders = [...new Set(folders)]; // unique
|
|
folders.sort();
|
|
|
|
folders = folders.map(folder => {
|
|
let filesInFolder = files
|
|
.filter(file => file.path[0] === folder)
|
|
.map(file => ({
|
|
...file,
|
|
path: file.path.slice(1),
|
|
parent: [...file.parent, file.path[0]],
|
|
}));
|
|
|
|
const children = findFolders(filesInFolder); // recursion
|
|
|
|
return {
|
|
is_folder: true,
|
|
path: [folder],
|
|
parent: files[0].parent,
|
|
children,
|
|
covered: children.reduce((sum, file) => sum + file.covered, 0),
|
|
coverable: children.reduce((sum, file) => sum + file.coverable, 0),
|
|
prevRun: {
|
|
covered: children.reduce((sum, file) => sum + file.prevRun.covered, 0),
|
|
coverable: children.reduce((sum, file) => sum + file.prevRun.coverable, 0),
|
|
}
|
|
};
|
|
});
|
|
|
|
return [
|
|
...folders,
|
|
...files.filter(file => file.path.length === 1),
|
|
];
|
|
}
|
|
|
|
class App extends React.Component {
|
|
constructor(...args) {
|
|
super(...args);
|
|
|
|
this.state = {
|
|
current: [],
|
|
};
|
|
}
|
|
|
|
componentDidMount() {
|
|
this.updateStateFromLocation();
|
|
window.addEventListener("hashchange", () => this.updateStateFromLocation(), false);
|
|
}
|
|
|
|
updateStateFromLocation() {
|
|
if (window.location.hash.length > 1) {
|
|
const current = window.location.hash.substr(1).split('/');
|
|
this.setState({current});
|
|
} else {
|
|
this.setState({current: []});
|
|
}
|
|
}
|
|
|
|
getCurrentPath() {
|
|
let file = this.props.root;
|
|
let path = [file];
|
|
for (let p of this.state.current) {
|
|
file = file.children.find(file => file.path[0] === p);
|
|
if (!file) {
|
|
return path;
|
|
}
|
|
path.push(file);
|
|
}
|
|
return path;
|
|
}
|
|
|
|
render() {
|
|
const path = this.getCurrentPath();
|
|
const file = path[path.length - 1];
|
|
|
|
let w = null;
|
|
if (file.is_folder) {
|
|
w = e(FilesList, {
|
|
folder: file,
|
|
onSelectFile: this.selectFile.bind(this),
|
|
onBack: path.length > 1 ? this.back.bind(this) : null,
|
|
});
|
|
} else {
|
|
w = e(DisplayFile, {
|
|
file,
|
|
onBack: this.back.bind(this),
|
|
});
|
|
}
|
|
|
|
return e('div', {className: 'app'}, w);
|
|
}
|
|
|
|
selectFile(file) {
|
|
this.setState(({current}) => {
|
|
return {current: [...current, file.path[0]]};
|
|
}, () => this.updateHash());
|
|
}
|
|
|
|
back(file) {
|
|
this.setState(({current}) => {
|
|
return {current: current.slice(0, current.length - 1)};
|
|
}, () => this.updateHash());
|
|
}
|
|
|
|
updateHash() {
|
|
if (!this.state.current || !this.state.current.length) {
|
|
window.location = '#';
|
|
} else {
|
|
window.location = '#' + this.state.current.join('/');
|
|
}
|
|
}
|
|
}
|
|
|
|
function FilesList({folder, onSelectFile, onBack}) {
|
|
let files = folder.children;
|
|
return e('div', {className: 'display-folder'},
|
|
e(FileHeader, {file: folder, onBack}),
|
|
e('table', {className: 'files-list'},
|
|
e('thead', {className: 'files-list__head'},
|
|
e('tr', null,
|
|
e('th', null, "Path"),
|
|
e('th', null, "Coverage")
|
|
)
|
|
),
|
|
e('tbody', {className: 'files-list__body'},
|
|
files.map(file => e(File, {file, onClick: onSelectFile}))
|
|
)
|
|
)
|
|
);
|
|
}
|
|
|
|
function File({file, onClick}) {
|
|
const coverage = file.coverable ? file.covered / file.coverable * 100 : -1;
|
|
const coverageDelta = file.prevRun &&
|
|
(file.covered / file.coverable * 100 - file.prevRun.covered / file.prevRun.coverable * 100);
|
|
|
|
return e('tr', {
|
|
className: 'files-list__file'
|
|
+ (coverage >= 0 && coverage < 50 ? ' files-list__file_low': '')
|
|
+ (coverage >= 50 && coverage < 80 ? ' files-list__file_medium': '')
|
|
+ (coverage >= 80 ? ' files-list__file_high': '')
|
|
+ (file.is_folder ? ' files-list__file_folder': ''),
|
|
onClick: () => onClick(file),
|
|
},
|
|
e('td', null, pathToString(file.path)),
|
|
e('td', null,
|
|
file.covered + ' / ' + file.coverable +
|
|
(coverage >= 0 ? ' (' + coverage.toFixed(2) + '%)' : ''),
|
|
e('span', {title: 'Change from the previous run'},
|
|
(coverageDelta ? ` (${coverageDelta > 0 ? '+' : ''}${coverageDelta.toFixed(2)}%)` : ''))
|
|
)
|
|
);
|
|
}
|
|
|
|
function DisplayFile({file, onBack}) {
|
|
return e('div', {className: 'display-file'},
|
|
e(FileHeader, {file, onBack}),
|
|
e(FileContent, {file})
|
|
);
|
|
}
|
|
|
|
function FileHeader({file, onBack}) {
|
|
const coverage = file.covered / file.coverable * 100;
|
|
const coverageDelta = file.prevRun && (coverage - file.prevRun.covered / file.prevRun.coverable * 100);
|
|
|
|
return e('div', {className: 'file-header'},
|
|
onBack ? e('a', {className: 'file-header__back', onClick: onBack}, 'Back') : null,
|
|
e('div', {className: 'file-header__name'}, pathToString([...file.parent, ...file.path])),
|
|
e('div', {className: 'file-header__stat'},
|
|
'Covered: ' + file.covered + ' of ' + file.coverable +
|
|
(file.coverable ? ' (' + coverage.toFixed(2) + '%)' : ''),
|
|
e('span', {title: 'Change from the previous run'},
|
|
(coverageDelta ? ` (${coverageDelta > 0 ? '+' : ''}${coverageDelta.toFixed(2)}%)` : ''))
|
|
)
|
|
);
|
|
}
|
|
|
|
function FileContent({file}) {
|
|
return e('div', {className: 'file-content'},
|
|
file.content.split(/\r?\n/).map((line, index) => {
|
|
const trace = file.traces.find(trace => trace.line === index + 1);
|
|
const covered = trace && trace.stats.Line;
|
|
const uncovered = trace && !trace.stats.Line;
|
|
return e('pre', {
|
|
className: 'code-line'
|
|
+ (covered ? ' code-line_covered' : '')
|
|
+ (uncovered ? ' code-line_uncovered' : ''),
|
|
title: trace ? JSON.stringify(trace.stats, null, 2) : null,
|
|
}, line);
|
|
})
|
|
);
|
|
}
|
|
|
|
(function(){
|
|
const commonPath = findCommonPath(data.files);
|
|
const prevFilesMap = new Map();
|
|
|
|
previousData && previousData.files.forEach((file) => {
|
|
const path = file.path.slice(commonPath.length).join('/');
|
|
prevFilesMap.set(path, file);
|
|
});
|
|
|
|
const files = data.files.map((file) => {
|
|
const path = file.path.slice(commonPath.length);
|
|
const { covered = 0, coverable = 0 } = prevFilesMap.get(path.join('/')) || {};
|
|
return {
|
|
...file,
|
|
path,
|
|
parent: commonPath,
|
|
prevRun: { covered, coverable },
|
|
};
|
|
});
|
|
|
|
const children = findFolders(files);
|
|
|
|
const root = {
|
|
is_folder: true,
|
|
children,
|
|
path: commonPath,
|
|
parent: [],
|
|
covered: children.reduce((sum, file) => sum + file.covered, 0),
|
|
coverable: children.reduce((sum, file) => sum + file.coverable, 0),
|
|
prevRun: {
|
|
covered: children.reduce((sum, file) => sum + file.prevRun.covered, 0),
|
|
coverable: children.reduce((sum, file) => sum + file.prevRun.coverable, 0),
|
|
}
|
|
};
|
|
|
|
ReactDOM.render(e(App, {root, prevFilesMap}), document.getElementById('root'));
|
|
}());
|
|
</script>
|
|
</body>
|
|
</html> |