Skip to main content

sofie_model/
gateways.rs

1use std::collections::BTreeMap;
2use std::path::{Path, PathBuf};
3
4use syn::{Item, ItemTrait};
5
6use crate::config::settings_keys;
7use crate::handlers::HandlerScope;
8use crate::naming::snake_case;
9use crate::provisions::{ImplSite, RootExports};
10use crate::syntax::{has_attribute, line_of};
11use crate::{ConfigKey, ModelError};
12
13#[derive(Clone, Debug, PartialEq, Eq)]
14pub struct Gateway {
15    pub name: String,
16    pub path: Vec<String>,
17    pub adapters: Vec<GatewayAdapter>,
18    pub file: PathBuf,
19    pub line: usize,
20}
21
22#[derive(Clone, Debug, PartialEq, Eq)]
23pub struct GatewayAdapter {
24    pub ty: Vec<String>,
25    pub is_adapter: bool,
26    pub vendor: Option<String>,
27    pub settings: Vec<ConfigKey>,
28    pub file: PathBuf,
29    pub line: usize,
30}
31
32pub(crate) struct GatewayTrait {
33    path: Vec<String>,
34    file: PathBuf,
35    line: usize,
36}
37
38pub(crate) fn gateway_trait(
39    item: &Item,
40    file: &Path,
41    module_path: &[String],
42) -> Option<GatewayTrait> {
43    let Item::Trait(ItemTrait { attrs, ident, .. }) = item else {
44        return None;
45    };
46    has_attribute(attrs, "gateway").then(|| GatewayTrait {
47        path: [module_path, &[ident.to_string()]].concat(),
48        file: file.to_path_buf(),
49        line: line_of(item),
50    })
51}
52
53pub(crate) fn gateways(
54    traits: Vec<GatewayTrait>,
55    sites: &[ImplSite],
56    syntax: &BTreeMap<PathBuf, syn::File>,
57    exports: &RootExports,
58    module: &str,
59) -> Result<Vec<Gateway>, ModelError> {
60    let resolved: Vec<(Vec<String>, Vec<String>, &ImplSite)> = sites
61        .iter()
62        .map(|site| {
63            let items = syntax.get(&site.file).map_or(&[][..], |file| &file.items);
64            let scope = HandlerScope::new(site.module_path.clone(), items);
65            (scope.callee(&site.trait_path), scope.within(&site.ty), site)
66        })
67        .collect();
68    let adapters: Vec<(&Vec<String>, &ImplSite)> = resolved
69        .iter()
70        .filter(|(trait_path, _, _)| trait_path.last().is_some_and(|name| name == "Adapter"))
71        .map(|(_, ty, site)| (ty, *site))
72        .collect();
73    let mut gateways = Vec::new();
74    for found in traits {
75        let mut implementations = Vec::new();
76        for (_, ty, site) in resolved.iter().filter(|(path, _, _)| *path == found.path) {
77            let adapter = adapters.iter().find(|(adapter, _)| *adapter == ty);
78            let vendor = adapter.and_then(|(_, adapter)| adapter.vendor.clone());
79            let settings = match (adapter, &vendor) {
80                (Some((_, adapter)), Some(vendor)) => {
81                    let prefix = format!("{module}_{vendor}").to_ascii_uppercase();
82                    settings_keys(syntax, adapter.settings.as_deref(), &prefix)?
83                }
84                _ => Vec::new(),
85            };
86            implementations.push(GatewayAdapter {
87                ty: exports.public(ty),
88                is_adapter: adapter.is_some(),
89                vendor,
90                settings,
91                file: site.file.clone(),
92                line: site.line,
93            });
94        }
95        gateways.push(Gateway {
96            name: snake_case(found.path.last().map_or("", String::as_str)),
97            adapters: implementations,
98            path: exports.public(&found.path),
99            file: found.file,
100            line: found.line,
101        });
102    }
103    Ok(gateways)
104}