1use alloc::string::ToString;
2use alloc::vec::Vec;
3
4use miden_objects::account::{Account, AccountId};
5use miden_objects::note::{NoteId, NoteTag};
6use miden_tx::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable};
7use tracing::warn;
8
9use crate::Client;
10use crate::errors::ClientError;
11use crate::store::{InputNoteRecord, NoteRecordError};
12
13impl<AUTH> Client<AUTH> {
15 pub async fn get_note_tags(&self) -> Result<Vec<NoteTagRecord>, ClientError> {
28 self.store.get_note_tags().await.map_err(Into::into)
29 }
30
31 pub async fn add_note_tag(&mut self, tag: NoteTag) -> Result<(), ClientError> {
33 match self
34 .store
35 .add_note_tag(NoteTagRecord { tag, source: NoteTagSource::User })
36 .await
37 .map_err(Into::into)
38 {
39 Ok(true) => Ok(()),
40 Ok(false) => {
41 warn!("Tag {} is already being tracked", tag);
42 Ok(())
43 },
44 Err(err) => Err(err),
45 }
46 }
47
48 pub async fn remove_note_tag(&mut self, tag: NoteTag) -> Result<(), ClientError> {
50 if self
51 .store
52 .remove_note_tag(NoteTagRecord { tag, source: NoteTagSource::User })
53 .await?
54 == 0
55 {
56 warn!("Tag {} wasn't being tracked", tag);
57 }
58
59 Ok(())
60 }
61}
62
63#[derive(Debug, PartialEq, Eq, Clone, Copy)]
65pub struct NoteTagRecord {
66 pub tag: NoteTag,
67 pub source: NoteTagSource,
68}
69
70#[derive(Debug, PartialEq, Eq, Clone, Copy)]
73pub enum NoteTagSource {
74 Account(AccountId),
76 Note(NoteId),
78 User,
80}
81
82impl NoteTagRecord {
83 pub fn with_note_source(tag: NoteTag, note_id: NoteId) -> Self {
84 Self {
85 tag,
86 source: NoteTagSource::Note(note_id),
87 }
88 }
89
90 pub fn with_account_source(tag: NoteTag, account_id: AccountId) -> Self {
91 Self {
92 tag,
93 source: NoteTagSource::Account(account_id),
94 }
95 }
96}
97
98impl Serializable for NoteTagSource {
99 fn write_into<W: ByteWriter>(&self, target: &mut W) {
100 match self {
101 NoteTagSource::Account(account_id) => {
102 target.write_u8(0);
103 account_id.write_into(target);
104 },
105 NoteTagSource::Note(note_id) => {
106 target.write_u8(1);
107 note_id.write_into(target);
108 },
109 NoteTagSource::User => target.write_u8(2),
110 }
111 }
112}
113
114impl Deserializable for NoteTagSource {
115 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
116 match source.read_u8()? {
117 0 => Ok(NoteTagSource::Account(AccountId::read_from(source)?)),
118 1 => Ok(NoteTagSource::Note(NoteId::read_from(source)?)),
119 2 => Ok(NoteTagSource::User),
120 val => Err(DeserializationError::InvalidValue(format!("Invalid tag source: {val}"))),
121 }
122 }
123}
124
125impl PartialEq<NoteTag> for NoteTagRecord {
126 fn eq(&self, other: &NoteTag) -> bool {
127 self.tag == *other
128 }
129}
130
131impl TryInto<NoteTagRecord> for &InputNoteRecord {
132 type Error = NoteRecordError;
133
134 fn try_into(self) -> Result<NoteTagRecord, Self::Error> {
135 match self.metadata() {
136 Some(metadata) => Ok(NoteTagRecord::with_note_source(metadata.tag(), self.id())),
137 None => Err(NoteRecordError::ConversionError(
138 "Input Note Record does not contain tag".to_string(),
139 )),
140 }
141 }
142}
143
144impl From<&Account> for NoteTagRecord {
145 fn from(account: &Account) -> Self {
146 NoteTagRecord::with_account_source(NoteTag::from_account_id(account.id()), account.id())
147 }
148}