1use std::collections::HashMap;
6use std::path::PathBuf;
7use std::sync::Arc;
8
9use chrono::NaiveDate;
10
11use crate::config::types::ResolvedConfig;
12use crate::frontmatter::{Frontmatter, ParsedDocument, parse, serialize_with_order};
13use crate::templates::repository::LoadedTemplate;
14use crate::types::{TypeDefinition, TypeRegistry};
15
16#[derive(Debug, Clone, Default)]
19pub struct CoreMetadata {
20 pub note_type: Option<String>,
21 pub title: Option<String>,
22 pub project_id: Option<String>,
23 pub task_id: Option<String>,
24 pub meeting_id: Option<String>,
25 pub task_counter: Option<u32>,
26 pub project: Option<String>, pub date: Option<String>, pub week: Option<String>, }
30
31impl CoreMetadata {
32 pub fn to_yaml_map(&self) -> HashMap<String, serde_yaml::Value> {
34 let mut map = HashMap::new();
35 if let Some(ref t) = self.note_type {
36 map.insert("type".into(), serde_yaml::Value::String(t.clone()));
37 }
38 if let Some(ref t) = self.title {
39 map.insert("title".into(), serde_yaml::Value::String(t.clone()));
40 }
41 if let Some(ref id) = self.project_id {
42 map.insert("project-id".into(), serde_yaml::Value::String(id.clone()));
43 }
44 if let Some(ref id) = self.task_id {
45 map.insert("task-id".into(), serde_yaml::Value::String(id.clone()));
46 }
47 if let Some(ref id) = self.meeting_id {
48 map.insert("meeting-id".into(), serde_yaml::Value::String(id.clone()));
49 }
50 if let Some(counter) = self.task_counter {
51 map.insert("task_counter".into(), serde_yaml::Value::Number(counter.into()));
52 }
53 if let Some(ref p) = self.project {
54 map.insert("project".into(), serde_yaml::Value::String(p.clone()));
55 }
56 if let Some(ref d) = self.date {
57 map.insert("date".into(), serde_yaml::Value::String(d.clone()));
58 }
59 if let Some(ref w) = self.week {
60 map.insert("week".into(), serde_yaml::Value::String(w.clone()));
61 }
62 map
63 }
64
65 pub fn apply_to_content(
70 &self,
71 content: &str,
72 field_order: Option<&[String]>,
73 ) -> Result<String, String> {
74 let parsed = parse(content).map_err(|e| e.to_string())?;
75 let mut fields = parsed.frontmatter.map(|fm| fm.fields).unwrap_or_default();
76 fields.extend(self.to_yaml_map());
77 let doc = ParsedDocument {
78 frontmatter: Some(Frontmatter { fields }),
79 body: parsed.body,
80 };
81 Ok(serialize_with_order(&doc, field_order))
82 }
83}
84
85pub struct CreationContext<'a> {
87 pub title: String,
89 pub type_name: String,
90
91 pub config: &'a ResolvedConfig,
93 pub typedef: Option<Arc<TypeDefinition>>,
94 pub registry: &'a TypeRegistry,
95
96 pub vars: HashMap<String, String>,
98 pub core_metadata: CoreMetadata,
99
100 pub output_path: Option<PathBuf>,
102
103 pub template: Option<LoadedTemplate>,
105
106 pub batch_mode: bool,
108
109 pub reference_date: Option<NaiveDate>,
111}
112
113impl<'a> CreationContext<'a> {
114 pub fn new(
116 type_name: &str,
117 title: &str,
118 config: &'a ResolvedConfig,
119 registry: &'a TypeRegistry,
120 ) -> Self {
121 let typedef = registry.get(type_name);
122
123 let core_metadata = CoreMetadata {
124 note_type: Some(type_name.to_string()),
125 title: Some(title.to_string()),
126 ..Default::default()
127 };
128
129 let vars = HashMap::from([
130 ("title".to_string(), title.to_string()),
131 ("type".to_string(), type_name.to_string()),
132 ]);
133
134 Self {
135 title: title.to_string(),
136 type_name: type_name.to_string(),
137 config,
138 typedef,
139 registry,
140 vars,
141 core_metadata,
142 output_path: None,
143 template: None,
144 batch_mode: false,
145 reference_date: None,
146 }
147 }
148
149 pub fn with_vars(mut self, cli_vars: HashMap<String, String>) -> Self {
151 self.vars.extend(cli_vars);
152 self
153 }
154
155 pub fn with_template(mut self, template: LoadedTemplate) -> Self {
157 self.template = Some(template);
158 self
159 }
160
161 pub fn with_batch_mode(mut self, batch: bool) -> Self {
163 self.batch_mode = batch;
164 self
165 }
166
167 pub fn get_var(&self, key: &str) -> Option<&str> {
169 self.vars.get(key).map(|s| s.as_str())
170 }
171
172 pub fn set_var(&mut self, key: impl Into<String>, value: impl Into<String>) {
174 self.vars.insert(key.into(), value.into());
175 }
176
177 pub fn to_prompt_context(&self) -> PromptContext<'_> {
179 PromptContext {
180 config: self.config,
181 type_name: &self.type_name,
182 title: &self.title,
183 provided_vars: &self.vars,
184 batch_mode: self.batch_mode,
185 }
186 }
187}
188
189pub struct PromptContext<'a> {
191 pub config: &'a ResolvedConfig,
192 pub type_name: &'a str,
193 pub title: &'a str,
194 pub provided_vars: &'a HashMap<String, String>,
195 pub batch_mode: bool,
196}
197
198#[derive(Debug, Clone)]
200pub struct FieldPrompt {
201 pub field_name: String,
202 pub prompt_text: String,
203 pub prompt_type: PromptType,
204 pub required: bool,
205 pub default_value: Option<String>,
206}
207
208#[derive(Debug, Clone)]
210pub enum PromptType {
211 Text,
213 Multiline,
215 Select(Vec<String>),
217 ProjectSelector,
219}