use clap::{ArgGroup, Args, Parser, Subcommand, ValueEnum};
use clap_complete::Shell;
#[derive(Parser)]
#[command(
name = "medi",
author = "Claes Adamsson @cladam",
version,
about = "⚡ medi: A speedy CLI Markdown manager ⚡",
long_about = None)]
#[command(propagate_version = true)]
pub struct Cli {
#[command(subcommand)]
pub command: Commands,
}
#[derive(ValueEnum, Clone, Debug, Default)]
pub enum SortBy {
#[default] Key,
Created,
Modified,
}
#[derive(Args, Debug)]
#[command(group(
ArgGroup::new("input_source")
.required(true)
))]
pub struct ImportArgs {
#[arg(long, group = "input_source")]
pub dir: Option<String>,
#[arg(long, group = "input_source", requires = "key")]
pub file: Option<String>,
#[arg(long)]
pub key: Option<String>,
#[arg(long, action = clap::ArgAction::SetTrue)]
pub overwrite: bool,
}
#[derive(ValueEnum, Clone, Debug)]
pub enum ExportFormat {
Markdown,
Json,
}
#[derive(Args, Debug)]
pub struct ExportArgs {
pub path: String,
#[arg(long, value_enum, default_value_t = ExportFormat::Markdown)]
pub format: ExportFormat,
#[arg(long, short)]
pub tag: Vec<String>,
}
#[derive(Subcommand, Clone, Debug)]
pub enum TaskCommands {
Add {
note_key: String,
description: String,
},
List,
Done {
task_id: u64,
},
Prio {
task_id: u64,
},
Delete {
task_id: u64,
},
Reset {
#[arg(long, short, action = clap::ArgAction::SetTrue)]
force: bool,
},
}
#[derive(Subcommand)]
pub enum Commands {
#[command(after_help = "EXAMPLE:\n \
# Interactively (default): Opens your default editor for long-form content.\n \
medi new \"my-long-article\"\n\n \
# With a direct message: Perfect for quick, one-line notes. \n \
medi new quick-idea -m \"Remember to buy milk\"\n\n \
# From a pipe: Use the output of other commands as your note content.\n \
echo \"This is a note from a pipe\" | medi new piped-note \n\n \
# With tags: Add tags to your note for better organization.\n \
medi new \"my-long-article\" --tag tag1 --tag tag2\n\n \
# With a title: Specify a title for your note.\n \
medi new \"my-long-article\" --title \"My Long Article\"\n")]
New {
key: String,
#[arg(short, long)]
message: Option<String>,
#[arg(short = 'T', long)]
tag: Vec<String>,
#[arg(short, long)]
title: Option<String>,
#[arg(long)]
template: Option<String>,
},
#[command(after_help = "EXAMPLE:\n \
# Edit an existing note: Opens your default editor for long-form content.\n \
medi edit \"my-long-article\"\n\n \
# Add tags to a note: Adds one or more tags to the note.\n \
medi edit \"my-long-article\" --add-tag tag1 --add-tag tag2\n\n \
# Remove tags from a note: Removes one or more tags from the note.\n \
medi edit \"my-long-article\" --rm-tag tag1 --rm-tag tag2\n")]
Edit {
key: String,
#[arg(long, short = 'a')]
add_tag: Vec<String>,
#[arg(long, short = 'r')]
rm_tag: Vec<String>,
},
#[command(after_help = "EXAMPLE:\n \
# Get a note: Displays the content of the note with the specified key.\n \
medi get \"my-long-article\"\n\n \
# Use this command to quickly view the content of a note without editing it.\n \
# Pipe to a Markdown renderer like mdcat \n \
medi get \"my-first-article\" | mdcat\n\n \
# You can also use this command to extract specific notes from a list.\n \
# For example, to get a note with a specific key:\n \
medi list | grep -o \"my-article\" | xargs medi get\n\n \
# Write the output to a file:\n \
medi get \"my-long-article\" > my-note.md\n\n \
# Use --json to output the note in JSON format:\n \
medi get \"my-long-article\" --json\n\n \
# Use --tag to retrieve all notes with a specific tag:\n \
medi get --tag my-tag\n")]
Get {
#[arg(required_unless_present("tag"))]
keys: Vec<String>,
#[arg(long, short, conflicts_with = "keys")]
tag: Vec<String>,
#[arg(long, action = clap::ArgAction::SetTrue)]
json: bool,
},
#[command(after_help = "EXAMPLE:\n \
# List all notes: Displays a list of all notes in the database.\n \
medi list\n\n \
# Use this command to quickly see all your notes and their keys.\n \
# You can also pipe the output to other commands for further processing.\n \
medi list | grep -o \"my-article\" | xargs medi get\n\n \
# Use --sort-by to sort the notes by key, created date, or modified date\n \
medi list --sort-by key")]
List {
#[arg(long, short, value_enum, default_value_t = SortBy::Key)]
sort_by: SortBy,
},
#[command(after_help = "EXAMPLE:\n \
# Create a note that links to another note:\n \
medi new medi-project -m \"medi is a CLI tool built in [[rust]].\"\n\n \
# Create the target note:\n \
medi new rust -m \"A systems programming language.\"\n\n \
# Find backlinks: Lists all notes that link to the specified note key.\n \
medi backlinks rust\n\n \
# Use this command to discover relationships between your notes and see which notes reference a particular note.")]
Backlinks {
key: String,
},
#[command(after_help = "EXAMPLE:\n \
# Delete a note: Removes the note with the specified key.\n \
medi delete \"my-long-article\"\n\n \
# Use --force to skip confirmation.\n \
medi delete \"my-long-article\" --force\n\n \
# Note: Use this command with caution, as it will permanently delete the note.")]
Delete {
key: String,
#[arg(long, short, action = clap::ArgAction::SetTrue)]
force: bool,
},
#[command(after_help = "EXAMPLE:\n \
# Search for notes containing a specific term: Finds notes with 'meeting' in the content.\n \
medi search meeting")]
Search {
query: String,
},
#[command(after_help = "EXAMPLE:\n \
# Reindex the search index: Rebuilds the search index from the existing notes.\n \
medi reindex\n\n \
# Use this command if you suspect the search index is out of sync with the notes.")]
Reindex,
#[command(after_help = "EXAMPLE:\n \
# Find and edit a note: Opens an interactive prompt to search and edit notes.\n \
medi find\n\n \
# Use this command to quickly locate and modify notes without needing to remember their keys.")]
Find,
#[command(after_help = "EXAMPLE:\n \
# Import from a directory: Imports all .md files from the specified directory.\n \
medi import --dir /path/to/notes\n\n \
# Import a single file: Imports a single .md file with an mandatory key.\n \
medi import --file /path/to/note.md --key my-note\n\n \
# Use --overwrite to replace an existing note with the same key.\n \
medi import --file /path/to/note.md --key my-note --overwrite")]
Import(ImportArgs),
Export(ExportArgs),
#[command(after_help = "EXAMPLE:\n \
# Add a new task linked to a note:\n \
medi task add my-note \"Finish writing the introduction\"\n\n \
# List all open tasks:\n \
medi task list\n\n \
# Mark a task as done:\n \
medi task done 1\n \n \
# Prioritise a task:\n \
medi task prio 42\n\n \
# Delete a task:\n \
medi task delete 42\n\n \
# Reset all tasks (use with caution):\n \
medi task reset")]
Task {
#[command(subcommand)]
command: TaskCommands,
},
#[command(after_help = "EXAMPLE:\n \
# Show a summary of the notes and tags in the database.\n] \
medi status\n\n \
medi status --key my-note")]
Status {
key: Option<String>,
},
#[command(after_help = "EXAMPLE:\n \
# Lint all notes: Checks all notes for common issues.\n \
medi lint\n\n \
# Lint a specific note: Checks the note with the specified key for issues.\n \
medi lint --key my-note")]
Lint {
key: Option<String>,
},
#[command(after_help = "EXAMPLE:\n \
# Render a note: Opens a live preview of the note in your default web browser.\n \
medi preview my-note\n\n \
# Use this command to quickly view how your Markdown note will look when rendered.")]
Preview {
key: String,
},
#[command(name = "generate-completion", hide = true)] Completion {
#[arg(value_enum)]
shell: Shell,
},
#[command(name = "update", hide = true)] Update,
}