Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env.dist
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
NOST_WORK_CURRENCY=EUR
RUST_LOG=debug
NOT_PATH=/your-not-path/not
28 changes: 27 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
[package]
name = "nost"
version = "0.1.0"
version = "0.2.1"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
regex = "1"
dotenv = "0.15.0"
Expand All @@ -13,10 +12,11 @@ log = "0.4.28"
env_logger = "0.11.8"
toml = "0.8"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0.149"

[dependencies.uuid]
version = "1.17.0"
# Lets you generate random UUIDs
# generate random UUIDs
features = [
"v4",
]
Expand Down
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ Copy `config.toml.dist` into `config.toml` and update the values. For example:
```toml
not_path="/home/gaetan/not"
language="fr"
log_level="debug"
```

## Build the app
Expand Down
1 change: 0 additions & 1 deletion config.toml.dist
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
not_path=""
# optionnal, default is 'en', only 'en' and 'fr' are supported for now
language="en"
log_level="debug"
6 changes: 3 additions & 3 deletions src/annotations/annotate.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
use uuid::Uuid;

use crate::{
dates::get::get_now_as_string, events::models::NotEvent, files::append::append,
dates::get::get_now_as_string, events::models::EventName, files::append::append,
files::check_content::ends_with_line_break,
};

pub fn annotate(
date: Option<&str>,
event: NotEvent,
event: EventName,
input_uid: Option<&Uuid>,
not_path: &str,
workday: Option<&str>,
Expand Down Expand Up @@ -73,7 +73,7 @@ mod tests {
// Call annotate
super::annotate(
None,
crate::events::models::NotEvent::CreateNot,
crate::events::models::EventName::CreateNot,
None,
file_path.to_str().unwrap(),
None,
Expand Down
4 changes: 2 additions & 2 deletions src/annotations/filter.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use crate::{annotations::models::Annotation, events::models::NotEvent};
use crate::{annotations::models::Annotation, events::models::EventName};

pub fn filter_annotation_by_events(
annotations: Vec<Annotation>,
event: Vec<NotEvent>,
event: Vec<EventName>,
) -> Vec<Annotation> {
annotations
.into_iter()
Expand Down
4 changes: 2 additions & 2 deletions src/annotations/models.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use crate::events::models::NotEvent;
use crate::events::models::EventName;
use chrono::{DateTime, FixedOffset};
use uuid::Uuid;

#[derive(Debug, Clone)]
pub struct Annotation {
pub _uid: Uuid,
pub event: NotEvent,
pub event: EventName,
pub datetime: DateTime<FixedOffset>,
// todo: remove from core, should be plugin specific
pub workday: Option<String>,
Expand Down
9 changes: 6 additions & 3 deletions src/annotations/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use uuid::Uuid;

use crate::{
annotations::{extract::extract_field_from_annotation, models::Annotation},
events::models::NotEvent,
events::models::EventName,
};

pub fn parse_annotation(annotation_in_text: &str) -> Result<Annotation, &str> {
Expand All @@ -15,7 +15,7 @@ pub fn parse_annotation(annotation_in_text: &str) -> Result<Annotation, &str> {

// extract event
let event = extract_field_from_annotation(annotation_in_text, "event")
.and_then(|event_str| NotEvent::from_str(&event_str).ok())
.and_then(|event_str| EventName::from_str(&event_str).ok())
.ok_or("Missing or invalid event")?;

// extract uid
Expand Down Expand Up @@ -46,7 +46,10 @@ mod tests {
annotation.datetime.to_rfc3339(),
"2025-09-29T00:00:43+02:00"
);
assert_eq!(annotation.event, crate::events::models::NotEvent::StartWork);
assert_eq!(
annotation.event,
crate::events::models::EventName::StartWork
);
assert_eq!(
annotation._uid.to_string(),
"b86bc6ed-50a5-4ef2-bdd3-e17baef11eff"
Expand Down
1 change: 1 addition & 0 deletions src/commands/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod new;
pub mod new_folder;
16 changes: 14 additions & 2 deletions src/commands/new.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use crate::files::create::create_file;
use crate::{
files::create::{create_file, create_note_file_with_folders},
projects::initialize::initialize_project,
};

pub fn new(args: Vec<String>) {
pub fn new_legacy(args: Vec<String>) {
if args.len() > 2 {
println!("Creating not with title: {}", args[1]);
create_file(Some(args[2].clone())).unwrap();
Expand All @@ -10,3 +13,12 @@ pub fn new(args: Vec<String>) {

std::process::exit(0);
}

pub fn new() {
println!("Creating new note for today...");
let _ = initialize_project();
let _ = create_note_file_with_folders("default".to_string());

println!("✅ Note has been created successfully!");
std::process::exit(0);
}
50 changes: 50 additions & 0 deletions src/commands/new_folder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use std::fs::{create_dir_all, File};

use crate::{
configurations::get::get_value_from_config, files::build_paths::build_folder_path_for_now,
};

pub fn new_folder() -> std::io::Result<String> {
// compose path of the day
let not_path = get_value_from_config("not_path").unwrap();
let new_folder_path = build_folder_path_for_now(&not_path);

// create folder of the composed path (and recursive parents if needed)
match create_dir_all(&new_folder_path) {
Ok(_result) => {
println!("✅ Folder has been created successfully!");

// create a the config { "created_at": "2025-12-31T23:59:59Z", "type": "day" }
let config_file_path = format!("{}{}", &new_folder_path, ".not-config.json");

match File::create(&config_file_path) {
Ok(_file) => {
println!("✅ Config file created: {}", config_file_path);
// add minimal content to the config file
}
Err(e) => {
eprintln!("Error creating file: {}", e);
}
};

// create the default file in the folder
let default_file_path = format!("{}{}", &new_folder_path, "not.md");

match File::create(&default_file_path) {
Ok(_file) => {
println!("✅ Default file created: {}", default_file_path);
// add minimal content to the default file: the date and add an info in config file
}
Err(e) => {
eprintln!("Error creating file: {}", e);
}
};
}
Err(e) => {
eprintln!("Error creating folder: {}", e);
return Err(e);
}
}

Ok("Nost has created the folder successfully!".to_string())
}
4 changes: 4 additions & 0 deletions src/configurations/get.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ pub fn get_config() -> Result<Config, Box<dyn std::error::Error>> {
Ok(config)
}

/**
* Get a specific value from the configuration file based on the provided key.
* i.e. get_value_from_config("not_path") will return the value of the "not_path" key in the configuration file.
*/
pub fn get_value_from_config(key: &str) -> Result<String, Box<dyn std::error::Error>> {
let configuration = get_config().unwrap();

Expand Down
4 changes: 1 addition & 3 deletions src/configurations/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,17 @@ use std::path::PathBuf;
pub struct Config {
pub not_path: PathBuf,
pub language: String,
pub log_level: String,
}

impl Config {
pub fn keys() -> &'static [&'static str] {
&["not_path", "language", "log_level"]
&["not_path", "language"]
}

pub fn get_value(&self, key: &str) -> Option<String> {
match key {
"not_path" => Some(self.not_path.to_string_lossy().into_owned()),
"language" => Some(self.language.clone()),
"log_level" => Some(self.log_level.clone()),
_ => None,
}
}
Expand Down
8 changes: 6 additions & 2 deletions src/dates/get.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
use chrono::{Datelike, Local};
use chrono::{DateTime, Datelike, Local};

pub fn get_now_as_string() -> String {
let now = Local::now();
let now: DateTime<Local> = Local::now();
format!("{}{}", now.format("%Y-%m-%dT%H:%M:%S"), now.format("%:z"))
}

pub fn get_day_as_string(datetime: DateTime<Local>) -> String {
format!("{}", datetime.format("%d"))
}

pub fn get_week_of_month() -> u32 {
let today = chrono::Local::now().date_naive();

Expand Down
1 change: 1 addition & 0 deletions src/events/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod models;
pub mod record;
47 changes: 35 additions & 12 deletions src/events/models.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,55 @@
use chrono::Local;
use serde::{Deserialize, Serialize};
use std::fmt;
use uuid::Uuid;

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum NotEvent {
// todo: move in work event enum
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum EventName {
StartWork,
StopWork,
// keep in core
CreateNot,
}

impl fmt::Display for NotEvent {
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Event {
pub datetime: String,
pub event: String,
pub day: String,
pub not_type: String,
pub uid: String,
}

impl Event {
pub fn now(event_name: EventName, not_type: String) -> Self {
let now = Local::now();
Self {
datetime: now.to_rfc3339(),
event: format!("{}", event_name),
day: now.format("%Y-%m-%d").to_string(),
not_type,
uid: Uuid::new_v4().to_string(),
}
}
}

impl fmt::Display for EventName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
NotEvent::StartWork => write!(f, "START_WORK"),
NotEvent::StopWork => write!(f, "STOP_WORK"),
NotEvent::CreateNot => write!(f, "CREATE_NOT"),
EventName::StartWork => write!(f, "START_WORK"),
EventName::StopWork => write!(f, "STOP_WORK"),
EventName::CreateNot => write!(f, "CREATE_NOT"),
}
}
}

impl std::str::FromStr for NotEvent {
impl std::str::FromStr for EventName {
type Err = ();

fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
match s {
"START_WORK" => Ok(NotEvent::StartWork),
"STOP_WORK" => Ok(NotEvent::StopWork),
"CREATE_NOT" => Ok(NotEvent::CreateNot),
"START_WORK" => Ok(EventName::StartWork),
"STOP_WORK" => Ok(EventName::StopWork),
"CREATE_NOT" => Ok(EventName::CreateNot),
_ => Err(()),
}
}
Expand Down
Loading