refactor: rewrite in rust
This commit is contained in:
parent
20a3e8b437
commit
ed493cff29
72 changed files with 5684 additions and 3688 deletions
11
src/cli/commands/completions.rs
Normal file
11
src/cli/commands/completions.rs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
use clap::CommandFactory;
|
||||
use clap_complete::{generate, Shell};
|
||||
use std::io;
|
||||
|
||||
use crate::cli::Cli;
|
||||
|
||||
pub fn run(shell: Shell) {
|
||||
let mut cmd = Cli::command();
|
||||
let name = cmd.get_name().to_string();
|
||||
generate(shell, &mut cmd, name, &mut io::stdout());
|
||||
}
|
||||
73
src/cli/commands/edit.rs
Normal file
73
src/cli/commands/edit.rs
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
use std::fs;
|
||||
use std::process::Command;
|
||||
|
||||
use walkdir::WalkDir;
|
||||
|
||||
use crate::config::Settings;
|
||||
use crate::error::StreamdError;
|
||||
use crate::extract::parse_markdown_file;
|
||||
use crate::localize::{localize_stream_file, TaskConfiguration};
|
||||
use crate::models::LocalizedShard;
|
||||
|
||||
fn all_files() -> Result<Vec<LocalizedShard>, StreamdError> {
|
||||
let settings = Settings::load()?;
|
||||
let mut shards = Vec::new();
|
||||
|
||||
for entry in WalkDir::new(&settings.base_folder)
|
||||
.max_depth(1)
|
||||
.into_iter()
|
||||
.filter_map(|e| e.ok())
|
||||
{
|
||||
let path = entry.path();
|
||||
if path.extension().map(|e| e == "md").unwrap_or(false) {
|
||||
let file_name = path.to_string_lossy().to_string();
|
||||
let content = fs::read_to_string(path)?;
|
||||
let stream_file = parse_markdown_file(&file_name, &content);
|
||||
|
||||
if let Ok(shard) = localize_stream_file(&stream_file, &TaskConfiguration) {
|
||||
shards.push(shard);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(shards)
|
||||
}
|
||||
|
||||
pub fn run(number: i32) -> Result<(), StreamdError> {
|
||||
let all_shards = all_files()?;
|
||||
|
||||
// Sort by moment (timestamp)
|
||||
let mut sorted_shards = all_shards;
|
||||
sorted_shards.sort_by_key(|s| s.moment);
|
||||
|
||||
if sorted_shards.is_empty() {
|
||||
return Err(StreamdError::ConfigError("No files found".to_string()));
|
||||
}
|
||||
|
||||
let selected_index = if number >= 0 {
|
||||
// 0 = most recent, 1 = second most recent, etc.
|
||||
let idx = sorted_shards.len() as i32 - number;
|
||||
if idx < 0 {
|
||||
return Err(StreamdError::ConfigError(
|
||||
"Argument out of range".to_string(),
|
||||
));
|
||||
}
|
||||
idx as usize
|
||||
} else {
|
||||
// -1 = oldest, -2 = second oldest, etc.
|
||||
let idx = (-number - 1) as usize;
|
||||
if idx >= sorted_shards.len() {
|
||||
return Err(StreamdError::ConfigError(
|
||||
"Argument out of range".to_string(),
|
||||
));
|
||||
}
|
||||
idx
|
||||
};
|
||||
|
||||
if let Some(file_path) = sorted_shards[selected_index].location.get("file") {
|
||||
let editor = std::env::var("EDITOR").unwrap_or_else(|_| "vi".to_string());
|
||||
Command::new(&editor).arg(file_path).status()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
5
src/cli/commands/mod.rs
Normal file
5
src/cli/commands/mod.rs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
pub mod completions;
|
||||
pub mod edit;
|
||||
pub mod new;
|
||||
pub mod timesheet;
|
||||
pub mod todo;
|
||||
60
src/cli/commands/new.rs
Normal file
60
src/cli/commands/new.rs
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
use std::fs;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
|
||||
use chrono::Local;
|
||||
|
||||
use crate::config::Settings;
|
||||
use crate::error::StreamdError;
|
||||
use crate::extract::parse_markdown_file;
|
||||
|
||||
pub fn run() -> Result<(), StreamdError> {
|
||||
let settings = Settings::load()?;
|
||||
let streamd_directory = &settings.base_folder;
|
||||
|
||||
let timestamp = Local::now().format("%Y%m%d-%H%M%S").to_string();
|
||||
let preliminary_file_name = format!("{}_wip.md", timestamp);
|
||||
let preliminary_path = Path::new(streamd_directory).join(&preliminary_file_name);
|
||||
|
||||
// Create initial file with heading
|
||||
let content = "# ";
|
||||
let mut file = fs::File::create(&preliminary_path)?;
|
||||
file.write_all(content.as_bytes())?;
|
||||
drop(file);
|
||||
|
||||
// Open in editor
|
||||
let editor = std::env::var("EDITOR").unwrap_or_else(|_| "vi".to_string());
|
||||
let status = Command::new(&editor).arg(&preliminary_path).status()?;
|
||||
|
||||
if !status.success() {
|
||||
return Err(StreamdError::IoError(std::io::Error::other(
|
||||
"Editor exited with non-zero status",
|
||||
)));
|
||||
}
|
||||
|
||||
// Read the edited content
|
||||
let edited_content = fs::read_to_string(&preliminary_path)?;
|
||||
let parsed_content =
|
||||
parse_markdown_file(preliminary_path.to_string_lossy().as_ref(), &edited_content);
|
||||
|
||||
// Determine final filename based on markers
|
||||
let final_file_name = if let Some(ref shard) = parsed_content.shard {
|
||||
if !shard.markers.is_empty() {
|
||||
format!("{} {}.md", timestamp, shard.markers.join(" "))
|
||||
} else {
|
||||
format!("{}.md", timestamp)
|
||||
}
|
||||
} else {
|
||||
format!("{}.md", timestamp)
|
||||
};
|
||||
|
||||
let final_path = Path::new(streamd_directory).join(&final_file_name);
|
||||
|
||||
// Rename the file
|
||||
fs::rename(&preliminary_path, &final_path)?;
|
||||
|
||||
println!("Saved as {}", final_file_name);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
52
src/cli/commands/timesheet.rs
Normal file
52
src/cli/commands/timesheet.rs
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
use std::fs;
|
||||
|
||||
use walkdir::WalkDir;
|
||||
|
||||
use crate::config::Settings;
|
||||
use crate::error::StreamdError;
|
||||
use crate::extract::parse_markdown_file;
|
||||
use crate::localize::localize_stream_file;
|
||||
use crate::models::LocalizedShard;
|
||||
use crate::timesheet::{extract_timesheets, BasicTimesheetConfiguration};
|
||||
|
||||
fn all_files() -> Result<Vec<LocalizedShard>, StreamdError> {
|
||||
let settings = Settings::load()?;
|
||||
let mut shards = Vec::new();
|
||||
|
||||
for entry in WalkDir::new(&settings.base_folder)
|
||||
.max_depth(1)
|
||||
.into_iter()
|
||||
.filter_map(|e| e.ok())
|
||||
{
|
||||
let path = entry.path();
|
||||
if path.extension().map(|e| e == "md").unwrap_or(false) {
|
||||
let file_name = path.to_string_lossy().to_string();
|
||||
let content = fs::read_to_string(path)?;
|
||||
let stream_file = parse_markdown_file(&file_name, &content);
|
||||
|
||||
if let Ok(shard) = localize_stream_file(&stream_file, &BasicTimesheetConfiguration) {
|
||||
shards.push(shard);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(shards)
|
||||
}
|
||||
|
||||
pub fn run() -> Result<(), StreamdError> {
|
||||
let all_shards = all_files()?;
|
||||
let mut sheets = extract_timesheets(&all_shards)?;
|
||||
sheets.sort_by_key(|s| s.date);
|
||||
|
||||
for sheet in sheets {
|
||||
println!("{}", sheet.date);
|
||||
let times: Vec<String> = sheet
|
||||
.timecards
|
||||
.iter()
|
||||
.map(|card| format!("{},{}", card.from_time, card.to_time))
|
||||
.collect();
|
||||
println!("{}", times.join(","));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
56
src/cli/commands/todo.rs
Normal file
56
src/cli/commands/todo.rs
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
use std::fs;
|
||||
|
||||
use walkdir::WalkDir;
|
||||
|
||||
use crate::config::Settings;
|
||||
use crate::error::StreamdError;
|
||||
use crate::extract::parse_markdown_file;
|
||||
use crate::localize::{localize_stream_file, TaskConfiguration};
|
||||
use crate::models::LocalizedShard;
|
||||
use crate::query::find_shard_by_position;
|
||||
|
||||
fn all_files() -> Result<Vec<LocalizedShard>, StreamdError> {
|
||||
let settings = Settings::load()?;
|
||||
let mut shards = Vec::new();
|
||||
|
||||
for entry in WalkDir::new(&settings.base_folder)
|
||||
.max_depth(1)
|
||||
.into_iter()
|
||||
.filter_map(|e| e.ok())
|
||||
{
|
||||
let path = entry.path();
|
||||
if path.extension().map(|e| e == "md").unwrap_or(false) {
|
||||
let file_name = path.to_string_lossy().to_string();
|
||||
let content = fs::read_to_string(path)?;
|
||||
let stream_file = parse_markdown_file(&file_name, &content);
|
||||
|
||||
if let Ok(shard) = localize_stream_file(&stream_file, &TaskConfiguration) {
|
||||
shards.push(shard);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(shards)
|
||||
}
|
||||
|
||||
pub fn run() -> Result<(), StreamdError> {
|
||||
let all_shards = all_files()?;
|
||||
|
||||
for task_shard in find_shard_by_position(&all_shards, "task", "open") {
|
||||
if let Some(file_path) = task_shard.location.get("file") {
|
||||
let content = fs::read_to_string(file_path)?;
|
||||
let lines: Vec<&str> = content.lines().collect();
|
||||
|
||||
let start = task_shard.start_line.saturating_sub(1);
|
||||
let end = std::cmp::min(task_shard.end_line, lines.len());
|
||||
|
||||
println!("--- {}:{} ---", file_path, task_shard.start_line);
|
||||
for line in &lines[start..end] {
|
||||
println!("{}", line);
|
||||
}
|
||||
println!();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue