1pub mod file;
2pub mod imports;
3
4use crate::read_file;
5use crate::runtime::action::ActionName;
6use crate::runtime::builder::{builtin, ros_core, ros_nav};
7use crate::tree::parser::ast::{FileEntity, Tree};
8use crate::tree::parser::Parser;
9use crate::tree::project::file::File;
10use crate::tree::{cerr, TreeError};
11use std::collections::{HashMap, HashSet};
12use std::path::PathBuf;
13
14pub type FileName = String;
15pub type TreeName = String;
16pub type AliasName = String;
17
18#[derive(Debug, Default, Clone)]
25pub struct Project {
26 pub root: PathBuf,
27 pub main: (FileName, TreeName),
28 pub files: HashMap<FileName, File>,
29 pub std: HashSet<ActionName>,
30}
31
32impl<'a> Project {
33 pub fn find_file(&'a self, f_name: &str) -> Result<&'a File, TreeError> {
34 self.files.get(f_name).ok_or(cerr(format!(
35 "unexpected error: the file {f_name} not exists"
36 )))
37 }
38 pub fn find_root(&'a self, name: &TreeName, file: &FileName) -> Result<&'a Tree, TreeError> {
39 self.find_file(file)?
40 .definitions
41 .get(name)
42 .ok_or(cerr(format!("no root {name} in {file}")))
43 }
44
45 pub fn find_tree(&self, file: &FileName, tree: &TreeName) -> Option<&Tree> {
46 self.files.get(file).and_then(|f| f.definitions.get(tree))
47 }
48
49 pub fn build_with_root(
60 main_file: FileName,
61 main_call: TreeName,
62 root: PathBuf,
63 ) -> Result<Project, TreeError> {
64 debug!(
65 target:"ast",
66 "built project with root: {:?}, main file: {} and root definition: {} ",
67 &root, main_file, main_call
68 );
69 let mut project = Project {
70 root: root.clone(),
71 main: ("".to_string(), "".to_string()),
72 files: Default::default(),
73 std: Default::default(),
74 };
75 project.main = (main_file.clone(), main_call);
76 project.parse_file(root, main_file)?;
77 Ok(project)
78 }
79 pub fn build(main_file: FileName, root: PathBuf) -> Result<Project, TreeError> {
83 let mut project = Project {
84 root: root.clone(),
85 main: ("".to_string(), "".to_string()),
86 files: Default::default(),
87 std: Default::default(),
88 };
89
90 project.parse_file(root.clone(), main_file.clone())?;
91
92 let main_call = project
93 .files
94 .get(main_file.as_str())
95 .and_then(|file| file.definitions.iter().find(|(_name, t)| t.is_root()))
96 .map(|(name, _)| name.to_string())
97 .ok_or(TreeError::IOError(format!(
98 "no root operation in the file {}",
99 main_file.clone()
100 )))?;
101 debug!(
102 target:"ast",
103 "built project with root: {:?}, main file: {} and root definition: {} ",
104 &root, main_file, main_call
105 );
106 project.main = (main_file, main_call);
107 Ok(project)
108 }
109 pub fn build_from_text(text: String) -> Result<Project, TreeError> {
116 let mut project = Project {
117 root: PathBuf::new(),
118 main: ("".to_string(), "".to_string()),
119 files: Default::default(),
120 std: Default::default(),
121 };
122
123 project.parse_text(text)?;
124
125 let main_call = project
126 .files
127 .get("_")
128 .and_then(|file| file.definitions.iter().find(|(_name, t)| t.is_root()))
129 .map(|(name, _)| name.to_string())
130 .ok_or(TreeError::IOError(
131 "no root operation in the given text".to_string(),
132 ))?;
133 debug!(target:"ast","built project from text with root: {}", main_call);
134 project.main = ("_".to_string(), main_call);
135 Ok(project)
136 }
137
138 fn parse_text(&mut self, text: String) -> Result<(), TreeError> {
139 let ast_file = Parser::new(text.as_str())?.parse()?;
140
141 let mut file = File::new("_".to_string());
142 for ent in ast_file.0.into_iter() {
143 match ent {
144 FileEntity::Tree(t) => file.add_def(t)?,
145 FileEntity::Import(i) => {
146 self.parse_file(PathBuf::new(), i.f_name().to_string())?;
147 file.add_import(i)?
148 }
149 };
150 }
151
152 self.files.insert(file.name.clone(), file);
153 Ok(())
154 }
155
156 fn parse_file(&mut self, root: PathBuf, file: FileName) -> Result<(), TreeError> {
157 let text = file_to_str(root.clone(), file.clone())?;
158 let ast_file = Parser::new(text.as_str())?.parse()?;
159
160 if !self.files.contains_key(file.as_str()) {
161 let mut file = File::new(file);
162
163 for ent in ast_file.0.into_iter() {
164 match ent {
165 FileEntity::Tree(t) => file.add_def(t)?,
166 FileEntity::Import(i) => {
167 self.parse_file(root.clone(), i.f_name().to_string())?;
168 file.add_import(i)?
169 }
170 };
171 }
172
173 self.files.insert(file.name.clone(), file);
174 }
175 Ok(())
176 }
177}
178fn file_to_str(root: PathBuf, file: FileName) -> Result<String, TreeError> {
179 if file.contains("::") {
180 let parts: Vec<_> = file.split("::").collect();
181 if parts.len() != 2 {
182 return Err(TreeError::IOError(format!("invalid file name: {}", file)));
183 } else {
184 match parts.as_slice() {
185 ["std", "actions"] => Ok(builtin::builtin_actions_file()),
186 ["ros", "nav2"] => Ok(ros_nav::ros_actions_file()),
187 ["ros", "core"] => Ok(ros_core::ros_actions_file()),
188 _ => Err(TreeError::IOError(format!("invalid file name: {}", file))),
189 }
190 }
191 } else {
192 let mut path = root;
193 path.push(file);
194 Ok(read_file(&path)?)
195 }
196}