#gemini #client #fun #experimental #actor #cli #aims

bin+lib trotter

Trotter 🎠 is an experimental crate that aims to make writing Gemini clients fun and easy

12 releases (3 stable)

1.0.2 Jul 4, 2024
1.0.1 Mar 31, 2024
0.6.0 Nov 21, 2023
0.5.2 Nov 18, 2023
0.1.0 Nov 11, 2023

#836 in Network programming

Download history 377/week @ 2024-03-31 22/week @ 2024-04-07 214/week @ 2024-04-14 12/week @ 2024-04-21 3/week @ 2024-04-28 5/week @ 2024-05-19 15/week @ 2024-05-26 7/week @ 2024-06-02 14/week @ 2024-06-09 9/week @ 2024-06-16 16/week @ 2024-06-23 122/week @ 2024-06-30 16/week @ 2024-07-07 12/week @ 2024-07-14

167 downloads per month
Used in 3 crates (2 directly)

GPL-3.0-only

37KB
809 lines

🎠 Trotter

Trotter is a fun and experimental gemini client crate.

Installing trot cli: cargo install --features cli trotter.

For simple requests, use trot and trot_in.

#[tokio::main]
async fn main() {
    trotter::trot("geminiprotocol.net") // gemini:// prefix and root slash can be elided
        .await
        .unwrap();

    trotter::trot_in("localhost/input", "notice me!")
        .await
        .unwrap();
}

😊 Requests

🎭 Actor

Actors store information relevant in multiple requests.

use trotter::{Actor, UserAgent};

#[tokio::main]
async fn main() {
    let owo = Actor::default()
        .user_agent(UserAgent::Indexer)
        .cert_file("id/owo.crt")
        .key_file("id/owo.key");

    owo.get("localhost")
        .await
        .unwrap();
}

🤖 User Agent

Did you know there's a version of the robots.txt standard for gemini? (robots.txt for Gemini)

Trotter has robots functionality built-in. Once you set your user-agent, you will receive a RobotDenied error if you try to access a page you are disallowed from.

I strongly suggest you do this if you're using Trotter for a project that depends on other peoples' content.

🌕 Titan

Titan is gemini's sister-protocol for uploading files.

To use titan, create a Titan struct and pass it to Actor::upload.

use trotter::{Actor, Titan};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let g = Actor::default()
        .upload(
            "titan://localhost/",
            Titan {
                content:  "Example content :DDDDDDDDDDDDDDDDDDDD".into(),
                mimetype: "text/plain".into(),
                token:    None,
            },
        )
        .await?
        .gemtext()?;
    println!("{g}");

    Ok(())
}

📖 Parse

use trotter::parse::Gemtext;

fn main() {
    let txt = "# 💎
## Is
### So
> effing
* dope
man
=> /path/to/somewhere i can take u there
``` alt text goes here
Here's a table
| The | Best |
|-----|------|
| 😘  | 😪   |
```";

    let gemtext = Gemtext::parse(txt);

    println!("{gemtext:#?}");
}

Tips

Certificates

With openssl installed, you can use the following shell functions to generate and inspect x509 certificates.

certgen() { [ -f "${1:?usage: certgen [domain]}.key" ] || [ -f "$1.crt" ] || ( openssl req -new -subj "/CN=$1" -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 -days 3650 -nodes -out "$1.crt" -keyout "$1.key" && printf '📜 Cert generated\n' ) ;}
certinfo(){ openssl x509 -noout -text < "${1:?usage: certinfo [file]}" ;}

Todo

  • Add --generate-cert option to cli.
  • Fix broken robots.txt :(
  • Tofu store directory
  • Byte read/write timeout

Dependencies

~6–16MB
~221K SLoC