1use std::path;
6
7use crate::prelude as txl;
8use crate::traits::*;
9use crate::*;
10
11#[derive(PartialEq, Eq, Debug)]
13pub struct FileLocation {
14 pub path: String,
15 pub extension: Option<String>,
16 pub area: Option<String>,
17}
18
19impl Parsable for FileLocation {
20 fn parse_impl<S: TexlangState>(input: &mut vm::ExpandedStream<S>) -> txl::Result<Self> {
22 let mut raw_string = String::new();
23 let mut area_delimiter = None;
24 let mut ext_delimiter = None;
25 loop {
26 let t = match input.next()? {
27 None => break,
28 Some(t) => t,
29 };
30 if let token::Value::Space(_) = t.value() {
31 break;
32 }
33 let c = match t.char() {
34 None => {
35 input.back(t);
36 break;
37 }
38 Some(c) => c,
39 };
40 match c {
41 '>' | ':' => {
42 area_delimiter = Some(raw_string.len() + 1);
43 ext_delimiter = None;
44 }
45 '.' => {
46 ext_delimiter = Some(raw_string.len());
47 }
48 _ => (),
49 }
50 raw_string.push(c);
51 }
52
53 Ok(FileLocation {
54 path: raw_string
55 [area_delimiter.unwrap_or(0)..ext_delimiter.unwrap_or(raw_string.len())]
56 .into(),
57 extension: ext_delimiter.map(|j| raw_string[j + 1..].into()),
58 area: area_delimiter.map(|i| raw_string[..i].into()),
59 })
60 }
61}
62
63impl FileLocation {
64 pub fn determine_full_path(
65 &self,
66 working_directory: Option<&path::Path>,
67 default_extension: &str,
68 ) -> path::PathBuf {
69 let mut path: path::PathBuf = match self.area {
70 None => match working_directory {
71 None => Default::default(),
72 Some(working_directory) => working_directory.into(),
73 },
74 Some(_) => {
75 panic!("Texlang does not have support for file areas yet");
80 }
81 };
82 path.push(std::ffi::OsString::from(&self.path));
83 path.set_extension(std::ffi::OsString::from(
84 self.extension.as_deref().unwrap_or(default_extension),
85 ));
86 if !path.is_absolute() {
87 panic!("TODO: handle this error (path is relative and no working directory set)");
88 }
89 path
90 }
91}
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96 use crate::parse::testing::*;
97
98 parse_success_tests![
99 (
100 path_only,
101 "path/to/file",
102 FileLocation {
103 path: "path/to/file".to_string(),
104 extension: None,
105 area: None,
106 },
107 ),
108 (
109 path_only_newline,
110 "path/to/file\n",
111 FileLocation {
112 path: "path/to/file".to_string(),
113 extension: None,
114 area: None,
115 },
116 ),
117 (
118 path_only_control_sequence,
119 r"path/to/file\relax more",
120 FileLocation {
121 path: "path/to/file".to_string(),
122 extension: None,
123 area: None,
124 },
125 ),
126 (
127 path_only_trailing_word,
128 "path/to/file something",
129 FileLocation {
130 path: "path/to/file".to_string(),
131 extension: None,
132 area: None,
133 },
134 ),
135 (
136 extension_only,
137 ".tex",
138 FileLocation {
139 path: "".to_string(),
140 extension: Some("tex".to_string()),
141 area: None,
142 },
143 ),
144 (
145 empty,
146 "",
147 FileLocation {
148 path: "".to_string(),
149 extension: None,
150 area: None,
151 },
152 ),
153 (
154 path_and_extension,
155 "path/to/file.tex",
156 FileLocation {
157 path: "path/to/file".to_string(),
158 extension: Some("tex".to_string()),
159 area: None,
160 },
161 ),
162 (
163 path_and_area_with_langle,
164 "area>path/to/file",
165 FileLocation {
166 path: "path/to/file".to_string(),
167 extension: None,
168 area: Some("area>".to_string()),
169 },
170 ),
171 (
172 path_and_area_with_colon,
173 "area:path/to/file",
174 FileLocation {
175 path: "path/to/file".to_string(),
176 extension: None,
177 area: Some("area:".to_string()),
178 },
179 ),
180 (
181 area_only,
182 "area:",
183 FileLocation {
184 path: "".to_string(),
185 extension: None,
186 area: Some("area:".to_string()),
187 },
188 ),
189 (
190 path_and_extension_and_area_with_colon,
191 "area:path/to/file.tex",
192 FileLocation {
193 path: "path/to/file".to_string(),
194 extension: Some("tex".to_string()),
195 area: Some("area:".to_string()),
196 },
197 ),
198 ];
199}