aboutsummaryrefslogtreecommitdiff
path: root/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs97
1 files changed, 97 insertions, 0 deletions
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..801da3c
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,97 @@
1use futures_util::TryStreamExt;
2use hyper::service::{make_service_fn, service_fn};
3use hyper::{Body, Method, Request, Response, Server, StatusCode};
4use nanoid::nanoid;
5use rusqlite::{params, Connection, OpenFlags, Result, NO_PARAMS};
6use url::form_urlencoded;
7
8use std::collections::HashMap;
9use std::path::{Path, PathBuf};
10use std::str::from_utf8;
11
12fn shorten<S: AsRef<str>>(url: S, conn: &mut Connection) -> Result<String> {
13 let mut stmt = conn.prepare("select * from urls where link = ?1")?;
14 let mut rows = stmt.query(params![url.as_ref().to_string()])?;
15 if let Some(row) = rows.next()? {
16 return row.get(1);
17 } else {
18 let new_id = nanoid!(4);
19 conn.execute(
20 "insert into urls (link, shortlink) values (?1, ?2)",
21 params![url.as_ref().to_string(), new_id],
22 )?;
23 return Ok(new_id);
24 }
25}
26
27fn get_link<S: AsRef<str>>(url: S, conn: &mut Connection) -> Result<Option<String>> {
28 eprintln!("{}", url.as_ref());
29 let url = url.as_ref();
30 let mut stmt = conn.prepare("select * from urls where shortlink = ?1")?;
31 let mut rows = stmt.query(params![url.to_string()])?;
32 if let Some(row) = rows.next()? {
33 return Ok(row.get(0)?);
34 } else {
35 return Ok(None);
36 }
37}
38
39async fn shortner_service(req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
40 let mut conn = init_db("./urls.db_3").unwrap();
41
42 match req.method() {
43 &Method::POST => {
44 let b = hyper::body::to_bytes(req).await?;
45 let params = form_urlencoded::parse(b.as_ref())
46 .into_owned()
47 .collect::<HashMap<String, String>>();
48
49 if let Some(n) = params.get("shorten") {
50 let shortlink = shorten(n, &mut conn).unwrap();
51 return Ok(Response::new(Body::from(shortlink)));
52 } else {
53 return Ok(Response::new(Body::from("invalid form")));
54 };
55 }
56 &Method::GET => {
57 let shortlink = req.uri().path().to_string();
58 let link = get_link(&shortlink[1..], &mut conn);
59 if let Some(l) = link.unwrap() {
60 return Ok(Response::new(Body::from(l)));
61 } else {
62 return Ok(Response::new(Body::from("not found!")));
63 }
64 }
65 _ => unimplemented!(),
66 }
67}
68
69fn init_db<P: AsRef<Path>>(p: P) -> Result<Connection> {
70 let conn = Connection::open_with_flags(
71 p,
72 OpenFlags::SQLITE_OPEN_CREATE | OpenFlags::SQLITE_OPEN_READ_WRITE,
73 )?;
74 conn.execute(
75 "CREATE TABLE IF NOT EXISTS urls (
76 link TEXT PRIMARY KEY,
77 shortlink TEXT NOT NULL
78 )",
79 params![],
80 )?;
81 return Ok(conn);
82}
83
84#[tokio::main]
85async fn main() -> Result<()> {
86 let addr = ([127, 0, 0, 1], 3000).into();
87
88 let service =
89 make_service_fn(|_| async { Ok::<_, hyper::Error>(service_fn(shortner_service)) });
90
91 let server = Server::bind(&addr).serve(service);
92
93 println!("Listening on http://{}", addr);
94
95 server.await.unwrap();
96 Ok(())
97}