Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: CI
on:
- push
- pull_request
jobs:
build:
name: Rust clippy and unit tests
runs-on: ubuntu-latest
strategy:
matrix:
rustup_channel:
- stable
- beta
- nightly
dev_flag:
- ""
- "--release"
steps:
- uses: actions/checkout@v2
- name: rustup install
run: |
rustup toolchain update --no-self-update ${{ matrix.rustup_channel }}
rustup default ${{ matrix.rustup_channel }}
- name: install just
run: cargo install just
- run: just build
- name: unit tests
run: just test
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/target
25 changes: 22 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
// <!--
riir_macro::print_literally! {
// -->
# RIIR
why not Rewrite It In Rust (**RIIR**)

Are you an author or contributor to a software project?

Have you ever been asked to rewrite, or consider rewriting that project in [Rust](https://www.rust-lang.org/)?
Have you ever been asked to rewrite, or consider rewriting that project in [Rust][rust-lang]?

If so, you may have been a victim of the RIIR agenda that is sweeping the web.

If this has happened to you, please [report it](https://github.com/ansuz/RIIR/issues/) so that something can be done.
If this has happened to you, please [report it][issues] so that something can be done.


## FAQ
Expand All @@ -18,7 +21,23 @@ No. This is a joke.

### Y U HATE RUST SO MUCH?

I don't, actually. I believe that those who spend their time asking people to rewrite their projects are probably not themselves active Rust developers, as those active devs are probably busy [writing memory-safe code](https://trac.torproject.org/projects/tor/ticket/11331).
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HTTPS links have // which prevents the () from being paired, causing errors at the lexing stage. They have been migrated to footnote style which does not wrap URLs inside brackets.

I don't, actually. I believe that those who spend their time asking people to rewrite their projects are probably not themselves active Rust developers, as those active devs are probably busy [writing memory-safe code][memory-safe].

### R U OFFENDING ME?
![](rust.png)

## Building
This readme is valid Rust language.
It can be built into an executable by running the [just][just] command:

just build

Then run "just test", and this readme will be regenerated in Rust code!

[rust-lang]: https://www.rust-lang.org/
[issues]: https://github.com/ansuz/RIIR/issues/
[memory-safe]: https://trac.torproject.org/projects/tor/ticket/11331
[just]: https://github.com/casey/just

// <!--
} // -->
19 changes: 19 additions & 0 deletions justfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
build: clean build-macro
#!/usr/bin/env bash
LIB_PATH=$(echo -n target/debug/deps/libriir_macro-*.so)
rustc \
--crate-name riir \
--edition=2018 \
README.md \
--crate-type bin \
--out-dir target/debug \
-L dependency=target/debug/deps \
--extern riir_macro=$LIB_PATH
clean:
#!/usr/bin/env bash
rm target/debug/deps/libriir_macro-*.so || true
build-macro:
cd riir-macro && cargo build --target-dir=../target $( (rustc --version | grep nightly >/dev/null) && echo --features span)

test:
cd riir-macro && cargo test --target-dir=../target $( (rustc --version | grep nightly >/dev/null) && echo --features span)
33 changes: 33 additions & 0 deletions riir-macro/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions riir-macro/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "riir-macro"
version = "0.1.0"
edition = "2018"

[lib]
proc-macro = true

[dependencies]
proc-macro2 = "1.0.18"
quote = "1.0.6"

[features]
span = ["proc-macro2/span-locations"]
142 changes: 142 additions & 0 deletions riir-macro/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
#![cfg_attr(feature = "span", feature(proc_macro_span))]

use proc_macro2::{Delimiter, Span, TokenStream, TokenTree};
use quote::quote;

#[proc_macro]
pub fn print_literally(ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
let string = print_ts(ts.into());
let ts = quote! {
fn main() {
print!(#string);
}
};
ts.into()
}

fn print_ts(ts: TokenStream) -> String {
let mut output = String::new();

#[cfg_attr(feature = "span", allow(unused_mut, unused_variables))]
let mut was_hash = false;
#[cfg_attr(not(feature = "span"), allow(unused_mut, unused_variables))]
let mut last_span: Option<Span> = None;

for tt in ts {
#[cfg(feature = "span")]
{
if let Some(last_span) = last_span {
let end = last_span.end();
let start = tt.span().start();
let end_col = if start.line > end.line {
let lines = start.line - end.line;
output.extend((0..lines).map(|_| '\n'));
0
} else {
end.column
};
let space_size = start.column as isize - end_col as isize;
output.extend((0..space_size).map(|_| ' '));
}
}
#[cfg_attr(not(feature = "span"), allow(unused_assignments))]
{
last_span = Some(tt.span());
}
match tt {
TokenTree::Ident(ident) => {
#[cfg_attr(not(feature = "span"), allow(unused_mut))]
let mut string = ident.to_string();
#[cfg(feature = "span")]
{
fill_space(ident.span(), &mut string);
}
#[cfg(not(feature = "span"))]
{
string.push(' ');
}
output.push_str(&string);
}
TokenTree::Punct(punct) => {
let mut string = punct.as_char().to_string();
#[cfg(feature = "span")]
{
fill_space(punct.span(), &mut string);
}
#[cfg(not(feature = "span"))]
{
use proc_macro2::Spacing;

if punct.as_char() == '#' {
if !was_hash {
string = format!("\n{}", string);
}
was_hash = true;
}
if punct.spacing() == Spacing::Joint {
string.push(' ');
}
}
output.push_str(&string);
}
TokenTree::Literal(lit) => {
#[cfg_attr(not(feature = "span"), allow(unused_mut))]
let mut string = lit.to_string();
#[cfg(feature = "span")]
{
fill_space(lit.span(), &mut string);
}
output.push_str(&string);
}
TokenTree::Group(group) => {
let mut string = String::new();
let (l, r) = match group.delimiter() {
Delimiter::Parenthesis => ("(", ")"),
Delimiter::Bracket => ("[", "]"),
Delimiter::Brace => ("{", "}"),
Delimiter::None => ("", ""),
};
string.push_str(l);
string.push_str(&print_ts(group.stream()));
string.push_str(r);
#[cfg(feature = "span")]
{
fill_space(group.span(), &mut string);
}
output.push_str(&string);
}
}
}
output
}

#[cfg(feature = "span")]
fn fill_space(span: Span, string: &mut String) {
let start_col = if span.end().line > span.start().line {
let lines = span.end().line - span.start().line;
string.extend((0..lines).map(|_| '\n'));
-1
} else {
(span.start().column + string.len()) as isize
};
let space_size = span.end().column as isize - start_col;
string.extend((0..space_size).map(|_| ' '));
}

#[cfg(test)]
#[test]
fn test() {
use std::{fs, path::PathBuf};

let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("test.txt");
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test file was made external instead of using quote!(), because quote!() does not allow literal expressions with a #.

let content = fs::read_to_string(path).unwrap();
let actual = print_ts(content.parse().unwrap());
#[cfg(feature = "span")]
{
assert_eq!(actual, content.trim());
}
#[cfg(not(feature = "span"))]
{
// TODO write unit tests for non-span-sensitive code
}
}
2 changes: 2 additions & 0 deletions riir-macro/test.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
a bc
## de f
1 change: 1 addition & 0 deletions rust-toolchain
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
nightly