pub struct ProxyWatcher { /* private fields */ }Expand description
OS proxy watcher: initial snapshot, then debounced changes (equality ignores captured_at).
Own OS threads — one, and on Linux one more per live source, plus one for
WatchOptions::poll_interval (elsewhere that interval shortens the single thread’s
own wait instead of adding one). Drop joins them.
ProxyWatcher::health for route liveness. Win/macOS/Linux;
else Error::Unsupported. Applies no *_proxy convention of its own — only KDE’s
ProxyType = 4 builds a ProxyEnv, from the variables
kioslaverc names.
The stream is level-triggered: while a subscriber keeps polling, a finite number of
polls always reaches a WatchEvent::Snapshot matching the current
state, a WatchEvent::Error, or None. An Error item does not
end the stream; None comes only once the backend thread has stopped, and is preceded
by a snapshot reporting WatchHealth::stopped. Undelivered snapshots fold into the
newest one, so a subscriber that stops polling loses the history between snapshots,
never current; undelivered failures are capped at 1024, oldest
discarded first. The trim runs when a snapshot is published, so a failure arriving
after a full one holds the queue at 1025 until the next snapshot trims it back.
One subscriber, not a broadcast. Only the most recently registered waker is kept, and
poll_next takes &mut, so the only way to poll a watcher from more than one task is
to put it behind a lock — after which the task that polled first is parked with its
waker overwritten, and stays parked until something else on this watcher wakes it. To
fan a stream out, poll it in one place and forward.
Send but not Sync — on every platform, for a different platform reason each
time. So current, health, state
and poll_now take &self and do no OS I/O, yet still belong to
the thread that owns the watcher, and Arc<ProxyWatcher> is not Send. Move the
watcher into the task that polls it and forward from there what other threads need.
Under the tokio feature watch_channel is that forwarding already written — named in
backticks rather than linked because it exists only under that feature; without it the
shape is the same by hand, one owner republishing current into a
Mutex or a channel after every item. Neither recovers
poll_now, which stays with the owner: send it a request rather than
sharing the watcher.
The Linux threads read the process environment, and keep reading it: every
notification re-reads the desktop and sandbox variables, at a moment the caller does
not choose. std::env::set_var and std::env::remove_var are sound only while no
other thread is reading the environment, so treat them as unavailable for as long as a
watcher is alive — set what you need before constructing one. Windows is exempt;
std documents both as always safe there.
use std::future::poll_fn;
use std::pin::Pin;
use futures_core::Stream;
use proxy_watch::{ProxyWatcher, WatchEvent};
let mut watcher = ProxyWatcher::new()?;
println!("current: {:?}", watcher.current().effective);
let next = futures_executor::block_on(poll_fn(|cx| Pin::new(&mut watcher).poll_next(cx)));
match next {
Some(WatchEvent::Snapshot { state, .. }) => {
println!("now: {:?} (live: {})", state.config.effective, state.health.is_fully_live());
}
Some(WatchEvent::Error { error, .. }) => eprintln!("re-read failed: {error}"),
_ => {}
}Implementations§
Source§impl ProxyWatcher
impl ProxyWatcher
Sourcepub fn new() -> Result<Self, Error>
pub fn new() -> Result<Self, Error>
Start watching with WatchOptions::default.
§Errors
Error::Unsupported, Error::Io, Error::Sandboxed (Linux, in a sandbox
with no dconf), or a parse error from malformed OS settings.
Sourcepub fn with_options(options: WatchOptions) -> Result<Self, Error>
pub fn with_options(options: WatchOptions) -> Result<Self, Error>
Start watching with explicit options.
Blocks the way read does, and can pay that price twice: the notification route is
armed before the configuration is read, not after, so a change landing between the
two is already queued rather than lost. macOS is the exception on both counts — it
subscribes on the watcher thread instead, closing the same window with the re-read
that thread opens with, and that subscription and the read each run the same
five-second retry budget, so a configd that is still coming up can hold this call
for ten seconds.
§Errors
Same as ProxyWatcher::new.
Sourcepub fn current(&self) -> ProxyConfig
pub fn current(&self) -> ProxyConfig
The most recent snapshot (no OS I/O; may briefly wait for the internal mutex). Equals the constructor read right after start.
Sourcepub fn health(&self) -> WatchHealth
pub fn health(&self) -> WatchHealth
Construction-time routes plus any runtime degradation (see WatchHealth).
Does no OS I/O; it may briefly wait for the same internal mutex
ProxyWatcher::current uses.
Sourcepub fn state(&self) -> WatchState
pub fn state(&self) -> WatchState
Sourcepub fn poll_now(&self)
pub fn poll_now(&self)
Request an immediate re-read (same debounce/equality path as a native notify).
Call it when the caller knows something this crate cannot see: a connection attempt failed in a way that might be proxy-related, or the caller’s own network-change watch fired. Watching the network itself is not this crate’s job, and this is what stands in for it.
Returns immediately, and may deliver nothing. Sharing a notification’s path means
sharing its equality skip: a re-read that finds the configuration and the health
unchanged publishes no WatchEvent, and asking again does not force one. The
Stream reports change, not completion — and so does everything else:
current and state answer the same before and
after such a re-read, so they are where the current answer comes from, not evidence
that this request landed. WatchEvent::Error says the same for the recovery case.
Trait Implementations§
Source§impl Debug for ProxyWatcher
impl Debug for ProxyWatcher
Source§impl Stream for ProxyWatcher
impl Stream for ProxyWatcher
Source§type Item = WatchEvent
type Item = WatchEvent
Auto Trait Implementations§
impl !RefUnwindSafe for ProxyWatcher
impl !Sync for ProxyWatcher
impl !UnwindSafe for ProxyWatcher
impl Freeze for ProxyWatcher
impl Send for ProxyWatcher
impl Unpin for ProxyWatcher
impl UnsafeUnpin for ProxyWatcher
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
impl<T> ErasedDestructor for Twhere
T: 'static,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<S> IntoStream for Swhere
S: Stream,
impl<S> IntoStream for Swhere
S: Stream,
Source§impl<T> Pipe for Twhere
T: ?Sized,
impl<T> Pipe for Twhere
T: ?Sized,
Source§fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
Source§fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read moreSource§fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read moreSource§fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
Source§fn pipe_borrow_mut<'a, B, R>(
&'a mut self,
func: impl FnOnce(&'a mut B) -> R,
) -> R
fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
Source§fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
self, then passes self.as_ref() into the pipe function.Source§fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
self, then passes self.as_mut() into the pipe
function.Source§fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
self, then passes self.deref() into the pipe function.impl<T> Read<Exclusive, BecauseExclusive> for Twhere
T: ?Sized,
Source§impl<S1> StreamExt for S1where
S1: Stream,
impl<S1> StreamExt for S1where
S1: Stream,
Source§fn merge<T, S2>(
self,
other: S2,
) -> Merge2<T, S1, <S2 as IntoStream>::IntoStream>where
S1: Stream<Item = T>,
S2: IntoStream<Item = T>,
fn merge<T, S2>(
self,
other: S2,
) -> Merge2<T, S1, <S2 as IntoStream>::IntoStream>where
S1: Stream<Item = T>,
S2: IntoStream<Item = T>,
Source§fn chain<T, S2>(self, other: S2) -> Chain2<S1, <S2 as IntoStream>::IntoStream>
fn chain<T, S2>(self, other: S2) -> Chain2<S1, <S2 as IntoStream>::IntoStream>
Source§fn zip<T, S2>(self, other: S2) -> Zip2<S1, <S2 as IntoStream>::IntoStream>
fn zip<T, S2>(self, other: S2) -> Zip2<S1, <S2 as IntoStream>::IntoStream>
Source§fn co(self) -> FromStream<Self>where
Self: Sized,
fn co(self) -> FromStream<Self>where
Self: Sized,
alloc only.Source§fn wait_until<D>(
self,
deadline: D,
) -> WaitUntil<Self, <D as IntoFuture>::IntoFuture>where
Self: Sized,
D: IntoFuture,
fn wait_until<D>(
self,
deadline: D,
) -> WaitUntil<Self, <D as IntoFuture>::IntoFuture>where
Self: Sized,
D: IntoFuture,
Source§impl<S> StreamExt for S
impl<S> StreamExt for S
Source§fn next(&mut self) -> NextFuture<'_, Self> ⓘwhere
Self: Unpin,
fn next(&mut self) -> NextFuture<'_, Self> ⓘwhere
Self: Unpin,
Source§fn try_next<T, E>(&mut self) -> TryNextFuture<'_, Self> ⓘ
fn try_next<T, E>(&mut self) -> TryNextFuture<'_, Self> ⓘ
Source§fn count(self) -> CountFuture<Self> ⓘwhere
Self: Sized,
fn count(self) -> CountFuture<Self> ⓘwhere
Self: Sized,
Source§fn map<T, F>(self, f: F) -> Map<Self, F>
fn map<T, F>(self, f: F) -> Map<Self, F>
Source§fn flat_map<U, F>(self, f: F) -> FlatMap<Self, U, F>
fn flat_map<U, F>(self, f: F) -> FlatMap<Self, U, F>
Source§fn then<F, Fut>(self, f: F) -> Then<Self, F, Fut>
fn then<F, Fut>(self, f: F) -> Then<Self, F, Fut>
Source§fn filter_map<T, F>(self, f: F) -> FilterMap<Self, F>
fn filter_map<T, F>(self, f: F) -> FilterMap<Self, F>
Source§fn take(self, n: usize) -> Take<Self>where
Self: Sized,
fn take(self, n: usize) -> Take<Self>where
Self: Sized,
n items of the stream. Read moreSource§fn take_while<P>(self, predicate: P) -> TakeWhile<Self, P>
fn take_while<P>(self, predicate: P) -> TakeWhile<Self, P>
Source§fn skip(self, n: usize) -> Skip<Self>where
Self: Sized,
fn skip(self, n: usize) -> Skip<Self>where
Self: Sized,
n items of the stream. Read moreSource§fn skip_while<P>(self, predicate: P) -> SkipWhile<Self, P>
fn skip_while<P>(self, predicate: P) -> SkipWhile<Self, P>
Source§fn step_by(self, step: usize) -> StepBy<Self>where
Self: Sized,
fn step_by(self, step: usize) -> StepBy<Self>where
Self: Sized,
stepth item. Read moreSource§fn chain<U>(self, other: U) -> Chain<Self, U>
fn chain<U>(self, other: U) -> Chain<Self, U>
Source§fn collect<C>(self) -> CollectFuture<Self, C> ⓘ
fn collect<C>(self) -> CollectFuture<Self, C> ⓘ
Source§fn try_collect<T, E, C>(self) -> TryCollectFuture<Self, C> ⓘ
fn try_collect<T, E, C>(self) -> TryCollectFuture<Self, C> ⓘ
Source§fn partition<B, P>(self, predicate: P) -> PartitionFuture<Self, P, B> ⓘ
fn partition<B, P>(self, predicate: P) -> PartitionFuture<Self, P, B> ⓘ
predicate is true and those for which it is
false, and then collects them into two collections. Read moreSource§fn fold<T, F>(self, init: T, f: F) -> FoldFuture<Self, F, T> ⓘ
fn fold<T, F>(self, init: T, f: F) -> FoldFuture<Self, F, T> ⓘ
Source§fn try_fold<T, E, F, B>(
&mut self,
init: B,
f: F,
) -> TryFoldFuture<'_, Self, F, B> ⓘ
fn try_fold<T, E, F, B>( &mut self, init: B, f: F, ) -> TryFoldFuture<'_, Self, F, B> ⓘ
Source§fn scan<St, B, F>(self, initial_state: St, f: F) -> Scan<Self, St, F>
fn scan<St, B, F>(self, initial_state: St, f: F) -> Scan<Self, St, F>
Source§fn enumerate(self) -> Enumerate<Self>where
Self: Sized,
fn enumerate(self) -> Enumerate<Self>where
Self: Sized,
(index, item). Read moreSource§fn inspect<F>(self, f: F) -> Inspect<Self, F>
fn inspect<F>(self, f: F) -> Inspect<Self, F>
Source§fn nth(&mut self, n: usize) -> NthFuture<'_, Self> ⓘwhere
Self: Unpin,
fn nth(&mut self, n: usize) -> NthFuture<'_, Self> ⓘwhere
Self: Unpin,
nth item of the stream. Read moreSource§fn last(self) -> LastFuture<Self> ⓘwhere
Self: Sized,
fn last(self) -> LastFuture<Self> ⓘwhere
Self: Sized,
Source§fn find<P>(&mut self, predicate: P) -> FindFuture<'_, Self, P> ⓘ
fn find<P>(&mut self, predicate: P) -> FindFuture<'_, Self, P> ⓘ
Source§fn find_map<F, B>(&mut self, f: F) -> FindMapFuture<'_, Self, F> ⓘ
fn find_map<F, B>(&mut self, f: F) -> FindMapFuture<'_, Self, F> ⓘ
Source§fn position<P>(&mut self, predicate: P) -> PositionFuture<'_, Self, P> ⓘ
fn position<P>(&mut self, predicate: P) -> PositionFuture<'_, Self, P> ⓘ
Source§fn for_each<F>(self, f: F) -> ForEachFuture<Self, F> ⓘ
fn for_each<F>(self, f: F) -> ForEachFuture<Self, F> ⓘ
Source§fn try_for_each<F, E>(&mut self, f: F) -> TryForEachFuture<'_, Self, F> ⓘ
fn try_for_each<F, E>(&mut self, f: F) -> TryForEachFuture<'_, Self, F> ⓘ
Source§fn zip<U>(self, other: U) -> Zip<Self, U>
fn zip<U>(self, other: U) -> Zip<Self, U>
Source§fn unzip<A, B, FromA, FromB>(self) -> UnzipFuture<Self, FromA, FromB> ⓘ
fn unzip<A, B, FromA, FromB>(self) -> UnzipFuture<Self, FromA, FromB> ⓘ
Source§fn race<S>(self, other: S) -> Race<Self, S>
fn race<S>(self, other: S) -> Race<Self, S>
race and std only.other stream, with no preference for either stream when both are ready. Read moreSource§fn drain(&mut self) -> Drain<'_, Self>
fn drain(&mut self) -> Drain<'_, Self>
Source§impl<T> Tap for T
impl<T> Tap for T
Source§fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
Borrow<B> of a value. Read moreSource§fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
BorrowMut<B> of a value. Read moreSource§fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
AsRef<R> view of a value. Read moreSource§fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
AsMut<R> view of a value. Read moreSource§fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
Deref::Target of a value. Read moreSource§fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
Deref::Target of a value. Read moreSource§fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
.tap() only in debug builds, and is erased in release builds.Source§fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
.tap_mut() only in debug builds, and is erased in release
builds.Source§fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
.tap_borrow() only in debug builds, and is erased in release
builds.Source§fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
.tap_borrow_mut() only in debug builds, and is erased in release
builds.Source§fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
.tap_ref() only in debug builds, and is erased in release
builds.Source§fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
.tap_ref_mut() only in debug builds, and is erased in release
builds.Source§fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
.tap_deref() only in debug builds, and is erased in release
builds.