diff --git a/Cargo.lock b/Cargo.lock index 71a25e5..beba827 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -670,6 +670,15 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + [[package]] name = "fs_extra" version = "1.3.0" @@ -1232,6 +1241,12 @@ dependencies = [ "once_cell", ] +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + [[package]] name = "pin-project" version = "1.1.5" @@ -1526,6 +1541,18 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "sha-1" version = "0.10.1" @@ -2209,6 +2236,7 @@ dependencies = [ "scru128", "serde", "serde_json", + "serde_urlencoded", "ssri", "static_assertions", "tempfile", diff --git a/Cargo.toml b/Cargo.toml index 6327eb7..46c7bb8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ hyper-util = { version = "0.1", features = ["full"] } scru128 = { version = "3", features = ["serde"] } serde = { version = "1", features = ["derive"] } serde_json = "1" +serde_urlencoded = "0.7.1" ssri = "9.2.0" tokio = { version = "1", features = ["full"] } tokio-stream = "0.1.15" diff --git a/foo.nu b/foo.nu index ee78589..a698456 100755 --- a/foo.nu +++ b/foo.nu @@ -1,11 +1,20 @@ -#!/usr/bin/env -S nu --stdin +#!/usr/bin/env -S nu -def h. [path: string] { - curl -sN --unix-socket ./store/sock $"'localhost($path)'" | lines | each { from json } -} +alias and-then = if ($in | is-not-empty) +alias ? = if ($in | is-not-empty) { $in } +alias ?? = ? else { return } -let clip = ( h. / | first ) +def h. [ + path: string + --last-id: string +] { + let query = ( $last_id | and-then { $"?( {last_id: $last_id} | url build-query)" } ) + let url = $"localhost($path)($query)" + curl -sN --unix-socket ./store/sock $url | lines | each { from json } +} -$clip.hash +h. / --last-id "foo" -h. $"/cas/($clip.hash)" +# let clip = ( h. / | first ) +# $clip.hash +# h. $"/cas/($clip.hash)" diff --git a/src/http.rs b/src/http.rs index 1c40db2..0fc39c6 100644 --- a/src/http.rs +++ b/src/http.rs @@ -49,7 +49,9 @@ async fn get(store: Store, req: Request) -> HTTPResult { eprintln!("path: {:?}", req.uri().path()); match match_route(req.uri().path()) { Routes::Root => { - let rx = store.subscribe(ReadOptions { follow: false }).await; + let rx = store + .subscribe(ReadOptions::from_query(req.uri().query())) + .await; let stream = ReceiverStream::new(rx); let stream = stream.map(|frame| { eprintln!("streaming"); diff --git a/src/store.rs b/src/store.rs index 8d456a0..d0c6af7 100644 --- a/src/store.rs +++ b/src/store.rs @@ -23,9 +23,33 @@ pub struct Store { commands_tx: mpsc::Sender, } -#[derive(Debug)] +use serde::Deserializer; + +fn deserialize_follow<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + let s: String = Deserialize::deserialize(deserializer)?; + match s.as_str() { + "false" | "no" | "0" => Ok(false), + _ => Ok(true), + } +} + +#[derive(Deserialize, Debug, Default)] pub struct ReadOptions { + #[serde(default, deserialize_with = "deserialize_follow")] pub follow: bool, + pub last_id: Option, +} + +impl ReadOptions { + pub fn from_query(query: Option<&str>) -> Self { + match query { + Some(q) => serde_urlencoded::from_str(q).unwrap(), + None => Self::default(), + } + } } #[derive(Debug)] @@ -130,3 +154,18 @@ mod tests { assert_impl_all!(Store: Send, Sync); } } + +#[cfg(test)] +mod tests_read_options { + use super::*; + + #[test] + fn test_from_query() { + assert_eq!(ReadOptions::from_query(None).follow, false); + assert_eq!(ReadOptions::from_query(Some("foo=bar")).follow, false); + assert_eq!(ReadOptions::from_query(Some("follow")).follow, true); + assert_eq!(ReadOptions::from_query(Some("follow=1")).follow, true); + assert_eq!(ReadOptions::from_query(Some("follow=yes")).follow, true); + assert_eq!(ReadOptions::from_query(Some("follow=true")).follow, true); + } +}