Skip to content

Commit 379f594

Browse files
committed
better support for temporary environments
1 parent 0f18477 commit 379f594

File tree

4 files changed

+97
-93
lines changed

4 files changed

+97
-93
lines changed

cli/src/main.rs

Lines changed: 62 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#![feature(let_chains)]
12
use anyhow::Result;
23
use clap::{Parser, Subcommand};
34
use ontoenv::api::{OntoEnv, ResolveTarget};
@@ -23,44 +24,45 @@ struct Cli {
2324
/// Debug mode - sets the RUST_LOG level to debug, defaults to warning level
2425
#[clap(long, action, default_value = "false")]
2526
debug: bool,
27+
/// Directories to search for ontologies. If not provided, the current directory is used.
28+
#[clap(long, short, num_args = 1..)]
29+
search_directories: Option<Vec<PathBuf>>,
2630
/// Resolution policy for determining which ontology to use when there are multiple with the same name
2731
#[clap(long, short, default_value = "default")]
2832
policy: Option<String>,
33+
/// Temporary (non-persistent) mode - will not save the environment to disk
34+
#[clap(long, short, action)]
35+
temporary: bool,
36+
/// Require ontology names to be unique; will raise an error if multiple ontologies have the same name
37+
#[clap(long, action)]
38+
require_ontology_names: bool,
39+
/// Strict mode - will raise an error if an ontology is not found
40+
#[clap(long, action, default_value = "false")]
41+
strict: bool,
42+
/// Offline mode - will not attempt to fetch ontologies from the web
43+
#[clap(long, short, action, default_value = "false")]
44+
offline: bool,
45+
/// Glob patterns for which files to include, defaults to ['*.ttl','*.xml','*.n3']
46+
#[clap(long, short, num_args = 1..)]
47+
includes: Vec<String>,
48+
/// Glob patterns for which files to exclude, defaults to []
49+
#[clap(long, short, num_args = 1..)]
50+
excludes: Vec<String>,
51+
/// Do not search for ontologies in the search directories
52+
#[clap(long = "no-search", short = 'n', action)]
53+
no_search: bool,
2954
}
3055

