Skip to main content

hara_native/kernel/
generated.rs

1use std::collections::{HashMap, HashSet};
2
3use super::Form;
4
5const FOUNDATION_LIBRARIES: &[(&str, &str, &str)] = &[
6    ("string", "std.foundation.string", "str"),
7    ("promise", "std.foundation.promise", "promise"),
8    ("bytes", "std.foundation.bytes", "bytes"),
9    ("coroutine", "std.foundation.coroutine", "co"),
10    ("pretty", "std.foundation.pretty", "pretty"),
11];
12
13pub(crate) fn foundation_library_alias(library: &str) -> Option<&'static str> {
14    FOUNDATION_LIBRARIES
15        .iter()
16        .find(|(name, _, _)| *name == library)
17        .map(|(_, _, alias)| *alias)
18}
19
20#[path = "generated/rewrite.rs"]
21mod rewrite;
22
23#[derive(Debug, Clone, Default)]
24pub struct GeneratedNamespaceConfig {
25    aliases: HashMap<String, String>,
26    global_alias: Option<String>,
27    global_aliases: HashMap<String, String>,
28    declared_global_imports: Vec<String>,
29    lazy_aliases: HashMap<String, String>,
30    refers: HashMap<String, String>,
31    macro_refers: HashMap<String, String>,
32    required_namespaces: Vec<String>,
33    used_namespaces: Vec<String>,
34    used_exclusions: HashMap<String, HashSet<String>>,
35    internal_access: HashSet<String>,
36    excluded_foundation_libraries: HashSet<String>,
37    excluded_foundation: HashSet<String>,
38    exposed_foundation: Option<HashSet<String>>,
39    native_flavor: Option<String>,
40    native_imports: Vec<(String, String)>,
41    native_flavor_imports: Vec<(String, String)>,
42    role: String,
43    blank: bool,
44}
45
46impl GeneratedNamespaceConfig {
47    pub fn defaults() -> Self {
48        Self {
49            aliases: HashMap::new(),
50            global_alias: None,
51            global_aliases: HashMap::new(),
52            declared_global_imports: Vec::new(),
53            lazy_aliases: HashMap::new(),
54            refers: HashMap::new(),
55            macro_refers: HashMap::new(),
56            required_namespaces: Vec::new(),
57            used_namespaces: Vec::new(),
58            used_exclusions: HashMap::new(),
59            internal_access: HashSet::new(),
60            excluded_foundation_libraries: HashSet::new(),
61            excluded_foundation: HashSet::new(),
62            exposed_foundation: None,
63            native_flavor: None,
64            native_imports: Vec::new(),
65            native_flavor_imports: Vec::new(),
66            role: "standard".into(),
67            blank: false,
68        }
69    }
70
71    pub fn configure(clauses: &[Form]) -> Result<Self, String> {
72        Self::configure_with(clauses, known_namespace)
73    }
74
75    pub fn configure_with(
76        clauses: &[Form],
77        available: impl Fn(&str) -> bool,
78    ) -> Result<Self, String> {
79        let mut excluded = HashSet::new();
80        let mut overrides = HashMap::new();
81        let mut requires = Vec::new();
82        let mut uses = Vec::new();
83        let mut excluded_foundation = HashSet::new();
84        let mut exposed_foundation = None;
85        let mut override_seen = false;
86        let mut blank = false;
87        let mut config_seen = false;
88        let mut native_flavor = None;
89        let mut native_flavor_imports = Vec::new();
90        let mut native_imports = Vec::new();
91        let mut role = "standard".to_owned();
92        let mut global_alias = None;
93        let mut declared_global_imports = Vec::new();
94
95        for clause in clauses {
96            let values = list(clause, "ns clauses must be non-empty lists")?;
97            let head = values.first().ok_or("ns clauses must be non-empty lists")?;
98            let name = keyword(head, "ns clause must start with a keyword")?;
99            match name {
100                "config" => {
101                    if config_seen {
102                        return Err("ns accepts only one :config clause".into());
103                    }
104                    config_seen = true;
105                    if values.len() != 2 {
106                        return Err(":config expects one map".into());
107                    }
108                    parse_config(
109                        &values[1],
110                        &mut blank,
111                        &mut excluded_foundation,
112                        &mut exposed_foundation,
113                        &mut override_seen,
114                        &mut excluded,
115                        &mut overrides,
116                        &mut role,
117                        &mut global_alias,
118                        &mut declared_global_imports,
119                    )?;
120                }
121                "require" => requires.extend(values[1..].iter().cloned()),
122                "use" => uses.extend(values[1..].iter().cloned()),
123                "flavor" => {
124                    if native_flavor.is_some() {
125                        return Err("ns accepts only one :flavor clause".into());
126                    }
127                    let (flavor, imports) = parse_native_flavor(values)?;
128                    native_flavor = Some(flavor);
129                    native_flavor_imports = imports;
130                }
131                "import" => parse_native_imports(&values[1..], &mut native_imports)?,
132                other => return Err(format!("Unsupported ns clause: :{other}")),
133            }
134        }
135
136        if blank && override_seen {
137            return Err(":config :blank true cannot be combined with :override".into());
138        }
139        if blank && exposed_foundation.is_some() {
140            return Err(":config :blank true cannot be combined with :only".into());
141        }
142        if override_seen && exposed_foundation.is_some() {
143            return Err(":config :override cannot be combined with :only".into());
144        }
145
146        for library in overrides.keys() {
147            if excluded.contains(library) {
148                return Err(format!(
149                    "Foundation library cannot be both excluded and aliased: {library}"
150                ));
151            }
152        }
153
154        let mut config = Self::default();
155        config.excluded_foundation_libraries = excluded.clone();
156        config.excluded_foundation = excluded_foundation;
157        config.exposed_foundation = exposed_foundation;
158        config.global_alias = global_alias;
159        config.declared_global_imports = declared_global_imports;
160        config.native_flavor = native_flavor;
161        config.native_imports = native_imports;
162        config.native_flavor_imports = native_flavor_imports;
163        config.role = role;
164        config.blank = blank;
165        for (library, namespace, _) in FOUNDATION_LIBRARIES {
166            if excluded.contains(*library) {
167                continue;
168            }
169            if let Some(alias) = overrides.get(*library) {
170                config.put_alias(alias, namespace)?;
171            }
172        }
173        for require in requires {
174            config.apply_require(&require, &available)?;
175        }
176        for use_form in uses {
177            config.apply_use(&use_form, &available)?;
178        }
179        Ok(config)
180    }
181
182    pub fn required_namespaces(&self) -> &[String] {
183        &self.required_namespaces
184    }
185
186    pub fn lazy_target(&self, alias: &str) -> Option<&str> {
187        self.lazy_aliases.get(alias).map(String::as_str)
188    }
189
190    pub fn used_namespaces(&self) -> &[String] {
191        &self.used_namespaces
192    }
193
194    pub fn used_symbol_excluded(&self, namespace: &str, symbol: &str) -> bool {
195        self.used_exclusions
196            .get(namespace)
197            .is_some_and(|excluded| excluded.contains(symbol))
198    }
199
200    pub fn internal_access(&self) -> &HashSet<String> {
201        &self.internal_access
202    }
203
204    pub fn excluded_foundation(&self) -> &HashSet<String> {
205        &self.excluded_foundation
206    }
207
208    pub fn excluded_foundation_libraries(&self) -> &HashSet<String> {
209        &self.excluded_foundation_libraries
210    }
211
212    pub fn exposed_foundation(&self) -> Option<&HashSet<String>> {
213        self.exposed_foundation.as_ref()
214    }
215
216    pub fn blank(&self) -> bool {
217        self.blank
218    }
219
220    pub fn native_flavor(&self) -> Option<&str> {
221        self.native_flavor.as_deref()
222    }
223
224    pub fn native_imports(&self) -> &[(String, String)] {
225        &self.native_imports
226    }
227
228    pub fn native_flavor_imports(&self) -> &[(String, String)] {
229        &self.native_flavor_imports
230    }
231
232    pub fn role(&self) -> &str {
233        &self.role
234    }
235
236    pub fn aliases(&self) -> Vec<(String, String)> {
237        self.aliases
238            .iter()
239            .map(|(alias, namespace)| (alias.clone(), namespace.clone()))
240            .collect()
241    }
242
243    pub fn global_alias(&self) -> Option<&str> {
244        self.global_alias.as_deref()
245    }
246
247    pub fn declared_global_imports(&self) -> &[String] {
248        &self.declared_global_imports
249    }
250
251    pub fn set_global_aliases(&mut self, aliases: impl IntoIterator<Item = (String, String)>) {
252        self.global_aliases = aliases.into_iter().collect();
253    }
254
255    fn put_alias(&mut self, alias: &str, namespace: &str) -> Result<(), String> {
256        if alias.is_empty() {
257            return Err("Namespace alias cannot be empty".into());
258        }
259        if alias == "-" {
260            return Err("Namespace alias is reserved: -".into());
261        }
262        if let Some(native_namespace) = crate::core::canonical_native_symbol(alias) {
263            return Err(format!(
264                "Namespace alias already refers to {native_namespace}: {alias}"
265            ));
266        }
267        if let Some(previous) = self.aliases.get(alias) {
268            if previous != namespace {
269                return Err(format!(
270                    "Namespace alias already refers to {previous}: {alias}"
271                ));
272            }
273            return Ok(());
274        }
275        self.aliases.insert(alias.into(), namespace.into());
276        Ok(())
277    }
278
279    pub fn apply_require(
280        &mut self,
281        form: &Form,
282        available: &impl Fn(&str) -> bool,
283    ) -> Result<(), String> {
284        let (target, options) = match form {
285            Form::Vector(items) => {
286                let target = match items.first() {
287                    Some(Form::Symbol(target)) => target.as_str(),
288                    _ => return Err(":require namespace must be a symbol".into()),
289                };
290                (normalize_namespace(target), &items[1..])
291            }
292            Form::List(items)
293                if items.len() == 2
294                    && matches!(&items[0], Form::Symbol(q) if q == "quote")
295                    && matches!(&items[1], Form::Symbol(_)) =>
296            {
297                let target = match &items[1] {
298                    Form::Symbol(target) => target.as_str(),
299                    _ => unreachable!(),
300                };
301                (normalize_namespace(target), &[][..])
302            }
303            _ => return Err(":require expects vectors such as [hara.lib.string :as str]".into()),
304        };
305        if !known_namespace(target) && !available(target) {
306            return Err(format!(
307                "Cannot require missing generated namespace: {target}"
308            ));
309        }
310        if options.len() % 2 != 0 {
311            return Err(format!("Malformed :require options for {target}"));
312        }
313        let lazy = options.chunks(2).any(|option| {
314            matches!(&option[0], Form::Keyword(name) if name == "lazy")
315                && matches!(&option[1], Form::Bool(true))
316        });
317        let has_alias = options
318            .chunks(2)
319            .any(|option| matches!(&option[0], Form::Keyword(name) if name == "as"));
320        if lazy && !has_alias {
321            return Err(":require :lazy requires :as".into());
322        }
323        if !lazy && !self.required_namespaces.iter().any(|value| value == target) {
324            self.required_namespaces.push(target.into());
325        }
326        for option in options.chunks(2) {
327            let name = keyword(&option[0], "Malformed :require options")?;
328            match name {
329                "as" => {
330                    let alias = symbol(&option[1], ":require :as expects an unqualified symbol")?;
331                    if alias.contains('/') {
332                        return Err(":require :as expects an unqualified symbol".into());
333                    }
334                    self.put_alias(alias, target)?;
335                    if lazy {
336                        self.lazy_aliases.insert(alias.into(), target.into());
337                    }
338                }
339                "refer" => {
340                    if lazy {
341                        return Err(":require :lazy cannot be combined with :refer".into());
342                    }
343                    if matches!(&option[1], Form::Keyword(name) if name == "all") {
344                        if !self.used_namespaces.iter().any(|value| value == target) {
345                            self.used_namespaces.push(target.into());
346                        }
347                        continue;
348                    }
349                    let names = vector(
350                        &option[1],
351                        ":require :refer expects a vector of symbols or :all",
352                    )?;
353                    for value in names {
354                        let name = symbol(value, ":require :refer expects unqualified symbols")?;
355                        if qualified_symbol(name) {
356                            return Err(":require :refer expects unqualified symbols".into());
357                        }
358                        let canonical = canonical(target, name);
359                        if let Some(previous) = self.refers.insert(name.into(), canonical) {
360                            return Err(format!(
361                                "Referred symbol already exists: {name} ({previous})"
362                            ));
363                        }
364                    }
365                }
366                "refer-macros" => {
367                    if lazy {
368                        return Err(":require :lazy cannot be combined with :refer-macros".into());
369                    }
370                    let names = vector(
371                        &option[1],
372                        ":require :refer-macros expects a vector of symbols",
373                    )?;
374                    for value in names {
375                        let name =
376                            symbol(value, ":require :refer-macros expects unqualified symbols")?;
377                        if qualified_symbol(name) {
378                            return Err(":require :refer-macros expects unqualified symbols".into());
379                        }
380                        let canonical = canonical(target, name);
381                        if let Some(previous) =
382                            self.macro_refers.insert(name.into(), canonical.clone())
383                        {
384                            if previous != canonical {
385                                return Err(format!(
386                                    "Referred macro already exists: {name} ({previous})"
387                                ));
388                            }
389                        }
390                    }
391                }
392                "lazy" => {
393                    if !matches!(&option[1], Form::Bool(true)) {
394                        return Err(":require :lazy expects true".into());
395                    }
396                }
397                "reload" => {
398                    if !matches!(&option[1], Form::Bool(true)) {
399                        return Err(":require :reload expects true".into());
400                    }
401                }
402                "access" => {
403                    if !matches!(&option[1], Form::Bool(true)) {
404                        return Err(":require :access expects true".into());
405                    }
406                    self.internal_access.insert(target.into());
407                }
408                "exclude" => {
409                    let names =
410                        vector(&option[1], ":require :exclude expects a vector of symbols")?;
411                    for value in names {
412                        let name = symbol(value, ":require :exclude expects unqualified symbols")?;
413                        if qualified_symbol(name) {
414                            return Err(":require :exclude expects unqualified symbols".into());
415                        }
416                        self.used_exclusions
417                            .entry(target.into())
418                            .or_default()
419                            .insert(name.into());
420                        if target == "std.foundation" {
421                            self.excluded_foundation.insert(name.into());
422                        }
423                    }
424                }
425                other => return Err(format!("Unsupported :require option: :{other}")),
426            }
427        }
428        Ok(())
429    }
430
431    pub fn apply_use(
432        &mut self,
433        form: &Form,
434        available: &impl Fn(&str) -> bool,
435    ) -> Result<(), String> {
436        let target = match form {
437            Form::Symbol(target) if !target.contains('/') => normalize_namespace(target),
438            _ => return Err(":use expects unqualified namespace symbols".into()),
439        };
440        if !known_namespace(target) && !available(target) {
441            return Err(format!("Cannot use missing generated namespace: {target}"));
442        }
443        if !self.required_namespaces.iter().any(|value| value == target) {
444            self.required_namespaces.push(target.into());
445        }
446        if !self.used_namespaces.iter().any(|value| value == target) {
447            self.used_namespaces.push(target.into());
448        }
449        Ok(())
450    }
451}
452
453fn parse_native_flavor(values: &[Form]) -> Result<(String, Vec<(String, String)>), String> {
454    let flavor = match values.get(1) {
455        Some(Form::Keyword(flavor)) if !flavor.contains('/') && flavor != "wasm" => flavor,
456        Some(Form::Keyword(flavor)) if flavor == "wasm" => {
457            return Err("native/unsupported-flavor: :wasm (Wasm modules use :import)".into())
458        }
459        Some(Form::Keyword(flavor)) => return Err(format!("native/invalid-flavor: :{flavor}")),
460        _ => return Err(":flavor expects an unqualified host keyword".into()),
461    };
462    Err(format!(
463        "native/unsupported-flavor: :{flavor} (host flavors are only available on JVM/.NET runtimes)"
464    ))
465}
466
467fn parse_native_imports(
468    specifications: &[Form],
469    imports: &mut Vec<(String, String)>,
470) -> Result<(), String> {
471    for specification in specifications {
472        match specification {
473            Form::Symbol(module) if !module.contains('/') => {
474                imports.push((module.clone(), module.clone()));
475            }
476            Form::Vector(values) if !values.is_empty() => {
477                let package = match &values[0] {
478                    Form::Symbol(package) if !package.contains('/') => package,
479                    _ => return Err(":import package must be a symbol".into()),
480                };
481                if values.len() == 1 {
482                    return Err(":import package vector requires at least one module".into());
483                }
484                for module in &values[1..] {
485                    let module = match module {
486                        Form::Symbol(module) if !module.contains('/') && !module.contains('.') => {
487                            module
488                        }
489                        _ => return Err(":import module must be an unqualified symbol".into()),
490                    };
491                    imports.push((module.clone(), format!("{package}.{module}")));
492                }
493            }
494            _ => return Err(":import expects module symbols or package vectors".into()),
495        }
496    }
497    Ok(())
498}
499
500fn parse_config(
501    form: &Form,
502    blank: &mut bool,
503    foundation_overrides: &mut HashSet<String>,
504    foundation_exposure: &mut Option<HashSet<String>>,
505    override_seen: &mut bool,
506    excluded: &mut HashSet<String>,
507    overrides: &mut HashMap<String, String>,
508    role: &mut String,
509    global_alias: &mut Option<String>,
510    declared_global_imports: &mut Vec<String>,
511) -> Result<(), String> {
512    let options = match form {
513        Form::Map(options) => options,
514        _ => return Err(":config expects one map".into()),
515    };
516    for (key, value) in options {
517        match keyword(key, ":config keys must be unqualified keywords")? {
518            "blank" => {
519                *blank = match value {
520                    Form::Bool(value) => *value,
521                    _ => return Err(":config :blank expects a boolean".into()),
522                };
523            }
524            "override" => {
525                *override_seen = true;
526                for item in vector(
527                    value,
528                    ":config :override expects a vector of unqualified symbols",
529                )? {
530                    let name = symbol(
531                        item,
532                        ":config :override expects a vector of unqualified symbols",
533                    )?;
534                    if qualified_symbol(name) {
535                        return Err(
536                            ":config :override expects a vector of unqualified symbols".into()
537                        );
538                    }
539                    if !foundation_overrides.insert(name.into()) {
540                        return Err(format!("Duplicate Foundation override: {name}"));
541                    }
542                }
543            }
544            "only" => {
545                let mut exposed = HashSet::new();
546                for item in vector(
547                    value,
548                    ":config :only expects a vector of unqualified symbols",
549                )? {
550                    let name = symbol(
551                        item,
552                        ":config :only expects a vector of unqualified symbols",
553                    )?;
554                    if qualified_symbol(name) {
555                        return Err(
556                            ":config :only expects a vector of unqualified symbols".into()
557                        );
558                    }
559                    if !exposed.insert(name.into()) {
560                        return Err(format!("Duplicate Foundation selection: {name}"));
561                    }
562                }
563                *foundation_exposure = Some(exposed);
564            }
565            "rename" => {
566                parse_rename(value, excluded, overrides)?;
567            }
568            "role" => {
569                let value = keyword(
570                    value,
571                    ":config :role expects :default, :internal, or :facade",
572                )?;
573                if !matches!(value, "default" | "internal" | "facade") {
574                    return Err(":config :role expects :default, :internal, or :facade".into());
575                }
576                *role = if value == "default" {
577                    "standard".to_owned()
578                } else {
579                    value.to_owned()
580                };
581            }
582            "set-global-alias" => {
583                let value = symbol(
584                    value,
585                    ":config :set-global-alias expects an unqualified symbol",
586                )?;
587                if qualified_symbol(value) {
588                    return Err(":config :set-global-alias expects an unqualified symbol".into());
589                }
590                if value == "-" {
591                    return Err(":config :set-global-alias is reserved: -".into());
592                }
593                *global_alias = Some(value.to_owned());
594            }
595            "set-global" => {
596                for item in vector(
597                    value,
598                    ":config :set-global expects a vector of qualified Vars",
599                )? {
600                    let name = symbol(
601                        item,
602                        ":config :set-global expects a vector of qualified Vars",
603                    )?;
604                    if !qualified_symbol(name) {
605                        return Err(":config :set-global expects qualified Vars".into());
606                    }
607                    if declared_global_imports.iter().any(|value| value == name) {
608                        return Err(format!("Duplicate global import: {name}"));
609                    }
610                    declared_global_imports.push(name.into());
611                }
612            }
613            other => return Err(format!("Unsupported :config option: :{other}")),
614        }
615    }
616    Ok(())
617}
618
619fn parse_rename(
620    form: &Form,
621    excluded: &mut HashSet<String>,
622    overrides: &mut HashMap<String, String>,
623) -> Result<(), String> {
624    if matches!(form, Form::Keyword(name) if name == "all") {
625        return Ok(());
626    }
627    let options = match form {
628        Form::Map(options) => options,
629        _ => return Err(":rename expects :all or an options map".into()),
630    };
631    for (key, value) in options {
632        match keyword(key, ":rename option keys must be keywords")? {
633            "exclude" => {
634                for item in vector(
635                    value,
636                    ":rename :exclude expects a vector of library symbols",
637                )? {
638                    let library = library(symbol(
639                        item,
640                        ":rename :exclude expects unqualified library symbols",
641                    )?)?;
642                    if !excluded.insert(library.into()) {
643                        return Err(format!("Duplicate Foundation library exclusion: {library}"));
644                    }
645                }
646            }
647            "alias" => {
648                let aliases = match value {
649                    Form::Map(aliases) => aliases,
650                    _ => return Err(":rename :alias expects a map".into()),
651                };
652                for (library_form, alias_form) in aliases {
653                    let library = library(symbol(
654                        library_form,
655                        ":rename :alias expects library symbols",
656                    )?)?;
657                    let alias =
658                        symbol(alias_form, "Foundation library aliases must be unqualified symbols")?;
659                    if alias.contains('/') {
660                        return Err("Foundation library aliases must be unqualified symbols".into());
661                    }
662                    if overrides.insert(library.into(), alias.into()).is_some() {
663                        return Err(format!("Duplicate Foundation library alias: {library}"));
664                    }
665                }
666            }
667            other => return Err(format!("Unsupported :config :rename option: :{other}")),
668        }
669    }
670    Ok(())
671}
672
673fn list<'a>(form: &'a Form, error: &str) -> Result<&'a [Form], String> {
674    match form {
675        Form::List(values) => Ok(values),
676        _ => Err(error.into()),
677    }
678}
679fn vector<'a>(form: &'a Form, error: &str) -> Result<&'a [Form], String> {
680    match form {
681        Form::Vector(values) => Ok(values),
682        _ => Err(error.into()),
683    }
684}
685fn keyword<'a>(form: &'a Form, error: &str) -> Result<&'a str, String> {
686    match form {
687        Form::Keyword(value) => Ok(value),
688        _ => Err(error.into()),
689    }
690}
691fn symbol<'a>(form: &'a Form, error: &str) -> Result<&'a str, String> {
692    match form {
693        Form::Symbol(value) => Ok(value),
694        _ => Err(error.into()),
695    }
696}
697fn qualified_symbol(value: &str) -> bool {
698    value != "/" && value.contains('/')
699}
700fn library(value: &str) -> Result<&str, String> {
701    if value.contains('/') {
702        return Err("Foundation library names must be unqualified symbols".into());
703    }
704    FOUNDATION_LIBRARIES
705        .iter()
706        .find(|(library, _, _)| *library == value)
707        .map(|(library, _, _)| *library)
708        .ok_or_else(|| format!("Unknown Foundation library: {value}"))
709}
710pub(crate) fn normalize_namespace(value: &str) -> &str {
711    match value {
712        "core" | "hara.lib.core" => "std.foundation",
713        "hara.lib.string" => "std.foundation.string",
714        "hara.lib.promise" => "std.foundation.promise",
715        "hara.lib.bytes" => "std.foundation.bytes",
716        "hara.lib.socket" => "std.native.Socket",
717        "hara.lib.file" => "std.native.File",
718        value => value,
719    }
720}
721pub(crate) fn known_namespace(value: &str) -> bool {
722    let value = normalize_namespace(value);
723    value == "std.foundation"
724        || value == "std.foundation.coroutine"
725        || value == "std.native"
726        || value.starts_with("std.native.")
727        || FOUNDATION_LIBRARIES
728            .iter()
729            .any(|(_, namespace, _)| *namespace == value)
730}
731fn canonical(namespace: &str, method: &str) -> String {
732    let namespace = normalize_namespace(namespace);
733    if namespace.starts_with("std.native.") {
734        return format!("{namespace}/{method}");
735    }
736    if namespace == "std.foundation" {
737        return format!("std.foundation/{method}");
738    }
739    // Coroutine operations are evaluator control forms, not ordinary HAL
740    // function calls. Keep their canonical names so `co/yield` and
741    // `co/await` remain visible to the fiber evaluator instead of routing
742    // through the synchronous Foundation wrapper namespace.
743    if namespace == "std.foundation.coroutine" {
744        return format!("std.foundation.coroutine/{method}");
745    }
746    if FOUNDATION_LIBRARIES
747        .iter()
748        .any(|(_, library_namespace, _)| *library_namespace == namespace)
749    {
750        return format!("{namespace}/{method}");
751    }
752    match (namespace, method) {
753        ("std.foundation", method) => method.into(),
754        ("std.lib.string", method) => format!("str/{method}"),
755        ("std.lib.promise", "then") => "promise/then".into(),
756        ("std.lib.promise", "catch") => "promise/catch".into(),
757        ("std.lib.promise", method) => format!("promise/{method}"),
758        ("std.lib.bytes", method) => format!("bytes/{method}"),
759        ("std.lib.socket", method) => format!("socket/{method}"),
760        ("std.lib.file", method) => format!("file/{method}"),
761        (namespace, method) => format!("{namespace}/{method}"),
762    }
763}
764
765#[cfg(test)]
766#[path = "generated/tests.rs"]
767mod tests;