plantuml_server_client_rs/
collector.rs1use crate::{IncludesMetadata, IncludesMetadataItem};
2use anyhow::{Context as _, Result};
3use derive_getters::Getters;
4use plantuml_parser::{IncludeToken, IncludesCollections, PathResolver, PlantUmlFileData};
5use std::collections::{BTreeMap, HashMap, HashSet};
6use std::fs::read_to_string;
7use std::path::PathBuf;
8
9pub struct Collector;
11
12#[derive(Getters, Debug)]
14pub struct CollectedContainer {
15 path: PathBuf,
16 includes: IncludesCollections,
17 include_metadata: IncludesMetadata,
18}
19
20impl Collector {
21 pub fn collect(path: PathBuf, data: &PlantUmlFileData) -> Result<CollectedContainer> {
23 let mut includes = HashMap::new();
24 let mut include_metadata = BTreeMap::new();
25 let mut negative = HashSet::new();
26
27 Self::collect_includes_inner(
28 path.clone(),
29 data,
30 &mut includes,
31 &mut include_metadata,
32 &mut negative,
33 )?;
34
35 tracing::debug!("includes = {includes:?}");
36 tracing::debug!("include_metadata = {include_metadata:?}");
37
38 Ok(CollectedContainer {
39 path,
40 includes: IncludesCollections::new(includes),
41 include_metadata: include_metadata.into(),
42 })
43 }
44
45 fn collect_includes_inner(
47 path: PathBuf,
48 data: &PlantUmlFileData,
49 includes: &mut HashMap<PathBuf, PlantUmlFileData>,
50 include_metadata: &mut BTreeMap<PathBuf, Vec<IncludesMetadataItem>>,
51 negative: &mut HashSet<PathBuf>,
52 ) -> Result<()> {
53 tracing::info!("collect path = {path:?}");
54
55 let resolver = PathResolver::new(path);
56 tracing::debug!("resolver = {resolver:?}");
57
58 for data in data.iter() {
59 for include in data.includes().iter() {
60 let mut inner_resolver = resolver.clone();
61 let relative_path: PathBuf = include.filepath().into();
62 inner_resolver.add(relative_path.clone());
63 let path = inner_resolver
64 .build()
65 .context("failed to resolve path to collect files")?;
66 tracing::debug!("path from inner_resolver = {path:?}");
67
68 Self::insert_metadata_if_needed(
69 include,
70 &resolver,
71 &inner_resolver,
72 include_metadata,
73 )?;
74
75 if includes.contains_key(&path) || negative.contains(&path) {
77 continue;
78 }
79
80 let Ok(data) = read_to_string(&path) else {
81 tracing::warn!("failed to read file: path = {path:?}");
82 negative.insert(path);
83 continue;
84 };
85
86 let Ok(content) = PlantUmlFileData::parse_from_str(data) else {
87 tracing::warn!("failed to parse PlantUML content: path = {path:?}");
88 negative.insert(path);
89 continue;
90 };
91
92 includes.insert(path.clone(), content.clone());
93 Self::collect_includes_inner(path, &content, includes, include_metadata, negative)?;
94 }
95 }
96
97 Ok(())
98 }
99
100 fn insert_metadata_if_needed(
101 include: &IncludeToken,
102 resolver: &PathResolver,
103 inner_resolver: &PathResolver,
104 include_metadata: &mut BTreeMap<PathBuf, Vec<IncludesMetadataItem>>,
105 ) -> Result<()> {
106 let relative_path: PathBuf = include.filepath().into();
107 let path = inner_resolver.build()?;
108 let raw_path = inner_resolver.build_without_normalize()?;
109 let base_path = resolver
110 .build()
111 .context("failed to resolve path to metadata base_path")?;
112
113 let metadata = IncludesMetadataItem::builder()
114 .path(raw_path)
115 .base_path(base_path)
116 .relative_path(relative_path)
117 .index(include.index())
118 .id(include.id().map(|x| x.into()))
119 .build();
120 include_metadata
121 .entry(path.clone())
122 .or_default()
123 .push(metadata);
124
125 Ok(())
126 }
127}