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