diff options
Diffstat (limited to 'src/main.rs')
-rw-r--r-- | src/main.rs | 97 |
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 @@ | |||
1 | use futures_util::TryStreamExt; | ||
2 | use hyper::service::{make_service_fn, service_fn}; | ||
3 | use hyper::{Body, Method, Request, Response, Server, StatusCode}; | ||
4 | use nanoid::nanoid; | ||
5 | use rusqlite::{params, Connection, OpenFlags, Result, NO_PARAMS}; | ||
6 | use url::form_urlencoded; | ||
7 | |||
8 | use std::collections::HashMap; | ||
9 | use std::path::{Path, PathBuf}; | ||
10 | use std::str::from_utf8; | ||
11 | |||
12 | fn 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 | |||
27 | fn 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 | |||
39 | async 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 | |||
69 | fn 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] | ||
85 | async 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 | } | ||