1use color_eyre::Result;
2use regex::Regex;
3use std::collections::HashMap;
4use std::fs;
5use std::path::Path;
6
7#[derive(Debug, Clone, Copy)]
8pub enum ImportFormat {
9 DotEnv,
10 Json,
11 Yaml,
12 Text,
13}
14
15impl ImportFormat {
16 pub fn from_extension(path: &str) -> Result<Self> {
22 let ext = Path::new(path).extension().and_then(|s| s.to_str()).unwrap_or("");
23
24 match ext.to_lowercase().as_str() {
25 "env" => Ok(Self::DotEnv),
26 "json" => Ok(Self::Json),
27 "yaml" | "yml" => Ok(Self::Yaml),
28 "txt" | "text" => Ok(Self::Text),
29 _ => {
30 let filename = Path::new(path).file_name().and_then(|s| s.to_str()).unwrap_or("");
32
33 if filename.starts_with('.') && filename.contains("env") {
34 Ok(Self::DotEnv)
35 } else {
36 Ok(Self::Text) }
38 }
39 }
40 }
41}
42
43#[derive(Debug, Clone, Default)]
44pub struct Importer {
45 variables: HashMap<String, String>,
46}
47
48impl Importer {
49 #[must_use]
50 pub fn new() -> Self {
51 Self::default()
52 }
53
54 pub fn import_from_file(&mut self, path: &str, format: ImportFormat) -> Result<()> {
62 let content = fs::read_to_string(path)?;
63
64 match format {
65 ImportFormat::DotEnv => self.parse_dotenv(&content),
66 ImportFormat::Json => self.parse_json(&content)?,
67 ImportFormat::Yaml => self.parse_yaml(&content),
68 ImportFormat::Text => self.parse_text(&content),
69 }
70
71 Ok(())
72 }
73
74 #[must_use]
75 pub fn get_variables(&self) -> Vec<(String, String)> {
76 self.variables.iter().map(|(k, v)| (k.clone(), v.clone())).collect()
77 }
78
79 pub fn filter_by_patterns(&mut self, patterns: &[String]) {
80 let mut matched = HashMap::new();
81
82 for pattern in patterns {
83 let regex_pattern = if pattern.contains('*') || pattern.contains('?') {
84 wildcard_to_regex(pattern)
85 } else {
86 format!("^{}$", regex::escape(pattern))
87 };
88
89 if let Ok(re) = Regex::new(®ex_pattern) {
90 for (key, value) in &self.variables {
91 if re.is_match(key) {
92 matched.insert(key.clone(), value.clone());
93 }
94 }
95 }
96 }
97
98 self.variables = matched;
99 }
100
101 pub fn add_prefix(&mut self, prefix: &str) {
102 let mut prefixed = HashMap::new();
103
104 for (key, value) in self.variables.drain() {
105 prefixed.insert(format!("{prefix}{key}"), value);
106 }
107
108 self.variables = prefixed;
109 }
110
111 fn parse_dotenv(&mut self, content: &str) {
112 for line in content.lines() {
113 let line = line.trim();
114
115 if line.is_empty() || line.starts_with('#') {
117 continue;
118 }
119
120 if let Some(eq_pos) = line.find('=') {
122 let key = line[..eq_pos].trim();
123 let value = line[eq_pos + 1..].trim();
124
125 if key.is_empty() || key.contains(' ') {
127 continue;
128 }
129
130 let processed_value = if (value.starts_with('"') && value.ends_with('"'))
132 || (value.starts_with('\'') && value.ends_with('\''))
133 {
134 let unquoted = &value[1..value.len() - 1];
136
137 Self::unescape_string(unquoted)
139 } else {
140 if let Some(comment_pos) = value.find(" #") {
142 value[..comment_pos].trim().to_string()
143 } else {
144 value.to_string()
145 }
146 };
147
148 self.variables.insert(key.to_string(), processed_value);
149 }
150 }
151 }
152
153 fn unescape_string(input: &str) -> String {
154 let mut result = String::new();
155 let mut chars = input.chars().peekable();
156
157 while let Some(ch) = chars.next() {
158 if ch == '\\' {
159 match chars.peek() {
160 Some('\\') => {
161 result.push('\\');
162 chars.next(); }
164 Some('n') => {
165 result.push('\n');
166 chars.next(); }
168 Some('r') => {
169 result.push('\r');
170 chars.next(); }
172 Some('t') => {
173 result.push('\t');
174 chars.next(); }
176 Some('"') => {
177 result.push('"');
178 chars.next(); }
180 Some('\'') => {
181 result.push('\'');
182 chars.next(); }
184 _ => {
185 result.push('\\');
187 }
188 }
189 } else {
190 result.push(ch);
191 }
192 }
193
194 result
195 }
196
197 fn parse_json(&mut self, content: &str) -> Result<()> {
198 let parsed: serde_json::Value = serde_json::from_str(content)?;
199
200 if let Some(obj) = parsed.as_object() {
202 if obj.contains_key("variables") {
204 if let Some(vars) = obj["variables"].as_array() {
205 for var in vars {
206 if let (Some(name), Some(value)) = (
207 var.get("name").and_then(|v| v.as_str()),
208 var.get("value").and_then(|v| v.as_str()),
209 ) {
210 self.variables.insert(name.to_string(), value.to_string());
211 }
212 }
213 }
214 } else {
215 for (key, value) in obj {
217 if let Some(val_str) = value.as_str() {
218 self.variables.insert(key.clone(), val_str.to_string());
219 }
220 }
221 }
222 }
223
224 Ok(())
225 }
226
227 fn parse_yaml(&mut self, content: &str) {
228 let mut skip_remaining = false;
230
231 for line in content.lines() {
232 let line = line.trim();
233
234 if line.is_empty() || line.starts_with('#') {
236 continue;
237 }
238
239 if line == "---" {
241 skip_remaining = true;
242 continue;
243 }
244
245 if skip_remaining {
247 continue;
248 }
249
250 if let Some(colon_pos) = line.find(':') {
252 let key = line[..colon_pos].trim();
253 let value = line[colon_pos + 1..].trim();
254
255 let processed_value = if (value.starts_with('"') && value.ends_with('"'))
257 || (value.starts_with('\'') && value.ends_with('\''))
258 {
259 value[1..value.len() - 1].to_string()
260 } else {
261 value.to_string()
262 };
263
264 self.variables.insert(key.to_string(), processed_value);
265 }
266 }
267 }
268
269 fn parse_text(&mut self, content: &str) {
270 self.parse_dotenv(content);
272 }
273}
274
275fn wildcard_to_regex(pattern: &str) -> String {
276 let mut regex = String::new();
277 regex.push('^');
278
279 for ch in pattern.chars() {
280 match ch {
281 '*' => regex.push_str(".*"),
282 '?' => regex.push('.'),
283 '.' | '+' | '^' | '$' | '(' | ')' | '[' | ']' | '{' | '}' | '|' | '\\' => {
284 regex.push('\\');
285 regex.push(ch);
286 }
287 _ => regex.push(ch),
288 }
289 }
290
291 regex.push('$');
292 regex
293}
294
295#[cfg(test)]
296mod tests {
297 use super::*;
298 use std::io::Write;
299 use tempfile::NamedTempFile;
300
301 fn create_temp_file(content: &str, extension: &str) -> NamedTempFile {
303 let mut file = NamedTempFile::new().unwrap();
304 if !extension.is_empty() {
305 file = NamedTempFile::with_suffix(extension).unwrap();
306 }
307 file.write_all(content.as_bytes()).unwrap();
308 file.flush().unwrap();
309 file
310 }
311
312 #[test]
313 fn test_import_format_from_extension() {
314 assert!(matches!(
315 ImportFormat::from_extension("file.env").unwrap(),
316 ImportFormat::DotEnv
317 ));
318 assert!(matches!(
319 ImportFormat::from_extension("file.ENV").unwrap(),
320 ImportFormat::DotEnv
321 ));
322 assert!(matches!(
323 ImportFormat::from_extension("file.json").unwrap(),
324 ImportFormat::Json
325 ));
326 assert!(matches!(
327 ImportFormat::from_extension("file.JSON").unwrap(),
328 ImportFormat::Json
329 ));
330 assert!(matches!(
331 ImportFormat::from_extension("file.yaml").unwrap(),
332 ImportFormat::Yaml
333 ));
334 assert!(matches!(
335 ImportFormat::from_extension("file.yml").unwrap(),
336 ImportFormat::Yaml
337 ));
338 assert!(matches!(
339 ImportFormat::from_extension("file.txt").unwrap(),
340 ImportFormat::Text
341 ));
342 assert!(matches!(
343 ImportFormat::from_extension("file.text").unwrap(),
344 ImportFormat::Text
345 ));
346
347 assert!(matches!(
349 ImportFormat::from_extension("file.xyz").unwrap(),
350 ImportFormat::Text
351 ));
352 assert!(matches!(
353 ImportFormat::from_extension("file").unwrap(),
354 ImportFormat::Text
355 ));
356
357 assert!(matches!(
359 ImportFormat::from_extension(".env").unwrap(),
360 ImportFormat::DotEnv
361 ));
362 assert!(matches!(
363 ImportFormat::from_extension(".env.local").unwrap(),
364 ImportFormat::DotEnv
365 ));
366 assert!(matches!(
367 ImportFormat::from_extension(".env.production").unwrap(),
368 ImportFormat::DotEnv
369 ));
370 }
371
372 #[test]
373 fn test_parse_dotenv_basic() {
374 let mut importer = Importer::new();
375 let content = r#"
376# This is a comment
377KEY1=value1
378KEY2=value2
379KEY3=value with spaces
380
381# Another comment
382KEY4="quoted value"
383KEY5='single quoted'
384"#;
385
386 importer.parse_dotenv(content);
387 let vars = importer.get_variables();
388 let vars_map: HashMap<_, _> = vars.into_iter().collect();
389
390 assert_eq!(vars_map.get("KEY1").unwrap(), "value1");
391 assert_eq!(vars_map.get("KEY2").unwrap(), "value2");
392 assert_eq!(vars_map.get("KEY3").unwrap(), "value with spaces");
393 assert_eq!(vars_map.get("KEY4").unwrap(), "quoted value");
394 assert_eq!(vars_map.get("KEY5").unwrap(), "single quoted");
395 }
396
397 #[test]
398 fn test_parse_dotenv_with_escapes() {
399 let mut importer = Importer::new();
400 let content = r#"
401ESCAPED="line1\nline2\ttab"
402BACKSLASH="path\\to\\file"
403DOUBLE_BACKSLASH="path\\\\to\\\\file"
404QUOTE="He said \"hello\""
405"#;
406
407 importer.parse_dotenv(content);
408 let vars = importer.get_variables();
409 let vars_map: HashMap<_, _> = vars.into_iter().collect();
410
411 assert_eq!(vars_map.get("ESCAPED").unwrap(), "line1\nline2\ttab");
412 assert_eq!(vars_map.get("BACKSLASH").unwrap(), "path\\to\\file");
413 assert_eq!(vars_map.get("DOUBLE_BACKSLASH").unwrap(), "path\\\\to\\\\file");
414 assert_eq!(vars_map.get("QUOTE").unwrap(), "He said \"hello\"");
415 }
416
417 #[test]
418 fn test_parse_dotenv_inline_comments() {
419 let mut importer = Importer::new();
420 let content = r#"
421KEY1=value1 # This is an inline comment
422KEY2=value#notacomment
423KEY3=value # comment
424KEY4="value # not a comment in quotes"
425"#;
426
427 importer.parse_dotenv(content);
428 let vars = importer.get_variables();
429 let vars_map: HashMap<_, _> = vars.into_iter().collect();
430
431 assert_eq!(vars_map.get("KEY1").unwrap(), "value1");
432 assert_eq!(vars_map.get("KEY2").unwrap(), "value#notacomment");
433 assert_eq!(vars_map.get("KEY3").unwrap(), "value");
434 assert_eq!(vars_map.get("KEY4").unwrap(), "value # not a comment in quotes");
435 }
436
437 #[test]
438 fn test_parse_dotenv_edge_cases() {
439 let mut importer = Importer::new();
440 let content = r"
441# Empty value
442EMPTY=
443# No spaces around equals
444COMPACT=value
445# Spaces in key should be ignored
446INVALID KEY=value
447# Key with spaces is invalid
448KEY WITH SPACES=value
449# Just equals sign
450=value
451# No equals sign
452NOEQUALS
453# Multiple equals signs
454KEY=value=with=equals
455# Unicode values
456UNICODE=こんにちは
457# Special characters
458SPECIAL=!@#$%^&*()
459";
460
461 importer.parse_dotenv(content);
462 let vars = importer.get_variables();
463 let vars_map: HashMap<_, _> = vars.into_iter().collect();
464
465 assert_eq!(vars_map.get("EMPTY").unwrap(), "");
466 assert_eq!(vars_map.get("COMPACT").unwrap(), "value");
467 assert!(!vars_map.contains_key("INVALID KEY"));
468 assert!(!vars_map.contains_key("KEY WITH SPACES"));
469 assert!(!vars_map.contains_key("NOEQUALS"));
470 assert_eq!(vars_map.get("KEY").unwrap(), "value=with=equals");
471 assert_eq!(vars_map.get("UNICODE").unwrap(), "こんにちは");
472 assert_eq!(vars_map.get("SPECIAL").unwrap(), "!@#$%^&*()");
473 }
474
475 #[test]
476 fn test_parse_json_simple() {
477 let mut importer = Importer::new();
478 let content = r#"{
479 "KEY1": "value1",
480 "KEY2": "value2",
481 "KEY3": "value with spaces"
482 }"#;
483
484 importer.parse_json(content).unwrap();
485 let vars = importer.get_variables();
486 let vars_map: HashMap<_, _> = vars.into_iter().collect();
487
488 assert_eq!(vars_map.get("KEY1").unwrap(), "value1");
489 assert_eq!(vars_map.get("KEY2").unwrap(), "value2");
490 assert_eq!(vars_map.get("KEY3").unwrap(), "value with spaces");
491 }
492
493 #[test]
494 fn test_parse_json_structured() {
495 let mut importer = Importer::new();
496 let content = r#"{
497 "exported_at": "2024-01-01T00:00:00Z",
498 "count": 3,
499 "variables": [
500 {"name": "KEY1", "value": "value1"},
501 {"name": "KEY2", "value": "value2"},
502 {"name": "KEY3", "value": "value3"}
503 ]
504 }"#;
505
506 importer.parse_json(content).unwrap();
507 let vars = importer.get_variables();
508 let vars_map: HashMap<_, _> = vars.into_iter().collect();
509
510 assert_eq!(vars_map.len(), 3);
511 assert_eq!(vars_map.get("KEY1").unwrap(), "value1");
512 assert_eq!(vars_map.get("KEY2").unwrap(), "value2");
513 assert_eq!(vars_map.get("KEY3").unwrap(), "value3");
514 }
515
516 #[test]
517 fn test_parse_json_invalid() {
518 let mut importer = Importer::new();
519 let content = "not valid json";
520
521 assert!(importer.parse_json(content).is_err());
522 }
523
524 #[test]
525 fn test_parse_json_non_string_values() {
526 let mut importer = Importer::new();
527 let content = r#"{
528 "STRING": "value",
529 "NUMBER": 42,
530 "BOOLEAN": true,
531 "NULL": null,
532 "ARRAY": [1, 2, 3],
533 "OBJECT": {"nested": "value"}
534 }"#;
535
536 importer.parse_json(content).unwrap();
537 let vars = importer.get_variables();
538 let vars_map: HashMap<_, _> = vars.into_iter().collect();
539
540 assert_eq!(vars_map.len(), 1);
542 assert_eq!(vars_map.get("STRING").unwrap(), "value");
543 }
544
545 #[test]
546 fn test_parse_yaml_basic() {
547 let mut importer = Importer::new();
548 let content = r"
549# YAML comment
550KEY1: value1
551KEY2: value2
552KEY3: value with spaces
553---
554KEY4: after document marker
555";
556
557 importer.parse_yaml(content);
558 let vars = importer.get_variables();
559 let vars_map: HashMap<_, _> = vars.into_iter().collect();
560
561 assert_eq!(vars_map.get("KEY1").unwrap(), "value1");
562 assert_eq!(vars_map.get("KEY2").unwrap(), "value2");
563 assert_eq!(vars_map.get("KEY3").unwrap(), "value with spaces");
564 assert!(!vars_map.contains_key("KEY4")); }
566
567 #[test]
568 fn test_parse_yaml_quoted() {
569 let mut importer = Importer::new();
570 let content = r#"
571KEY1: "quoted value"
572KEY2: 'single quoted'
573KEY3: "value: with colon"
574KEY4: unquoted: with colon
575"#;
576
577 importer.parse_yaml(content);
578 let vars = importer.get_variables();
579 let vars_map: HashMap<_, _> = vars.into_iter().collect();
580
581 assert_eq!(vars_map.get("KEY1").unwrap(), "quoted value");
582 assert_eq!(vars_map.get("KEY2").unwrap(), "single quoted");
583 assert_eq!(vars_map.get("KEY3").unwrap(), "value: with colon");
584 assert_eq!(vars_map.get("KEY4").unwrap(), "unquoted: with colon");
585 }
586
587 #[test]
588 fn test_parse_yaml_edge_cases() {
589 let mut importer = Importer::new();
590 let content = r"
591# Empty value
592EMPTY:
593EMPTY2:
594# No space after colon
595COMPACT:value
596# Multiple colons
597URL: http://example.com:8080
598# Special characters
599SPECIAL: !@#$%^&*()
600";
601
602 importer.parse_yaml(content);
603 let vars = importer.get_variables();
604 let vars_map: HashMap<_, _> = vars.into_iter().collect();
605
606 assert_eq!(vars_map.get("EMPTY").unwrap(), "");
607 assert_eq!(vars_map.get("EMPTY2").unwrap(), "");
608 assert_eq!(vars_map.get("COMPACT").unwrap(), "value");
609 assert_eq!(vars_map.get("URL").unwrap(), "http://example.com:8080");
610 assert_eq!(vars_map.get("SPECIAL").unwrap(), "!@#$%^&*()");
611 }
612
613 #[test]
614 fn test_import_from_file_dotenv() {
615 let content = "KEY1=value1\nKEY2=value2";
616 let file = create_temp_file(content, ".env");
617
618 let mut importer = Importer::new();
619 importer
620 .import_from_file(file.path().to_str().unwrap(), ImportFormat::DotEnv)
621 .unwrap();
622
623 let vars = importer.get_variables();
624 assert_eq!(vars.len(), 2);
625 }
626
627 #[test]
628 fn test_import_from_file_auto_detect() {
629 let env_file = create_temp_file("KEY=value", ".env");
631 let mut importer = Importer::new();
632 let format = ImportFormat::from_extension(env_file.path().to_str().unwrap()).unwrap();
633 importer
634 .import_from_file(env_file.path().to_str().unwrap(), format)
635 .unwrap();
636 assert_eq!(importer.get_variables().len(), 1);
637
638 let json_file = create_temp_file(r#"{"KEY": "value"}"#, ".json");
640 let mut importer = Importer::new();
641 let format = ImportFormat::from_extension(json_file.path().to_str().unwrap()).unwrap();
642 importer
643 .import_from_file(json_file.path().to_str().unwrap(), format)
644 .unwrap();
645 assert_eq!(importer.get_variables().len(), 1);
646 }
647
648 #[test]
649 fn test_filter_by_patterns_exact() {
650 let mut importer = Importer::new();
651 importer.variables.insert("KEY1".to_string(), "value1".to_string());
652 importer.variables.insert("KEY2".to_string(), "value2".to_string());
653 importer.variables.insert("OTHER".to_string(), "value3".to_string());
654
655 importer.filter_by_patterns(&["KEY1".to_string(), "KEY2".to_string()]);
656
657 let vars = importer.get_variables();
658 let vars_map: HashMap<_, _> = vars.into_iter().collect();
659
660 assert_eq!(vars_map.len(), 2);
661 assert!(vars_map.contains_key("KEY1"));
662 assert!(vars_map.contains_key("KEY2"));
663 assert!(!vars_map.contains_key("OTHER"));
664 }
665
666 #[test]
667 fn test_filter_by_patterns_wildcard() {
668 let mut importer = Importer::new();
669 importer.variables.insert("API_KEY".to_string(), "value1".to_string());
670 importer
671 .variables
672 .insert("API_SECRET".to_string(), "value2".to_string());
673 importer
674 .variables
675 .insert("DATABASE_URL".to_string(), "value3".to_string());
676 importer.variables.insert("OTHER".to_string(), "value4".to_string());
677
678 importer.filter_by_patterns(&["API_*".to_string()]);
679
680 let vars = importer.get_variables();
681 let vars_map: HashMap<_, _> = vars.into_iter().collect();
682
683 assert_eq!(vars_map.len(), 2);
684 assert!(vars_map.contains_key("API_KEY"));
685 assert!(vars_map.contains_key("API_SECRET"));
686 assert!(!vars_map.contains_key("DATABASE_URL"));
687 }
688
689 #[test]
690 fn test_filter_by_patterns_question_mark() {
691 let mut importer = Importer::new();
692 importer.variables.insert("KEY1".to_string(), "value1".to_string());
693 importer.variables.insert("KEY2".to_string(), "value2".to_string());
694 importer.variables.insert("KEY10".to_string(), "value3".to_string());
695 importer.variables.insert("OTHER".to_string(), "value4".to_string());
696
697 importer.filter_by_patterns(&["KEY?".to_string()]);
698
699 let vars = importer.get_variables();
700 let vars_map: HashMap<_, _> = vars.into_iter().collect();
701
702 assert_eq!(vars_map.len(), 2);
703 assert!(vars_map.contains_key("KEY1"));
704 assert!(vars_map.contains_key("KEY2"));
705 assert!(!vars_map.contains_key("KEY10")); }
707
708 #[test]
709 fn test_filter_by_patterns_multiple() {
710 let mut importer = Importer::new();
711 importer.variables.insert("API_KEY".to_string(), "value1".to_string());
712 importer.variables.insert("DB_HOST".to_string(), "value2".to_string());
713 importer.variables.insert("DB_PORT".to_string(), "value3".to_string());
714 importer.variables.insert("OTHER".to_string(), "value4".to_string());
715
716 importer.filter_by_patterns(&["API_*".to_string(), "DB_*".to_string()]);
717
718 let vars = importer.get_variables();
719 assert_eq!(vars.len(), 3);
720 }
721
722 #[test]
723 fn test_add_prefix() {
724 let mut importer = Importer::new();
725 importer.variables.insert("KEY1".to_string(), "value1".to_string());
726 importer.variables.insert("KEY2".to_string(), "value2".to_string());
727
728 importer.add_prefix("PREFIX_");
729
730 let vars = importer.get_variables();
731 let vars_map: HashMap<_, _> = vars.into_iter().collect();
732
733 assert_eq!(vars_map.len(), 2);
734 assert!(vars_map.contains_key("PREFIX_KEY1"));
735 assert!(vars_map.contains_key("PREFIX_KEY2"));
736 assert!(!vars_map.contains_key("KEY1"));
737 assert!(!vars_map.contains_key("KEY2"));
738 assert_eq!(vars_map.get("PREFIX_KEY1").unwrap(), "value1");
739 assert_eq!(vars_map.get("PREFIX_KEY2").unwrap(), "value2");
740 }
741
742 #[test]
743 fn test_add_prefix_empty() {
744 let mut importer = Importer::new();
745 importer.variables.insert("KEY1".to_string(), "value1".to_string());
746
747 importer.add_prefix("");
748
749 let vars = importer.get_variables();
750 let vars_map: HashMap<_, _> = vars.into_iter().collect();
751
752 assert!(vars_map.contains_key("KEY1"));
753 assert_eq!(vars_map.get("KEY1").unwrap(), "value1");
754 }
755
756 #[test]
757 fn test_wildcard_to_regex() {
758 let regex = wildcard_to_regex("API_*");
760 assert_eq!(regex, "^API_.*$");
761
762 let regex = wildcard_to_regex("KEY?");
764 assert_eq!(regex, "^KEY.$");
765
766 let regex = wildcard_to_regex("KEY.VALUE");
768 assert_eq!(regex, "^KEY\\.VALUE$");
769
770 let regex = wildcard_to_regex("KEY[1]");
771 assert_eq!(regex, "^KEY\\[1\\]$");
772
773 let regex = wildcard_to_regex("*_KEY_?");
775 assert_eq!(regex, "^.*_KEY_.$");
776 }
777
778 #[test]
779 fn test_complex_import_workflow() {
780 let content = r#"
782# Database configuration
783DB_HOST=localhost
784DB_PORT=5432
785DB_USER=admin
786DB_PASSWORD="secret password"
787
788# API configuration
789API_KEY=abc123
790API_SECRET=xyz789
791API_URL=https://api.example.com
792
793# Feature flags
794FEATURE_NEW_UI=true
795FEATURE_BETA=false
796
797# Paths
798APP_PATH=/usr/local/app
799LOG_PATH=/var/log/app
800"#;
801
802 let file = create_temp_file(content, ".env");
803
804 let mut importer = Importer::new();
805 importer
806 .import_from_file(file.path().to_str().unwrap(), ImportFormat::DotEnv)
807 .unwrap();
808
809 assert_eq!(importer.get_variables().len(), 11);
811
812 importer.filter_by_patterns(&["DB_*".to_string()]);
814 assert_eq!(importer.get_variables().len(), 4);
815
816 importer.add_prefix("TEST_");
818
819 let vars = importer.get_variables();
820 let vars_map: HashMap<_, _> = vars.into_iter().collect();
821
822 assert!(vars_map.contains_key("TEST_DB_HOST"));
823 assert!(vars_map.contains_key("TEST_DB_PORT"));
824 assert!(vars_map.contains_key("TEST_DB_USER"));
825 assert!(vars_map.contains_key("TEST_DB_PASSWORD"));
826 assert_eq!(vars_map.get("TEST_DB_PASSWORD").unwrap(), "secret password");
827 }
828
829 #[test]
830 fn test_parse_text_format() {
831 let mut importer = Importer::new();
832 let content = "KEY1=value1\nKEY2=value2";
834
835 importer.parse_text(content);
836 let vars = importer.get_variables();
837
838 assert_eq!(vars.len(), 2);
839 }
840
841 #[test]
842 fn test_empty_content() {
843 let mut importer = Importer::new();
844
845 importer.parse_dotenv("");
846 assert_eq!(importer.get_variables().len(), 0);
847
848 importer.parse_json("{}").unwrap();
849 assert_eq!(importer.get_variables().len(), 0);
850
851 importer.parse_yaml("");
852 assert_eq!(importer.get_variables().len(), 0);
853 }
854
855 #[test]
856 fn test_file_not_found() {
857 let mut importer = Importer::new();
858 let result = importer.import_from_file("/non/existent/file.env", ImportFormat::DotEnv);
859 assert!(result.is_err());
860 }
861}