notedeck

One damus client to rule them all
git clone git://jb55.com/notedeck
Log | Files | Refs | README | LICENSE

support.rs (3947B)


      1 use tracing::error;
      2 
      3 use notedeck::{DataPath, DataPathType, Directory};
      4 
      5 pub struct Support {
      6     directory: Directory,
      7     mailto_url: String,
      8     most_recent_log: Option<String>,
      9 }
     10 
     11 fn new_log_dir(paths: &DataPath) -> Directory {
     12     Directory::new(paths.path(DataPathType::Log))
     13 }
     14 
     15 impl Support {
     16     pub fn new(path: &DataPath) -> Self {
     17         let directory = new_log_dir(path);
     18 
     19         Self {
     20             mailto_url: MailtoBuilder::new(SUPPORT_EMAIL.to_string())
     21                 .with_subject("Help Needed".to_owned())
     22                 .with_content(EMAIL_TEMPLATE.to_owned())
     23                 .build(),
     24             directory,
     25             most_recent_log: None,
     26         }
     27     }
     28 }
     29 
     30 static MAX_LOG_LINES: usize = 500;
     31 static SUPPORT_EMAIL: &str = "support@damus.io";
     32 static EMAIL_TEMPLATE: &str = concat!("version ", env!("CARGO_PKG_VERSION"), "\nCommit hash: ", env!("GIT_COMMIT_HASH"), "\n\nDescribe the bug you have encountered:\n<-- your statement here -->\n\n===== Paste your log below =====\n\n");
     33 
     34 impl Support {
     35     pub fn refresh(&mut self) {
     36         self.most_recent_log = get_log_str(&self.directory);
     37     }
     38 
     39     pub fn get_mailto_url(&self) -> &str {
     40         &self.mailto_url
     41     }
     42 
     43     pub fn get_log_dir(&self) -> Option<&str> {
     44         self.directory.file_path.to_str()
     45     }
     46 
     47     pub fn get_most_recent_log(&self) -> Option<&String> {
     48         self.most_recent_log.as_ref()
     49     }
     50 }
     51 
     52 fn get_log_str(interactor: &Directory) -> Option<String> {
     53     match interactor.get_most_recent() {
     54         Ok(Some(most_recent_name)) => {
     55             match interactor.get_file_last_n_lines(most_recent_name.clone(), MAX_LOG_LINES) {
     56                 Ok(file_output) => {
     57                     return Some(
     58                         get_prefix(
     59                             &most_recent_name,
     60                             file_output.output_num_lines,
     61                             file_output.total_lines_in_file,
     62                         ) + &file_output.output,
     63                     )
     64                 }
     65                 Err(e) => {
     66                     error!(
     67                         "Error retrieving the last lines from file {}: {:?}",
     68                         most_recent_name, e
     69                     );
     70                 }
     71             }
     72         }
     73         Ok(None) => {
     74             error!("No files were found.");
     75         }
     76         Err(e) => {
     77             error!("Error fetching the most recent file: {:?}", e);
     78         }
     79     }
     80 
     81     None
     82 }
     83 
     84 fn get_prefix(file_name: &str, lines_displayed: usize, num_total_lines: usize) -> String {
     85     format!(
     86         "===\nDisplaying the last {lines_displayed} of {num_total_lines} lines in file {file_name}\n===\n\n",
     87     )
     88 }
     89 
     90 struct MailtoBuilder {
     91     content: Option<String>,
     92     address: String,
     93     subject: Option<String>,
     94 }
     95 
     96 impl MailtoBuilder {
     97     fn new(address: String) -> Self {
     98         Self {
     99             content: None,
    100             address,
    101             subject: None,
    102         }
    103     }
    104 
    105     // will be truncated so the whole URL is at most 2000 characters
    106     pub fn with_content(mut self, content: String) -> Self {
    107         self.content = Some(content);
    108         self
    109     }
    110 
    111     pub fn with_subject(mut self, subject: String) -> Self {
    112         self.subject = Some(subject);
    113         self
    114     }
    115 
    116     pub fn build(self) -> String {
    117         let mut url = String::new();
    118 
    119         url.push_str("mailto:");
    120         url.push_str(&self.address);
    121 
    122         let has_subject = self.subject.is_some();
    123 
    124         if has_subject || self.content.is_some() {
    125             url.push('?');
    126         }
    127 
    128         if let Some(subject) = self.subject {
    129             url.push_str("subject=");
    130             url.push_str(&urlencoding::encode(&subject));
    131         }
    132 
    133         if let Some(content) = self.content {
    134             if has_subject {
    135                 url.push('&');
    136             }
    137 
    138             url.push_str("body=");
    139 
    140             let body = urlencoding::encode(&content);
    141 
    142             url.push_str(&body);
    143         }
    144 
    145         url
    146     }
    147 }