Skip to main content

es_fluent_shared/
registry.rs

1//! This module provides types for representing FTL variants and type information.
2
3use crate::meta::TypeKind;
4pub use crate::namespace::NamespaceRule;
5use std::convert::AsRef;
6use std::path::Path;
7
8/// A variant representing a single FTL key entry.
9#[derive(Clone, Debug, Eq, Hash, PartialEq)]
10pub struct FtlVariant {
11    pub name: &'static str,
12    pub ftl_key: &'static str,
13    pub args: &'static [&'static str],
14    /// The module path from `module_path!()`.
15    pub module_path: &'static str,
16    /// The line number from `line!()` macro.
17    pub line: u32,
18}
19
20/// Type information for FTL registration, used by derive macros and the CLI.
21#[derive(Clone, Debug, Eq, Hash, PartialEq)]
22pub struct FtlTypeInfo {
23    pub type_kind: TypeKind,
24    pub type_name: &'static str,
25    pub variants: &'static [FtlVariant],
26    /// The file path where this type is defined (from `file!()` macro).
27    pub file_path: &'static str,
28    /// The module path where this type is defined (from `module_path!()` macro).
29    pub module_path: &'static str,
30    /// Optional namespace for FTL file output. If Some, the type will be written to
31    /// `{lang}/{crate}/{namespace}.ftl` instead of `{lang}/{crate}.ftl`.
32    pub namespace: Option<NamespaceRule>,
33}
34
35impl AsRef<FtlTypeInfo> for FtlTypeInfo {
36    fn as_ref(&self) -> &FtlTypeInfo {
37        self
38    }
39}
40
41impl FtlTypeInfo {
42    /// Resolve the namespace for this type, if configured.
43    pub fn resolved_namespace(&self, manifest_dir: &Path) -> Option<String> {
44        self.namespace
45            .as_ref()
46            .map(|rule| rule.resolve(self.file_path, Some(manifest_dir)))
47    }
48}
49
50#[cfg(test)]
51mod tests {
52    use super::NamespaceRule;
53    use std::path::PathBuf;
54
55    fn test_manifest_dir() -> PathBuf {
56        if cfg!(windows) {
57            PathBuf::from(r"C:\repo\app")
58        } else {
59            PathBuf::from("/repo/app")
60        }
61    }
62
63    #[test]
64    fn file_namespace_uses_stem() {
65        let manifest_dir = test_manifest_dir();
66        let file_path = manifest_dir.join("src").join("lib.rs");
67        let namespace = NamespaceRule::File.resolve(
68            file_path.to_str().expect("utf-8 test path"),
69            Some(&manifest_dir),
70        );
71        assert_eq!(namespace, "lib");
72    }
73
74    #[test]
75    fn file_relative_strips_manifest_and_src() {
76        let manifest_dir = test_manifest_dir();
77        let file_path = manifest_dir.join("src").join("ui").join("button.rs");
78        let namespace = NamespaceRule::FileRelative.resolve(
79            file_path.to_str().expect("utf-8 test path"),
80            Some(&manifest_dir),
81        );
82        assert_eq!(namespace, "ui/button");
83    }
84
85    #[test]
86    fn file_relative_falls_back_to_stem_outside_manifest() {
87        let manifest_dir = test_manifest_dir();
88        let file_path = if cfg!(windows) {
89            PathBuf::from(r"C:\other\src\lib.rs")
90        } else {
91            PathBuf::from("/other/src/lib.rs")
92        };
93        let namespace = NamespaceRule::FileRelative.resolve(
94            file_path.to_str().expect("utf-8 test path"),
95            Some(&manifest_dir),
96        );
97        assert_eq!(namespace, "lib");
98    }
99
100    #[test]
101    fn folder_namespace_uses_parent_folder_name() {
102        let manifest_dir = test_manifest_dir();
103        let file_path = manifest_dir
104            .join("src")
105            .join("ui")
106            .join("forms")
107            .join("button.rs");
108        let namespace = NamespaceRule::Folder.resolve(
109            file_path.to_str().expect("utf-8 test path"),
110            Some(&manifest_dir),
111        );
112        assert_eq!(namespace, "forms");
113    }
114
115    #[test]
116    fn folder_relative_strips_manifest_and_src() {
117        let manifest_dir = test_manifest_dir();
118        let file_path = manifest_dir
119            .join("src")
120            .join("ui")
121            .join("forms")
122            .join("button.rs");
123        let namespace = NamespaceRule::FolderRelative.resolve(
124            file_path.to_str().expect("utf-8 test path"),
125            Some(&manifest_dir),
126        );
127        assert_eq!(namespace, "ui/forms");
128    }
129
130    #[test]
131    fn folder_relative_keeps_src_for_root_module() {
132        let manifest_dir = test_manifest_dir();
133        let file_path = manifest_dir.join("src").join("lib.rs");
134        let namespace = NamespaceRule::FolderRelative.resolve(
135            file_path.to_str().expect("utf-8 test path"),
136            Some(&manifest_dir),
137        );
138        assert_eq!(namespace, "src");
139    }
140
141    #[test]
142    fn folder_relative_falls_back_to_parent_outside_manifest() {
143        let manifest_dir = test_manifest_dir();
144        let file_path = if cfg!(windows) {
145            PathBuf::from(r"C:\other\src\lib.rs")
146        } else {
147            PathBuf::from("/other/src/lib.rs")
148        };
149        let namespace = NamespaceRule::FolderRelative.resolve(
150            file_path.to_str().expect("utf-8 test path"),
151            Some(&manifest_dir),
152        );
153        assert_eq!(namespace, "src");
154    }
155}