neofetch for your did document
1use clap::Parser;
2use color_eyre::Result;
3use jacquard::identity::JacquardResolver;
4use jacquard::types::ident::AtIdentifier;
5use jacquard::identity::resolver::{DidStep, HandleStep, MiniDoc, PlcSource, ResolverOptions};
6use ratatui::layout::{Constraint, Direction, Layout};
7use ratatui::style::Modifier;
8use ratatui::text::Span;
9use ratatui::widgets::{Block, Cell, Padding, Paragraph, Row, Table};
10use ratatui::{Frame, Terminal, TerminalOptions, Viewport};
11use ratatui::prelude::{Color, CrosstermBackend, Style};
12use std::io::{stdout, Stdout};
13use miette::IntoDiagnostic;
14
15pub struct App {
16 pub did: String,
17 pub handle: String,
18 pub pds: String,
19 pub signing_key: String
20
21}
22
23impl App {
24 pub fn new(did_document: &MiniDoc<'_>) -> Self {
25 App {
26 did: did_document.did.to_string(),
27 handle: did_document.handle.to_string(),
28 pds: did_document.pds.to_string(),
29 signing_key: did_document.signing_key.to_string()
30 }
31 }
32}
33
34
35#[derive(Parser, Debug)]
36#[command(version, about, long_about = "neofetch but for atprotocol")]
37struct Args {
38 /// Your AT Protocol handle or DID
39 #[arg(short, long)]
40 identifier: String,
41}
42
43#[tokio::main]
44async fn main() -> miette::Result<()> {
45 let args = Args::parse();
46
47
48 let identifier = AtIdentifier::new(&args.identifier)?;
49
50 let request_client = reqwest::Client::new();
51
52 let mut handle_order = vec![];
53 handle_order.push(HandleStep::DnsTxt);
54 handle_order.push(HandleStep::HttpsWellKnown);
55 handle_order.push(HandleStep::PdsResolveHandle);
56
57 let resolver_options = ResolverOptions::new()
58 .plc_source(PlcSource::slingshot_default())
59 .handle_order(handle_order)
60 .did_order(vec![
61 DidStep::DidWebHttps,
62 DidStep::PlcHttp,
63 DidStep::PdsResolveDid,])
64 .validate_doc_id(true)
65 .public_fallback_for_handle(true)
66 .build();
67
68 let resolver = JacquardResolver::new(request_client, resolver_options);
69
70
71 let mini_doc_response = resolver.fetch_mini_doc_via_slingshot_identifier(&identifier).await?;
72 let did_document= mini_doc_response.parse()?;
73
74 let app = App::new(&did_document);
75
76
77 let backend = CrosstermBackend::new(stdout());
78
79 let content_height: u16 = 40;
80 let total_height = content_height + 2;
81
82 let options = TerminalOptions {
83 viewport: Viewport::Inline(total_height),
84 };
85
86 let mut terminal = Terminal::with_options(backend, options).into_diagnostic()?;
87 let _ = run(&mut terminal, &app);
88
89 Ok(())
90
91}
92
93
94
95fn run(terminal: &mut Terminal<CrosstermBackend<Stdout>>, app: &App) -> Result<()> {
96 terminal.draw(|frame| {
97 render(frame, app);
98 })?;
99
100 Ok(())
101}
102
103fn render(frame: &mut Frame, app: &App) {
104
105 let system_data = vec![
106 ("DID", app.did.clone()),
107 ("Handle", app.handle.clone()),
108 ("PDS", app.pds.clone()),
109 ("Signing Key", app.signing_key.clone()),
110 ];
111
112 let constraints = [
113 Constraint::Percentage(35),
114 Constraint::Percentage(65),
115 ];
116
117 let rows: Vec<Row> = system_data
118 .iter()
119 .map(|(label, value)| {
120 Row::new(vec![
121 Cell::from(Span::styled(
122 format!("{}:", label),
123 Style::default().fg(Color::Reset).add_modifier(Modifier::BOLD),
124 )),
125 Cell::from(Span::styled(
126 value,
127 Style::default().fg(Color::White),
128 )),
129 ])
130 })
131 .collect();
132
133 let table_constraints = [
134 Constraint::Length(15),
135 Constraint::Min(0),
136 ];
137
138
139 let info_block = Block::default().padding(Padding::top(1));
140 let info_table = Table::new(rows, table_constraints).block(info_block);
141 let chunks = Layout::default().direction(Direction::Horizontal)
142 .constraints(constraints).split(frame.area());
143
144 let ascii_area = chunks[0];
145 let info_area = chunks[1];
146
147
148 let ascii_art = r#"
149 ==;;!!*;;;:;;;;;
150 $$$!;;;;;;=;;::;;;;;;;;;
151 $$#$*!;==;;;;;;;;=!;;;;;;;;;
152 $#$$$$#;; ;;;;;;;;;
153 =*###=; ;;;;;;;;
154 ;;;;;; ;;;;;;=*$$$=;;; ;;;;;;
155 !==;;; ;;;;;;;;;*$$$$*;= ;;;;;;
156 =!;;;= ;;;;;;;;;;=$$$$$$$$ =;;;;;
157=;;;;; ;;;;;; $$$$$$ !=;;;;
158$#!=;; ;;==!$ $$$$$ $###!=
159;===== ===!$$ $$$$$ ##!!*!
160==$$== ===$$$ $$$$$# **!=!!
161$$$==== *###$$$$$$$##$###****!*!!=;;;
162 ==$=== $$$$#####****!!!===;;;;==!
163 =!==#$ $###******! ====;;=====
164 =!$$$$#
165 !*$$$####
166 $$$#*#*!=!=;::~~~~--
167 *!**=;;;::::~~-----
168 :::::~:~~~-----
169 "#;
170
171 let ascii_block = Block::default().padding(Padding::vertical(0));
172
173 let ascii_widget = Paragraph::new(ascii_art)
174 .block(ascii_block);
175
176
177
178 frame.render_widget(ascii_widget, ascii_area);
179 frame.render_widget(info_table, info_area);
180}