use mdstore::config::IdStrategy;
use mdstore::{CreateOptions, Filters, Item, TypeConfig, TypeFeatures, UpdateOptions};
use std::collections::HashMap;
use std::path::{Path, PathBuf};
fn issue_config() -> TypeConfig {
TypeConfig {
name: "Issue".to_string(),
identifier: IdStrategy::Uuid,
features: TypeFeatures {
display_number: true,
status: true,
priority: true,
assets: false,
org_sync: false,
move_item: true,
duplicate: true,
},
statuses: vec![
"open".to_string(),
"in-progress".to_string(),
"closed".to_string(),
],
default_status: Some("open".to_string()),
priority_levels: Some(3),
custom_fields: Vec::new(),
}
}
async fn create_items(
type_dir: &Path,
config: &TypeConfig,
) -> Result<(Item, Item), Box<dyn std::error::Error>> {
let issue1 = mdstore::create(
type_dir,
config,
CreateOptions {
title: "Fix login timeout".to_string(),
body: "Users experience timeouts after 30s of inactivity.".to_string(),
id: None,
status: Some("open".to_string()),
priority: Some(1),
tags: None,
projects: None,
custom_fields: HashMap::new(),
comment: None,
},
)
.await?;
println!(
"Created issue #{}: {} (id: {})",
issue1.frontmatter.display_number.unwrap_or(0),
issue1.title,
issue1.id
);
let issue2 = mdstore::create(
type_dir,
config,
CreateOptions {
title: "Add dark mode".to_string(),
body: "Support system-level dark mode preference.".to_string(),
id: None,
status: Some("open".to_string()),
priority: Some(3),
tags: None,
projects: None,
custom_fields: HashMap::from([(
"component".to_string(),
serde_json::json!("frontend"),
)]),
comment: None,
},
)
.await?;
println!(
"Created issue #{}: {}",
issue2.frontmatter.display_number.unwrap_or(0),
issue2.title
);
Ok((issue1, issue2))
}
async fn demo_list_update_get(
type_dir: &Path,
config: &TypeConfig,
issue1: &Item,
) -> Result<(), Box<dyn std::error::Error>> {
let open_issues = mdstore::list(type_dir, Filters::new().with_status("open")).await?;
println!("\nOpen issues: {}", open_issues.len());
for item in &open_issues {
println!(
" #{} [P{}] {}",
item.frontmatter.display_number.unwrap_or(0),
item.frontmatter.priority.unwrap_or(0),
item.title
);
}
let updated = mdstore::update(
type_dir,
config,
&issue1.id,
UpdateOptions {
status: Some("in-progress".to_string()),
..Default::default()
},
)
.await?;
println!(
"\nUpdated '{}' -> status: {}",
updated.title,
updated.frontmatter.status.as_deref().unwrap_or("none")
);
let fetched = mdstore::get(type_dir, &issue1.id).await?;
println!(
"\nFetched: {} (status: {}, priority: {})",
fetched.title,
fetched.frontmatter.status.as_deref().unwrap_or("none"),
fetched.frontmatter.priority.unwrap_or(0)
);
Ok(())
}
async fn demo_delete_restore(
type_dir: &Path,
issue2: &Item,
) -> Result<(), Box<dyn std::error::Error>> {
mdstore::soft_delete(type_dir, &issue2.id).await?;
println!("\nSoft-deleted '{}'", issue2.title);
let visible = mdstore::list(type_dir, Filters::default()).await?;
println!("Visible items: {}", visible.len());
let all = mdstore::list(type_dir, Filters::new().include_deleted()).await?;
println!("All items (including deleted): {}", all.len());
mdstore::restore(type_dir, &issue2.id).await?;
println!("Restored '{}'", issue2.title);
mdstore::delete(type_dir, &issue2.id, true).await?;
println!("Hard-deleted '{}'", issue2.title);
let remaining = mdstore::list(type_dir, Filters::default()).await?;
println!("\nRemaining items: {}", remaining.len());
Ok(())
}
fn output_dir() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR")).join("examples/output/basic_crud")
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let base = output_dir();
if base.exists() {
tokio::fs::remove_dir_all(&base).await?;
}
let type_dir = base.join("issues");
let config = issue_config();
let (issue1, issue2) = create_items(&type_dir, &config).await?;
demo_list_update_get(&type_dir, &config, &issue1).await?;
demo_delete_restore(&type_dir, &issue2).await?;
Ok(())
}