Explain runtime invariants and avoid redundant unwraps
This commit is contained in:
1 parent
34f2f07301
commit
1b51ba83b4
116 files changed
+1643
-515
No files matched your search
@@ -47,7 +47,10 @@ pub fn daemonize() -> Result<(), String> {
|
||||
}
|
||||
|
||||
fn confirm(child: &mut Child) -> Result<(), String> {
|
||||
let stdout = child.stdout.take().unwrap();
|
||||
let stdout = child
|
||||
.stdout
|
||||
.take()
|
||||
.expect("the child was spawned with piped standard output");
|
||||
let (tx, rx) = std::sync::mpsc::sync_channel(1);
|
||||
std::thread::spawn(move || {
|
||||
let mut reader = BufReader::new(stdout);
|
||||
|
||||
@@ -136,7 +136,10 @@ impl Agent {
|
||||
let cancelled = Arc::new(AtomicBool::new(false));
|
||||
|
||||
{
|
||||
let mut active = self.active.lock().unwrap();
|
||||
let mut active = self
|
||||
.active
|
||||
.lock()
|
||||
.expect("authentication session ownership must remain consistent across callbacks");
|
||||
if active.contains_key(&cookie) {
|
||||
return Err(zbus::fdo::Error::Failed("Duplicate request".into()));
|
||||
}
|
||||
@@ -166,7 +169,10 @@ impl Agent {
|
||||
})
|
||||
.await;
|
||||
|
||||
self.active.lock().unwrap().remove(&cookie);
|
||||
self.active
|
||||
.lock()
|
||||
.expect("authentication session ownership must remain consistent across callbacks")
|
||||
.remove(&cookie);
|
||||
match result.map_err(|error| zbus::fdo::Error::Failed(error.to_string()))? {
|
||||
Ok(true) => Ok(()),
|
||||
Ok(false) => Err(zbus::fdo::Error::Failed("Authentication cancelled".into())),
|
||||
@@ -175,7 +181,12 @@ impl Agent {
|
||||
}
|
||||
|
||||
fn cancel_authentication(&self, cookie: String) {
|
||||
if let Some(cancelled) = self.active.lock().unwrap().get(&cookie) {
|
||||
if let Some(cancelled) = self
|
||||
.active
|
||||
.lock()
|
||||
.expect("authentication session ownership must remain consistent across callbacks")
|
||||
.get(&cookie)
|
||||
{
|
||||
cancelled.store(true, Ordering::Release);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,11 +153,17 @@ fn read_events(
|
||||
for line in lines {
|
||||
let Ok(line) = line else { break };
|
||||
if let Ok(event) = serde_json::from_str(&line) {
|
||||
queue.lock().unwrap().push_back(event);
|
||||
queue
|
||||
.lock()
|
||||
.expect("authentication prompt queue must remain consistent across workers")
|
||||
.push_back(event);
|
||||
}
|
||||
}
|
||||
// The agent owns this pipe. EOF (including agent death) closes the prompt.
|
||||
queue.lock().unwrap().push_back(PromptEvent::Cancel);
|
||||
queue
|
||||
.lock()
|
||||
.expect("authentication prompt queue must remain consistent across workers")
|
||||
.push_back(PromptEvent::Cancel);
|
||||
}
|
||||
|
||||
pub fn run() -> Result<(), Box<dyn std::error::Error>> {
|
||||
@@ -348,7 +354,12 @@ impl cosmic::Application for Prompt {
|
||||
self.material = result.ok();
|
||||
}
|
||||
Message::Tick => {
|
||||
let events: Vec<_> = self.incoming.lock().unwrap().drain(..).collect();
|
||||
let events: Vec<_> = self
|
||||
.incoming
|
||||
.lock()
|
||||
.expect("authentication prompt queue must remain consistent across workers")
|
||||
.drain(..)
|
||||
.collect();
|
||||
let mut refocus = false;
|
||||
for event in events {
|
||||
match event {
|
||||
|
||||
@@ -469,7 +469,9 @@ fn run_session(
|
||||
} else if stopped {
|
||||
Ok(SessionOutcome::Cancelled)
|
||||
} else {
|
||||
Ok(SessionOutcome::Completed(outcome.unwrap()))
|
||||
Ok(SessionOutcome::Completed(outcome.expect(
|
||||
"the session loop finished only after receiving a completion outcome",
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +68,10 @@ enum Error {
|
||||
|
||||
impl Pairing {
|
||||
fn begin(&self, target: &str) -> Result<u64, String> {
|
||||
let mut state = self.0.lock().unwrap();
|
||||
let mut state = self
|
||||
.0
|
||||
.lock()
|
||||
.expect("pairing agent state must remain consistent across callbacks");
|
||||
if state.target.is_some() {
|
||||
return Err("Another device is already pairing.".into());
|
||||
}
|
||||
@@ -78,7 +81,10 @@ impl Pairing {
|
||||
}
|
||||
|
||||
fn finish(&self, generation: u64) {
|
||||
let mut state = self.0.lock().unwrap();
|
||||
let mut state = self
|
||||
.0
|
||||
.lock()
|
||||
.expect("pairing agent state must remain consistent across callbacks");
|
||||
if state.generation == generation {
|
||||
state.target = None;
|
||||
state.prompt = None;
|
||||
@@ -87,7 +93,10 @@ impl Pairing {
|
||||
}
|
||||
|
||||
pub fn cancel_local(&self) -> Option<String> {
|
||||
let mut state = self.0.lock().unwrap();
|
||||
let mut state = self
|
||||
.0
|
||||
.lock()
|
||||
.expect("pairing agent state must remain consistent across callbacks");
|
||||
state.generation = state.generation.wrapping_add(1);
|
||||
state.prompt = None;
|
||||
state.reply = None;
|
||||
@@ -95,11 +104,18 @@ impl Pairing {
|
||||
}
|
||||
|
||||
pub fn prompt(&self) -> Option<Prompt> {
|
||||
self.0.lock().unwrap().prompt.clone()
|
||||
self.0
|
||||
.lock()
|
||||
.expect("pairing agent state must remain consistent across callbacks")
|
||||
.prompt
|
||||
.clone()
|
||||
}
|
||||
|
||||
pub fn reply(&self, id: u64, value: Option<Zeroizing<String>>) {
|
||||
let mut state = self.0.lock().unwrap();
|
||||
let mut state = self
|
||||
.0
|
||||
.lock()
|
||||
.expect("pairing agent state must remain consistent across callbacks");
|
||||
if state.prompt.as_ref().is_some_and(|p| p.id == id) {
|
||||
if let Some(reply) = state.reply.take() {
|
||||
let _ = reply.send(value);
|
||||
@@ -126,7 +142,10 @@ impl Pairing {
|
||||
sender: Option<&str>,
|
||||
) -> Result<Zeroizing<String>, Error> {
|
||||
let (id, rx) = {
|
||||
let mut state = self.0.lock().unwrap();
|
||||
let mut state = self
|
||||
.0
|
||||
.lock()
|
||||
.expect("pairing agent state must remain consistent across callbacks");
|
||||
Self::check(&state, device.as_str(), sender)?;
|
||||
if state.reply.is_some() {
|
||||
return Err(Error::Rejected(
|
||||
@@ -159,7 +178,10 @@ impl Pairing {
|
||||
text: String,
|
||||
sender: Option<&str>,
|
||||
) -> Result<(), Error> {
|
||||
let mut state = self.0.lock().unwrap();
|
||||
let mut state = self
|
||||
.0
|
||||
.lock()
|
||||
.expect("pairing agent state must remain consistent across callbacks");
|
||||
Self::check(&state, device.as_str(), sender)?;
|
||||
state.serial = state.serial.wrapping_add(1);
|
||||
state.prompt = Some(Prompt {
|
||||
@@ -171,7 +193,10 @@ impl Pairing {
|
||||
}
|
||||
|
||||
fn dismiss(&self, sender: Option<&str>) -> Result<(), Error> {
|
||||
let mut state = self.0.lock().unwrap();
|
||||
let mut state = self
|
||||
.0
|
||||
.lock()
|
||||
.expect("pairing agent state must remain consistent across callbacks");
|
||||
if sender.is_none() || state.owner.as_deref() != sender {
|
||||
return Err(Error::Rejected("Unknown sender".into()));
|
||||
}
|
||||
@@ -289,7 +314,10 @@ impl Pairing {
|
||||
fn release(&self, #[zbus(header)] header: Header<'_>) -> Result<(), Error> {
|
||||
self.dismiss(header.sender().map(|s| s.as_str()))?;
|
||||
self.cancel_local();
|
||||
self.0.lock().unwrap().owner = None;
|
||||
self.0
|
||||
.lock()
|
||||
.expect("pairing agent state must remain consistent across callbacks")
|
||||
.owner = None;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -308,17 +336,29 @@ impl Client {
|
||||
.map_err(|e| e.to_string())?;
|
||||
let mut registered = self.registered.lock().await;
|
||||
if registered.as_ref() == Some(&owner)
|
||||
&& self.agent.0.lock().unwrap().owner.as_ref() == Some(&owner)
|
||||
&& self
|
||||
.agent
|
||||
.0
|
||||
.lock()
|
||||
.expect("pairing agent state must remain consistent across callbacks")
|
||||
.owner
|
||||
.as_ref()
|
||||
== Some(&owner)
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
self.agent.0.lock().unwrap().owner = Some(owner.clone());
|
||||
self.agent
|
||||
.0
|
||||
.lock()
|
||||
.expect("pairing agent state must remain consistent across callbacks")
|
||||
.owner = Some(owner.clone());
|
||||
self.connection
|
||||
.object_server()
|
||||
.at(PATH, self.agent.clone())
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
let path = OwnedObjectPath::try_from(PATH).unwrap();
|
||||
let path = OwnedObjectPath::try_from(PATH)
|
||||
.expect("the pairing agent has a valid static object path");
|
||||
proxy(
|
||||
&self.connection,
|
||||
"org.bluez",
|
||||
@@ -341,13 +381,26 @@ impl Client {
|
||||
}
|
||||
let result = async {
|
||||
self.register_agent().await?;
|
||||
if self.agent.0.lock().unwrap().generation != generation {
|
||||
if self
|
||||
.agent
|
||||
.0
|
||||
.lock()
|
||||
.expect("pairing agent state must remain consistent across callbacks")
|
||||
.generation
|
||||
!= generation
|
||||
{
|
||||
return Err("Pairing canceled.".into());
|
||||
}
|
||||
bluetooth::device_call(&self.connection, path, "Pair").await
|
||||
}
|
||||
.await;
|
||||
let still_active = self.agent.0.lock().unwrap().generation == generation;
|
||||
let still_active = self
|
||||
.agent
|
||||
.0
|
||||
.lock()
|
||||
.expect("pairing agent state must remain consistent across callbacks")
|
||||
.generation
|
||||
== generation;
|
||||
self.agent.finish(generation);
|
||||
if !still_active {
|
||||
return Err("Pairing canceled.".into());
|
||||
|
||||
@@ -170,7 +170,12 @@ impl Client {
|
||||
pub(crate) async fn discover(&self, path: String, epoch: u64) -> Result<(), String> {
|
||||
// BlueZ discovery is reference counted per D-Bus caller. Never stop
|
||||
// discovery owned by another app, and never acquire a lease twice.
|
||||
if self.discovery.lock().unwrap().contains_key(&path) {
|
||||
if self
|
||||
.discovery
|
||||
.lock()
|
||||
.expect("Bluetooth discovery ownership must remain consistent across callbacks")
|
||||
.contains_key(&path)
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
adapter_call(&self.connection, &path, "StartDiscovery").await?;
|
||||
@@ -179,13 +184,18 @@ impl Client {
|
||||
return Err("Discovery canceled.".into());
|
||||
}
|
||||
let lease = std::time::Instant::now();
|
||||
self.discovery.lock().unwrap().insert(path.clone(), lease);
|
||||
self.discovery
|
||||
.lock()
|
||||
.expect("Bluetooth discovery ownership must remain consistent across callbacks")
|
||||
.insert(path.clone(), lease);
|
||||
let leases = self.discovery.clone();
|
||||
let connection = self.connection.clone();
|
||||
tokio::spawn(async move {
|
||||
tokio::time::sleep(Duration::from_secs(30)).await;
|
||||
let owned = {
|
||||
let mut leases = leases.lock().unwrap();
|
||||
let mut leases = leases.lock().expect(
|
||||
"Bluetooth discovery ownership must remain consistent across callbacks",
|
||||
);
|
||||
if leases.get(&path) == Some(&lease) {
|
||||
leases.remove(&path);
|
||||
true
|
||||
@@ -201,7 +211,12 @@ impl Client {
|
||||
}
|
||||
|
||||
pub(crate) async fn stop_discovery(&self) -> Result<(), String> {
|
||||
let paths = std::mem::take(&mut *self.discovery.lock().unwrap());
|
||||
let paths = std::mem::take(
|
||||
&mut *self
|
||||
.discovery
|
||||
.lock()
|
||||
.expect("Bluetooth discovery ownership must remain consistent across callbacks"),
|
||||
);
|
||||
let mut result = Ok(());
|
||||
for (path, _) in paths {
|
||||
if let Err(error) = adapter_call(&self.connection, &path, "StopDiscovery").await {
|
||||
@@ -212,7 +227,10 @@ impl Client {
|
||||
}
|
||||
|
||||
pub(crate) fn discovering(&self, path: &str) -> bool {
|
||||
self.discovery.lock().unwrap().contains_key(path)
|
||||
self.discovery
|
||||
.lock()
|
||||
.expect("Bluetooth discovery ownership must remain consistent across callbacks")
|
||||
.contains_key(path)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,9 @@ impl App {
|
||||
column([])
|
||||
.spacing(8)
|
||||
.push(self.label("Bluetooth unavailable", 14.))
|
||||
.push(self.connection_note(result.as_ref().unwrap_err())),
|
||||
.push(self.connection_note(result.as_ref().expect_err(
|
||||
"the unavailable-services branch contains a connection error",
|
||||
))),
|
||||
);
|
||||
};
|
||||
let mut body = column([]).spacing(6);
|
||||
|
||||
@@ -163,7 +163,12 @@ impl App {
|
||||
}
|
||||
if accepted
|
||||
&& !super::agent::valid_answer(
|
||||
&self.connections.prompt.as_ref().unwrap().kind,
|
||||
&self
|
||||
.connections
|
||||
.prompt
|
||||
.as_ref()
|
||||
.expect("the pairing reply branch requires an active prompt")
|
||||
.kind,
|
||||
&self.connections.pairing_input,
|
||||
)
|
||||
{
|
||||
|
||||
@@ -229,7 +229,12 @@ impl Client {
|
||||
|
||||
fn cleanup(&self) {
|
||||
let target = self.agent.cancel_local();
|
||||
let paths = std::mem::take(&mut *self.discovery.lock().unwrap());
|
||||
let paths = std::mem::take(
|
||||
&mut *self
|
||||
.discovery
|
||||
.lock()
|
||||
.expect("Bluetooth discovery ownership must remain consistent across callbacks"),
|
||||
);
|
||||
let connection = self.connection.clone();
|
||||
self.runtime.spawn(async move {
|
||||
if let Some(path) = target {
|
||||
|
||||
@@ -20,7 +20,9 @@ impl App {
|
||||
column([])
|
||||
.spacing(8)
|
||||
.push(self.label("Wi-Fi unavailable", 14.))
|
||||
.push(self.connection_note(result.as_ref().unwrap_err())),
|
||||
.push(self.connection_note(result.as_ref().expect_err(
|
||||
"the unavailable-services branch contains a connection error",
|
||||
))),
|
||||
);
|
||||
};
|
||||
let mut body = column([]).spacing(6);
|
||||
|
||||
@@ -258,7 +258,9 @@ impl cosmic::Application for App {
|
||||
}
|
||||
.into();
|
||||
let error = initial.as_ref().err().cloned();
|
||||
let current = initial.unwrap_or_else(|_| Snapshot::parse(String::new()).unwrap());
|
||||
let current = initial.unwrap_or_else(|_| {
|
||||
Snapshot::parse(String::new()).expect("an empty Settings document uses valid defaults")
|
||||
});
|
||||
let resolved = ferese_theme_client::service::current();
|
||||
let font = fonts::interface_font(&resolved.presented.tokens.typography.font_family);
|
||||
let native_palette = visuals::Palette::from_resolved(&resolved.presented);
|
||||
@@ -540,7 +542,10 @@ impl cosmic::Application for App {
|
||||
if let Some((id, appearance)) = &self.gallery_focus
|
||||
&& self.gallery_position(id, *appearance) == Some((list, index))
|
||||
{
|
||||
let (id, appearance) = self.gallery_focus.take().unwrap();
|
||||
let (id, appearance) = self
|
||||
.gallery_focus
|
||||
.take()
|
||||
.expect("gallery focus was requested before this focus operation");
|
||||
return cosmic::iced::advanced::widget::operate(
|
||||
cosmic::iced::advanced::widget::operation::focusable::focus(
|
||||
gallery_id(&id, appearance),
|
||||
@@ -1100,7 +1105,9 @@ impl App {
|
||||
{
|
||||
return self.apply_wallpaper_edits(wallpaper_controls::edits(
|
||||
wallpaper_controls::Target::Both,
|
||||
path.rsplit('.').next().unwrap(),
|
||||
path.rsplit('.')
|
||||
.next()
|
||||
.expect("splitting a field path always yields its final component"),
|
||||
value.clone(),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -321,10 +321,13 @@ impl App {
|
||||
.map(|panel| panel.resolved_geometry())
|
||||
.unwrap_or_default(),
|
||||
)
|
||||
.unwrap();
|
||||
*default = geometry[field.path.rsplit('.').next().unwrap()]
|
||||
.expect("typed configuration and IPC values must serialize to JSON");
|
||||
*default =
|
||||
geometry[field.path.rsplit('.').next().expect(
|
||||
"splitting a field path always yields its final component",
|
||||
)]
|
||||
.as_f64()
|
||||
.unwrap();
|
||||
.expect("splitting a field path always yields its final component");
|
||||
}
|
||||
let pinned = self.draft.item(&field.path).is_some();
|
||||
let reset = pinned.then_some(Message::PanelEdit(Action::ClearGeometry(
|
||||
|
||||
@@ -258,7 +258,7 @@ impl Widget<Message, Theme, Renderer> for Editor<'_> {
|
||||
.find_map(|(region, bounds)| {
|
||||
matches!(region, Region::Item(id, _) if id == item).then_some(*bounds)
|
||||
})
|
||||
.unwrap();
|
||||
.expect("the active drag branch retains the pressed item and its measured bounds");
|
||||
state.drag = Some(Drag {
|
||||
item: item.clone(),
|
||||
origin: cursor,
|
||||
@@ -272,7 +272,9 @@ impl Widget<Message, Theme, Renderer> for Editor<'_> {
|
||||
}
|
||||
}
|
||||
Event::Mouse(mouse::Event::CursorMoved { .. }) if state.drag.is_some() => {
|
||||
let drag = state.drag.as_mut().unwrap();
|
||||
let drag = state.drag.as_mut().expect(
|
||||
"the active drag branch retains the pressed item and its measured bounds",
|
||||
);
|
||||
if let Some(cursor) = cursor.position() {
|
||||
drag.cursor = cursor;
|
||||
drag.moved |= (cursor.x - drag.origin.x).hypot(cursor.y - drag.origin.y) >= 6.;
|
||||
@@ -291,7 +293,9 @@ impl Widget<Message, Theme, Renderer> for Editor<'_> {
|
||||
Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left))
|
||||
if state.drag.is_some() =>
|
||||
{
|
||||
let drag = state.drag.take().unwrap();
|
||||
let drag = state.drag.take().expect(
|
||||
"the active drag branch retains the pressed item and its measured bounds",
|
||||
);
|
||||
if drag.moved {
|
||||
if let Some(target) = cursor
|
||||
.position()
|
||||
|
||||
@@ -147,7 +147,7 @@ fn locate(panel: &Panel, id: &ItemId) -> Result<(String, usize), String> {
|
||||
.groups
|
||||
.iter()
|
||||
.find(|group| group.id == destination.group)
|
||||
.unwrap();
|
||||
.expect("the selected destination group belongs to its resolved zone");
|
||||
if let Some(index) = group.items.iter().position(|item| &item.id == id) {
|
||||
return Ok((path, index));
|
||||
}
|
||||
@@ -309,7 +309,7 @@ pub(super) fn plan(snapshot: &Snapshot, action: Action) -> Result<Vec<Edit>, Str
|
||||
}
|
||||
let group = records(&candidate, &format!("panels.0.{}.groups", zone.key()))[0]["id"]
|
||||
.as_str()
|
||||
.unwrap()
|
||||
.expect("validated panel groups and item kinds have string identifiers")
|
||||
.to_owned();
|
||||
edits.extend(plan(
|
||||
&candidate,
|
||||
@@ -337,7 +337,13 @@ pub(super) fn plan(snapshot: &Snapshot, action: Action) -> Result<Vec<Edit>, Str
|
||||
}
|
||||
let (path, index) = locate(panel, &id)?;
|
||||
if field == "style"
|
||||
&& !matches!(panel.item(&id).unwrap().kind, ItemKind::Workspaces { .. })
|
||||
&& !matches!(
|
||||
panel
|
||||
.item(&id)
|
||||
.expect("the item edit resolved its stable ID before checking its kind")
|
||||
.kind,
|
||||
ItemKind::Workspaces { .. }
|
||||
)
|
||||
{
|
||||
return Err("Workspace style belongs to Workspaces items.".into());
|
||||
}
|
||||
@@ -349,11 +355,14 @@ pub(super) fn plan(snapshot: &Snapshot, action: Action) -> Result<Vec<Edit>, Str
|
||||
}
|
||||
let path = group_path(panel, &destination)?;
|
||||
let name = serde_json::to_value(kind).map_err(|error| error.to_string())?;
|
||||
let name = name["kind"].as_str().unwrap();
|
||||
let name = name["kind"]
|
||||
.as_str()
|
||||
.expect("validated panel groups and item kinds have string identifiers");
|
||||
let count = destinations(panel)
|
||||
.iter()
|
||||
.map(|destination| {
|
||||
let path = group_path(panel, destination).unwrap();
|
||||
let path = group_path(panel, destination)
|
||||
.expect("catalog destinations refer to existing panel groups");
|
||||
records(snapshot, &path).len()
|
||||
})
|
||||
.sum::<usize>();
|
||||
@@ -366,7 +375,7 @@ pub(super) fn plan(snapshot: &Snapshot, action: Action) -> Result<Vec<Edit>, Str
|
||||
}
|
||||
})
|
||||
.find(|id| panel.item(&ItemId(id.clone())).is_none())
|
||||
.unwrap();
|
||||
.expect("one more candidate ID than existing items guarantees an unused ID");
|
||||
let mut items = records(snapshot, &path);
|
||||
items.push(
|
||||
serde_json::to_value(Item::new(&id, kind)).map_err(|error| error.to_string())?,
|
||||
@@ -381,7 +390,7 @@ pub(super) fn plan(snapshot: &Snapshot, action: Action) -> Result<Vec<Edit>, Str
|
||||
let id = (1..=count + 1)
|
||||
.map(|number| format!("{}-{number}", zone.key()))
|
||||
.find(|id| locate_group(panel, &GroupId(id.clone())).is_err())
|
||||
.unwrap();
|
||||
.expect("one more candidate ID than existing items guarantees an unused ID");
|
||||
let path = format!("panels.0.{}.groups", zone.key());
|
||||
let mut groups = records(snapshot, &path);
|
||||
groups.push(serde_json::json!({
|
||||
@@ -432,7 +441,8 @@ pub(super) fn plan(snapshot: &Snapshot, action: Action) -> Result<Vec<Edit>, Str
|
||||
"padding_vertical" | "padding_horizontal" => {
|
||||
let (zone, index) = locate_group(panel, &id)?;
|
||||
let mut padding =
|
||||
serde_json::to_value(zone.definition(panel).groups[index].padding).unwrap();
|
||||
serde_json::to_value(zone.definition(panel).groups[index].padding)
|
||||
.expect("typed configuration and IPC values must serialize to JSON");
|
||||
padding[usize::from(field == "padding_horizontal")] = value;
|
||||
("padding".to_owned(), padding)
|
||||
}
|
||||
|
||||
@@ -140,12 +140,12 @@ impl App {
|
||||
.into_iter()
|
||||
.flat_map(|zone| &zone.definition(panel).groups)
|
||||
.find(|group| group.id == destination.group)
|
||||
.unwrap();
|
||||
.expect("the inspector selection refers to an item or group in the current panel");
|
||||
let index = group
|
||||
.items
|
||||
.iter()
|
||||
.position(|candidate| candidate.id == item.id)
|
||||
.unwrap();
|
||||
.expect("the inspector selection refers to an item or group in the current panel");
|
||||
rows.push(self.panel_divider())
|
||||
.push(
|
||||
self.inspector_actions(
|
||||
@@ -293,7 +293,7 @@ impl App {
|
||||
.groups
|
||||
.iter()
|
||||
.position(|candidate| candidate.id == group.id)
|
||||
.unwrap();
|
||||
.expect("the inspector selection refers to an item or group in the current panel");
|
||||
rows = rows.push(
|
||||
self.inspector_actions(
|
||||
(index > 0).then(|| Message::PanelEdit(Action::EarlierGroup(group.id.clone()))),
|
||||
@@ -335,7 +335,12 @@ impl App {
|
||||
fn panel_field(&self, item: &Item, field: Field) -> Element<'static, Message> {
|
||||
let p = crate::visuals::Palette::from_resolved(&self.resolved.presented);
|
||||
let id = item.id.clone();
|
||||
let name = field.path.rsplit('.').next().unwrap().to_owned();
|
||||
let name = field
|
||||
.path
|
||||
.rsplit('.')
|
||||
.next()
|
||||
.expect("splitting a field path always yields its final component")
|
||||
.to_owned();
|
||||
let values = serde_json::to_value(item).expect("serializable panel item");
|
||||
match field.kind {
|
||||
Kind::Choice { default, choices } => {
|
||||
|
||||
@@ -328,7 +328,9 @@ impl App {
|
||||
return setting(
|
||||
&self.draft,
|
||||
self.wallpaper.target,
|
||||
path.rsplit('.').next().unwrap(),
|
||||
path.rsplit('.')
|
||||
.next()
|
||||
.expect("splitting a field path always yields its final component"),
|
||||
fallback,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -336,7 +336,11 @@ impl FereseShell {
|
||||
presentation.rows = rows;
|
||||
continue;
|
||||
}
|
||||
let runtime = output.runtime.hub.as_ref().unwrap();
|
||||
let runtime = output
|
||||
.runtime
|
||||
.hub
|
||||
.as_ref()
|
||||
.expect("this activity action belongs to an output with a Morph hub");
|
||||
let name = self
|
||||
.output_for_bar(output.bar)
|
||||
.map(|output| output.name.as_str())
|
||||
|
||||
@@ -67,7 +67,11 @@ impl Compact {
|
||||
+ count as f32 * (ICON + TEXT_GAP)
|
||||
+ count.saturating_sub(1) as f32 * (2. * ITEM_GAP + 1.)
|
||||
+ if hidden > 0 {
|
||||
ITEM_GAP + self.badge.unwrap().1
|
||||
ITEM_GAP
|
||||
+ self
|
||||
.badge
|
||||
.expect("hidden activity count is nonzero only when its badge was measured")
|
||||
.1
|
||||
} else {
|
||||
0.
|
||||
};
|
||||
|
||||
@@ -451,8 +451,11 @@ fn parse_document(
|
||||
|
||||
impl ShellConfig {
|
||||
pub(crate) fn apply_theme(&mut self, theme: &ferese_config::theme::ResolvedTheme) {
|
||||
let config: ThemeConfig =
|
||||
serde_json::from_value(serde_json::to_value(&theme.tokens).unwrap()).unwrap();
|
||||
let config: ThemeConfig = serde_json::from_value(
|
||||
serde_json::to_value(&theme.tokens)
|
||||
.expect("typed configuration and IPC values must serialize to JSON"),
|
||||
)
|
||||
.expect("resolved theme tokens must match their consumer schema");
|
||||
self.theme = shell_theme(&config);
|
||||
self.theme.appearance = theme.appearance;
|
||||
self.theme.high_contrast = theme.accessibility.increase_contrast;
|
||||
|
||||
@@ -295,7 +295,7 @@ impl FereseShell {
|
||||
let current = Mode::ALL
|
||||
.iter()
|
||||
.position(|mode| *mode == self.display_mode.selected)
|
||||
.unwrap();
|
||||
.expect("Mode::ALL contains every selectable display mode");
|
||||
let mode = match key {
|
||||
Key::Character(character) => character
|
||||
.parse::<usize>()
|
||||
|
||||
@@ -460,7 +460,10 @@ impl FereseShell {
|
||||
launcher.rows = rows;
|
||||
launcher.file_status = status;
|
||||
let task = self.merge_launcher_windows(true);
|
||||
let launcher = self.launcher.as_mut().unwrap();
|
||||
let launcher = self
|
||||
.launcher
|
||||
.as_mut()
|
||||
.expect("merging launcher windows keeps the active launcher alive");
|
||||
launcher.selected = selected
|
||||
.and_then(|id| launcher.rows.iter().position(|row| row.id == id))
|
||||
.unwrap_or(0);
|
||||
|
||||
@@ -51,7 +51,9 @@ impl FereseShell {
|
||||
&& let Some(effects) = &launcher.effects
|
||||
{
|
||||
let _ = effects.set_presentation(
|
||||
&launcher.regions.lock().unwrap(),
|
||||
&launcher.regions.lock().expect(
|
||||
"surface rendering state must remain consistent across callbacks",
|
||||
),
|
||||
launcher.progress.current as f32,
|
||||
[],
|
||||
ferese_surface_effects_v1::Role::Popover,
|
||||
|
||||
@@ -79,7 +79,11 @@ impl Service {
|
||||
return;
|
||||
}
|
||||
self.requested = key.clone();
|
||||
self.queue.0.lock().unwrap().pending = key;
|
||||
self.queue
|
||||
.0
|
||||
.lock()
|
||||
.expect("artwork decode queue must remain consistent across workers")
|
||||
.pending = key;
|
||||
self.queue.1.notify_one();
|
||||
}
|
||||
|
||||
@@ -94,7 +98,11 @@ impl Service {
|
||||
|
||||
impl Drop for Service {
|
||||
fn drop(&mut self) {
|
||||
self.queue.0.lock().unwrap().stopped = true;
|
||||
self.queue
|
||||
.0
|
||||
.lock()
|
||||
.expect("artwork decode queue must remain consistent across workers")
|
||||
.stopped = true;
|
||||
self.queue.1.notify_one();
|
||||
}
|
||||
}
|
||||
@@ -107,18 +115,27 @@ fn work(
|
||||
let mut cache: VecDeque<Artwork> = VecDeque::new();
|
||||
loop {
|
||||
let (lock, changed) = &*queue;
|
||||
let mut state = lock.lock().unwrap();
|
||||
let mut state = lock
|
||||
.lock()
|
||||
.expect("artwork decode queue must remain consistent across workers");
|
||||
while state.pending.is_none() && !state.stopped {
|
||||
state = changed.wait(state).unwrap();
|
||||
state = changed
|
||||
.wait(state)
|
||||
.expect("artwork decode queue must remain consistent across workers");
|
||||
}
|
||||
|
||||
if state.stopped {
|
||||
return;
|
||||
}
|
||||
let key = state.pending.take().unwrap();
|
||||
let key = state
|
||||
.pending
|
||||
.take()
|
||||
.expect("the artwork wait completed with a pending request");
|
||||
drop(state);
|
||||
let artwork = match cache.iter().position(|art| art.key == key) {
|
||||
Some(index) => cache.remove(index).unwrap(),
|
||||
Some(index) => cache
|
||||
.remove(index)
|
||||
.expect("the cache lookup returned an existing artwork index"),
|
||||
None => Artwork {
|
||||
handle: load(&key.uri),
|
||||
key,
|
||||
|
||||
@@ -503,7 +503,9 @@ impl<M> Widget<M, Theme, cosmic::Renderer> for Motion<'_, M> {
|
||||
viewport: &Rectangle,
|
||||
) {
|
||||
if let Some(regions) = &self.regions {
|
||||
let mut collected = regions.lock().unwrap();
|
||||
let mut collected = regions
|
||||
.lock()
|
||||
.expect("surface rendering state must remain consistent across callbacks");
|
||||
collected.clear();
|
||||
self.content.as_widget_mut().operate(
|
||||
tree,
|
||||
|
||||
@@ -250,7 +250,10 @@ impl FereseShell {
|
||||
.as_ref()
|
||||
.is_some_and(|surface| !wanted || selected.as_ref() != Some(&surface.output))
|
||||
{
|
||||
let surface = self.notification_surface.take().unwrap();
|
||||
let surface = self
|
||||
.notification_surface
|
||||
.take()
|
||||
.expect("notification teardown selected an existing notification surface");
|
||||
tasks.push(destroy_layer_surface(surface.id));
|
||||
}
|
||||
if !wanted {
|
||||
@@ -630,7 +633,9 @@ impl FereseShell {
|
||||
&& let Some(effects) = &surface.effects
|
||||
{
|
||||
let _ = effects.set_presentation(
|
||||
&surface.regions.lock().unwrap(),
|
||||
&surface.regions.lock().expect(
|
||||
"surface rendering state must remain consistent across callbacks",
|
||||
),
|
||||
1.0,
|
||||
self.notifications
|
||||
.popup_groups_at(now)
|
||||
|
||||
@@ -91,7 +91,10 @@ impl Server {
|
||||
hints: HashMap<String, zbus::zvariant::OwnedValue>,
|
||||
expire_timeout: i32,
|
||||
) -> zbus::fdo::Result<u32> {
|
||||
let mut registry = self.registry.lock().unwrap();
|
||||
let mut registry = self
|
||||
.registry
|
||||
.lock()
|
||||
.expect("notification ownership registry must remain consistent across callbacks");
|
||||
let replacing = registry.live.contains_key(&replaces_id);
|
||||
if !replacing && registry.live.len() >= HISTORY_LIMIT {
|
||||
return Err(zbus::fdo::Error::LimitsExceeded(
|
||||
@@ -156,7 +159,10 @@ impl Server {
|
||||
}
|
||||
|
||||
fn close_notification(&self, id: u32) -> zbus::fdo::Result<()> {
|
||||
let mut registry = self.registry.lock().unwrap();
|
||||
let mut registry = self
|
||||
.registry
|
||||
.lock()
|
||||
.expect("notification ownership registry must remain consistent across callbacks");
|
||||
let revision = *registry
|
||||
.live
|
||||
.get(&id)
|
||||
@@ -249,7 +255,7 @@ impl Service {
|
||||
continue;
|
||||
}
|
||||
Command::Close(id, revision, reason) => {
|
||||
if !registry.lock().unwrap().close(id, revision, reason) {
|
||||
if !registry.lock().expect("notification ownership registry must remain consistent across callbacks").close(id, revision, reason) {
|
||||
continue;
|
||||
}
|
||||
connection.emit_signal(
|
||||
@@ -261,7 +267,7 @@ impl Service {
|
||||
)
|
||||
}
|
||||
Command::Action(id, revision, action) => {
|
||||
if registry.lock().unwrap().live.get(&id) != Some(&revision) {
|
||||
if registry.lock().expect("notification ownership registry must remain consistent across callbacks").live.get(&id) != Some(&revision) {
|
||||
continue;
|
||||
}
|
||||
connection.emit_signal(
|
||||
|
||||
@@ -844,7 +844,9 @@ impl FereseShell {
|
||||
let body: Element<'_, cosmic::Action<Message>> = match mode {
|
||||
_ if activity.is_some() => self.hub_activity_strip(
|
||||
id,
|
||||
activity.unwrap(),
|
||||
activity.expect(
|
||||
"the activity-specific hub view requires its selected activity",
|
||||
),
|
||||
mode == Mode::Peek,
|
||||
intrinsic,
|
||||
opacity,
|
||||
|
||||
@@ -140,8 +140,14 @@ type PollState = (Mutex<(u64, bool, Option<ActionError>, bool, u64)>, Condvar);
|
||||
fn wait_for_poll(shared: &PollState) -> Option<u64> {
|
||||
let state = shared
|
||||
.1
|
||||
.wait_while(shared.0.lock().unwrap(), |state| state.1 && !state.3)
|
||||
.unwrap();
|
||||
.wait_while(
|
||||
shared
|
||||
.0
|
||||
.lock()
|
||||
.expect("status polling state must not be used after a worker panics"),
|
||||
|state| state.1 && !state.3,
|
||||
)
|
||||
.expect("status polling state must not be used after a worker panics");
|
||||
(!state.3).then_some(state.0)
|
||||
}
|
||||
|
||||
@@ -184,7 +190,11 @@ impl Service {
|
||||
let mut night_light_monitor = night_light::Monitor::start(polling.clone()).ok();
|
||||
|
||||
while let Some(before) = wait_for_poll(&polling) {
|
||||
let revision = polling.0.lock().unwrap().4;
|
||||
let revision = polling
|
||||
.0
|
||||
.lock()
|
||||
.expect("status polling state must not be used after a worker panics")
|
||||
.4;
|
||||
if night_light_monitor.is_none() {
|
||||
night_light_monitor = night_light::Monitor::start(polling.clone()).ok();
|
||||
}
|
||||
@@ -222,7 +232,10 @@ impl Service {
|
||||
snapshot.audio = audio.read(before);
|
||||
snapshot.network = network.read(before, network::read);
|
||||
snapshot.bluetooth = bluetooth.read(before, bluetooth::read);
|
||||
let state = polling.0.lock().unwrap();
|
||||
let state = polling
|
||||
.0
|
||||
.lock()
|
||||
.expect("status polling state must not be used after a worker panics");
|
||||
|
||||
if state.3 {
|
||||
break;
|
||||
@@ -282,15 +295,24 @@ impl Service {
|
||||
|
||||
for (id, action) in pending {
|
||||
{
|
||||
let mut state = shared.0.lock().unwrap();
|
||||
let mut state = shared
|
||||
.0
|
||||
.lock()
|
||||
.expect("status polling state must not be used after a worker panics");
|
||||
state.0 = id;
|
||||
state.1 = true;
|
||||
}
|
||||
let settings = settings.lock().unwrap().clone();
|
||||
let settings = settings
|
||||
.lock()
|
||||
.expect("status polling state must not be used after a worker panics")
|
||||
.clone();
|
||||
let error = execute(&action, settings.as_deref())
|
||||
.err()
|
||||
.map(|message| ActionError { action, message });
|
||||
let mut state = shared.0.lock().unwrap();
|
||||
let mut state = shared
|
||||
.0
|
||||
.lock()
|
||||
.expect("status polling state must not be used after a worker panics");
|
||||
state.1 = false;
|
||||
state.2 = error;
|
||||
state.4 = state.4.wrapping_add(1);
|
||||
@@ -298,7 +320,11 @@ impl Service {
|
||||
}
|
||||
}
|
||||
|
||||
shared.0.lock().unwrap().3 = true;
|
||||
shared
|
||||
.0
|
||||
.lock()
|
||||
.expect("status polling state must not be used after a worker panics")
|
||||
.3 = true;
|
||||
shared.1.notify_one();
|
||||
});
|
||||
|
||||
@@ -325,7 +351,10 @@ impl Service {
|
||||
}
|
||||
|
||||
pub fn update_settings(&self, settings: Option<Vec<String>>) {
|
||||
*self.settings.lock().unwrap() = settings;
|
||||
*self
|
||||
.settings
|
||||
.lock()
|
||||
.expect("status polling state must not be used after a worker panics") = settings;
|
||||
}
|
||||
|
||||
pub fn subscription(&self) -> cosmic::iced::Subscription<Update> {
|
||||
|
||||
@@ -60,18 +60,26 @@ impl Signals {
|
||||
pw::init();
|
||||
|
||||
loop {
|
||||
if *stop.stopped.lock().unwrap() {
|
||||
if *stop.stopped.lock().expect(
|
||||
"audio listener lifecycle state must remain consistent across workers",
|
||||
) {
|
||||
break;
|
||||
}
|
||||
|
||||
let _ = monitor(state.clone(), &stop, remote.as_deref());
|
||||
state.set_alive(false);
|
||||
*stop.sender.lock().unwrap() = None;
|
||||
let stopped = stop.stopped.lock().unwrap();
|
||||
*stop.sender.lock().expect(
|
||||
"audio listener lifecycle state must remain consistent across workers",
|
||||
) = None;
|
||||
let stopped = stop.stopped.lock().expect(
|
||||
"audio listener lifecycle state must remain consistent across workers",
|
||||
);
|
||||
let (stopped, _) = stop
|
||||
.wake
|
||||
.wait_timeout_while(stopped, Duration::from_secs(5), |value| !*value)
|
||||
.unwrap();
|
||||
.expect(
|
||||
"audio listener lifecycle state must remain consistent across workers",
|
||||
);
|
||||
|
||||
if *stopped {
|
||||
break;
|
||||
@@ -115,10 +123,20 @@ impl Signals {
|
||||
|
||||
impl Drop for Signals {
|
||||
fn drop(&mut self) {
|
||||
*self.stop.stopped.lock().unwrap() = true;
|
||||
*self
|
||||
.stop
|
||||
.stopped
|
||||
.lock()
|
||||
.expect("audio listener lifecycle state must remain consistent across workers") = true;
|
||||
self.stop.wake.notify_one();
|
||||
|
||||
if let Some(sender) = self.stop.sender.lock().unwrap().as_ref() {
|
||||
if let Some(sender) = self
|
||||
.stop
|
||||
.sender
|
||||
.lock()
|
||||
.expect("audio listener lifecycle state must remain consistent across workers")
|
||||
.as_ref()
|
||||
{
|
||||
let _ = sender.send(());
|
||||
}
|
||||
}
|
||||
@@ -273,9 +291,17 @@ fn monitor(state: Arc<State>, stop: &Stop, remote: Option<&str>) -> Result<(), p
|
||||
mainloop.quit();
|
||||
}
|
||||
});
|
||||
*stop.sender.lock().unwrap() = Some(sender);
|
||||
*stop
|
||||
.sender
|
||||
.lock()
|
||||
.expect("audio listener lifecycle state must remain consistent across workers") =
|
||||
Some(sender);
|
||||
|
||||
if !*stop.stopped.lock().unwrap() {
|
||||
if !*stop
|
||||
.stopped
|
||||
.lock()
|
||||
.expect("audio listener lifecycle state must remain consistent across workers")
|
||||
{
|
||||
mainloop.run();
|
||||
}
|
||||
|
||||
|
||||
@@ -21,8 +21,12 @@ fn menu_positioner(
|
||||
};
|
||||
SctkPositioner {
|
||||
anchor_rect: rectangle,
|
||||
anchor: anchor.try_into().unwrap(),
|
||||
gravity: gravity.try_into().unwrap(),
|
||||
anchor: anchor
|
||||
.try_into()
|
||||
.expect("popup placement uses supported anchor and gravity combinations"),
|
||||
gravity: gravity
|
||||
.try_into()
|
||||
.expect("popup placement uses supported anchor and gravity combinations"),
|
||||
offset: (rectangle.width / 2, gap),
|
||||
size_limits: Limits::NONE
|
||||
.max_width(368.0)
|
||||
|
||||
@@ -164,7 +164,10 @@ impl OpenMenu {
|
||||
}
|
||||
}
|
||||
if let Some(effects) = &self.effects {
|
||||
let regions = self.regions.lock().unwrap();
|
||||
let regions = self
|
||||
.regions
|
||||
.lock()
|
||||
.expect("surface rendering state must remain consistent across callbacks");
|
||||
if let Err(error) = effects.set_regions(®ions) {
|
||||
eprintln!("ferese-shell: could not prepare popover material: {error}");
|
||||
}
|
||||
@@ -355,7 +358,9 @@ impl FereseShell {
|
||||
|now| {
|
||||
if let Some(effects) = &menu.effects {
|
||||
let _ = effects.set_presentation(
|
||||
&menu.regions.lock().unwrap(),
|
||||
&menu.regions.lock().expect(
|
||||
"surface rendering state must remain consistent across callbacks",
|
||||
),
|
||||
menu.motion.progress_at(now),
|
||||
[],
|
||||
super::ferese_surface_effects_v1::Role::Popover,
|
||||
|
||||
@@ -557,7 +557,10 @@ impl FereseShell {
|
||||
.as_ref()
|
||||
.is_some_and(|entry| entry.id == layer_id) =>
|
||||
{
|
||||
let entry = self.notification_surface.as_mut().unwrap();
|
||||
let entry = self
|
||||
.notification_surface
|
||||
.as_mut()
|
||||
.expect("the notification update branch matched an existing surface");
|
||||
|
||||
if entry.effects.is_none() {
|
||||
match EffectsBinding::attach_role(
|
||||
@@ -766,7 +769,10 @@ impl EffectsBinding {
|
||||
queue,
|
||||
}));
|
||||
}
|
||||
let context = context.as_ref().unwrap().clone();
|
||||
let context = context
|
||||
.as_ref()
|
||||
.expect("surface effects binding initialized its native display context")
|
||||
.clone();
|
||||
let effects = context
|
||||
.manager
|
||||
.as_ref()
|
||||
|
||||
@@ -436,7 +436,11 @@ impl FereseShell {
|
||||
let Content::Power(power) = modal.content else {
|
||||
return self.close_system_modal();
|
||||
};
|
||||
let approval = modal.inhibitors.as_ref().unwrap().clone();
|
||||
let approval = modal
|
||||
.inhibitors
|
||||
.as_ref()
|
||||
.expect("power confirmation was populated with its inhibitor decision")
|
||||
.clone();
|
||||
let action = match power {
|
||||
PowerAction::Logout(serial) => {
|
||||
if let Some(control) = &self.control {
|
||||
@@ -516,7 +520,9 @@ impl FereseShell {
|
||||
|now| {
|
||||
if let Some(effects) = &surface.effects {
|
||||
let _ = effects.set_presentation(
|
||||
&surface.regions.lock().unwrap(),
|
||||
&surface.regions.lock().expect(
|
||||
"surface rendering state must remain consistent across callbacks",
|
||||
),
|
||||
modal.motion.progress_at(now),
|
||||
[],
|
||||
ferese_surface_effects_v1::Role::Modal,
|
||||
|
||||
@@ -305,8 +305,12 @@ impl FereseShell {
|
||||
width: bounds.width.round() as i32,
|
||||
height: bounds.height.round() as i32,
|
||||
},
|
||||
anchor: anchor.try_into().unwrap(),
|
||||
gravity: gravity.try_into().unwrap(),
|
||||
anchor: anchor
|
||||
.try_into()
|
||||
.expect("popup placement uses supported anchor and gravity combinations"),
|
||||
gravity: gravity
|
||||
.try_into()
|
||||
.expect("popup placement uses supported anchor and gravity combinations"),
|
||||
offset: (0, gap),
|
||||
constraint_adjustment: 3,
|
||||
size_limits: cosmic::iced::Limits::NONE.max_width(320.).max_height(64.),
|
||||
|
||||
Reference in new issue
Block a user