1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
//! Quill file/query convenience methods.
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use crate::value::QuillValue;
use super::{FileTreeNode, Quill};
impl Quill {
/// Get the list of typst packages to download, if specified in Quill.yaml
pub fn typst_packages(&self) -> Vec<String> {
self.metadata
.get("typst_packages")
.and_then(|v| v.as_array())
.map(|arr| {
arr.iter()
.filter_map(|v| v.as_str().map(|s| s.to_string()))
.collect()
})
.unwrap_or_default()
}
/// Get default values from cached config-derived defaults
///
/// Returns a reference to the pre-computed defaults HashMap that was extracted
/// during Quill construction.
///
/// This is used by `ParsedDocument::with_defaults()` to apply default values
/// to missing fields.
pub fn extract_defaults(&self) -> &HashMap<String, QuillValue> {
&self.defaults
}
/// Get example values from cached config-derived examples
///
/// Returns a reference to the pre-computed examples HashMap that was extracted
/// during Quill construction.
pub fn extract_examples(&self) -> &HashMap<String, Vec<QuillValue>> {
&self.examples
}
/// Get file contents by path (relative to quill root)
pub fn get_file<P: AsRef<Path>>(&self, path: P) -> Option<&[u8]> {
self.files.get_file(path)
}
/// Check if a file exists in memory
pub fn file_exists<P: AsRef<Path>>(&self, path: P) -> bool {
self.files.file_exists(path)
}
/// Check if a directory exists in memory
pub fn dir_exists<P: AsRef<Path>>(&self, path: P) -> bool {
self.files.dir_exists(path)
}
/// List files in a directory (non-recursive, returns file names only)
pub fn list_files<P: AsRef<Path>>(&self, path: P) -> Vec<String> {
self.files.list_files(path)
}
/// List subdirectories in a directory (non-recursive, returns directory names only)
pub fn list_subdirectories<P: AsRef<Path>>(&self, path: P) -> Vec<String> {
self.files.list_subdirectories(path)
}
/// List all files in a directory (returns paths relative to quill root)
pub fn list_directory<P: AsRef<Path>>(&self, dir_path: P) -> Vec<PathBuf> {
let dir_path = dir_path.as_ref();
let filenames = self.files.list_files(dir_path);
// Convert filenames to full paths
filenames
.iter()
.map(|name| {
if dir_path == Path::new("") {
PathBuf::from(name)
} else {
dir_path.join(name)
}
})
.collect()
}
/// List all directories in a directory (returns paths relative to quill root)
pub fn list_directories<P: AsRef<Path>>(&self, dir_path: P) -> Vec<PathBuf> {
let dir_path = dir_path.as_ref();
let subdirs = self.files.list_subdirectories(dir_path);
// Convert subdirectory names to full paths
subdirs
.iter()
.map(|name| {
if dir_path == Path::new("") {
PathBuf::from(name)
} else {
dir_path.join(name)
}
})
.collect()
}
/// Get all files matching a pattern (supports glob-style wildcards)
pub fn find_files<P: AsRef<Path>>(&self, pattern: P) -> Vec<PathBuf> {
let pattern_str = pattern.as_ref().to_string_lossy();
let mut matches = Vec::new();
// Compile the glob pattern
let glob_pattern = match glob::Pattern::new(&pattern_str) {
Ok(pat) => pat,
Err(_) => return matches, // Invalid pattern returns empty results
};
// Recursively search the tree for matching files
Self::find_files_recursive(&self.files, Path::new(""), &glob_pattern, &mut matches);
matches.sort();
matches
}
/// Helper method to recursively search for files matching a pattern
fn find_files_recursive(
node: &FileTreeNode,
current_path: &Path,
pattern: &glob::Pattern,
matches: &mut Vec<PathBuf>,
) {
match node {
FileTreeNode::File { .. } => {
let path_str = current_path.to_string_lossy();
if pattern.matches(&path_str) {
matches.push(current_path.to_path_buf());
}
}
FileTreeNode::Directory { files } => {
for (name, child_node) in files {
let child_path = if current_path == Path::new("") {
PathBuf::from(name)
} else {
current_path.join(name)
};
Self::find_files_recursive(child_node, &child_path, pattern, matches);
}
}
}
}
}