зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1639449 - Vendor new application-services to expose kinto migration and sync change notifications. r=lina
Differential Revision: https://phabricator.services.mozilla.com/D76092
This commit is contained in:
Родитель
f43c78c21f
Коммит
6abadd577b
|
@ -25,7 +25,7 @@ rev = "0dc3e6e7c5371fe21f69b847f61c65fe6d6dc317"
|
|||
[source."https://github.com/mozilla/application-services"]
|
||||
git = "https://github.com/mozilla/application-services"
|
||||
replace-with = "vendored-sources"
|
||||
rev = "77a7f5eb12a8d93f2bd71bd4d844405e06743365"
|
||||
rev = "57a07e89e9ac92756b60b67c4a6ee06975b86288"
|
||||
|
||||
[source."https://github.com/mozilla-spidermonkey/jsparagus"]
|
||||
git = "https://github.com/mozilla-spidermonkey/jsparagus"
|
||||
|
|
|
@ -1273,7 +1273,7 @@ checksum = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3"
|
|||
[[package]]
|
||||
name = "error-support"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/mozilla/application-services?rev=77a7f5eb12a8d93f2bd71bd4d844405e06743365#77a7f5eb12a8d93f2bd71bd4d844405e06743365"
|
||||
source = "git+https://github.com/mozilla/application-services?rev=57a07e89e9ac92756b60b67c4a6ee06975b86288#57a07e89e9ac92756b60b67c4a6ee06975b86288"
|
||||
dependencies = [
|
||||
"failure",
|
||||
]
|
||||
|
@ -1606,7 +1606,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "fxa-client"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/mozilla/application-services?rev=77a7f5eb12a8d93f2bd71bd4d844405e06743365#77a7f5eb12a8d93f2bd71bd4d844405e06743365"
|
||||
source = "git+https://github.com/mozilla/application-services?rev=57a07e89e9ac92756b60b67c4a6ee06975b86288#57a07e89e9ac92756b60b67c4a6ee06975b86288"
|
||||
dependencies = [
|
||||
"base64 0.12.0",
|
||||
"byteorder",
|
||||
|
@ -2295,7 +2295,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "interrupt-support"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/mozilla/application-services?rev=77a7f5eb12a8d93f2bd71bd4d844405e06743365#77a7f5eb12a8d93f2bd71bd4d844405e06743365"
|
||||
source = "git+https://github.com/mozilla/application-services?rev=57a07e89e9ac92756b60b67c4a6ee06975b86288#57a07e89e9ac92756b60b67c4a6ee06975b86288"
|
||||
|
||||
[[package]]
|
||||
name = "intl-memoizer"
|
||||
|
@ -3228,7 +3228,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "nss"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/mozilla/application-services?rev=77a7f5eb12a8d93f2bd71bd4d844405e06743365#77a7f5eb12a8d93f2bd71bd4d844405e06743365"
|
||||
source = "git+https://github.com/mozilla/application-services?rev=57a07e89e9ac92756b60b67c4a6ee06975b86288#57a07e89e9ac92756b60b67c4a6ee06975b86288"
|
||||
dependencies = [
|
||||
"base64 0.12.0",
|
||||
"error-support",
|
||||
|
@ -3242,12 +3242,12 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "nss_build_common"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/mozilla/application-services?rev=77a7f5eb12a8d93f2bd71bd4d844405e06743365#77a7f5eb12a8d93f2bd71bd4d844405e06743365"
|
||||
source = "git+https://github.com/mozilla/application-services?rev=57a07e89e9ac92756b60b67c4a6ee06975b86288#57a07e89e9ac92756b60b67c4a6ee06975b86288"
|
||||
|
||||
[[package]]
|
||||
name = "nss_sys"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/mozilla/application-services?rev=77a7f5eb12a8d93f2bd71bd4d844405e06743365#77a7f5eb12a8d93f2bd71bd4d844405e06743365"
|
||||
source = "git+https://github.com/mozilla/application-services?rev=57a07e89e9ac92756b60b67c4a6ee06975b86288#57a07e89e9ac92756b60b67c4a6ee06975b86288"
|
||||
dependencies = [
|
||||
"nss_build_common",
|
||||
]
|
||||
|
@ -3372,9 +3372,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.3.1"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1c601810575c99596d4afc46f78a678c80105117c379eb3650cf99b8a21ce5b"
|
||||
checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d"
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
|
@ -3894,7 +3894,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "rc_crypto"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/mozilla/application-services?rev=77a7f5eb12a8d93f2bd71bd4d844405e06743365#77a7f5eb12a8d93f2bd71bd4d844405e06743365"
|
||||
source = "git+https://github.com/mozilla/application-services?rev=57a07e89e9ac92756b60b67c4a6ee06975b86288#57a07e89e9ac92756b60b67c4a6ee06975b86288"
|
||||
dependencies = [
|
||||
"base64 0.12.0",
|
||||
"ece",
|
||||
|
@ -4454,7 +4454,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sql-support"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/mozilla/application-services?rev=77a7f5eb12a8d93f2bd71bd4d844405e06743365#77a7f5eb12a8d93f2bd71bd4d844405e06743365"
|
||||
source = "git+https://github.com/mozilla/application-services?rev=57a07e89e9ac92756b60b67c4a6ee06975b86288#57a07e89e9ac92756b60b67c4a6ee06975b86288"
|
||||
dependencies = [
|
||||
"ffi-support",
|
||||
"interrupt-support",
|
||||
|
@ -4651,7 +4651,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sync-guid"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/mozilla/application-services?rev=77a7f5eb12a8d93f2bd71bd4d844405e06743365#77a7f5eb12a8d93f2bd71bd4d844405e06743365"
|
||||
source = "git+https://github.com/mozilla/application-services?rev=57a07e89e9ac92756b60b67c4a6ee06975b86288#57a07e89e9ac92756b60b67c4a6ee06975b86288"
|
||||
dependencies = [
|
||||
"base64 0.12.0",
|
||||
"rand",
|
||||
|
@ -4662,7 +4662,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sync15"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/mozilla/application-services?rev=77a7f5eb12a8d93f2bd71bd4d844405e06743365#77a7f5eb12a8d93f2bd71bd4d844405e06743365"
|
||||
source = "git+https://github.com/mozilla/application-services?rev=57a07e89e9ac92756b60b67c4a6ee06975b86288#57a07e89e9ac92756b60b67c4a6ee06975b86288"
|
||||
dependencies = [
|
||||
"base16",
|
||||
"base64 0.12.0",
|
||||
|
@ -4685,7 +4685,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sync15-traits"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/mozilla/application-services?rev=77a7f5eb12a8d93f2bd71bd4d844405e06743365#77a7f5eb12a8d93f2bd71bd4d844405e06743365"
|
||||
source = "git+https://github.com/mozilla/application-services?rev=57a07e89e9ac92756b60b67c4a6ee06975b86288#57a07e89e9ac92756b60b67c4a6ee06975b86288"
|
||||
dependencies = [
|
||||
"failure",
|
||||
"ffi-support",
|
||||
|
@ -5301,7 +5301,7 @@ checksum = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce"
|
|||
[[package]]
|
||||
name = "viaduct"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/mozilla/application-services?rev=77a7f5eb12a8d93f2bd71bd4d844405e06743365#77a7f5eb12a8d93f2bd71bd4d844405e06743365"
|
||||
source = "git+https://github.com/mozilla/application-services?rev=57a07e89e9ac92756b60b67c4a6ee06975b86288#57a07e89e9ac92756b60b67c4a6ee06975b86288"
|
||||
dependencies = [
|
||||
"failure",
|
||||
"failure_derive",
|
||||
|
@ -5417,7 +5417,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "webext-storage"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/mozilla/application-services?rev=77a7f5eb12a8d93f2bd71bd4d844405e06743365#77a7f5eb12a8d93f2bd71bd4d844405e06743365"
|
||||
source = "git+https://github.com/mozilla/application-services?rev=57a07e89e9ac92756b60b67c4a6ee06975b86288#57a07e89e9ac92756b60b67c4a6ee06975b86288"
|
||||
dependencies = [
|
||||
"error-support",
|
||||
"failure",
|
||||
|
|
|
@ -21,4 +21,4 @@ nsstring = { path = "../../../../xpcom/rust/nsstring" }
|
|||
xpcom = { path = "../../../../xpcom/rust/xpcom" }
|
||||
storage_variant = { path = "../../../../storage/variant" }
|
||||
thin-vec = { version = "0.1", features = ["gecko-ffi"] }
|
||||
fxa-client = { git = "https://github.com/mozilla/application-services", rev = "77a7f5eb12a8d93f2bd71bd4d844405e06743365", features = ["gecko"] }
|
||||
fxa-client = { git = "https://github.com/mozilla/application-services", rev = "57a07e89e9ac92756b60b67c4a6ee06975b86288", features = ["gecko"] }
|
||||
|
|
|
@ -8,14 +8,14 @@ edition = "2018"
|
|||
[dependencies]
|
||||
atomic_refcell = "0.1"
|
||||
cstr = "0.1"
|
||||
interrupt-support = { git = "https://github.com/mozilla/application-services", rev = "77a7f5eb12a8d93f2bd71bd4d844405e06743365" }
|
||||
interrupt-support = { git = "https://github.com/mozilla/application-services", rev = "57a07e89e9ac92756b60b67c4a6ee06975b86288" }
|
||||
log = "0.4"
|
||||
moz_task = { path = "../../../xpcom/rust/moz_task" }
|
||||
nserror = { path = "../../../xpcom/rust/nserror" }
|
||||
nsstring = { path = "../../../xpcom/rust/nsstring" }
|
||||
serde_json = "1"
|
||||
storage_variant = { path = "../../../storage/variant" }
|
||||
sync15-traits = { git = "https://github.com/mozilla/application-services", rev = "77a7f5eb12a8d93f2bd71bd4d844405e06743365" }
|
||||
sync15-traits = { git = "https://github.com/mozilla/application-services", rev = "57a07e89e9ac92756b60b67c4a6ee06975b86288" }
|
||||
xpcom = { path = "../../../xpcom/rust/xpcom" }
|
||||
|
||||
[dependencies.thin-vec]
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"b94ceb62a211c20a148b1d65b1c8d33183e7ab09f91db79049eab9638075ee1e","examples/devices_api.rs":"87dfa7e92b33f4a3b0334aacf379a082149db85800947e927dbbf409dbb568ca","examples/migration.rs":"2577e4eb3a14ac8a4e81b6ab79b6658871225b08deb2fb573211fd02daebbdfa","examples/oauth_flow.rs":"99b0cb7b2a12051d2e0893ca9202124e0b229e888ef541518ac1c64ce89d6aff","src/commands/mod.rs":"0197979f5851da2300a481b6f7d0bd1ac7e00b08383339564b2372519965ca58","src/commands/send_tab.rs":"07e9ddc6b172bb3cf1af61f610fb4d39f73e9d63e304a05903f60ee76e3fa75e","src/config.rs":"cfc435dd49c3cd552091d2d46e30f2d67cfb3ba562caade3b5224550896c49a1","src/device.rs":"ffc8dfbf2d564393bbe80ef60907a06982db695d06208babe0608b821f56fb75","src/error.rs":"845280fdd99b13eb79d370f3585ccf4df21cfa4c97697bd9f173fa960b735886","src/ffi.rs":"ffd787adf013d57f776a7a5aac0b895b7bbbd83fbd0ebf5e6f139b404ea40a46","src/fxa_msg_types.proto":"e09fa503f531e83e28d8a813f20338df0cf16f96b7059975d55421a0657656a6","src/http_client.rs":"a0b8f955682ca02af601fbbc60d6476fa07e15916b0bc54b5cf1b0fab4b49dfe","src/lib.rs":"bc6303f7b87bd9f28fc9c4358d5914afe6b2b17f8f76543f413ab2b5c4f20dfa","src/migrator.rs":"8711aad7272f24c4d4d6e57142bafea883331022a35951f06151b7c9855e2c7a","src/mozilla.appservices.fxaclient.protobuf.rs":"e28aaa9ebf0874c9b9e9a482aaf0ee5205b5bff031d4f998d740db198831e9bb","src/oauth.rs":"ae70655b4b38df8313d633be4be52af232981ad3b98334545abc9b447b4a3cea","src/oauth/attached_clients.rs":"e0fd277d4294fd7982100e171cce790ea4acefb22ab1c7ab7b36f8593038cc43","src/profile.rs":"7197b630ebb992fb517a4fd9f5c4c03a24d5f66c4d888ccd5489eda074a5e556","src/push.rs":"dc1e846d4a5b8e7b0615583b4649cf3b0643d608651f6b7a0432f1aebdbb5197","src/scoped_keys.rs":"509bab207ac38e3662c0df9cd58b874f48c91b0728441bdefd684b56b536c514","src/scopes.rs":"2cb0799b0fb8b338124137456fa3cf8d6f56c0ea5709daef4e305c06dd0045c5","src/send_tab.rs":"8fc9ce5d56aa796cd72f7a920e695dfc23146b329f682812a12cb8c336b177ed","src/state_persistence.rs":"7a4a50726b6da30609dc6ca279419806e07c1843c9d7e5cdc1ce0db5ab9a5a2b","src/util.rs":"ecfd2d55dd0b329aac07979a3849485f932af6dd2990f02805ad4668f2c97d67"},"package":null}
|
||||
{"files":{"Cargo.toml":"d9be79dd941f9a16b7e1b861820a718ce1eacc3085f62c1738157e1c8a47f0e0","examples/devices_api.rs":"87dfa7e92b33f4a3b0334aacf379a082149db85800947e927dbbf409dbb568ca","examples/migration.rs":"2577e4eb3a14ac8a4e81b6ab79b6658871225b08deb2fb573211fd02daebbdfa","examples/oauth_flow.rs":"99b0cb7b2a12051d2e0893ca9202124e0b229e888ef541518ac1c64ce89d6aff","src/commands/mod.rs":"0197979f5851da2300a481b6f7d0bd1ac7e00b08383339564b2372519965ca58","src/commands/send_tab.rs":"07e9ddc6b172bb3cf1af61f610fb4d39f73e9d63e304a05903f60ee76e3fa75e","src/config.rs":"b7771f5eb581b85a664202c8a63af158f36b672825c0dc164a2d986bfc2bf6fd","src/device.rs":"ffc8dfbf2d564393bbe80ef60907a06982db695d06208babe0608b821f56fb75","src/error.rs":"845280fdd99b13eb79d370f3585ccf4df21cfa4c97697bd9f173fa960b735886","src/ffi.rs":"ffd787adf013d57f776a7a5aac0b895b7bbbd83fbd0ebf5e6f139b404ea40a46","src/fxa_msg_types.proto":"e09fa503f531e83e28d8a813f20338df0cf16f96b7059975d55421a0657656a6","src/http_client.rs":"a0b8f955682ca02af601fbbc60d6476fa07e15916b0bc54b5cf1b0fab4b49dfe","src/lib.rs":"7b151b9a84bcd6a9bca7ecb1689e8909259ee058666ce20bcf8182b9b080c3b0","src/migrator.rs":"8711aad7272f24c4d4d6e57142bafea883331022a35951f06151b7c9855e2c7a","src/mozilla.appservices.fxaclient.protobuf.rs":"e28aaa9ebf0874c9b9e9a482aaf0ee5205b5bff031d4f998d740db198831e9bb","src/oauth.rs":"ae70655b4b38df8313d633be4be52af232981ad3b98334545abc9b447b4a3cea","src/oauth/attached_clients.rs":"e0fd277d4294fd7982100e171cce790ea4acefb22ab1c7ab7b36f8593038cc43","src/profile.rs":"7197b630ebb992fb517a4fd9f5c4c03a24d5f66c4d888ccd5489eda074a5e556","src/push.rs":"dc1e846d4a5b8e7b0615583b4649cf3b0643d608651f6b7a0432f1aebdbb5197","src/scoped_keys.rs":"509bab207ac38e3662c0df9cd58b874f48c91b0728441bdefd684b56b536c514","src/scopes.rs":"2cb0799b0fb8b338124137456fa3cf8d6f56c0ea5709daef4e305c06dd0045c5","src/send_tab.rs":"8fc9ce5d56aa796cd72f7a920e695dfc23146b329f682812a12cb8c336b177ed","src/state_persistence.rs":"7a4a50726b6da30609dc6ca279419806e07c1843c9d7e5cdc1ce0db5ab9a5a2b","src/util.rs":"ecfd2d55dd0b329aac07979a3849485f932af6dd2990f02805ad4668f2c97d67"},"package":null}
|
|
@ -7,14 +7,14 @@ license = "MPL-2.0"
|
|||
exclude = ["/android", "/ios"]
|
||||
|
||||
[dependencies]
|
||||
base64 = "0.12.0"
|
||||
base64 = "0.12"
|
||||
byteorder = "1.3"
|
||||
failure = "0.1"
|
||||
hex = "0.4"
|
||||
lazy_static = "1.4.0"
|
||||
lazy_static = "1.4"
|
||||
log = "0.4"
|
||||
prost = "0.6.1"
|
||||
prost-derive = "0.6.1"
|
||||
prost = "0.6"
|
||||
prost-derive = "0.6"
|
||||
serde = { version = "1", features = ["rc"] }
|
||||
serde_derive = "1"
|
||||
serde_json = "1"
|
||||
|
@ -30,7 +30,7 @@ viaduct-reqwest = { path = "../support/viaduct-reqwest" }
|
|||
cli-support = { path = "../support/cli" }
|
||||
dialoguer = "0.6"
|
||||
webbrowser = "0.5"
|
||||
mockiato = "0.9.5"
|
||||
mockiato = "0.9"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
|
|
@ -55,6 +55,7 @@ pub struct RemoteConfig {
|
|||
}
|
||||
|
||||
pub(crate) const CONTENT_URL_RELEASE: &str = "https://accounts.firefox.com";
|
||||
pub(crate) const CONTENT_URL_CHINA: &str = "https://accounts.firefox.com.cn";
|
||||
|
||||
impl Config {
|
||||
pub fn release(client_id: &str, redirect_uri: &str) -> Self {
|
||||
|
@ -70,7 +71,7 @@ impl Config {
|
|||
}
|
||||
|
||||
pub fn china(client_id: &str, redirect_uri: &str) -> Self {
|
||||
Self::new("https://accounts.firefox.com.cn", client_id, redirect_uri)
|
||||
Self::new(CONTENT_URL_CHINA, client_id, redirect_uri)
|
||||
}
|
||||
|
||||
pub fn localdev(client_id: &str, redirect_uri: &str) -> Self {
|
||||
|
|
|
@ -162,6 +162,10 @@ impl FirefoxAccount {
|
|||
if self.state.config.content_url()? == Url::parse(config::CONTENT_URL_RELEASE)? {
|
||||
return Ok(Url::parse("https://firefox.com/pair")?);
|
||||
}
|
||||
// Similarly special case for the China server.
|
||||
if self.state.config.content_url()? == Url::parse(config::CONTENT_URL_CHINA)? {
|
||||
return Ok(Url::parse("https://firefox.com.cn/pair")?);
|
||||
}
|
||||
Ok(self.state.config.pair_url()?)
|
||||
}
|
||||
|
||||
|
@ -571,6 +575,13 @@ mod tests {
|
|||
assert_eq!(
|
||||
fxa.get_pairing_authority_url().unwrap().as_str(),
|
||||
"https://firefox.com/pair"
|
||||
);
|
||||
|
||||
let config = Config::china("12345678", "https://foo.bar");
|
||||
let fxa = FirefoxAccount::with_config(config);
|
||||
assert_eq!(
|
||||
fxa.get_pairing_authority_url().unwrap().as_str(),
|
||||
"https://firefox.com.cn/pair"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"fdff2d3a254e13c2199b10cdeebb6c6af1b0445dbb1a9166c50bbf91e44b5955","README.md":"14dd59e435d179c21c3b4b880bbe3cc6e5999b9f9ac9431f3f9aa3f43902e3fa","src/aes.rs":"820a74d1c1b9b5c818f5e4c4b39afb4346e56b8512a0f280c0bd92b763f50486","src/ec.rs":"3dfb1b2f630e855a37be7b2c03121d069d0b1f0f65e06bcd46493e2a0206be99","src/ecdh.rs":"6a970e6a30dfba4c5f4d113a5b5f3a814ee650a54eba903f8a50b47e180a1ceb","src/error.rs":"de521060e8ec9ad2c125815eec45ef690ad479ab9d41dab5a26294ee6acd9980","src/lib.rs":"7e9e1ebfaf13af124a5226a46e01e70743f3419eb7acc38ffaf202605bb33b89","src/pk11/context.rs":"ab3cdc8949fc1974523f0c6bf376ab933646df499d568a908076ad80b11c7c56","src/pk11/mod.rs":"d78368654f9a8bc12f1403c4a096b63cf9834820ea6ed48418b9afaa0fc2299e","src/pk11/slot.rs":"9f0aa039a55e7b26dc2dd5d2d3451497af71d147513f59e9c89b1166e89b2dda","src/pk11/sym_key.rs":"6dd1bae6e4c97665d0535fd0165736a2174edcb316f068ac3a8c73e5d4c20509","src/pk11/types.rs":"e42789b44e6c783a24d09c4ca955d70a305b20a35320c9c14c54c796e165b93e","src/secport.rs":"b4fbb007963a20cfd3170f37b35aa2816a0d7bf78bae9dbc64c83f5b8f15d2cb","src/util.rs":"236c46206bb6cd130c07f9da4fd603e23166c550a1ba675f4d752b056d13c27f"},"package":null}
|
||||
{"files":{"Cargo.toml":"079319f9f7b8f3faf7f423f029f263c42f1c4836a9a9ef78af9733f5e47929bd","README.md":"14dd59e435d179c21c3b4b880bbe3cc6e5999b9f9ac9431f3f9aa3f43902e3fa","src/aes.rs":"820a74d1c1b9b5c818f5e4c4b39afb4346e56b8512a0f280c0bd92b763f50486","src/ec.rs":"3dfb1b2f630e855a37be7b2c03121d069d0b1f0f65e06bcd46493e2a0206be99","src/ecdh.rs":"6a970e6a30dfba4c5f4d113a5b5f3a814ee650a54eba903f8a50b47e180a1ceb","src/error.rs":"de521060e8ec9ad2c125815eec45ef690ad479ab9d41dab5a26294ee6acd9980","src/lib.rs":"7e9e1ebfaf13af124a5226a46e01e70743f3419eb7acc38ffaf202605bb33b89","src/pk11/context.rs":"ab3cdc8949fc1974523f0c6bf376ab933646df499d568a908076ad80b11c7c56","src/pk11/mod.rs":"d78368654f9a8bc12f1403c4a096b63cf9834820ea6ed48418b9afaa0fc2299e","src/pk11/slot.rs":"9f0aa039a55e7b26dc2dd5d2d3451497af71d147513f59e9c89b1166e89b2dda","src/pk11/sym_key.rs":"6dd1bae6e4c97665d0535fd0165736a2174edcb316f068ac3a8c73e5d4c20509","src/pk11/types.rs":"e42789b44e6c783a24d09c4ca955d70a305b20a35320c9c14c54c796e165b93e","src/secport.rs":"b4fbb007963a20cfd3170f37b35aa2816a0d7bf78bae9dbc64c83f5b8f15d2cb","src/util.rs":"236c46206bb6cd130c07f9da4fd603e23166c550a1ba675f4d752b056d13c27f"},"package":null}
|
|
@ -9,7 +9,7 @@ license = "MPL-2.0"
|
|||
crate-type = ["lib"]
|
||||
|
||||
[dependencies]
|
||||
base64 = "0.12.0"
|
||||
base64 = "0.12"
|
||||
error-support = { path = "../../error" }
|
||||
failure = "0.1"
|
||||
failure_derive = "0.1"
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"CHANGELOG.md":"30fa41d2cd20250b4d4defdef99cd18843b38f7991e8eed40801b94b8542ce13","Cargo.lock":"147a02b888dc8555251ba130e61c87afbd3c2af8ccac923b22773904ddb0a85e","Cargo.toml":"79f3565fce8df325c6096c50594228bfd7bfb9cef2dba1fc25ea48ba2fe9e5a0","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"d7203c0e18700de8e1408c3f4bb96075df271fea9ab678f6ad2e09855aefa7ed","examples/bench.rs":"1597a52529f75d6c5ad0b86759a775b1d723dfa810e2016317283b13594219da","examples/bench_acquire.rs":"3956f01158abaa1e15f1c633e6f96caf1257bca7bb4311c9568fdbc8347906f9","examples/bench_vs_lazy_static.rs":"d527294a2e73b53ac5faed8b316dfd1ae2a06adb31384134af21f10ce76333a5","examples/lazy_static.rs":"90541b093ed1d1cbb73f4097ff02cf80657e28264d281d6a31d96a708fdfea90","examples/reentrant_init_deadlocks.rs":"ff84929de27a848e5b155549caa96db5db5f030afca975f8ba3f3da640083001","examples/regex.rs":"4a2e0fb093c7f5bbe0fff8689fc0c670c5334344a1bfda376f5faa98a05d459f","examples/test_synchronization.rs":"1fe6828a2bfe5b5fbcaf287fcf02d746e757d89db81a2e32f24b849272dd1e90","src/imp_pl.rs":"2ec567e4a0f3b5adc7399822ced03243aa065b61ac50d81c485e952ddf676c7e","src/imp_std.rs":"bfd6e77afa84defffcc7a8b6bc0226353f95c12fd373866884cd8421c9326446","src/lib.rs":"20f7f610a6ad51dbe59b3dff328c6539520d00cec6d60bcfd3b3c0deb5efd06c","tests/test.rs":"d41dcc82bc03a52a1d1985b155d08de991919ae190a424012981a1e6f395eb20"},"package":"b1c601810575c99596d4afc46f78a678c80105117c379eb3650cf99b8a21ce5b"}
|
||||
{"files":{"CHANGELOG.md":"0fe534ed96d710708290f0bb4506845529023ccc7cc5c25ea927df5516cd54e0","Cargo.lock":"cac2b1e8c08537f890d727b94fd334253d0a220a2d7fad866cf87641e6f2eb4e","Cargo.toml":"b9934c39f6c937a32fc6b596d1a67cf417e952487a7a0aaa8733c1acfa2527ce","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"d7203c0e18700de8e1408c3f4bb96075df271fea9ab678f6ad2e09855aefa7ed","examples/bench.rs":"1597a52529f75d6c5ad0b86759a775b1d723dfa810e2016317283b13594219da","examples/bench_acquire.rs":"9f4912ca262194cb55e893c33739c85c2f4868d07905b9dd3238552b6ce8a6e4","examples/bench_vs_lazy_static.rs":"d527294a2e73b53ac5faed8b316dfd1ae2a06adb31384134af21f10ce76333a5","examples/lazy_static.rs":"90541b093ed1d1cbb73f4097ff02cf80657e28264d281d6a31d96a708fdfea90","examples/reentrant_init_deadlocks.rs":"ff84929de27a848e5b155549caa96db5db5f030afca975f8ba3f3da640083001","examples/regex.rs":"4a2e0fb093c7f5bbe0fff8689fc0c670c5334344a1bfda376f5faa98a05d459f","examples/test_synchronization.rs":"88abd5c16275bb2f2d77eaecf369d97681404a77b8edd0021f24bfd377c46be3","src/imp_pl.rs":"8b994723144ff733a4872a3e1e68debd5ee2ce27b8e613e60d2744cae748052a","src/imp_std.rs":"b9d39dd1bbe8ebb779cd625dfd64b8f78a9dd6f0d531768ae61888f8416363b3","src/lib.rs":"3e128b775a7035322007590491914b6b23c4fb8f993354918d30a5f653557961","tests/test.rs":"800e951dc22e7d30cf05195904f49c162cc5e82db53afbb541b5be5ee19203a0"},"package":"0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d"}
|
|
@ -1,5 +1,11 @@
|
|||
# Changelog
|
||||
|
||||
## 1.4.0
|
||||
|
||||
- upgrade `parking_lot` to `0.10` (note that this bumps MSRV with `parking_lot` feature enabled to `1.36.0`).
|
||||
- add `OnceCell::take`.
|
||||
- upgrade crossbeam utils (private dependency) to `0.7`.
|
||||
|
||||
## 1.3.1
|
||||
|
||||
- remove unnecessary `F: fmt::Debug` bound from `impl fmt::Debug for Lazy<T, F>`.
|
||||
|
|
|
@ -2,12 +2,17 @@
|
|||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.6"
|
||||
version = "0.7.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.2.1"
|
||||
|
@ -28,9 +33,10 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.6.6"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
@ -42,58 +48,51 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.66"
|
||||
version = "0.2.70"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.3.3"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "maybe-uninit"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.3.0"
|
||||
version = "2.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.3.1"
|
||||
version = "1.4.0"
|
||||
dependencies = [
|
||||
"crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.9.0"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"lock_api 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lock_api 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot_core 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.6.2"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
|
@ -104,53 +103,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.3.3"
|
||||
version = "1.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex-syntax 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"aho-corasick 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex-syntax 0.6.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.13"
|
||||
version = "0.6.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver-parser"
|
||||
version = "0.7.0"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "0.6.13"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
|
@ -180,26 +155,23 @@ version = "0.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[metadata]
|
||||
"checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d"
|
||||
"checksum aho-corasick 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)" = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada"
|
||||
"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
|
||||
"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
|
||||
"checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6"
|
||||
"checksum crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
|
||||
"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
"checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558"
|
||||
"checksum lock_api 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "79b2de95ecb4691949fea4716ca53cdbcfccb2c612e19644a8bad05edcf9f47b"
|
||||
"checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
|
||||
"checksum memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3197e20c7edb283f87c071ddfc7a2cca8f8e0b888c242959846a6fce03c72223"
|
||||
"checksum parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252"
|
||||
"checksum parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b"
|
||||
"checksum libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)" = "3baa92041a6fec78c687fa0cc2b3fae8884f743d672cf551bed1d6dac6988d0f"
|
||||
"checksum lock_api 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75"
|
||||
"checksum memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
|
||||
"checksum parking_lot 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e"
|
||||
"checksum parking_lot_core 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3"
|
||||
"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
|
||||
"checksum regex 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b5508c1941e4e7cb19965abef075d35a9a8b5cdf0846f30b4050e9b55dc55e87"
|
||||
"checksum regex-syntax 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e734e891f5b408a29efbf8309e656876276f49ab6a6ac208600b4419bd893d90"
|
||||
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||
"checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d"
|
||||
"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
"checksum smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6"
|
||||
"checksum regex 1.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a6020f034922e3194c711b82a627453881bc4682166cabb07134a10c26ba7692"
|
||||
"checksum regex-syntax 0.6.17 (registry+https://github.com/rust-lang/crates.io-index)" = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae"
|
||||
"checksum scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
"checksum smallvec 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4"
|
||||
"checksum thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
|
||||
"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
|
||||
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
[package]
|
||||
edition = "2018"
|
||||
name = "once_cell"
|
||||
version = "1.3.1"
|
||||
version = "1.4.0"
|
||||
authors = ["Aleksey Kladov <aleksey.kladov@gmail.com>"]
|
||||
exclude = ["*.png", "*.svg", "/Cargo.lock.min", "/.travis.yml", "/run-miri-tests.sh", "rustfmt.toml"]
|
||||
description = "Single assignment cells and lazy values."
|
||||
|
@ -52,11 +52,11 @@ required-features = ["std"]
|
|||
name = "test_synchronization"
|
||||
required-features = ["std"]
|
||||
[dependencies.parking_lot]
|
||||
version = "0.9.0"
|
||||
version = "0.10.0"
|
||||
optional = true
|
||||
default_features = false
|
||||
[dev-dependencies.crossbeam-utils]
|
||||
version = "0.6.0"
|
||||
version = "0.7.2"
|
||||
|
||||
[dev-dependencies.lazy_static]
|
||||
version = "1.0.0"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/// Benchmark the overhead that the synchronization of `OnceCell::get` causes.
|
||||
/// We do some other operations that write to memory to get an imprecise but somewhat realistic
|
||||
/// measurement.
|
||||
//! Benchmark the overhead that the synchronization of `OnceCell::get` causes.
|
||||
//! We do some other operations that write to memory to get an imprecise but somewhat realistic
|
||||
//! measurement.
|
||||
|
||||
use once_cell::sync::OnceCell;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
@ -29,7 +29,7 @@ fn thread_main(i: usize) {
|
|||
let mut data = [i; 128];
|
||||
let mut accum = 0usize;
|
||||
for _ in 0..N_ROUNDS {
|
||||
let _value = CELL.get_or_init(|| i+1);
|
||||
let _value = CELL.get_or_init(|| i + 1);
|
||||
let k = OTHER.fetch_add(data[accum & 0x7F] as usize, Ordering::Relaxed);
|
||||
for j in data.iter_mut() {
|
||||
*j = (*j).wrapping_add(accum);
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
/// Test if the OnceCell properly synchronizes.
|
||||
/// Needs to be run in release mode.
|
||||
///
|
||||
/// We create a `Vec` with `N_ROUNDS` of `OnceCell`s. All threads will walk the `Vec`, and race to
|
||||
/// be the first one to initialize a cell.
|
||||
/// Every thread adds the results of the cells it sees to an accumulator, which is compared at the
|
||||
/// end.
|
||||
/// All threads should end up with the same result.
|
||||
//! Test if the OnceCell properly synchronizes.
|
||||
//! Needs to be run in release mode.
|
||||
//!
|
||||
//! We create a `Vec` with `N_ROUNDS` of `OnceCell`s. All threads will walk the `Vec`, and race to
|
||||
//! be the first one to initialize a cell.
|
||||
//! Every thread adds the results of the cells it sees to an accumulator, which is compared at the
|
||||
//! end.
|
||||
//! All threads should end up with the same result.
|
||||
|
||||
use once_cell::sync::OnceCell;
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
use std::{
|
||||
cell::UnsafeCell,
|
||||
mem::{self, MaybeUninit},
|
||||
panic::{RefUnwindSafe, UnwindSafe},
|
||||
ptr,
|
||||
sync::atomic::{AtomicBool, Ordering},
|
||||
};
|
||||
|
||||
|
@ -9,7 +11,7 @@ use parking_lot::{lock_api::RawMutex as _RawMutex, RawMutex};
|
|||
pub(crate) struct OnceCell<T> {
|
||||
mutex: Mutex,
|
||||
is_initialized: AtomicBool,
|
||||
pub(crate) value: UnsafeCell<Option<T>>,
|
||||
value: UnsafeCell<MaybeUninit<T>>,
|
||||
}
|
||||
|
||||
// Why do we need `T: Send`?
|
||||
|
@ -28,7 +30,7 @@ impl<T> OnceCell<T> {
|
|||
OnceCell {
|
||||
mutex: Mutex::new(),
|
||||
is_initialized: AtomicBool::new(false),
|
||||
value: UnsafeCell::new(None),
|
||||
value: UnsafeCell::new(MaybeUninit::uninit()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -56,13 +58,76 @@ impl<T> OnceCell<T> {
|
|||
// - finally, if it returns Ok, we store the value and store the flag with
|
||||
// `Release`, which synchronizes with `Acquire`s.
|
||||
let value = f()?;
|
||||
let slot: &mut Option<T> = unsafe { &mut *self.value.get() };
|
||||
debug_assert!(slot.is_none());
|
||||
*slot = Some(value);
|
||||
// Safe b/c we have a unique access and no panic may happen
|
||||
// until the cell is marked as initialized.
|
||||
unsafe { self.as_mut_ptr().write(value) };
|
||||
self.is_initialized.store(true, Ordering::Release);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the reference to the underlying value, without checking if the cell
|
||||
/// is initialized.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Caller must ensure that the cell is in initialized state, and that
|
||||
/// the contents are acquired by (synchronized to) this thread.
|
||||
pub(crate) unsafe fn get_unchecked(&self) -> &T {
|
||||
debug_assert!(self.is_initialized());
|
||||
&*self.as_ptr()
|
||||
}
|
||||
|
||||
/// Gets the mutable reference to the underlying value.
|
||||
/// Returns `None` if the cell is empty.
|
||||
pub(crate) fn get_mut(&mut self) -> Option<&mut T> {
|
||||
if self.is_initialized() {
|
||||
// Safe b/c we have a unique access and value is initialized.
|
||||
Some(unsafe { &mut *self.as_mut_ptr() })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Consumes this `OnceCell`, returning the wrapped value.
|
||||
/// Returns `None` if the cell was empty.
|
||||
pub(crate) fn into_inner(self) -> Option<T> {
|
||||
if !self.is_initialized() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Safe b/c we have a unique access and value is initialized.
|
||||
let value: T = unsafe { ptr::read(self.as_ptr()) };
|
||||
|
||||
// It's OK to `mem::forget` without dropping, because both `self.mutex`
|
||||
// and `self.is_initialized` are not heap-allocated.
|
||||
mem::forget(self);
|
||||
|
||||
Some(value)
|
||||
}
|
||||
|
||||
fn as_ptr(&self) -> *const T {
|
||||
unsafe {
|
||||
let slot: &MaybeUninit<T> = &*self.value.get();
|
||||
slot.as_ptr()
|
||||
}
|
||||
}
|
||||
|
||||
fn as_mut_ptr(&self) -> *mut T {
|
||||
unsafe {
|
||||
let slot: &mut MaybeUninit<T> = &mut *self.value.get();
|
||||
slot.as_mut_ptr()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for OnceCell<T> {
|
||||
fn drop(&mut self) {
|
||||
if self.is_initialized() {
|
||||
// Safe b/c we have a unique access and value is initialized.
|
||||
unsafe { ptr::drop_in_place(self.as_mut_ptr()) };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper around parking_lot's `RawMutex` which has `const fn` new.
|
||||
|
@ -92,9 +157,8 @@ impl Drop for MutexGuard<'_> {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
fn test_size() {
|
||||
use std::mem::size_of;
|
||||
|
||||
assert_eq!(size_of::<OnceCell<u32>>(), 3 * size_of::<u32>());
|
||||
assert_eq!(size_of::<OnceCell<bool>>(), 2 * size_of::<bool>() + size_of::<u8>());
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
use std::{
|
||||
cell::{Cell, UnsafeCell},
|
||||
hint::unreachable_unchecked,
|
||||
marker::PhantomData,
|
||||
panic::{RefUnwindSafe, UnwindSafe},
|
||||
sync::atomic::{AtomicBool, AtomicUsize, Ordering},
|
||||
|
@ -21,7 +22,7 @@ pub(crate) struct OnceCell<T> {
|
|||
// that far. It was stabilized in 1.36.0, so, if you are reading this and
|
||||
// it's higher than 1.46.0 outside, please send a PR! ;) (and do the same
|
||||
// for `Lazy`, while we are at it).
|
||||
pub(crate) value: UnsafeCell<Option<T>>,
|
||||
value: UnsafeCell<Option<T>>,
|
||||
}
|
||||
|
||||
// Why do we need `T: Send`?
|
||||
|
@ -90,22 +91,59 @@ impl<T> OnceCell<T> {
|
|||
{
|
||||
let mut f = Some(f);
|
||||
let mut res: Result<(), E> = Ok(());
|
||||
let slot = &self.value;
|
||||
let slot: *mut Option<T> = self.value.get();
|
||||
initialize_inner(&self.state_and_queue, &mut || {
|
||||
let f = f.take().unwrap();
|
||||
match f() {
|
||||
Ok(value) => {
|
||||
unsafe { *slot.get() = Some(value) };
|
||||
unsafe { *slot = Some(value) };
|
||||
true
|
||||
}
|
||||
Err(e) => {
|
||||
res = Err(e);
|
||||
Err(err) => {
|
||||
res = Err(err);
|
||||
false
|
||||
}
|
||||
}
|
||||
});
|
||||
res
|
||||
}
|
||||
|
||||
/// Get the reference to the underlying value, without checking if the cell
|
||||
/// is initialized.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Caller must ensure that the cell is in initialized state, and that
|
||||
/// the contents are acquired by (synchronized to) this thread.
|
||||
pub(crate) unsafe fn get_unchecked(&self) -> &T {
|
||||
debug_assert!(self.is_initialized());
|
||||
let slot: &Option<T> = &*self.value.get();
|
||||
match slot {
|
||||
Some(value) => value,
|
||||
// This unsafe does improve performance, see `examples/bench`.
|
||||
None => {
|
||||
debug_assert!(false);
|
||||
unreachable_unchecked()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the mutable reference to the underlying value.
|
||||
/// Returns `None` if the cell is empty.
|
||||
pub(crate) fn get_mut(&mut self) -> Option<&mut T> {
|
||||
// Safe b/c we have a unique access.
|
||||
unsafe { &mut *self.value.get() }.as_mut()
|
||||
}
|
||||
|
||||
/// Consumes this `OnceCell`, returning the wrapped value.
|
||||
/// Returns `None` if the cell was empty.
|
||||
#[inline]
|
||||
pub(crate) fn into_inner(self) -> Option<T> {
|
||||
// Because `into_inner` takes `self` by value, the compiler statically
|
||||
// verifies that it is not currently borrowed.
|
||||
// So, it is safe to move out `Option<T>`.
|
||||
self.value.into_inner()
|
||||
}
|
||||
}
|
||||
|
||||
// Corresponds to `std::sync::Once::call_inner`
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
/*!
|
||||
# Overview
|
||||
|
||||
`once_cell` provides two new cell-like types, `unsync::OnceCell` and `sync::OnceCell`. `OnceCell`
|
||||
might store arbitrary non-`Copy` types, can be assigned to at most once and provide direct access
|
||||
to the stored contents. In a nutshell, API looks *roughly* like this:
|
||||
`once_cell` provides two new cell-like types, [`unsync::OnceCell`] and [`sync::OnceCell`]. A `OnceCell`
|
||||
might store arbitrary non-`Copy` types, can be assigned to at most once and provides direct access
|
||||
to the stored contents. In a nutshell, the API looks *roughly* like this:
|
||||
|
||||
```rust,ignore
|
||||
impl<T> OnceCell<T> {
|
||||
|
@ -13,12 +13,16 @@ impl<T> OnceCell<T> {
|
|||
}
|
||||
```
|
||||
|
||||
Note that, like with `RefCell` and `Mutex`, the `set` method requires only a shared reference.
|
||||
Because of the single assignment restriction `get` can return an `&T` instead of `Ref<T>`
|
||||
Note that, like with [`RefCell`] and [`Mutex`], the `set` method requires only a shared reference.
|
||||
Because of the single assignment restriction `get` can return a `&T` instead of `Ref<T>`
|
||||
or `MutexGuard<T>`.
|
||||
|
||||
The `sync` flavor is thread-safe (that is, implements [`Sync`]) trait, while the `unsync` one is not.
|
||||
The `sync` flavor is thread-safe (that is, implements the [`Sync`] trait), while the `unsync` one is not.
|
||||
|
||||
[`unsync::OnceCell`]: unsync/struct.OnceCell.html
|
||||
[`sync::OnceCell`]: sync/struct.ONceCell.html
|
||||
[`RefCell`]: https://doc.rust-lang.org/std/cell/struct.RefCell.html
|
||||
[`Mutex`]: https://doc.rust-lang.org/std/sync/struct.Mutex.html
|
||||
[`Sync`]: https://doc.rust-lang.org/std/marker/trait.Sync.html
|
||||
|
||||
# Patterns
|
||||
|
@ -58,7 +62,7 @@ fn main() {
|
|||
|
||||
## Lazy initialized global data
|
||||
|
||||
This is essentially `lazy_static!` macro, but without a macro.
|
||||
This is essentially the `lazy_static!` macro, but without a macro.
|
||||
|
||||
```rust
|
||||
use std::{sync::Mutex, collections::HashMap};
|
||||
|
@ -76,7 +80,7 @@ fn global_data() -> &'static Mutex<HashMap<i32, String>> {
|
|||
}
|
||||
```
|
||||
|
||||
There are also `sync::Lazy` and `unsync::Lazy` convenience types to streamline this pattern:
|
||||
There are also the [`sync::Lazy`] and [`unsync::Lazy`] convenience types to streamline this pattern:
|
||||
|
||||
```rust
|
||||
use std::{sync::Mutex, collections::HashMap};
|
||||
|
@ -94,6 +98,9 @@ fn main() {
|
|||
}
|
||||
```
|
||||
|
||||
[`sync::Lazy`]: sync/struct.Lazy.html
|
||||
[`unsync::Lazy`]: unsync/struct.Lazy.html
|
||||
|
||||
## General purpose lazy evaluation
|
||||
|
||||
Unlike `lazy_static!`, `Lazy` works with local variables.
|
||||
|
@ -148,7 +155,7 @@ macro_rules! regex {
|
|||
}
|
||||
```
|
||||
|
||||
This macro can be useful to avoid "compile regex on every loop iteration" problem.
|
||||
This macro can be useful to avoid the "compile regex on every loop iteration" problem.
|
||||
|
||||
# Comparison with std
|
||||
|
||||
|
@ -170,24 +177,27 @@ equivalents with `RefCell` and `Mutex`.
|
|||
|
||||
# Minimum Supported `rustc` Version
|
||||
|
||||
This crate's minimum supported `rustc` version is `1.31.1`.
|
||||
This crate's minimum supported `rustc` version is `1.31.1` (or `1.36.0` with the
|
||||
`parking_lot` feature enabled).
|
||||
|
||||
If only `std` feature is enabled, MSRV will be updated conservatively.
|
||||
If only the `std` feature is enabled, MSRV will be updated conservatively.
|
||||
When using other features, like `parking_lot`, MSRV might be updated more frequently, up to the latest stable.
|
||||
In both cases, increasing MSRV is *not* considered a semver-breaking change.
|
||||
|
||||
# Implementation details
|
||||
|
||||
Implementation is based on [`lazy_static`](https://github.com/rust-lang-nursery/lazy-static.rs/)
|
||||
and [`lazy_cell`](https://github.com/indiv0/lazycell/) crates and `std::sync::Once`. In some sense,
|
||||
The implementation is based on the [`lazy_static`](https://github.com/rust-lang-nursery/lazy-static.rs/)
|
||||
and [`lazy_cell`](https://github.com/indiv0/lazycell/) crates and [`std::sync::Once`]. In some sense,
|
||||
`once_cell` just streamlines and unifies those APIs.
|
||||
|
||||
To implement a sync flavor of `OnceCell`, this crates uses either a custom re-implementation of
|
||||
`std::sync::Once` or `parking_lot::Mutex`. This is controlled by the `parking_lot` feature, which
|
||||
is enabled by default. Performance is the same for both cases, but `parking_lot` based `OnceCell<T>`
|
||||
is enabled by default. Performance is the same for both cases, but the `parking_lot` based `OnceCell<T>`
|
||||
is smaller by up to 16 bytes.
|
||||
|
||||
This crate uses unsafe.
|
||||
This crate uses `unsafe`.
|
||||
|
||||
[`std::sync::Once`]: https://doc.rust-lang.org/std/sync/struct.Once.html
|
||||
|
||||
# F.A.Q.
|
||||
|
||||
|
@ -202,7 +212,7 @@ Unlike `once_cell`, `lazy_static` supports spinlock-based implementation of bloc
|
|||
`lazy_static` has received significantly more real world testing, but `once_cell` is also a widely
|
||||
used crate.
|
||||
|
||||
**Should I use sync or unsync flavor?**
|
||||
**Should I use the sync or unsync flavor?**
|
||||
|
||||
Because Rust compiler checks thread safety for you, it's impossible to accidentally use `unsync` where
|
||||
`sync` is required. So, use `unsync` in single-threaded code and `sync` in multi-threaded. It's easy
|
||||
|
@ -236,18 +246,20 @@ mod imp;
|
|||
pub mod unsync {
|
||||
use core::{
|
||||
cell::{Cell, UnsafeCell},
|
||||
fmt,
|
||||
fmt, mem,
|
||||
ops::{Deref, DerefMut},
|
||||
};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use std::panic::{RefUnwindSafe, UnwindSafe};
|
||||
|
||||
/// A cell which can be written to only once. Not thread safe.
|
||||
/// A cell which can be written to only once. It is not thread safe.
|
||||
///
|
||||
/// Unlike `:td::cell::RefCell`, a `OnceCell` provides simple `&`
|
||||
/// Unlike [`std::cell::RefCell`], a `OnceCell` provides simple `&`
|
||||
/// references to the contents.
|
||||
///
|
||||
/// [`std::cell::RefCell`]: https://doc.rust-lang.org/std/cell/struct.RefCell.html
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use once_cell::unsync::OnceCell;
|
||||
|
@ -323,7 +335,7 @@ pub mod unsync {
|
|||
OnceCell { inner: UnsafeCell::new(None) }
|
||||
}
|
||||
|
||||
/// Gets the reference to the underlying value.
|
||||
/// Gets a reference to the underlying value.
|
||||
///
|
||||
/// Returns `None` if the cell is empty.
|
||||
pub fn get(&self) -> Option<&T> {
|
||||
|
@ -331,7 +343,7 @@ pub mod unsync {
|
|||
unsafe { &*self.inner.get() }.as_ref()
|
||||
}
|
||||
|
||||
/// Gets the mutable reference to the underlying value.
|
||||
/// Gets a mutable reference to the underlying value.
|
||||
///
|
||||
/// Returns `None` if the cell is empty.
|
||||
pub fn get_mut(&mut self) -> Option<&mut T> {
|
||||
|
@ -443,6 +455,27 @@ pub mod unsync {
|
|||
Ok(self.get().unwrap())
|
||||
}
|
||||
|
||||
/// Takes the value out of this `OnceCell`, moving it back to an uninitialized state.
|
||||
///
|
||||
/// Has no effect and returns `None` if the `OnceCell` hasn't been initialized.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use once_cell::unsync::OnceCell;
|
||||
///
|
||||
/// let mut cell: OnceCell<String> = OnceCell::new();
|
||||
/// assert_eq!(cell.take(), None);
|
||||
///
|
||||
/// let mut cell = OnceCell::new();
|
||||
/// cell.set("hello".to_string()).unwrap();
|
||||
/// assert_eq!(cell.take(), Some("hello".to_string()));
|
||||
/// assert_eq!(cell.get(), None);
|
||||
/// ```
|
||||
pub fn take(&mut self) -> Option<T> {
|
||||
mem::replace(self, Self::default()).into_inner()
|
||||
}
|
||||
|
||||
/// Consumes the `OnceCell`, returning the wrapped value.
|
||||
///
|
||||
/// Returns `None` if the cell was empty.
|
||||
|
@ -569,8 +602,7 @@ pub mod unsync {
|
|||
pub mod sync {
|
||||
use std::{
|
||||
cell::Cell,
|
||||
fmt,
|
||||
hint::unreachable_unchecked,
|
||||
fmt, mem,
|
||||
ops::{Deref, DerefMut},
|
||||
panic::RefUnwindSafe,
|
||||
};
|
||||
|
@ -663,7 +695,7 @@ pub mod sync {
|
|||
/// method never blocks.
|
||||
pub fn get(&self) -> Option<&T> {
|
||||
if self.0.is_initialized() {
|
||||
// Safe b/c checked is_initialize
|
||||
// Safe b/c value is initialized.
|
||||
Some(unsafe { self.get_unchecked() })
|
||||
} else {
|
||||
None
|
||||
|
@ -674,28 +706,18 @@ pub mod sync {
|
|||
///
|
||||
/// Returns `None` if the cell is empty.
|
||||
pub fn get_mut(&mut self) -> Option<&mut T> {
|
||||
// Safe b/c we have a unique access.
|
||||
unsafe { &mut *self.0.value.get() }.as_mut()
|
||||
self.0.get_mut()
|
||||
}
|
||||
|
||||
/// Get the reference to the underlying value, without checking if the
|
||||
/// cell is initialized.
|
||||
///
|
||||
/// Safety:
|
||||
/// # Safety
|
||||
///
|
||||
/// Caller must ensure that the cell is in initialized state, and that
|
||||
/// the contents are acquired by (synchronized to) this thread.
|
||||
pub unsafe fn get_unchecked(&self) -> &T {
|
||||
debug_assert!(self.0.is_initialized());
|
||||
let slot: &Option<T> = &*self.0.value.get();
|
||||
match slot {
|
||||
Some(value) => value,
|
||||
// This unsafe does improve performance, see `examples/bench`.
|
||||
None => {
|
||||
debug_assert!(false);
|
||||
unreachable_unchecked()
|
||||
}
|
||||
}
|
||||
self.0.get_unchecked()
|
||||
}
|
||||
|
||||
/// Sets the contents of this cell to `value`.
|
||||
|
@ -704,6 +726,7 @@ pub mod sync {
|
|||
/// full.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use once_cell::sync::OnceCell;
|
||||
///
|
||||
|
@ -802,11 +825,32 @@ pub mod sync {
|
|||
}
|
||||
self.0.initialize(f)?;
|
||||
|
||||
// Safe b/c called initialize
|
||||
// Safe b/c value is initialized.
|
||||
debug_assert!(self.0.is_initialized());
|
||||
Ok(unsafe { self.get_unchecked() })
|
||||
}
|
||||
|
||||
/// Takes the value out of this `OnceCell`, moving it back to an uninitialized state.
|
||||
///
|
||||
/// Has no effect and returns `None` if the `OnceCell` hasn't been initialized.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use once_cell::sync::OnceCell;
|
||||
///
|
||||
/// let mut cell: OnceCell<String> = OnceCell::new();
|
||||
/// assert_eq!(cell.take(), None);
|
||||
///
|
||||
/// let mut cell = OnceCell::new();
|
||||
/// cell.set("hello".to_string()).unwrap();
|
||||
/// assert_eq!(cell.take(), Some("hello".to_string()));
|
||||
/// assert_eq!(cell.get(), None);
|
||||
/// ```
|
||||
pub fn take(&mut self) -> Option<T> {
|
||||
mem::replace(self, Self::default()).into_inner()
|
||||
}
|
||||
|
||||
/// Consumes the `OnceCell`, returning the wrapped value. Returns
|
||||
/// `None` if the cell was empty.
|
||||
///
|
||||
|
@ -823,17 +867,16 @@ pub mod sync {
|
|||
/// assert_eq!(cell.into_inner(), Some("hello".to_string()));
|
||||
/// ```
|
||||
pub fn into_inner(self) -> Option<T> {
|
||||
// Because `into_inner` takes `self` by value, the compiler statically verifies
|
||||
// that it is not currently borrowed. So it is safe to move out `Option<T>`.
|
||||
self.0.value.into_inner()
|
||||
self.0.into_inner()
|
||||
}
|
||||
}
|
||||
|
||||
/// A value which is initialized on the first access.
|
||||
///
|
||||
/// This type is thread-safe and can be used in statics:
|
||||
/// This type is thread-safe and can be used in statics.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use std::collections::HashMap;
|
||||
///
|
||||
|
@ -893,7 +936,7 @@ pub mod sync {
|
|||
|
||||
impl<T, F: FnOnce() -> T> Lazy<T, F> {
|
||||
/// Forces the evaluation of this lazy value and
|
||||
/// returns a reference to result. This is equivalent
|
||||
/// returns a reference to the result. This is equivalent
|
||||
/// to the `Deref` impl, but is explicit.
|
||||
///
|
||||
/// # Example
|
||||
|
|
|
@ -448,7 +448,6 @@ mod sync {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore)] // leaks memory
|
||||
fn static_lazy() {
|
||||
static XS: Lazy<Vec<i32>> = Lazy::new(|| {
|
||||
let mut xs = Vec::new();
|
||||
|
@ -467,7 +466,6 @@ mod sync {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore)] // leaks memory
|
||||
fn static_lazy_via_fn() {
|
||||
fn xs() -> &'static Vec<i32> {
|
||||
static XS: OnceCell<Vec<i32>> = OnceCell::new();
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"8646f7268b0833f97755a8f8b8853af990a49f9194e7112a58e017d32017c7b6","README.md":"0c208185ac719f9a2c1cdf62c1e6cdf65ce3d65407f0e99feef4933233843a5b","src/aead.rs":"cf7082c25ee981f5bbe304c5127c908f66753e31f44d9c96a62264c156f9db95","src/aead/aes_cbc.rs":"80461cddfa6e99f982d855af599393136dd11eb9b50fb11ac7c75427d9f24c14","src/aead/aes_gcm.rs":"7aa5651e4246532bb1ffcbdece0fb99e9e08094700ba0048ba239c2543db4376","src/agreement.rs":"6c65f5cdc9636fe425531b4b48b8e0625d0dd480b4f8b78a5504f99b033ccefd","src/constant_time.rs":"2ca0f8274227c88566f015fe0d143ba57d1413d01f9eb2a03535ea8105bf5b70","src/digest.rs":"526ac46c43b410164fb4330e4ee20705b240fc72f47e24d725ef6f0d132bd371","src/ece_crypto.rs":"25df8cbb1bb483e454979aa43d2f6900614b4ce07d92be29adbd824d513601b2","src/error.rs":"517bd8e26295e61a065e9f2ca1e6bed817c9e5fd17d010e4c93cf21e581ba380","src/hawk_crypto.rs":"a64fcebb8228c291e5e5718b1e6519c2e959a257c46cdfa7dc40b8d68968a959","src/hkdf.rs":"d535bd716873fecfe243fbe10a21c6da99eacc59ae5777390f229158c4baee2f","src/hmac.rs":"e67a551e2266f310e05c4a21924479e471bc00217605ce9f4ed3cdcc0034a5a4","src/lib.rs":"1ffe5815521cf4199360cd93a2ecc3b6070296fe4ebe991b28611aafca9604e0","src/rand.rs":"7daa4d3c06b469f50e8c6ae7e2f2f651250440ea4bede5e5a8dfe5a4c5a079cb"},"package":null}
|
||||
{"files":{"Cargo.toml":"9a98941d1f7cb1922f19ef845fdcbf1bd3a1eac56df79bf03848586b4400def8","README.md":"0c208185ac719f9a2c1cdf62c1e6cdf65ce3d65407f0e99feef4933233843a5b","src/aead.rs":"cf7082c25ee981f5bbe304c5127c908f66753e31f44d9c96a62264c156f9db95","src/aead/aes_cbc.rs":"80461cddfa6e99f982d855af599393136dd11eb9b50fb11ac7c75427d9f24c14","src/aead/aes_gcm.rs":"7aa5651e4246532bb1ffcbdece0fb99e9e08094700ba0048ba239c2543db4376","src/agreement.rs":"6c65f5cdc9636fe425531b4b48b8e0625d0dd480b4f8b78a5504f99b033ccefd","src/constant_time.rs":"2ca0f8274227c88566f015fe0d143ba57d1413d01f9eb2a03535ea8105bf5b70","src/digest.rs":"526ac46c43b410164fb4330e4ee20705b240fc72f47e24d725ef6f0d132bd371","src/ece_crypto.rs":"25df8cbb1bb483e454979aa43d2f6900614b4ce07d92be29adbd824d513601b2","src/error.rs":"517bd8e26295e61a065e9f2ca1e6bed817c9e5fd17d010e4c93cf21e581ba380","src/hawk_crypto.rs":"a64fcebb8228c291e5e5718b1e6519c2e959a257c46cdfa7dc40b8d68968a959","src/hkdf.rs":"d535bd716873fecfe243fbe10a21c6da99eacc59ae5777390f229158c4baee2f","src/hmac.rs":"e67a551e2266f310e05c4a21924479e471bc00217605ce9f4ed3cdcc0034a5a4","src/lib.rs":"1ffe5815521cf4199360cd93a2ecc3b6070296fe4ebe991b28611aafca9604e0","src/rand.rs":"7daa4d3c06b469f50e8c6ae7e2f2f651250440ea4bede5e5a8dfe5a4c5a079cb"},"package":null}
|
|
@ -9,14 +9,14 @@ license = "MPL-2.0"
|
|||
crate-type = ["lib"]
|
||||
|
||||
[dependencies]
|
||||
base64 = "0.12.0"
|
||||
base64 = "0.12"
|
||||
failure = "0.1"
|
||||
failure_derive = "0.1"
|
||||
error-support = { path = "../error" }
|
||||
nss = { path = "nss" }
|
||||
libsqlite3-sys = { version = "0.18.0", features = ["bundled"] }
|
||||
hawk = { version = "3.1", default-features = false, optional = true }
|
||||
ece = { version = "1.1.2", default-features = false, features = ["serializable-keys"], optional = true }
|
||||
ece = { version = "1.1", default-features = false, features = ["serializable-keys"], optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
hex = "0.4"
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"6ac08b70091eff4fc18499837eef7b330aeeda34da64c707a322a2cdaac0ae31","doc/query-plan.md":"fc877e6cbf1b0e089ec99ee4f34673cd9b3fe1a23c8fcfec20cf286cdc0cd0d0","src/conn_ext.rs":"1126009dd562a333d336c6230814b03de970e2eceaef51b3a3ecd23484a3e23b","src/each_chunk.rs":"8aaba842e43b002fbc0fee95d14ce08faa7187b1979c765b2e270cd4802607a5","src/interrupt.rs":"76c829dce08673e06cf1273030a134cd38f713f9b8a9c80982e753a1fe1437a2","src/lib.rs":"cceb1d597dfc01e1141b89351bc875d7b2a680c272642eee53221c3aab9a70e0","src/maybe_cached.rs":"0b18425595055883a98807fbd62ff27a79c18af34e7cb3439f8c3438463ef2dd","src/query_plan.rs":"c0cc296ddf528a949f683317cea2da67ff5caee8042cf20ff00d9f8f54272ad8","src/repeat.rs":"1885f4dd36cc21fabad1ba28ad2ff213ed17707c57564e1c0d7b0349112118bb"},"package":null}
|
||||
{"files":{"Cargo.toml":"882e889584e6ed5383c8d59aa14298bf10718eddaf644994a3489651f8880bfa","doc/query-plan.md":"fc877e6cbf1b0e089ec99ee4f34673cd9b3fe1a23c8fcfec20cf286cdc0cd0d0","src/conn_ext.rs":"1126009dd562a333d336c6230814b03de970e2eceaef51b3a3ecd23484a3e23b","src/each_chunk.rs":"8aaba842e43b002fbc0fee95d14ce08faa7187b1979c765b2e270cd4802607a5","src/interrupt.rs":"76c829dce08673e06cf1273030a134cd38f713f9b8a9c80982e753a1fe1437a2","src/lib.rs":"cceb1d597dfc01e1141b89351bc875d7b2a680c272642eee53221c3aab9a70e0","src/maybe_cached.rs":"0b18425595055883a98807fbd62ff27a79c18af34e7cb3439f8c3438463ef2dd","src/query_plan.rs":"c0cc296ddf528a949f683317cea2da67ff5caee8042cf20ff00d9f8f54272ad8","src/repeat.rs":"1885f4dd36cc21fabad1ba28ad2ff213ed17707c57564e1c0d7b0349112118bb"},"package":null}
|
|
@ -11,7 +11,7 @@ log_query_plans = []
|
|||
|
||||
[dependencies]
|
||||
log = "0.4"
|
||||
lazy_static = "1.4.0"
|
||||
lazy_static = "1.4"
|
||||
interrupt-support = { path = "../interrupt" }
|
||||
ffi-support = "0.4"
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"206fef066f785b22aa1239362d1340b966807af10d43e611b99dad72194b23b3","src/lib.rs":"729e562be4e63ec7db2adc00753a019ae77c11ce82637a893ea18122580c3c98","src/rusqlite_support.rs":"827d314605d8c741efdf238a0780a891c88bc56026a3e6dcfa534772a4852fb3","src/serde_support.rs":"519b5eb59ca7be555d522f2186909db969069dc9586a5fe4047d4ec176b2368a"},"package":null}
|
||||
{"files":{"Cargo.toml":"510cbe95c5ce6d702d691f063358a3ef38c04aba3957959bd2a4263d6aea415f","src/lib.rs":"729e562be4e63ec7db2adc00753a019ae77c11ce82637a893ea18122580c3c98","src/rusqlite_support.rs":"827d314605d8c741efdf238a0780a891c88bc56026a3e6dcfa534772a4852fb3","src/serde_support.rs":"519b5eb59ca7be555d522f2186909db969069dc9586a5fe4047d4ec176b2368a"},"package":null}
|
|
@ -9,7 +9,7 @@ edition = "2018"
|
|||
rusqlite = { version = "0.23.1", optional = true }
|
||||
serde = { version = "1", optional = true }
|
||||
rand = { version = "0.7", optional = true }
|
||||
base64 = { version = "0.12.0", optional = true }
|
||||
base64 = { version = "0.12", optional = true }
|
||||
|
||||
[features]
|
||||
random = ["rand", "base64"]
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"af79d2ee647cdcba1a18f10c5f6ddcf53264d7d70895b043a94bb8940837910a","README.md":"8b2d669841fa7618a762b7e2cfaabfcabfb87a74c9725013061aedc6ed9e37de","src/bso_record.rs":"62ba03737ed12c01112109badb274814b38a7e76800dccf6eb32034cc6648f1b","src/changeset.rs":"9f29a1e4f953e0e6525bec6f73b88042afc0208bbfb9dcc56e11fe7f4cd55f8a","src/client.rs":"25d7e357021c3893c13ea28aa5bf616ec58832e88b74e123580e4bfd9339025b","src/clients/engine.rs":"140447f3e6746e0d41c02c791107e11635d96eee19ec3f9761b7b21a77918d67","src/clients/mod.rs":"5666240fccb9da8e904f5305e05b01ddbf816bf81916c3a756c3108fea4132fa","src/clients/record.rs":"f555d2cb2d713553280ca07fa55fc8a46bb6e56deb0a430a688c2f6f6a91493e","src/clients/ser.rs":"10aee1110410e3d8f38cbeaf1ea9b00ce8c4eab4d48feb3de206e5fbe38ca46d","src/coll_state.rs":"3093a0eeed8f8b44bc71cf04584d861d4e50fe636a81c560def290bf59aa4f5a","src/collection_keys.rs":"5a167524e1d653c2d2ca794b99b3e1afc236d3036c9445bdaec8f34fe7938ec5","src/error.rs":"4ca6adeaae748836021d9e7b2d642ae28d3696a7ecc49b4f1b79e68b0f3a188d","src/key_bundle.rs":"ceddf59ac19a5757c967afe1795a810faf2ae65a762e085685d4be811328b2f8","src/lib.rs":"b735f28c60dd737cd59aea075c0ec563aba3ee42c6d38b27efc9eba040dc4448","src/migrate_state.rs":"214f4fc1d98c8a6f10e1dcb7dcaf9dc9d866b4908c09c5cc16fed6d3218828d4","src/record_types.rs":"02bb3d352fb808131d298f9b90d9c95b7e9e0138b97c5401f3b9fdacc5562f44","src/request.rs":"ae2a42ea4cbf5f9b6452d05add19dedb4783e1b4f66b7e48e90acc7792a7f165","src/state.rs":"bae333c014065fb02563ce08add2f109d6d18a26a0f24dfd637c636c8dc6dc7c","src/status.rs":"d1efab1e992d0340e602abafd02e049a9d75a51f5b0efe4ebcb3963e156c92d5","src/sync.rs":"4dcb6e015b9670a9af96647e4f25a0fc723cc6f7eef8ceee4d9d7f9af43cc969","src/sync_multiple.rs":"e8e1e338838d1c13c948ead101242c7ba161ebfde853e7319a54488f2591c2e0","src/telemetry.rs":"742ed86dcd18b9d99ba51b5a54121dc6158d5b7ed567249e9d7f5a65c4c40a66","src/token.rs":"5714815e27555b2ec6eb866ac7891f49e8744945b8e813a1dfc387c8a23709cd","src/util.rs":"10bba86298c50ffd4316aee7e4212efa36db76f6d2412b57596b5375ca6713dc"},"package":null}
|
||||
{"files":{"Cargo.toml":"31f7be1c7b4b3808fe49b762e4e6bfbca89b9ce352ec44e35c64c24de9dff6ef","README.md":"8b2d669841fa7618a762b7e2cfaabfcabfb87a74c9725013061aedc6ed9e37de","src/bso_record.rs":"62ba03737ed12c01112109badb274814b38a7e76800dccf6eb32034cc6648f1b","src/changeset.rs":"9f29a1e4f953e0e6525bec6f73b88042afc0208bbfb9dcc56e11fe7f4cd55f8a","src/client.rs":"25d7e357021c3893c13ea28aa5bf616ec58832e88b74e123580e4bfd9339025b","src/clients/engine.rs":"140447f3e6746e0d41c02c791107e11635d96eee19ec3f9761b7b21a77918d67","src/clients/mod.rs":"5666240fccb9da8e904f5305e05b01ddbf816bf81916c3a756c3108fea4132fa","src/clients/record.rs":"f555d2cb2d713553280ca07fa55fc8a46bb6e56deb0a430a688c2f6f6a91493e","src/clients/ser.rs":"10aee1110410e3d8f38cbeaf1ea9b00ce8c4eab4d48feb3de206e5fbe38ca46d","src/coll_state.rs":"3093a0eeed8f8b44bc71cf04584d861d4e50fe636a81c560def290bf59aa4f5a","src/collection_keys.rs":"5a167524e1d653c2d2ca794b99b3e1afc236d3036c9445bdaec8f34fe7938ec5","src/error.rs":"4ca6adeaae748836021d9e7b2d642ae28d3696a7ecc49b4f1b79e68b0f3a188d","src/key_bundle.rs":"ceddf59ac19a5757c967afe1795a810faf2ae65a762e085685d4be811328b2f8","src/lib.rs":"b735f28c60dd737cd59aea075c0ec563aba3ee42c6d38b27efc9eba040dc4448","src/migrate_state.rs":"214f4fc1d98c8a6f10e1dcb7dcaf9dc9d866b4908c09c5cc16fed6d3218828d4","src/record_types.rs":"02bb3d352fb808131d298f9b90d9c95b7e9e0138b97c5401f3b9fdacc5562f44","src/request.rs":"ae2a42ea4cbf5f9b6452d05add19dedb4783e1b4f66b7e48e90acc7792a7f165","src/state.rs":"bae333c014065fb02563ce08add2f109d6d18a26a0f24dfd637c636c8dc6dc7c","src/status.rs":"d1efab1e992d0340e602abafd02e049a9d75a51f5b0efe4ebcb3963e156c92d5","src/sync.rs":"4dcb6e015b9670a9af96647e4f25a0fc723cc6f7eef8ceee4d9d7f9af43cc969","src/sync_multiple.rs":"e8e1e338838d1c13c948ead101242c7ba161ebfde853e7319a54488f2591c2e0","src/telemetry.rs":"742ed86dcd18b9d99ba51b5a54121dc6158d5b7ed567249e9d7f5a65c4c40a66","src/token.rs":"5714815e27555b2ec6eb866ac7891f49e8744945b8e813a1dfc387c8a23709cd","src/util.rs":"10bba86298c50ffd4316aee7e4212efa36db76f6d2412b57596b5375ca6713dc"},"package":null}
|
|
@ -10,7 +10,7 @@ exclude = ["/android", "/ios"]
|
|||
default = []
|
||||
|
||||
[dependencies]
|
||||
base64 = "0.12.0"
|
||||
base64 = "0.12"
|
||||
ffi-support = "0.4"
|
||||
serde = "1"
|
||||
serde_derive = "1"
|
||||
|
@ -18,7 +18,7 @@ serde_json = "1"
|
|||
url = "2.1"
|
||||
log = "0.4"
|
||||
lazy_static = "1.4"
|
||||
base16 = "0.2.1"
|
||||
base16 = "0.2"
|
||||
failure = "0.1"
|
||||
rc_crypto = { path = "../support/rc_crypto", features = ["hawk"] }
|
||||
viaduct = { path = "../viaduct" }
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"9fd0a19d4dd0ebec89c118e09d430fd75b178513d3d63034acb72ab16b199e4a","README.md":"a6856d0f86aaade17cb9fa61c153aca085903d0676fae953022aeab235996cb7","src/backend.rs":"ee89fbb451ffafa82b561cf12cfbef59cd25acdcf5d5953a17099eb8dff4ec24","src/backend/ffi.rs":"f040c9dd47c8a6834a0895ee8b3ff2f252ed984c58332cda278939000780cdd2","src/error.rs":"129aa1f35a1435dc7a1f8a04eb59ef6ebc2e2229614209aa16463f7e2e01c042","src/fetch_msg_types.proto":"ba1aee0b6aaaec42fe20d3431c11dec20984a7c1278c039b90d63677d993db34","src/headers.rs":"2a666eb19b65274dd1e8bdd4c7074619f6a59c62f8df54caeead2f081800e5f2","src/headers/name.rs":"d6b54cb134f3c72e7dd24b7009d34e207a4a6c3fa48b2557287f38f7e8d643b0","src/lib.rs":"d710e4e7ebe2f01e4439f4fe7dd5cd1153c98a0438c6cb266ac32501743d53ab","src/mozilla.appservices.httpconfig.protobuf.rs":"d6e0873c1e4a0e5117ea4f9e37e1621dd5050a4159fae6672364aa60da517d7a","src/settings.rs":"5da12ad4a407d50999ffa499bf2ab27904af03d32f61c2f63b3f86e7b9883b7a"},"package":null}
|
||||
{"files":{"Cargo.toml":"e320766233dfaa665e29e4ba7784a2693a80238df28cde78da9b6bd1e06e917c","README.md":"a6856d0f86aaade17cb9fa61c153aca085903d0676fae953022aeab235996cb7","src/backend.rs":"ee89fbb451ffafa82b561cf12cfbef59cd25acdcf5d5953a17099eb8dff4ec24","src/backend/ffi.rs":"f040c9dd47c8a6834a0895ee8b3ff2f252ed984c58332cda278939000780cdd2","src/error.rs":"129aa1f35a1435dc7a1f8a04eb59ef6ebc2e2229614209aa16463f7e2e01c042","src/fetch_msg_types.proto":"ba1aee0b6aaaec42fe20d3431c11dec20984a7c1278c039b90d63677d993db34","src/headers.rs":"2a666eb19b65274dd1e8bdd4c7074619f6a59c62f8df54caeead2f081800e5f2","src/headers/name.rs":"d6b54cb134f3c72e7dd24b7009d34e207a4a6c3fa48b2557287f38f7e8d643b0","src/lib.rs":"d710e4e7ebe2f01e4439f4fe7dd5cd1153c98a0438c6cb266ac32501743d53ab","src/mozilla.appservices.httpconfig.protobuf.rs":"d6e0873c1e4a0e5117ea4f9e37e1621dd5050a4159fae6672364aa60da517d7a","src/settings.rs":"5da12ad4a407d50999ffa499bf2ab27904af03d32f61c2f63b3f86e7b9883b7a"},"package":null}
|
|
@ -19,7 +19,7 @@ url = "2.1"
|
|||
log = "0.4"
|
||||
serde = "1"
|
||||
serde_json = "1"
|
||||
once_cell = "1.3.1"
|
||||
prost = "0.6.1"
|
||||
prost-derive = "0.6.1"
|
||||
once_cell = "1.4"
|
||||
prost = "0.6"
|
||||
prost-derive = "0.6"
|
||||
ffi-support = "0.4"
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"d5782ce7188018b6e8fa8a99298472d403b6f11d9b7c67b0fd28acbcbdf37109","README.md":"1fd617294339930ee1ad5172377648b268cce0216fc3971facbfe7c6839e9ab1","build.rs":"2b827a62155a3d724cdb4c198270ea467439e537403f82fa873321ac55a69a63","sql/create_schema.sql":"cbb6d432e578c69614199f9e82f8103da5c1f6df5d7af4f77ea1be5869000b26","sql/create_sync_temp_tables.sql":"3e7f113899745e1d2af162a520300fc74b1b32202f69f928353854bc1f7b1b8e","src/api.rs":"c91c8e1fb01e08df8873e51c93842206af6ff839c46c9f5255be17d49a3fdef2","src/db.rs":"e04f19ab2e3da4423ce49d43b6c9a6d86be4e573f54421061bea04ef875afb2a","src/error.rs":"c956152633ad6c787f8b7322b619f807354d4c3cb2ecc35c549c3b4bcd98079e","src/lib.rs":"62aa85ab62d91caa07d58987260f52329c7ab793224e8a1d30e07ce446263d48","src/schema.rs":"cd5a03c2d2dc1eebdea30054c6f6a7a6b302184e9ad1f40de659f6b972c481cf","src/store.rs":"035cca3ebf7311cd9c08b8027fd10c113159c86300eb4a8e1fc8434dd56afd3b","src/sync/bridge.rs":"e60fec0f8f167f893bb2055f4623c9cc4dc6921acafd13dcc4e6cfd228b3cb42","src/sync/incoming.rs":"76e494dbe0583bdc3cb9567cbfd202072e80304dded75da9b57c7662e489bc2e","src/sync/mod.rs":"c9ef7561b3ba898e5f5036efc15bcbc95a2975cabbf82ef1d1623349f4897ce7","src/sync/outgoing.rs":"2e0434359ba5005d730aebdac2e29980005f56e62003d89e7def78fcf8d13c5a","src/sync/sync_tests.rs":"e2c665046f3ad2a665eee2b5b33a89ae8804af42bf93fe7b2694280eb5b2a9cc"},"package":null}
|
||||
{"files":{"Cargo.toml":"ffce3f5619bd479ed23e09780ed814f7e2b16a7b82e21cc00b2b91373e205d44","README.md":"1fd617294339930ee1ad5172377648b268cce0216fc3971facbfe7c6839e9ab1","build.rs":"2b827a62155a3d724cdb4c198270ea467439e537403f82fa873321ac55a69a63","sql/create_schema.sql":"cbb6d432e578c69614199f9e82f8103da5c1f6df5d7af4f77ea1be5869000b26","sql/create_sync_temp_tables.sql":"76517b1ec8022ca2ef53cf8c98b2ad0c289e7f2e2eb2a4b7abb7ad441fe27c6c","src/api.rs":"363674be0123604416fcdde1651c54cae5f8d7869408c12901d212814aa2293e","src/db.rs":"e04f19ab2e3da4423ce49d43b6c9a6d86be4e573f54421061bea04ef875afb2a","src/error.rs":"c956152633ad6c787f8b7322b619f807354d4c3cb2ecc35c549c3b4bcd98079e","src/lib.rs":"9dd35c00948358f495ba04953a22fbce5e4b663346747d531b3bbd553f4c2816","src/migration.rs":"a0e41c40c3b636f642f606655d8fcd36e65be13218c71beea0c16394e71f0f44","src/schema.rs":"cd5a03c2d2dc1eebdea30054c6f6a7a6b302184e9ad1f40de659f6b972c481cf","src/store.rs":"37b0bc71944255b394b0b7d287e1124e8b872d6638bc77250db0290876c03b26","src/sync/bridge.rs":"e60fec0f8f167f893bb2055f4623c9cc4dc6921acafd13dcc4e6cfd228b3cb42","src/sync/incoming.rs":"0bcbe042340a9989ce9d18dde5eb38e81580d824d9496c34d890ff8a18f92730","src/sync/mod.rs":"7255fa036635de1ab9223ccdb34fc6164e6978024ae480a9268743a203860448","src/sync/outgoing.rs":"2e0434359ba5005d730aebdac2e29980005f56e62003d89e7def78fcf8d13c5a","src/sync/sync_tests.rs":"e2c665046f3ad2a665eee2b5b33a89ae8804af42bf93fe7b2694280eb5b2a9cc"},"package":null}
|
|
@ -13,7 +13,7 @@ default = []
|
|||
error-support = { path = "../support/error" }
|
||||
failure = "0.1"
|
||||
interrupt-support = { path = "../support/interrupt" }
|
||||
lazy_static = "1.4.0"
|
||||
lazy_static = "1.4"
|
||||
log = "0.4"
|
||||
serde = "1"
|
||||
serde_json = "1"
|
||||
|
@ -30,6 +30,7 @@ features = ["functions", "bundled", "serde_json"]
|
|||
[dev-dependencies]
|
||||
env_logger = "0.7"
|
||||
prettytable-rs = "0.8"
|
||||
tempfile = "3"
|
||||
|
||||
# A *direct* dep on the -sys crate is required for our build.rs
|
||||
# to see the DEP_SQLITE3_LINK_TARGET env var that cargo sets
|
||||
|
|
|
@ -21,6 +21,18 @@ CREATE TEMP TABLE IF NOT EXISTS storage_sync_staging (
|
|||
|
||||
DELETE FROM temp.storage_sync_staging;
|
||||
|
||||
-- We record the changes we are making via sync in this table, so that at the
|
||||
-- end of the sync extensions can find out via notifications what changes
|
||||
-- were applied.
|
||||
CREATE TEMP TABLE IF NOT EXISTS storage_sync_applied (
|
||||
ext_id TEXT NOT NULL UNIQUE,
|
||||
|
||||
/* A StorageChanges value serialized as JSON. */
|
||||
changes TEXT NOT NULL
|
||||
);
|
||||
|
||||
DELETE FROM temp.storage_sync_applied;
|
||||
|
||||
-- We store metadata about items we are uploading in this temp table. After
|
||||
-- we get told the upload was successful we use this to update the local
|
||||
-- tables.
|
||||
|
|
|
@ -115,39 +115,37 @@ fn remove_from_db(tx: &Transaction<'_>, ext_id: &str) -> Result<()> {
|
|||
#[serde(rename_all = "camelCase")]
|
||||
pub struct StorageValueChange {
|
||||
#[serde(skip_serializing)]
|
||||
key: String,
|
||||
pub key: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
old_value: Option<JsonValue>,
|
||||
pub old_value: Option<JsonValue>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
new_value: Option<JsonValue>,
|
||||
pub new_value: Option<JsonValue>,
|
||||
}
|
||||
|
||||
// This is, largely, a helper so that this serializes correctly as per the
|
||||
// chrome.storage.sync spec. If not for custom serialization it should just
|
||||
// be a plain vec
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Default, Clone, PartialEq)]
|
||||
pub struct StorageChanges {
|
||||
changes: Vec<StorageValueChange>,
|
||||
}
|
||||
|
||||
impl StorageChanges {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
changes: Vec::new(),
|
||||
}
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
fn with_capacity(n: usize) -> Self {
|
||||
pub fn with_capacity(n: usize) -> Self {
|
||||
Self {
|
||||
changes: Vec::with_capacity(n),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.changes.is_empty()
|
||||
}
|
||||
|
||||
fn push(&mut self, change: StorageValueChange) {
|
||||
pub fn push(&mut self, change: StorageValueChange) {
|
||||
self.changes.push(change)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
mod api;
|
||||
mod db;
|
||||
pub mod error;
|
||||
mod migration;
|
||||
mod schema;
|
||||
pub mod store;
|
||||
mod sync;
|
||||
|
|
|
@ -0,0 +1,305 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use crate::error::*;
|
||||
use rusqlite::{Connection, OpenFlags, Transaction, NO_PARAMS};
|
||||
use serde_json::{Map, Value};
|
||||
use sql_support::ConnExt;
|
||||
use std::path::Path;
|
||||
|
||||
// Simple migration from the "old" kinto-with-sqlite-backing implementation
|
||||
// to ours.
|
||||
// Could almost be trivially done in JS using the regular public API if not
|
||||
// for:
|
||||
// * We don't want to enforce the same quotas when migrating.
|
||||
// * We'd rather do the entire migration in a single transaction for perf
|
||||
// reasons.
|
||||
|
||||
// The sqlite database we migrate from has a very simple structure:
|
||||
// * table collection_data with columns collection_name, record_id and record
|
||||
// * `collection_name` is a string of form "default/{extension_id}"
|
||||
// * `record_id` is `key-{key}`
|
||||
// * `record` is a string with json, of form: {
|
||||
// id: {the record id repeated},
|
||||
// key: {the key},
|
||||
// data: {the actual data},
|
||||
// _status: {sync status},
|
||||
// last_modified: {timestamp},
|
||||
// }
|
||||
// So the info we need is stored somewhat redundantly.
|
||||
// Further:
|
||||
// * There's a special collection_name "default/storage-sync-crypto" that
|
||||
// we don't want to migrate. Its record_id is 'keys' and its json has no
|
||||
// `data`
|
||||
|
||||
// Note we don't enforce a quota - we migrate everything - even if this means
|
||||
// it's too big for the server to store. This is a policy decision - it's better
|
||||
// to not lose data than to try and work out what data can be disposed of, as
|
||||
// the addon has the ability to determine this.
|
||||
|
||||
// Our error strategy is "ignore read errors, propagate write errors" under the
|
||||
// assumption that the former tends to mean a damaged DB or file-system and is
|
||||
// unlikely to work if we try later (eg, replacing the disk isn't likely to
|
||||
// uncorrupt the DB), where the latter is likely to be disk-space or file-system
|
||||
// error, but retry might work (eg, replacing the disk then trying again might
|
||||
// make the writes work)
|
||||
|
||||
// The struct we read from the DB.
|
||||
struct LegacyRow {
|
||||
col_name: String, // collection_name column
|
||||
record: String, // record column
|
||||
}
|
||||
|
||||
impl LegacyRow {
|
||||
// Parse the 2 columns from the DB into the data we need to insert into
|
||||
// our target database.
|
||||
fn parse(&self) -> Option<Parsed<'_>> {
|
||||
if self.col_name.len() < 8 {
|
||||
log::trace!("collection_name of '{}' is too short", self.col_name);
|
||||
return None;
|
||||
}
|
||||
if &self.col_name[..8] != "default/" {
|
||||
log::trace!("collection_name of '{}' isn't ours", self.col_name);
|
||||
return None;
|
||||
}
|
||||
let ext_id = &self.col_name[8..];
|
||||
let mut record_map = match serde_json::from_str(&self.record) {
|
||||
Ok(Value::Object(m)) => m,
|
||||
Ok(o) => {
|
||||
log::info!("skipping non-json-object 'record' column");
|
||||
log::trace!("record value is json, but not an object: {}", o);
|
||||
return None;
|
||||
}
|
||||
Err(e) => {
|
||||
log::info!("skipping non-json 'record' column");
|
||||
log::trace!("record value isn't json: {}", e);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
let key = match record_map.remove("key") {
|
||||
Some(Value::String(s)) if !s.is_empty() => s,
|
||||
Some(o) => {
|
||||
log::trace!("key is json but not a string: {}", o);
|
||||
return None;
|
||||
}
|
||||
_ => {
|
||||
log::trace!("key doesn't exist in the map");
|
||||
return None;
|
||||
}
|
||||
};
|
||||
let data = match record_map.remove("data") {
|
||||
Some(d) => d,
|
||||
_ => {
|
||||
log::trace!("data doesn't exist in the map");
|
||||
return None;
|
||||
}
|
||||
};
|
||||
Some(Parsed { ext_id, key, data })
|
||||
}
|
||||
}
|
||||
|
||||
// The info we parse from the raw DB strings.
|
||||
struct Parsed<'a> {
|
||||
ext_id: &'a str,
|
||||
key: String,
|
||||
data: serde_json::Value,
|
||||
}
|
||||
|
||||
pub fn migrate(tx: &Transaction<'_>, filename: &Path) -> Result<usize> {
|
||||
// We do the grouping manually, collecting string values as we go.
|
||||
let mut last_ext_id = "".to_string();
|
||||
let mut curr_values: Vec<(String, serde_json::Value)> = Vec::new();
|
||||
let mut num_extensions = 0;
|
||||
for row in read_rows(filename) {
|
||||
log::trace!("processing '{}' - '{}'", row.col_name, row.record);
|
||||
let parsed = match row.parse() {
|
||||
Some(p) => p,
|
||||
None => continue,
|
||||
};
|
||||
// Do our "grouping"
|
||||
if parsed.ext_id != last_ext_id {
|
||||
if last_ext_id != "" && !curr_values.is_empty() {
|
||||
// a different extension id - write what we have to the DB.
|
||||
do_insert(tx, &last_ext_id, curr_values)?;
|
||||
num_extensions += 1;
|
||||
}
|
||||
last_ext_id = parsed.ext_id.to_string();
|
||||
curr_values = Vec::new();
|
||||
}
|
||||
// no 'else' here - must also enter this block on ext_id change.
|
||||
if parsed.ext_id == last_ext_id {
|
||||
curr_values.push((parsed.key.to_string(), parsed.data));
|
||||
log::trace!(
|
||||
"extension {} now has {} keys",
|
||||
parsed.ext_id,
|
||||
curr_values.len()
|
||||
);
|
||||
}
|
||||
}
|
||||
// and the last one
|
||||
if last_ext_id != "" && !curr_values.is_empty() {
|
||||
// a different extension id - write what we have to the DB.
|
||||
do_insert(tx, &last_ext_id, curr_values)?;
|
||||
num_extensions += 1;
|
||||
}
|
||||
log::info!("migrated {} extensions", num_extensions);
|
||||
Ok(num_extensions)
|
||||
}
|
||||
|
||||
fn read_rows(filename: &Path) -> Vec<LegacyRow> {
|
||||
let flags = OpenFlags::SQLITE_OPEN_NO_MUTEX | OpenFlags::SQLITE_OPEN_READ_ONLY;
|
||||
let src_conn = match Connection::open_with_flags(&filename, flags) {
|
||||
Ok(conn) => conn,
|
||||
Err(e) => {
|
||||
log::warn!("Failed to open the source DB: {}", e);
|
||||
return Vec::new();
|
||||
}
|
||||
};
|
||||
// Failure to prepare the statement probably just means the source DB is
|
||||
// damaged.
|
||||
let mut stmt = match src_conn.prepare(
|
||||
"SELECT collection_name, record FROM collection_data
|
||||
ORDER BY collection_name",
|
||||
) {
|
||||
Ok(stmt) => stmt,
|
||||
Err(e) => {
|
||||
log::warn!("Failed to prepare the statement: {}", e);
|
||||
return Vec::new();
|
||||
}
|
||||
};
|
||||
let rows = match stmt.query_and_then(NO_PARAMS, |row| -> Result<LegacyRow> {
|
||||
Ok(LegacyRow {
|
||||
col_name: row.get(0)?,
|
||||
record: row.get(1)?,
|
||||
})
|
||||
}) {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
log::warn!("Failed to read any rows from the source DB: {}", e);
|
||||
return Vec::new();
|
||||
}
|
||||
};
|
||||
|
||||
rows.filter_map(Result::ok).collect()
|
||||
}
|
||||
|
||||
fn do_insert(tx: &Transaction<'_>, ext_id: &str, vals: Vec<(String, Value)>) -> Result<()> {
|
||||
let mut map = Map::with_capacity(vals.len());
|
||||
for (key, val) in vals {
|
||||
map.insert(key, val);
|
||||
}
|
||||
tx.execute_named_cached(
|
||||
"INSERT OR REPLACE INTO storage_sync_data(ext_id, data, sync_change_counter)
|
||||
VALUES (:ext_id, :data, 1)",
|
||||
rusqlite::named_params! {
|
||||
":ext_id": &ext_id,
|
||||
":data": &Value::Object(map),
|
||||
},
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::api;
|
||||
use crate::db::{test::new_mem_db, StorageDb};
|
||||
use serde_json::json;
|
||||
use tempfile::tempdir;
|
||||
|
||||
// Create a test database, populate it via the callback, migrate it, and
|
||||
// return a connection to the new, migrated DB for further checking.
|
||||
fn do_migrate<F>(num_expected: usize, f: F) -> StorageDb
|
||||
where
|
||||
F: FnOnce(&Connection),
|
||||
{
|
||||
let tmpdir = tempdir().unwrap();
|
||||
let path = tmpdir.path().join("source.db");
|
||||
let flags = OpenFlags::SQLITE_OPEN_NO_MUTEX
|
||||
| OpenFlags::SQLITE_OPEN_CREATE
|
||||
| OpenFlags::SQLITE_OPEN_READ_WRITE;
|
||||
let mut conn = Connection::open_with_flags(path, flags).expect("open should work");
|
||||
let tx = conn.transaction().expect("should be able to get a tx");
|
||||
tx.execute_batch(
|
||||
"CREATE TABLE collection_data (
|
||||
collection_name TEXT,
|
||||
record_id TEXT,
|
||||
record TEXT
|
||||
);",
|
||||
)
|
||||
.expect("create should work");
|
||||
f(&tx);
|
||||
tx.commit().expect("should commit");
|
||||
conn.close().expect("close should work");
|
||||
|
||||
// now migrate
|
||||
let mut db = new_mem_db();
|
||||
let tx = db.transaction().expect("tx should work");
|
||||
|
||||
let num = migrate(&tx, &tmpdir.path().join("source.db")).expect("migrate should work");
|
||||
tx.commit().expect("should work");
|
||||
assert_eq!(num, num_expected);
|
||||
db
|
||||
}
|
||||
|
||||
fn assert_has(c: &Connection, ext_id: &str, expect: Value) {
|
||||
assert_eq!(
|
||||
api::get(c, ext_id, json!(null)).expect("should get"),
|
||||
expect
|
||||
);
|
||||
}
|
||||
|
||||
#[allow(clippy::unreadable_literal)]
|
||||
#[test]
|
||||
fn test_happy_paths() {
|
||||
// some real data.
|
||||
let conn = do_migrate(2, |c| {
|
||||
c.execute_batch(
|
||||
r#"INSERT INTO collection_data(collection_name, record)
|
||||
VALUES
|
||||
('default/{e7fefcf3-b39c-4f17-5215-ebfe120a7031}', '{"id":"key-userWelcomed","key":"userWelcomed","data":1570659224457,"_status":"synced","last_modified":1579755940527}'),
|
||||
('default/{e7fefcf3-b39c-4f17-5215-ebfe120a7031}', '{"id":"key-isWho","key":"isWho","data":"4ec8109f","_status":"synced","last_modified":1579755940497}'),
|
||||
('default/storage-sync-crypto', '{"id":"keys","keys":{"default":["rQ=","lR="],"collections":{"extension@redux.devtools":["Bd=","ju="]}}}'),
|
||||
('default/https-everywhere@eff.org', '{"id":"key-userRules","key":"userRules","data":[],"_status":"synced","last_modified":1570079920045}'),
|
||||
('default/https-everywhere@eff.org', '{"id":"key-ruleActiveStates","key":"ruleActiveStates","data":{},"_status":"synced","last_modified":1570079919993}'),
|
||||
('default/https-everywhere@eff.org', '{"id":"key-migration_5F_version","key":"migration_version","data":2,"_status":"synced","last_modified":1570079919966}')
|
||||
"#,
|
||||
).expect("should popuplate")
|
||||
});
|
||||
|
||||
assert_has(
|
||||
&conn,
|
||||
"{e7fefcf3-b39c-4f17-5215-ebfe120a7031}",
|
||||
json!({"userWelcomed": 1570659224457u64, "isWho": "4ec8109f"}),
|
||||
);
|
||||
assert_has(
|
||||
&conn,
|
||||
"https-everywhere@eff.org",
|
||||
json!({"userRules": [], "ruleActiveStates": {}, "migration_version": 2}),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sad_paths() {
|
||||
do_migrate(0, |c| {
|
||||
c.execute_batch(
|
||||
r#"INSERT INTO collection_data(collection_name, record)
|
||||
VALUES
|
||||
('default/test', '{"key":2,"data":1}'), -- key not a string
|
||||
('default/test', '{"key":"","data":1}'), -- key empty string
|
||||
('default/test', '{"xey":"k","data":1}'), -- key missing
|
||||
('default/test', '{"key":"k","xata":1}'), -- data missing
|
||||
('default/test', '{"key":"k","data":1'), -- invalid json
|
||||
('xx/test', '{"key":"k","data":1}'), -- bad key format
|
||||
('default', '{"key":"k","data":1}'), -- bad key format 2
|
||||
('default/', '{"key":"k","data":1}'), -- bad key format 3
|
||||
('defaultx/test', '{"key":"k","data":1}'), -- bad key format 4
|
||||
('', '') -- empty strings
|
||||
"#,
|
||||
)
|
||||
.expect("should populate");
|
||||
});
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@
|
|||
use crate::api::{self, StorageChanges};
|
||||
use crate::db::StorageDb;
|
||||
use crate::error::*;
|
||||
use crate::migration::migrate;
|
||||
use crate::sync;
|
||||
use std::path::Path;
|
||||
use std::result;
|
||||
|
@ -116,6 +117,26 @@ impl Store {
|
|||
pub fn close(self) -> result::Result<(), (Store, Error)> {
|
||||
self.db.close().map_err(|(db, err)| (Store { db }, err))
|
||||
}
|
||||
|
||||
/// Gets the changes which the current sync applied. Should be used
|
||||
/// immediately after the bridged engine is told to apply incoming changes,
|
||||
/// and can be used to notify observers of the StorageArea of the changes
|
||||
/// that were applied.
|
||||
/// The result is a Vec of already JSON stringified changes.
|
||||
pub fn get_synced_changes(&self) -> Result<Vec<sync::SyncedExtensionChange>> {
|
||||
sync::get_synced_changes(&self.db)
|
||||
}
|
||||
|
||||
/// Migrates data from a database in the format of the "old" kinto
|
||||
/// implementation. Returns the count of webextensions for whom data was
|
||||
/// migrated.
|
||||
/// Note that `filename` isn't normalized or canonicalized.
|
||||
pub fn migrate(&self, filename: impl AsRef<Path>) -> Result<usize> {
|
||||
let tx = self.db.unchecked_transaction()?;
|
||||
let result = migrate(&tx, filename.as_ref())?;
|
||||
tx.commit()?;
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -11,6 +11,7 @@ use sql_support::ConnExt;
|
|||
use sync15_traits::Payload;
|
||||
use sync_guid::Guid as SyncGuid;
|
||||
|
||||
use crate::api::{StorageChanges, StorageValueChange};
|
||||
use crate::error::*;
|
||||
|
||||
use super::{merge, remove_matching_keys, JsonMap, Record};
|
||||
|
@ -25,6 +26,20 @@ pub enum DataState {
|
|||
Exists(JsonMap),
|
||||
}
|
||||
|
||||
// A little helper to create a StorageChanges object when we are creating
|
||||
// a new value with multiple keys that doesn't exist locally.
|
||||
fn changes_for_new_incoming(new: &JsonMap) -> StorageChanges {
|
||||
let mut result = StorageChanges::with_capacity(new.len());
|
||||
for (key, val) in new.iter() {
|
||||
result.push(StorageValueChange {
|
||||
key: key.clone(),
|
||||
old_value: None,
|
||||
new_value: Some(val.clone()),
|
||||
});
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
// This module deals exclusively with the Map inside a JsonValue::Object().
|
||||
// This helper reads such a Map from a SQL row, ignoring anything which is
|
||||
// either invalid JSON or a different JSON type.
|
||||
|
@ -172,14 +187,21 @@ pub fn get_incoming(conn: &Connection) -> Result<Vec<(IncomingItem, IncomingStat
|
|||
|
||||
/// This is the set of actions we know how to take *locally* for incoming
|
||||
/// records. Which one depends on the IncomingState.
|
||||
/// Every state which updates also records the set of changes we should notify
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum IncomingAction {
|
||||
/// We should locally delete the data for this record
|
||||
DeleteLocally,
|
||||
DeleteLocally { changes: StorageChanges },
|
||||
/// We will take the remote.
|
||||
TakeRemote { data: JsonMap },
|
||||
TakeRemote {
|
||||
data: JsonMap,
|
||||
changes: StorageChanges,
|
||||
},
|
||||
/// We merged this data - this is what we came up with.
|
||||
Merge { data: JsonMap },
|
||||
Merge {
|
||||
data: JsonMap,
|
||||
changes: StorageChanges,
|
||||
},
|
||||
/// Entry exists locally and it's the same as the incoming record.
|
||||
Same,
|
||||
}
|
||||
|
@ -213,6 +235,7 @@ pub fn plan_incoming(s: IncomingState) -> IncomingAction {
|
|||
(DataState::Exists(incoming_data), DataState::Deleted, _) => {
|
||||
// Incoming data, removed locally. Server wins.
|
||||
IncomingAction::TakeRemote {
|
||||
changes: changes_for_new_incoming(&incoming_data),
|
||||
data: incoming_data,
|
||||
}
|
||||
}
|
||||
|
@ -220,13 +243,16 @@ pub fn plan_incoming(s: IncomingState) -> IncomingAction {
|
|||
// Deleted remotely.
|
||||
// Treat this as a delete of every key that we
|
||||
// know was present at the time.
|
||||
let result = remove_matching_keys(local_data, &mirror);
|
||||
let (result, changes) = remove_matching_keys(local_data, &mirror);
|
||||
if result.is_empty() {
|
||||
// If there were no more keys left, we can
|
||||
// delete our version too.
|
||||
IncomingAction::DeleteLocally
|
||||
IncomingAction::DeleteLocally { changes }
|
||||
} else {
|
||||
IncomingAction::Merge { data: result }
|
||||
IncomingAction::Merge {
|
||||
data: result,
|
||||
changes,
|
||||
}
|
||||
}
|
||||
}
|
||||
(DataState::Deleted, DataState::Exists(local_data), DataState::Deleted) => {
|
||||
|
@ -236,12 +262,15 @@ pub fn plan_incoming(s: IncomingState) -> IncomingAction {
|
|||
// Treat this as a delete of every key that we
|
||||
// knew was present. Unfortunately, we don't know
|
||||
// any keys that were present, so we delete no keys.
|
||||
IncomingAction::Merge { data: local_data }
|
||||
IncomingAction::Merge {
|
||||
data: local_data,
|
||||
changes: StorageChanges::new(),
|
||||
}
|
||||
}
|
||||
(DataState::Deleted, DataState::Deleted, _) => {
|
||||
// We agree with the remote (regardless of what we
|
||||
// have mirrored).
|
||||
IncomingAction::DeleteLocally
|
||||
IncomingAction::Same
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -261,11 +290,15 @@ pub fn plan_incoming(s: IncomingState) -> IncomingAction {
|
|||
// We would normally remove keys that we knew were
|
||||
// present on the server, but we don't know what
|
||||
// was on the server, so we don't remove anything.
|
||||
IncomingAction::Merge { data: local_data }
|
||||
IncomingAction::Merge {
|
||||
data: local_data,
|
||||
changes: StorageChanges::new(),
|
||||
}
|
||||
}
|
||||
(DataState::Exists(incoming_data), DataState::Deleted) => {
|
||||
// No data locally, but some is incoming - take it.
|
||||
IncomingAction::TakeRemote {
|
||||
changes: changes_for_new_incoming(&incoming_data),
|
||||
data: incoming_data,
|
||||
}
|
||||
}
|
||||
|
@ -280,7 +313,10 @@ pub fn plan_incoming(s: IncomingState) -> IncomingAction {
|
|||
// This means a local deletion is being replaced by, or just re-doing
|
||||
// the incoming record.
|
||||
match incoming {
|
||||
DataState::Exists(data) => IncomingAction::TakeRemote { data },
|
||||
DataState::Exists(data) => IncomingAction::TakeRemote {
|
||||
changes: changes_for_new_incoming(&data),
|
||||
data,
|
||||
},
|
||||
DataState::Deleted => IncomingAction::Same,
|
||||
}
|
||||
}
|
||||
|
@ -288,13 +324,30 @@ pub fn plan_incoming(s: IncomingState) -> IncomingAction {
|
|||
// Only the staging record exists - this means it's the first time
|
||||
// we've ever seen it. No conflict possible, just take the remote.
|
||||
match incoming {
|
||||
DataState::Exists(data) => IncomingAction::TakeRemote { data },
|
||||
DataState::Deleted => IncomingAction::DeleteLocally,
|
||||
DataState::Exists(data) => IncomingAction::TakeRemote {
|
||||
changes: changes_for_new_incoming(&data),
|
||||
data,
|
||||
},
|
||||
DataState::Deleted => IncomingAction::DeleteLocally {
|
||||
changes: StorageChanges::new(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn insert_changes(tx: &Transaction<'_>, ext_id: &str, changes: &StorageChanges) -> Result<()> {
|
||||
tx.execute_named_cached(
|
||||
"INSERT INTO temp.storage_sync_applied (ext_id, changes)
|
||||
VALUES (:ext_id, :changes)",
|
||||
&[
|
||||
(":ext_id", &ext_id),
|
||||
(":changes", &serde_json::to_string(&changes)?),
|
||||
],
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Apply the actions necessary to fully process the incoming items.
|
||||
pub fn apply_actions(
|
||||
tx: &Transaction<'_>,
|
||||
|
@ -306,15 +359,16 @@ pub fn apply_actions(
|
|||
|
||||
log::trace!("action for '{}': {:?}", item.ext_id, action);
|
||||
match action {
|
||||
IncomingAction::DeleteLocally => {
|
||||
IncomingAction::DeleteLocally { changes } => {
|
||||
// Can just nuke it entirely.
|
||||
tx.execute_named_cached(
|
||||
"DELETE FROM storage_sync_data WHERE ext_id = :ext_id",
|
||||
&[(":ext_id", &item.ext_id)],
|
||||
)?;
|
||||
insert_changes(tx, &item.ext_id, &changes)?;
|
||||
}
|
||||
// We want to update the local record with 'data' and after this update the item no longer is considered dirty.
|
||||
IncomingAction::TakeRemote { data } => {
|
||||
IncomingAction::TakeRemote { data, changes } => {
|
||||
tx.execute_named_cached(
|
||||
"INSERT OR REPLACE INTO storage_sync_data(ext_id, data, sync_change_counter)
|
||||
VALUES (:ext_id, :data, 0)",
|
||||
|
@ -323,11 +377,12 @@ pub fn apply_actions(
|
|||
(":data", &serde_json::Value::Object(data)),
|
||||
],
|
||||
)?;
|
||||
insert_changes(tx, &item.ext_id, &changes)?;
|
||||
}
|
||||
|
||||
// We merged this data, so need to update locally but still consider
|
||||
// it dirty because the merged data must be uploaded.
|
||||
IncomingAction::Merge { data } => {
|
||||
IncomingAction::Merge { data, changes } => {
|
||||
tx.execute_named_cached(
|
||||
"UPDATE storage_sync_data SET data = :data, sync_change_counter = sync_change_counter + 1 WHERE ext_id = :ext_id",
|
||||
&[
|
||||
|
@ -335,6 +390,7 @@ pub fn apply_actions(
|
|||
(":data", &serde_json::Value::Object(data)),
|
||||
]
|
||||
)?;
|
||||
insert_changes(tx, &item.ext_id, &changes)?;
|
||||
}
|
||||
|
||||
// Both local and remote ended up the same - only need to nuke the
|
||||
|
@ -344,6 +400,7 @@ pub fn apply_actions(
|
|||
"UPDATE storage_sync_data SET sync_change_counter = 0 WHERE ext_id = :ext_id",
|
||||
&[(":ext_id", &item.ext_id)],
|
||||
)?;
|
||||
// no changes to write
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -376,12 +433,53 @@ mod tests {
|
|||
result
|
||||
}
|
||||
|
||||
// Can't find a way to import this from crate::sync::tests...
|
||||
// Can't find a way to import these from crate::sync::tests...
|
||||
macro_rules! map {
|
||||
($($map:tt)+) => {
|
||||
json!($($map)+).as_object().unwrap().clone()
|
||||
};
|
||||
}
|
||||
macro_rules! change {
|
||||
($key:literal, None, None) => {
|
||||
StorageValueChange {
|
||||
key: $key.to_string(),
|
||||
old_value: None,
|
||||
new_value: None,
|
||||
};
|
||||
};
|
||||
($key:literal, $old:tt, None) => {
|
||||
StorageValueChange {
|
||||
key: $key.to_string(),
|
||||
old_value: Some(json!($old)),
|
||||
new_value: None,
|
||||
};
|
||||
};
|
||||
($key:literal, None, $new:tt) => {
|
||||
StorageValueChange {
|
||||
key: $key.to_string(),
|
||||
old_value: None,
|
||||
new_value: Some(json!($new)),
|
||||
};
|
||||
};
|
||||
($key:literal, $old:tt, $new:tt) => {
|
||||
StorageValueChange {
|
||||
key: $key.to_string(),
|
||||
old_value: Some(json!($old)),
|
||||
new_value: Some(json!($new)),
|
||||
};
|
||||
};
|
||||
}
|
||||
macro_rules! changes {
|
||||
( $( $change:expr ),* ) => {
|
||||
{
|
||||
let mut changes = StorageChanges::new();
|
||||
$(
|
||||
changes.push($change);
|
||||
)*
|
||||
changes
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_incoming_populates_staging() -> Result<()> {
|
||||
|
@ -554,6 +652,31 @@ mod tests {
|
|||
.expect("query should work")
|
||||
}
|
||||
|
||||
fn get_applied_item_changes(conn: &Connection) -> Option<StorageChanges> {
|
||||
// no custom deserialize for storagechanges and we only need it for
|
||||
// tests, so do it manually.
|
||||
conn.try_query_row::<_, Error, _>(
|
||||
"SELECT changes FROM temp.storage_sync_applied WHERE ext_id = 'ext_id'",
|
||||
&[],
|
||||
|row| Ok(serde_json::from_str(&row.get::<_, String>("changes")?)?),
|
||||
true,
|
||||
)
|
||||
.expect("query should work")
|
||||
.map(|val: serde_json::Value| {
|
||||
let ob = val.as_object().expect("should be an object of items");
|
||||
let mut result = StorageChanges::with_capacity(ob.len());
|
||||
for (key, val) in ob.into_iter() {
|
||||
let details = val.as_object().expect("elts should be objects");
|
||||
result.push(StorageValueChange {
|
||||
key: key.to_string(),
|
||||
old_value: details.get("oldValue").cloned(),
|
||||
new_value: details.get("newValue").cloned(),
|
||||
});
|
||||
}
|
||||
result
|
||||
})
|
||||
}
|
||||
|
||||
fn do_apply_action(tx: &Transaction<'_>, action: IncomingAction) {
|
||||
let item = IncomingItem {
|
||||
guid: SyncGuid::new("guid"),
|
||||
|
@ -573,10 +696,17 @@ mod tests {
|
|||
api::get(&tx, "ext_id", json!(null))?,
|
||||
json!({"foo": "local"})
|
||||
);
|
||||
do_apply_action(&tx, IncomingAction::DeleteLocally);
|
||||
let changes = changes![change!("foo", "local", None)];
|
||||
do_apply_action(
|
||||
&tx,
|
||||
IncomingAction::DeleteLocally {
|
||||
changes: changes.clone(),
|
||||
},
|
||||
);
|
||||
assert_eq!(api::get(&tx, "ext_id", json!(null))?, json!({}));
|
||||
// and there should not be a local record at all.
|
||||
assert!(get_local_item(&tx).is_none());
|
||||
assert_eq!(get_applied_item_changes(&tx), Some(changes));
|
||||
tx.rollback()?;
|
||||
|
||||
// TakeRemote - replace local data with remote and marked as not dirty.
|
||||
|
@ -594,10 +724,12 @@ mod tests {
|
|||
sync_change_counter: 1
|
||||
})
|
||||
);
|
||||
let changes = changes![change!("foo", "local", "remote")];
|
||||
do_apply_action(
|
||||
&tx,
|
||||
IncomingAction::TakeRemote {
|
||||
data: map!({"foo": "remote"}),
|
||||
changes: changes.clone(),
|
||||
},
|
||||
);
|
||||
// data should exist locally with the remote data and not be dirty.
|
||||
|
@ -608,6 +740,7 @@ mod tests {
|
|||
sync_change_counter: 0
|
||||
})
|
||||
);
|
||||
assert_eq!(get_applied_item_changes(&tx), Some(changes));
|
||||
tx.rollback()?;
|
||||
|
||||
// Merge - like ::TakeRemote, but data remains dirty.
|
||||
|
@ -625,10 +758,12 @@ mod tests {
|
|||
sync_change_counter: 1
|
||||
})
|
||||
);
|
||||
let changes = changes![change!("foo", "local", "remote")];
|
||||
do_apply_action(
|
||||
&tx,
|
||||
IncomingAction::Merge {
|
||||
data: map!({"foo": "remote"}),
|
||||
changes: changes.clone(),
|
||||
},
|
||||
);
|
||||
assert_eq!(
|
||||
|
@ -638,6 +773,7 @@ mod tests {
|
|||
sync_change_counter: 2
|
||||
})
|
||||
);
|
||||
assert_eq!(get_applied_item_changes(&tx), Some(changes));
|
||||
tx.rollback()?;
|
||||
|
||||
// Same - data stays the same but is marked not dirty.
|
||||
|
@ -663,6 +799,7 @@ mod tests {
|
|||
sync_change_counter: 0
|
||||
})
|
||||
);
|
||||
assert_eq!(get_applied_item_changes(&tx), None);
|
||||
tx.rollback()?;
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -9,7 +9,11 @@ mod outgoing;
|
|||
#[cfg(test)]
|
||||
mod sync_tests;
|
||||
|
||||
use crate::api::{StorageChanges, StorageValueChange};
|
||||
use crate::db::StorageDb;
|
||||
use crate::error::*;
|
||||
use serde_derive::*;
|
||||
use sql_support::ConnExt;
|
||||
use sync_guid::Guid as SyncGuid;
|
||||
|
||||
pub use bridge::BridgedEngine;
|
||||
|
@ -41,6 +45,8 @@ fn merge(mut other: JsonMap, mut ours: JsonMap, parent: Option<JsonMap>) -> Inco
|
|||
return IncomingAction::Same;
|
||||
}
|
||||
let old_incoming = other.clone();
|
||||
// worst case is keys in each are unique.
|
||||
let mut changes = StorageChanges::with_capacity(other.len() + ours.len());
|
||||
if let Some(parent) = parent {
|
||||
// Perform 3-way merge. First, for every key in parent,
|
||||
// compare the parent value with the incoming value to compute
|
||||
|
@ -52,6 +58,15 @@ fn merge(mut other: JsonMap, mut ours: JsonMap, parent: Option<JsonMap>) -> Inco
|
|||
"merge: key {} was updated in incoming - copying value locally",
|
||||
key
|
||||
);
|
||||
let old_value = ours.remove(&key);
|
||||
let new_value = Some(incoming_value.clone());
|
||||
if old_value != new_value {
|
||||
changes.push(StorageValueChange {
|
||||
key: key.clone(),
|
||||
old_value,
|
||||
new_value,
|
||||
});
|
||||
}
|
||||
ours.insert(key, incoming_value);
|
||||
}
|
||||
} else {
|
||||
|
@ -61,7 +76,13 @@ fn merge(mut other: JsonMap, mut ours: JsonMap, parent: Option<JsonMap>) -> Inco
|
|||
"merge: key {} no longer present in incoming - removing it locally",
|
||||
key
|
||||
);
|
||||
ours.remove(&key);
|
||||
if let Some(old_value) = ours.remove(&key) {
|
||||
changes.push(StorageValueChange {
|
||||
key,
|
||||
old_value: Some(old_value),
|
||||
new_value: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -73,6 +94,11 @@ fn merge(mut other: JsonMap, mut ours: JsonMap, parent: Option<JsonMap>) -> Inco
|
|||
"merge: key {} doesn't occur in parent - copying from incoming",
|
||||
key
|
||||
);
|
||||
changes.push(StorageValueChange {
|
||||
key: key.clone(),
|
||||
old_value: None,
|
||||
new_value: Some(incoming_value.clone()),
|
||||
});
|
||||
ours.insert(key, incoming_value);
|
||||
}
|
||||
} else {
|
||||
|
@ -80,22 +106,69 @@ fn merge(mut other: JsonMap, mut ours: JsonMap, parent: Option<JsonMap>) -> Inco
|
|||
// the corresponding value in other.
|
||||
log::trace!("merge: no parent - copying all keys from incoming");
|
||||
for (key, incoming_value) in other.into_iter() {
|
||||
let old_value = ours.remove(&key);
|
||||
let new_value = Some(incoming_value.clone());
|
||||
if old_value != new_value {
|
||||
changes.push(StorageValueChange {
|
||||
key: key.clone(),
|
||||
old_value,
|
||||
new_value,
|
||||
});
|
||||
}
|
||||
ours.insert(key, incoming_value);
|
||||
}
|
||||
}
|
||||
|
||||
if ours == old_incoming {
|
||||
IncomingAction::TakeRemote { data: old_incoming }
|
||||
IncomingAction::TakeRemote {
|
||||
data: old_incoming,
|
||||
changes,
|
||||
}
|
||||
} else {
|
||||
IncomingAction::Merge { data: ours }
|
||||
IncomingAction::Merge {
|
||||
data: ours,
|
||||
changes,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_matching_keys(mut ours: JsonMap, blacklist: &JsonMap) -> JsonMap {
|
||||
fn remove_matching_keys(mut ours: JsonMap, blacklist: &JsonMap) -> (JsonMap, StorageChanges) {
|
||||
let mut changes = StorageChanges::with_capacity(blacklist.len());
|
||||
for key in blacklist.keys() {
|
||||
ours.remove(key);
|
||||
if let Some(old_value) = ours.remove(key) {
|
||||
changes.push(StorageValueChange {
|
||||
key: key.clone(),
|
||||
old_value: Some(old_value),
|
||||
new_value: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
ours
|
||||
(ours, changes)
|
||||
}
|
||||
|
||||
/// Holds a JSON-serialized map of all synced changes for an extension.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct SyncedExtensionChange {
|
||||
/// The extension ID.
|
||||
pub ext_id: String,
|
||||
/// The contents of a `StorageChanges` struct, in JSON format. We don't
|
||||
/// deserialize these because they need to be passed back to the browser
|
||||
/// as strings anyway.
|
||||
pub changes: String,
|
||||
}
|
||||
|
||||
// Fetches the applied changes we stashed in the storage_sync_applied table.
|
||||
pub fn get_synced_changes(db: &StorageDb) -> Result<Vec<SyncedExtensionChange>> {
|
||||
let signal = db.begin_interrupt_scope();
|
||||
let sql = "SELECT ext_id, changes FROM temp.storage_sync_applied";
|
||||
db.conn()
|
||||
.query_rows_and_then_named(sql, &[], |row| -> Result<_> {
|
||||
signal.err_if_interrupted()?;
|
||||
Ok(SyncedExtensionChange {
|
||||
ext_id: row.get("ext_id")?,
|
||||
changes: row.get("changes")?,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Helpers for tests
|
||||
|
@ -114,8 +187,8 @@ pub mod test {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::test::new_syncable_mem_db;
|
||||
use super::*;
|
||||
use crate::error::*;
|
||||
use serde_json::json;
|
||||
|
||||
// a macro for these tests - constructs a serde_json::Value::Object
|
||||
|
@ -125,6 +198,51 @@ mod tests {
|
|||
};
|
||||
}
|
||||
|
||||
macro_rules! change {
|
||||
($key:literal, None, None) => {
|
||||
StorageValueChange {
|
||||
key: $key.to_string(),
|
||||
old_value: None,
|
||||
new_value: None,
|
||||
};
|
||||
};
|
||||
($key:literal, $old:tt, None) => {
|
||||
StorageValueChange {
|
||||
key: $key.to_string(),
|
||||
old_value: Some(json!($old)),
|
||||
new_value: None,
|
||||
};
|
||||
};
|
||||
($key:literal, None, $new:tt) => {
|
||||
StorageValueChange {
|
||||
key: $key.to_string(),
|
||||
old_value: None,
|
||||
new_value: Some(json!($new)),
|
||||
};
|
||||
};
|
||||
($key:literal, $old:tt, $new:tt) => {
|
||||
StorageValueChange {
|
||||
key: $key.to_string(),
|
||||
old_value: Some(json!($old)),
|
||||
new_value: Some(json!($new)),
|
||||
};
|
||||
};
|
||||
}
|
||||
macro_rules! changes {
|
||||
( ) => {
|
||||
StorageChanges::new()
|
||||
};
|
||||
( $( $change:expr ),* ) => {
|
||||
{
|
||||
let mut changes = StorageChanges::new();
|
||||
$(
|
||||
changes.push($change);
|
||||
)*
|
||||
changes
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_3way_merging() -> Result<()> {
|
||||
// No conflict - identical local and remote.
|
||||
|
@ -143,7 +261,8 @@ mod tests {
|
|||
Some(map!({"parent_only": "parent", "common": "old_common"})),
|
||||
),
|
||||
IncomingAction::Merge {
|
||||
data: map!({"other_only": "other", "ours_only": "ours", "common": "common"})
|
||||
data: map!({"other_only": "other", "ours_only": "ours", "common": "common"}),
|
||||
changes: changes![change!("other_only", None, "other")],
|
||||
}
|
||||
);
|
||||
// Simple conflict - parent value is neither local nor incoming. incoming wins.
|
||||
|
@ -154,7 +273,11 @@ mod tests {
|
|||
Some(map!({"parent_only": "parent", "common": "parent"})),
|
||||
),
|
||||
IncomingAction::Merge {
|
||||
data: map!({"other_only": "other", "ours_only": "ours", "common": "incoming"})
|
||||
data: map!({"other_only": "other", "ours_only": "ours", "common": "incoming"}),
|
||||
changes: changes![
|
||||
change!("common", "local", "incoming"),
|
||||
change!("other_only", None, "other")
|
||||
],
|
||||
}
|
||||
);
|
||||
// Local change, no conflict.
|
||||
|
@ -165,7 +288,8 @@ mod tests {
|
|||
Some(map!({"parent_only": "parent", "common": "old_value"})),
|
||||
),
|
||||
IncomingAction::Merge {
|
||||
data: map!({"other_only": "other", "ours_only": "ours", "common": "new_value"})
|
||||
data: map!({"other_only": "other", "ours_only": "ours", "common": "new_value"}),
|
||||
changes: changes![change!("other_only", None, "other")],
|
||||
}
|
||||
);
|
||||
// Field was removed remotely.
|
||||
|
@ -177,6 +301,10 @@ mod tests {
|
|||
),
|
||||
IncomingAction::TakeRemote {
|
||||
data: map!({"other_only": "other"}),
|
||||
changes: changes![
|
||||
change!("common", "old_value", None),
|
||||
change!("other_only", None, "other")
|
||||
],
|
||||
}
|
||||
);
|
||||
// Field was removed remotely but we added another one.
|
||||
|
@ -188,6 +316,10 @@ mod tests {
|
|||
),
|
||||
IncomingAction::Merge {
|
||||
data: map!({"other_only": "other", "new_key": "new_value"}),
|
||||
changes: changes![
|
||||
change!("common", "old_value", None),
|
||||
change!("other_only", None, "other")
|
||||
],
|
||||
}
|
||||
);
|
||||
// Field was removed both remotely and locally.
|
||||
|
@ -199,6 +331,7 @@ mod tests {
|
|||
),
|
||||
IncomingAction::Merge {
|
||||
data: map!({"new_key": "new_value"}),
|
||||
changes: changes![],
|
||||
}
|
||||
);
|
||||
Ok(())
|
||||
|
@ -211,7 +344,58 @@ mod tests {
|
|||
map!({"key1": "value1", "key2": "value2"}),
|
||||
&map!({"key1": "ignored", "key3": "ignored"})
|
||||
),
|
||||
map!({"key2": "value2"})
|
||||
(
|
||||
map!({"key2": "value2"}),
|
||||
changes![change!("key1", "value1", None)]
|
||||
)
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_synced_changes() -> Result<()> {
|
||||
let db = new_syncable_mem_db();
|
||||
db.execute_batch(&format!(
|
||||
r#"INSERT INTO temp.storage_sync_applied (ext_id, changes)
|
||||
VALUES
|
||||
('an-extension', '{change1}'),
|
||||
('ext"id', '{change2}')
|
||||
"#,
|
||||
change1 = serde_json::to_string(&changes![change!("key1", "old-val", None)])?,
|
||||
change2 = serde_json::to_string(&changes![change!("key-for-second", None, "new-val")])?
|
||||
))?;
|
||||
let changes = get_synced_changes(&db)?;
|
||||
assert_eq!(changes[0].ext_id, "an-extension");
|
||||
// sanity check it's valid!
|
||||
let c1: JsonMap =
|
||||
serde_json::from_str(&changes[0].changes).expect("changes must be an object");
|
||||
assert_eq!(
|
||||
c1.get("key1")
|
||||
.expect("must exist")
|
||||
.as_object()
|
||||
.expect("must be an object")
|
||||
.get("oldValue"),
|
||||
Some(&json!("old-val"))
|
||||
);
|
||||
|
||||
// phew - do it again to check the string got escaped.
|
||||
assert_eq!(
|
||||
changes[1],
|
||||
SyncedExtensionChange {
|
||||
ext_id: "ext\"id".into(),
|
||||
changes: r#"{"key-for-second":{"newValue":"new-val"}}"#.into(),
|
||||
}
|
||||
);
|
||||
assert_eq!(changes[1].ext_id, "ext\"id");
|
||||
let c2: JsonMap =
|
||||
serde_json::from_str(&changes[1].changes).expect("changes must be an object");
|
||||
assert_eq!(
|
||||
c2.get("key-for-second")
|
||||
.expect("must exist")
|
||||
.as_object()
|
||||
.expect("must be an object")
|
||||
.get("newValue"),
|
||||
Some(&json!("new-val"))
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -18,5 +18,5 @@ xpcom = { path = "../../../../../xpcom/rust/xpcom" }
|
|||
serde = "1"
|
||||
serde_json = "1"
|
||||
storage_variant = { path = "../../../../../storage/variant" }
|
||||
sql-support = { git = "https://github.com/mozilla/application-services", rev = "77a7f5eb12a8d93f2bd71bd4d844405e06743365" }
|
||||
webext-storage = { git = "https://github.com/mozilla/application-services", rev = "77a7f5eb12a8d93f2bd71bd4d844405e06743365" }
|
||||
sql-support = { git = "https://github.com/mozilla/application-services", rev = "57a07e89e9ac92756b60b67c4a6ee06975b86288" }
|
||||
webext-storage = { git = "https://github.com/mozilla/application-services", rev = "57a07e89e9ac92756b60b67c4a6ee06975b86288" }
|
||||
|
|
|
@ -55,7 +55,6 @@ unic-langid-ffi = { path = "../../../../intl/locale/rust/unic-langid-ffi" }
|
|||
fluent-langneg = { version = "0.12.1", features = ["cldr"] }
|
||||
fluent-langneg-ffi = { path = "../../../../intl/locale/rust/fluent-langneg-ffi" }
|
||||
|
||||
|
||||
# Note: `modern_sqlite` means rusqlite's bindings file be for a sqlite with
|
||||
# version less than or equal to what we link to. This isn't a problem because we
|
||||
# tend to keep this up to date, but it needs to be taken into consideration when
|
||||
|
@ -67,7 +66,7 @@ fluent-ffi = { path = "../../../../intl/l10n/rust/fluent-ffi" }
|
|||
firefox-accounts-bridge = { path = "../../../../services/fxaccounts/rust-bridge/firefox-accounts-bridge", optional=true }
|
||||
|
||||
[target.'cfg(not(target_os = "android"))'.dependencies]
|
||||
viaduct = { git = "https://github.com/mozilla/application-services", rev = "77a7f5eb12a8d93f2bd71bd4d844405e06743365"}
|
||||
viaduct = { git = "https://github.com/mozilla/application-services", rev = "57a07e89e9ac92756b60b67c4a6ee06975b86288"}
|
||||
webext_storage_bridge = { path = "../../../components/extensions/storage/webext_storage_bridge" }
|
||||
|
||||
[build-dependencies]
|
||||
|
|
Загрузка…
Ссылка в новой задаче