From db28093f4edf6196ce536184dea1cfe24f70a698 Mon Sep 17 00:00:00 2001 From: yaqubroli Date: Sun, 18 Dec 2022 21:57:59 -0800 Subject: [PATCH] Refactored database, added templating, added shortener --- Cargo.lock | 8 ++++---- Cargo.toml | 6 ++++-- config_defaults.toml | 1 + index.html | 2 ++ results.html | 12 ++++++++++++ src/database.rs | 212 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------- src/endpoints.rs | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++++----------- src/main.rs | 67 ++++++++++++++++++++++++++++++++++++++++++++++++------------------- src/settings.rs | 11 +++++++++-- src/shortener.rs | 39 +++++++++++++++++++++++++++++++++++++++ src/templating.rs | 22 ++++++++++++++++++++++ src/url.rs | 8 ++++++++ static/style.css | 5 +++++ target/.rustc_info.json | 2 +- target/debug/url_shortener | 0 target/debug/url_shortener.d | 2 +- 16 files changed, 283 insertions(+), 180 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6f17a54..b0a1473 100644 --- a/Cargo.lock +++ a/Cargo.lock @@ -2071,18 +2071,18 @@ [[package]] name = "thiserror" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" +checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" +checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index c18c07f..ec982a0 100644 --- a/Cargo.toml +++ a/Cargo.toml @@ -11,8 +11,10 @@ [dependencies] # diesel = { version = "2.0.2", features = ["mysql"] } actix-web = "4" -serde = { version = "1.0", features = ["derive"] } +actix-files = "0.6.2" actix-settings = "0.6.0" +serde = { version = "1.0", features = ["derive"] } mysql = "23.0.0" -actix-files = "0.6.2" +# +# base64 = "0.20.0" # log = "0.4"diff --git a/config_defaults.toml b/config_defaults.toml index 9599a8e..5185ce6 100644 --- a/config_defaults.toml +++ a/config_defaults.toml @@ -1,7 +1,8 @@ [actix] # For more info, see: https://docs.rs/actix-web/4/actix_web/struct.HttpServer.html. hosts = [ + # @ts-ignore ["0.0.0.0", 9000] # This should work for both development and deployment... # # ... but other entries are possible, as well. ] diff --git a/index.html b/index.html index d4db8c6..2563b5a 100644 --- a/index.html +++ a/index.html @@ -1,7 +1,9 @@ URL submission form + +

URL shortener

diff --git a/results.html b/results.html new file mode 100644 index 0000000..61ed301 100644 --- /dev/null +++ a/results.html @@ -1,0 +1,12 @@ + + + + URL shortened + + + + +

URL shortened successfully

+

Your url is accessible at {{domain}}/{{shortened}}

