···2233use migration::prelude::Local;
44use migration::types::*;
55-use sea_orm::ActiveValue::Set;
65use sea_orm::entity::prelude::*;
66+use sea_orm::ActiveValue::Set;
77use std::future::ready;
88use std::pin::Pin;
99···5656 'life0: 'async_trait,
5757 {
5858 let now = Local::now().naive_local();
5959-6060- // set modified at
6159 self.modified_at = Set(now);
6262-6363- // we set the timestamp to non-utc
6460 if insert && self.created_at.is_not_set() {
6561 self.created_at = Set(now);
6662 }
6767-6863 if insert && self.nano_id.is_not_set() {
6964 self.nano_id = Set(NanoId::default());
7065 }
+1-6
crates/dto/src/entity/task.rs
···2233use migration::prelude::Local;
44use migration::types::*;
55-use sea_orm::ActiveValue::Set;
65use sea_orm::entity::prelude::*;
66+use sea_orm::ActiveValue::Set;
77use std::future::ready;
88use std::pin::Pin;
99···5454 'life0: 'async_trait,
5555 {
5656 let now = Local::now().naive_local();
5757-5858- // set modified at
5957 self.modified_at = Set(now);
6060-6161- // we set the timestamp to non-utc
6258 if insert && self.created_at.is_not_set() {
6359 self.created_at = Set(now);
6460 }
6565-6661 if insert && self.nano_id.is_not_set() {
6762 self.nano_id = Set(NanoId::default());
6863 }
+1-6
crates/dto/src/entity/zettel.rs
···11//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0
2233use migration::{prelude::Local, types::*};
44-use sea_orm::ActiveValue::Set;
54use sea_orm::entity::prelude::*;
55+use sea_orm::ActiveValue::Set;
66use std::{future::ready, pin::Pin};
7788#[sea_orm::model]
···3838 'life0: 'async_trait,
3939 {
4040 let now = Local::now().naive_local();
4141-4242- // set modified at
4341 self.modified_at = Set(now);
4444-4545- // we set the timestamp to non-utc
4642 if insert && self.created_at.is_not_set() {
4743 self.created_at = Set(now);
4844 }
4949-5045 if insert && self.nano_id.is_not_set() {
5146 self.nano_id = Set(NanoId::default());
5247 }
+16
crates/dto/src/lib.rs
···88/// exported traits for the database
99pub use sea_orm::ActiveModelTrait;
1010pub use sea_orm::ActiveValue;
1111+pub use sea_orm::ColumnTrait;
1212+pub use sea_orm::EntityTrait;
1313+pub use sea_orm::IntoActiveModel;
1414+pub use sea_orm::QueryFilter;
11151216/// Exporting this as a generic NanoId.
1317pub use migration::types::NanoId;
···2731/// Everything related to groups.
2832pub use entity::group::ActiveModel as GroupActiveModel;
2933pub use entity::group::ActiveModelEx as GroupActiveModelEx;
3434+pub use entity::group::Column as GroupColumns;
3035pub use entity::group::Entity as GroupEntity;
3136pub use entity::group::Model as GroupModel;
3237pub use entity::group::ModelEx as GroupModelEx;
···3439/// Everything related to tasks.
3540pub use entity::task::ActiveModel as TaskActiveModel;
3641pub use entity::task::ActiveModelEx as TaskActiveModelEx;
4242+pub use entity::task::Column as TaskColumns;
3743pub use entity::task::Entity as TaskEntity;
3844pub use entity::task::Model as TaskModel;
3945pub use entity::task::ModelEx as TaskModelEx;
···4147/// Everything related to zetetl's.
4248pub use entity::zettel::ActiveModel as ZettelActiveModel;
4349pub use entity::zettel::ActiveModelEx as ZettelActiveModelEx;
5050+pub use entity::zettel::Column as ZettelColumns;
4451pub use entity::zettel::Entity as ZettelEntity;
4552pub use entity::zettel::Model as ZettelModel;
4653pub use entity::zettel::ModelEx as ZettelModelEx;
···4855/// Everything related to tag's.
4956pub use entity::tag::ActiveModel as TagActiveModel;
5057pub use entity::tag::ActiveModelEx as TagActiveModelEx;
5858+pub use entity::tag::Column as TagColumns;
5159pub use entity::tag::Entity as TagEntity;
5260pub use entity::tag::Model as TagModel;
5361pub use entity::tag::ModelEx as TagModelEx;
6262+6363+/// Everything related to the zettel_tag entries.
6464+pub use entity::zettel_tag::ActiveModel as ZettelTagActiveModel;
6565+pub use entity::zettel_tag::ActiveModelEx as ZettelTagActiveModelEx;
6666+pub use entity::zettel_tag::Column as ZettelTagColumns;
6767+pub use entity::zettel_tag::Entity as ZettelTagEntity;
6868+pub use entity::zettel_tag::Model as ZettelTagModel;
6969+pub use entity::zettel_tag::ModelEx as ZettelTagModelEx;
+1
src/types/kasten.rs
···6677/// The `Kasten` that stores the `Link`s between `Zettel`s
88#[derive(Debug, Clone)]
99+#[expect(dead_code)]
910pub struct Kasten {
1011 /// Private field so it can only be instantiated from a `Path`
1112 _private: (),
+168-1
src/types/zettel.rs
···11-use dto::{DateTime, TagEntity, ZettelActiveModel, ZettelEntity, ZettelModelEx};
11+use dto::{
22+ ActiveModelTrait, ActiveValue, ColumnTrait, DateTime, EntityTrait as _, IntoActiveModel,
33+ QueryFilter, TagActiveModel, TagEntity, ZettelActiveModel, ZettelEntity, ZettelModelEx,
44+ ZettelTagActiveModel, ZettelTagColumns, ZettelTagEntity,
55+};
26use serde::{Deserialize, Serialize};
37use std::{
48 fmt::Display,
59 path::{Path, PathBuf},
610};
1111+use tracing::info;
712813use color_eyre::eyre::{Error, Result, eyre};
914use dto::NanoId;
···100105 fn absolute_path(&self, ws: &Workspace) -> PathBuf {
101106 ws.root.clone().join(&self.file_path)
102107 }
108108+109109+ /// uses the id and root to parse out of the root directory
110110+ pub async fn from_id(id: &ZettelId, ws: &Workspace) -> Result<Self> {
111111+ let mut path = ws.root.clone();
112112+ path.push(id.0.to_string());
113113+ Self::from_path(path, ws).await
114114+ }
115115+116116+ pub async fn from_path(path: impl Into<PathBuf>, ws: &Workspace) -> Result<Self> {
117117+ let path: PathBuf = path.into();
118118+119119+ let id = ZettelId::try_from(path.as_path())?;
120120+121121+ let (front_matter, _) = FrontMatter::extract_from_file(&ws.root.clone().join(path)).await?;
122122+123123+ let mut zettel_tag_strings = front_matter.tag_strings.clone();
124124+125125+ zettel_tag_strings.sort();
126126+127127+ // get the zettel from the db
128128+ let db_zettel: ZettelModelEx = if let Some(z) = ZettelEntity::load()
129129+ .with(TagEntity)
130130+ .filter_by_nano_id(id.clone())
131131+ .one(&ws.db)
132132+ .await?
133133+ {
134134+ z
135135+ } else {
136136+ // if zettel is missing from db, we just add it here
137137+ info!("adding zettel to db");
138138+ let am = ZettelActiveModel {
139139+ nano_id: ActiveValue::Set(id.clone().into()),
140140+ title: ActiveValue::Set(front_matter.title.clone()),
141141+ ..Default::default()
142142+ };
143143+144144+ am.insert(&ws.db).await?;
145145+146146+ ZettelEntity::load()
147147+ .with(TagEntity)
148148+ .filter_by_nano_id(id.clone())
149149+ .one(&ws.db)
150150+ .await?
151151+ .expect("we just inserted the zettel")
152152+ };
153153+154154+ // get the tags for it
155155+ for db_tag in db_zettel.tags {
156156+ if let Ok(idx) = zettel_tag_strings.binary_search(&db_tag.name) {
157157+ // we remove tags we have already processed
158158+ zettel_tag_strings.remove(idx);
159159+ } else {
160160+ // the db says the file has tag `x`, but that tag is missing from the
161161+ // front matter, we can assume its gone, lets delete that link
162162+ let x = ZettelTagEntity::find()
163163+ .filter(ZettelTagColumns::ZettelNanoId.eq(id.0.clone()))
164164+ .filter(ZettelTagColumns::TagNanoId.eq(db_tag.nano_id))
165165+ .one(&ws.db)
166166+ .await?
167167+ .expect("this link must exist");
168168+169169+ x.into_active_model().delete(&ws.db).await?;
170170+ }
171171+ }
172172+173173+ // now any tags that are left inside zettel_tag_strings,
174174+ // we have to put them inside the db
175175+ for new_tag in zettel_tag_strings {
176176+ // create a new tag
177177+ let tag = TagActiveModel {
178178+ name: ActiveValue::Set(new_tag),
179179+ ..Default::default()
180180+ }
181181+ .insert(&ws.db)
182182+ .await?;
183183+184184+ // this zettel has this tag now
185185+ let _ = ZettelTagActiveModel {
186186+ zettel_nano_id: ActiveValue::Set(id.to_string()),
187187+ tag_nano_id: ActiveValue::Set(tag.nano_id.to_string()),
188188+ }
189189+ .insert(&ws.db)
190190+ .await?;
191191+ }
192192+193193+ if front_matter.title != db_zettel.title {
194194+ let am = ZettelActiveModel {
195195+ id: ActiveValue::Unchanged(db_zettel.id),
196196+ title: ActiveValue::Set(front_matter.title.clone()),
197197+ ..Default::default()
198198+ };
199199+200200+ am.update(&ws.db).await?;
201201+ }
202202+203203+ Ok(ZettelEntity::load()
204204+ .with(TagEntity)
205205+ .filter_by_nano_id(id.clone())
206206+ .one(&ws.db)
207207+ .await?
208208+ .expect("We just inserted it right above")
209209+ .into())
210210+ }
211211+212212+ // pub fn apply_node_transform(&self, node: &mut Node<Zettel, Link>) {
213213+ // node.set_label(self.front_matter.title.to_owned());
214214+ // let disp = node.display_mut();
215215+ // disp.radius = 100.0;
216216+ // }
217217+218218+ // fn links_from_content(src_id: &ZettelId, content: &str, ws: &Workspace) -> ZkResult<Vec<Link>> {
219219+ // let parsed = Parser::new(content);
220220+221221+ // let mut links = vec![];
222222+223223+ // for event in parsed {
224224+ // if let Event::Start(MkTag::Link { dest_url, .. }) = event {
225225+ // info!("Found dest_url: {dest_url:#?}");
226226+227227+ // let dest_path = {
228228+ // // remove leading "./"
229229+ // let without_prefix = dest_url.strip_prefix("./").unwrap_or(&dest_url);
230230+231231+ // // remove "#" and everything after it
232232+ // let without_anchor = without_prefix.split('#').next().unwrap();
233233+234234+ // // add .md if not present
235235+ // let normalized = if without_anchor.ends_with(".md") {
236236+ // without_anchor.to_string()
237237+ // } else {
238238+ // format!("{}.md", without_anchor)
239239+ // };
240240+241241+ // let mut tmp_root = ws.root.clone();
242242+ // tmp_root.push(normalized);
243243+ // tmp_root
244244+ // };
245245+ // // simplest way to validate that the path exists
246246+ // let canon_url = match dest_path.canonicalize() {
247247+ // Ok(canon_url) => canon_url,
248248+ // Err(_) => {
249249+ // error!("Link not found!: {dest_path:?}");
250250+ // continue;
251251+ // }
252252+ // };
253253+254254+ // let dst_id = ZettelId::try_from(canon_url)?;
255255+256256+ // let link = Link::new(src_id, dst_id);
257257+258258+ // links.push(link)
259259+ // }
260260+ // }
261261+262262+ // Ok(links)
263263+ // }
103264}
104265105266impl From<ZettelModelEx> for Zettel {
···179340 f.write_str(&self.0.to_string())
180341 }
181342}
343343+344344+impl From<ZettelId> for NanoId {
345345+ fn from(value: ZettelId) -> Self {
346346+ value.0
347347+ }
348348+}