notedeck

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

support.rs (3961B)


      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 {} of {} lines in file {}\n===\n\n",
     87         lines_displayed, num_total_lines, file_name,
     88     )
     89 }
     90 
     91 struct MailtoBuilder {
     92     content: Option<String>,
     93     address: String,
     94     subject: Option<String>,
     95 }
     96 
     97 impl MailtoBuilder {
     98     fn new(address: String) -> Self {
     99         Self {
    100             content: None,
    101             address,
    102             subject: None,
    103         }
    104     }
    105 
    106     // will be truncated so the whole URL is at most 2000 characters
    107     pub fn with_content(mut self, content: String) -> Self {
    108         self.content = Some(content);
    109         self
    110     }
    111 
    112     pub fn with_subject(mut self, subject: String) -> Self {
    113         self.subject = Some(subject);
    114         self
    115     }
    116 
    117     pub fn build(self) -> String {
    118         let mut url = String::new();
    119 
    120         url.push_str("mailto:");
    121         url.push_str(&self.address);
    122 
    123         let has_subject = self.subject.is_some();
    124 
    125         if has_subject || self.content.is_some() {
    126             url.push('?');
    127         }
    128 
    129         if let Some(subject) = self.subject {
    130             url.push_str("subject=");
    131             url.push_str(&urlencoding::encode(&subject));
    132         }
    133 
    134         if let Some(content) = self.content {
    135             if has_subject {
    136                 url.push('&');
    137             }
    138 
    139             url.push_str("body=");
    140 
    141             let body = urlencoding::encode(&content);
    142 
    143             url.push_str(&body);
    144         }
    145 
    146         url
    147     }
    148 }