1use crate::Task;
2use linkme::distributed_slice;
3use std::sync::OnceLock;
4
5#[non_exhaustive]
7pub enum Compat {
8 NamedGlobal { name: &'static str, spawn: fn(Task) },
10 #[doc(hidden)]
12 #[deprecated(since = "1.0.3", note = "use NamedGlobal instead")]
13 Global(fn(Task)),
14 #[allow(clippy::type_complexity)]
15 Local(fn() -> Option<fn(Task)>),
17}
18
19#[distributed_slice]
57pub static COMPATS: [Compat] = [..];
58
59#[derive(Clone, Copy)]
60pub(crate) enum Failure {
61 NotFound,
62 #[allow(dead_code)]
63 MultipleGlobals,
64}
65
66fn pick_global(choose: Option<&str>) -> Result<fn(Task), Failure> {
67 let mut globals = 0;
68 let mut last_named = None;
69 let mut last_unnamed = None;
70 match COMPATS.iter().find_map(|compat| match compat {
71 Compat::Local(_) => None,
72 #[allow(deprecated)]
73 Compat::Global(global) => {
74 globals += 1;
75 last_unnamed = Some(global);
76 None
77 }
78 Compat::NamedGlobal { spawn, name } => {
79 if choose == Some(name) {
80 Some(spawn)
81 } else {
82 globals += 1;
83 last_named = Some(spawn);
84 None
85 }
86 }
87 }) {
88 Some(spawn) => Ok(*spawn),
89 None => {
90 #[cfg(feature = "panic-multiple-global-spawners")]
91 if globals > 1 {
92 return Err(Failure::MultipleGlobals);
93 }
94 last_named
95 .or(last_unnamed)
96 .ok_or(Failure::NotFound)
97 .copied()
98 }
99 }
100}
101
102fn find_global() -> Result<fn(Task), Failure> {
103 static FOUND: OnceLock<Result<fn(Task), Failure>> = OnceLock::new();
104 if let Some(found) = FOUND.get() {
105 return *found;
106 }
107 let choose = std::env::var("SPAWNS_GLOBAL_SPAWNER").ok();
108 let result = pick_global(choose.as_deref());
109 *FOUND.get_or_init(|| result)
110}
111
112fn find_local() -> Option<fn(Task)> {
113 COMPATS.iter().find_map(|compat| match compat {
114 Compat::Local(local) => local(),
115 #[allow(deprecated)]
116 Compat::Global(_) => None,
117 Compat::NamedGlobal { .. } => None,
118 })
119}
120
121pub(crate) fn find_spawn() -> Option<fn(Task)> {
122 match COMPATS.len() {
123 0 => return None,
124 1 => match COMPATS[0] {
125 Compat::NamedGlobal { spawn, .. } => return Some(spawn),
126 #[allow(deprecated)]
127 Compat::Global(spawn) => return Some(spawn),
128 Compat::Local(local) => return local(),
129 },
130 _ => {}
131 }
132 match find_local()
133 .ok_or(Failure::NotFound)
134 .or_else(|_| find_global())
135 {
136 Ok(spawn) => Some(spawn),
137 Err(Failure::NotFound) => None,
138 Err(Failure::MultipleGlobals) => panic!("multiple global spawners"),
139 }
140}