neofetch for your did document
at main 5.2 kB view raw
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}