absolutely slayed testing with iced task
[?]
May 19, 2025, 6:55 AM
WGID4LS4EISIOXB5Y5SOFGEF5PLBJSCPFCETH2CGRTFN3NC4WGJQCDependencies
- [2]
6YZAVBWUInitial commit - [3]
KLR5FRIBadd fs state read/write of repos - [4]
WT3GA27Padd cursor with selection - [5]
UB2ITZJSrefresh changed files on FS changes - [6]
YBJRDOTCmake all repo actions async - [7]
2VUX5BTDload identity - [8]
A5YBC77Vrecord! - [9]
Z2CJPWZEfocus record message text_editor on spawn - [10]
AMPZ2BXKshow changed files diffs (only Edit atm) - [11]
V55EAIWQadd src file LRU cache - [12]
6SW7UVSHupdate iced version - [13]
B4RMW5AEadd syntax highlighter to untracked files contents - [14]
ZVI4AWERwoot contents_diff - [15]
OQ6HSAWHshow record log - [16]
NWJD6VM6mv libflowers libflorescence - [17]
4ELJZGRJload and store all change diffs at once - [18]
HC7ROIBCmove main diffs state out of cursor - [19]
CALXOZXAflatten crates dir - [20]
L6KSEFQImove cursor related stuff into its module - [21]
BFN2VHZSrefactor file stuff into sub-mod - [22]
3SYSJKYLadd app icon - [23]
23SFYK4Qbig view refactor into a new crate - [24]
MYGIBRRHwip custom theme - [25]
IQDCHWCPload a pijul repo - [26]
SWWE2R6Mdisplay basic repo stuff - [27]
D7A7MSIHallow to defer or abandon record, add buttons - [28]
HOJZI52Yrename flowers_ui to inflorescence - [29]
QMAUTRB6refactor diff - [30]
XSZZB47Urefactor stuff into lib - [31]
JE44NYHMdisplay log files diffs - [32]
AHWWRC73navigate log entries
Change contents
- file addition: task.rs[19.364]
//! A drop-in replacement for [`iced::Task`] that allows for easy evaluation for//! testing.mod wrappers;pub use wrappers::*;#[cfg(not(test))]pub use iced::Task;#[cfg(test)]pub struct Task<T> {stream: Option<iced::futures::stream::BoxStream<'static, T>>,}#[cfg(test)]pub async fn await_next_result<T>(tasks: &mut Task<T>) -> Option<T> {let Task { stream } = tasks;if let Some(stream) = stream.as_mut() {if let Some(msg) = iced::futures::stream::StreamExt::next(stream).await{return Some(msg);}}None}#[cfg(test)]impl<T> Task<T> {pub fn none() -> Self {Self { stream: None }}pub fn done(value: T) -> Task<T>whereT: Send + 'static,{Self::future(iced::futures::future::ready(value))}pub fn perform<A>(future: impl Future<Output = A> + Send + 'static,f: impl FnOnce(A) -> T + Send + 'static,) -> SelfwhereT: Send + 'static,A: Send + 'static,{Self::future(iced::futures::FutureExt::map(future, f))}pub fn run<A>(stream: impl iced::futures::stream::Stream<Item = A> + Send + 'static,f: impl Fn(A) -> T + Send + 'static,) -> SelfwhereT: 'static,{Self::stream(iced::futures::stream::StreamExt::map(stream, f))}pub fn batch(tasks: impl IntoIterator<Item = Self>) -> SelfwhereT: 'static,{let mut select_all = iced::futures::stream::SelectAll::new();for task in tasks.into_iter() {if let Some(stream) = task.stream {select_all.push(stream);}}Self {stream: Some(iced::futures::stream::StreamExt::boxed(select_all)),}}pub fn map<O>(self, mut f: impl FnMut(T) -> O + Send + 'static) -> Task<O>whereT: Send + 'static,O: Send + 'static,{self.then(move |output| Task::done(f(output)))}pub fn then<O>(self,mut f: impl FnMut(T) -> Task<O> + Send + 'static,) -> Task<O>whereT: Send + 'static,O: Send + 'static,{Task {stream: match self.stream {None => None,Some(stream) => Some(iced::futures::stream::StreamExt::boxed(iced::futures::stream::StreamExt::flat_map(stream,move |output| {f(output).stream.unwrap_or_else(|| {iced::futures::stream::StreamExt::boxed(iced::futures::stream::empty(),)})},),)),},}}pub fn future(future: impl Future<Output = T> + Send + 'static) -> SelfwhereT: 'static,{Self::stream(iced::futures::stream::once(future))}pub fn stream(stream: impl iced::futures::stream::Stream<Item = T> + Send + 'static,) -> SelfwhereT: 'static,{Self {stream: Some(iced::futures::stream::StreamExt::boxed(stream)),}}}#[cfg(test)]mod test {use super::*;#[tokio::test]async fn test_run_task() {let mut task = Task::done(123_usize);let result = await_next_result(&mut task).await;assert_eq!(result, Some(123_usize))}} - file addition: task[19.364]
- file addition: wrappers.rs[0.3625]
//! Wrappers for iced tasks to allow testing.use std::path::PathBuf;use super::Task;use iced::window;/// Opens a new window with the given [`Settings`]; producing the [`Id`]/// of the new window on completion.pub fn open_window(settings: window::Settings,) -> (window::Id, Task<window::Id>) {#[cfg(not(test))]let (window_id, task) = window::open(settings);#[cfg(test)]let (window_id, task) = {let _ = settings;let id = window::Id::unique();(id, Task::done(id))};(window_id, task)}/// Changes the icon of the window using an icon from the content of an image/// file at the given path.pub fn window_set_icon<T>(window_id: window::Id, path: PathBuf) -> Task<T>whereT: Send + 'static,{#[cfg(not(test))]let set_icon_task = Task::perform(async move {use iced::advanced::graphics::image::image_rs::ImageFormat;let icon_bytes = tokio::fs::read(path).await.unwrap();window::icon::from_file_data(&icon_bytes, Some(ImageFormat::Png)).unwrap()},|msg| msg,).then(move |icon| window::set_icon(window_id, icon));#[cfg(test)]let set_icon_task = {let _ = (window_id, path);Task::none()};set_icon_task}/// Focuses the next focusable widget.pub fn widget_focus_next<T>() -> Task<T> {#[cfg(not(test))]let task = iced::widget::focus_next();#[cfg(test)]let task = Task::none();task} - edit in inflorescence/src/main.rs at line 1
pub mod task; - replacement in inflorescence/src/main.rs at line 7[10.1220]→[13.26:41](∅→∅),[13.41]→[21.28:42](∅→∅),[21.42]→[18.18:49](∅→∅),[13.41]→[18.18:49](∅→∅),[13.96]→[13.96:140](∅→∅)
use core::str;use std::cmp;use std::collections::HashMap;use std::path::PathBuf;use std::sync::Arc;use task::Task; - replacement in inflorescence/src/main.rs at line 10
use inflorescence_view::{app, theme, Theme};use inflorescence_view::{app, Theme}; - replacement in inflorescence/src/main.rs at line 16
use iced::{widget, window, Element, Subscription, Task};use iced::{window, Element, Subscription}; - edit in inflorescence/src/main.rs at line 21
use tokio::fs; - edit in inflorescence/src/main.rs at line 25
use std::cmp;use std::collections::HashMap;use std::path::PathBuf;use std::sync::Arc; - edit in inflorescence/src/main.rs at line 31
#[cfg(not(test))] - edit in inflorescence/src/main.rs at line 33
use core::str; - edit in inflorescence/src/main.rs at line 36
- replacement in inflorescence/src/main.rs at line 51
.theme(theme).theme(inflorescence_view::theme) - replacement in inflorescence/src/main.rs at line 58
window::open(window::Settings::default());let set_icon_task = Task::perform(async {use iced::advanced::graphics::image::image_rs::ImageFormat;let icon_bytes = fs::read("assets/icon.png").await.unwrap();window::icon::from_file_data(&icon_bytes, Some(ImageFormat::Png)).unwrap()},|msg| msg,).then(move |icon| window::set_icon(window_id, icon));task::open_window(window::Settings::default());let set_icon_task =// TODO: `include_bytes!`?task::window_set_icon(window_id, PathBuf::from("assets/icon.png")); - replacement in inflorescence/src/main.rs at line 256
return widget::focus_next();return task::widget_focus_next(); - edit in inflorescence/src/file.rs at line 7
use crate::task::Task; - edit in inflorescence/src/file.rs at line 16
use iced::Task; - edit in inflorescence/src/cursor.rs at line 6
use crate::task::Task; - edit in inflorescence/src/cursor.rs at line 10
use iced::Task; - edit in inflorescence/Cargo.toml at line 36[6.18435]
[dev-dependencies][dev-dependencies.tokio]workspace = truefeatures = ["macros"] - edit in Cargo.lock at line 5560
"tokio-macros", - edit in Cargo.lock at line 5562
][[package]]name = "tokio-macros"version = "2.5.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"dependencies = ["proc-macro2","quote","syn 2.0.100",