18个版本 (4个主要破坏)
2023.10.27 |
|
---|---|
4.0.1 | 2024年7月4日 |
4.0.0 | 2024年2月29日 |
3.0.0 | 2024年2月28日 |
0.9.1 | 2023年11月23日 |
#663 in 网络编程
用于dovetail
42KB
806 行
🦊 Fluffer
Fluffer是一个有趣且实验性的Gemini服务器框架。
📔 概述
路由是返回任何实现GemBytes
特质的泛型函数。
有一些有用的内置实现。请在实验时查阅GemBytes
和Fluff
。同时,查看示例。
use fluffer::{App, Fluff};
#[tokio::main]
async fn main() {
App::default()
.route("/", |_| async {
"# Welcome\n=> /u32 Should show a number\n=> /pic 🦊 Here's a cool picture!"
})
.route("/u32", |_| async { 777 })
.route("/pic", |_| async { Fluff::File("picture.png".to_string()) })
.run()
.await;
}
💎 GemBytes
GemBytes
特质有一个返回Gemini字节响应的方法
<STATUS><SPACE><META>\r\n<CONTENT>
请记住,你必须包含空格字符<SPACE>
——即使<META>
为空。
要在类型上实现GemBytes
就是决定适当的响应。
例如:您可以使用格式化的gemtext表示MIME模糊的类型。
use fluffer::{GemBytes, async_trait};
struct Profile {
name: String,
bio: String,
}
#[async_trait]
impl GemBytes for Profile {
async fn gem_bytes(&self) -> Vec<u8> {
format!("20 text/gemini\r\n# {},\n\n## Bio\n\n{}", self.name, self.bio).into_bytes()
}
}
🙃 身份
Gemini使用证书来识别客户端。Client
结构体实现了常见功能。
🔗 输入、查询和参数
输入
调用Client::input
返回请求的查询行百分比值解码。
App::default()
.route("/" |c| async {
c.input().unwrap_or("no input 😥".to_string())
})
.run()
.await
.unwrap()
查询
对于不处理用户输入的路由,查询适合跟踪请求之间的UI状态。
例如,您可以通过重定向到具有特殊查询名称的路径将警告或错误消息添加到gemtext文档中。(例如 /home?err=bad%20thingg%20happened
),
Fluff变体Fluff::RedirectQueries
通过将请求重定向到包含键值查询的路由来提供帮助。
使用Client::query
来检查查询值。
参数
参数来自您在路由路径中定义的模式。
在路由字符串中定义一个参数,并通过调用Client::parameter
来访问它。
App::default()
.route("/page=:number" |c| async {
format!("{}", c.parameter("number").unwrap_or("0"))
})
.run()
.await
.unwrap()
如果您不熟悉matchit
,这里有一些示例
"/owo/:A/:B"
定义了A
和B
。(/owo/this_is_A/this_is_B
)"/page=:N/filter=:F
定义了N
和F
。(/page=20/filter=date
)
请注意:一些客户端会根据它们的URL缓存页面。您可能希望在更新频繁的路由中避免使用参数。
🏃 状态
Fluffer允许您选择一个数据对象作为Client
的泛型。
use fluffer::App;
use std::sync::{Arc, Mutex};
// Alias for Client<State>
type Client = fluffer::Client<Arc<Mutex<State>>>;
#[derive(Default)]
struct State {
visitors: u32,
}
async fn index(c: Client) -> String {
let mut state = c.state.lock().unwrap();
state.visitors += 1;
format!("Visitors: {}", state.visitors)
}
#[tokio::main]
async fn main() {
let state = Arc::new(Mutex::new(State::default()));
App::default()
.state(state) // <- Must be called first.
.route("/", index)
.run()
.await
.unwrap()
}
🌕 泰坦
Titan是用于上传文件的协议。
您可以通过调用App::titan
而不是App::route
来在路由上启用Titan。
在启用了Titan的路由上,Client
中的titan
属性可能会产生一个资源。
use fluffer::{App, Client};
async fn index(c: Client) -> String {
if let Some(titan) = c.titan {
return format!(
"Size: {}\nMime: {}\nContent: {}\nToken: {}",
titan.size,
titan.mime,
std::str::from_utf8(&titan.content).unwrap_or("[not utf8]"),
titan.token.unwrap_or(String::from("[no token]")),
);
}
format!(
"Hello, I'm expecting a text/plain gemini request.\n=> titan://{} Click me",
c.url.domain().unwrap_or("")
)
}
#[tokio::main]
async fn main() {
App::default()
.titan("/", index, 20_000_000) // < limits content size to 20mb
.run()
.await
.unwrap()
}
✨ 功能
名称 | 描述 | 默认 |
---|---|---|
interactive |
启用在运行时生成密钥/证书的提示。 | 是 |
anyhow |
为anyhow 启用GemBytes (不建议在调试之外使用) |
否 |
reqwest |
为reqwest::Result 和reqwest::Response 启用GemBytes |
否 |
依赖项
~9–24MB
~381K SLoC