dotrain/cli/
rainconfig.rs1use rain_metadata::Store;
2use serde::{Serialize, Deserialize};
3use std::{
4 path::PathBuf,
5 sync::{Arc, RwLock},
6 fs::{read, read_to_string, read_dir},
7};
8
9pub(crate) const RAINCONFIG_DESCRIPTION: &str = r"
10Description:
11rainconfig.json provides configuration details and information required for .rain composer.
12
13usually it should be placed at the root directory of the working workspace and named as
14'rainconfig.json', however if this is not desired at times, it is possible to pass any path for
15rainconfig when using the dotrain command using --config option.
16
17all fields in the rainconfig are optional and are as follows:
18
19 - include: Specifies a list of directories (files/folders) to be included and watched.
20 folders will be watched recursively for .rain files. These files will be available as
21 dotrain meta in the cas so if their hash is specified in a composition target they will
22 get resolved.
23
24 - subgraphs: Additional subgraph endpoint URLs to include when searching for metas of
25 specified meta hashes in a rainlang document.
26";
27pub(crate) const RAINCONFIG_INCLUDE_DESCRIPTION: &str = r"Specifies a list of directories (files/folders) to be included and watched. folders will be watched recursively for .rain files. These files will be available as dotrain meta in the cas so if their hash is specified in a compilation target they will get resolved.";
28pub(crate) const RAINCONFIG_SUBGRAPHS_DESCRIPTION: &str = r"Additional subgraph endpoint URLs to include when searching for metas of specified meta hashes in a rainlang document.";
29
30#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
32pub struct RainConfigStruct {
33 pub include: Option<Vec<PathBuf>>,
34 pub subgraphs: Option<Vec<String>>,
35}
36
37impl RainConfigStruct {
38 pub fn read(path: &PathBuf) -> anyhow::Result<RainConfigStruct> {
40 let content = read(path)?;
41 let rainconfig: RainConfigStruct = serde_json::from_slice(&content)?;
42 Ok(rainconfig)
43 }
44
45 pub fn read_included_files(&self, force: bool) -> anyhow::Result<Vec<(PathBuf, String)>> {
46 let mut files_contents = vec![];
47 if let Some(included_dirs) = &self.include {
48 for included_dir in included_dirs {
49 match read_dotrain_files(included_dir, force) {
50 Ok(v) => files_contents.extend(v),
51 Err(e) => {
52 if !force {
53 Err(e)?
54 }
55 }
56 }
57 }
58 }
59 Ok(files_contents)
60 }
61
62 fn process(&self, force: bool) -> anyhow::Result<Vec<(PathBuf, String)>> {
63 let mut dotrains = vec![];
64 match self.read_included_files(force) {
65 Ok(v) => dotrains.extend(v),
66 Err(e) => {
67 if !force {
68 Err(e)?
69 }
70 }
71 }
72 Ok(dotrains)
73 }
74
75 pub fn build_store(&self) -> anyhow::Result<Arc<RwLock<Store>>> {
77 let empty = vec![];
78 let subgraphs = self.subgraphs.as_ref().unwrap_or(&empty);
79 let dotrains = self.process(true)?;
80 let mut store = Store::default();
81 store.add_subgraphs(subgraphs);
82 for (path, text) in dotrains {
83 if let Some(uri) = path.to_str() {
84 let uri = uri.to_string();
85 store.set_dotrain(&text, &uri, true)?;
86 } else {
87 return Err(anyhow::anyhow!(format!(
88 "could not derive a valid utf-8 encoded URI from path: {:?}",
89 path
90 )));
91 }
92 }
93 Ok(Arc::new(RwLock::new(store)))
94 }
95
96 pub fn force_build_store(&self) -> anyhow::Result<Arc<RwLock<Store>>> {
98 let empty = vec![];
99 let subgraphs = self.subgraphs.as_ref().unwrap_or(&empty);
100 let dotrains = self.process(false)?;
101 let mut store = Store::default();
102 store.add_subgraphs(subgraphs);
103 for (path, text) in dotrains {
104 if let Some(uri) = path.to_str() {
105 let uri = uri.to_string();
106 store.set_dotrain(&text, &uri, true)?;
107 }
108 }
109 Ok(Arc::new(RwLock::new(store)))
110 }
111}
112
113fn read_dotrain_files(path: &PathBuf, force: bool) -> anyhow::Result<Vec<(PathBuf, String)>> {
115 let mut files_contents = vec![];
116 for read_dir_result in read_dir(path)? {
117 let dir = read_dir_result?.path();
118 if dir.is_dir() {
119 match read_dotrain_files(&dir, force) {
120 Ok(v) => files_contents.extend(v),
121 Err(e) => {
122 if !force {
123 Err(e)?
124 }
125 }
126 }
127 } else if dir.is_file() {
128 if let Some(ext) = dir.extension() {
129 if ext == "rain" {
130 match read_to_string(&dir) {
131 Ok(v) => files_contents.push((dir.clone(), v)),
132 Err(e) => {
133 if !force {
134 Err(e)?
135 }
136 }
137 }
138 }
139 }
140 }
141 }
142 Ok(files_contents)
143}