1use camino::{Utf8Path, Utf8PathBuf};
5use tera::Context;
6
7use crate::Result;
8use crate::config::{MountEntry, MountStrategy};
9use crate::paths;
10use crate::template::{self, Engine};
11
12#[derive(Debug, Clone)]
15pub struct ResolvedMount {
16 pub src: Utf8PathBuf,
23 pub dst: Utf8PathBuf,
24 pub strategy: MountStrategy,
25}
26
27pub fn resolve(
28 source: &Utf8Path,
29 entries: &[MountEntry],
30 default_strategy: MountStrategy,
31 engine: &mut Engine,
32 ctx: &Context,
33) -> Result<Vec<ResolvedMount>> {
34 let mut out = Vec::with_capacity(entries.len());
35 for e in entries {
36 if let Some(when) = &e.when {
37 if !template::eval_truthy(when, engine, ctx)? {
44 continue;
45 }
46 }
47 let src_str = engine.render(e.src.as_str(), ctx)?;
48 let src = paths::resolve_mount_src(source, src_str.trim());
49 let dst_str = engine.render(&e.dst, ctx)?;
50 let dst = paths::expand_tilde(dst_str.trim());
51 out.push(ResolvedMount {
52 src,
53 dst,
54 strategy: e.strategy.unwrap_or(default_strategy),
55 });
56 }
57 Ok(out)
58}
59
60#[cfg(test)]
61mod tests {
62 use super::*;
63 use crate::template;
64 use crate::vars::YuiVars;
65
66 fn vars() -> YuiVars {
67 YuiVars {
68 os: "linux".into(),
69 arch: "x86_64".into(),
70 host: "test".into(),
71 user: "u".into(),
72 source: "/dotfiles".into(),
73 }
74 }
75
76 fn source() -> Utf8PathBuf {
77 Utf8PathBuf::from("/dotfiles")
78 }
79
80 #[test]
81 fn renders_dst_and_filters_when_false() {
82 let entries = vec![
83 MountEntry {
84 src: "home".into(),
85 dst: "/{{ yui.os }}/u".into(),
86 when: None,
87 strategy: None,
88 },
89 MountEntry {
90 src: "appdata".into(),
91 dst: "/appdata".into(),
92 when: Some("{{ yui.os == 'windows' }}".into()),
93 strategy: None,
94 },
95 ];
96 let mut e = Engine::new();
97 let ctx = template::config_context(&vars());
98 let s = source();
99 let resolved = resolve(&s, &entries, MountStrategy::Marker, &mut e, &ctx).unwrap();
100 assert_eq!(resolved.len(), 1);
101 assert_eq!(resolved[0].src, Utf8PathBuf::from("/dotfiles/home"));
103 assert_eq!(resolved[0].dst, Utf8PathBuf::from("/linux/u"));
104 assert_eq!(resolved[0].strategy, MountStrategy::Marker);
105 }
106
107 #[test]
108 fn when_true_keeps_entry() {
109 let entries = vec![MountEntry {
110 src: "home".into(),
111 dst: "/h".into(),
112 when: Some("{{ yui.os == 'linux' }}".into()),
113 strategy: None,
114 }];
115 let mut e = Engine::new();
116 let ctx = template::config_context(&vars());
117 let s = source();
118 let resolved = resolve(&s, &entries, MountStrategy::Marker, &mut e, &ctx).unwrap();
119 assert_eq!(resolved.len(), 1);
120 }
121
122 #[test]
123 fn bare_when_form_works() {
124 let entries = vec![MountEntry {
129 src: "home".into(),
130 dst: "/h".into(),
131 when: Some("yui.os == 'linux'".into()),
132 strategy: None,
133 }];
134 let mut e = Engine::new();
135 let ctx = template::config_context(&vars());
136 let s = source();
137 let resolved = resolve(&s, &entries, MountStrategy::Marker, &mut e, &ctx).unwrap();
138 assert_eq!(resolved.len(), 1);
139 }
140
141 #[test]
142 fn bare_when_form_filters_when_false() {
143 let entries = vec![MountEntry {
144 src: "home".into(),
145 dst: "/h".into(),
146 when: Some("yui.os == 'no-such-os'".into()),
147 strategy: None,
148 }];
149 let mut e = Engine::new();
150 let ctx = template::config_context(&vars());
151 let s = source();
152 let resolved = resolve(&s, &entries, MountStrategy::Marker, &mut e, &ctx).unwrap();
153 assert!(resolved.is_empty());
154 }
155
156 #[test]
157 fn per_entry_strategy_overrides_default() {
158 let entries = vec![MountEntry {
159 src: "home".into(),
160 dst: "/h".into(),
161 when: None,
162 strategy: Some(MountStrategy::PerFile),
163 }];
164 let mut e = Engine::new();
165 let ctx = template::config_context(&vars());
166 let s = source();
167 let resolved = resolve(&s, &entries, MountStrategy::Marker, &mut e, &ctx).unwrap();
168 assert_eq!(resolved[0].strategy, MountStrategy::PerFile);
169 }
170
171 #[test]
175 fn absolute_src_is_returned_verbatim() {
176 let entries = vec![MountEntry {
177 src: "/abs/private/home".into(),
178 dst: "/h".into(),
179 when: None,
180 strategy: None,
181 }];
182 let mut e = Engine::new();
183 let ctx = template::config_context(&vars());
184 let s = source();
185 let resolved = resolve(&s, &entries, MountStrategy::Marker, &mut e, &ctx).unwrap();
186 assert_eq!(resolved[0].src, Utf8PathBuf::from("/abs/private/home"));
187 }
188
189 #[test]
193 fn src_renders_via_tera() {
194 let entries = vec![MountEntry {
195 src: "private/{{ yui.host }}/home".into(),
196 dst: "/h".into(),
197 when: None,
198 strategy: None,
199 }];
200 let mut e = Engine::new();
201 let ctx = template::config_context(&vars());
202 let s = source();
203 let resolved = resolve(&s, &entries, MountStrategy::Marker, &mut e, &ctx).unwrap();
204 assert_eq!(
206 resolved[0].src,
207 Utf8PathBuf::from("/dotfiles/private/test/home")
208 );
209 }
210}