Skip to content

Commit

Permalink
Add BrowserHistory and BrowserLocation (#171)
Browse files Browse the repository at this point in the history
* Add Initial Skeletal Implementation.

* Add Browser and Any variant.

* Rename path to pathname.

* Move state to location.

* Add tests.

* Add some documentation.

* Fix documentation.

* Fix doc tests.

* More matching, less destructing.

* More Cows, Less Strings.

* To serialize, or not to serialize?

* Tests should be sane without serialize.

* Expect to throw.

* Name history, history!

* Must have a message.

* Once serialize, now state & query.
  • Loading branch information
futursolo committed Nov 29, 2021
1 parent 91eb9e0 commit 2245cd9
Show file tree
Hide file tree
Showing 14 changed files with 974 additions and 2 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ gloo-storage = { version = "0.2.0", path = "crates/storage" }
gloo-render = { version = "0.1.0", path = "crates/render" }
gloo-console = { version = "0.2.1", path = "crates/console" }
gloo-utils = { version = "0.1.1", path = "crates/utils" }
gloo-history = { version = "0.1.0", path = "crates/history" }

[features]
default = []
Expand All @@ -37,4 +38,5 @@ members = [
"crates/storage",
"crates/console",
"crates/utils",
"crates/history",
]
38 changes: 38 additions & 0 deletions crates/history/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
[package]
name = "gloo-history"
version = "0.1.0"
description = "Universal Session History"
authors = ["Rust and WebAssembly Working Group"]
edition = "2018"
license = "MIT OR Apache-2.0"
readme = "README.md"
repository = "https://github.com/rustwasm/gloo/tree/master/crates/history"
homepage = "https://github.com/rustwasm/gloo"
categories = ["api-bindings", "history", "wasm"]

[dependencies]
wasm-bindgen = "0.2"
gloo-utils = { version = "0.1.0", path = "../utils" }
gloo-events = { version = "0.1.0", path = "../events" }
serde = { version = "1", optional = true }
serde_urlencoded = { version = "0.7", optional = true }
serde-wasm-bindgen = { version = "0.3.1", optional = true }
thiserror = { version = "1.0", optional = true }

[dependencies.web-sys]
version = "0.3"
features = [
"History",
"Window",
"Location",
]

[dev-dependencies]
wasm-bindgen-test = "0.3"
serde = { version = "1", features = ["derive"] }
gloo-timers = { version = "0.2.0", features = ["futures"], path = "../timers" }

[features]
query = ["serde", "thiserror", "serde_urlencoded"]
state = ["serde", "thiserror", "serde-wasm-bindgen"]
default = ["query", "state"]
203 changes: 203 additions & 0 deletions crates/history/src/any.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
use std::borrow::Cow;

#[cfg(feature = "serde")]
use serde::de::DeserializeOwned;
#[cfg(feature = "serde")]
use serde::Serialize;

use crate::browser::{BrowserHistory, BrowserLocation};
#[cfg(feature = "serde")]
use crate::error::HistoryResult;
use crate::history::History;
use crate::listener::HistoryListener;
use crate::location::Location;

/// A [`History`] that provides a universial API to the underlying history type.
#[derive(Clone, PartialEq, Debug)]
pub enum AnyHistory {
/// A Browser History.
Browser(BrowserHistory),
}

/// The [`Location`] for [`AnyHistory`]
#[derive(Clone, PartialEq, Debug)]
pub enum AnyLocation {
/// A Browser Location.
Browser(BrowserLocation),
}

impl History for AnyHistory {
type Location = AnyLocation;

fn len(&self) -> usize {
match self {
Self::Browser(m) => m.len(),
}
}

fn go(&self, delta: isize) {
match self {
Self::Browser(m) => m.go(delta),
}
}

fn push<'a>(&self, route: impl Into<Cow<'a, str>>) {
match self {
Self::Browser(m) => m.push(route),
}
}

fn replace<'a>(&self, route: impl Into<Cow<'a, str>>) {
match self {
Self::Browser(m) => m.replace(route),
}
}

#[cfg(feature = "state")]
fn push_with_state<'a, T>(&self, route: impl Into<Cow<'a, str>>, state: T) -> HistoryResult<()>
where
T: Serialize + 'static,
{
match self {
Self::Browser(m) => m.push_with_state(route, state),
}
}

#[cfg(feature = "state")]
fn replace_with_state<'a, T>(
&self,
route: impl Into<Cow<'a, str>>,
state: T,
) -> HistoryResult<()>
where
T: Serialize + 'static,
{
match self {
Self::Browser(m) => m.replace_with_state(route, state),
}
}

#[cfg(feature = "query")]
fn push_with_query<'a, Q>(&self, route: impl Into<Cow<'a, str>>, query: Q) -> HistoryResult<()>
where
Q: Serialize,
{
match self {
Self::Browser(m) => m.push_with_query(route, query),
}
}
#[cfg(feature = "query")]
fn replace_with_query<'a, Q>(
&self,
route: impl Into<Cow<'a, str>>,
query: Q,
) -> HistoryResult<()>
where
Q: Serialize,
{
match self {
Self::Browser(m) => m.replace_with_query(route, query),
}
}

#[cfg(all(feature = "query", feature = "state"))]
fn push_with_query_and_state<'a, Q, T>(
&self,
route: impl Into<Cow<'a, str>>,
query: Q,
state: T,
) -> HistoryResult<()>
where
Q: Serialize,
T: Serialize + 'static,
{
match self {
Self::Browser(m) => m.push_with_query_and_state(route, query, state),
}
}

#[cfg(all(feature = "query", feature = "state"))]
fn replace_with_query_and_state<'a, Q, T>(
&self,
route: impl Into<Cow<'a, str>>,
query: Q,
state: T,
) -> HistoryResult<()>
where
Q: Serialize,
T: Serialize + 'static,
{
match self {
Self::Browser(m) => m.replace_with_query_and_state(route, query, state),
}
}

fn listen<CB>(&self, callback: CB) -> HistoryListener
where
CB: Fn() + 'static,
{
match self {
Self::Browser(m) => m.listen(callback),
}
}

fn location(&self) -> Self::Location {
match self {
Self::Browser(m) => AnyLocation::Browser(m.location()),
}
}
}

impl Location for AnyLocation {
type History = AnyHistory;

fn path(&self) -> String {
match self {
Self::Browser(m) => m.path(),
}
}

fn search(&self) -> String {
match self {
Self::Browser(m) => m.search(),
}
}

#[cfg(feature = "query")]
fn query<T>(&self) -> HistoryResult<T>
where
T: DeserializeOwned,
{
match self {
Self::Browser(m) => m.query(),
}
}

fn hash(&self) -> String {
match self {
Self::Browser(m) => m.hash(),
}
}

#[cfg(feature = "state")]
fn state<T>(&self) -> HistoryResult<T>
where
T: DeserializeOwned + 'static,
{
match self {
Self::Browser(m) => m.state(),
}
}
}

impl From<BrowserHistory> for AnyHistory {
fn from(m: BrowserHistory) -> AnyHistory {
AnyHistory::Browser(m)
}
}

impl From<BrowserLocation> for AnyLocation {
fn from(m: BrowserLocation) -> AnyLocation {
AnyLocation::Browser(m)
}
}

0 comments on commit 2245cd9

Please sign in to comment.