use capsec::prelude::*;
struct ConfigService {
cap: Cap<FsRead>,
base_dir: String,
}
#[allow(dead_code)] impl ConfigService {
fn new(base_dir: impl Into<String>, cap: Cap<FsRead>) -> Self {
Self {
cap,
base_dir: base_dir.into(),
}
}
fn get(&self, key: &str) -> Option<String> {
let path = format!("{}/{}.toml", self.base_dir, key);
capsec::fs::read_to_string(&path, &self.cap).ok()
}
fn exists(&self, key: &str) -> bool {
let path = format!("{}/{}.toml", self.base_dir, key);
capsec::fs::metadata(&path, &self.cap).is_ok()
}
fn list_keys(&self) -> Vec<String> {
capsec::fs::read_dir(&self.base_dir, &self.cap)
.into_iter()
.flatten()
.filter_map(|e| e.ok())
.filter_map(|e| {
let path = e.path();
if path.extension().and_then(|s| s.to_str()) == Some("toml") {
path.file_stem().and_then(|s| s.to_str().map(String::from))
} else {
None
}
})
.collect()
}
}
struct OutputWriter {
cap: Cap<FsWrite>,
output_dir: String,
}
impl OutputWriter {
fn new(output_dir: impl Into<String>, cap: Cap<FsWrite>) -> Self {
Self {
cap,
output_dir: output_dir.into(),
}
}
fn save(&self, name: &str, data: &str) -> Result<(), CapSecError> {
let path = format!("{}/{}", self.output_dir, name);
capsec::fs::write(&path, data.as_bytes(), &self.cap)
}
fn ensure_dir(&self) -> Result<(), CapSecError> {
capsec::fs::create_dir_all(&self.output_dir, &self.cap)
}
}
fn process_and_save(config: &ConfigService, output: &OutputWriter) {
let db_config = config
.get("database")
.unwrap_or_else(|| "host=localhost port=5432".into());
let app_config = config
.get("app")
.unwrap_or_else(|| "mode=development".into());
let report = format!(
"Config Report\n=============\nDB: {}\nApp: {}\nKeys found: {:?}",
db_config,
app_config,
config.list_keys()
);
if let Err(e) = output.ensure_dir() {
eprintln!("Warning: could not create output dir: {e}");
}
match output.save("report.txt", &report) {
Ok(()) => println!("Report saved."),
Err(e) => eprintln!("Warning: could not save report: {e}"),
}
println!("{report}");
}
#[capsec::main]
fn main(root: CapRoot) {
let config = ConfigService::new("/tmp/capsec-demo/config", root.grant::<FsRead>());
let output = OutputWriter::new("/tmp/capsec-demo/output", root.grant::<FsWrite>());
process_and_save(&config, &output);
println!("\nDone. The domain wrapper pattern hid all capabilities from business logic.");
}