Initial commit
This commit is contained in:
parent
0676fb3cd5
commit
2aeb2d5e19
11 changed files with 1761 additions and 4 deletions
6
.gitignore
vendored
6
.gitignore
vendored
|
@ -1,10 +1,8 @@
|
||||||
|
.idea/
|
||||||
|
|
||||||
# Generated by Cargo
|
# Generated by Cargo
|
||||||
# will have compiled files and executables
|
# will have compiled files and executables
|
||||||
/target/
|
/target/
|
||||||
|
|
||||||
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
|
||||||
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
|
||||||
Cargo.lock
|
|
||||||
|
|
||||||
# These are backup files generated by rustfmt
|
# These are backup files generated by rustfmt
|
||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
|
|
3
COPYING
Normal file
3
COPYING
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
This project is dual-licensed under the WTFPL and 0BSD licenses, copies of both are included.
|
||||||
|
|
||||||
|
You may use this code under the terms of either license.
|
1505
Cargo.lock
generated
Normal file
1505
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
18
Cargo.toml
Normal file
18
Cargo.toml
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
[package]
|
||||||
|
name = "bin"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Jordan Doyle <jordan@doyle.la>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
rocket = "0.4"
|
||||||
|
lazy_static = "1.2"
|
||||||
|
gpw = "0.1"
|
||||||
|
syntect = "3.0"
|
||||||
|
chashmap = { git = "https://github.com/redox-os/tfs" }
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
|
||||||
|
[dependencies.rocket_contrib]
|
||||||
|
version = "*"
|
||||||
|
default-features = false
|
||||||
|
features = ["tera_templates"]
|
1
LICENSE
Normal file
1
LICENSE
Normal file
|
@ -0,0 +1 @@
|
||||||
|
WTFPL OR 0BSD
|
5
LICENSE.0BSD
Normal file
5
LICENSE.0BSD
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
Copyright (C) 2019 by Jordan Doyle <jordan@doyle.la>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
13
LICENSE.WTFPL
Normal file
13
LICENSE.WTFPL
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
Version 2, December 2004
|
||||||
|
|
||||||
|
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
|
||||||
|
|
||||||
|
Everyone is permitted to copy and distribute verbatim or modified
|
||||||
|
copies of this license document, and changing it is allowed as long
|
||||||
|
as the name is changed.
|
||||||
|
|
||||||
|
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. You just DO WHAT THE FUCK YOU WANT TO.
|
110
src/main.rs
Normal file
110
src/main.rs
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
#![feature(proc_macro_hygiene, decl_macro)]
|
||||||
|
|
||||||
|
#[macro_use] extern crate lazy_static;
|
||||||
|
|
||||||
|
#[macro_use] extern crate rocket;
|
||||||
|
extern crate rocket_contrib;
|
||||||
|
|
||||||
|
extern crate gpw;
|
||||||
|
extern crate syntect;
|
||||||
|
extern crate chashmap;
|
||||||
|
|
||||||
|
use rocket_contrib::templates::Template;
|
||||||
|
use rocket::response::Redirect;
|
||||||
|
use rocket::request::Form;
|
||||||
|
|
||||||
|
use serde::Serialize;
|
||||||
|
use syntect::parsing::SyntaxSet;
|
||||||
|
use syntect::highlighting::ThemeSet;
|
||||||
|
use syntect::easy::HighlightLines;
|
||||||
|
use syntect::html::{styled_line_to_highlighted_html, IncludeBackground};
|
||||||
|
|
||||||
|
use chashmap::CHashMap;
|
||||||
|
use std::borrow::Cow;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref ENTRIES: CHashMap<String, String> = CHashMap::new();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(FromForm)]
|
||||||
|
struct IndexForm {
|
||||||
|
val: String
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/")]
|
||||||
|
fn index() -> Template {
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct Index {}
|
||||||
|
|
||||||
|
Template::render("index", Index {})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Generates a randomly generated id, stores the given paste under that id and then returns the id.
|
||||||
|
fn store_paste(content: String) -> String {
|
||||||
|
thread_local!(static KEYGEN: RefCell<gpw::PasswordGenerator> = RefCell::new(gpw::PasswordGenerator::default()));
|
||||||
|
|
||||||
|
let id = KEYGEN.with(|k| k.borrow_mut().next().unwrap());
|
||||||
|
ENTRIES.insert(id.clone(), content);
|
||||||
|
id
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("/", data = "<input>")]
|
||||||
|
fn submit(input: Form<IndexForm>) -> Redirect {
|
||||||
|
let id = store_paste(input.into_inner().val);
|
||||||
|
Redirect::to(format!("/{}", id))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[put("/", data = "<input>")]
|
||||||
|
fn submit_raw(input: String) -> String {
|
||||||
|
format!("https://{}/{}", "localhost:8000", store_paste(input))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Takes the content of a paste and the extension passed in by the viewer and will return the content
|
||||||
|
/// highlighted in the appropriate format in HTML.
|
||||||
|
fn highlight(content: &str, ext: &str) -> Option<String> {
|
||||||
|
lazy_static! {
|
||||||
|
static ref SS: SyntaxSet = SyntaxSet::load_defaults_newlines();
|
||||||
|
static ref TS: ThemeSet = ThemeSet::load_defaults();
|
||||||
|
}
|
||||||
|
|
||||||
|
let syntax = SS.find_syntax_by_extension(ext)?;
|
||||||
|
let mut h = HighlightLines::new(syntax, &TS.themes["base16-ocean.dark"]);
|
||||||
|
let regions = h.highlight(content, &SS);
|
||||||
|
|
||||||
|
Some(styled_line_to_highlighted_html(®ions[..], IncludeBackground::No))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/<key>")]
|
||||||
|
fn render<'a>(key: String) -> Option<Template> {
|
||||||
|
let mut splitter = key.splitn(2, ".");
|
||||||
|
let key = splitter.next().unwrap();
|
||||||
|
let ext = splitter.next();
|
||||||
|
|
||||||
|
// get() returns a read-only lock, we're not going to be writing to this key
|
||||||
|
// again so we can hold this for as long as we want
|
||||||
|
let entry = ENTRIES.get(key)?;
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct Render<'a> {
|
||||||
|
content: Cow<'a, String>
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(Template::render("paste", Render {
|
||||||
|
content: match ext {
|
||||||
|
None => Cow::Borrowed(&*entry),
|
||||||
|
Some(extension) => Cow::Owned(highlight(&*entry, extension)?)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
rocket::ignite()
|
||||||
|
.attach(Template::fairing())
|
||||||
|
.mount("/", routes![index, submit, submit_raw, render])
|
||||||
|
.launch();
|
||||||
|
}
|
29
src/templates/base.html.tera
Normal file
29
src/templates/base.html.tera
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>bin.</title>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
|
||||||
|
html, body { margin: 0; }
|
||||||
|
|
||||||
|
body {
|
||||||
|
min-height: 100vh;
|
||||||
|
padding: 2rem;
|
||||||
|
background: #263238;
|
||||||
|
color: #B0BEC5;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
{% block styles %}
|
||||||
|
{% endblock styles %}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{% block content %}
|
||||||
|
{% endblock content %}
|
||||||
|
</body>
|
||||||
|
</html>
|
59
src/templates/index.html.tera
Normal file
59
src/templates/index.html.tera
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
{% extends "base" %}
|
||||||
|
|
||||||
|
{% block styles %}
|
||||||
|
form { flex: 1; }
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: inherit;
|
||||||
|
font-family: monospace;
|
||||||
|
resize: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
button[type="submit"] {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 1rem;
|
||||||
|
right: 1rem;
|
||||||
|
|
||||||
|
height: 3rem;
|
||||||
|
width: 3rem;
|
||||||
|
border: none;
|
||||||
|
border-radius: 50%;
|
||||||
|
|
||||||
|
background: #2196F3;
|
||||||
|
|
||||||
|
color: white;
|
||||||
|
font-size: 2rem;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
button[type="submit"].hidden { display: none; }
|
||||||
|
{% endblock styles %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<form action="/" method="post">
|
||||||
|
<textarea name="val" placeholder="bin something" autofocus autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"></textarea>
|
||||||
|
|
||||||
|
<button type="submit" title="⌘+⏎">✎</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const form = document.querySelector('form');
|
||||||
|
const input = document.querySelector('textarea');
|
||||||
|
const button = document.querySelector('button[type="submit"]');
|
||||||
|
|
||||||
|
const onInput = () => button.classList.toggle('hidden', !input.value);
|
||||||
|
input.addEventListener('input', onInput);
|
||||||
|
onInput();
|
||||||
|
|
||||||
|
document.body.addEventListener('keydown', (e) => {
|
||||||
|
if (e.keyCode == 13 && e.metaKey) {
|
||||||
|
form.submit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock content %}
|
16
src/templates/paste.html.tera
Normal file
16
src/templates/paste.html.tera
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
{% extends "base" %}
|
||||||
|
|
||||||
|
{% block styles %}
|
||||||
|
pre {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
overflow: scroll;
|
||||||
|
font-family: Courier;
|
||||||
|
line-height: 1.1;
|
||||||
|
}
|
||||||
|
{% endblock styles %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<pre><code>{{ content | safe }}</code></pre>
|
||||||
|
{% endblock content %}
|
Loading…
Add table
Add a link
Reference in a new issue