The backend of a gist server written in Rust
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

snippet.rs 2.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. use chrono::{Local, NaiveDateTime};
  2. use crypto::digest::Digest;
  3. use crypto::sha2::Sha256;
  4. use diesel::pg::PgConnection as PGC;
  5. use diesel::prelude::*;
  6. use syntect::highlighting::ThemeSet;
  7. use syntect::html::highlighted_html_for_string;
  8. use syntect::parsing::SyntaxSet;
  9. use crate::schema::snippets;
  10. use crate::schema::snippets::columns::uuid;
  11. #[derive(Queryable, Serialize, Deserialize)]
  12. pub struct Snippet {
  13. pub id: i32,
  14. pub title: String,
  15. pub body: String,
  16. pub created_at: Option<NaiveDateTime>,
  17. pub filetype: Option<String>,
  18. pub formatted_body: String,
  19. pub uuid: String,
  20. }
  21. #[derive(Serialize, Deserialize)]
  22. pub struct ApiSnippet {
  23. pub title: String,
  24. pub body: String,
  25. pub filetype: Option<String>,
  26. }
  27. #[derive(Insertable, AsChangeset)]
  28. #[table_name = "snippets"]
  29. pub struct InsertableSnippet {
  30. pub title: String,
  31. pub body: String,
  32. pub filetype: Option<String>,
  33. pub formatted_body: String,
  34. pub uuid: String,
  35. }
  36. fn format_snippet(filetype: Option<String>, body: String) -> String {
  37. filetype.map_or(
  38. format!("<pre class='plaintext'>\n{}\n</pre>", body),
  39. |filetype| {
  40. let ps = SyntaxSet::load_defaults_newlines();
  41. let ts = ThemeSet::load_defaults();
  42. ps.find_syntax_by_extension(&filetype).map_or(
  43. format!("<pre class='plaintext'>\n{}\n</pre>", body),
  44. |syntax| {
  45. highlighted_html_for_string(
  46. &htmlescape::decode_html(&body).expect("Invalid HTML"),
  47. &ps,
  48. syntax,
  49. &ts.themes["Solarized (light)"],
  50. )
  51. },
  52. )
  53. },
  54. )
  55. }
  56. fn generate_uuid(snippet: &ApiSnippet) -> String {
  57. let current_timestamp = Local::now().to_string();
  58. let mut hasher = Sha256::new();
  59. hasher.input_str(&format!(
  60. "{}{}{}",
  61. snippet.title, snippet.body, current_timestamp
  62. ));
  63. hasher.result_str().to_string()
  64. }
  65. pub fn get(connection: &PGC, snippet_uuid: &str) -> QueryResult<Snippet> {
  66. snippets::table
  67. .filter(uuid.eq(snippet_uuid))
  68. .first(connection)
  69. }
  70. pub fn insert(snippet: ApiSnippet, connection: &PGC) -> QueryResult<Snippet> {
  71. let body = htmlescape::encode_minimal(&snippet.body.clone());
  72. let formatted_snippet = InsertableSnippet {
  73. filetype: snippet.filetype.clone(),
  74. title: snippet.title.clone(),
  75. body: body.clone(),
  76. uuid: generate_uuid(&snippet),
  77. formatted_body: format_snippet(snippet.filetype, body),
  78. };
  79. diesel::insert_into(snippets::table)
  80. .values(formatted_snippet)
  81. .get_result(connection)
  82. }