3156
#[derive(Debug, Subcommand)]
3257
enum Commands {
3358
/// Create a new ontology environment
3459
Init {
35-
/// Directories to search for ontologies. If not provided, the current directory is used.
36-
search_directories: Option<Vec<PathBuf>>,
37-
/// Require ontology names to be unique; will raise an error if multiple ontologies have the same name
38-
#[clap(long, action)]
39-
require_ontology_names: bool,
40-
/// Strict mode - will raise an error if an ontology is not found
41-
#[clap(long, short, action, default_value = "false")]
42-
strict: bool,
43-
/// Offline mode - will not attempt to fetch ontologies from the web
44-
#[clap(long, short, action, default_value = "false")]
45-
offline: bool,
46-
/// Glob patterns for which files to include, defaults to ['*.ttl','*.xml','*.n3']
47-
#[clap(long, short, num_args = 1..)]
48-
includes: Vec<String>,
49-
/// Glob patterns for which files to exclude, defaults to []
50-
#[clap(long, short, num_args = 1..)]
51-
excludes: Vec<String>,
5260
/// Overwrite the environment if it already exists
5361
#[clap(long, short, default_value = "false")]
5462
overwrite: bool,
5563
/// A JSON file containing a list of ontologies to add to the environment
5664
#[clap(long = "list", short = 'l')]
5765
ontology_list_file: Option<String>,
58-
/// Do not search for ontologies in the search directories
59-
#[clap(long = "no-search", short = 'n', action)]
60-
no_search: bool,
61-
/// Use a temporary directory for the environment
62-
#[clap(long, short, action)]
63-
temporary: bool,
6466
},
6567
/// Prints the version of the ontoenv binary
6668
Version,
@@ -132,34 +134,42 @@ fn main() -> Result<()> {
132134

133135
let policy = cmd.policy.unwrap_or_else(|| "default".to_string());
134136

137+
let config: Config = Config::new(
138+
current_dir()?,
139+
cmd.search_directories,
140+
&cmd.includes,
141+
&cmd.excludes,
142+
cmd.require_ontology_names,
143+
cmd.strict,
144+
cmd.offline,
145+
policy,
146+
false,
147+
cmd.temporary,
148+
)?;
149+
150+
// create the env object to use in the subcommand.
151+
// - if temporary is true, create a new env object each time
152+
// - if temporary is false, load the env from the .ontoenv directory if it exists
153+
let mut env = if cmd.temporary {
154+
let mut env = OntoEnv::init(config, false)?;
155+
env.update()?;
156+
env
157+
} else {
158+
// if the command is NOT init and the .ontoenv directory doesn't exist, raise an error
159+
let path = current_dir()?;
160+
if let Commands::Init { .. } = cmd.command && !path.exists() {
161+
return Err(anyhow::anyhow!(
162+
"OntoEnv not found. Run `ontoenv init` to create a new OntoEnv."
163+
));
164+
}
165+
OntoEnv::load_from_directory(path)?
166+
};
167+
135168
match cmd.command {
136169
Commands::Init {
137-
search_directories,
138-
require_ontology_names,
139-
strict,
140-
offline,
141-
includes,
142-
excludes,
143170
overwrite,
144171
ontology_list_file,
145-
no_search,
146-
temporary,
147172
} => {
148-
// if search_directories is empty, use the current directory
149-
let config = Config::new(
150-
current_dir()?,
151-
search_directories,
152-
&includes,
153-
&excludes,
154-
require_ontology_names,
155-
strict,
156-
offline,
157-
policy,
158-
no_search,
159-
temporary,
160-
)?;
161-
let mut env = OntoEnv::init(config, overwrite)?;
162-
163173
// if an ontology config file is provided, load it and add the ontologies
164174
if let Some(file) = ontology_list_file {
165175
let file = File::open(file)?;
@@ -181,16 +191,17 @@ fn main() -> Result<()> {
181191
}
182192
Commands::Status => {
183193
// load env from .ontoenv/ontoenv.json
184-
let path = current_dir()?;
185-
let env = OntoEnv::load_from_directory(path)?;
186194
let status = env.status()?;
187195
// pretty print the status
188196
println!("{}", status);
189197
}
190198
Commands::Refresh => {
191-
// load env from .ontoenv/ontoenv.json
192-
let path = current_dir()?;
193-
let mut env = OntoEnv::load_from_directory(path)?;
199+
// if temporary, raise an error
200+
if cmd.temporary {
201+
return Err(anyhow::anyhow!(
202+
"Cannot refresh in temporary mode. Run `ontoenv init` to create a new OntoEnv."
203+
));
204+
}
194205
env.update()?;
195206
env.save_to_directory()?;
196207
}
@@ -200,16 +211,6 @@ fn main() -> Result<()> {
200211
remove_owl_imports,
201212
destination,
202213
} => {
203-
// load env from .ontoenv/ontoenv.json
204-
let path = current_dir()?;
205-
// if the path doesn't exist, raise an error
206-
if !path.exists() {
207-
return Err(anyhow::anyhow!(
208-
"OntoEnv not found. Run `ontoenv init` to create a new OntoEnv."
209-
));
210-
}
211-
let env = OntoEnv::load_from_directory(path)?;
212-
213214
// make ontology an IRI
214215
let iri = NamedNode::new(ontology).map_err(|e| anyhow::anyhow!(e.to_string()))?;
215216

@@ -232,10 +233,6 @@ fn main() -> Result<()> {
232233
}
233234
}
234235
Commands::Add { url, file } => {
235-
// load env from .ontoenv/ontoenv.json
236-
let path = current_dir()?;
237-
let mut env = OntoEnv::load_from_directory(path)?;
238-
239236
let location: OntologyLocation = match (url, file) {
240237
(Some(url), None) => OntologyLocation::Url(url),
241238
(None, Some(file)) => OntologyLocation::File(PathBuf::from(file)),
@@ -246,9 +243,6 @@ fn main() -> Result<()> {
246243
env.save_to_directory()?;
247244
}
248245
Commands::ListOntologies => {
249-
// load env from .ontoenv/ontoenv.json
250-
let path = current_dir()?;
251-
let env = OntoEnv::load_from_directory(path)?;
252246
// print list of ontology URLs from env.onologies.values() sorted alphabetically
253247
let mut ontologies: Vec<&GraphIdentifier> = env.ontologies().keys().collect();
254248
ontologies.sort_by(|a, b| a.name().cmp(&b.name()));
@@ -258,25 +252,16 @@ fn main() -> Result<()> {
258252
}
259253
}
260254
Commands::ListLocations => {
261-
// load env from .ontoenv/ontoenv.json
262-
let path = current_dir()?;
263-
let env = OntoEnv::load_from_directory(path)?;
264255
let mut ontologies: Vec<&GraphIdentifier> = env.ontologies().keys().collect();
265256
ontologies.sort_by(|a, b| a.location().as_str().cmp(b.location().as_str()));
266257
for ont in ontologies {
267258
println!("{}", ont.location().as_str());
268259
}
269260
}
270261
Commands::Dump { contains } => {
271-
// load env from .ontoenv/ontoenv.json
272-
let path = current_dir()?;
273-
let env = OntoEnv::load_from_directory(path)?;
274262
env.dump(contains.as_deref());
275263
}
276264
Commands::DepGraph { roots, output } => {
277-
// load env from .ontoenv/ontoenv.json
278-
let path = current_dir()?;
279-
let env = OntoEnv::load_from_directory(path)?;
280265
let dot = if let Some(roots) = roots {
281266
let roots: Vec<GraphIdentifier> = roots
282267
.iter()
@@ -305,9 +290,6 @@ fn main() -> Result<()> {
305290
}
306291
}
307292
Commands::Dependents { ontologies } => {
308-
// load env from .ontoenv/ontoenv.json
309-
let path = current_dir()?;
310-
let env = OntoEnv::load_from_directory(path)?;
311293
for ont in ontologies {
312294
let iri = NamedNode::new(ont).map_err(|e| anyhow::anyhow!(e.to_string()))?;
313295
let dependents = env.get_dependents(&iri)?;
@@ -318,9 +300,6 @@ fn main() -> Result<()> {
318300
}
319301
}
320302
Commands::Doctor => {
321-
// load env from .ontoenv/ontoenv.json
322-
let path = current_dir()?;
323-
let env = OntoEnv::load_from_directory(path)?;
324303
env.doctor();
325304
}
326305
Commands::Reset => {

lib/src/api.rs

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ impl OntoEnv {
7373

7474
/// Saves the current environment to the .ontoenv directory.
7575
pub fn save_to_directory(&self) -> Result<()> {
76+
if self.config.temporary {
77+
return Err(anyhow::anyhow!("Cannot save a temporary environment"));
78+
}
7679
let ontoenv_dir = self.config.root.join(".ontoenv");
7780
info!("Saving ontology environment to: {:?}", ontoenv_dir);
7881
std::fs::create_dir_all(&ontoenv_dir)?;
@@ -100,6 +103,14 @@ impl OntoEnv {
100103
self.io.flush()
101104
}
102105

106+
pub fn new_temporary(&self) -> Result<Self> {
107+
let io: Box<dyn GraphIO> = Box::new(crate::io::MemoryGraphIO::new(
108+
self.config.offline,
109+
self.config.strict,
110+
));
111+
Ok(Self::new(self.env.clone(), io, self.config.clone()))
112+
}
113+
103114
/// Loads the environment from the .ontoenv directory.
104115
pub fn load_from_directory(root: PathBuf) -> Result<Self> {
105116
let ontoenv_dir = root.join(".ontoenv");
@@ -135,16 +146,24 @@ impl OntoEnv {
135146
}
136147
env.locations = locations;
137148

138-
// Initialize the IO
139-
let io: Box<dyn GraphIO> = if config.temporary {
140-
Box::new(crate::io::MemoryGraphIO::new(config.offline, config.strict))
141-
} else {
142-
Box::new(crate::io::PersistentGraphIO::new(
149+
// Initialize the IO to the persistent graph type. We know that it exists because we
150+
// are loading from a directory
151+
let mut io: Box<dyn GraphIO> = Box::new(crate::io::PersistentGraphIO::new(
143152
ontoenv_dir.into(),
144153
config.offline,
145154
config.strict,
146-
)?)
147-
};
155+
)?);
156+
157+
// copy the graphs from the persistent store to the memory store if we are a 'temporary'
158+
// environment
159+
if config.temporary {
160+
let mut new_io = Box::new(crate::io::MemoryGraphIO::new(config.offline, config.strict));
161+
for ontology in env.ontologies().values() {
162+
let graph = io.get_graph(ontology.id())?;
163+
new_io.add_graph(ontology.id().clone(), graph);
164+
}
165+
io = new_io;
166+
}
148167

149168
Ok(OntoEnv {
150169
env,
@@ -209,7 +228,7 @@ impl OntoEnv {
209228
pub fn init(config: Config, overwrite: bool) -> Result<Self> {
210229
let ontoenv_dir = config.root.join(".ontoenv");
211230

212-
if ontoenv_dir.exists() {
231+
if !config.temporary && ontoenv_dir.exists() {
213232
if overwrite {
214233
info!(
215234
"Directory exists and will be overwritten: {:?}",
@@ -224,7 +243,9 @@ impl OntoEnv {
224243
}
225244
}
226245

227-
std::fs::create_dir_all(&ontoenv_dir)?;
246+
if !config.temporary {
247+
std::fs::create_dir_all(&ontoenv_dir)?;
248+
}
228249

229250
let env = Environment::new();
230251
let io: Box<dyn GraphIO> = match config.temporary {

lib/src/io.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,10 @@ impl MemoryGraphIO {
271271
strict,
272272
}
273273
}
274+
275+
pub fn add_graph(&mut self, id: GraphIdentifier, graph: Graph) {
276+
self.graphs.insert(id, graph);
277+
}
274278
}
275279

276280
impl GraphIO for MemoryGraphIO {

lib/tests/ontoenv_test.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use anyhow::Result;
22
use ontoenv::config::{Config, HowCreated};
33
use ontoenv::ontology::OntologyLocation;
4-
use ontoenv::OntoEnv;
4+
use ontoenv::api::OntoEnv;
55
use oxigraph::model::NamedNodeRef;
66
use std::path::PathBuf;
77
use tempdir::TempDir;

0 commit comments

Comments
 (0)