+ +diff --git a/src/database.rs b/src/database.rs index d95b144..ebeefb7 100644 --- a/src/database.rs +++ a/src/database.rs @@ -1,142 +1,98 @@ +use actix_settings::BasicSettings; +use crate::settings; + use mysql::prelude::*; use mysql::*; -//use crate::config::{Config}; - -/* pub fn init(config: Config) -> Pool { - let url = format!( - "mysql://{}:{}@{}:{}/{}", - config.db.user, config.db.password, config.db.host, config.db.port, config.db.database - ); - println!("Connecting to database at {}.", url); - let pool = Pool::new(url.as_str()).expect("Unable to connect to database."); - if !does_table_exist(&pool) { - println!("Table does not exist. Creating them."); - create_table(&pool); +#[derive(Debug)] +pub struct RetrievedUrl { + pub url: String, + pub success: bool +} + +#[derive(Debug)] +pub struct SubmittedUrl { + pub shortened: String, + pub success: bool +} + +// Description: This function takes in a settings struct and returns a mysql connection pool +pub async fn init (settings: &BasicSettings) -> Pool { + let database_settings = &settings.application.database; + let url = format!("mysql://{}:{}@{}:{}/{}", database_settings.username, database_settings.password, database_settings.host, database_settings.port, database_settings.database); + let pool = Pool::new(url.as_str()).unwrap(); + // create the table if it doesn't exist + let mut connection = pool.get_conn().unwrap(); + if create_table(&mut connection).await { + println!("Created table `urls`"); + } else { + println!("Table `urls` already exists"); } pool -} */ - -pub fn does_table_exist(pool: &Pool) -> bool { - let mut conn = pool.get_conn().unwrap(); - let result: Vec = conn - .exec_map( - r" - SELECT table_name FROM information_schema.tables WHERE table_schema = :database - ", - params! { - "database" => "url_shortener" - }, - |table_name| table_name, - ) - .unwrap(); - result.len() > 0 } -pub fn create_table(pool: &Pool) { - let mut conn = pool.get_conn().unwrap(); - conn.query_drop( - r" - CREATE TABLE IF NOT EXISTS `urls` ( - `id` INT NOT NULL AUTO_INCREMENT, - `url` VARCHAR(255) NOT NULL, - `shortened` VARCHAR(255) NOT NULL, - PRIMARY KEY (`id`) - ) ENGINE=InnoDB; - ", - ) - .unwrap(); - println!("Table created."); -} - -pub fn insert_url(pool: &Pool, url: &str, shortened: &str) { - let mut conn = pool.get_conn().unwrap(); - conn.exec_drop( - r" - INSERT INTO `urls` (`url`, `shortened`) VALUES (:url, :shortened) - ", - params! { - "url" => url, - "shortened" => shortened - }, - ) - .unwrap(); +// Description: This function takes in a connection and a shortened url and returns a RetrievedUrl struct, where `url` is the url column and `success` is true if the shortened url exists in the database +pub async fn retrieve_url (connection: &mut PooledConn, shortened: &str) -> RetrievedUrl { + let mut result = connection.exec_iter("SELECT url FROM urls WHERE shortened = :shortened", params! { + "shortened" => shortened + }).unwrap(); + let row = result.next().unwrap(); + let url = row.unwrap().get::("url").unwrap(); + RetrievedUrl { + url, + success: true + } } -pub fn get_url(pool: &Pool, shortened: &str) -> Option { - let mut conn = pool.get_conn().unwrap(); - let result: Vec = conn - .exec_map( - r" - SELECT `url` FROM `urls` WHERE `shortened` = :shortened - ", - params! { +// Description: This function takes in a connection and a url and a shortened url and returns a SubmittedUrl struct, where `shortened` is the shortened url column and `success` is true if the shortened url does not exist in the database +pub async fn submit_url (connection: &mut PooledConn, url: &str, shortened: &str) -> SubmittedUrl { + let row = connection.exec_iter("SELECT shortened FROM urls WHERE shortened = :shortened", params! { + "shortened" => shortened + }).unwrap().next(); + if row.is_some() { + SubmittedUrl { + shortened: shortened.to_string(), + success: false + } + } else { + let row = connection.exec_iter("SELECT shortened FROM urls WHERE url = :url", params! { + "url" => url + }).unwrap().next(); + if row.is_some() { + let shortened = row.unwrap().unwrap().get::("shortened").unwrap(); + SubmittedUrl { + shortened, + success: true + } + } else { + connection.exec_drop("INSERT INTO urls (url, shortened) VALUES (:url, :shortened)", params! { + "url" => url, "shortened" => shortened - }, - |url| url, - ) - .unwrap(); - result.into_iter().next() -} - -pub fn get_shortened(pool: &Pool, url: &str) -> Option { - let mut conn = pool.get_conn().unwrap(); - let result: Vec = conn - .exec_map( - r" - SELECT `shortened` FROM `urls` WHERE `url` = :url - ", - params! { - "url" => url - }, - |shortened| shortened, - ) - .unwrap(); - result.into_iter().next() -} - -pub fn url_exists(pool: &Pool, url: &str) -> bool { - let mut conn = pool.get_conn().unwrap(); - let result: Vec = conn - .exec_map( - r" - SELECT `url` FROM `urls` WHERE `url` = :url - ", - params! { - "url" => url - }, - |url| url, - ) - .unwrap(); - result.len() > 0 + }).unwrap(); + SubmittedUrl { + shortened: shortened.to_string(), + success: true + } + } + } } -pub fn shortened_exists(pool: &Pool, shortened: &str) -> bool { - let mut conn = pool.get_conn().unwrap(); - let result: Vec = conn - .exec_map( - r" - SELECT `shortened` FROM `urls` WHERE `shortened` = :shortened - ", - params! { - "shortened" => shortened - }, - |shortened| shortened, - ) - .unwrap(); - result.len() > 0 +// Description: This function takes in a connection and returns a u64 which is the number of rows in the table +pub async fn count_urls (connection: &mut PooledConn) -> u64 { + let mut result = connection.exec_iter("SELECT COUNT(*) FROM urls", ()).unwrap(); + let row = result.next().unwrap(); + let count = row.unwrap().get::("COUNT(*)").unwrap(); + count +} + +// Description: This function takes in a connection and returns a bool which is true if the table was created +pub async fn create_table (connection: &mut PooledConn) -> bool { + let mut result = connection.exec_iter("CREATE TABLE IF NOT EXISTS urls (url VARCHAR(255) NOT NULL, shortened VARCHAR(255) NOT NULL, PRIMARY KEY (shortened))", ()).unwrap(); + let row = result.next(); + if row.is_some() { + true + } else { + false + } } -pub fn count_urls(pool: &Pool) -> u64 { - let mut conn = pool.get_conn().unwrap(); - let result: Vec = conn - .exec_map( - r" - SELECT COUNT(*) FROM `urls` - ", - (), - |count| count, - ) - .unwrap(); - result.into_iter().next().unwrap() -} diff --git a/src/endpoints.rs b/src/endpoints.rs index 78e1a02..e16a1a0 100644 --- a/src/endpoints.rs +++ a/src/endpoints.rs @@ -1,29 +1,61 @@ -use actix_files::NamedFile; use std::path::PathBuf; -use actix_web::{web, HttpResponse, HttpRequest, Responder}; +use actix_files::NamedFile; +use actix_web::{web, HttpResponse, Responder}; use serde::{Deserialize, Serialize}; + +use crate::{database::{self, SubmittedUrl}, shortener::{self, base64}, url, templating}; #[derive(Deserialize, Serialize, Debug)] pub struct Submission { url: String } - -pub async fn file(req: HttpRequest) -> Result { - let path_string = req.match_info().query("filename"); - let path: PathBuf = if path_string != "" { - PathBuf::from(path_string) - } else { - PathBuf::from("index.html") - }; - Ok(NamedFile::open(path)?) +// write a version of the above function, but without using NamedFile and using standard rust fs libraries instead +pub async fn static_file(path: web::Path) -> impl Responder { + let path_string = path.into_inner(); + println!("Accessing file {:?}", path_string); + let file = std::fs::read_to_string(PathBuf::from(format!("static/{}", path_string))).unwrap(); + HttpResponse::Ok().body(file) } -pub async fn hello(req: HttpRequest) -> impl Responder { - //println!("HTTP Host Address: {:?}", req.app_data::().unwrap().config.http.host); - HttpResponse::Ok().body("Hello world!") +/* -> Result { + let path_string = path.into_inner(); + println!("Accessing file {:?}", path_string); + Ok(NamedFile::open(PathBuf::from(format!("static/{}", path_string)))?) +} */ + +pub async fn index() -> Result { + Ok(NamedFile::open("index.html")?) +} +pub async fn submit_url(form: web::Form, app_data: web::Data) -> impl Responder { + let url = url::format_url(form.url.clone()); + let mut connection = app_data.database.get_conn().unwrap(); + let count = database::count_urls(&mut connection).await; + for n in 0..3{ + let shortened = shortener::base64(count); + let submitted_url = database::submit_url(&mut connection, &url, &shortened).await; + if submitted_url.success { + return HttpResponse::Ok().body( + templating::read_and_apply_templates( + PathBuf::from("results.html"), + templating::TemplateSchema { + url: url, + shortened: submitted_url.shortened, + domain: app_data.config.application.templating.domain.clone(), + count: count.to_string() + } + ) + ); + } + } + HttpResponse::InternalServerError().body("An error occured while submitting your URL") } -pub async fn submit_url(form: web::Form) -> impl Responder { - println!("Received submission: {:?}", form); - HttpResponse::Ok().body(format!("Received submission: {:?}", form)) +// Takes a shortened URL and redirects to the original URL +pub async fn redirect_url(path: web::Path<(String)>, app_data: web::Data) -> impl Responder { + println!("Redirect request recieved to {:?}", path); + let (shortened) = path.into_inner(); + let mut connection = app_data.database.get_conn().unwrap(); + let retrieved_url = database::retrieve_url(&mut connection, &shortened).await; + println!("Redirecting to {:?}", retrieved_url.url); + HttpResponse::Found().header("Location", retrieved_url.url).finish() }diff --git a/src/main.rs b/src/main.rs index a18b191..222801b 100644 --- a/src/main.rs +++ a/src/main.rs @@ -1,41 +1,58 @@ +use actix_settings::{BasicSettings, ApplySettings}; use actix_web::{web, App, HttpServer, middleware::Condition, middleware::Logger, middleware::Compress}; -// use mysql::Pool; +use mysql::Pool; pub mod database; pub mod endpoints; pub mod settings; +pub mod shortener; +pub mod url; +pub mod templating; /* TODO: - * - make config accessible universally, or somehow transfer config data to how functions work - * - make database accessible universally - * - make shortened url generation - * - make shortened url redirect + * - clean up code + * - add HTML templates * - implement other functions (pastebin maybe?) */ +#[derive(Clone)] +pub struct AppData { + config: BasicSettings, + database: Pool +} + #[actix_web::main] async fn main() -> std::io::Result<()> { + println!("Importing settings..."); let settings = settings::init(); - HttpServer::new(move || { - let settings = settings.clone(); - App::new() - // Grab compression settings from config.toml - .wrap(Condition::new( - settings.actix.enable_compression, - Compress::default(), - )) - // Grab logger settings from config.toml - .wrap(Condition::new( - settings.actix.enable_log, - Logger::default(), - )) - .app_data(web::Data::new(settings.clone())) - .route("/", web::get().to(endpoints::hello)) - .route("/{filename:.*}", web::get().to(endpoints::file)) - .route("/", web::post().to(endpoints::submit_url)) + println!("Starting server..."); + let server = HttpServer::new({ + let app_data = AppData { + config: settings.clone(), + database: database::init(&settings).await + }; + move || { + App::new() + // Grab compression settings from config.toml + .wrap(Condition::new( + settings.actix.enable_compression, + Compress::default(), + )) + // Grab logger settings from config.toml + .wrap(Condition::new( + settings.actix.enable_log, + Logger::default(), + )) + .app_data(web::Data::new(app_data.clone())) + .route("/", web::get().to(endpoints::index)) + .route("/static/{filename}", web::get().to(endpoints::static_file)) + .route("/{shortened}", web::get().to(endpoints::redirect_url)) + .route("/", web::post().to(endpoints::submit_url)) + } }) - .bind(("127.0.0.1", 4000))? - .run() - .await + .apply_settings(&settings) + .run(); + println!("Server started!"); + server.await }diff --git a/src/settings.rs b/src/settings.rs index bc54768..b195b88 100644 --- a/src/settings.rs +++ a/src/settings.rs @@ -1,9 +1,10 @@ -use actix_settings::{ApplySettings as _, Settings, BasicSettings}; +use actix_settings::{BasicSettings}; use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Deserialize, Serialize)] pub struct AppSettings { - pub database: DatabaseSettings + pub database: DatabaseSettings, + pub templating: TemplatingSettings } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -13,6 +14,12 @@ pub username: String, pub password: String, pub database: String +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct TemplatingSettings { + pub enabled: bool, + pub domain: String } pub fn init () -> BasicSettings { diff --git a/src/shortener.rs b/src/shortener.rs new file mode 100644 index 0000000..2e90ea5 100644 --- /dev/null +++ a/src/shortener.rs @@ -1,0 +1,39 @@ +// Dangling library of functions to create as short of a shortened string as humanly possible. + +const BASE64_CHARS: [char; 64] = [ + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', + 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', + 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', + 'v', 'w', 'x', 'y', 'z', '-', '_', +]; + +pub fn base64(num: u64) -> String { + let mut result = String::new(); + let mut num = num; + while num > 0 { + let remainder = num % 64; + num = num / 64; + result.push(BASE64_CHARS[remainder as usize]); + } + result +} + +// creates a string of random base64 characters, initial length is 4, but if the total amount of strings exceeds 64^4, it will increase the length of the string by 1 +/* +pub fn random_base64(num: u64) -> String { + let mut result = String::new(); + let mut num = num; + let mut length = 4; + while num > 0 { + let remainder = num % 64; + num = num / 64; + result.push(BASE64_CHARS[remainder as usize]); + if num > 0 { + length += 1; + } + } + while result.len() < length { + result.push(BASE64_CHARS[rand::random::() % 64]); + } + result +} */diff --git a/src/templating.rs b/src/templating.rs new file mode 100644 index 0000000..da40319 100644 --- /dev/null +++ a/src/templating.rs @@ -1,0 +1,22 @@ +use std::fs::File; +use std::io::Read; +use std::path::PathBuf; + +pub struct TemplateSchema { + pub url: String, + pub shortened: String, + pub domain: String, + pub count: String +} + +pub fn read_and_apply_templates(path: PathBuf, schema: TemplateSchema) -> String { + let mut file = File::open(path).unwrap(); + let mut contents = String::new(); + file.read_to_string(&mut contents).unwrap(); + // Hardcoded templates, will change this if/when the amount of templates increases + contents + .replace("{{url}}", &schema.url) + .replace("{{shortened}}", &schema.shortened) + .replace("{{domain}}", &schema.domain) + .replace("{{count}}", &schema.count) +}diff --git a/src/url.rs b/src/url.rs new file mode 100644 index 0000000..486f045 100644 --- /dev/null +++ a/src/url.rs @@ -1,0 +1,8 @@ +// checks if string could possibly be a valid URL, and if it isn't, formats it into a valid URL +pub fn format_url(url: String) -> String { + if url.contains("://") { + url + } else { + format!("http://{}", url) + } +}diff --git a/static/style.css b/static/style.css new file mode 100644 index 0000000..359f792 100644 --- /dev/null +++ a/static/style.css @@ -1,0 +1,5 @@ +body { + font-family: sans-serif; + background-color: #f0f0f0; + text-align: center; +}diff --git a/target/.rustc_info.json b/target/.rustc_info.json index 03f47ec..5d51843 100644 --- a/target/.rustc_info.json +++ a/target/.rustc_info.json @@ -1,1 +1,1 @@ -{"rustc_fingerprint":13098977256995595289,"outputs":{"15697416045686424142":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.dylib\nlib___.dylib\nlib___.a\nlib___.dylib\n","stderr":""},"10376369925670944939":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.dylib\nlib___.dylib\nlib___.a\nlib___.dylib\n/opt/homebrew/Cellar/rust/1.65.0\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_arch=\"aarch64\"\ntarget_endian=\"little\"\ntarget_env=\"\"\ntarget_family=\"unix\"\ntarget_feature=\"aes\"\ntarget_feature=\"crc\"\ntarget_feature=\"dit\"\ntarget_feature=\"dotprod\"\ntarget_feature=\"dpb\"\ntarget_feature=\"dpb2\"\ntarget_feature=\"fcma\"\ntarget_feature=\"fhm\"\ntarget_feature=\"flagm\"\ntarget_feature=\"fp16\"\ntarget_feature=\"frintts\"\ntarget_feature=\"jsconv\"\ntarget_feature=\"lor\"\ntarget_feature=\"lse\"\ntarget_feature=\"neon\"\ntarget_feature=\"paca\"\ntarget_feature=\"pacg\"\ntarget_feature=\"pan\"\ntarget_feature=\"pmuv3\"\ntarget_feature=\"ras\"\ntarget_feature=\"rcpc\"\ntarget_feature=\"rcpc2\"\ntarget_feature=\"rdm\"\ntarget_feature=\"sb\"\ntarget_feature=\"sha2\"\ntarget_feature=\"sha3\"\ntarget_feature=\"ssbs\"\ntarget_feature=\"vh\"\ntarget_has_atomic=\"128\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"macos\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"apple\"\nunix\n","stderr":""},"4614504638168534921":{"success":true,"status":"","code":0,"stdout":"rustc 1.65.0\nbinary: rustc\ncommit-hash: unknown\ncommit-date: unknown\nhost: aarch64-apple-darwin\nrelease: 1.65.0\nLLVM version: 15.0.0\n","stderr":""}},"successes":{}}+{"rustc_fingerprint":13098977256995595289,"outputs":{"10376369925670944939":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.dylib\nlib___.dylib\nlib___.a\nlib___.dylib\n/opt/homebrew/Cellar/rust/1.65.0\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_arch=\"aarch64\"\ntarget_endian=\"little\"\ntarget_env=\"\"\ntarget_family=\"unix\"\ntarget_feature=\"aes\"\ntarget_feature=\"crc\"\ntarget_feature=\"dit\"\ntarget_feature=\"dotprod\"\ntarget_feature=\"dpb\"\ntarget_feature=\"dpb2\"\ntarget_feature=\"fcma\"\ntarget_feature=\"fhm\"\ntarget_feature=\"flagm\"\ntarget_feature=\"fp16\"\ntarget_feature=\"frintts\"\ntarget_feature=\"jsconv\"\ntarget_feature=\"lor\"\ntarget_feature=\"lse\"\ntarget_feature=\"neon\"\ntarget_feature=\"paca\"\ntarget_feature=\"pacg\"\ntarget_feature=\"pan\"\ntarget_feature=\"pmuv3\"\ntarget_feature=\"ras\"\ntarget_feature=\"rcpc\"\ntarget_feature=\"rcpc2\"\ntarget_feature=\"rdm\"\ntarget_feature=\"sb\"\ntarget_feature=\"sha2\"\ntarget_feature=\"sha3\"\ntarget_feature=\"ssbs\"\ntarget_feature=\"vh\"\ntarget_has_atomic=\"128\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"macos\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"apple\"\nunix\n","stderr":""},"15697416045686424142":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.dylib\nlib___.dylib\nlib___.a\nlib___.dylib\n","stderr":""},"4614504638168534921":{"success":true,"status":"","code":0,"stdout":"rustc 1.65.0\nbinary: rustc\ncommit-hash: unknown\ncommit-date: unknown\nhost: aarch64-apple-darwin\nrelease: 1.65.0\nLLVM version: 15.0.0\n","stderr":""}},"successes":{}}diff --git a/target/debug/url_shortener b/target/debug/url_shortener index f650a3f7de1ee2e49f753d57a2a7d053c2580946..7858f6b0913859e465e41578426dfb769e3c9e8f 100755 Binary files a/target/debug/url_shortener and a/target/debug/url_shortener differ diff --git a/target/debug/url_shortener.d b/target/debug/url_shortener.d index 5cefc33..e49228f 100644 --- a/target/debug/url_shortener.d +++ a/target/debug/url_shortener.d @@ -1,1 +1,1 @@ -/Users/yaqub/OneDrive\ -\ University\ of\ St\ Andrews/dev/rust/url_shortener/target/debug/url_shortener: /Users/yaqub/OneDrive\ -\ University\ of\ St\ Andrews/dev/rust/url_shortener/build.rs /Users/yaqub/OneDrive\ -\ University\ of\ St\ Andrews/dev/rust/url_shortener/src/database.rs /Users/yaqub/OneDrive\ -\ University\ of\ St\ Andrews/dev/rust/url_shortener/src/endpoints.rs /Users/yaqub/OneDrive\ -\ University\ of\ St\ Andrews/dev/rust/url_shortener/src/main.rs /Users/yaqub/OneDrive\ -\ University\ of\ St\ Andrews/dev/rust/url_shortener/src/settings.rs +/Users/yaqub/OneDrive\ -\ University\ of\ St\ Andrews/dev/rust/url_shortener/target/debug/url_shortener: /Users/yaqub/OneDrive\ -\ University\ of\ St\ Andrews/dev/rust/url_shortener/build.rs /Users/yaqub/OneDrive\ -\ University\ of\ St\ Andrews/dev/rust/url_shortener/src/database.rs /Users/yaqub/OneDrive\ -\ University\ of\ St\ Andrews/dev/rust/url_shortener/src/endpoints.rs /Users/yaqub/OneDrive\ -\ University\ of\ St\ Andrews/dev/rust/url_shortener/src/main.rs /Users/yaqub/OneDrive\ -\ University\ of\ St\ Andrews/dev/rust/url_shortener/src/settings.rs /Users/yaqub/OneDrive\ -\ University\ of\ St\ Andrews/dev/rust/url_shortener/src/shortener.rs /Users/yaqub/OneDrive\ -\ University\ of\ St\ Andrews/dev/rust/url_shortener/src/templating.rs /Users/yaqub/OneDrive\ -\ University\ of\ St\ Andrews/dev/rust/url_shortener/src/url.rs -- rgit 0.1.5