Backed out 4 changesets (bug 1883321, bug 1900537) for causing multiple failures. CLOSED TREE
Backed out changeset 7b1ab2c1bfb6 (bug 1900537) Backed out changeset 1306fe3ef6d3 (bug 1900537) Backed out changeset e83b4e090bc0 (bug 1883321) Backed out changeset b9e50942a0cd (bug 1883321)
This commit is contained in:
@@ -15,21 +15,6 @@ git = "https://github.com/FirefoxGraphics/wpf-gpu-raster"
|
||||
rev = "99979da091fd58fba8477e7fcdf5ec0727102916"
|
||||
replace-with = "vendored-sources"
|
||||
|
||||
[source."git+https://github.com/beurdouche/mls-platform-api?rev=7fb935bb93fdcc80f7f5e76d516c85a540024b53"]
|
||||
git = "https://github.com/beurdouche/mls-platform-api"
|
||||
rev = "7fb935bb93fdcc80f7f5e76d516c85a540024b53"
|
||||
replace-with = "vendored-sources"
|
||||
|
||||
[source."git+https://github.com/beurdouche/mls-rs?rev=96eb66e158c86171c70ff8147c0e5f020e54f3d1"]
|
||||
git = "https://github.com/beurdouche/mls-rs"
|
||||
rev = "96eb66e158c86171c70ff8147c0e5f020e54f3d1"
|
||||
replace-with = "vendored-sources"
|
||||
|
||||
[source."git+https://github.com/beurdouche/nss-gk-api?rev=82e780f47026b84a0e0a06bff17fa95661d129a3"]
|
||||
git = "https://github.com/beurdouche/nss-gk-api"
|
||||
rev = "82e780f47026b84a0e0a06bff17fa95661d129a3"
|
||||
replace-with = "vendored-sources"
|
||||
|
||||
[source."git+https://github.com/chris-zen/coremidi.git?rev=fc68464b5445caf111e41f643a2e69ccce0b4f83"]
|
||||
git = "https://github.com/chris-zen/coremidi.git"
|
||||
rev = "fc68464b5445caf111e41f643a2e69ccce0b4f83"
|
||||
|
||||
211
Cargo.lock
generated
211
Cargo.lock
generated
@@ -1416,15 +1416,6 @@ dependencies = [
|
||||
"libdbus-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "debug_tree"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d1ec383f2d844902d3c34e4253ba11ae48513cdaddc565cf1a6518db09a8e57"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "debugid"
|
||||
version = "0.8.0"
|
||||
@@ -2343,10 +2334,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2451,7 +2440,6 @@ dependencies = [
|
||||
"mdns_service",
|
||||
"midir_impl",
|
||||
"mime-guess-ffi",
|
||||
"mls_gk",
|
||||
"moz_asserts",
|
||||
"mozannotation_client",
|
||||
"mozannotation_server",
|
||||
@@ -3790,17 +3778,6 @@ version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5"
|
||||
|
||||
[[package]]
|
||||
name = "maybe-async"
|
||||
version = "0.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5cf92c10c7e361d6b99666ec1c6f9805b0bea2c3bd8c78dc6fe98ac5bd78db11"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "md-5"
|
||||
version = "0.10.5"
|
||||
@@ -4054,169 +4031,6 @@ dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mls-platform-api"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/beurdouche/mls-platform-api?rev=7fb935bb93fdcc80f7f5e76d516c85a540024b53#7fb935bb93fdcc80f7f5e76d516c85a540024b53"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"hex",
|
||||
"mls-rs",
|
||||
"mls-rs-crypto-nss",
|
||||
"mls-rs-provider-sqlite",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mls-rs"
|
||||
version = "0.39.1"
|
||||
source = "git+https://github.com/beurdouche/mls-rs?rev=96eb66e158c86171c70ff8147c0e5f020e54f3d1#96eb66e158c86171c70ff8147c0e5f020e54f3d1"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"cfg-if",
|
||||
"debug_tree",
|
||||
"futures",
|
||||
"getrandom",
|
||||
"hex",
|
||||
"itertools",
|
||||
"maybe-async",
|
||||
"mls-rs-codec",
|
||||
"mls-rs-core",
|
||||
"mls-rs-identity-x509",
|
||||
"mls-rs-provider-sqlite",
|
||||
"rand_core",
|
||||
"rayon",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"wasm-bindgen",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mls-rs-codec"
|
||||
version = "0.5.3"
|
||||
source = "git+https://github.com/beurdouche/mls-rs?rev=96eb66e158c86171c70ff8147c0e5f020e54f3d1#96eb66e158c86171c70ff8147c0e5f020e54f3d1"
|
||||
dependencies = [
|
||||
"mls-rs-codec-derive",
|
||||
"thiserror",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mls-rs-codec-derive"
|
||||
version = "0.1.1"
|
||||
source = "git+https://github.com/beurdouche/mls-rs?rev=96eb66e158c86171c70ff8147c0e5f020e54f3d1#96eb66e158c86171c70ff8147c0e5f020e54f3d1"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mls-rs-core"
|
||||
version = "0.18.0"
|
||||
source = "git+https://github.com/beurdouche/mls-rs?rev=96eb66e158c86171c70ff8147c0e5f020e54f3d1#96eb66e158c86171c70ff8147c0e5f020e54f3d1"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"hex",
|
||||
"maybe-async",
|
||||
"mls-rs-codec",
|
||||
"serde",
|
||||
"serde_bytes",
|
||||
"thiserror",
|
||||
"wasm-bindgen",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mls-rs-crypto-hpke"
|
||||
version = "0.9.0"
|
||||
source = "git+https://github.com/beurdouche/mls-rs?rev=96eb66e158c86171c70ff8147c0e5f020e54f3d1#96eb66e158c86171c70ff8147c0e5f020e54f3d1"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"cfg-if",
|
||||
"maybe-async",
|
||||
"mls-rs-core",
|
||||
"mls-rs-crypto-traits",
|
||||
"thiserror",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mls-rs-crypto-nss"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/beurdouche/mls-rs?rev=96eb66e158c86171c70ff8147c0e5f020e54f3d1#96eb66e158c86171c70ff8147c0e5f020e54f3d1"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"hex",
|
||||
"maybe-async",
|
||||
"mls-rs-core",
|
||||
"mls-rs-crypto-hpke",
|
||||
"mls-rs-crypto-traits",
|
||||
"nss-gk-api",
|
||||
"rand_core",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mls-rs-crypto-traits"
|
||||
version = "0.10.0"
|
||||
source = "git+https://github.com/beurdouche/mls-rs?rev=96eb66e158c86171c70ff8147c0e5f020e54f3d1#96eb66e158c86171c70ff8147c0e5f020e54f3d1"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"maybe-async",
|
||||
"mls-rs-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mls-rs-identity-x509"
|
||||
version = "0.11.0"
|
||||
source = "git+https://github.com/beurdouche/mls-rs?rev=96eb66e158c86171c70ff8147c0e5f020e54f3d1#96eb66e158c86171c70ff8147c0e5f020e54f3d1"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"maybe-async",
|
||||
"mls-rs-core",
|
||||
"thiserror",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mls-rs-provider-sqlite"
|
||||
version = "0.11.0"
|
||||
source = "git+https://github.com/beurdouche/mls-rs?rev=96eb66e158c86171c70ff8147c0e5f020e54f3d1#96eb66e158c86171c70ff8147c0e5f020e54f3d1"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"hex",
|
||||
"maybe-async",
|
||||
"mls-rs-core",
|
||||
"rand",
|
||||
"rusqlite",
|
||||
"thiserror",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mls_gk"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"hex",
|
||||
"log",
|
||||
"mls-platform-api",
|
||||
"nserror",
|
||||
"nss-gk-api",
|
||||
"nsstring",
|
||||
"rusqlite",
|
||||
"static_prefs",
|
||||
"thin-vec",
|
||||
"xpcom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "moz_asserts"
|
||||
version = "0.1.0"
|
||||
@@ -4678,10 +4492,10 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "nss-gk-api"
|
||||
version = "0.3.0"
|
||||
source = "git+https://github.com/beurdouche/nss-gk-api?rev=82e780f47026b84a0e0a06bff17fa95661d129a3#82e780f47026b84a0e0a06bff17fa95661d129a3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c17aec6d4e1822c023689899f09311592a36cbf6de8f85dfaf5f01976790d8d"
|
||||
dependencies = [
|
||||
"bindgen 0.69.4",
|
||||
"log",
|
||||
"mozbuild",
|
||||
"once_cell",
|
||||
"pkcs11-bindings",
|
||||
@@ -7731,27 +7545,6 @@ dependencies = [
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"zeroize_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize_derive"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerovec"
|
||||
version = "0.10.4"
|
||||
|
||||
@@ -14,7 +14,6 @@ members = [
|
||||
"security/manager/ssl/tests/unit/test_builtins",
|
||||
"security/manager/ssl/ipcclientcerts",
|
||||
"security/manager/ssl/osclientcerts",
|
||||
"security/mls/mls_gk",
|
||||
"testing/geckodriver",
|
||||
"toolkit/components/uniffi-bindgen-gecko-js",
|
||||
"toolkit/crashreporter/client/app",
|
||||
@@ -200,7 +199,6 @@ plist = { path = "third_party/rust/plist" }
|
||||
|
||||
# To-be-published changes.
|
||||
unicode-bidi = { git = "https://github.com/servo/unicode-bidi", rev = "ca612daf1c08c53abe07327cb3e6ef6e0a760f0c" }
|
||||
nss-gk-api = { git = "https://github.com/beurdouche/nss-gk-api", rev = "82e780f47026b84a0e0a06bff17fa95661d129a3"}
|
||||
|
||||
# Other overrides
|
||||
any_all_workaround = { git = "https://github.com/hsivonen/any_all_workaround", rev = "7fb1b7034c9f172aade21ee1c8554e8d8a48af80" }
|
||||
|
||||
@@ -45,7 +45,6 @@
|
||||
#include "mozilla/dom/Performance.h"
|
||||
#include "mozilla/dom/PopupBlocker.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/quota/QuotaManager.h"
|
||||
#include "mozilla/dom/Record.h"
|
||||
#include "mozilla/dom/ReportingHeader.h"
|
||||
#include "mozilla/dom/UnionTypes.h"
|
||||
@@ -60,7 +59,6 @@
|
||||
#include "mozilla/RemoteDecoderManagerChild.h"
|
||||
#include "mozilla/KeySystemConfig.h"
|
||||
#include "mozilla/WheelHandlingHelper.h"
|
||||
#include "nsString.h"
|
||||
#include "nsNativeTheme.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "mozJSModuleLoader.h"
|
||||
@@ -88,9 +86,6 @@
|
||||
|
||||
namespace mozilla::dom {
|
||||
|
||||
// Setup logging
|
||||
extern mozilla::LazyLogModule gMlsLog;
|
||||
|
||||
/* static */
|
||||
void ChromeUtils::NondeterministicGetWeakMapKeys(
|
||||
GlobalObject& aGlobal, JS::Handle<JS::Value> aMap,
|
||||
@@ -1422,279 +1417,6 @@ void ChromeUtils::ClearStyleSheetCache(GlobalObject&) {
|
||||
SharedStyleSheetCache::Clear();
|
||||
}
|
||||
|
||||
void ChromeUtils::ClearMessagingLayerSecurityStateByPrincipal(
|
||||
GlobalObject&, nsIPrincipal* aPrincipal, ErrorResult& aRv) {
|
||||
MOZ_LOG(gMlsLog, LogLevel::Debug,
|
||||
("ClearMessagingLayerSecurityStateByPrincipal"));
|
||||
|
||||
if (NS_WARN_IF(!aPrincipal)) {
|
||||
MOZ_LOG(gMlsLog, LogLevel::Error, ("Principal is null"));
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the storage origin key
|
||||
nsAutoCString originKey;
|
||||
aRv = aPrincipal->GetStorageOriginKey(originKey);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
MOZ_LOG(gMlsLog, LogLevel::Error, ("Failed to get storage origin key"));
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the origin attributes suffix
|
||||
nsAutoCString originAttrSuffix;
|
||||
aRv = aPrincipal->GetOriginSuffix(originAttrSuffix);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
MOZ_LOG(gMlsLog, LogLevel::Error,
|
||||
("Failed to get origin attributes suffix"));
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
// Construct the full origin key
|
||||
nsAutoCString fullOriginKey = originKey + originAttrSuffix;
|
||||
|
||||
// Get the profile directory
|
||||
nsCOMPtr<nsIFile> file;
|
||||
aRv = NS_GetSpecialDirectory("ProfD", getter_AddRefs(file));
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
MOZ_LOG(gMlsLog, LogLevel::Error, ("Failed to get profile directory"));
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
aRv = file->AppendNative("mls"_ns);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
MOZ_LOG(gMlsLog, LogLevel::Error,
|
||||
("Failed to append 'mls' to directory path"));
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
aRv = file->AppendNative(fullOriginKey);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
MOZ_LOG(gMlsLog, LogLevel::Error,
|
||||
("Failed to append full origin key to the file path"));
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove the directory recursively
|
||||
aRv = file->Remove(/* recursive */ true);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
MOZ_LOG(gMlsLog, LogLevel::Error,
|
||||
("Failed to remove : %s", file->HumanReadablePath().get()));
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_LOG(gMlsLog, LogLevel::Debug,
|
||||
("Successfully cleared MLS state for principal"));
|
||||
}
|
||||
|
||||
void ChromeUtils::ClearMessagingLayerSecurityStateBySite(
|
||||
GlobalObject&, const nsACString& aSchemelessSite,
|
||||
const dom::OriginAttributesPatternDictionary& aPattern, ErrorResult& aRv) {
|
||||
MOZ_LOG(gMlsLog, LogLevel::Debug, ("ClearMessagingLayerSecurityStateBySite"));
|
||||
|
||||
// Check if the schemeless site is empty
|
||||
if (NS_WARN_IF(aSchemelessSite.IsEmpty())) {
|
||||
MOZ_LOG(gMlsLog, LogLevel::Error, ("Schemeless site is empty"));
|
||||
aRv.Throw(NS_ERROR_INVALID_ARG);
|
||||
return;
|
||||
}
|
||||
|
||||
// Site pattern
|
||||
OriginAttributesPattern pattern(aPattern);
|
||||
|
||||
// Partition pattern
|
||||
// This pattern is used to (additionally) clear state partitioned under
|
||||
// aSchemelessSite.
|
||||
OriginAttributesPattern partitionPattern = pattern;
|
||||
partitionPattern.mPartitionKeyPattern.Construct();
|
||||
partitionPattern.mPartitionKeyPattern.Value().mBaseDomain.Construct(
|
||||
NS_ConvertUTF8toUTF16(aSchemelessSite));
|
||||
|
||||
// Get the profile directory
|
||||
nsCOMPtr<nsIFile> profileDir;
|
||||
aRv = NS_GetSpecialDirectory("ProfD", getter_AddRefs(profileDir));
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
MOZ_LOG(gMlsLog, LogLevel::Error, ("Failed to get profile directory"));
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the 'mls' directory
|
||||
nsCOMPtr<nsIFile> mlsDir;
|
||||
aRv = profileDir->Clone(getter_AddRefs(mlsDir));
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
MOZ_LOG(gMlsLog, LogLevel::Error, ("Failed to clone profile directory"));
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
aRv = mlsDir->AppendNative("mls"_ns);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
MOZ_LOG(gMlsLog, LogLevel::Error,
|
||||
("Failed to append 'mls' to directory path"));
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
// Reverse the base domain using the existing function
|
||||
nsAutoCString targetReversedBaseDomain(aSchemelessSite);
|
||||
std::reverse(targetReversedBaseDomain.BeginWriting(),
|
||||
targetReversedBaseDomain.EndWriting());
|
||||
|
||||
MOZ_LOG(gMlsLog, LogLevel::Debug,
|
||||
("Reversed base domain: %s", targetReversedBaseDomain.get()));
|
||||
|
||||
// Enumerate files in the 'mls' directory
|
||||
nsCOMPtr<nsIDirectoryEnumerator> dirEnum;
|
||||
aRv = mlsDir->GetDirectoryEntries(getter_AddRefs(dirEnum));
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
MOZ_LOG(gMlsLog, LogLevel::Error,
|
||||
("Failed to get directory entries in 'mls' directory"));
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
// Iterate through all entries in the directory
|
||||
nsCOMPtr<nsIFile> entry;
|
||||
while (NS_SUCCEEDED(dirEnum->GetNextFile(getter_AddRefs(entry))) && entry) {
|
||||
nsAutoCString entryName;
|
||||
aRv = entry->GetNativeLeafName(entryName);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
MOZ_LOG(gMlsLog, LogLevel::Error,
|
||||
("Failed to get native leaf name for entry"));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find the position of .sqlite.enc or .key in the entry name
|
||||
int32_t sqliteEncPos = entryName.RFind(".sqlite.enc");
|
||||
int32_t keyPos = entryName.RFind(".key");
|
||||
|
||||
// Remove the .sqlite.enc or .key suffix from the entryName
|
||||
if (sqliteEncPos != kNotFound) {
|
||||
entryName.SetLength(sqliteEncPos);
|
||||
} else if (keyPos != kNotFound) {
|
||||
entryName.SetLength(keyPos);
|
||||
}
|
||||
|
||||
// Decode the entry name
|
||||
nsAutoCString decodedEntryName;
|
||||
aRv = mozilla::Base64Decode(entryName, decodedEntryName);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
MOZ_LOG(gMlsLog, LogLevel::Debug,
|
||||
("Failed to decode entry name: %s", entryName.get()));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find the origin attributes suffix in the entry name by taking the
|
||||
// value of the entry name after the ^ separator
|
||||
int32_t separatorPos = decodedEntryName.FindChar('^');
|
||||
|
||||
// We extract the origin attributes suffix from the entry name
|
||||
nsAutoCString originSuffix;
|
||||
originSuffix.Assign(Substring(decodedEntryName, separatorPos));
|
||||
|
||||
// Populate the origin attributes from the suffix
|
||||
OriginAttributes originAttrs;
|
||||
if (NS_WARN_IF(!originAttrs.PopulateFromSuffix(originSuffix))) {
|
||||
MOZ_LOG(gMlsLog, LogLevel::Error,
|
||||
("Failed to populate origin attributes from suffix"));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if the entry name starts with the reversed base domain
|
||||
if (StringBeginsWith(decodedEntryName, targetReversedBaseDomain)) {
|
||||
MOZ_LOG(gMlsLog, LogLevel::Debug,
|
||||
("Entry file: %s", entry->HumanReadablePath().get()));
|
||||
|
||||
// If there is a valid origin attributes suffix, we remove the entry
|
||||
// only if it matches.
|
||||
if (pattern.Matches(originAttrs)) {
|
||||
aRv = entry->Remove(/* recursive */ false);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
MOZ_LOG(gMlsLog, LogLevel::Error,
|
||||
("Failed to remove file: %s", decodedEntryName.get()));
|
||||
}
|
||||
MOZ_LOG(gMlsLog, LogLevel::Debug,
|
||||
("Removed file: %s", decodedEntryName.get()));
|
||||
}
|
||||
}
|
||||
|
||||
// If there is a valid origin attributes suffix, we remove the entry
|
||||
// only if it matches. We are checking for state partitioned under
|
||||
// aSchemelessSite.
|
||||
if (partitionPattern.Matches(originAttrs)) {
|
||||
aRv = entry->Remove(/* recursive */ false);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
MOZ_LOG(gMlsLog, LogLevel::Error,
|
||||
("Failed to remove file: %s", decodedEntryName.get()));
|
||||
}
|
||||
MOZ_LOG(gMlsLog, LogLevel::Debug,
|
||||
("Removed file: %s", decodedEntryName.get()));
|
||||
}
|
||||
}
|
||||
|
||||
// Close the directory enumerator
|
||||
dirEnum->Close();
|
||||
}
|
||||
|
||||
void ChromeUtils::ClearMessagingLayerSecurityState(GlobalObject&,
|
||||
ErrorResult& aRv) {
|
||||
MOZ_LOG(gMlsLog, LogLevel::Debug, ("ClearMessagingLayerSecurityState"));
|
||||
|
||||
// Get the profile directory
|
||||
nsCOMPtr<nsIFile> profileDir;
|
||||
aRv = NS_GetSpecialDirectory("ProfD", getter_AddRefs(profileDir));
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
MOZ_LOG(gMlsLog, LogLevel::Error, ("Failed to get profile directory"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Construct the MLS directory path
|
||||
nsCOMPtr<nsIFile> mlsDir;
|
||||
aRv = profileDir->Clone(getter_AddRefs(mlsDir));
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
MOZ_LOG(gMlsLog, LogLevel::Error, ("Failed to clone profile directory"));
|
||||
return;
|
||||
}
|
||||
aRv = mlsDir->AppendNative("mls"_ns);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
MOZ_LOG(gMlsLog, LogLevel::Error,
|
||||
("Failed to append 'mls' to directory path"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the directory exists
|
||||
bool exists;
|
||||
aRv = mlsDir->Exists(&exists);
|
||||
if (NS_WARN_IF(aRv.Failed() || !exists)) {
|
||||
MOZ_LOG(gMlsLog, LogLevel::Debug, ("MLS directory does not exist"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove the MLS directory recursively
|
||||
aRv = mlsDir->Remove(/* recursive */ true);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
MOZ_LOG(gMlsLog, LogLevel::Error, ("Failed to remove MLS directory"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Log the directory path
|
||||
MOZ_LOG(gMlsLog, LogLevel::Debug,
|
||||
("Deleted MLS directory: %s", mlsDir->HumanReadablePath().get()));
|
||||
|
||||
// Recreate the MLS directory
|
||||
aRv = mlsDir->Create(nsIFile::DIRECTORY_TYPE, 0755);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
MOZ_LOG(gMlsLog, LogLevel::Error, ("Failed to recreate MLS directory"));
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_LOG(gMlsLog, LogLevel::Debug, ("Successfully cleared all MLS state"));
|
||||
}
|
||||
|
||||
void ChromeUtils::ClearScriptCacheByPrincipal(GlobalObject&,
|
||||
nsIPrincipal* aForPrincipal) {
|
||||
SharedScriptCache::Clear(Some(aForPrincipal));
|
||||
|
||||
@@ -193,16 +193,6 @@ class ChromeUtils {
|
||||
|
||||
static void ClearStyleSheetCache(GlobalObject& aGlobal);
|
||||
|
||||
static void ClearMessagingLayerSecurityStateByPrincipal(
|
||||
GlobalObject&, nsIPrincipal* aPrincipal, ErrorResult& aRv);
|
||||
|
||||
static void ClearMessagingLayerSecurityStateBySite(
|
||||
GlobalObject& aGlobal, const nsACString& aSchemelessSite,
|
||||
const dom::OriginAttributesPatternDictionary& aPattern, ErrorResult& aRv);
|
||||
|
||||
static void ClearMessagingLayerSecurityState(GlobalObject& aGlobal,
|
||||
ErrorResult& aRv);
|
||||
|
||||
static void ClearScriptCacheByPrincipal(GlobalObject&,
|
||||
nsIPrincipal* aForPrincipal);
|
||||
|
||||
|
||||
@@ -137,24 +137,6 @@ custom GetUserMediaInsec calls MediaDevices.getUserMedia from an insecure contex
|
||||
custom MozGetUserMediaInsec calls Navigator.mozGetUserMedia from an insecure context
|
||||
method MediaDevices.getDisplayMedia
|
||||
|
||||
// Non-standard Messaging Layer Security
|
||||
method MLS.stateDelete
|
||||
method MLS.stateDeleteGroup
|
||||
method MLS.generateSignatureKeypair
|
||||
method MLS.generateCredentialBasic
|
||||
method MLS.generateKeyPackage
|
||||
method MLS.groupCreate
|
||||
method MLS.groupJoin
|
||||
method MLS.groupAdd
|
||||
method MLS.groupProposeAdd
|
||||
method MLS.groupRemove
|
||||
method MLS.groupProposeRemove
|
||||
method MLS.groupClose
|
||||
method MLS.groupMembers
|
||||
method MLS.receive
|
||||
method MLS.send
|
||||
method MLS.deriveExporter
|
||||
|
||||
// Non-standard Document.mozSetImageElement.
|
||||
method Document.mozSetImageElement
|
||||
|
||||
|
||||
@@ -65,24 +65,6 @@ method console.timeStamp
|
||||
method console.profile
|
||||
method console.profileEnd
|
||||
|
||||
// Non-standard Messaging Layer Security
|
||||
method MLS.stateDelete
|
||||
method MLS.stateDeleteGroup
|
||||
method MLS.generateSignatureKeypair
|
||||
method MLS.generateCredentialBasic
|
||||
method MLS.generateKeyPackage
|
||||
method MLS.groupCreate
|
||||
method MLS.groupJoin
|
||||
method MLS.groupAdd
|
||||
method MLS.groupProposeAdd
|
||||
method MLS.groupRemove
|
||||
method MLS.groupProposeRemove
|
||||
method MLS.groupClose
|
||||
method MLS.groupMembers
|
||||
method MLS.receive
|
||||
method MLS.send
|
||||
method MLS.deriveExporter
|
||||
|
||||
// Unsupported web APIs in Private Browsing Mode
|
||||
custom PrivateBrowsingIDBFactoryOpen calls indexedDB.open in Private Browsing Mode
|
||||
custom PrivateBrowsingIDBFactoryDeleteDatabase calls indexedDB.deleteDatabase in Private Browsing Mode
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -243,25 +243,6 @@ namespace ChromeUtils {
|
||||
*/
|
||||
undefined clearStyleSheetCache();
|
||||
|
||||
/**
|
||||
* Clears the Messaging Layer Security state by schemeless site.
|
||||
* This includes associated state-partitioned cache.
|
||||
*/
|
||||
[Throws]
|
||||
undefined clearMessagingLayerSecurityStateBySite(UTF8String schemelessSite, optional OriginAttributesPatternDictionary pattern = {});
|
||||
|
||||
/**
|
||||
* Clears the Messaging Layer Security state by principal.
|
||||
*/
|
||||
[Throws]
|
||||
undefined clearMessagingLayerSecurityStateByPrincipal(Principal principal);
|
||||
|
||||
/**
|
||||
* Clears all Messaging Layer Security related state across domains
|
||||
*/
|
||||
[Throws]
|
||||
undefined clearMessagingLayerSecurityState();
|
||||
|
||||
/**
|
||||
* Clears the JavaScript cache by schemeless site. This includes associated
|
||||
* state-partitioned cache.
|
||||
|
||||
635
dom/mls/MLS.cpp
635
dom/mls/MLS.cpp
@@ -1,635 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#include "mozilla/dom/MLS.h"
|
||||
#include "mozilla/dom/MLSGroupView.h"
|
||||
#include "mozilla/dom/TypedArray.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIGlobalObject.h"
|
||||
#include "mozilla/ipc/PBackgroundChild.h"
|
||||
#include "mozilla/ipc/BackgroundChild.h"
|
||||
#include "mozilla/dom/MLSTransactionChild.h"
|
||||
#include "mozilla/dom/MLSTransactionMessage.h"
|
||||
#include "mozilla/dom/PMLSTransaction.h"
|
||||
#include "mozilla/ipc/Endpoint.h"
|
||||
#include "mozilla/BasePrincipal.h"
|
||||
#include "MLSGroupView.h"
|
||||
#include "nsTArray.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "mozilla/Span.h"
|
||||
#include "nsDebug.h"
|
||||
#include "MLSLogging.h"
|
||||
#include "MLSTypeUtils.h"
|
||||
|
||||
namespace mozilla::dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(MLS, mGlobalObject)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(MLS)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(MLS)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MLS)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
// Setup logging
|
||||
mozilla::LazyLogModule gMlsLog("MLS");
|
||||
|
||||
/* static */ already_AddRefed<MLS> MLS::Constructor(GlobalObject& aGlobalObject,
|
||||
ErrorResult& aRv) {
|
||||
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, ("MLS::Constructor()"));
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> global(
|
||||
do_QueryInterface(aGlobalObject.GetAsSupports()));
|
||||
if (NS_WARN_IF(!global)) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Get the principal and perform some validation on it.
|
||||
// We do not allow MLS in Private Browsing Mode for now.
|
||||
nsIPrincipal* principal = global->PrincipalOrNull();
|
||||
if (!principal || !principal->GetIsContentPrincipal() ||
|
||||
principal->GetIsInPrivateBrowsing()) {
|
||||
aRv.ThrowSecurityError("Cannot create MLS store for origin");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Create the endpoints for the MLS actor
|
||||
mozilla::ipc::Endpoint<PMLSTransactionParent> parentEndpoint;
|
||||
mozilla::ipc::Endpoint<PMLSTransactionChild> childEndpoint;
|
||||
MOZ_ALWAYS_SUCCEEDS(
|
||||
PMLSTransaction::CreateEndpoints(&parentEndpoint, &childEndpoint));
|
||||
|
||||
mozilla::ipc::PBackgroundChild* backgroundChild =
|
||||
mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
|
||||
if (!backgroundChild) {
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Bind the child actor, and send the parent endpoint.
|
||||
RefPtr<MLSTransactionChild> actor = new MLSTransactionChild();
|
||||
MOZ_ALWAYS_TRUE(childEndpoint.Bind(actor));
|
||||
|
||||
MOZ_ALWAYS_TRUE(backgroundChild->SendCreateMLSTransaction(
|
||||
std::move(parentEndpoint), WrapNotNull(principal)));
|
||||
|
||||
return MakeAndAddRef<MLS>(global, actor);
|
||||
}
|
||||
|
||||
MLS::MLS(nsIGlobalObject* aGlobalObject, MLSTransactionChild* aActor)
|
||||
: mGlobalObject(aGlobalObject), mTransactionChild(aActor) {
|
||||
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, ("MLS::MLS()"));
|
||||
}
|
||||
|
||||
MLS::~MLS() {
|
||||
if (mTransactionChild) {
|
||||
mTransactionChild->Close();
|
||||
}
|
||||
}
|
||||
|
||||
JSObject* MLS::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
|
||||
return MLS_Binding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
//
|
||||
// API
|
||||
//
|
||||
|
||||
already_AddRefed<Promise> MLS::DeleteState(ErrorResult& aRv) {
|
||||
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, ("MLS::DeleteState()"));
|
||||
|
||||
// Create a new Promise object for the result
|
||||
RefPtr<Promise> promise = Promise::Create(mGlobalObject, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mTransactionChild->SendRequestStateDelete(
|
||||
[promise](bool result) {
|
||||
if (result) {
|
||||
promise->MaybeResolveWithUndefined();
|
||||
} else {
|
||||
promise->MaybeReject(NS_ERROR_FAILURE);
|
||||
}
|
||||
},
|
||||
[promise](::mozilla::ipc::ResponseRejectReason) {
|
||||
promise->MaybeRejectWithUnknownError("deleteState failed");
|
||||
});
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<Promise> MLS::GenerateIdentity(ErrorResult& aRv) {
|
||||
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, ("MLS::GenerateIdentity()"));
|
||||
|
||||
// Create a new Promise object for the result
|
||||
RefPtr<Promise> promise = Promise::Create(mGlobalObject, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mTransactionChild->SendRequestGenerateIdentityKeypair()->Then(
|
||||
GetCurrentSerialEventTarget(), __func__,
|
||||
[promise, self = RefPtr{this}](Maybe<RawBytes>&& result) {
|
||||
// Check if the value is Nothing
|
||||
if (result.isNothing()) {
|
||||
promise->MaybeRejectWithUnknownError(
|
||||
"generateIdentityKeypair failed");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the context from the GlobalObject
|
||||
AutoJSAPI jsapi;
|
||||
if (NS_WARN_IF(!jsapi.Init(self->mGlobalObject))) {
|
||||
promise->MaybeRejectWithUnknownError(
|
||||
"generateIdentityKeypair failed");
|
||||
return;
|
||||
}
|
||||
JSContext* cx = jsapi.cx();
|
||||
|
||||
// Construct the Uint8Array object
|
||||
ErrorResult error;
|
||||
JS::Rooted<JSObject*> content(
|
||||
cx, Uint8Array::Create(cx, result->data(), error));
|
||||
error.WouldReportJSException();
|
||||
if (error.Failed()) {
|
||||
promise->MaybeReject(std::move(error));
|
||||
return;
|
||||
}
|
||||
|
||||
// Construct MLSBytes with the client identifer as content
|
||||
RootedDictionary<MLSBytes> rvalue(cx);
|
||||
rvalue.mType = MLSObjectType::Client_identifier;
|
||||
rvalue.mContent.Init(content);
|
||||
|
||||
// Resolve the promise with the MLSBytes object
|
||||
promise->MaybeResolve(rvalue);
|
||||
},
|
||||
[promise](::mozilla::ipc::ResponseRejectReason aReason) {
|
||||
promise->MaybeRejectWithUnknownError("generateIdentity failed");
|
||||
});
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<Promise> MLS::GenerateCredential(
|
||||
const MLSBytesOrUint8ArrayOrUTF8String& aJsCredContent, ErrorResult& aRv) {
|
||||
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug,
|
||||
("MLS::GenerateCredentialBasic()"));
|
||||
|
||||
// Handle the credential content parameter
|
||||
nsTArray<uint8_t> credContent = ExtractMLSBytesOrUint8ArrayOrUTF8String(
|
||||
MLSObjectType::Credential_basic, aJsCredContent, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Check if the credContent is empty
|
||||
if (NS_WARN_IF(credContent.IsEmpty())) {
|
||||
aRv.ThrowTypeError("The credential content must not be empty");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Create a new Promise object for the result
|
||||
RefPtr<Promise> promise = Promise::Create(mGlobalObject, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mTransactionChild->SendRequestGenerateCredentialBasic(credContent)
|
||||
->Then(
|
||||
GetCurrentSerialEventTarget(), __func__,
|
||||
[promise, self = RefPtr{this}](Maybe<RawBytes>&& result) {
|
||||
// Check if the value is Nothing
|
||||
if (result.isNothing()) {
|
||||
promise->MaybeRejectWithUnknownError(
|
||||
"generateCredentialBasic failed");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the context from the GlobalObject
|
||||
AutoJSAPI jsapi;
|
||||
if (NS_WARN_IF(!jsapi.Init(self->mGlobalObject))) {
|
||||
promise->MaybeRejectWithUnknownError(
|
||||
"generateCredentialBasic failed");
|
||||
return;
|
||||
}
|
||||
JSContext* cx = jsapi.cx();
|
||||
|
||||
// Construct the Uint8Array object
|
||||
ErrorResult error;
|
||||
JS::Rooted<JSObject*> content(
|
||||
cx, Uint8Array::Create(cx, result->data(), error));
|
||||
error.WouldReportJSException();
|
||||
if (error.Failed()) {
|
||||
promise->MaybeReject(std::move(error));
|
||||
return;
|
||||
}
|
||||
|
||||
// Construct MLSBytes with the client identifer as content
|
||||
RootedDictionary<MLSBytes> rvalue(cx);
|
||||
rvalue.mType = MLSObjectType::Credential_basic;
|
||||
rvalue.mContent.Init(content);
|
||||
|
||||
// Resolve the promise
|
||||
promise->MaybeResolve(rvalue);
|
||||
},
|
||||
[promise](::mozilla::ipc::ResponseRejectReason aReason) {
|
||||
promise->MaybeRejectWithUnknownError(
|
||||
"generateCredentialBasic failed");
|
||||
});
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<Promise> MLS::GenerateKeyPackage(
|
||||
const MLSBytesOrUint8Array& aJsClientIdentifier,
|
||||
const MLSBytesOrUint8Array& aJsCredential, ErrorResult& aRv) {
|
||||
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, ("MLS::GenerateKeyPackage()"));
|
||||
|
||||
// Handle the client identifier parameter
|
||||
nsTArray<uint8_t> clientIdentifier = ExtractMLSBytesOrUint8Array(
|
||||
MLSObjectType::Client_identifier, aJsClientIdentifier, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Check if the client identifier is empty
|
||||
if (NS_WARN_IF(clientIdentifier.IsEmpty())) {
|
||||
aRv.ThrowTypeError("The client identifier must not be empty");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Handle the credential parameter
|
||||
nsTArray<uint8_t> credential = ExtractMLSBytesOrUint8Array(
|
||||
MLSObjectType::Credential_basic, aJsCredential, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Check if the credential is empty
|
||||
if (NS_WARN_IF(credential.IsEmpty())) {
|
||||
aRv.ThrowTypeError("The credential must not be empty");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Create a new Promise object for the result
|
||||
RefPtr<Promise> promise = Promise::Create(mGlobalObject, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Use the static method or instance to send the IPC message
|
||||
mTransactionChild->SendRequestGenerateKeyPackage(clientIdentifier, credential)
|
||||
->Then(
|
||||
GetCurrentSerialEventTarget(), __func__,
|
||||
[promise, self = RefPtr{this}](Maybe<RawBytes>&& keyPackage) {
|
||||
// Check if the value is Nothing
|
||||
if (keyPackage.isNothing()) {
|
||||
promise->MaybeReject(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the context from the GlobalObject
|
||||
AutoJSAPI jsapi;
|
||||
if (NS_WARN_IF(!jsapi.Init(self->mGlobalObject))) {
|
||||
promise->MaybeReject(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
JSContext* cx = jsapi.cx();
|
||||
|
||||
// Construct the Uint8Array object
|
||||
ErrorResult error;
|
||||
JS::Rooted<JSObject*> content(
|
||||
cx, Uint8Array::Create(cx, keyPackage->data(), error));
|
||||
error.WouldReportJSException();
|
||||
if (error.Failed()) {
|
||||
promise->MaybeReject(std::move(error));
|
||||
return;
|
||||
}
|
||||
|
||||
// Construct MLSBytes with the client identifer as content
|
||||
RootedDictionary<MLSBytes> rvalue(cx);
|
||||
rvalue.mType = MLSObjectType::Key_package;
|
||||
rvalue.mContent.Init(content);
|
||||
|
||||
// Resolve the promise
|
||||
promise->MaybeResolve(rvalue);
|
||||
},
|
||||
[promise](::mozilla::ipc::ResponseRejectReason aReason) {
|
||||
promise->MaybeRejectWithUnknownError("generateKeyPackage failed");
|
||||
});
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<Promise> MLS::GroupCreate(
|
||||
const MLSBytesOrUint8Array& aJsClientIdentifier,
|
||||
const MLSBytesOrUint8Array& aJsCredential, ErrorResult& aRv) {
|
||||
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, ("MLS::GroupCreate()"));
|
||||
|
||||
// Handle the client identifier parameter
|
||||
nsTArray<uint8_t> clientIdentifier = ExtractMLSBytesOrUint8Array(
|
||||
MLSObjectType::Client_identifier, aJsClientIdentifier, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Check if the client identifier is empty
|
||||
if (NS_WARN_IF(clientIdentifier.IsEmpty())) {
|
||||
aRv.ThrowTypeError("The client identifier must not be empty");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Handle the credential parameter
|
||||
nsTArray<uint8_t> credential = ExtractMLSBytesOrUint8Array(
|
||||
MLSObjectType::Credential_basic, aJsCredential, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Check if the credential is empty
|
||||
if (NS_WARN_IF(credential.IsEmpty())) {
|
||||
aRv.ThrowTypeError("The credential must not be empty");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Log the hex of clientIdentifier
|
||||
if (MOZ_LOG_TEST(gMlsLog, LogLevel::Debug)) {
|
||||
nsAutoCString clientIdHex;
|
||||
for (uint8_t byte : clientIdentifier) {
|
||||
clientIdHex.AppendPrintf("%02X", byte);
|
||||
}
|
||||
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug,
|
||||
("clientIdentifier in hex: %s\n", clientIdHex.get()));
|
||||
}
|
||||
|
||||
// Initialize jsGroupIdentifier to one byte of value 0xFF.
|
||||
// We do not want to allow choosing the GID at this point.
|
||||
// This value not being of the correct length will be discarded
|
||||
// internally and a fresh GID will be generated.
|
||||
//
|
||||
// In the future, the caller will allow choosing the GID.
|
||||
AutoTArray<uint8_t, 1> groupIdentifier{0xFF};
|
||||
|
||||
// Create a new Promise object for the result
|
||||
RefPtr<Promise> promise = Promise::Create(mGlobalObject, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Use the static method or instance to send the IPC message
|
||||
mTransactionChild
|
||||
->SendRequestGroupCreate(clientIdentifier, credential, groupIdentifier)
|
||||
->Then(
|
||||
GetCurrentSerialEventTarget(), __func__,
|
||||
[promise, self = RefPtr{this},
|
||||
clientIdentifier(std::move(clientIdentifier))](
|
||||
Maybe<mozilla::security::mls::GkGroupIdEpoch>&&
|
||||
groupIdEpoch) mutable {
|
||||
// Check if the value is Nothing
|
||||
if (groupIdEpoch.isNothing()) {
|
||||
promise->MaybeReject(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<MLSGroupView> group =
|
||||
new MLSGroupView(self, std::move(groupIdEpoch->group_id),
|
||||
std::move(clientIdentifier));
|
||||
|
||||
promise->MaybeResolve(group);
|
||||
},
|
||||
[promise](::mozilla::ipc::ResponseRejectReason aReason) {
|
||||
MOZ_LOG(gMlsLog, mozilla::LogLevel::Error,
|
||||
("IPC message rejected with reason: %d",
|
||||
static_cast<int>(aReason)));
|
||||
promise->MaybeRejectWithUnknownError("groupCreate failed");
|
||||
});
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<mozilla::dom::Promise> MLS::GroupGet(
|
||||
const MLSBytesOrUint8Array& aJsGroupIdentifier,
|
||||
const MLSBytesOrUint8Array& aJsClientIdentifier, ErrorResult& aRv) {
|
||||
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, ("MLS::GroupGet()"));
|
||||
|
||||
// Handle the group identifier parameter
|
||||
nsTArray<uint8_t> groupIdentifier = ExtractMLSBytesOrUint8Array(
|
||||
MLSObjectType::Group_identifier, aJsGroupIdentifier, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Check if the group identifier is empty
|
||||
if (NS_WARN_IF(groupIdentifier.IsEmpty())) {
|
||||
aRv.ThrowTypeError("The group identifier must not be empty");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Handle the client identifier parameter
|
||||
nsTArray<uint8_t> clientIdentifier = ExtractMLSBytesOrUint8Array(
|
||||
MLSObjectType::Client_identifier, aJsClientIdentifier, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Check if the client identifier is empty
|
||||
if (NS_WARN_IF(clientIdentifier.IsEmpty())) {
|
||||
aRv.ThrowTypeError("The client identifier must not be empty");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Create a new Promise object for the result
|
||||
RefPtr<Promise> promise = Promise::Create(mGlobalObject, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Initialize label, context and len
|
||||
// We pass this through IPC to be able to reuse the same code for different
|
||||
// labels in the future
|
||||
AutoTArray<uint8_t, 7> label{'l', 'i', 'v', 'e', 'n', 'e', 's', 's'};
|
||||
AutoTArray<uint8_t, 1> context{0x00};
|
||||
uint64_t len = 32;
|
||||
|
||||
// Use the static method or instance to send the IPC message
|
||||
mTransactionChild
|
||||
->SendRequestExportSecret(groupIdentifier, clientIdentifier, label,
|
||||
context, len)
|
||||
->Then(
|
||||
GetCurrentSerialEventTarget(), __func__,
|
||||
[promise, self = RefPtr{this},
|
||||
groupIdentifier(std::move(groupIdentifier)),
|
||||
clientIdentifier(std::move(clientIdentifier))](
|
||||
Maybe<mozilla::security::mls::GkExporterOutput>&&
|
||||
exporterOutput) mutable {
|
||||
// Check if the exporterOutput contains a value
|
||||
if (exporterOutput.isNothing()) {
|
||||
promise->MaybeReject(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<MLSGroupView> group =
|
||||
new MLSGroupView(self, std::move(exporterOutput->group_id),
|
||||
std::move(clientIdentifier));
|
||||
promise->MaybeResolve(group);
|
||||
},
|
||||
[promise](::mozilla::ipc::ResponseRejectReason aReason) {
|
||||
promise->MaybeRejectWithUnknownError("exportSecret failed");
|
||||
});
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<Promise> MLS::GroupJoin(
|
||||
const MLSBytesOrUint8Array& aJsClientIdentifier,
|
||||
const MLSBytesOrUint8Array& aJsWelcome, ErrorResult& aRv) {
|
||||
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, ("MLS::GroupJoin()"));
|
||||
|
||||
// Handle the client identifier parameter
|
||||
nsTArray<uint8_t> clientIdentifier = ExtractMLSBytesOrUint8Array(
|
||||
MLSObjectType::Client_identifier, aJsClientIdentifier, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Check if the client identifier is empty
|
||||
if (NS_WARN_IF(clientIdentifier.IsEmpty())) {
|
||||
aRv.ThrowTypeError("The client identifier must not be empty");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Handle the welcome parameter
|
||||
nsTArray<uint8_t> welcome =
|
||||
ExtractMLSBytesOrUint8Array(MLSObjectType::Welcome, aJsWelcome, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Check if the welcome is empty
|
||||
if (NS_WARN_IF(welcome.IsEmpty())) {
|
||||
aRv.ThrowTypeError("The welcome must not be empty");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Create a new Promise object for the result
|
||||
RefPtr<Promise> promise = Promise::Create(mGlobalObject, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mTransactionChild->SendRequestGroupJoin(clientIdentifier, welcome)
|
||||
->Then(
|
||||
GetCurrentSerialEventTarget(), __func__,
|
||||
[promise, self = RefPtr{this},
|
||||
clientIdentifier(std::move(clientIdentifier))](
|
||||
Maybe<mozilla::security::mls::GkGroupIdEpoch>&&
|
||||
groupIdEpoch) mutable {
|
||||
// Check if the value is Nothing
|
||||
if (groupIdEpoch.isNothing()) {
|
||||
promise->MaybeReject(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
// Returns groupId and epoch
|
||||
RefPtr<MLSGroupView> group =
|
||||
new MLSGroupView(self, std::move(groupIdEpoch->group_id),
|
||||
std::move(clientIdentifier));
|
||||
|
||||
// Resolve the promise
|
||||
promise->MaybeResolve(group);
|
||||
},
|
||||
[promise](::mozilla::ipc::ResponseRejectReason aReason) {
|
||||
promise->MaybeRejectWithUnknownError("groupJoin failed");
|
||||
});
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<Promise> MLS::GetGroupIdFromMessage(
|
||||
const MLSBytesOrUint8Array& aJsMessage, ErrorResult& aRv) {
|
||||
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, ("MLS::GetGroupIdFromMessage()"));
|
||||
|
||||
// Handle the message parameter
|
||||
nsTArray<uint8_t> message =
|
||||
ExtractMLSBytesOrUint8ArrayWithUnknownType(aJsMessage, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Check if the message is empty
|
||||
if (NS_WARN_IF(message.IsEmpty())) {
|
||||
aRv.ThrowTypeError("The message must not be empty");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Create a new Promise object for the result
|
||||
RefPtr<Promise> promise = Promise::Create(mGlobalObject, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mTransactionChild->SendRequestGetGroupIdentifier(message)->Then(
|
||||
GetCurrentSerialEventTarget(), __func__,
|
||||
[promise, self = RefPtr{this},
|
||||
message(std::move(message))](Maybe<RawBytes>&& result) {
|
||||
// Check if the value is Nothing
|
||||
if (result.isNothing()) {
|
||||
promise->MaybeReject(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the context from the GlobalObject
|
||||
AutoJSAPI jsapi;
|
||||
if (NS_WARN_IF(!jsapi.Init(self->mGlobalObject))) {
|
||||
MOZ_LOG(gMlsLog, mozilla::LogLevel::Error,
|
||||
("Failed to initialize JSAPI"));
|
||||
promise->MaybeReject(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
JSContext* cx = jsapi.cx();
|
||||
|
||||
// Construct the Uint8Array objects based on the tag
|
||||
ErrorResult error;
|
||||
JS::Rooted<JSObject*> jsGroupId(
|
||||
cx, Uint8Array::Create(cx, result->data(), error));
|
||||
error.WouldReportJSException();
|
||||
if (error.Failed()) {
|
||||
promise->MaybeReject(std::move(error));
|
||||
return;
|
||||
}
|
||||
|
||||
// Construct the MLSBytes object for the groupId
|
||||
RootedDictionary<MLSBytes> rvalue(cx);
|
||||
rvalue.mType = MLSObjectType::Group_identifier;
|
||||
rvalue.mContent.Init(jsGroupId);
|
||||
|
||||
// Log if in debug mode
|
||||
if (MOZ_LOG_TEST(gMlsLog, LogLevel::Debug)) {
|
||||
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug,
|
||||
("Successfully constructed MLSBytes"));
|
||||
}
|
||||
|
||||
// Resolve the promise
|
||||
promise->MaybeResolve(rvalue);
|
||||
},
|
||||
[promise](::mozilla::ipc::ResponseRejectReason aReason) {
|
||||
MOZ_LOG(
|
||||
gMlsLog, mozilla::LogLevel::Error,
|
||||
("IPC call rejected with reason: %d", static_cast<int>(aReason)));
|
||||
promise->MaybeRejectWithUnknownError("getGroupIdFromMessage failed");
|
||||
});
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
} // namespace mozilla::dom
|
||||
@@ -1,74 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef mozilla_dom_MLS_h
|
||||
#define mozilla_dom_MLS_h
|
||||
|
||||
// #include "mozilla/dom/TypedArray.h"
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/MLSBinding.h"
|
||||
#include "mozilla/dom/MLSTransactionChild.h"
|
||||
#include "nsIGlobalObject.h"
|
||||
|
||||
class nsIGlobalObject;
|
||||
|
||||
namespace mozilla::dom {
|
||||
|
||||
class MLSGroupView;
|
||||
|
||||
class MLS final : public nsISupports, public nsWrapperCache {
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(MLS)
|
||||
|
||||
static already_AddRefed<MLS> Constructor(GlobalObject& aGlobal,
|
||||
ErrorResult& aRv);
|
||||
|
||||
explicit MLS(nsIGlobalObject* aGlobalObject, MLSTransactionChild* aActor);
|
||||
|
||||
nsIGlobalObject* GetParentObject() const { return mGlobalObject; }
|
||||
|
||||
JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
already_AddRefed<mozilla::dom::Promise> DeleteState(ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<mozilla::dom::Promise> GenerateIdentity(ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<mozilla::dom::Promise> GenerateCredential(
|
||||
const MLSBytesOrUint8ArrayOrUTF8String& aJsCredContent, ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<mozilla::dom::Promise> GenerateKeyPackage(
|
||||
const MLSBytesOrUint8Array& aJsClientIdentifier,
|
||||
const MLSBytesOrUint8Array& aJsCredential, ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<mozilla::dom::Promise> GroupCreate(
|
||||
const MLSBytesOrUint8Array& aJsClientIdentifier,
|
||||
const MLSBytesOrUint8Array& aJsCredential, ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<mozilla::dom::Promise> GroupGet(
|
||||
const MLSBytesOrUint8Array& aJsGroupIdentifier,
|
||||
const MLSBytesOrUint8Array& aJsClientIdentifier, ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<mozilla::dom::Promise> GroupJoin(
|
||||
const MLSBytesOrUint8Array& aJsClientIdentifier,
|
||||
const MLSBytesOrUint8Array& aJsWelcome, ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<mozilla::dom::Promise> GetGroupIdFromMessage(
|
||||
const MLSBytesOrUint8Array& aJsMessage, ErrorResult& aRv);
|
||||
|
||||
private:
|
||||
friend class MLSGroupView;
|
||||
|
||||
virtual ~MLS();
|
||||
nsCOMPtr<nsIGlobalObject> mGlobalObject;
|
||||
RefPtr<MLSTransactionChild> mTransactionChild;
|
||||
};
|
||||
|
||||
} // namespace mozilla::dom
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,79 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef mozilla_dom_MLSGroup_h
|
||||
#define mozilla_dom_MLSGroup_h
|
||||
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/MLS.h"
|
||||
|
||||
class nsIGlobalObject;
|
||||
|
||||
namespace mozilla::dom {
|
||||
|
||||
class MLSGroupView final : public nsISupports, public nsWrapperCache {
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(MLSGroupView)
|
||||
|
||||
explicit MLSGroupView(MLS* aMLS, nsTArray<uint8_t>&& aGroupId,
|
||||
nsTArray<uint8_t>&& aClientId);
|
||||
|
||||
nsISupports* GetParentObject() const { return mMLS; }
|
||||
|
||||
JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
void GetGroupId(JSContext* aCx, JS::MutableHandle<JSObject*> aGroupId,
|
||||
ErrorResult& aRv);
|
||||
void GetClientId(JSContext* aCx, JS::MutableHandle<JSObject*> aClientId,
|
||||
ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<mozilla::dom::Promise> DeleteState(ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<mozilla::dom::Promise> Add(
|
||||
const MLSBytesOrUint8Array& aJsKeyPackage, ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<mozilla::dom::Promise> ProposeAdd(
|
||||
const MLSBytesOrUint8Array& aJsKeyPackage, ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<mozilla::dom::Promise> Remove(
|
||||
const MLSBytesOrUint8Array& aJsRemClientIdentifier, ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<mozilla::dom::Promise> ProposeRemove(
|
||||
const MLSBytesOrUint8Array& aJsRemClientIdentifier, ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<mozilla::dom::Promise> Close(ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<mozilla::dom::Promise> Details(ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<mozilla::dom::Promise> Send(
|
||||
const MLSBytesOrUint8ArrayOrUTF8String& aJsMessage, ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<mozilla::dom::Promise> Receive(
|
||||
const MLSBytesOrUint8Array& aJsMessage, ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<mozilla::dom::Promise> ApplyPendingCommit(ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<mozilla::dom::Promise> ExportSecret(
|
||||
const MLSBytesOrUint8ArrayOrUTF8String& aJsLabel,
|
||||
const MLSBytesOrUint8Array& aJsContext, const uint64_t aLen,
|
||||
ErrorResult& aRv);
|
||||
|
||||
private:
|
||||
virtual ~MLSGroupView() { mozilla::DropJSObjects(this); }
|
||||
|
||||
RefPtr<MLS> mMLS;
|
||||
nsTArray<uint8_t> mGroupId;
|
||||
nsTArray<uint8_t> mClientId;
|
||||
JS::Heap<JSObject*> mJsGroupId;
|
||||
JS::Heap<JSObject*> mJsClientId;
|
||||
};
|
||||
|
||||
} // namespace mozilla::dom
|
||||
|
||||
#endif
|
||||
@@ -1,16 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef mozilla_dom_MLSLogging_h
|
||||
#define mozilla_dom_MLSLogging_h
|
||||
|
||||
#include "mozilla/Logging.h"
|
||||
|
||||
namespace mozilla::dom {
|
||||
extern LazyLogModule gMlsLog;
|
||||
}
|
||||
|
||||
#endif // mozilla_dom_MLSLogging_h
|
||||
@@ -1,22 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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/. */
|
||||
|
||||
#include "mozilla/dom/MLSTransactionChild.h"
|
||||
#include "MLSLogging.h"
|
||||
|
||||
namespace mozilla::dom {
|
||||
|
||||
MLSTransactionChild::MLSTransactionChild() {
|
||||
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug,
|
||||
("MLSTransactionChild::MLSTransactionChild() - Constructor called"));
|
||||
}
|
||||
|
||||
MLSTransactionChild::~MLSTransactionChild() {
|
||||
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug,
|
||||
("MLSTransactionChild::~MLSTransactionChild() - Destructor called"));
|
||||
}
|
||||
|
||||
} // namespace mozilla::dom
|
||||
@@ -1,27 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef mozilla_dom_MLSTransactionChild_h
|
||||
#define mozilla_dom_MLSTransactionChild_h
|
||||
|
||||
#include "mozilla/dom/PMLSTransaction.h"
|
||||
#include "mozilla/dom/PMLSTransactionChild.h"
|
||||
|
||||
namespace mozilla::dom {
|
||||
|
||||
class MLSTransactionChild final : public PMLSTransactionChild {
|
||||
public:
|
||||
NS_INLINE_DECL_REFCOUNTING(MLSTransactionChild, override)
|
||||
|
||||
MLSTransactionChild();
|
||||
|
||||
protected:
|
||||
virtual ~MLSTransactionChild();
|
||||
};
|
||||
|
||||
} // namespace mozilla::dom
|
||||
|
||||
#endif // mozilla_dom_MLSTransactionChild_h
|
||||
@@ -1,65 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#include "nsTArray.h"
|
||||
#include "ipc/IPCMessageUtils.h"
|
||||
#include "ipc/IPCMessageUtilsSpecializations.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/security/mls/mls_gk_ffi_generated.h"
|
||||
#include "MLSTransactionMessage.h"
|
||||
#include "mozilla/dom/MLSTransactionMessage.h"
|
||||
|
||||
using namespace mozilla::security::mls;
|
||||
|
||||
void IPC::ParamTraits<mozilla::security::mls::GkReceived>::Write(
|
||||
MessageWriter* aWriter, const paramType& aValue) {
|
||||
// Serialize the enum variant tag
|
||||
IPC::WriteParam(aWriter, aValue.tag);
|
||||
|
||||
switch (aValue.tag) {
|
||||
case paramType::Tag::ApplicationMessage:
|
||||
IPC::WriteParam(aWriter, aValue.application_message._0);
|
||||
break;
|
||||
case paramType::Tag::GroupIdEpoch:
|
||||
IPC::WriteParam(aWriter, aValue.group_id_epoch._0);
|
||||
break;
|
||||
case paramType::Tag::CommitOutput:
|
||||
IPC::WriteParam(aWriter, aValue.commit_output._0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool IPC::ParamTraits<mozilla::security::mls::GkReceived>::Read(
|
||||
MessageReader* aReader, paramType* aResult) {
|
||||
MOZ_ASSERT(aResult->tag == paramType::Tag::None,
|
||||
"Clobbering already-initialized result");
|
||||
|
||||
// Deserialize the tag
|
||||
if (!IPC::ReadParam(aReader, &aResult->tag)) {
|
||||
// Ensure that the tag has a safe value for the destructor before returning.
|
||||
aResult->tag = paramType::Tag::None;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Use placement-new to initialize the relevant variant of the internal union,
|
||||
// then deserialize the bodies.
|
||||
switch (aResult->tag) {
|
||||
case paramType::Tag::None:
|
||||
return true; // No data payload
|
||||
case paramType::Tag::ApplicationMessage:
|
||||
new (&aResult->application_message) paramType::ApplicationMessage_Body;
|
||||
return IPC::ReadParam(aReader, &aResult->application_message._0);
|
||||
case paramType::Tag::GroupIdEpoch:
|
||||
new (&aResult->group_id_epoch) paramType::GroupIdEpoch_Body;
|
||||
return IPC::ReadParam(aReader, &aResult->group_id_epoch._0);
|
||||
case paramType::Tag::CommitOutput:
|
||||
new (&aResult->commit_output) paramType::CommitOutput_Body;
|
||||
return IPC::ReadParam(aReader, &aResult->commit_output._0);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef mozilla_dom_MLSTransactionMessage_h
|
||||
#define mozilla_dom_MLSTransactionMessage_h
|
||||
|
||||
#include "nsTArray.h"
|
||||
#include "ipc/IPCMessageUtilsSpecializations.h"
|
||||
#include "mozilla/security/mls/mls_gk_ffi_generated.h"
|
||||
#include "ipc/EnumSerializer.h"
|
||||
#include "ipc/IPCMessageUtils.h"
|
||||
|
||||
using namespace mozilla::security::mls;
|
||||
|
||||
namespace IPC {
|
||||
|
||||
template <>
|
||||
struct ParamTraits<mozilla::security::mls::GkReceived::Tag>
|
||||
: public ContiguousEnumSerializerInclusive<
|
||||
mozilla::security::mls::GkReceived::Tag,
|
||||
mozilla::security::mls::GkReceived::Tag::None,
|
||||
mozilla::security::mls::GkReceived::Tag::CommitOutput> {};
|
||||
template <>
|
||||
struct ParamTraits<mozilla::security::mls::GkReceived> {
|
||||
using paramType = mozilla::security::mls::GkReceived;
|
||||
static void Write(MessageWriter* aWriter, const paramType& aValue);
|
||||
static bool Read(MessageReader* aReader, paramType* aResult);
|
||||
};
|
||||
|
||||
DEFINE_IPC_SERIALIZER_WITH_FIELDS(mozilla::security::mls::GkGroupIdEpoch,
|
||||
group_id, group_epoch);
|
||||
|
||||
DEFINE_IPC_SERIALIZER_WITH_FIELDS(mozilla::security::mls::GkMlsCommitOutput,
|
||||
commit, welcome, group_info, ratchet_tree,
|
||||
identity);
|
||||
|
||||
DEFINE_IPC_SERIALIZER_WITH_FIELDS(mozilla::security::mls::GkClientIdentifiers,
|
||||
identity, credential);
|
||||
|
||||
DEFINE_IPC_SERIALIZER_WITH_FIELDS(mozilla::security::mls::GkGroupMembers,
|
||||
group_id, group_epoch, group_members);
|
||||
|
||||
DEFINE_IPC_SERIALIZER_WITH_FIELDS(mozilla::security::mls::GkExporterOutput,
|
||||
group_id, group_epoch, label, context,
|
||||
exporter);
|
||||
|
||||
}; // namespace IPC
|
||||
|
||||
#endif // mozilla_dom_MLSTransactionMessage_h
|
||||
@@ -1,574 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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/. */
|
||||
|
||||
#include "MLSTransactionParent.h"
|
||||
#include "MLSTransactionMessage.h"
|
||||
#include "mozilla/dom/quota/QuotaManager.h"
|
||||
#include "mozilla/security/mls/mls_gk_ffi_generated.h"
|
||||
#include "MLSLogging.h"
|
||||
#include "mozilla/Base64.h"
|
||||
|
||||
#include "nsIFile.h"
|
||||
#include "nsIPrincipal.h"
|
||||
#include "nsString.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
using mozilla::dom::quota::QuotaManager;
|
||||
|
||||
namespace mozilla::dom {
|
||||
|
||||
/* static */ nsresult MLSTransactionParent::CreateDirectoryIfNotExists(
|
||||
nsIFile* aDir) {
|
||||
nsresult rv = aDir->Create(nsIFile::DIRECTORY_TYPE, 0755);
|
||||
if (rv == NS_ERROR_FILE_ALREADY_EXISTS) {
|
||||
// Evaluate if the file is a directory
|
||||
bool isDirectory = false;
|
||||
rv = aDir->IsDirectory(&isDirectory);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Check if the file is actually a directory
|
||||
if (!isDirectory) {
|
||||
return NS_ERROR_FILE_NOT_DIRECTORY;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* static */ nsresult MLSTransactionParent::ConstructDatabasePrefixPath(
|
||||
nsCOMPtr<nsIFile>& aFile) {
|
||||
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug,
|
||||
("MLSTransactionParent::ConstructDatabasePath()"));
|
||||
|
||||
// Get the base path from the quota manager
|
||||
QuotaManager* quotaManager = QuotaManager::Get();
|
||||
if (NS_WARN_IF(!quotaManager)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Create an nsIFile object from the path
|
||||
nsresult rv =
|
||||
NS_NewLocalFile(quotaManager->GetBasePath(), getter_AddRefs(aFile));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Append the hardcoded "mls" directory name to the path
|
||||
rv = aFile->AppendNative("mls"_ns);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* static */ nsresult MLSTransactionParent::ConstructDatabaseFullPath(
|
||||
nsCOMPtr<nsIFile>& aFile, nsIPrincipal* aPrincipal,
|
||||
nsCString& aDatabasePath) {
|
||||
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug,
|
||||
("MLSTransactionParent::ConstructDatabaseFullPath()"));
|
||||
|
||||
// Get StorageOriginKey
|
||||
nsAutoCString originKey;
|
||||
nsresult rv = aPrincipal->GetStorageOriginKey(originKey);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Get OriginSuffix
|
||||
nsAutoCString originAttrSuffix;
|
||||
rv = aPrincipal->GetOriginSuffix(originAttrSuffix);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Set the base path and origin
|
||||
nsAutoCString origin = originKey + originAttrSuffix;
|
||||
|
||||
// Encode the origin with its suffix
|
||||
nsAutoCString encodedOrigin;
|
||||
rv = mozilla::Base64Encode(origin, encodedOrigin);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug,
|
||||
("MLSTransactionParent::ConstructDatabasePath() - origin: %s",
|
||||
origin.get()));
|
||||
|
||||
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug,
|
||||
("MLSTransactionParent::ConstructDatabasePath() - encodedOrigin: "
|
||||
"%s",
|
||||
encodedOrigin.get()));
|
||||
|
||||
// Append the origin to the path
|
||||
rv = aFile->AppendNative(encodedOrigin);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Get the updated path back into the nsCString
|
||||
nsAutoString databasePathUTF16;
|
||||
rv = aFile->GetPath(databasePathUTF16);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
aDatabasePath = NS_ConvertUTF16toUTF8(databasePathUTF16);
|
||||
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug,
|
||||
("MLSTransactionParent::ConstructDatabasePath() - databasePath: %s",
|
||||
aDatabasePath.get()));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void MLSTransactionParent::ActorDestroy(ActorDestroyReason) {
|
||||
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug,
|
||||
("MLSTransactionParent::ActorDestroy()"));
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult MLSTransactionParent::RecvRequestStateDelete(
|
||||
RequestStateDeleteResolver&& aResolver) {
|
||||
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug,
|
||||
("MLSTransactionParent::RecvRequestStateDelete()"));
|
||||
|
||||
// Call to the MLS rust code
|
||||
nsresult rv = security::mls::mls_state_delete(&mDatabasePath);
|
||||
|
||||
aResolver(NS_SUCCEEDED(rv));
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult MLSTransactionParent::RecvRequestGroupStateDelete(
|
||||
const nsTArray<uint8_t>& aGroupIdentifier,
|
||||
const nsTArray<uint8_t>& aIdentifier,
|
||||
RequestGroupStateDeleteResolver&& aResolver) {
|
||||
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug,
|
||||
("MLSTransactionParent::RecvRequestGroupStateDelete()"));
|
||||
|
||||
// Call to the MLS rust code
|
||||
security::mls::GkGroupIdEpoch groupIdEpoch;
|
||||
nsresult rv = security::mls::mls_state_delete_group(
|
||||
&mDatabasePath, aGroupIdentifier.Elements(), aGroupIdentifier.Length(),
|
||||
aIdentifier.Elements(), aIdentifier.Length(), &groupIdEpoch);
|
||||
|
||||
// Return Nothing if failed
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aResolver(Nothing());
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
// Return the result if success
|
||||
aResolver(Some(std::move(groupIdEpoch)));
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
MLSTransactionParent::RecvRequestGenerateIdentityKeypair(
|
||||
RequestGenerateIdentityKeypairResolver&& aResolver) {
|
||||
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug,
|
||||
("MLSTransactionParent::RecvRequestGenerateIdentityKeypair()"));
|
||||
|
||||
// Call to the MLS rust code
|
||||
nsTArray<uint8_t> signatureIdentifier;
|
||||
nsresult rv = security::mls::mls_generate_signature_keypair(
|
||||
&mDatabasePath, &signatureIdentifier);
|
||||
|
||||
// Return Nothing if failed
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aResolver(Nothing());
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
// Return the result if success
|
||||
aResolver(Some(RawBytes{std::move(signatureIdentifier)}));
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
MLSTransactionParent::RecvRequestGenerateCredentialBasic(
|
||||
const nsTArray<uint8_t>& aCredContent,
|
||||
RequestGenerateCredentialBasicResolver&& aResolver) {
|
||||
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug,
|
||||
("MLSTransactionParent::RecvRequestGenerateCredentialBasic()"));
|
||||
|
||||
// Call to the MLS rust code
|
||||
nsTArray<uint8_t> credential;
|
||||
nsresult rv = security::mls::mls_generate_credential_basic(
|
||||
aCredContent.Elements(), aCredContent.Length(), &credential);
|
||||
|
||||
// Return Nothing if failed
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aResolver(Nothing());
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
// Return the result if success
|
||||
aResolver(Some(RawBytes{std::move(credential)}));
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult MLSTransactionParent::RecvRequestGenerateKeyPackage(
|
||||
const nsTArray<uint8_t>& aIdentifier, const nsTArray<uint8_t>& aCredential,
|
||||
RequestGenerateKeyPackageResolver&& aResolver) {
|
||||
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug,
|
||||
("MLSTransactionParent::RecvRequestGenerateKeyPackage()"));
|
||||
|
||||
// Call to the MLS rust code
|
||||
nsTArray<uint8_t> keyPackage;
|
||||
nsresult rv = security::mls::mls_generate_keypackage(
|
||||
&mDatabasePath, aIdentifier.Elements(), aIdentifier.Length(),
|
||||
aCredential.Elements(), aCredential.Length(), &keyPackage);
|
||||
|
||||
// Return Nothing if failed
|
||||
if (NS_FAILED(rv)) {
|
||||
aResolver(Nothing());
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
// Return the result if success
|
||||
aResolver(Some(RawBytes{std::move(keyPackage)}));
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult MLSTransactionParent::RecvRequestGroupCreate(
|
||||
const nsTArray<uint8_t>& aIdentifier, const nsTArray<uint8_t>& aCredential,
|
||||
const nsTArray<uint8_t>& aInOptGroupIdentifier,
|
||||
RequestGroupCreateResolver&& aResolver) {
|
||||
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug,
|
||||
("MLSTransactionParent::RecvRequestGroupCreate()"));
|
||||
|
||||
// Call to the MLS rust code
|
||||
security::mls::GkGroupIdEpoch groupIdEpoch;
|
||||
nsresult rv = security::mls::mls_group_create(
|
||||
&mDatabasePath, aIdentifier.Elements(), aIdentifier.Length(),
|
||||
aCredential.Elements(), aCredential.Length(),
|
||||
aInOptGroupIdentifier.Elements(), aInOptGroupIdentifier.Length(),
|
||||
&groupIdEpoch);
|
||||
|
||||
// Return Nothing if failed
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aResolver(Nothing());
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
// Return the result if success
|
||||
aResolver(Some(std::move(groupIdEpoch)));
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult MLSTransactionParent::RecvRequestGroupJoin(
|
||||
const nsTArray<uint8_t>& aIdentifier, const nsTArray<uint8_t>& aWelcome,
|
||||
RequestGroupJoinResolver&& aResolver) {
|
||||
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug,
|
||||
("MLSTransactionParent::RecvRequestGroupJoin()"));
|
||||
|
||||
// Call to the MLS rust code
|
||||
security::mls::GkGroupIdEpoch groupIdEpoch;
|
||||
nsresult rv = security::mls::mls_group_join(
|
||||
&mDatabasePath, aIdentifier.Elements(), aIdentifier.Length(),
|
||||
aWelcome.Elements(), aWelcome.Length(), &groupIdEpoch);
|
||||
|
||||
// Return Nothing if failed
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aResolver(Nothing());
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
// Return the result if success
|
||||
aResolver(Some(std::move(groupIdEpoch)));
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult MLSTransactionParent::RecvRequestGroupAdd(
|
||||
const nsTArray<uint8_t>& aGroupIdentifier,
|
||||
const nsTArray<uint8_t>& aIdentifier, const nsTArray<uint8_t>& aKeyPackage,
|
||||
RequestGroupAddResolver&& aResolver) {
|
||||
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug,
|
||||
("MLSTransactionParent::RecvRequestGroupAdd()"));
|
||||
|
||||
// Call to the MLS rust code
|
||||
security::mls::GkMlsCommitOutput commitOutput;
|
||||
nsresult rv = security::mls::mls_group_add(
|
||||
&mDatabasePath, aGroupIdentifier.Elements(), aGroupIdentifier.Length(),
|
||||
aIdentifier.Elements(), aIdentifier.Length(), aKeyPackage.Elements(),
|
||||
aKeyPackage.Length(), &commitOutput);
|
||||
|
||||
// Return Nothing if failed
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aResolver(Nothing());
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
// Return the result if success
|
||||
aResolver(Some(std::move(commitOutput)));
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult MLSTransactionParent::RecvRequestGroupProposeAdd(
|
||||
const nsTArray<uint8_t>& aGroupIdentifier,
|
||||
const nsTArray<uint8_t>& aIdentifier, const nsTArray<uint8_t>& aKeyPackage,
|
||||
RequestGroupProposeAddResolver&& aResolver) {
|
||||
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug,
|
||||
("MLSTransactionParent::RecvRequestGroupProposeAdd()"));
|
||||
|
||||
// Call to the MLS rust code
|
||||
nsTArray<uint8_t> proposal;
|
||||
nsresult rv = security::mls::mls_group_propose_add(
|
||||
&mDatabasePath, aGroupIdentifier.Elements(), aGroupIdentifier.Length(),
|
||||
aIdentifier.Elements(), aIdentifier.Length(), aKeyPackage.Elements(),
|
||||
aKeyPackage.Length(), &proposal);
|
||||
|
||||
// Return Nothing if failed
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aResolver(Nothing());
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
// Return the result if success
|
||||
aResolver(Some(RawBytes{std::move(proposal)}));
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult MLSTransactionParent::RecvRequestGroupRemove(
|
||||
const nsTArray<uint8_t>& aGroupIdentifier,
|
||||
const nsTArray<uint8_t>& aIdentifier,
|
||||
const nsTArray<uint8_t>& aRemIdentifier,
|
||||
RequestGroupRemoveResolver&& aResolver) {
|
||||
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug,
|
||||
("MLSTransactionParent::RecvRequestGroupRemove()"));
|
||||
|
||||
// Call to the MLS rust code
|
||||
security::mls::GkMlsCommitOutput commitOutput;
|
||||
nsresult rv = security::mls::mls_group_remove(
|
||||
&mDatabasePath, aGroupIdentifier.Elements(), aGroupIdentifier.Length(),
|
||||
aIdentifier.Elements(), aIdentifier.Length(), aRemIdentifier.Elements(),
|
||||
aRemIdentifier.Length(), &commitOutput);
|
||||
|
||||
// Return Nothing if failed
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aResolver(Nothing());
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
// Return the result if success
|
||||
aResolver(Some(std::move(commitOutput)));
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult MLSTransactionParent::RecvRequestGroupProposeRemove(
|
||||
const nsTArray<uint8_t>& aGroupIdentifier,
|
||||
const nsTArray<uint8_t>& aIdentifier,
|
||||
const nsTArray<uint8_t>& aRemIdentifier,
|
||||
RequestGroupProposeRemoveResolver&& aResolver) {
|
||||
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug,
|
||||
("MLSTransactionParent::RecvRequestGroupProposeRemove()"));
|
||||
|
||||
nsTArray<uint8_t> proposal;
|
||||
nsresult rv = security::mls::mls_group_propose_remove(
|
||||
&mDatabasePath, aGroupIdentifier.Elements(), aGroupIdentifier.Length(),
|
||||
aIdentifier.Elements(), aIdentifier.Length(), aRemIdentifier.Elements(),
|
||||
aRemIdentifier.Length(), &proposal);
|
||||
|
||||
// Return Nothing if failed
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aResolver(Nothing());
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
// Return the result if success
|
||||
aResolver(Some(RawBytes{std::move(proposal)}));
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult MLSTransactionParent::RecvRequestGroupClose(
|
||||
const nsTArray<uint8_t>& aGroupIdentifier,
|
||||
const nsTArray<uint8_t>& aIdentifier,
|
||||
RequestGroupCloseResolver&& aResolver) {
|
||||
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug,
|
||||
("MLSTransactionParent::RecvRequestGroupClose()"));
|
||||
|
||||
// Call to the MLS rust code
|
||||
security::mls::GkMlsCommitOutput commitOutput;
|
||||
nsresult rv = security::mls::mls_group_close(
|
||||
&mDatabasePath, aGroupIdentifier.Elements(), aGroupIdentifier.Length(),
|
||||
aIdentifier.Elements(), aIdentifier.Length(), &commitOutput);
|
||||
|
||||
// Return Nothing if failed
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aResolver(Nothing());
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
// Return the result if success
|
||||
aResolver(Some(std::move(commitOutput)));
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult MLSTransactionParent::RecvRequestGroupDetails(
|
||||
const nsTArray<uint8_t>& aGroupIdentifier,
|
||||
const nsTArray<uint8_t>& aIdentifier,
|
||||
RequestGroupDetailsResolver&& aResolver) {
|
||||
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug,
|
||||
("MLSTransactionParent::RecvRequestGroupDetails()"));
|
||||
|
||||
// Call to the MLS rust code
|
||||
security::mls::GkGroupMembers members;
|
||||
nsresult rv = security::mls::mls_group_members(
|
||||
&mDatabasePath, aGroupIdentifier.Elements(), aGroupIdentifier.Length(),
|
||||
aIdentifier.Elements(), aIdentifier.Length(), &members);
|
||||
|
||||
// Return Nothing if failed
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aResolver(Nothing());
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
// Return the result if success
|
||||
aResolver(Some(std::move(members)));
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult MLSTransactionParent::RecvRequestReceive(
|
||||
const nsTArray<uint8_t>& aClientIdentifier,
|
||||
const nsTArray<uint8_t>& aMessage, RequestReceiveResolver&& aResolver) {
|
||||
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug,
|
||||
("MLSTransactionParent::RecvRequestReceive()"));
|
||||
|
||||
// Call to the MLS rust code
|
||||
GkReceived received;
|
||||
nsTArray<uint8_t> group_id_bytes;
|
||||
|
||||
nsresult rv = security::mls::mls_receive(
|
||||
&mDatabasePath, aClientIdentifier.Elements(), aClientIdentifier.Length(),
|
||||
aMessage.Elements(), aMessage.Length(), &group_id_bytes, &received);
|
||||
|
||||
// Return Nothing if failed
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aResolver(GkReceived());
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
// Return the result if success
|
||||
aResolver(received);
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult MLSTransactionParent::RecvRequestApplyPendingCommit(
|
||||
const nsTArray<uint8_t>& aGroupIdentifier,
|
||||
const nsTArray<uint8_t>& aClientIdentifier,
|
||||
RequestApplyPendingCommitResolver&& aResolver) {
|
||||
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug,
|
||||
("MLSTransactionParent::RecvRequestApplyPendingCommit()"));
|
||||
|
||||
// Call to the MLS rust code
|
||||
GkReceived received;
|
||||
nsresult rv = security::mls::mls_receive_ack(
|
||||
&mDatabasePath, aGroupIdentifier.Elements(), aGroupIdentifier.Length(),
|
||||
aClientIdentifier.Elements(), aClientIdentifier.Length(), &received);
|
||||
|
||||
// Return Nothing if failed
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aResolver(GkReceived());
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
// Return the result if success
|
||||
aResolver(received);
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult MLSTransactionParent::RecvRequestSend(
|
||||
const nsTArray<uint8_t>& aGroupIdentifier,
|
||||
const nsTArray<uint8_t>& aIdentifier, const nsTArray<uint8_t>& aMessage,
|
||||
RequestSendResolver&& aResolver) {
|
||||
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug,
|
||||
("MLSTransactionParent::RecvRequestSend()"));
|
||||
|
||||
// Call to the MLS rust code
|
||||
nsTArray<uint8_t> outputMessage;
|
||||
nsresult rv = security::mls::mls_send(
|
||||
&mDatabasePath, aGroupIdentifier.Elements(), aGroupIdentifier.Length(),
|
||||
aIdentifier.Elements(), aIdentifier.Length(), aMessage.Elements(),
|
||||
aMessage.Length(), &outputMessage);
|
||||
|
||||
// Return Nothing if failed
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aResolver(Nothing());
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
// Return the result if success
|
||||
aResolver(Some(RawBytes{std::move(outputMessage)}));
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult MLSTransactionParent::RecvRequestExportSecret(
|
||||
const nsTArray<uint8_t>& aGroupIdentifier,
|
||||
const nsTArray<uint8_t>& aIdentifier, const nsTArray<uint8_t>& aLabel,
|
||||
const nsTArray<uint8_t>& aContext, uint64_t aLen,
|
||||
RequestExportSecretResolver&& aResolver) {
|
||||
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug,
|
||||
("MLSTransactionParent::RecvRequestExportSecret()"));
|
||||
|
||||
// Call to the MLS rust code
|
||||
security::mls::GkExporterOutput exporterOutput;
|
||||
nsresult rv = security::mls::mls_derive_exporter(
|
||||
&mDatabasePath, aGroupIdentifier.Elements(), aGroupIdentifier.Length(),
|
||||
aIdentifier.Elements(), aIdentifier.Length(), aLabel.Elements(),
|
||||
aLabel.Length(), aContext.Elements(), aContext.Length(), aLen,
|
||||
&exporterOutput);
|
||||
|
||||
// Return Nothing if failed
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aResolver(Nothing());
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
// Return the result if success
|
||||
aResolver(Some(std::move(exporterOutput)));
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult MLSTransactionParent::RecvRequestGetGroupIdentifier(
|
||||
const nsTArray<uint8_t>& aMessage,
|
||||
RequestGetGroupIdentifierResolver&& aResolver) {
|
||||
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug,
|
||||
("MLSTransactionParent::RecvRequestGetGroupIdentifier()"));
|
||||
|
||||
nsTArray<uint8_t> groupId;
|
||||
nsresult rv = security::mls::mls_get_group_id(aMessage.Elements(),
|
||||
aMessage.Length(), &groupId);
|
||||
|
||||
// Return Nothing if failed
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aResolver(Nothing());
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
// Return the result if success
|
||||
aResolver(Some(RawBytes{std::move(groupId)}));
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
} // namespace mozilla::dom
|
||||
@@ -1,128 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef mozilla_dom_MLSTransactionParent_h
|
||||
#define mozilla_dom_MLSTransactionParent_h
|
||||
|
||||
#include "mozilla/dom/PMLSTransaction.h"
|
||||
#include "mozilla/dom/PMLSTransactionParent.h"
|
||||
#include "nsIPrincipal.h"
|
||||
|
||||
namespace mozilla::dom {
|
||||
|
||||
class MLSTransactionParent final : public PMLSTransactionParent {
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MLSTransactionParent, override);
|
||||
|
||||
explicit MLSTransactionParent(const nsACString& aDatabasePath)
|
||||
: mDatabasePath(aDatabasePath) {};
|
||||
|
||||
static nsresult CreateDirectoryIfNotExists(nsIFile* aDir);
|
||||
|
||||
static nsresult ConstructDatabasePrefixPath(nsCOMPtr<nsIFile>& aFile);
|
||||
|
||||
static nsresult ConstructDatabaseFullPath(nsCOMPtr<nsIFile>& aFile,
|
||||
nsIPrincipal* aPrincipal,
|
||||
nsCString& aDatabasePath);
|
||||
|
||||
void ActorDestroy(ActorDestroyReason) override;
|
||||
|
||||
mozilla::ipc::IPCResult RecvRequestStateDelete(
|
||||
RequestStateDeleteResolver&& aResolver);
|
||||
|
||||
mozilla::ipc::IPCResult RecvRequestGroupStateDelete(
|
||||
const nsTArray<uint8_t>& aGroupIdentifier,
|
||||
const nsTArray<uint8_t>& aIdentifier,
|
||||
RequestGroupStateDeleteResolver&& aResolver);
|
||||
|
||||
mozilla::ipc::IPCResult RecvRequestGenerateIdentityKeypair(
|
||||
RequestGenerateIdentityKeypairResolver&& aResolver);
|
||||
|
||||
mozilla::ipc::IPCResult RecvRequestGenerateCredentialBasic(
|
||||
const nsTArray<uint8_t>& aCredContent,
|
||||
RequestGenerateCredentialBasicResolver&& aResolver);
|
||||
|
||||
mozilla::ipc::IPCResult RecvRequestGenerateKeyPackage(
|
||||
const nsTArray<uint8_t>& aIdentifier,
|
||||
const nsTArray<uint8_t>& aCredential,
|
||||
RequestGenerateKeyPackageResolver&& aResolver);
|
||||
|
||||
mozilla::ipc::IPCResult RecvRequestGroupCreate(
|
||||
const nsTArray<uint8_t>& aIdentifier,
|
||||
const nsTArray<uint8_t>& aCredential,
|
||||
const nsTArray<uint8_t>& aInOptGroupIdentifier,
|
||||
RequestGroupCreateResolver&& aResolver);
|
||||
|
||||
mozilla::ipc::IPCResult RecvRequestGroupJoin(
|
||||
const nsTArray<uint8_t>& aIdentifier, const nsTArray<uint8_t>& aWelcome,
|
||||
RequestGroupJoinResolver&& aResolver);
|
||||
|
||||
mozilla::ipc::IPCResult RecvRequestGroupAdd(
|
||||
const nsTArray<uint8_t>& aGroupIdentifier,
|
||||
const nsTArray<uint8_t>& aIdentifier,
|
||||
const nsTArray<uint8_t>& aKeyPackage,
|
||||
RequestGroupAddResolver&& aResolver);
|
||||
|
||||
mozilla::ipc::IPCResult RecvRequestGroupProposeAdd(
|
||||
const nsTArray<uint8_t>& aGroupIdentifier,
|
||||
const nsTArray<uint8_t>& aIdentifier,
|
||||
const nsTArray<uint8_t>& aKeyPackage,
|
||||
RequestGroupProposeAddResolver&& aResolver);
|
||||
|
||||
mozilla::ipc::IPCResult RecvRequestGroupRemove(
|
||||
const nsTArray<uint8_t>& aGroupIdentifier,
|
||||
const nsTArray<uint8_t>& aIdentifier,
|
||||
const nsTArray<uint8_t>& aRemIdentifier,
|
||||
RequestGroupRemoveResolver&& aResolver);
|
||||
|
||||
mozilla::ipc::IPCResult RecvRequestGroupProposeRemove(
|
||||
const nsTArray<uint8_t>& aGroupIdentifier,
|
||||
const nsTArray<uint8_t>& aIdentifier,
|
||||
const nsTArray<uint8_t>& aRemIdentifier,
|
||||
RequestGroupProposeRemoveResolver&& aResolver);
|
||||
|
||||
mozilla::ipc::IPCResult RecvRequestGroupClose(
|
||||
const nsTArray<uint8_t>& aGroupIdentifier,
|
||||
const nsTArray<uint8_t>& aIdentifier,
|
||||
RequestGroupCloseResolver&& aResolver);
|
||||
|
||||
mozilla::ipc::IPCResult RecvRequestGroupDetails(
|
||||
const nsTArray<uint8_t>& aGroupIdentifier,
|
||||
const nsTArray<uint8_t>& aIdentifier,
|
||||
RequestGroupDetailsResolver&& aResolver);
|
||||
|
||||
mozilla::ipc::IPCResult RecvRequestReceive(
|
||||
const nsTArray<uint8_t>& aClientIdentifier,
|
||||
const nsTArray<uint8_t>& aMessage, RequestReceiveResolver&& aResolver);
|
||||
|
||||
mozilla::ipc::IPCResult RecvRequestApplyPendingCommit(
|
||||
const nsTArray<uint8_t>& aGroupIdentifier,
|
||||
const nsTArray<uint8_t>& aClientIdentifier,
|
||||
RequestApplyPendingCommitResolver&& aResolver);
|
||||
|
||||
mozilla::ipc::IPCResult RecvRequestSend(
|
||||
const nsTArray<uint8_t>& aGroupIdentifier,
|
||||
const nsTArray<uint8_t>& aIdentifier, const nsTArray<uint8_t>& aMessage,
|
||||
RequestSendResolver&& aResolver);
|
||||
|
||||
mozilla::ipc::IPCResult RecvRequestExportSecret(
|
||||
const nsTArray<uint8_t>& aGroupIdentifier,
|
||||
const nsTArray<uint8_t>& aIdentifier, const nsTArray<uint8_t>& aLabel,
|
||||
const nsTArray<uint8_t>& aContext, uint64_t aLen,
|
||||
RequestExportSecretResolver&& aResolver);
|
||||
|
||||
mozilla::ipc::IPCResult RecvRequestGetGroupIdentifier(
|
||||
const nsTArray<uint8_t>& aMessage,
|
||||
RequestGetGroupIdentifierResolver&& aResolver);
|
||||
|
||||
protected:
|
||||
~MLSTransactionParent() = default;
|
||||
nsCString mDatabasePath;
|
||||
};
|
||||
|
||||
} // namespace mozilla::dom
|
||||
|
||||
#endif
|
||||
@@ -1,95 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#include "mozilla/dom/MLSBinding.h"
|
||||
#include "mozilla/dom/TypedArray.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
namespace mozilla::dom {
|
||||
|
||||
nsTArray<uint8_t> ExtractMLSBytesOrUint8ArrayWithUnknownType(
|
||||
const MLSBytesOrUint8Array& aArgument, ErrorResult& aRv) {
|
||||
// The data can be in a Uint8Array or MLSBytes.
|
||||
const Uint8Array* array = nullptr;
|
||||
|
||||
if (aArgument.IsMLSBytes()) {
|
||||
array = &aArgument.GetAsMLSBytes().mContent;
|
||||
} else {
|
||||
MOZ_ASSERT(aArgument.IsUint8Array());
|
||||
array = &aArgument.GetAsUint8Array();
|
||||
}
|
||||
|
||||
// Append the data from the Uint8Array to the output array
|
||||
nsTArray<uint8_t> bytes;
|
||||
if (array && !array->AppendDataTo(bytes)) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return nsTArray<uint8_t>();
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
nsTArray<uint8_t> ExtractMLSBytesOrUint8Array(
|
||||
MLSObjectType aExpectedType, const MLSBytesOrUint8Array& aArgument,
|
||||
ErrorResult& aRv) {
|
||||
// The data can be in a Uint8Array or MLSBytes.
|
||||
const Uint8Array* array = nullptr;
|
||||
|
||||
if (aArgument.IsMLSBytes()) {
|
||||
// Check if the type of MLSBytes matches the expected type
|
||||
if (aArgument.GetAsMLSBytes().mType != aExpectedType) {
|
||||
aRv.ThrowTypeError("Input data has an invalid type");
|
||||
return nsTArray<uint8_t>();
|
||||
}
|
||||
array = &aArgument.GetAsMLSBytes().mContent;
|
||||
} else {
|
||||
MOZ_ASSERT(aArgument.IsUint8Array());
|
||||
array = &aArgument.GetAsUint8Array();
|
||||
}
|
||||
|
||||
// Append the data from the Uint8Array to the output array
|
||||
nsTArray<uint8_t> bytes;
|
||||
if (array && !array->AppendDataTo(bytes)) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return nsTArray<uint8_t>();
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
nsTArray<uint8_t> ExtractMLSBytesOrUint8ArrayOrUTF8String(
|
||||
MLSObjectType aExpectedType,
|
||||
const MLSBytesOrUint8ArrayOrUTF8String& aArgument, ErrorResult& aRv) {
|
||||
// The data can be in a Uint8Array, MLSBytes, or UTF8String.
|
||||
const Uint8Array* array = nullptr;
|
||||
nsTArray<uint8_t> bytes;
|
||||
if (aArgument.IsMLSBytes()) {
|
||||
// Check if the type of MLSBytes matches the expected type
|
||||
if (aArgument.GetAsMLSBytes().mType != aExpectedType) {
|
||||
aRv.ThrowTypeError("Input data has an invalid type");
|
||||
return nsTArray<uint8_t>();
|
||||
}
|
||||
array = &aArgument.GetAsMLSBytes().mContent;
|
||||
} else if (aArgument.IsUint8Array()) {
|
||||
array = &aArgument.GetAsUint8Array();
|
||||
} else {
|
||||
MOZ_ASSERT(aArgument.IsUTF8String());
|
||||
const nsACString& string = aArgument.GetAsUTF8String();
|
||||
if (!bytes.AppendElements(string.BeginReading(), string.Length(),
|
||||
fallible)) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return nsTArray<uint8_t>();
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
// Append the data from the Uint8Array to the output array
|
||||
if (array && !array->AppendDataTo(bytes)) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return nsTArray<uint8_t>();
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
} // namespace mozilla::dom
|
||||
@@ -1,28 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef mozilla_dom_MLSTypeUtils_h
|
||||
#define mozilla_dom_MLSTypeUtils_h
|
||||
|
||||
#include "mozilla/dom/MLSBinding.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
namespace mozilla::dom {
|
||||
|
||||
nsTArray<uint8_t> ExtractMLSBytesOrUint8ArrayWithUnknownType(
|
||||
const MLSBytesOrUint8Array& aArgument, ErrorResult& aRv);
|
||||
|
||||
nsTArray<uint8_t> ExtractMLSBytesOrUint8Array(
|
||||
MLSObjectType aExpectedType, const MLSBytesOrUint8Array& aArgument,
|
||||
ErrorResult& aRv);
|
||||
|
||||
nsTArray<uint8_t> ExtractMLSBytesOrUint8ArrayOrUTF8String(
|
||||
MLSObjectType aExpectedType,
|
||||
const MLSBytesOrUint8ArrayOrUTF8String& aArgument, ErrorResult& aRv);
|
||||
|
||||
} // namespace mozilla::dom
|
||||
|
||||
#endif // mozilla_dom_MLSTypeUtils_h
|
||||
@@ -1,45 +0,0 @@
|
||||
/* 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/. */
|
||||
|
||||
include protocol PBackground;
|
||||
using struct mozilla::security::mls::GkGroupIdEpoch from "mozilla/dom/MLSTransactionMessage.h";
|
||||
using struct mozilla::security::mls::GkMlsCommitOutput from "mozilla/dom/MLSTransactionMessage.h";
|
||||
using struct mozilla::security::mls::GkGroupMembers from "mozilla/dom/MLSTransactionMessage.h";
|
||||
using struct mozilla::security::mls::GkExporterOutput from "mozilla/dom/MLSTransactionMessage.h";
|
||||
using struct mozilla::security::mls::GkReceived from "mozilla/dom/MLSTransactionMessage.h";
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
/* This type exists because we cannot write uint8_t[]? */
|
||||
struct RawBytes {
|
||||
uint8_t[] data;
|
||||
};
|
||||
|
||||
[ChildProc=anydom]
|
||||
async protocol PMLSTransaction
|
||||
{
|
||||
parent:
|
||||
async RequestStateDelete() returns (bool result);
|
||||
async RequestGroupStateDelete(uint8_t[] groupIdentifier, uint8_t[] identifier) returns (GkGroupIdEpoch? result);
|
||||
async RequestGenerateIdentityKeypair() returns (RawBytes? result);
|
||||
async RequestGenerateCredentialBasic(uint8_t[] credName) returns (RawBytes? result);
|
||||
async RequestGenerateKeyPackage(uint8_t[] identity, uint8_t[] credential) returns (RawBytes? result);
|
||||
async RequestGroupCreate(uint8_t[] identifier, uint8_t[] credential, uint8_t[] inOptGroupIdentifier) returns (GkGroupIdEpoch? result);
|
||||
async RequestGroupJoin(uint8_t[] identifier, uint8_t[] welcome) returns (GkGroupIdEpoch? result);
|
||||
async RequestGroupAdd(uint8_t[] groupIdentifier, uint8_t[] identifier, uint8_t[] keyPackage) returns (GkMlsCommitOutput? result);
|
||||
async RequestGroupProposeAdd(uint8_t[] groupIdentifier, uint8_t[] identifier, uint8_t[] keyPackage) returns (RawBytes? result);
|
||||
async RequestGroupRemove(uint8_t[] groupIdentifier, uint8_t[] identifier, uint8_t[] remIdentifier) returns (GkMlsCommitOutput? result);
|
||||
async RequestGroupProposeRemove(uint8_t[] groupIdentifier, uint8_t[] identifier, uint8_t[] remIdentifier) returns (RawBytes? result);
|
||||
async RequestGroupClose(uint8_t[] groupIdentifier, uint8_t[] identifier) returns (GkMlsCommitOutput? result);
|
||||
async RequestGroupDetails(uint8_t[] groupIdentifier, uint8_t[] identifier) returns (GkGroupMembers? result);
|
||||
async RequestReceive(uint8_t[] identifier, uint8_t[] message) returns (GkReceived result);
|
||||
async RequestApplyPendingCommit(uint8_t[] groupIdentifier, uint8_t[] identifier) returns (GkReceived result);
|
||||
async RequestSend(uint8_t[] groupIdentifier, uint8_t[] identifier, uint8_t[] message) returns (RawBytes? result);
|
||||
async RequestExportSecret(uint8_t[] groupIdentifier, uint8_t[] identifier, uint8_t[] label, uint8_t[] content, uint64_t len) returns (GkExporterOutput? result);
|
||||
async RequestGetGroupIdentifier(uint8_t[] message) returns (RawBytes? result);
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
@@ -1,37 +0,0 @@
|
||||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
with Files("**"):
|
||||
BUG_COMPONENT = ("Core", "DOM: Security")
|
||||
|
||||
IPDL_SOURCES += [
|
||||
"PMLSTransaction.ipdl",
|
||||
]
|
||||
|
||||
EXPORTS.mozilla.dom += [
|
||||
"MLS.h",
|
||||
"MLSGroupView.h",
|
||||
"MLSLogging.h",
|
||||
"MLSTransactionChild.h",
|
||||
"MLSTransactionMessage.h",
|
||||
"MLSTransactionParent.h",
|
||||
"MLSTypeUtils.h",
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
"MLS.cpp",
|
||||
"MLSGroupView.cpp",
|
||||
"MLSTransactionChild.cpp",
|
||||
"MLSTransactionMessage.cpp",
|
||||
"MLSTransactionParent.cpp",
|
||||
"MLSTypeUtils.cpp",
|
||||
]
|
||||
|
||||
include("/ipc/chromium/chromium-config.mozbuild")
|
||||
|
||||
FINAL_LIBRARY = "xul"
|
||||
|
||||
MOCHITEST_MANIFESTS += ["tests/mochitest.toml"]
|
||||
@@ -1,47 +0,0 @@
|
||||
/* 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 strict";
|
||||
|
||||
//
|
||||
// Array equality
|
||||
//
|
||||
function arraysAreEqual(arr1, arr2) {
|
||||
if (arr1.length !== arr2.length) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < arr1.length; i++) {
|
||||
if (arr1[i] !== arr2[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// Serialization / Derserialization helpers
|
||||
//
|
||||
function stringToByteArray(str) {
|
||||
return new TextEncoder().encode(str);
|
||||
}
|
||||
|
||||
function byteArrayToString(byteArray) {
|
||||
return new TextDecoder().decode(new Uint8Array(byteArray).buffer);
|
||||
}
|
||||
|
||||
function stringToArrayBuffer(str) {
|
||||
return new Uint8Array(new TextEncoder().encode(str)).buffer;
|
||||
}
|
||||
|
||||
function byteArrayToHexString(buffer) {
|
||||
const byteArray = new Uint8Array(buffer);
|
||||
const hexParts = [];
|
||||
for (let i = 0; i < byteArray.length; i++) {
|
||||
const hex = byteArray[i].toString(16);
|
||||
const paddedHex = ("00" + hex).slice(-2);
|
||||
hexParts.push(paddedHex);
|
||||
}
|
||||
return hexParts.join("");
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
[DEFAULT]
|
||||
support-files = [
|
||||
"head_mls.js",
|
||||
]
|
||||
scheme = "https"
|
||||
prefs = [
|
||||
"security.mls.enabled=true",
|
||||
]
|
||||
|
||||
["test_derive_exporter.html"]
|
||||
|
||||
["test_group_add.html"]
|
||||
|
||||
["test_group_close.html"]
|
||||
|
||||
["test_group_create.html"]
|
||||
|
||||
["test_group_join.html"]
|
||||
|
||||
["test_group_remove.html"]
|
||||
|
||||
["test_scenario.html"]
|
||||
|
||||
["test_send_receive.html"]
|
||||
@@ -1,108 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for Messaging Layer Security</title>
|
||||
<!-- SimpleTest Helpers -->
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
<!-- Local Helpers -->
|
||||
<script src="head_mls.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
async function test_derive_exporter() {
|
||||
|
||||
const mls = new MLS();
|
||||
|
||||
// Generate Identity KeyPairs for Alice and Bob
|
||||
let alice = await mls.generateIdentity();
|
||||
let bob = await mls.generateIdentity();
|
||||
|
||||
info("Alice Client ID:", byteArrayToHexString(alice.content));
|
||||
info("Bob Client ID:", byteArrayToHexString(bob.content));
|
||||
|
||||
|
||||
// Generate Credentials for Alice and Bob
|
||||
let credential_alice = await mls.generateCredential("alice");
|
||||
let credential_bob = await mls.generateCredential("bob");
|
||||
|
||||
// Generate a KeyPackage for Bob
|
||||
let kp_bob = await mls.generateKeyPackage(bob, credential_bob);
|
||||
|
||||
// Creation of a Group by Alice
|
||||
let group_alice = await mls.groupCreate(alice, credential_alice);
|
||||
info("Group Alice:", JSON.stringify(group_alice));
|
||||
|
||||
// Get membership of the group
|
||||
let members_alice_0 = await group_alice.details();
|
||||
|
||||
// Test that the returned group membership is not null
|
||||
info("Membership @ Epoch 0:", JSON.stringify(members_alice_0));
|
||||
is(members_alice_0.members.length, 1, "There should be exactly one member in the group");
|
||||
info("Member Client ID:", byteArrayToHexString(members_alice_0.members[0].clientId));
|
||||
info("Alice Client ID:", byteArrayToHexString(alice.content));
|
||||
is(byteArrayToHexString(members_alice_0.members[0].clientId), byteArrayToHexString(alice.content), "The client ID of the member should match Alice's client ID");
|
||||
|
||||
// Alice adds Bob to a group
|
||||
let commit_output = await group_alice.add(kp_bob);
|
||||
|
||||
// Test that the returned commit output is not null
|
||||
info("Commit Output 1:", JSON.stringify(commit_output));
|
||||
isnot(byteArrayToHexString(commit_output.commit), "", "Commit Output commit should not be an empty string");
|
||||
|
||||
// Alice receives the commit
|
||||
let group_and_epoch_1_alice = await group_alice.receive(commit_output.commit);
|
||||
|
||||
// Test that the new group identifier and epoch are valid
|
||||
info("Alice's Group Identifier and Epoch:", JSON.stringify(group_and_epoch_1_alice));
|
||||
isnot(byteArrayToHexString(group_and_epoch_1_alice.groupId), "", "Group ID should not be an empty string");
|
||||
isnot(byteArrayToHexString(group_and_epoch_1_alice.groupEpoch), "", "Group Epoch should not be an empty string");
|
||||
|
||||
// Get membership of the group
|
||||
let members_alice_1 = await group_alice.details();
|
||||
|
||||
// Test that the returned group contain both Alice and Bob
|
||||
info("Membership @ Epoch 1:", JSON.stringify(members_alice_1));
|
||||
|
||||
// Test: the group should have exactly two members at epoch 1
|
||||
is(members_alice_1.members.length, 2, "There should be exactly two members in the group");
|
||||
|
||||
// Test: Bob should be in the group
|
||||
is(members_alice_1.members.some(member => byteArrayToHexString(member.clientId) === byteArrayToHexString(bob.content)), true, "Bob should be in the group");
|
||||
|
||||
// Test: Alice should be in the group
|
||||
is(members_alice_1.members.some(member => byteArrayToHexString(member.clientId) === byteArrayToHexString(alice.content)), true, "Alice should be in the group");
|
||||
|
||||
// Bob joins the group
|
||||
let group_bob = await mls.groupJoin(bob, commit_output.welcome);
|
||||
|
||||
// Test: compare the group identifier after the join
|
||||
is(byteArrayToHexString(group_alice.groupId), byteArrayToHexString(group_bob.groupId), "Alice GID == Bob GID");
|
||||
|
||||
// Create exporter labels and context
|
||||
const context_bytes = new Uint8Array([99, 111, 110, 116, 101, 120, 116]); // "context" in ASCII
|
||||
const exporter_len = 32;
|
||||
|
||||
// Alice generates an Exporter
|
||||
let exporter_alice = await group_alice.exportSecret(
|
||||
"label", context_bytes, exporter_len);
|
||||
|
||||
// Bob generates an Exporter
|
||||
let exporter_bob = await group_bob.exportSecret(
|
||||
"label", context_bytes, exporter_len);
|
||||
|
||||
// Test that exporters are identical on both side
|
||||
is(byteArrayToHexString(exporter_alice.exporter), byteArrayToHexString(exporter_bob.exporter), "Exporter Alice == Exporter Bob");
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
test_derive_exporter();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,76 +0,0 @@
|
||||
|
||||
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for Messaging Layer Security</title>
|
||||
<!-- SimpleTest Helpers -->
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
<!-- Local Helpers -->
|
||||
<script src="head_mls.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
async function test_group_add() {
|
||||
|
||||
const mls = new MLS();
|
||||
|
||||
// Generate Signature KeyPairs for Alice and Bob
|
||||
let alice = await mls.generateIdentity();
|
||||
let bob = await mls.generateIdentity();
|
||||
|
||||
// Generate Credentials for Alice and Bob
|
||||
let credential_alice = await mls.generateCredential("alice");
|
||||
let credential_bob = await mls.generateCredential("bob");
|
||||
|
||||
// Generate a KeyPackage for Bob
|
||||
let kp_bob = await mls.generateKeyPackage(bob, credential_bob);
|
||||
|
||||
// Creation of a Group by Alice
|
||||
let group_alice = await mls.groupCreate(alice, credential_alice);
|
||||
let members_alice_0 = await group_alice.details();
|
||||
|
||||
// Test: compare the group identifier to the invalid value
|
||||
info("Group ID:", byteArrayToHexString(group_alice.groupId));
|
||||
isnot(byteArrayToHexString(group_alice.groupId), "", "Group Identifier != ''");
|
||||
|
||||
// Alice adds Bob to a group
|
||||
let commit_output = await group_alice.add(kp_bob);
|
||||
|
||||
// Test: compare the commit output to the invalid value
|
||||
info("Commit Output:", byteArrayToHexString(commit_output.commit));
|
||||
isnot(byteArrayToHexString(commit_output.commit), "", "Commit != ''");
|
||||
|
||||
// Alice receives the commit
|
||||
let group_and_epoch_1_alice = await group_alice.receive(commit_output.commit);
|
||||
|
||||
// Test: make sure that the group epoch has been incremented by one
|
||||
const expectedEpoch = new Uint8Array(new BigUint64Array([1n]).buffer);
|
||||
is(byteArrayToHexString(group_and_epoch_1_alice.groupEpoch), byteArrayToHexString(expectedEpoch), "Group Epoch = 1");
|
||||
|
||||
// Get the group details
|
||||
let members_alice_1 = await group_alice.details();
|
||||
|
||||
// Test: the group should have exactly one member at epoch 0
|
||||
is(members_alice_0.members.length, 1, "There should be exactly one member in the group");
|
||||
|
||||
// Test: the group should have exactly two members at epoch 1
|
||||
is(members_alice_1.members.length, 2, "There should be exactly two members in the group");
|
||||
|
||||
// Test: compare the group members to the expected value
|
||||
is(members_alice_1.members.some(member => byteArrayToHexString(member.clientId) === byteArrayToHexString(bob.content)), true, "Bob should be in the group");
|
||||
is(members_alice_1.members.some(member => byteArrayToHexString(member.clientId) === byteArrayToHexString(alice.content)), true, "Alice should be in the group");
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
test_group_add();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,116 +0,0 @@
|
||||
|
||||
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for Messaging Layer Security</title>
|
||||
<!-- SimpleTest Helpers -->
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
<!-- Local Helpers -->
|
||||
<script src="head_mls.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
async function test_group_close() {
|
||||
|
||||
const mls = new MLS();
|
||||
|
||||
// Generate Identity KeyPairs for Alice and Bob
|
||||
let alice = await mls.generateIdentity();
|
||||
let bob = await mls.generateIdentity();
|
||||
|
||||
// Generate Credentials for Alice and Bob
|
||||
let credential_alice = await mls.generateCredential("alice");
|
||||
let credential_bob = await mls.generateCredential("bob");
|
||||
|
||||
// Generate a KeyPackage for Bob
|
||||
let kp_bob = await mls.generateKeyPackage(bob, credential_bob);
|
||||
|
||||
// Creation of a Group by Alice
|
||||
let group_alice = await mls.groupCreate(alice, credential_alice);
|
||||
|
||||
// Alice adds Bob to a group
|
||||
let commit_output = await group_alice.add(kp_bob);
|
||||
|
||||
// Alice receives her commit
|
||||
await group_alice.receive(commit_output.commit);
|
||||
|
||||
// Bob joins the group
|
||||
let group_bob = await mls.groupJoin(bob, commit_output.welcome);
|
||||
|
||||
// Test: compare the group identifier after the join
|
||||
is(byteArrayToHexString(group_alice.groupId), byteArrayToHexString(group_bob.groupId), "Alice Group ID == Bob Group ID");
|
||||
|
||||
// Test: compare the group members after the join
|
||||
let members_alice_1 = await group_alice.details();
|
||||
let members_bob_1 = await group_bob.details();
|
||||
|
||||
// Test: the group should have exactly two members at epoch 1
|
||||
is(members_alice_1.members.length, 2, "There should be exactly two members in the group");
|
||||
is(members_bob_1.members.length, 2, "There should be exactly two members in the group");
|
||||
|
||||
// Test: Bob should be in the group according to Alice's view
|
||||
is(members_alice_1.members.some(member => byteArrayToHexString(member.clientId) === byteArrayToHexString(bob.content)), true, "Bob should be in the group");
|
||||
|
||||
// Test: Alice should be in the group according to Alice's view
|
||||
is(members_alice_1.members.some(member => byteArrayToHexString(member.clientId) === byteArrayToHexString(alice.content)), true, "Alice should be in the group");
|
||||
|
||||
// Test: Bob should be in the group according to Bob's view
|
||||
is(members_bob_1.members.some(member => byteArrayToHexString(member.clientId) === byteArrayToHexString(bob.content)), true, "Bob should be in the group");
|
||||
|
||||
// Test: Alice should be in the group according to Bob's view
|
||||
is(members_bob_1.members.some(member => byteArrayToHexString(member.clientId) === byteArrayToHexString(alice.content)), true, "Alice should be in the group");
|
||||
// Bob closes the group
|
||||
let commit_output_2 = await group_bob.close();
|
||||
|
||||
// Info: print the commit output
|
||||
info("Commit Output:", JSON.stringify(commit_output_2));
|
||||
|
||||
// Alice receives the close of the group
|
||||
let group_and_epoch_final_alice = await group_alice.receive(commit_output_2.commit);
|
||||
|
||||
// Info: print the group and epoch final alice
|
||||
info("Group and Epoch Final Alice:", JSON.stringify(group_and_epoch_final_alice.groupIdEpoch));
|
||||
|
||||
// Bob processes its close of the group
|
||||
// This leaves Bob alone in the new epoch which is NOT 0xFF..FF
|
||||
let group_and_epoch_final_bob = await group_bob.receive(commit_output_2.commit);
|
||||
|
||||
// Info: print the group and epoch final Bob
|
||||
info("Group and Epoch Final Bob:", JSON.stringify(group_and_epoch_final_bob.groupIdEpoch));
|
||||
|
||||
// Test: compare the group members after the close
|
||||
let members_alice_2 = await group_alice.details();
|
||||
let members_bob_2 = await group_bob.details();
|
||||
|
||||
// This is counter intuitive, but true: Alice has two members in the group
|
||||
// after being removed by Bob, basically because she is left in her epoch.
|
||||
// Technically she can send messages in the previous epoch, but not in the current one.
|
||||
is(members_alice_2.members.length, 2, "Alice should have two members in the group");
|
||||
is(members_bob_2.members.length, 1, "Bob should be alone in the group");
|
||||
|
||||
// Test: check that alice has transitioned to the 0xFF..FF epoch
|
||||
is(byteArrayToHexString(group_and_epoch_final_alice.groupEpoch), "ffffffffffffffff", "Alice should have a returned epoch set to 0xFF..FF");
|
||||
|
||||
// Test: check that bob has transitioned to epoch 2
|
||||
const expectedEpoch = new Uint8Array(new BigUint64Array([2n]).buffer);
|
||||
is(byteArrayToHexString(group_and_epoch_final_bob.groupEpoch), byteArrayToHexString(expectedEpoch), "Bob should have transitioned to epoch 2");
|
||||
|
||||
// Bob is alone in the group and can remove its state
|
||||
let bob_deleted = await group_bob.deleteState();
|
||||
|
||||
is(bob_deleted, undefined, "Bob should have deleted his state for this group");
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
test_group_close();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,44 +0,0 @@
|
||||
|
||||
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for Messaging Layer Security</title>
|
||||
<!-- SimpleTest Helpers -->
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
<!-- Local Helpers -->
|
||||
<script src="head_mls.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
async function test_group_create() {
|
||||
|
||||
const mls = new MLS();
|
||||
|
||||
// Alice: Create signature keypair and credential
|
||||
const alice = await mls.generateIdentity();
|
||||
const credential_alice = await mls.generateCredential("alice");
|
||||
|
||||
// Alice: Create a group
|
||||
const group_alice = await mls.groupCreate(alice, credential_alice);
|
||||
|
||||
// Test: compare the generated group identifier to incorrect values
|
||||
// Note: there is no deterministic test for this value as it is generated randomly
|
||||
isnot(byteArrayToHexString(group_alice.groupId), "", "Group Identifier != ''");
|
||||
|
||||
// Test: the generated group epoch is of size 32
|
||||
is(group_alice.groupId.length, 32, "Group Epoch should be of size 32");
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
test_group_create();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,107 +0,0 @@
|
||||
|
||||
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for Messaging Layer Security</title>
|
||||
<!-- SimpleTest Helpers -->
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
<!-- Local Helpers -->
|
||||
<script src="head_mls.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
async function test_group_join() {
|
||||
|
||||
const mls = new MLS();
|
||||
|
||||
// Generate Identity KeyPairs for Alice and Bob
|
||||
let alice = await mls.generateIdentity();
|
||||
let bob = await mls.generateIdentity();
|
||||
|
||||
info("Alice Client ID:", byteArrayToHexString(alice.content));
|
||||
info("Bob Client ID:", byteArrayToHexString(bob.content));
|
||||
|
||||
|
||||
// Generate Credentials for Alice and Bob
|
||||
let credential_alice = await mls.generateCredential("alice");
|
||||
let credential_bob = await mls.generateCredential("bob");
|
||||
|
||||
// Generate a KeyPackage for Bob
|
||||
let kp_bob = await mls.generateKeyPackage(bob, credential_bob);
|
||||
|
||||
// Creation of a Group by Alice
|
||||
let group_alice = await mls.groupCreate(alice, credential_alice);
|
||||
info("Group Alice:", JSON.stringify(group_alice));
|
||||
|
||||
// Get membership of the group
|
||||
let members_alice_0 = await group_alice.details();
|
||||
|
||||
// Test that the returned group membership is not null
|
||||
info("Membership @ Epoch 0:", JSON.stringify(members_alice_0));
|
||||
is(members_alice_0.members.length, 1, "There should be exactly one member in the group");
|
||||
info("Member Client ID:", byteArrayToHexString(members_alice_0.members[0].clientId));
|
||||
info("Alice Client ID:", byteArrayToHexString(alice.content));
|
||||
is(byteArrayToHexString(members_alice_0.members[0].clientId), byteArrayToHexString(alice.content), "The client ID of the member should match Alice's client ID");
|
||||
|
||||
// Alice adds Bob to a group
|
||||
let commit_output = await group_alice.add(kp_bob);
|
||||
|
||||
// Test that the returned commit output is not null
|
||||
info("Commit Output 1:", JSON.stringify(commit_output));
|
||||
isnot(byteArrayToHexString(commit_output.commit), "", "Commit Output commit should not be an empty string");
|
||||
|
||||
// Alice receives the commit
|
||||
let group_and_epoch_1_alice = await group_alice.receive(commit_output.commit);
|
||||
|
||||
// Test that the new group identifier and epoch are valid
|
||||
info("Alice's Group Identifier and Epoch:", JSON.stringify(group_and_epoch_1_alice));
|
||||
isnot(byteArrayToHexString(group_and_epoch_1_alice.groupId), "", "Group ID should not be an empty string");
|
||||
isnot(byteArrayToHexString(group_and_epoch_1_alice.groupEpoch), "", "Group Epoch should not be an empty string");
|
||||
|
||||
// Get membership of the group
|
||||
let members_alice_1 = await group_alice.details();
|
||||
|
||||
// Bob joins the group
|
||||
let group_bob = await mls.groupJoin(bob, commit_output.welcome);
|
||||
let members_bob_1 = await group_bob.details();
|
||||
|
||||
// Test: compare the group identifier after the join
|
||||
is(byteArrayToHexString(group_alice.groupId), byteArrayToHexString(group_bob.groupId), "Alice GID == Bob GID");
|
||||
|
||||
// Test: the group should have two members
|
||||
info("Membership @ Epoch 1:", JSON.stringify(members_alice_1));
|
||||
is(members_alice_1.members.length, 2, "There should be exactly two members in the group");
|
||||
|
||||
// Test: the group should have exactly two members at epoch 0
|
||||
is(members_alice_0.members.length, 1, "There should be exactly one member in the group");
|
||||
|
||||
// Test: the group should have exactly two members at epoch 1
|
||||
is(members_alice_1.members.length, 2, "There should be exactly two members in the group");
|
||||
is(members_bob_1.members.length, 2, "There should be exactly two members in the group");
|
||||
|
||||
// Test: Bob should be in the group according to Alice's view
|
||||
is(members_alice_1.members.some(member => byteArrayToHexString(member.clientId) === byteArrayToHexString(bob.content)), true, "Bob should be in the group");
|
||||
|
||||
// Test: Alice should be in the group according to Alice's view
|
||||
is(members_alice_1.members.some(member => byteArrayToHexString(member.clientId) === byteArrayToHexString(alice.content)), true, "Alice should be in the group");
|
||||
|
||||
// Test: Bob should be in the group according to Bob's view
|
||||
is(members_bob_1.members.some(member => byteArrayToHexString(member.clientId) === byteArrayToHexString(bob.content)), true, "Bob should be in the group");
|
||||
|
||||
// Test: Alice should be in the group according to Bob's view
|
||||
is(members_bob_1.members.some(member => byteArrayToHexString(member.clientId) === byteArrayToHexString(alice.content)), true, "Alice should be in the group");
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
test_group_join();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,94 +0,0 @@
|
||||
|
||||
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for Messaging Layer Security</title>
|
||||
<!-- SimpleTest Helpers -->
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
<!-- Local Helpers -->
|
||||
<script src="head_mls.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
async function test_group_remove() {
|
||||
|
||||
const mls = new MLS();
|
||||
|
||||
// Generate Signature KeyPairs for Alice and Bob
|
||||
let alice = await mls.generateIdentity();
|
||||
let bob = await mls.generateIdentity();
|
||||
|
||||
// Generate Credentials for Alice and Bob
|
||||
let credential_alice = await mls.generateCredential("alice");
|
||||
let credential_bob = await mls.generateCredential("bob");
|
||||
|
||||
// Generate a KeyPackage for Bob
|
||||
let kp_bob = await mls.generateKeyPackage(bob, credential_bob);
|
||||
|
||||
// Creation of a Group by Alice
|
||||
let group = await mls.groupCreate(alice, credential_alice);
|
||||
|
||||
// Get membership of the group
|
||||
let membership_0 = await group.details();
|
||||
|
||||
// Test that the returned group membership is not null
|
||||
info("Membership @ Epoch 0:", JSON.stringify(membership_0));
|
||||
is(membership_0.members.length, 1, "There should be one member in the group");
|
||||
|
||||
// Alice adds Bob to a group
|
||||
let commit_output = await group.add(kp_bob);
|
||||
|
||||
// Test that the returned commit output is not null
|
||||
info("Commit 1:", byteArrayToHexString(commit_output.commit));
|
||||
isnot(byteArrayToHexString(commit_output.commit), "", "Commit != ''");
|
||||
|
||||
// Alice receives the commit
|
||||
await group.receive(commit_output.commit);
|
||||
|
||||
// Get membership of the group
|
||||
let membership_1 = await group.details();
|
||||
|
||||
// Test that the returned group membership is not null
|
||||
info("Membership @ Epoch 1:", JSON.stringify(membership_1));
|
||||
is(membership_1.members.length, 2, "There should be two members in the group");
|
||||
|
||||
// Alice removes Bob from the group
|
||||
let commit_output_2 = await group.remove(bob);
|
||||
|
||||
// Alice receives the commit
|
||||
await group.receive(commit_output_2.commit);
|
||||
|
||||
// Get membership of the group
|
||||
let membership_2 = await group.details();
|
||||
|
||||
// Test that the returned group membership is not null
|
||||
info("Membership @ Epoch 2:", JSON.stringify(membership_2));
|
||||
is(membership_2.members.length, 1, "There should be one member in the group");
|
||||
|
||||
// Verify that Alice is the only member in the group at epoch 0
|
||||
is(membership_0.members.length, 1, "Alice should be alone in the group at epoch 0");
|
||||
is(membership_0.members.some(member => byteArrayToHexString(member.clientId) === byteArrayToHexString(alice.content)), true, "Alice should be alone in the group at epoch 0");
|
||||
|
||||
// Verify that both Alice and Bob are members in the group at epoch 1
|
||||
is(membership_1.members.length, 2, "There should be two members in the group at epoch 1");
|
||||
is(membership_1.members.some(member => byteArrayToHexString(member.clientId) === byteArrayToHexString(bob.content)), true, "Bob should be in the group at epoch 1");
|
||||
is(membership_1.members.some(member => byteArrayToHexString(member.clientId) === byteArrayToHexString(alice.content)), true, "Alice should be in the group at epoch 1");
|
||||
|
||||
// Verify that Alice is the only member in the group at epoch 2
|
||||
is(membership_2.members.length, 1, "Alice should be alone in the group at epoch 2");
|
||||
is(membership_2.members.some(member => byteArrayToHexString(member.clientId) === byteArrayToHexString(alice.content)), true, "Alice should be alone in the group at epoch 2");
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
test_group_remove();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,77 +0,0 @@
|
||||
|
||||
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for Messaging Layer Security</title>
|
||||
<!-- SimpleTest Helpers -->
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
<!-- Local Helpers -->
|
||||
<script src="head_mls.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
async function test_scenario() {
|
||||
|
||||
const mls = new MLS();
|
||||
|
||||
// Alice: Create signature keypair and credential
|
||||
const alice = await mls.generateIdentity();
|
||||
const alice_credential = await mls.generateCredential("alice");
|
||||
|
||||
// Bob: Create signature keypair and credential
|
||||
const bob = await mls.generateIdentity();
|
||||
const bob_credential = await mls.generateCredential("bob");
|
||||
|
||||
|
||||
// Bob: Generate a key package
|
||||
const bob_key_package = await mls.generateKeyPackage(bob, bob_credential);
|
||||
|
||||
// Alice: Create a group
|
||||
let group_alice = await mls.groupCreate(alice, alice_credential);
|
||||
|
||||
// Alice: Add Bob to the group
|
||||
let commit_output = await group_alice.add(bob_key_package);
|
||||
|
||||
// Alice: process her Add commit
|
||||
await group_alice.receive(commit_output.commit);
|
||||
|
||||
// Bob: Join the group
|
||||
let group_bob = await mls.groupJoin(bob, commit_output.welcome);
|
||||
|
||||
// Test: compare group identifier from Alice and Bob
|
||||
is(byteArrayToHexString(group_alice.groupId), byteArrayToHexString(group_bob.groupId), "Alice GID == Bob GID");
|
||||
|
||||
// Alice & Bob: Export a secret
|
||||
const context_bytes = new Uint8Array([99, 111, 110, 116, 101, 120, 116]); // "context" in ASCII
|
||||
|
||||
const exportAlice = await group_alice.exportSecret("label", context_bytes, 15);
|
||||
const exportBob = await group_bob.exportSecret("label", context_bytes, 15);
|
||||
|
||||
// Test: compare exporter from Alice and Bob
|
||||
is(byteArrayToHexString(exportAlice.exporter), byteArrayToHexString(exportBob.exporter), "Exporter Alice == Exporter Bob");
|
||||
|
||||
// Bob: send a message to the group
|
||||
const message = new Uint8Array([72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 32, 33]); // "Hello World !" in ASCII
|
||||
const ctx = await group_bob.send(message);
|
||||
|
||||
// Alice: receive a message from the group
|
||||
const pt = await group_alice.receive(ctx);
|
||||
info("Alice received a message from Bob: " + JSON.stringify(pt));
|
||||
|
||||
// Test: compare the message and the decrypted message
|
||||
is(byteArrayToHexString(message), byteArrayToHexString(pt.content), "Plaintext == Decrypted Message");
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
test_scenario();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,73 +0,0 @@
|
||||
|
||||
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for Messaging Layer Security</title>
|
||||
<!-- SimpleTest Helpers -->
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
<!-- Local Helpers -->
|
||||
<script src="head_mls.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
async function test_send_receive() {
|
||||
|
||||
const mls = new MLS();
|
||||
|
||||
// Generate Signature KeyPairs for Alice and Bob
|
||||
let alice = await mls.generateIdentity();
|
||||
let bob = await mls.generateIdentity();
|
||||
|
||||
// Generate Credentials for Alice and Bob
|
||||
let credential_alice = await mls.generateCredential("alice");
|
||||
let credential_bob = await mls.generateCredential("bob");
|
||||
|
||||
// Generate a KeyPackage for Bob
|
||||
let kp_bob = await mls.generateKeyPackage(bob, credential_bob);
|
||||
|
||||
// Creation of a Group by Alice
|
||||
let group_alice = await mls.groupCreate(alice, credential_alice);
|
||||
|
||||
// Alice adds Bob to a group
|
||||
let commit_output = await group_alice.add(kp_bob);
|
||||
|
||||
// Test: the returned commit output is not null
|
||||
info("Commit Output 1:", JSON.stringify(commit_output));
|
||||
isnot(JSON.stringify(commit_output), "", "Commit Output != ''");
|
||||
|
||||
// Alice receives the commit
|
||||
let group_and_epoch_1_alice = await group_alice.receive(commit_output.commit);
|
||||
|
||||
// Info: the new group identifier and epoch are valid
|
||||
info("Alice Group Identifier and Epoch:", JSON.stringify(group_and_epoch_1_alice));
|
||||
|
||||
// Bob joins the group
|
||||
let group_bob = await mls.groupJoin(bob, commit_output.welcome);
|
||||
|
||||
// Test: compare the group identifier after the join
|
||||
is(byteArrayToHexString(group_alice.groupId), byteArrayToHexString(group_bob.groupId), "Alice GID == Bob GID");
|
||||
|
||||
// Bob sends a message to Alice
|
||||
const message = new Uint8Array([72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 32, 33]); // "Hello World !" in ASCII
|
||||
let ciphertext_bytes = await group_bob.send(message);
|
||||
|
||||
// Alice decrypts the message from Bob
|
||||
let received_message = await group_alice.receive(ciphertext_bytes);
|
||||
|
||||
// Test: compare the generated group identifier to incorrect values
|
||||
is(byteArrayToHexString(message), byteArrayToHexString(received_message.content), "Message Sent == Message Decrypted");
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
test_send_receive();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
@@ -55,7 +55,6 @@ DIRS += [
|
||||
"mathml",
|
||||
"media",
|
||||
"midi",
|
||||
"mls",
|
||||
"notification",
|
||||
"power",
|
||||
"push",
|
||||
|
||||
@@ -1,133 +0,0 @@
|
||||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/.
|
||||
*/
|
||||
|
||||
enum MLSObjectType {
|
||||
"group-epoch",
|
||||
"group-identifier",
|
||||
"group-info",
|
||||
"client-identifier",
|
||||
"credential-basic",
|
||||
"key-package",
|
||||
"proposal",
|
||||
"commit-output",
|
||||
"commit-processed",
|
||||
"welcome",
|
||||
"exporter-output",
|
||||
"exporter-label",
|
||||
"exporter-context",
|
||||
"application-message-ciphertext",
|
||||
"application-message-plaintext",
|
||||
};
|
||||
|
||||
dictionary MLSBytes {
|
||||
required MLSObjectType type;
|
||||
required Uint8Array content;
|
||||
};
|
||||
|
||||
dictionary MLSGroupMember {
|
||||
required Uint8Array clientId;
|
||||
required Uint8Array credential;
|
||||
};
|
||||
|
||||
dictionary MLSGroupDetails {
|
||||
required MLSObjectType type;
|
||||
required Uint8Array groupId;
|
||||
required Uint8Array groupEpoch;
|
||||
required sequence<MLSGroupMember> members;
|
||||
};
|
||||
|
||||
dictionary MLSCommitOutput {
|
||||
required MLSObjectType type;
|
||||
required Uint8Array groupId;
|
||||
required Uint8Array commit;
|
||||
Uint8Array welcome;
|
||||
Uint8Array groupInfo;
|
||||
Uint8Array ratchetTree;
|
||||
Uint8Array clientId;
|
||||
};
|
||||
|
||||
dictionary MLSExporterOutput {
|
||||
required MLSObjectType type;
|
||||
required Uint8Array groupId;
|
||||
required Uint8Array groupEpoch;
|
||||
required Uint8Array label;
|
||||
required Uint8Array context;
|
||||
required Uint8Array secret;
|
||||
};
|
||||
|
||||
dictionary MLSReceived {
|
||||
required MLSObjectType type;
|
||||
required Uint8Array groupId;
|
||||
Uint8Array groupEpoch;
|
||||
Uint8Array content;
|
||||
MLSCommitOutput commitOutput;
|
||||
};
|
||||
|
||||
typedef MLSBytes MLSClientId;
|
||||
typedef MLSBytes MLSGroupId;
|
||||
typedef MLSBytes MLSCredential;
|
||||
typedef MLSBytes MLSKeyPackage;
|
||||
typedef MLSBytes MLSProposal;
|
||||
typedef (MLSBytes or Uint8Array) MLSBytesOrUint8Array;
|
||||
typedef (Uint8Array or UTF8String) Uint8ArrayOrUTF8String;
|
||||
typedef (MLSBytes or Uint8Array or UTF8String) MLSBytesOrUint8ArrayOrUTF8String;
|
||||
|
||||
[Pref="security.mls.enabled",
|
||||
SecureContext,
|
||||
Exposed=(Window,Worker)]
|
||||
interface MLS {
|
||||
[Throws]
|
||||
constructor();
|
||||
[Throws]
|
||||
Promise<undefined> deleteState();
|
||||
[Throws]
|
||||
Promise<MLSClientId> generateIdentity();
|
||||
[Throws]
|
||||
Promise<MLSCredential> generateCredential(MLSBytesOrUint8ArrayOrUTF8String credentialContent);
|
||||
[Throws]
|
||||
Promise<MLSKeyPackage> generateKeyPackage(MLSBytesOrUint8Array clientId, MLSBytesOrUint8Array credential);
|
||||
[Throws]
|
||||
Promise<MLSGroupView> groupCreate(MLSBytesOrUint8Array clientId, MLSBytesOrUint8Array credential);
|
||||
[Throws]
|
||||
Promise<MLSGroupView?> groupGet(MLSBytesOrUint8Array groupId, MLSBytesOrUint8Array clientId);
|
||||
[Throws]
|
||||
Promise<MLSGroupView> groupJoin(MLSBytesOrUint8Array clientId, MLSBytesOrUint8Array welcome);
|
||||
// Utility functions
|
||||
[Throws]
|
||||
Promise<MLSGroupId> getGroupIdFromMessage(MLSBytesOrUint8Array message);
|
||||
};
|
||||
|
||||
[Pref="security.mls.enabled",
|
||||
SecureContext,
|
||||
Exposed=(Window,Worker)]
|
||||
interface MLSGroupView {
|
||||
[Throws]
|
||||
readonly attribute Uint8Array groupId;
|
||||
[Throws]
|
||||
readonly attribute Uint8Array clientId;
|
||||
[Throws]
|
||||
Promise<undefined> deleteState();
|
||||
[Throws]
|
||||
Promise<MLSCommitOutput> add(MLSBytesOrUint8Array keyPackage);
|
||||
[Throws]
|
||||
Promise<MLSProposal> proposeAdd(MLSBytesOrUint8Array keyPackage);
|
||||
[Throws]
|
||||
Promise<MLSCommitOutput> remove(MLSBytesOrUint8Array remClientId);
|
||||
[Throws]
|
||||
Promise<MLSProposal> proposeRemove(MLSBytesOrUint8Array remClientId);
|
||||
[Throws]
|
||||
Promise<MLSCommitOutput> close();
|
||||
[Throws]
|
||||
Promise<MLSGroupDetails> details();
|
||||
[Throws]
|
||||
Promise<MLSBytes> send(MLSBytesOrUint8ArrayOrUTF8String message);
|
||||
[Throws]
|
||||
Promise<MLSReceived> receive(MLSBytesOrUint8Array message);
|
||||
[Throws]
|
||||
Promise<MLSReceived> applyPendingCommit();
|
||||
[Throws]
|
||||
Promise<MLSExporterOutput> exportSecret(MLSBytesOrUint8ArrayOrUTF8String label, MLSBytesOrUint8Array context, unsigned long long length);
|
||||
};
|
||||
@@ -766,7 +766,6 @@ WEBIDL_FILES = [
|
||||
"MIDIPort.webidl",
|
||||
"MimeType.webidl",
|
||||
"MimeTypeArray.webidl",
|
||||
"MLS.webidl",
|
||||
"MouseEvent.webidl",
|
||||
"MouseScrollEvent.webidl",
|
||||
"MozFrameLoaderOwner.webidl",
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
#include "mozilla/dom/MIDIManagerParent.h"
|
||||
#include "mozilla/dom/MIDIPlatformService.h"
|
||||
#include "mozilla/dom/MIDIPortParent.h"
|
||||
#include "mozilla/dom/MLSTransactionParent.h"
|
||||
#include "mozilla/dom/MessagePortParent.h"
|
||||
#include "mozilla/dom/PGamepadEventChannelParent.h"
|
||||
#include "mozilla/dom/PGamepadTestChannelParent.h"
|
||||
@@ -83,7 +82,6 @@ using mozilla::dom::MIDIPortParent;
|
||||
using mozilla::dom::PMessagePortParent;
|
||||
using mozilla::dom::PMIDIManagerParent;
|
||||
using mozilla::dom::PMIDIPortParent;
|
||||
using mozilla::dom::PMLSTransactionParent;
|
||||
using mozilla::dom::PServiceWorkerContainerParent;
|
||||
using mozilla::dom::PServiceWorkerParent;
|
||||
using mozilla::dom::PServiceWorkerRegistrationParent;
|
||||
@@ -1165,82 +1163,6 @@ mozilla::ipc::IPCResult BackgroundParentImpl::RecvHasMIDIDevice(
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
// NOTE: Only accessed on the background thread.
|
||||
static StaticRefPtr<nsISerialEventTarget> sMLSTaskQueue;
|
||||
|
||||
class MLSTaskQueueShutdownTask final : public nsITargetShutdownTask {
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
void TargetShutdown() override { sMLSTaskQueue = nullptr; }
|
||||
|
||||
private:
|
||||
~MLSTaskQueueShutdownTask() = default;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(MLSTaskQueueShutdownTask, nsITargetShutdownTask)
|
||||
|
||||
mozilla::ipc::IPCResult BackgroundParentImpl::RecvCreateMLSTransaction(
|
||||
Endpoint<PMLSTransactionParent>&& aEndpoint,
|
||||
NotNull<nsIPrincipal*> aPrincipal) {
|
||||
AssertIsInMainProcess();
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
if (!aEndpoint.IsValid()) {
|
||||
return IPC_FAIL(this, "invalid endpoint for MLSTransaction");
|
||||
}
|
||||
|
||||
if (!sMLSTaskQueue) {
|
||||
nsCOMPtr<nsISerialEventTarget> taskQueue;
|
||||
MOZ_ALWAYS_SUCCEEDS(NS_CreateBackgroundTaskQueue(
|
||||
"MLSTaskQueue", getter_AddRefs(taskQueue)));
|
||||
sMLSTaskQueue = taskQueue.forget();
|
||||
|
||||
// Clean up the sMLSTaskQueue static when the PBackground thread shuts down.
|
||||
nsCOMPtr<nsITargetShutdownTask> shutdownTask =
|
||||
new MLSTaskQueueShutdownTask();
|
||||
MOZ_ALWAYS_SUCCEEDS(
|
||||
GetCurrentSerialEventTarget()->RegisterShutdownTask(shutdownTask));
|
||||
}
|
||||
|
||||
// Construct the database's prefix path
|
||||
nsCOMPtr<nsIFile> file;
|
||||
nsresult rv =
|
||||
mozilla::dom::MLSTransactionParent::ConstructDatabasePrefixPath(file);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
// The enpoint's destructor will close the actor
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
// Dispatch the task to the MLS task queue
|
||||
sMLSTaskQueue->Dispatch(NS_NewRunnableFunction(
|
||||
"CreateMLSTransactionRunnable",
|
||||
[endpoint = std::move(aEndpoint), file,
|
||||
principal = RefPtr{aPrincipal.get()}]() mutable {
|
||||
// Create the mls directory if it doesn't exist
|
||||
nsresult rv =
|
||||
mozilla::dom::MLSTransactionParent::CreateDirectoryIfNotExists(
|
||||
file);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Construct the database's full path
|
||||
nsAutoCString databasePath;
|
||||
rv = mozilla::dom::MLSTransactionParent::ConstructDatabaseFullPath(
|
||||
file, principal, databasePath);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the MLS transaction parent and bind it to the endpoint
|
||||
RefPtr<PMLSTransactionParent> result =
|
||||
new mozilla::dom::MLSTransactionParent(databasePath);
|
||||
endpoint.Bind(result);
|
||||
}));
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
already_AddRefed<mozilla::dom::PClientManagerParent>
|
||||
BackgroundParentImpl::AllocPClientManagerParent() {
|
||||
return mozilla::dom::AllocClientManagerParent();
|
||||
|
||||
@@ -296,10 +296,6 @@ class BackgroundParentImpl : public PBackgroundParent {
|
||||
mozilla::ipc::IPCResult RecvHasMIDIDevice(
|
||||
HasMIDIDeviceResolver&& aResolver) override;
|
||||
|
||||
mozilla::ipc::IPCResult RecvCreateMLSTransaction(
|
||||
Endpoint<PMLSTransactionParent>&& aEndpoint,
|
||||
NotNull<nsIPrincipal*> aPrincipal) override;
|
||||
|
||||
mozilla::ipc::IPCResult RecvStorageActivity(
|
||||
const PrincipalInfo& aPrincipalInfo) override;
|
||||
|
||||
|
||||
@@ -37,7 +37,6 @@ include protocol PCameras;
|
||||
include protocol PLockManager;
|
||||
include protocol PMIDIManager;
|
||||
include protocol PMIDIPort;
|
||||
include protocol PMLSTransaction;
|
||||
include protocol PQuota;
|
||||
include protocol PServiceWorker;
|
||||
include protocol PServiceWorkerContainer;
|
||||
@@ -265,8 +264,6 @@ parent:
|
||||
MIDIPortInfo portInfo, bool sysexEnabled);
|
||||
async HasMIDIDevice() returns (bool hasDevice);
|
||||
|
||||
async CreateMLSTransaction(Endpoint<PMLSTransactionParent> aEndpoint, nsIPrincipal aPrincipal);
|
||||
|
||||
// This method is used to propagate storage activities from the child actor
|
||||
// to the parent actor. See StorageActivityService.
|
||||
async StorageActivity(PrincipalInfo principalInfo);
|
||||
|
||||
@@ -16271,12 +16271,6 @@
|
||||
value: true
|
||||
mirror: always
|
||||
|
||||
# MLS
|
||||
- name: security.mls.enabled
|
||||
type: RelaxedAtomicBool
|
||||
value: false
|
||||
mirror: always
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Prefs starting with "signon."
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
[package]
|
||||
name = "mls_gk"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license = "Apache-2.0 OR MIT"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
nserror = { path = "../../../xpcom/rust/nserror" }
|
||||
nsstring = { path = "../../../xpcom/rust/nsstring" }
|
||||
xpcom = { path = "../../../xpcom/rust/xpcom" }
|
||||
static_prefs = { path = "../../../modules/libpref/init/static_prefs" }
|
||||
|
||||
mls-platform-api = { git = "https://github.com/beurdouche/mls-platform-api", rev="7fb935bb93fdcc80f7f5e76d516c85a540024b53", features = ["gecko"] }
|
||||
nss-gk-api = { git = "https://github.com/beurdouche/nss-gk-api", rev = "82e780f47026b84a0e0a06bff17fa95661d129a3", default-features = false }
|
||||
thin-vec = { version = "^0.2.12", features = ["gecko-ffi"] }
|
||||
hex = "^0.4.3"
|
||||
rusqlite = "^0.31.0"
|
||||
log = "^0.4.20"
|
||||
@@ -1,43 +0,0 @@
|
||||
header = """/* 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/. */"""
|
||||
autogen_warning = """/* DO NOT MODIFY THIS MANUALLY! This file was generated using cbindgen. See RunCbindgen.py */
|
||||
"""
|
||||
include_guard = "mozilla_dom_MlsGkFFI_h"
|
||||
include_version = true
|
||||
braces = "SameLine"
|
||||
line_length = 100
|
||||
tab_width = 2
|
||||
language = "C++"
|
||||
includes = ["nsStringFwd.h", "nsTArrayForwardDeclare.h"]
|
||||
# Put FFI calls in the `mozilla::security::mls` namespace.
|
||||
namespaces = ["mozilla", "security", "mls"]
|
||||
|
||||
[enum]
|
||||
derive_const_casts = true
|
||||
derive_tagged_enum_destructor = true
|
||||
cast_assert_name = "MOZ_DIAGNOSTIC_ASSERT"
|
||||
|
||||
[export.body]
|
||||
"GkReceived" = """
|
||||
GkReceived() : tag(Tag::None) {}
|
||||
GkReceived(GkReceived&& other) : tag(other.tag) {
|
||||
switch (tag) {
|
||||
case Tag::ApplicationMessage:
|
||||
new (&application_message) ApplicationMessage_Body(std::move(other.application_message));
|
||||
break;
|
||||
case Tag::GroupIdEpoch:
|
||||
new (&group_id_epoch) GroupIdEpoch_Body(std::move(other.group_id_epoch));
|
||||
break;
|
||||
case Tag::CommitOutput:
|
||||
new (&commit_output) CommitOutput_Body(std::move(other.commit_output));
|
||||
break;
|
||||
case Tag::None:
|
||||
break;
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
# Export `ThinVec` references as `nsTArray`.
|
||||
[export.rename]
|
||||
"ThinVec" = "nsTArray"
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,44 +0,0 @@
|
||||
/* 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 nsstring::nsACString;
|
||||
use std::fs;
|
||||
use std::io;
|
||||
|
||||
pub fn get_storage_path(storage_prefix: &nsACString) -> String {
|
||||
format!("{storage_prefix}.sqlite.enc")
|
||||
}
|
||||
|
||||
pub fn get_key_path(storage_prefix: &nsACString) -> String {
|
||||
format!("{storage_prefix}.key")
|
||||
}
|
||||
|
||||
fn read_existing_storage_key(key_path: &str) -> io::Result<[u8; 32]> {
|
||||
let key_hex = fs::read_to_string(key_path)?;
|
||||
let bytes = hex::decode(&key_hex).map_err(|e| io::Error::other(e))?;
|
||||
bytes[..].try_into().map_err(|e| io::Error::other(e))
|
||||
}
|
||||
|
||||
pub fn get_storage_key(storage_prefix: &nsACString) -> io::Result<[u8; 32]> {
|
||||
// Get the key path
|
||||
let key_path = get_key_path(storage_prefix);
|
||||
|
||||
// Try to read the existing key
|
||||
if let Ok(key) = read_existing_storage_key(&key_path) {
|
||||
return Ok(key);
|
||||
}
|
||||
|
||||
// We failed to read the key, so it must either not exist, or is invalid.
|
||||
// Generate a new one.
|
||||
nss_gk_api::init();
|
||||
let key: [u8; 32] = nss_gk_api::p11::random(32)[..]
|
||||
.try_into()
|
||||
.expect("nss returned the wrong number of bytes");
|
||||
|
||||
// Write the key to the file
|
||||
std::fs::write(key_path, &hex::encode(&key))?;
|
||||
|
||||
// Return the key
|
||||
Ok(key)
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
with Files("**"):
|
||||
BUG_COMPONENT = ("Core", "Security: MLS")
|
||||
|
||||
if CONFIG["COMPILE_ENVIRONMENT"]:
|
||||
CbindgenHeader("mls_gk_ffi_generated.h", inputs=["/security/mls/mls_gk"])
|
||||
|
||||
EXPORTS.mozilla.security.mls += [
|
||||
"!mls_gk_ffi_generated.h",
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = "xul"
|
||||
@@ -7,10 +7,6 @@
|
||||
with Files("**"):
|
||||
BUG_COMPONENT = ("Core", "Security: PSM")
|
||||
|
||||
DIRS += [
|
||||
"/security/mls",
|
||||
]
|
||||
|
||||
with Files("generate*.py"):
|
||||
BUG_COMPONENT = ("Firefox Build System", "General")
|
||||
|
||||
|
||||
@@ -1679,11 +1679,6 @@ who = "Mike Hommey <mh+mozilla@glandium.org>"
|
||||
criteria = "safe-to-deploy"
|
||||
delta = "2.3.2 -> 2.3.3"
|
||||
|
||||
[[audits.debug_tree]]
|
||||
who = "Benjamin Beurdouche <beurdouche@mozilla.com>"
|
||||
criteria = "safe-to-deploy"
|
||||
version = "0.4.0"
|
||||
|
||||
[[audits.debugid]]
|
||||
who = "Gabriele Svelto <gsvelto@mozilla.com>"
|
||||
criteria = "safe-to-deploy"
|
||||
@@ -3036,11 +3031,6 @@ who = "Mike Hommey <mh+mozilla@glandium.org>"
|
||||
criteria = "safe-to-deploy"
|
||||
delta = "0.1.9 -> 0.1.10"
|
||||
|
||||
[[audits.maybe-async]]
|
||||
who = "Benjamin Beurdouche <beurdouche@mozilla.com>"
|
||||
criteria = "safe-to-deploy"
|
||||
version = "0.2.10"
|
||||
|
||||
[[audits.md-5]]
|
||||
who = "Dana Keeler <dkeeler@mozilla.com>"
|
||||
criteria = "safe-to-deploy"
|
||||
@@ -3228,95 +3218,6 @@ who = "Mike Hommey <mh+mozilla@glandium.org>"
|
||||
criteria = "safe-to-deploy"
|
||||
delta = "0.8.8 -> 1.0.1"
|
||||
|
||||
[[audits.mls-rs]]
|
||||
who = "Benjamin Beurdouche <beurdouche@mozilla.com>"
|
||||
criteria = "safe-to-deploy"
|
||||
version = "0.39.1"
|
||||
|
||||
[[audits.mls-rs]]
|
||||
who = "Benjamin Beurdouche <beurdouche@mozilla.com>"
|
||||
criteria = "safe-to-deploy"
|
||||
delta = "0.39.1 -> 0.39.1@git:96eb66e158c86171c70ff8147c0e5f020e54f3d1"
|
||||
importable = false
|
||||
|
||||
[[audits.mls-rs-codec]]
|
||||
who = "Benjamin Beurdouche <beurdouche@mozilla.com>"
|
||||
criteria = "safe-to-deploy"
|
||||
version = "0.5.3"
|
||||
|
||||
[[audits.mls-rs-codec]]
|
||||
who = "Benjamin Beurdouche <beurdouche@mozilla.com>"
|
||||
criteria = "safe-to-deploy"
|
||||
delta = "0.5.3 -> 0.5.3@git:96eb66e158c86171c70ff8147c0e5f020e54f3d1"
|
||||
importable = false
|
||||
|
||||
[[audits.mls-rs-codec-derive]]
|
||||
who = "Benjamin Beurdouche <beurdouche@mozilla.com>"
|
||||
criteria = "safe-to-deploy"
|
||||
version = "0.1.1"
|
||||
notes = "No unsafe code"
|
||||
|
||||
[[audits.mls-rs-codec-derive]]
|
||||
who = "Benjamin Beurdouche <beurdouche@mozilla.com>"
|
||||
criteria = "safe-to-deploy"
|
||||
delta = "0.1.1 -> 0.1.1@git:96eb66e158c86171c70ff8147c0e5f020e54f3d1"
|
||||
importable = false
|
||||
|
||||
[[audits.mls-rs-core]]
|
||||
who = "Benjamin Beurdouche <beurdouche@mozilla.com>"
|
||||
criteria = "safe-to-deploy"
|
||||
version = "0.18.0"
|
||||
|
||||
[[audits.mls-rs-core]]
|
||||
who = "Benjamin Beurdouche <beurdouche@mozilla.com>"
|
||||
criteria = "safe-to-deploy"
|
||||
delta = "0.18.0 -> 0.18.0@git:96eb66e158c86171c70ff8147c0e5f020e54f3d1"
|
||||
importable = false
|
||||
|
||||
[[audits.mls-rs-crypto-hpke]]
|
||||
who = "Benjamin Beurdouche <beurdouche@mozilla.com>"
|
||||
criteria = "safe-to-deploy"
|
||||
version = "0.9.0"
|
||||
|
||||
[[audits.mls-rs-crypto-hpke]]
|
||||
who = "Benjamin Beurdouche <beurdouche@mozilla.com>"
|
||||
criteria = "safe-to-deploy"
|
||||
delta = "0.9.0 -> 0.9.0@git:96eb66e158c86171c70ff8147c0e5f020e54f3d1"
|
||||
importable = false
|
||||
|
||||
[[audits.mls-rs-crypto-traits]]
|
||||
who = "Benjamin Beurdouche <beurdouche@mozilla.com>"
|
||||
criteria = "safe-to-deploy"
|
||||
version = "0.10.0"
|
||||
|
||||
[[audits.mls-rs-crypto-traits]]
|
||||
who = "Benjamin Beurdouche <beurdouche@mozilla.com>"
|
||||
criteria = "safe-to-deploy"
|
||||
delta = "0.10.0 -> 0.10.0@git:96eb66e158c86171c70ff8147c0e5f020e54f3d1"
|
||||
importable = false
|
||||
|
||||
[[audits.mls-rs-identity-x509]]
|
||||
who = "Benjamin Beurdouche <beurdouche@mozilla.com>"
|
||||
criteria = "safe-to-deploy"
|
||||
version = "0.11.0"
|
||||
|
||||
[[audits.mls-rs-identity-x509]]
|
||||
who = "Benjamin Beurdouche <beurdouche@mozilla.com>"
|
||||
criteria = "safe-to-deploy"
|
||||
delta = "0.11.0 -> 0.11.0@git:96eb66e158c86171c70ff8147c0e5f020e54f3d1"
|
||||
importable = false
|
||||
|
||||
[[audits.mls-rs-provider-sqlite]]
|
||||
who = "Benjamin Beurdouche <beurdouche@mozilla.com>"
|
||||
criteria = "safe-to-deploy"
|
||||
version = "0.11.0"
|
||||
|
||||
[[audits.mls-rs-provider-sqlite]]
|
||||
who = "Benjamin Beurdouche <beurdouche@mozilla.com>"
|
||||
criteria = "safe-to-deploy"
|
||||
delta = "0.11.0 -> 0.11.0@git:96eb66e158c86171c70ff8147c0e5f020e54f3d1"
|
||||
importable = false
|
||||
|
||||
[[audits.moz_cbor]]
|
||||
who = "Bobby Holley <bobbyholley@gmail.com>"
|
||||
criteria = "safe-to-deploy"
|
||||
@@ -3457,12 +3358,6 @@ criteria = "safe-to-deploy"
|
||||
version = "0.2.1"
|
||||
notes = "Maintained by the CryptoEng team at Mozilla."
|
||||
|
||||
[[audits.nss-gk-api]]
|
||||
who = "Benjamin Beurdouche <beurdouche@mozilla.com>"
|
||||
criteria = "safe-to-deploy"
|
||||
delta = "0.3.0 -> 0.3.0@git:82e780f47026b84a0e0a06bff17fa95661d129a3"
|
||||
importable = false
|
||||
|
||||
[[audits.ntapi]]
|
||||
who = "Mike Hommey <mh+mozilla@glandium.org>"
|
||||
criteria = "safe-to-deploy"
|
||||
@@ -5829,20 +5724,6 @@ who = "Makoto Kato <m_kato@ga2.so-net.ne.jp>"
|
||||
criteria = "safe-to-deploy"
|
||||
version = "0.1.3"
|
||||
|
||||
[[audits.zeroize]]
|
||||
who = "Benjamin Beurdouche <beurdouche@mozilla.com>"
|
||||
criteria = "safe-to-deploy"
|
||||
version = "1.8.1"
|
||||
notes = """
|
||||
This code DOES contain unsafe code required to internally call volatiles
|
||||
for deleting data. This is expected and documented behavior.
|
||||
"""
|
||||
|
||||
[[audits.zeroize_derive]]
|
||||
who = "Benjamin Beurdouche <beurdouche@mozilla.com>"
|
||||
criteria = "safe-to-deploy"
|
||||
version = "1.4.2"
|
||||
|
||||
[[audits.zerovec]]
|
||||
who = "Makoto Kato <m_kato@ga2.so-net.ne.jp>"
|
||||
criteria = "safe-to-deploy"
|
||||
|
||||
@@ -107,38 +107,6 @@ notes = "This is a first-party crate which is also published to crates.io. We ce
|
||||
audit-as-crates-io = true
|
||||
notes = "This is a pinned version of the upstream code, presumably to get a fix that hadn't been released yet. We should consider switching to the latest official release."
|
||||
|
||||
[policy.mls-rs]
|
||||
audit-as-crates-io = true
|
||||
notes = "This is a pinned version of the upstream code, pending update of the crate."
|
||||
|
||||
[policy.mls-rs-codec]
|
||||
audit-as-crates-io = true
|
||||
notes = "This is a pinned version of the upstream code, pending update of the crate."
|
||||
|
||||
[policy.mls-rs-codec-derive]
|
||||
audit-as-crates-io = true
|
||||
notes = "This is a pinned version of the upstream code, pending update of the crate."
|
||||
|
||||
[policy.mls-rs-core]
|
||||
audit-as-crates-io = true
|
||||
notes = "This is a pinned version of the upstream code, pending update of the crate."
|
||||
|
||||
[policy.mls-rs-crypto-hpke]
|
||||
audit-as-crates-io = true
|
||||
notes = "This is a pinned version of the upstream code, pending update of the crate."
|
||||
|
||||
[policy.mls-rs-crypto-traits]
|
||||
audit-as-crates-io = true
|
||||
notes = "This is a pinned version of the upstream code, pending update of the crate."
|
||||
|
||||
[policy.mls-rs-identity-x509]
|
||||
audit-as-crates-io = true
|
||||
notes = "This is a pinned version of the upstream code, pending update of the crate."
|
||||
|
||||
[policy.mls-rs-provider-sqlite]
|
||||
audit-as-crates-io = true
|
||||
notes = "This is a pinned version of the upstream code, pending update of the crate."
|
||||
|
||||
[policy.mozbuild]
|
||||
audit-as-crates-io = false
|
||||
notes = "The crates.io version of this is just a placeholder to allow public crates to depend on mozbuild."
|
||||
@@ -178,10 +146,6 @@ audit-as-crates-io = false
|
||||
audit-as-crates-io = true
|
||||
notes = "Part of the wgpu repository, pinned as the rest of wgpu crates."
|
||||
|
||||
[policy.nss-gk-api]
|
||||
audit-as-crates-io = true
|
||||
notes = "This is a pinned version of the upstream code, pending update of the crate."
|
||||
|
||||
[policy.peek-poke]
|
||||
audit-as-crates-io = false
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
{"files":{"Cargo.lock":"5b4889dcaf005a28bd9848285763cda32c56c79f962572a7c9fdceee055752d7","Cargo.toml":"e95e92797cc7fdbdabef380743d3059e2dbb1f75986fd85937734431f5dfe49c","README.md":"46f7f4ae53e01156d7cdcd001c0b015ba506ae214520515686e6291e8c75b56e","doc/build/LICENSE.adoc":"56cd47a25f2bbb4f2f870c933aa04c47a2b68dea497f974c5c9c215583dec3dc","doc/build/asciidoc-coalescer.rb":"d3b8c4a9d02862b31e4fae1d3eba88104d24eae2e96e16cce50830931db49564","doc/readme_template.adoc":"d4fc1343d2a4caa1e2a76962c94d382c7cb6b38fcd82a34794670a593bfe78e2","examples/fibonacci.rs":"3c7b804e9ba14d3fe16f34240f5aae81c3b15a56db245c3d22ea0940fff3f9e1","examples/multi_line.rs":"21819b40021d852501e42aa4903131a257774d4972c28c4370967869e3f4bed8","examples/multiple_trees.rs":"0acbdfbf8a786de05a2272e24b0b54f0f2b8b37b245e8d032e92014fbe6d1e53","examples/nested.rs":"731e5d6993aac90a580050b871bb0b2399c9ae85d48efbf101c19118ae51223e","examples/no_macros.rs":"e9a92b2c444b67804e0949150dcb84b084efe1c2d74201a01f2f8a7d9419f93d","examples/out/fibonacci.txt":"75f837935a3f905010f9d8d7c825c9d8a444cb0930ad0c16f809abeee12ef782","examples/out/multi_line.txt":"a435cc9100fb9b2fed67e64a0aa337beebe818846de91c0c0c6e46a12e3122c7","examples/out/multiple_trees_A.txt":"0b9e72ec56efdd95052ffc1429ba14e548d72254abee89d737c38707c93152a3","examples/out/multiple_trees_B.txt":"380271246c3b7b7261701d5c845990d90f9bc5317bdb07ed03a395c47c09cbcf","examples/out/nested.txt":"089ade90dd6ffd2d3b86e8a60ed3be98895ccf4e2f22cdf069c5391c3161fc4f","examples/out/no_macros.txt":"3f9781067a454a462873683bb148a1b776cd8b7be30b10b25910fb734987f14e","examples/out/panic.txt":"0a95e6dcbb3be0721a21e3762d7e6fe21458ff773c0c9d141c92b63f9f03851a","examples/panic.rs":"3e8decf97d800539b6b2e7cbd681b114ed8a757d74841f717a7c3786f6d60c3e","src/default.rs":"1ef1cad3332cdda1f4c5ae09208c3b7d19244db186f671c796beda38135d268a","src/defer.rs":"621ca6e25f054765b995108916cb607b064e852cceaf2f21c20ef50fd2a5386c","src/internal.rs":"11756608f9968f649a9a2b8ed435126711a530b500d47c6a59fa0a7557ecb028","src/lib.rs":"3c20a1971517a8fb397bd9af54aee2caea7aaf3d9c973272a7b5238a93a2c495","src/scoped_branch.rs":"ff693738aff85ea925e3613f52787dbb6bb0add753ea8792207728ea403b2e1e","src/test.rs":"acc5cbb59203a62727c6e788e656f48df24ab744ccff98f6f0a0db03ac1ecdb1","src/tree_config.rs":"4d0f77f5e81f5c74996d6d8c44019ade6d5e3a1ff88c1be466af2f0d675ca0b3"},"package":"2d1ec383f2d844902d3c34e4253ba11ae48513cdaddc565cf1a6518db09a8e57"}
|
||||
212
third_party/rust/debug_tree/Cargo.lock
generated
vendored
212
third_party/rust/debug_tree/Cargo.lock
generated
vendored
@@ -1,212 +0,0 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "130aac562c0dd69c56b3b1cc8ffd2e17be31d0b6c25b61c96b76231aa23e39e1"
|
||||
|
||||
[[package]]
|
||||
name = "debug_tree"
|
||||
version = "0.4.0"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"once_cell",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c329ae8753502fb44ae4fc2b622fa2a94652c41e795143765ba0927f92ab780"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-executor",
|
||||
"futures-io",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0c77d04ce8edd9cb903932b608268b3fffec4163dc053b3b402bf47eac1f1a8"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f25592f769825e89b92358db00d26f965761e094951ac44d3663ef25b7ac464a"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f674f3e1bcb15b37284a90cedf55afdba482ab061c407a9c0ebbd0f3109741ba"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a638959aa96152c7a4cddf50fcb1e3fede0583b27157c26e67d6f99904090dc6"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a5081aa3de1f7542a794a397cde100ed903b0630152d0973479018fd85423a7"
|
||||
dependencies = [
|
||||
"proc-macro-hack",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3466821b4bc114d95b087b850a724c6f83115e929bc88f1fa98a3304a944c8a6"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b0a34e53cf6cdcd0178aa573aed466b646eb3db769570841fda0c7ede375a27"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22766cf25d64306bedf0384da004d05c9974ab104fcc4528f1236181c18004c5"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-macro",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"memchr",
|
||||
"pin-utils",
|
||||
"proc-macro-hack",
|
||||
"proc-macro-nested",
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1c601810575c99596d4afc46f78a678c80105117c379eb3650cf99b8a21ce5b"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "237844750cfbb86f67afe27eee600dfbbcb6188d734139b534cbfbf4f96792ae"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
version = "0.1.0-alpha.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-hack"
|
||||
version = "0.5.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f918f2b601f93baa836c1c2945faef682ba5b6d4828ecb45eeb7cc3c71b811b4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-nested"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e946095f9d3ed29ec38de908c22f95d9ac008e424c7bcae54c75a79c527c694"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c09721c6781493a2a492a96b5a5bf19b65917fe6728884e7c44dd0c60ca3435"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "123bd9499cfb380418d509322d7a6d52e5315f064fe4b3ad18a53d6b92c07859"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "0.2.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fa5e81d6bc4e67fe889d5783bd2a128ab2e0cfa487e0be16b6a8d177b101616"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"memchr",
|
||||
"pin-project-lite",
|
||||
"tokio-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
|
||||
33
third_party/rust/debug_tree/Cargo.toml
vendored
33
third_party/rust/debug_tree/Cargo.toml
vendored
@@ -1,33 +0,0 @@
|
||||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g., crates.io) dependencies
|
||||
#
|
||||
# If you believe there's an error in this file please file an
|
||||
# issue against the rust-lang/cargo repository. If you're
|
||||
# editing this file be aware that the upstream Cargo.toml
|
||||
# will likely look very different (and much more reasonable)
|
||||
|
||||
[package]
|
||||
edition = "2018"
|
||||
name = "debug_tree"
|
||||
version = "0.4.0"
|
||||
authors = ["Marty Papamanolis <marty@mindpipess.com>"]
|
||||
description = "Build a tree one element at a time and output it as a pretty string."
|
||||
readme = "README.md"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/martypapa/debug-tree"
|
||||
|
||||
[lib]
|
||||
name = "debug_tree"
|
||||
crate-type = ["lib"]
|
||||
[dependencies.once_cell]
|
||||
version = "1"
|
||||
[dev-dependencies.futures]
|
||||
version = "0.3.4"
|
||||
|
||||
[dev-dependencies.tokio]
|
||||
version = "0.2.9"
|
||||
features = ["macros", "fs"]
|
||||
284
third_party/rust/debug_tree/README.md
vendored
284
third_party/rust/debug_tree/README.md
vendored
@@ -1,284 +0,0 @@
|
||||
# Debug Tree
|
||||
|
||||
This library allows you to build a tree one element at a time and output it as a pretty string.
|
||||
|
||||
The tree can easily be output to a `String`, `stdout` or a file.
|
||||
|
||||
This is particularly convenient for generating clean output from nested and recursive functions.
|
||||
|
||||
* [Recursive Fibonacci Example](#recursive-fibonacci-example)
|
||||
* [Overview](#overview)
|
||||
* [More Examples](#more-examples)
|
||||
* [Multiple Tagged Trees](#multiple-tagged-trees)
|
||||
* [Nested Functions](#nested-functions)
|
||||
* [Panic](#panics)
|
||||
* [Without Macros](#without-macros)
|
||||
|
||||
|
||||
## Recursive Fibonacci Example
|
||||
|
||||
Using the `add_branch!()` macro at the start of the `factors()` function, you can generate an entire call tree, with minimal effort.
|
||||
|
||||
<!--{ fibonacci.rs | code: rust }-->
|
||||
```rust
|
||||
use debug_tree::*;
|
||||
|
||||
fn factors(x: usize) {
|
||||
add_branch!("{}", x); // <~ THE MAGIC LINE
|
||||
for i in 1..x {
|
||||
if x % i == 0 {
|
||||
factors(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// output to file at the end of this block
|
||||
defer_write!("examples/out/fibonacci.txt");
|
||||
add_branch!("A Fibonacci Tree");
|
||||
factors(6);
|
||||
add_leaf!("That's All Folks!");
|
||||
}
|
||||
```
|
||||
<!--{ end }-->
|
||||
|
||||
<!--{ out/fibonacci.txt | code }-->
|
||||
```
|
||||
A Fibonacci Tree
|
||||
├╼ 6
|
||||
│ ├╼ 1
|
||||
│ ├╼ 2
|
||||
│ │ └╼ 1
|
||||
│ └╼ 3
|
||||
│ └╼ 1
|
||||
└╼ That's All Folks!
|
||||
```
|
||||
<!--{ end }-->
|
||||
|
||||
## Overview
|
||||
|
||||
- Add a branch
|
||||
- `add_branch!("Hello, {}", "World")`
|
||||
- The branch will exit at the end of the current block
|
||||
|
||||
- Add a leaf
|
||||
- `add_leaf!("I am a {}", "leaf")`
|
||||
- Added to the current scoped branch
|
||||
|
||||
- Print a tree, or write it to file at the end of a block
|
||||
- `defer_print!()`
|
||||
- `defer_write!("filename.txt")`
|
||||
- The tree will be empty after these calls
|
||||
- To prevent clearing, use `defer_peek_print!` and `defer_peek_write!`
|
||||
|
||||
|
||||
- Handle multiple trees using named trees
|
||||
- `add_branch_to!("A", "I'm a branch on tree 'A'")`
|
||||
- `add_leaf_to!("A", "I'm a leaf on tree 'A'")`
|
||||
- `defer_print!("A")`
|
||||
- `defer_write!("A", "filename.txt")`
|
||||
|
||||
- Get a named tree
|
||||
- `tree("TREE_NAME")`
|
||||
|
||||
- Retrieve the pretty-string from a tree
|
||||
- `tree("TREE_NAME").string()`
|
||||
|
||||
|
||||
- Usage across threads
|
||||
- `default_tree()` is local to each thread
|
||||
- Named trees are shared between threads
|
||||
|
||||
## More Examples
|
||||
|
||||
### Multiple Tagged Trees
|
||||
|
||||
If you need multiple, separated trees you can use a name tag.
|
||||
|
||||
<!--{ multiple_trees.rs | code: rust }-->
|
||||
```rust
|
||||
use debug_tree::*;
|
||||
|
||||
fn populate(tree_name: &str, n_children: usize) {
|
||||
add_branch_to!(tree_name, "{} TREE", tree_name);
|
||||
for _ in 0..n_children {
|
||||
populate(tree_name, n_children / 2);
|
||||
}
|
||||
}
|
||||
fn main() {
|
||||
// Override tree config (just for "B")
|
||||
let b_tree = tree("B");
|
||||
b_tree.set_config_override(
|
||||
TreeConfig::new()
|
||||
.indent(4)
|
||||
.symbols(TreeSymbols::with_rounded().leaf("> ")),
|
||||
);
|
||||
defer_write!(b_tree, "examples/out/multiple_trees_B.txt");
|
||||
defer_write!("A", "examples/out/multiple_trees_A.txt");
|
||||
|
||||
populate("A", 2);
|
||||
populate("B", 3);
|
||||
}
|
||||
```
|
||||
<!--{ end }-->
|
||||
<!--{ out/multiple_trees_A.txt | code }-->
|
||||
```
|
||||
A TREE
|
||||
├╼ A TREE
|
||||
│ └╼ A TREE
|
||||
└╼ A TREE
|
||||
└╼ A TREE
|
||||
```
|
||||
<!--{ end }-->
|
||||
<!--{ out/multiple_trees_B.txt | code }-->
|
||||
```
|
||||
B TREE
|
||||
├──> B TREE
|
||||
│ ╰──> B TREE
|
||||
├──> B TREE
|
||||
│ ╰──> B TREE
|
||||
╰──> B TREE
|
||||
╰──> B TREE
|
||||
```
|
||||
<!--{ end }-->
|
||||
|
||||
### Nested Functions
|
||||
|
||||
Branches also make nested function calls a lot easier to follow.
|
||||
|
||||
<!--{ nested.rs | code: rust }-->
|
||||
```rust
|
||||
use debug_tree::*;
|
||||
fn a() {
|
||||
add_branch!("a");
|
||||
b();
|
||||
c();
|
||||
}
|
||||
fn b() {
|
||||
add_branch!("b");
|
||||
c();
|
||||
}
|
||||
fn c() {
|
||||
add_branch!("c");
|
||||
add_leaf!("Nothing to see here");
|
||||
}
|
||||
|
||||
fn main() {
|
||||
defer_write!("examples/out/nested.txt");
|
||||
a();
|
||||
}
|
||||
```
|
||||
<!--{ end }-->
|
||||
<!--{ out/nested.txt | code }-->
|
||||
```
|
||||
a
|
||||
├╼ b
|
||||
│ └╼ c
|
||||
│ └╼ Nothing to see here
|
||||
└╼ c
|
||||
└╼ Nothing to see here
|
||||
```
|
||||
<!--{ end }-->
|
||||
|
||||
### Line Breaks
|
||||
|
||||
Newlines in multi-line strings are automatically indented.
|
||||
|
||||
<!--{ multi_line.rs | code: rust }-->
|
||||
```rust
|
||||
use debug_tree::*;
|
||||
fn main() {
|
||||
// output to file at the end of this block
|
||||
defer_write!("examples/out/multi_line.txt");
|
||||
add_branch!("1");
|
||||
add_leaf!("1.1\nAnother line...\n... and one more line");
|
||||
add_leaf!("1.2");
|
||||
}
|
||||
```
|
||||
<!--{ end }-->
|
||||
|
||||
<!--{ out/multi_line.txt | code }-->
|
||||
```
|
||||
1
|
||||
├╼ 1.1
|
||||
│ Another line...
|
||||
│ ... and one more line
|
||||
└╼ 1.2
|
||||
```
|
||||
<!--{ end }-->
|
||||
|
||||
### Panics
|
||||
Even if there is a panic, the tree is not lost!
|
||||
The `defer_` functions were introduced to allow the tree
|
||||
to be printed our written to file in the case of a `panic!` or early return.
|
||||
|
||||
<!--{ panic.rs | code: rust }-->
|
||||
```rust
|
||||
use debug_tree::*;
|
||||
|
||||
fn i_will_panic() {
|
||||
add_branch!("Here are my last words");
|
||||
add_leaf!("Stay calm, and try not to panic");
|
||||
panic!("I told you so...")
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// output to file at the end of this block
|
||||
defer_write!("examples/out/panic.txt");
|
||||
// print at the end of this block
|
||||
{
|
||||
add_branch!("By using the 'defer_' functions");
|
||||
add_branch!("Output will still be generated");
|
||||
add_branch!("Otherwise you might lose your valuable tree!");
|
||||
}
|
||||
add_branch!("Now for something crazy...");
|
||||
i_will_panic();
|
||||
}
|
||||
```
|
||||
<!--{ end }-->
|
||||
|
||||
<!--{ out/panic.txt | code }-->
|
||||
```
|
||||
By using the 'defer_' functions
|
||||
└╼ Output will still be generated
|
||||
└╼ Otherwise you might lose your valuable tree!
|
||||
Now for something crazy...
|
||||
└╼ Here are my last words
|
||||
└╼ Stay calm, and try not to panic
|
||||
```
|
||||
<!--{ end }-->
|
||||
|
||||
|
||||
### Without Macros
|
||||
|
||||
If you prefer not using macros, you can construct `TreeBuilder`s manually.
|
||||
|
||||
<!--{ no_macros.rs | code: rust }-->
|
||||
```rust
|
||||
use debug_tree::TreeBuilder;
|
||||
|
||||
fn main() {
|
||||
// Make a new tree.
|
||||
let tree = TreeBuilder::new();
|
||||
|
||||
// Add a scoped branch. The next item added will belong to the branch.
|
||||
let mut branch = tree.add_branch("1 Branch");
|
||||
|
||||
// Add a leaf to the current branch
|
||||
tree.add_leaf("1.1 Child");
|
||||
|
||||
// Leave scope early
|
||||
branch.release();
|
||||
tree.add_leaf("2 Sibling");
|
||||
// output to file
|
||||
tree.write("examples/out/no_macros.txt").ok(); // Write and flush.
|
||||
}
|
||||
```
|
||||
<!--{ end }-->
|
||||
<!--{ out/no_macros.txt | code }-->
|
||||
```
|
||||
1 Branch
|
||||
└╼ 1.1 Child
|
||||
2 Sibling
|
||||
```
|
||||
<!--{ end }-->
|
||||
@@ -1,19 +0,0 @@
|
||||
Copyright (C) 2014-2019 The Asciidoctor Project
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -1,66 +0,0 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
# This script coalesces the AsciiDoc content from a document master into a
|
||||
# single output file. It does so by resolving all preprocessor directives in
|
||||
# the document, and in any files which are included. The resolving of include
|
||||
# directives is likely of most interest to users of this script.
|
||||
#
|
||||
# This script works by using Asciidoctor's PreprocessorReader to read and
|
||||
# resolve all the lines in the specified input file. The script then writes the
|
||||
# result to the output.
|
||||
#
|
||||
# The script only recognizes attributes passed in as options or those defined
|
||||
# in the document header. It does not currently process attributes defined in
|
||||
# other, arbitrary locations within the document.
|
||||
#
|
||||
# You can find a similar extension written against AsciidoctorJ here:
|
||||
# https://github.com/hibernate/hibernate-asciidoctor-extensions/blob/master/src/main/java/org/hibernate/infra/asciidoctor/extensions/savepreprocessed/SavePreprocessedOutputPreprocessor.java
|
||||
|
||||
# TODO
|
||||
# - add cli option to write attributes passed to cli to header of document
|
||||
# - escape all preprocessor directives after lines are processed (these are preprocessor directives that were escaped in the input)
|
||||
# - wrap in a custom converter so it can be used as an extension
|
||||
|
||||
require 'asciidoctor'
|
||||
require 'optparse'
|
||||
|
||||
options = { attributes: [], output: '-' }
|
||||
OptionParser.new do |opts|
|
||||
opts.banner = 'Usage: ruby asciidoc-coalescer.rb [OPTIONS] FILE'
|
||||
opts.on('-a', '--attribute key[=value]', 'A document attribute to set in the form of key[=value]') do |a|
|
||||
options[:attributes] << a
|
||||
end
|
||||
opts.on('-o', '--output FILE', 'Write output to FILE instead of stdout.') do |o|
|
||||
options[:output] = o
|
||||
end
|
||||
end.parse!
|
||||
|
||||
unless (source_file = ARGV.shift)
|
||||
warn 'Please specify an AsciiDoc source file to coalesce.'
|
||||
exit 1
|
||||
end
|
||||
|
||||
unless (output_file = options[:output]) == '-'
|
||||
if (output_file = File.expand_path output_file) == (File.expand_path source_file)
|
||||
warn 'Source and output cannot be the same file.'
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
|
||||
# NOTE first, resolve attributes defined at the end of the document header
|
||||
# QUESTION can we do this in a single load?
|
||||
doc = Asciidoctor.load_file source_file, safe: :unsafe, header_only: true, attributes: options[:attributes]
|
||||
# NOTE quick and dirty way to get the attributes set or unset by the document header
|
||||
header_attr_names = (doc.instance_variable_get :@attributes_modified).to_a
|
||||
header_attr_names.each {|k| doc.attributes[%(#{k}!)] = '' unless doc.attr? k }
|
||||
|
||||
doc = Asciidoctor.load_file source_file, safe: :unsafe, parse: false, attributes: doc.attributes
|
||||
# FIXME also escape ifdef, ifndef, ifeval and endif directives
|
||||
# FIXME do this more carefully by reading line by line; if input differs by output by leading backslash, restore original line
|
||||
lines = doc.reader.read.gsub(/^include::(?=.*\[\]$)/m, '\\include::')
|
||||
|
||||
if output_file == '-'
|
||||
puts lines
|
||||
else
|
||||
File.open(output_file, 'w') {|f| f.write lines }
|
||||
end
|
||||
127
third_party/rust/debug_tree/doc/readme_template.adoc
vendored
127
third_party/rust/debug_tree/doc/readme_template.adoc
vendored
@@ -1,127 +0,0 @@
|
||||
:examples: ../examples/
|
||||
|
||||
= Debug Tree
|
||||
|
||||
This library allows you to build a tree one element at a time and output it as a pretty string.
|
||||
|
||||
The tree can easily be output to a `String`, `stdout` or a file.
|
||||
|
||||
This is particularly convenient for generating clean output from nested and recursive functions.
|
||||
|
||||
|
||||
:toc:
|
||||
|
||||
== Recursive Fibonacci Example
|
||||
|
||||
Using the `add_branch!()` macro at the start of the `factors()` function, you can generate an entire call tree, with minimal effort.
|
||||
|
||||
[source,rust]
|
||||
----
|
||||
include::{examples}fibonacci.rs[]
|
||||
----
|
||||
|
||||
----
|
||||
include::{examples}out/fibonacci.txt[]
|
||||
----
|
||||
== Overview
|
||||
|
||||
* Add a branch
|
||||
- `add_branch!("Hello, {}", "World")`
|
||||
- The branch will exit at the end of the current block
|
||||
|
||||
* Add a leaf
|
||||
- `add_leaf!("I am a {}", "leaf")`
|
||||
- Added to the current scoped branch
|
||||
|
||||
* Print a tree, or write it to file at the end of a block
|
||||
- `defer_print!()`
|
||||
- `defer_write!("filename.txt")`
|
||||
- The tree will be empty after these calls
|
||||
- To prevent clearing, use `defer_peek_print!` and `defer_peek_write!`
|
||||
|
||||
* Get the trees pretty-string
|
||||
-
|
||||
|
||||
* Handle multiple trees using named trees
|
||||
- `add_branch_to!("A", "I'm a branch on tree 'A'")`
|
||||
- `add_leaf_to!("A", "I'm a leaf on tree 'A'")`
|
||||
- `defer_print!("A")`
|
||||
- `defer_write!("A", "filename.txt")`
|
||||
|
||||
* Get a named tree
|
||||
- `tree("TREE_NAME")`
|
||||
|
||||
* Retrieve the pretty-string from a tree
|
||||
- `tree("TREE_NAME").string()`
|
||||
|
||||
|
||||
* Usage across threads
|
||||
- `default_tree()` is local to each thread
|
||||
- Named trees are shared between threads
|
||||
|
||||
== More Examples
|
||||
|
||||
=== Multiple Tagged Trees
|
||||
|
||||
If you need multiple, separated trees you can use a name tag.
|
||||
|
||||
[source,rust]
|
||||
----
|
||||
include::{examples}multiple_trees.rs[]
|
||||
----
|
||||
----
|
||||
include::{examples}out/multiple_trees_A.txt[]
|
||||
----
|
||||
----
|
||||
include::{examples}out/multiple_trees_B.txt[]
|
||||
----
|
||||
|
||||
=== Nested Functions
|
||||
|
||||
Branches also make nested function calls a lot easier to follow.
|
||||
|
||||
[source,rust]
|
||||
----
|
||||
include::{examples}nested.rs[]
|
||||
----
|
||||
----
|
||||
include::{examples}out/nested.txt[]
|
||||
----
|
||||
|
||||
=== Line Breaks
|
||||
|
||||
Newlines in multi-line strings are automatically indented.
|
||||
|
||||
[source,rust]
|
||||
----
|
||||
include::{examples}multi_line.rs[]
|
||||
----
|
||||
----
|
||||
include::{examples}out/multi_line.txt[]
|
||||
----
|
||||
|
||||
=== Panics
|
||||
Even if there is a panic, the tree is not lost!
|
||||
The `defer_` functions were introduced to allow the tree
|
||||
to be printed our written to file in the case of a `panic!` or early return.
|
||||
|
||||
[source,rust]
|
||||
----
|
||||
include::{examples}panic.rs[]
|
||||
----
|
||||
----
|
||||
include::{examples}out/panic.txt[]
|
||||
----
|
||||
|
||||
|
||||
=== Without Macros
|
||||
|
||||
If you prefer not using macros, you can construct `TreeBuilder`s manually.
|
||||
|
||||
[source,rust]
|
||||
----
|
||||
include::{examples}no_macros.rs[]
|
||||
----
|
||||
----
|
||||
include::{examples}out/no_macros.txt[]
|
||||
----
|
||||
@@ -1,18 +0,0 @@
|
||||
use debug_tree::*;
|
||||
|
||||
fn factors(x: usize) {
|
||||
add_branch!("{}", x); // <~ THE MAGIC LINE
|
||||
for i in 1..x {
|
||||
if x % i == 0 {
|
||||
factors(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// output to file at the end of this block
|
||||
defer_write!("examples/out/fibonacci.txt");
|
||||
add_branch!("A Fibonacci Tree");
|
||||
factors(6);
|
||||
add_leaf!("That's All Folks!");
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
use debug_tree::*;
|
||||
fn main() {
|
||||
// output to file at the end of this block
|
||||
defer_write!("examples/out/multi_line.txt");
|
||||
add_branch!("1");
|
||||
add_leaf!("1.1\nAnother line...\n... and one more line");
|
||||
add_leaf!("1.2");
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
use debug_tree::*;
|
||||
|
||||
fn populate(tree_name: &str, n_children: usize) {
|
||||
add_branch_to!(tree_name, "{} TREE", tree_name);
|
||||
for _ in 0..n_children {
|
||||
populate(tree_name, n_children / 2);
|
||||
}
|
||||
}
|
||||
fn main() {
|
||||
// Override tree config (just for "B")
|
||||
let b_tree = tree("B");
|
||||
b_tree.set_config_override(
|
||||
TreeConfig::new()
|
||||
.indent(4)
|
||||
.symbols(TreeSymbols::with_rounded().leaf("> ")),
|
||||
);
|
||||
defer_write!(b_tree, "examples/out/multiple_trees_B.txt");
|
||||
defer_write!("A", "examples/out/multiple_trees_A.txt");
|
||||
|
||||
populate("A", 2);
|
||||
populate("B", 3);
|
||||
}
|
||||
19
third_party/rust/debug_tree/examples/nested.rs
vendored
19
third_party/rust/debug_tree/examples/nested.rs
vendored
@@ -1,19 +0,0 @@
|
||||
use debug_tree::*;
|
||||
fn a() {
|
||||
add_branch!("a");
|
||||
b();
|
||||
c();
|
||||
}
|
||||
fn b() {
|
||||
add_branch!("b");
|
||||
c();
|
||||
}
|
||||
fn c() {
|
||||
add_branch!("c");
|
||||
add_leaf!("Nothing to see here");
|
||||
}
|
||||
|
||||
fn main() {
|
||||
defer_write!("examples/out/nested.txt");
|
||||
a();
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
use debug_tree::TreeBuilder;
|
||||
|
||||
fn main() {
|
||||
// Make a new tree.
|
||||
let tree = TreeBuilder::new();
|
||||
|
||||
// Add a scoped branch. The next item added will belong to the branch.
|
||||
let mut branch = tree.add_branch("1 Branch");
|
||||
|
||||
// Add a leaf to the current branch
|
||||
tree.add_leaf("1.1 Child");
|
||||
|
||||
// Leave scope early
|
||||
branch.release();
|
||||
tree.add_leaf("2 Sibling");
|
||||
// output to file
|
||||
tree.write("examples/out/no_macros.txt").ok(); // Write and flush.
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
A Fibonacci Tree
|
||||
├╼ 6
|
||||
│ ├╼ 1
|
||||
│ ├╼ 2
|
||||
│ │ └╼ 1
|
||||
│ └╼ 3
|
||||
│ └╼ 1
|
||||
└╼ That's All Folks!
|
||||
@@ -1,5 +0,0 @@
|
||||
1
|
||||
├╼ 1.1
|
||||
│ Another line...
|
||||
│ ... and one more line
|
||||
└╼ 1.2
|
||||
@@ -1,5 +0,0 @@
|
||||
A TREE
|
||||
├╼ A TREE
|
||||
│ └╼ A TREE
|
||||
└╼ A TREE
|
||||
└╼ A TREE
|
||||
@@ -1,7 +0,0 @@
|
||||
B TREE
|
||||
├──> B TREE
|
||||
│ ╰──> B TREE
|
||||
├──> B TREE
|
||||
│ ╰──> B TREE
|
||||
╰──> B TREE
|
||||
╰──> B TREE
|
||||
@@ -1,6 +0,0 @@
|
||||
a
|
||||
├╼ b
|
||||
│ └╼ c
|
||||
│ └╼ Nothing to see here
|
||||
└╼ c
|
||||
└╼ Nothing to see here
|
||||
@@ -1,3 +0,0 @@
|
||||
1 Branch
|
||||
└╼ 1.1 Child
|
||||
2 Sibling
|
||||
@@ -1,6 +0,0 @@
|
||||
By using the 'defer_' functions
|
||||
└╼ Output will still be generated
|
||||
└╼ Otherwise you might lose your valuable tree!
|
||||
Now for something crazy...
|
||||
└╼ Here are my last words
|
||||
└╼ Stay calm, and try not to panic
|
||||
20
third_party/rust/debug_tree/examples/panic.rs
vendored
20
third_party/rust/debug_tree/examples/panic.rs
vendored
@@ -1,20 +0,0 @@
|
||||
use debug_tree::*;
|
||||
|
||||
fn i_will_panic() {
|
||||
add_branch!("Here are my last words");
|
||||
add_leaf!("Stay calm, and try not to panic");
|
||||
panic!("I told you so...")
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// output to file at the end of this block
|
||||
defer_write!("examples/out/panic.txt");
|
||||
// print at the end of this block
|
||||
{
|
||||
add_branch!("By using the 'defer_' functions");
|
||||
add_branch!("Output will still be generated");
|
||||
add_branch!("Otherwise you might lose your valuable tree!");
|
||||
}
|
||||
add_branch!("Now for something crazy...");
|
||||
i_will_panic();
|
||||
}
|
||||
167
third_party/rust/debug_tree/src/default.rs
vendored
167
third_party/rust/debug_tree/src/default.rs
vendored
@@ -1,167 +0,0 @@
|
||||
use crate::TreeBuilder;
|
||||
|
||||
/// Returns the default tree for the current thread
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use debug_tree::default_tree;
|
||||
/// default_tree().add_leaf("A new leaf");
|
||||
/// assert_eq!("A new leaf", default_tree().peek_string());
|
||||
/// ```
|
||||
pub fn default_tree() -> TreeBuilder {
|
||||
thread_local! {
|
||||
static DEFAULT_BUILDER: TreeBuilder = TreeBuilder::new();
|
||||
}
|
||||
DEFAULT_BUILDER.with(|f| f.clone())
|
||||
}
|
||||
|
||||
/// Adds a leaf to the default tree with the given text and formatting arguments
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `text...` - Formatted text arguments, as per `format!(...)`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// #[macro_use]
|
||||
/// use debug_tree::{default_tree, add_leaf};
|
||||
/// fn main() {
|
||||
/// add_leaf!("A {} leaf", "new");
|
||||
/// assert_eq!("A new leaf", &default_tree().peek_string());
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! add_leaf {
|
||||
($($arg:tt)*) => {
|
||||
if $crate::default::default_tree().is_enabled() {
|
||||
$crate::default::default_tree().add_leaf(&format!($($arg)*))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Adds the value as a leaf to the default tree.
|
||||
///
|
||||
/// Returns the given `value` argument.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `value` - An expression that implements the `Display` trait.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// #[macro_use]
|
||||
/// use debug_tree::{default_tree, add_leaf_value};
|
||||
/// fn main() {
|
||||
/// let value = add_leaf_value!(10);
|
||||
/// assert_eq!("10", &default_tree().string());
|
||||
/// assert_eq!(10, value);
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! add_leaf_value {
|
||||
($value:expr) => {{
|
||||
let v = $value;
|
||||
if $crate::default::default_tree().is_enabled() {
|
||||
$crate::default::default_tree().add_leaf(&format!("{}", &v));
|
||||
}
|
||||
v
|
||||
}};
|
||||
}
|
||||
|
||||
/// Adds a scoped branch to the default tree with the given text and formatting arguments
|
||||
/// The branch will be exited at the end of the current block.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `text...` - Formatted text arguments, as per `format!(...)`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// #[macro_use]
|
||||
/// use debug_tree::{default_tree, add_branch, add_leaf};
|
||||
/// fn main() {
|
||||
/// {
|
||||
/// add_branch!("New {}", "Branch"); // _branch enters scope
|
||||
/// // tree is now pointed inside new branch.
|
||||
/// add_leaf!("Child of {}", "Branch");
|
||||
/// // Block ends, so tree exits the current branch.
|
||||
/// }
|
||||
/// add_leaf!("Sibling of {}", "Branch");
|
||||
/// assert_eq!("\
|
||||
/// New Branch
|
||||
/// └╼ Child of Branch
|
||||
/// Sibling of Branch" , &default_tree().string());
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! add_branch {
|
||||
() => {
|
||||
let _debug_tree_branch = if $crate::default::default_tree().is_enabled() {
|
||||
$crate::default::default_tree().enter_scoped()
|
||||
} else {
|
||||
$crate::scoped_branch::ScopedBranch::none()
|
||||
};
|
||||
};
|
||||
($($arg:tt)*) => {
|
||||
let _debug_tree_branch = if $crate::default::default_tree().is_enabled() {
|
||||
$crate::default::default_tree().add_branch(&format!($($arg)*))
|
||||
} else {
|
||||
$crate::scoped_branch::ScopedBranch::none()
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::default_tree;
|
||||
use crate::*;
|
||||
|
||||
#[test]
|
||||
fn unnamed_branch() {
|
||||
add_leaf!("1");
|
||||
add_branch!();
|
||||
add_leaf!("1.1");
|
||||
{
|
||||
add_branch!();
|
||||
add_leaf!("1.1.1");
|
||||
}
|
||||
add_leaf!("1.2");
|
||||
default_tree().peek_print();
|
||||
assert_eq!(
|
||||
"\
|
||||
1
|
||||
├╼ 1.1
|
||||
│ └╼ 1.1.1
|
||||
└╼ 1.2",
|
||||
default_tree().string()
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn named_branch() {
|
||||
add_branch!("11");
|
||||
{
|
||||
add_branch!("11.1");
|
||||
add_leaf!("11.1.1");
|
||||
}
|
||||
add_leaf!("11.2");
|
||||
default_tree().peek_print();
|
||||
assert_eq!(
|
||||
"\
|
||||
11
|
||||
├╼ 11.1
|
||||
│ └╼ 11.1.1
|
||||
└╼ 11.2",
|
||||
default_tree().string()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn leaf_with_value() {
|
||||
let value = add_leaf_value!(10);
|
||||
default_tree().peek_print();
|
||||
assert_eq!("10", default_tree().string());
|
||||
assert_eq!(10, value);
|
||||
}
|
||||
}
|
||||
46
third_party/rust/debug_tree/src/defer.rs
vendored
46
third_party/rust/debug_tree/src/defer.rs
vendored
@@ -1,46 +0,0 @@
|
||||
use crate::TreeBuilder;
|
||||
|
||||
/// A deferred function called with an argument, `TreeBuilder`
|
||||
pub struct DeferredFn<F: Fn(TreeBuilder) -> ()> {
|
||||
tree: Option<TreeBuilder>,
|
||||
action: Option<F>,
|
||||
}
|
||||
|
||||
impl<F> DeferredFn<F>
|
||||
where
|
||||
F: Fn(TreeBuilder) -> (),
|
||||
{
|
||||
/// Create a new deferred function based on `tree`
|
||||
pub fn new(tree: TreeBuilder, action: F) -> Self {
|
||||
DeferredFn {
|
||||
tree: Some(tree),
|
||||
action: Some(action),
|
||||
}
|
||||
}
|
||||
/// Create an empty deferred function
|
||||
/// This does nothing when scope ends
|
||||
pub fn none() -> Self {
|
||||
DeferredFn {
|
||||
tree: None,
|
||||
action: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Disables the deferred function
|
||||
/// This prevents the function from executing when the scope ends
|
||||
pub fn cancel(&mut self) {
|
||||
self.tree = None;
|
||||
self.action = None;
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> Drop for DeferredFn<F>
|
||||
where
|
||||
F: Fn(TreeBuilder) -> (),
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
if let (Some(x), Some(action)) = (&self.tree, &self.action) {
|
||||
action(x.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
281
third_party/rust/debug_tree/src/internal.rs
vendored
281
third_party/rust/debug_tree/src/internal.rs
vendored
@@ -1,281 +0,0 @@
|
||||
use crate::tree_config::{tree_config, TreeConfig};
|
||||
use std::cmp::max;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
/// Tree that holds `text` for the current leaf and a list of `children` that are the branches.
|
||||
#[derive(Debug)]
|
||||
pub struct Tree {
|
||||
pub text: Option<String>,
|
||||
pub children: Vec<Tree>,
|
||||
}
|
||||
|
||||
/// Position of the element relative to its siblings
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum Position {
|
||||
Inside,
|
||||
First,
|
||||
Last,
|
||||
Only,
|
||||
}
|
||||
|
||||
impl Tree {
|
||||
/// Create a new tree with some optional text.
|
||||
pub fn new(text: Option<&str>) -> Tree {
|
||||
Tree {
|
||||
text: text.map(|x| x.to_string()),
|
||||
children: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Navigate to the branch at the given `path` relative to this tree.
|
||||
/// If a valid branch is found by following the path, it is returned.
|
||||
pub fn at_mut(&mut self, path: &[usize]) -> Option<&mut Tree> {
|
||||
match path.first() {
|
||||
Some(&i) => match self.children.get_mut(i) {
|
||||
Some(x) => x.at_mut(&path[1..]),
|
||||
_ => None,
|
||||
},
|
||||
_ => Some(self),
|
||||
}
|
||||
}
|
||||
|
||||
/// "Render" this tree as a list of `String`s.
|
||||
/// Each string represents a line in the tree.
|
||||
/// `does_continue` is a bool for each column indicating whether the tree continues.
|
||||
pub fn lines(
|
||||
&self,
|
||||
does_continue: &Vec<bool>,
|
||||
index: usize,
|
||||
pool_size: usize,
|
||||
config: &TreeConfig,
|
||||
) -> Vec<String> {
|
||||
let does_continue = if config.show_first_level && does_continue.is_empty() {
|
||||
vec![true]
|
||||
} else {
|
||||
does_continue.clone()
|
||||
};
|
||||
let position = match index {
|
||||
_ if pool_size == 1 => Position::Only,
|
||||
_ if (index + 1) == pool_size => Position::Last,
|
||||
0 => Position::First,
|
||||
_ => Position::Inside,
|
||||
};
|
||||
let mut next_continue = does_continue.clone();
|
||||
next_continue.push(match position {
|
||||
Position::Inside | Position::First => true,
|
||||
Position::Last | Position::Only => false,
|
||||
});
|
||||
|
||||
let mut txt = String::new();
|
||||
let pad: String;
|
||||
if does_continue.len() > 1 {
|
||||
for &i in &does_continue[2..] {
|
||||
txt.push_str(&format!(
|
||||
"{}{:indent$}",
|
||||
if i { config.symbols.continued } else { " " },
|
||||
"",
|
||||
indent = max(config.indent, 1) - 1
|
||||
));
|
||||
}
|
||||
pad = txt.clone();
|
||||
let branch_size = max(config.indent, 2usize) - 2;
|
||||
let branch = match config.symbols.branch.len() {
|
||||
0 => "-".repeat(branch_size),
|
||||
1 => config.symbols.branch.repeat(branch_size),
|
||||
_n => config
|
||||
.symbols
|
||||
.branch
|
||||
.repeat(branch_size)
|
||||
.chars()
|
||||
.take(branch_size)
|
||||
.collect::<String>(),
|
||||
};
|
||||
|
||||
let is_multiline = self
|
||||
.text
|
||||
.as_ref()
|
||||
.map(|x| x.contains("\n"))
|
||||
.unwrap_or(false);
|
||||
|
||||
let first_leaf = match (is_multiline, config.symbols.multiline_first) {
|
||||
(true, Some(x)) => x,
|
||||
_ => config.symbols.leaf,
|
||||
};
|
||||
txt.push_str(&format!(
|
||||
"{}{}{}",
|
||||
match position {
|
||||
Position::Only => config.symbols.join_only,
|
||||
Position::First => config.symbols.join_first,
|
||||
Position::Last => config.symbols.join_last,
|
||||
Position::Inside => config.symbols.join_inner,
|
||||
},
|
||||
branch,
|
||||
first_leaf,
|
||||
));
|
||||
|
||||
let s = match &self.text {
|
||||
Some(x) => match is_multiline {
|
||||
true => format!(
|
||||
"{}",
|
||||
x.replace(
|
||||
"\n",
|
||||
&format!(
|
||||
"\n{}{}{}{}",
|
||||
&pad,
|
||||
match position {
|
||||
Position::Only | Position::Last =>
|
||||
" ".repeat(config.symbols.continued.chars().count()),
|
||||
_ => config.symbols.continued.to_string(),
|
||||
},
|
||||
" ".repeat(branch_size),
|
||||
match &config.symbols.multiline_continued {
|
||||
Some(multi) => multi.to_string(),
|
||||
_ => " ".repeat(first_leaf.chars().count()),
|
||||
}
|
||||
),
|
||||
)
|
||||
),
|
||||
false => x.clone(),
|
||||
},
|
||||
_ => String::new(),
|
||||
};
|
||||
txt.push_str(&s);
|
||||
} else {
|
||||
if let Some(x) = &self.text {
|
||||
txt.push_str(&x);
|
||||
}
|
||||
}
|
||||
let mut ret = vec![txt];
|
||||
for (index, x) in self.children.iter().enumerate() {
|
||||
for line in x.lines(&next_continue, index, self.children.len(), config) {
|
||||
ret.push(line);
|
||||
}
|
||||
}
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
/// Holds the current state of the tree, including the path to the branch.
|
||||
/// Multiple trees may point to the same data.
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct TreeBuilderBase {
|
||||
data: Arc<Mutex<Tree>>,
|
||||
path: Vec<usize>,
|
||||
dive_count: usize,
|
||||
config: Option<TreeConfig>,
|
||||
is_enabled: bool,
|
||||
}
|
||||
|
||||
impl TreeBuilderBase {
|
||||
/// Create a new state
|
||||
pub fn new() -> TreeBuilderBase {
|
||||
TreeBuilderBase {
|
||||
data: Arc::new(Mutex::new(Tree::new(None))),
|
||||
path: vec![],
|
||||
dive_count: 1,
|
||||
config: None,
|
||||
is_enabled: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_enabled(&mut self, enabled: bool) {
|
||||
self.is_enabled = enabled;
|
||||
}
|
||||
pub fn is_enabled(&self) -> bool {
|
||||
self.is_enabled
|
||||
}
|
||||
|
||||
pub fn add_leaf(&mut self, text: &str) {
|
||||
let &dive_count = &self.dive_count;
|
||||
if dive_count > 0 {
|
||||
for i in 0..dive_count {
|
||||
let mut n = 0;
|
||||
if let Some(x) = self.data.lock().unwrap().at_mut(&self.path) {
|
||||
x.children.push(Tree::new(if i == max(1, dive_count) - 1 {
|
||||
Some(&text)
|
||||
} else {
|
||||
None
|
||||
}));
|
||||
n = x.children.len() - 1;
|
||||
}
|
||||
self.path.push(n);
|
||||
}
|
||||
self.dive_count = 0;
|
||||
} else {
|
||||
if let Some(x) = self
|
||||
.data
|
||||
.lock()
|
||||
.unwrap()
|
||||
.at_mut(&self.path[..max(1, self.path.len()) - 1])
|
||||
{
|
||||
x.children.push(Tree::new(Some(&text)));
|
||||
let n = match self.path.last() {
|
||||
Some(&x) => x + 1,
|
||||
_ => 0,
|
||||
};
|
||||
self.path.last_mut().map(|x| *x = n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_config_override(&mut self, config: Option<TreeConfig>) {
|
||||
self.config = config;
|
||||
}
|
||||
|
||||
pub fn config_override(&self) -> &Option<TreeConfig> {
|
||||
&self.config
|
||||
}
|
||||
pub fn config_override_mut(&mut self) -> &mut Option<TreeConfig> {
|
||||
&mut self.config
|
||||
}
|
||||
|
||||
pub fn enter(&mut self) {
|
||||
self.dive_count += 1;
|
||||
}
|
||||
|
||||
/// Try stepping up to the parent tree branch.
|
||||
/// Returns false if already at the top branch.
|
||||
pub fn exit(&mut self) -> bool {
|
||||
if self.dive_count > 0 {
|
||||
self.dive_count -= 1;
|
||||
true
|
||||
} else {
|
||||
if self.path.len() > 1 {
|
||||
self.path.pop();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn depth(&self) -> usize {
|
||||
max(1, self.path.len() + self.dive_count) - 1
|
||||
}
|
||||
|
||||
pub fn peek_print(&self) {
|
||||
println!("{}", self.peek_string());
|
||||
}
|
||||
|
||||
pub fn print(&mut self) {
|
||||
self.peek_print();
|
||||
self.clear();
|
||||
}
|
||||
pub fn clear(&mut self) {
|
||||
*self = Self::new();
|
||||
}
|
||||
|
||||
pub fn string(&mut self) -> String {
|
||||
let s = self.peek_string();
|
||||
self.clear();
|
||||
s
|
||||
}
|
||||
|
||||
pub fn peek_string(&self) -> String {
|
||||
let config = self
|
||||
.config_override()
|
||||
.clone()
|
||||
.unwrap_or_else(|| tree_config().clone());
|
||||
(&self.data.lock().unwrap().lines(&vec![], 0, 1, &config)[1..]).join("\n")
|
||||
}
|
||||
}
|
||||
805
third_party/rust/debug_tree/src/lib.rs
vendored
805
third_party/rust/debug_tree/src/lib.rs
vendored
@@ -1,805 +0,0 @@
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
#[macro_use]
|
||||
pub mod default;
|
||||
mod internal;
|
||||
pub mod scoped_branch;
|
||||
|
||||
pub mod defer;
|
||||
mod test;
|
||||
pub mod tree_config;
|
||||
|
||||
pub use default::default_tree;
|
||||
use once_cell::sync::Lazy;
|
||||
use scoped_branch::ScopedBranch;
|
||||
use std::collections::BTreeMap;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
|
||||
pub use crate::tree_config::*;
|
||||
|
||||
/// Reference wrapper for `TreeBuilderBase`
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TreeBuilder(Arc<Mutex<internal::TreeBuilderBase>>);
|
||||
|
||||
impl TreeBuilder {
|
||||
/// Returns a new `TreeBuilder` with an empty `Tree`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use debug_tree::TreeBuilder;
|
||||
/// let tree = TreeBuilder::new();
|
||||
/// ```
|
||||
pub fn new() -> TreeBuilder {
|
||||
TreeBuilder {
|
||||
0: Arc::new(Mutex::new(internal::TreeBuilderBase::new())),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the configuration override for displaying trees
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use debug_tree::{TreeBuilder, add_branch_to, add_leaf_to, TreeSymbols, TreeConfig};
|
||||
/// let tree = TreeBuilder::new();
|
||||
/// {
|
||||
/// add_branch_to!(tree, "1");
|
||||
/// {
|
||||
/// add_branch_to!(tree, "1.1");
|
||||
/// add_leaf_to!(tree, "1.1.1");
|
||||
/// add_leaf_to!(tree, "1.1.2");
|
||||
/// }
|
||||
/// add_leaf_to!(tree, "1.2");
|
||||
/// }
|
||||
/// add_leaf_to!(tree, "2");
|
||||
/// tree.set_config_override(TreeConfig::new()
|
||||
/// .show_first_level()
|
||||
/// .symbols(TreeSymbols::with_rounded()));
|
||||
/// tree.peek_print();
|
||||
/// assert_eq!("\
|
||||
/// ├╼ 1
|
||||
/// │ ├╼ 1.1
|
||||
/// │ │ ├╼ 1.1.1
|
||||
/// │ │ ╰╼ 1.1.2
|
||||
/// │ ╰╼ 1.2
|
||||
/// ╰╼ 2" , &tree.string());
|
||||
/// ```
|
||||
pub fn set_config_override(&self, config: TreeConfig) {
|
||||
let mut lock = self.0.lock().unwrap();
|
||||
lock.set_config_override(Some(config))
|
||||
}
|
||||
|
||||
/// Remove the configuration override
|
||||
/// The default configuration will be used instead
|
||||
pub fn remove_config_override(&self) {
|
||||
self.0.lock().unwrap().set_config_override(None);
|
||||
}
|
||||
|
||||
/// Update the configuration override for displaying trees
|
||||
/// If an override doesn't yet exist, it is created.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use debug_tree::{TreeBuilder, add_branch_to, add_leaf_to, TreeSymbols};
|
||||
/// let tree = TreeBuilder::new();
|
||||
/// {
|
||||
/// add_branch_to!(tree, "1");
|
||||
/// {
|
||||
/// add_branch_to!(tree, "1.1");
|
||||
/// add_leaf_to!(tree, "1.1.1");
|
||||
/// add_leaf_to!(tree, "1.1.2");
|
||||
/// }
|
||||
/// add_leaf_to!(tree, "1.2");
|
||||
/// }
|
||||
/// add_leaf_to!(tree, "2");
|
||||
/// tree.update_config_override(|x|{
|
||||
/// x.indent = 3;
|
||||
/// x.symbols = TreeSymbols::with_rounded();
|
||||
/// x.show_first_level = true;
|
||||
/// });
|
||||
/// tree.peek_print();
|
||||
/// assert_eq!("\
|
||||
/// ├─╼ 1
|
||||
/// │ ├─╼ 1.1
|
||||
/// │ │ ├─╼ 1.1.1
|
||||
/// │ │ ╰─╼ 1.1.2
|
||||
/// │ ╰─╼ 1.2
|
||||
/// ╰─╼ 2" , &tree.string());
|
||||
/// ```
|
||||
pub fn update_config_override<F: Fn(&mut TreeConfig)>(&self, update: F) {
|
||||
let mut lock = self.0.lock().unwrap();
|
||||
match lock.config_override_mut() {
|
||||
Some(x) => update(x),
|
||||
None => {
|
||||
let mut x = TreeConfig::default();
|
||||
update(&mut x);
|
||||
lock.set_config_override(Some(x));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the optional configuration override.
|
||||
pub fn get_config_override(&self) -> Option<TreeConfig> {
|
||||
let lock = self.0.lock().unwrap();
|
||||
lock.config_override().clone()
|
||||
}
|
||||
|
||||
/// Returns whether a configuration override is set.
|
||||
pub fn has_config_override(&self) -> bool {
|
||||
let lock = self.0.lock().unwrap();
|
||||
lock.config_override().is_some()
|
||||
}
|
||||
|
||||
/// Adds a new branch with text, `text` and returns a `ScopedBranch`.
|
||||
/// When the returned `ScopedBranch` goes out of scope, (likely the end of the current block),
|
||||
/// or if its `release()` method is called, the tree will step back out of the added branch.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `text` - A string slice to use as the newly added branch's text.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Exiting branch when end of scope is reached.
|
||||
/// ```
|
||||
/// use debug_tree::TreeBuilder;
|
||||
/// let tree = TreeBuilder::new();
|
||||
/// {
|
||||
/// let _branch = tree.add_branch("Branch"); // _branch enters scope
|
||||
/// // tree is now pointed inside new branch.
|
||||
/// tree.add_leaf("Child of Branch");
|
||||
/// // _branch leaves scope, tree moves up to parent branch.
|
||||
/// }
|
||||
/// tree.add_leaf("Sibling of Branch");
|
||||
/// assert_eq!("\
|
||||
/// Branch
|
||||
/// └╼ Child of Branch
|
||||
/// Sibling of Branch" , &tree.string());
|
||||
/// ```
|
||||
///
|
||||
/// Using `release()` before out of scope.
|
||||
/// ```
|
||||
/// use debug_tree::TreeBuilder;
|
||||
/// let tree = TreeBuilder::new();
|
||||
/// {
|
||||
/// let mut branch = tree.add_branch("Branch"); // branch enters scope
|
||||
/// // tree is now pointed inside new branch.
|
||||
/// tree.add_leaf("Child of Branch");
|
||||
/// branch.release();
|
||||
/// tree.add_leaf("Sibling of Branch");
|
||||
/// // branch leaves scope, but no effect because its `release()` method has already been called
|
||||
/// }
|
||||
/// assert_eq!("\
|
||||
/// Branch
|
||||
/// └╼ Child of Branch
|
||||
/// Sibling of Branch", &tree.string());
|
||||
/// ```
|
||||
pub fn add_branch(&self, text: &str) -> ScopedBranch {
|
||||
self.add_leaf(text);
|
||||
ScopedBranch::new(self.clone())
|
||||
}
|
||||
|
||||
/// Adds a new branch with text, `text` and returns a `ScopedBranch`.
|
||||
/// When the returned `ScopedBranch` goes out of scope, (likely the end of the current block),
|
||||
/// or if its `release()` method is called, the tree tree will step back out of the added branch.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `text` - A string slice to use as the newly added branch's text.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Stepping out of branch when end of scope is reached.
|
||||
/// ```
|
||||
/// use debug_tree::TreeBuilder;
|
||||
/// let tree = TreeBuilder::new();
|
||||
/// {
|
||||
/// tree.add_leaf("Branch");
|
||||
/// let _branch = tree.enter_scoped(); // _branch enters scope
|
||||
/// // tree is now pointed inside new branch.
|
||||
/// tree.add_leaf("Child of Branch");
|
||||
/// // _branch leaves scope, tree moves up to parent branch.
|
||||
/// }
|
||||
/// tree.add_leaf("Sibling of Branch");
|
||||
/// assert_eq!("\
|
||||
/// Branch
|
||||
/// └╼ Child of Branch
|
||||
/// Sibling of Branch", &tree.string());
|
||||
/// ```
|
||||
///
|
||||
/// Using `release()` before out of scope.
|
||||
/// ```
|
||||
/// use debug_tree::TreeBuilder;
|
||||
/// let tree = TreeBuilder::new();
|
||||
/// {
|
||||
/// tree.add_leaf("Branch");
|
||||
/// let mut branch = tree.enter_scoped(); // branch enters scope
|
||||
/// // tree is now pointed inside new branch.
|
||||
/// tree.add_leaf("Child of Branch");
|
||||
/// branch.release();
|
||||
/// tree.add_leaf("Sibling of Branch");
|
||||
/// // branch leaves scope, but no effect because its `release()` method has already been called
|
||||
/// }
|
||||
/// assert_eq!("\
|
||||
/// Branch
|
||||
/// └╼ Child of Branch
|
||||
/// Sibling of Branch", &tree.string());
|
||||
/// ```
|
||||
pub fn enter_scoped(&self) -> ScopedBranch {
|
||||
if self.is_enabled() {
|
||||
ScopedBranch::new(self.clone())
|
||||
} else {
|
||||
ScopedBranch::none()
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a leaf to current branch with the given text, `text`.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `text` - A string slice to use as the newly added leaf's text.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use debug_tree::TreeBuilder;
|
||||
/// let tree = TreeBuilder::new();
|
||||
/// tree.add_leaf("New leaf");
|
||||
/// ```
|
||||
pub fn add_leaf(&self, text: &str) {
|
||||
let mut x = self.0.lock().unwrap();
|
||||
if x.is_enabled() {
|
||||
x.add_leaf(&text);
|
||||
}
|
||||
}
|
||||
|
||||
/// Steps into a new child branch.
|
||||
/// Stepping out of the branch requires calling `exit()`.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use debug_tree::TreeBuilder;
|
||||
/// let tree = TreeBuilder::new();
|
||||
/// tree.add_leaf("Branch");
|
||||
/// tree.enter();
|
||||
/// tree.add_leaf("Child of Branch");
|
||||
/// assert_eq!("\
|
||||
/// Branch
|
||||
/// └╼ Child of Branch", &tree.string());
|
||||
/// ```
|
||||
pub fn enter(&self) {
|
||||
let mut x = self.0.lock().unwrap();
|
||||
if x.is_enabled() {
|
||||
x.enter();
|
||||
}
|
||||
}
|
||||
|
||||
/// Exits the current branch, to the parent branch.
|
||||
/// If no parent branch exists, no action is taken
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use debug_tree::TreeBuilder;
|
||||
/// let tree = TreeBuilder::new();
|
||||
/// tree.add_leaf("Branch");
|
||||
/// tree.enter();
|
||||
/// tree.add_leaf("Child of Branch");
|
||||
/// tree.exit();
|
||||
/// tree.add_leaf("Sibling of Branch");
|
||||
/// assert_eq!("\
|
||||
/// Branch
|
||||
/// └╼ Child of Branch
|
||||
/// Sibling of Branch", &tree.string());
|
||||
/// ```
|
||||
pub fn exit(&self) -> bool {
|
||||
let mut x = self.0.lock().unwrap();
|
||||
if x.is_enabled() {
|
||||
x.exit()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the depth of the current branch
|
||||
/// The initial depth when no branches have been adeed is 0.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use debug_tree::TreeBuilder;
|
||||
/// let tree = TreeBuilder::new();
|
||||
/// assert_eq!(0, tree.depth());
|
||||
/// let _b = tree.add_branch("Branch");
|
||||
/// assert_eq!(1, tree.depth());
|
||||
/// let _b = tree.add_branch("Child branch");
|
||||
/// assert_eq!(2, tree.depth());
|
||||
/// ```
|
||||
pub fn depth(&self) -> usize {
|
||||
self.0.lock().unwrap().depth()
|
||||
}
|
||||
|
||||
/// Prints the tree without clearing.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use debug_tree::TreeBuilder;
|
||||
/// let tree = TreeBuilder::new();
|
||||
/// tree.add_leaf("Leaf");
|
||||
/// tree.peek_print();
|
||||
/// // Leaf
|
||||
/// tree.peek_print();
|
||||
/// // Leaf
|
||||
/// // Leaf 2
|
||||
/// ```
|
||||
pub fn peek_print(&self) {
|
||||
self.0.lock().unwrap().peek_print();
|
||||
}
|
||||
|
||||
/// Prints the tree and then clears it.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use debug_tree::TreeBuilder;
|
||||
/// let tree = TreeBuilder::new();
|
||||
/// tree.add_leaf("Leaf");
|
||||
/// tree.print();
|
||||
/// // Leaf
|
||||
/// tree.add_leaf("Leaf 2");
|
||||
/// tree.print();
|
||||
/// // Leaf 2
|
||||
/// ```
|
||||
pub fn print(&self) {
|
||||
self.0.lock().unwrap().print();
|
||||
}
|
||||
|
||||
/// Returns the tree as a string without clearing the tree.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use debug_tree::TreeBuilder;
|
||||
/// let tree = TreeBuilder::new();
|
||||
/// tree.add_leaf("Leaf");
|
||||
/// assert_eq!("Leaf", tree.peek_string());
|
||||
/// tree.add_leaf("Leaf 2");
|
||||
/// assert_eq!("Leaf\nLeaf 2", tree.peek_string());
|
||||
/// ```
|
||||
pub fn peek_string(&self) -> String {
|
||||
self.0.lock().unwrap().peek_string()
|
||||
}
|
||||
|
||||
/// Returns the tree as a string and clears the tree.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use debug_tree::TreeBuilder;
|
||||
/// let tree = TreeBuilder::new();
|
||||
/// tree.add_leaf("Leaf");
|
||||
/// assert_eq!("Leaf", tree.string());
|
||||
/// tree.add_leaf("Leaf 2");
|
||||
/// assert_eq!("Leaf 2", tree.string());
|
||||
/// ```
|
||||
pub fn string(&self) -> String {
|
||||
self.0.lock().unwrap().string()
|
||||
}
|
||||
|
||||
/// Writes the tree to file without clearing.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use debug_tree::TreeBuilder;
|
||||
/// use std::fs::{read_to_string, create_dir};
|
||||
/// use std::io::Read;
|
||||
/// let tree = TreeBuilder::new();
|
||||
/// create_dir("test_out").ok();
|
||||
/// tree.add_leaf("Leaf");
|
||||
/// assert_eq!(tree.peek_string(), "Leaf");
|
||||
/// tree.peek_write("test_out/peek_write.txt");
|
||||
/// assert_eq!(read_to_string("test_out/peek_write.txt").unwrap(), "Leaf");
|
||||
/// assert_eq!(tree.peek_string(), "Leaf");
|
||||
/// ```
|
||||
pub fn peek_write(&self, path: &str) -> std::io::Result<()> {
|
||||
let mut file = File::create(path)?;
|
||||
file.write_all(self.peek_string().as_bytes())
|
||||
}
|
||||
|
||||
/// Writes the tree to file without clearing.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use debug_tree::TreeBuilder;
|
||||
/// use std::io::Read;
|
||||
/// use std::fs::{read_to_string, create_dir};
|
||||
/// let tree = TreeBuilder::new();
|
||||
/// create_dir("test_out").ok();
|
||||
/// tree.add_leaf("Leaf");
|
||||
/// assert_eq!(tree.peek_string(), "Leaf");
|
||||
/// tree.write("test_out/write.txt");
|
||||
/// assert_eq!(read_to_string("test_out/write.txt").unwrap(), "Leaf");
|
||||
/// assert_eq!(tree.peek_string(), "");
|
||||
/// ```
|
||||
pub fn write(&self, path: &str) -> std::io::Result<()> {
|
||||
let mut file = File::create(path)?;
|
||||
file.write_all(self.string().as_bytes())
|
||||
}
|
||||
|
||||
/// Clears the tree.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use debug_tree::TreeBuilder;
|
||||
/// let tree = TreeBuilder::new();
|
||||
/// tree.add_leaf("Leaf");
|
||||
/// assert_eq!("Leaf", tree.peek_string());
|
||||
/// tree.clear();
|
||||
/// assert_eq!("", tree.peek_string());
|
||||
/// ```
|
||||
pub fn clear(&self) {
|
||||
self.0.lock().unwrap().clear()
|
||||
}
|
||||
|
||||
/// Sets the enabled state of the tree.
|
||||
///
|
||||
/// If not enabled, the tree will not be modified by adding leaves or branches.
|
||||
/// Additionally, if called using the `add_`... macros, arguments will not be processed.
|
||||
/// This is particularly useful for suppressing output in production, with very little overhead.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// #[macro_use]
|
||||
/// use debug_tree::{TreeBuilder, add_leaf_to};
|
||||
/// let mut tree = TreeBuilder::new();
|
||||
/// tree.add_leaf("Leaf 1");
|
||||
/// tree.set_enabled(false);
|
||||
/// add_leaf_to!(tree, "Leaf 2");
|
||||
/// tree.set_enabled(true);
|
||||
/// add_leaf_to!(tree, "Leaf 3");
|
||||
/// assert_eq!("Leaf 1\nLeaf 3", tree.peek_string());
|
||||
/// ```
|
||||
pub fn set_enabled(&self, enabled: bool) {
|
||||
self.0.lock().unwrap().set_enabled(enabled);
|
||||
}
|
||||
|
||||
/// Returns the enabled state of the tree.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use debug_tree::TreeBuilder;
|
||||
/// let mut tree = TreeBuilder::new();
|
||||
/// assert_eq!(true, tree.is_enabled());
|
||||
/// tree.set_enabled(false);
|
||||
/// assert_eq!(false, tree.is_enabled());
|
||||
/// ```
|
||||
pub fn is_enabled(&self) -> bool {
|
||||
self.0.lock().unwrap().is_enabled()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait AsTree {
|
||||
fn as_tree(&self) -> TreeBuilder;
|
||||
fn is_tree_enabled(&self) -> bool {
|
||||
self.as_tree().is_enabled()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsTree for TreeBuilder {
|
||||
fn as_tree(&self) -> TreeBuilder {
|
||||
self.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_or_add_tree<T: AsRef<str>>(name: T) -> TreeBuilder {
|
||||
let mut map = TREE_MAP.lock().unwrap();
|
||||
match map.get(name.as_ref()) {
|
||||
Some(x) => x.clone(),
|
||||
_ => {
|
||||
let val = TreeBuilder::new();
|
||||
map.insert(name.as_ref().to_string(), val.clone());
|
||||
val
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_tree<T: AsRef<str>>(name: T) -> Option<TreeBuilder> {
|
||||
TREE_MAP.lock().unwrap().get(name.as_ref()).cloned()
|
||||
}
|
||||
|
||||
type TreeMap = BTreeMap<String, TreeBuilder>;
|
||||
|
||||
static TREE_MAP: Lazy<Arc<Mutex<TreeMap>>> =
|
||||
Lazy::new(|| -> Arc<Mutex<TreeMap>> { Arc::new(Mutex::new(TreeMap::new())) });
|
||||
|
||||
/// Sets the enabled state of the tree.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `name` - The tree name
|
||||
/// * `enabled` - The enabled state
|
||||
///
|
||||
pub fn set_enabled<T: AsRef<str>>(name: T, enabled: bool) {
|
||||
let mut map = TREE_MAP.lock().unwrap();
|
||||
match map.get_mut(name.as_ref()) {
|
||||
Some(x) => x.set_enabled(enabled),
|
||||
_ => {
|
||||
let tree = TreeBuilder::new();
|
||||
tree.set_enabled(enabled);
|
||||
map.insert(name.as_ref().to_string(), tree);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsRef<str>> AsTree for T {
|
||||
fn as_tree(&self) -> TreeBuilder {
|
||||
get_or_add_tree(self)
|
||||
}
|
||||
/// Check if the named tree is enabled and exists
|
||||
/// This does not create a new tree if non-existent
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `tree_name` - The tree name
|
||||
///
|
||||
fn is_tree_enabled(&self) -> bool {
|
||||
get_tree(self).map(|x| x.is_enabled()).unwrap_or(false)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the tree
|
||||
/// If there is no tree then one is created and then returned.
|
||||
pub fn tree<T: AsTree>(tree: T) -> TreeBuilder {
|
||||
tree.as_tree()
|
||||
}
|
||||
|
||||
/// Returns the tree named `name`
|
||||
/// If there is no tree named `name` then one is created and then returned.
|
||||
pub fn is_tree_enabled<T: AsTree>(tree: &T) -> bool {
|
||||
tree.is_tree_enabled()
|
||||
}
|
||||
|
||||
/// Calls [clear](TreeBuilder::clear) for the tree named `name`
|
||||
/// If there is no tree named `name` then one is created
|
||||
pub fn clear<T: AsRef<str>>(name: T) {
|
||||
name.as_tree().clear();
|
||||
}
|
||||
|
||||
/// Returns [string](TreeBuilder::string) for the tree named `name`
|
||||
/// If there is no tree named `name` then one is created
|
||||
pub fn string<T: AsRef<str>>(name: T) -> String {
|
||||
name.as_tree().string()
|
||||
}
|
||||
|
||||
/// Returns [peek_string](TreeBuilder::peek_string) for the tree named `name`
|
||||
/// If there is no tree named `name` then one is created
|
||||
pub fn peek_string<T: AsRef<str>>(name: T) -> String {
|
||||
name.as_tree().peek_string()
|
||||
}
|
||||
|
||||
/// Calls [print](TreeBuilder::print) for the tree named `name`
|
||||
/// If there is no tree named `name` then one is created
|
||||
pub fn print<T: AsRef<str>>(name: T) {
|
||||
name.as_tree().print();
|
||||
}
|
||||
|
||||
/// Calls [peek_print](TreeBuilder::peek_print) for the tree named `name`
|
||||
/// If there is no tree named `name` then one is created
|
||||
pub fn peek_print<T: AsRef<str>>(name: T) {
|
||||
name.as_tree().peek_print();
|
||||
}
|
||||
|
||||
/// Calls [write](TreeBuilder::write) for the tree named `name`
|
||||
/// If there is no tree named `name` then one is created
|
||||
pub fn write<T: AsRef<str>, P: AsRef<str>>(name: T, path: P) -> std::io::Result<()> {
|
||||
name.as_tree().write(path.as_ref())
|
||||
}
|
||||
|
||||
/// Calls [peek_print](TreeBuilder::peek_print) for the tree named `name`
|
||||
/// If there is no tree named `name` then one is created
|
||||
pub fn peek_write<T: AsRef<str>, P: AsRef<str>>(name: T, path: P) -> std::io::Result<()> {
|
||||
name.as_tree().peek_write(path.as_ref())
|
||||
}
|
||||
|
||||
/// Adds a leaf to given tree with the given text and formatting arguments
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `tree` - The tree that the leaf should be added to
|
||||
/// * `text...` - Formatted text arguments, as per `format!(...)`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// #[macro_use]
|
||||
/// use debug_tree::{TreeBuilder, add_leaf_to};
|
||||
/// fn main() {
|
||||
/// let tree = TreeBuilder::new();
|
||||
/// add_leaf_to!(tree, "A {} leaf", "new");
|
||||
/// assert_eq!("A new leaf", &tree.peek_string());
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! add_leaf_to {
|
||||
($tree:expr, $($arg:tt)*) => (if $crate::is_tree_enabled(&$tree) {
|
||||
use $crate::AsTree;
|
||||
$tree.as_tree().add_leaf(&format!($($arg)*))
|
||||
});
|
||||
}
|
||||
|
||||
/// Adds a leaf to given tree with the given `value` argument
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `tree` - The tree that the leaf should be added to
|
||||
/// * `value` - An expression that implements the `Display` trait.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// #[macro_use]
|
||||
/// use debug_tree::{TreeBuilder, add_leaf_value_to};
|
||||
/// fn main() {
|
||||
/// let tree = TreeBuilder::new();
|
||||
/// let value = add_leaf_value_to!(tree, 5 * 4 * 3 * 2);
|
||||
/// assert_eq!(120, value);
|
||||
/// assert_eq!("120", &tree.peek_string());
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! add_leaf_value_to {
|
||||
($tree:expr, $value:expr) => {{
|
||||
let v = $value;
|
||||
if $crate::is_tree_enabled(&$tree) {
|
||||
use $crate::AsTree;
|
||||
$tree.as_tree().add_leaf(&format!("{}", &v));
|
||||
}
|
||||
v
|
||||
}};
|
||||
}
|
||||
|
||||
/// Adds a scoped branch to given tree with the given text and formatting arguments
|
||||
/// The branch will be exited at the end of the current block.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `tree` - The tree that the leaf should be added to
|
||||
/// * `text...` - Formatted text arguments, as per `format!(...)`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// #[macro_use]
|
||||
/// use debug_tree::{TreeBuilder, add_branch_to, add_leaf_to};
|
||||
/// fn main() {
|
||||
/// let tree = TreeBuilder::new();
|
||||
/// {
|
||||
/// add_branch_to!(tree, "New {}", "Branch"); // _branch enters scope
|
||||
/// // tree is now pointed inside new branch.
|
||||
/// add_leaf_to!(tree, "Child of {}", "Branch");
|
||||
/// // Block ends, so tree exits the current branch.
|
||||
/// }
|
||||
/// add_leaf_to!(tree, "Sibling of {}", "Branch");
|
||||
/// assert_eq!("\
|
||||
/// New Branch
|
||||
/// └╼ Child of Branch
|
||||
/// Sibling of Branch" , &tree.string());
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! add_branch_to {
|
||||
($tree:expr) => {
|
||||
let _debug_tree_branch = if $crate::is_tree_enabled(&$tree) {
|
||||
use $crate::AsTree;
|
||||
$tree.as_tree().enter_scoped()
|
||||
} else {
|
||||
$crate::scoped_branch::ScopedBranch::none()
|
||||
};
|
||||
};
|
||||
($tree:expr, $($arg:tt)*) => {
|
||||
let _debug_tree_branch = if $crate::is_tree_enabled(&$tree) {
|
||||
use $crate::AsTree;
|
||||
$tree.as_tree().add_branch(&format!($($arg)*))
|
||||
} else {
|
||||
$crate::scoped_branch::ScopedBranch::none()
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/// Calls `function` with argument, `tree`, at the end of the current scope
|
||||
/// The function will only be executed if the tree is enabled when this macro is called
|
||||
#[macro_export]
|
||||
macro_rules! defer {
|
||||
($function:expr) => {
|
||||
let _debug_tree_defer = {
|
||||
use $crate::AsTree;
|
||||
if $crate::default::default_tree().is_enabled() {
|
||||
use $crate::AsTree;
|
||||
$crate::defer::DeferredFn::new($crate::default::default_tree(), $function)
|
||||
} else {
|
||||
$crate::defer::DeferredFn::none()
|
||||
}
|
||||
};
|
||||
};
|
||||
($tree:expr, $function:expr) => {
|
||||
let _debug_tree_defer = {
|
||||
use $crate::AsTree;
|
||||
if $tree.as_tree().is_enabled() {
|
||||
$crate::defer::DeferredFn::new($tree.as_tree(), $function)
|
||||
} else {
|
||||
$crate::defer::DeferredFn::none()
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/// Calls [print](TreeBuilder::print) on `tree` at the end of the current scope.
|
||||
/// The function will only be executed if the tree is enabled when this macro is called
|
||||
#[macro_export]
|
||||
macro_rules! defer_print {
|
||||
() => {
|
||||
$crate::defer!(|x| {
|
||||
x.print();
|
||||
})
|
||||
};
|
||||
($tree:expr) => {
|
||||
$crate::defer!($tree, |x| {
|
||||
x.print();
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
/// Calls [peek_print](TreeBuilder::peek_print) on `tree` at the end of the current scope.
|
||||
/// The function will only be executed if the tree is enabled when this macro is called
|
||||
#[macro_export]
|
||||
macro_rules! defer_peek_print {
|
||||
() => {
|
||||
$crate::defer!(|x| {
|
||||
x.peek_print();
|
||||
})
|
||||
};
|
||||
($tree:expr) => {
|
||||
$crate::defer!($tree, |x| {
|
||||
x.peek_print();
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
/// Calls [write](TreeBuilder::write) on `tree` at the end of the current scope.
|
||||
/// The function will only be executed if the tree is enabled when this macro is called
|
||||
#[macro_export]
|
||||
macro_rules! defer_write {
|
||||
($tree:expr, $path:expr) => {
|
||||
$crate::defer!($tree, |x| {
|
||||
if let Err(err) = x.write($path) {
|
||||
eprintln!("error during `defer_write`: {}", err);
|
||||
}
|
||||
})
|
||||
};
|
||||
($path:expr) => {
|
||||
$crate::defer!(|x| {
|
||||
if let Err(err) = x.write($path) {
|
||||
eprintln!("error during `defer_write`: {}", err);
|
||||
}
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
/// Calls [peek_write](TreeBuilder::peek_write) on `tree` at the end of the current scope.
|
||||
/// The function will only be executed if the tree is enabled when this macro is called
|
||||
#[macro_export]
|
||||
macro_rules! defer_peek_write {
|
||||
($tree:expr, $path:expr) => {
|
||||
$crate::defer!($tree, |x| {
|
||||
if let Err(err) = x.peek_write($path) {
|
||||
eprintln!("error during `defer_peek_write`: {}", err);
|
||||
}
|
||||
})
|
||||
};
|
||||
($path:expr) => {
|
||||
$crate::defer!(|x| {
|
||||
if let Err(err) = x.peek_write($path) {
|
||||
eprintln!("error during `defer_peek_write`: {}", err);
|
||||
}
|
||||
})
|
||||
};
|
||||
}
|
||||
26
third_party/rust/debug_tree/src/scoped_branch.rs
vendored
26
third_party/rust/debug_tree/src/scoped_branch.rs
vendored
@@ -1,26 +0,0 @@
|
||||
use crate::TreeBuilder;
|
||||
|
||||
pub struct ScopedBranch {
|
||||
state: Option<TreeBuilder>,
|
||||
}
|
||||
|
||||
impl ScopedBranch {
|
||||
pub fn new(state: TreeBuilder) -> ScopedBranch {
|
||||
state.enter();
|
||||
ScopedBranch { state: Some(state) }
|
||||
}
|
||||
pub fn none() -> ScopedBranch {
|
||||
ScopedBranch { state: None }
|
||||
}
|
||||
pub fn release(&mut self) {
|
||||
if let Some(x) = &self.state {
|
||||
x.exit();
|
||||
}
|
||||
self.state = None;
|
||||
}
|
||||
}
|
||||
impl Drop for ScopedBranch {
|
||||
fn drop(&mut self) {
|
||||
self.release();
|
||||
}
|
||||
}
|
||||
596
third_party/rust/debug_tree/src/test.rs
vendored
596
third_party/rust/debug_tree/src/test.rs
vendored
@@ -1,596 +0,0 @@
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::*;
|
||||
use futures::future::join5;
|
||||
use std::cmp::{max, min};
|
||||
use std::fs::{create_dir, read_to_string, remove_file};
|
||||
|
||||
#[test]
|
||||
fn test_branch() {
|
||||
let d: TreeBuilder = TreeBuilder::new();
|
||||
d.add_leaf("1");
|
||||
{
|
||||
let _l = d.enter_scoped();
|
||||
d.add_leaf("1.1");
|
||||
d.add_leaf("1.2");
|
||||
}
|
||||
d.add_leaf("2");
|
||||
d.add_leaf("3");
|
||||
let _l = d.enter_scoped();
|
||||
d.add_leaf("3.1");
|
||||
d.add_leaf("3.2");
|
||||
d.peek_print();
|
||||
assert_eq!(
|
||||
"\
|
||||
1
|
||||
├╼ 1.1
|
||||
└╼ 1.2
|
||||
2
|
||||
3
|
||||
├╼ 3.1
|
||||
└╼ 3.2",
|
||||
d.string()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_branch2() {
|
||||
let d = TreeBuilder::new();
|
||||
d.add_leaf("1");
|
||||
{
|
||||
let _scope = d.enter_scoped();
|
||||
d.add_leaf("1.1");
|
||||
{
|
||||
let _scope = d.enter_scoped();
|
||||
d.add_leaf("1.1.1");
|
||||
}
|
||||
}
|
||||
|
||||
d.add_leaf("2");
|
||||
d.enter();
|
||||
d.add_leaf("2.1");
|
||||
d.enter();
|
||||
d.add_leaf("2.1.1");
|
||||
d.peek_print();
|
||||
assert_eq!(
|
||||
"\
|
||||
1
|
||||
└╼ 1.1
|
||||
└╼ 1.1.1
|
||||
2
|
||||
└╼ 2.1
|
||||
└╼ 2.1.1",
|
||||
d.string()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple() {
|
||||
let d = TreeBuilder::new();
|
||||
d.add_leaf("Hi");
|
||||
assert_eq!("Hi", d.string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn depth() {
|
||||
let d = TreeBuilder::new();
|
||||
assert_eq!(0, d.depth());
|
||||
d.add_leaf("Hi");
|
||||
assert_eq!(0, d.depth());
|
||||
let _b = d.add_branch("Hi");
|
||||
assert_eq!(1, d.depth());
|
||||
d.add_leaf("Hi");
|
||||
assert_eq!(1, d.depth());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn indent() {
|
||||
let d = TreeBuilder::new();
|
||||
d.add_leaf("1");
|
||||
add_branch_to!(d);
|
||||
d.add_leaf("1.1");
|
||||
{
|
||||
add_branch_to!(d);
|
||||
d.add_leaf("1.1.1");
|
||||
}
|
||||
d.set_config_override(TreeConfig::new().indent(4));
|
||||
d.peek_print();
|
||||
assert_eq!(
|
||||
"\
|
||||
1
|
||||
└──╼ 1.1
|
||||
└──╼ 1.1.1",
|
||||
d.string()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn macros() {
|
||||
let d = TreeBuilder::new();
|
||||
add_leaf_to!(d, "1");
|
||||
{
|
||||
add_branch_to!(d);
|
||||
add_leaf_to!(d, "1.1")
|
||||
}
|
||||
d.peek_print();
|
||||
assert_eq!(
|
||||
"\
|
||||
1
|
||||
└╼ 1.1",
|
||||
d.string()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn macros_with_fn() {
|
||||
let d = TreeBuilder::new();
|
||||
let tree = || d.clone();
|
||||
add_leaf_to!(tree(), "1");
|
||||
{
|
||||
add_branch_to!(tree());
|
||||
add_leaf_to!(tree(), "1.1")
|
||||
}
|
||||
tree().peek_print();
|
||||
assert_eq!(
|
||||
"\
|
||||
1
|
||||
└╼ 1.1",
|
||||
d.string()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn leaf_with_value() {
|
||||
let d = TreeBuilder::new();
|
||||
let value = add_leaf_value_to!(d, 1);
|
||||
d.peek_print();
|
||||
assert_eq!("1", d.string());
|
||||
assert_eq!(1, value);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn macros2() {
|
||||
let d = TreeBuilder::new();
|
||||
add_branch_to!(d, "1");
|
||||
add_leaf_to!(d, "1.1");
|
||||
d.peek_print();
|
||||
assert_eq!(
|
||||
"\
|
||||
1
|
||||
└╼ 1.1",
|
||||
d.string()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mid() {
|
||||
let d = TreeBuilder::new();
|
||||
d.add_leaf(&format!("{}{}", "1", "0"));
|
||||
d.enter();
|
||||
d.add_leaf("10.1");
|
||||
d.add_leaf("10.2");
|
||||
d.enter();
|
||||
d.add_leaf("10.1.1");
|
||||
d.add_leaf("10.1.2\nNext line");
|
||||
d.exit();
|
||||
d.add_leaf(&format!("10.3"));
|
||||
d.peek_print();
|
||||
assert_eq!(
|
||||
"\
|
||||
10
|
||||
├╼ 10.1
|
||||
├╼ 10.2
|
||||
│ ├╼ 10.1.1
|
||||
│ └╼ 10.1.2
|
||||
│ Next line
|
||||
└╼ 10.3",
|
||||
d.string()
|
||||
);
|
||||
}
|
||||
|
||||
fn factors(x: usize) {
|
||||
add_branch!("{}", x);
|
||||
for i in 1..x {
|
||||
if x % i == 0 {
|
||||
factors(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn recursive() {
|
||||
factors(6);
|
||||
default_tree().peek_print();
|
||||
assert_eq!(
|
||||
"\
|
||||
6
|
||||
├╼ 1
|
||||
├╼ 2
|
||||
│ └╼ 1
|
||||
└╼ 3
|
||||
└╼ 1",
|
||||
default_tree().string()
|
||||
);
|
||||
}
|
||||
|
||||
fn a() {
|
||||
add_branch!("a");
|
||||
b();
|
||||
c();
|
||||
}
|
||||
|
||||
fn b() {
|
||||
add_branch!("b");
|
||||
c();
|
||||
}
|
||||
|
||||
fn c() {
|
||||
add_branch!("c");
|
||||
add_leaf!("Nothing to see here");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nested() {
|
||||
a();
|
||||
default_tree().peek_print();
|
||||
assert_eq!(
|
||||
"\
|
||||
a
|
||||
├╼ b
|
||||
│ └╼ c
|
||||
│ └╼ Nothing to see here
|
||||
└╼ c
|
||||
└╼ Nothing to see here",
|
||||
default_tree().string()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn disabled_output() {
|
||||
let tree = TreeBuilder::new();
|
||||
tree.set_enabled(false);
|
||||
add_leaf_to!(tree, "Leaf");
|
||||
tree.add_leaf("Leaf");
|
||||
|
||||
add_branch_to!(tree, "Branch");
|
||||
tree.add_branch("Branch");
|
||||
assert_eq!("", tree.string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn enabled_output() {
|
||||
let tree = TreeBuilder::new();
|
||||
tree.set_enabled(false);
|
||||
add_branch_to!(tree, "Ignored branch");
|
||||
add_leaf_to!(tree, "Ignored leaf");
|
||||
tree.set_enabled(true);
|
||||
add_leaf_to!(tree, "Leaf");
|
||||
tree.add_leaf("Leaf");
|
||||
|
||||
add_branch_to!(tree, "Branch");
|
||||
tree.add_branch("Branch");
|
||||
assert_eq!(
|
||||
"Leaf
|
||||
Leaf
|
||||
Branch
|
||||
└╼ Branch",
|
||||
tree.string()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tree_by_name() {
|
||||
clear("A");
|
||||
let b = tree("B");
|
||||
b.clear();
|
||||
{
|
||||
add_branch_to!("A", "1");
|
||||
add_branch_to!(b, "3");
|
||||
add_leaf_to!("A", "1.1");
|
||||
add_leaf_to!("B", "3.1");
|
||||
}
|
||||
add_leaf_to!("A", "2");
|
||||
peek_print("A");
|
||||
b.peek_print();
|
||||
assert_eq!(
|
||||
"1
|
||||
└╼ 1.1
|
||||
2",
|
||||
string("A")
|
||||
);
|
||||
assert_eq!(
|
||||
"3
|
||||
└╼ 3.1",
|
||||
b.string()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tree_by_name_disabled() {
|
||||
let d = tree("D");
|
||||
d.clear();
|
||||
d.set_enabled(true);
|
||||
clear("C");
|
||||
set_enabled("C", false);
|
||||
{
|
||||
add_branch_to!("C", "1");
|
||||
set_enabled("C", true);
|
||||
add_branch_to!(d, "3");
|
||||
add_leaf_to!("C", "1.1");
|
||||
d.set_enabled(false);
|
||||
add_leaf_to!("D", "3.1");
|
||||
}
|
||||
add_leaf_to!("C", "2");
|
||||
peek_print("C");
|
||||
d.peek_print();
|
||||
assert_eq!(
|
||||
"1.1
|
||||
2",
|
||||
string("C")
|
||||
);
|
||||
assert_eq!("3", d.string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn defer_write() {
|
||||
let tree = TreeBuilder::new();
|
||||
{
|
||||
create_dir("test_out").ok();
|
||||
remove_file("test_out/defer_write.txt").ok();
|
||||
File::create("test_out/defer_write.txt").unwrap();
|
||||
defer_write!(tree, "test_out/defer_write.txt");
|
||||
tree.add_leaf("Branch");
|
||||
assert_eq!(read_to_string("test_out/defer_write.txt").unwrap(), "");
|
||||
assert_eq!(tree.peek_string(), "Branch");
|
||||
}
|
||||
assert_eq!(tree.peek_string(), "");
|
||||
assert_eq!(
|
||||
read_to_string("test_out/defer_write.txt").unwrap(),
|
||||
"Branch"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn defer_peek_write() {
|
||||
let tree = TreeBuilder::new();
|
||||
{
|
||||
create_dir("test_out").ok();
|
||||
remove_file("test_out/defer_peek_write.txt").ok();
|
||||
File::create("test_out/defer_peek_write.txt").unwrap();
|
||||
defer_peek_write!(tree, "test_out/defer_peek_write.txt");
|
||||
tree.add_leaf("Branch");
|
||||
assert_eq!(read_to_string("test_out/defer_peek_write.txt").unwrap(), "");
|
||||
assert_eq!(tree.peek_string(), "Branch");
|
||||
}
|
||||
assert_eq!(tree.peek_string(), "Branch");
|
||||
assert_eq!(
|
||||
read_to_string("test_out/defer_peek_write.txt").unwrap(),
|
||||
"Branch"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
#[allow(unreachable_code)]
|
||||
fn defer_peek_write_panic() {
|
||||
let tree = TreeBuilder::new();
|
||||
{
|
||||
create_dir("test_out").ok();
|
||||
remove_file("test_out/defer_peek_write_panic.txt").ok();
|
||||
File::create("test_out/defer_peek_write_panic.txt").unwrap();
|
||||
defer_peek_write!(tree, "test_out/defer_peek_write_panic.txt");
|
||||
tree.add_leaf("This should be the only line in this file");
|
||||
assert_eq!(read_to_string("test_out/defer_peek_write.txt").unwrap(), "");
|
||||
assert_eq!(
|
||||
tree.peek_string(),
|
||||
"This should be the only line in this file"
|
||||
);
|
||||
panic!();
|
||||
tree.add_leaf("This line should not exist");
|
||||
}
|
||||
}
|
||||
|
||||
fn example_tree() -> TreeBuilder {
|
||||
let tree = TreeBuilder::new();
|
||||
{
|
||||
add_branch_to!(tree, "1");
|
||||
{
|
||||
add_branch_to!(tree, "1.1");
|
||||
add_leaf_to!(tree, "1.1.1");
|
||||
add_leaf_to!(tree, "1.1.2\nWith two\nextra lines");
|
||||
add_leaf_to!(tree, "1.1.3");
|
||||
}
|
||||
add_branch_to!(tree, "1.2");
|
||||
add_leaf_to!(tree, "1.2.1");
|
||||
}
|
||||
{
|
||||
add_branch_to!(tree, "2");
|
||||
add_leaf_to!(tree, "2.1");
|
||||
add_leaf_to!(tree, "2.2");
|
||||
}
|
||||
add_leaf_to!(tree, "3");
|
||||
tree
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_output() {
|
||||
let tree = example_tree();
|
||||
tree.set_config_override(
|
||||
TreeConfig::new()
|
||||
.indent(8)
|
||||
.symbols(TreeSymbols {
|
||||
continued: "| |",
|
||||
join_first: "|A|",
|
||||
join_last: "|Z|",
|
||||
join_inner: "|N|",
|
||||
join_only: "|O|",
|
||||
branch: "123456[NOT SHOWN]",
|
||||
leaf: ")}>",
|
||||
multiline_first: Some(")}MULTI>"),
|
||||
multiline_continued: Some(".. CONTINUED: "),
|
||||
})
|
||||
.show_first_level(),
|
||||
);
|
||||
tree.peek_print();
|
||||
assert_eq!(
|
||||
tree.string(),
|
||||
"\
|
||||
|A|123456)}>1
|
||||
| | |A|123456)}>1.1
|
||||
| | | | |A|123456)}>1.1.1
|
||||
| | | | |N|123456)}MULTI>1.1.2
|
||||
| | | | | | .. CONTINUED: With two
|
||||
| | | | | | .. CONTINUED: extra lines
|
||||
| | | | |Z|123456)}>1.1.3
|
||||
| | |Z|123456)}>1.2
|
||||
| | |O|123456)}>1.2.1
|
||||
|N|123456)}>2
|
||||
| | |A|123456)}>2.1
|
||||
| | |Z|123456)}>2.2
|
||||
|Z|123456)}>3"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_output_thick() {
|
||||
let tree = example_tree();
|
||||
tree.set_config_override(
|
||||
TreeConfig::new()
|
||||
.symbols(TreeSymbols::with_thick())
|
||||
.indent(4)
|
||||
.show_first_level(),
|
||||
);
|
||||
tree.peek_print();
|
||||
assert_eq!(
|
||||
tree.string(),
|
||||
"\
|
||||
┣━━╼ 1
|
||||
┃ ┣━━╼ 1.1
|
||||
┃ ┃ ┣━━╼ 1.1.1
|
||||
┃ ┃ ┣━━╼ 1.1.2
|
||||
┃ ┃ ┃ With two
|
||||
┃ ┃ ┃ extra lines
|
||||
┃ ┃ ┗━━╼ 1.1.3
|
||||
┃ ┗━━╼ 1.2
|
||||
┃ ┗━━╼ 1.2.1
|
||||
┣━━╼ 2
|
||||
┃ ┣━━╼ 2.1
|
||||
┃ ┗━━╼ 2.2
|
||||
┗━━╼ 3"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_output_pipes() {
|
||||
let tree = example_tree();
|
||||
tree.set_config_override(
|
||||
TreeConfig::new()
|
||||
.symbols(TreeSymbols::with_pipes())
|
||||
.indent(3)
|
||||
.show_first_level(),
|
||||
);
|
||||
tree.peek_print();
|
||||
assert_eq!(
|
||||
tree.string(),
|
||||
"\
|
||||
╠═╼ 1
|
||||
║ ╠═╼ 1.1
|
||||
║ ║ ╠═╼ 1.1.1
|
||||
║ ║ ╠═╼ 1.1.2
|
||||
║ ║ ║ With two
|
||||
║ ║ ║ extra lines
|
||||
║ ║ ╚═╼ 1.1.3
|
||||
║ ╚═╼ 1.2
|
||||
║ ╚═╼ 1.2.1
|
||||
╠═╼ 2
|
||||
║ ╠═╼ 2.1
|
||||
║ ╚═╼ 2.2
|
||||
╚═╼ 3"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_output_dashed() {
|
||||
let tree = example_tree();
|
||||
tree.set_config_override(
|
||||
TreeConfig::new()
|
||||
.symbols(TreeSymbols::with_dashed().multiline_continued(" > "))
|
||||
.indent(4)
|
||||
.show_first_level(),
|
||||
);
|
||||
tree.peek_print();
|
||||
assert_eq!(
|
||||
tree.string(),
|
||||
"\
|
||||
┊╌╌- 1
|
||||
┊ ┊╌╌- 1.1
|
||||
┊ ┊ ┊╌╌- 1.1.1
|
||||
┊ ┊ ┊╌╌- 1.1.2
|
||||
┊ ┊ ┊ > With two
|
||||
┊ ┊ ┊ > extra lines
|
||||
┊ ┊ '╌╌- 1.1.3
|
||||
┊ '╌╌- 1.2
|
||||
┊ '╌╌- 1.2.1
|
||||
┊╌╌- 2
|
||||
┊ ┊╌╌- 2.1
|
||||
┊ '╌╌- 2.2
|
||||
'╌╌- 3"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_output_rounded() {
|
||||
let tree = example_tree();
|
||||
tree.set_config_override(
|
||||
TreeConfig::new()
|
||||
.symbols(TreeSymbols::with_rounded())
|
||||
.indent(4),
|
||||
);
|
||||
tree.peek_print();
|
||||
assert_eq!(
|
||||
tree.string(),
|
||||
"\
|
||||
1
|
||||
├──╼ 1.1
|
||||
│ ├──╼ 1.1.1
|
||||
│ ├──╼ 1.1.2
|
||||
│ │ With two
|
||||
│ │ extra lines
|
||||
│ ╰──╼ 1.1.3
|
||||
╰──╼ 1.2
|
||||
╰──╼ 1.2.1
|
||||
2
|
||||
├──╼ 2.1
|
||||
╰──╼ 2.2
|
||||
3"
|
||||
);
|
||||
}
|
||||
|
||||
async fn wait_a_bit(tree: TreeBuilder, index: usize) {
|
||||
tree.print();
|
||||
add_branch_to!(tree, "inside async branch {}", index);
|
||||
tree.print();
|
||||
add_leaf_to!(tree, "inside async leaf {}", index);
|
||||
tree.print();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn async_barrier() {
|
||||
let tree = TreeBuilder::new();
|
||||
defer_peek_print!(tree);
|
||||
add_branch_to!(tree, "root");
|
||||
add_leaf_to!(tree, "before async");
|
||||
|
||||
let x2 = wait_a_bit(tree.clone(), 4);
|
||||
let x1 = wait_a_bit(tree.clone(), 5);
|
||||
let x3 = wait_a_bit(tree.clone(), 3);
|
||||
let x4 = wait_a_bit(tree.clone(), 2);
|
||||
let x5 = wait_a_bit(tree.clone(), 1);
|
||||
|
||||
add_leaf_to!(tree, "before join async");
|
||||
|
||||
join5(x1, x2, x3, x4, x5).await;
|
||||
add_leaf_to!(tree, "after join async");
|
||||
assert_eq!(tree.peek_string(), "after join async");
|
||||
}
|
||||
}
|
||||
224
third_party/rust/debug_tree/src/tree_config.rs
vendored
224
third_party/rust/debug_tree/src/tree_config.rs
vendored
@@ -1,224 +0,0 @@
|
||||
use once_cell::sync::Lazy;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TreeSymbols {
|
||||
/// A vertical base of the tree (│)
|
||||
pub continued: &'static str,
|
||||
|
||||
/// Symbol for joining the first branch in a group (├)
|
||||
pub join_first: &'static str,
|
||||
|
||||
/// Symbol for joining the last branch in a group (└)
|
||||
pub join_last: &'static str,
|
||||
|
||||
/// Symbol for joining a branch that is not first or last in its group (├)
|
||||
pub join_inner: &'static str,
|
||||
|
||||
/// Symbol for joining a branch if it is the only one in its group (├)
|
||||
pub join_only: &'static str,
|
||||
|
||||
/// A repeated branch token (─)
|
||||
pub branch: &'static str,
|
||||
|
||||
/// End of a leaf (╼)
|
||||
pub leaf: &'static str,
|
||||
|
||||
pub multiline_first: Option<&'static str>,
|
||||
pub multiline_continued: Option<&'static str>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TreeConfig {
|
||||
pub symbols: TreeSymbols,
|
||||
|
||||
/// Aside from the first branch, `indent` is equal to the number of spaces a child branch is
|
||||
/// shifted from its parent.
|
||||
pub indent: usize,
|
||||
|
||||
pub show_first_level: bool,
|
||||
}
|
||||
impl TreeSymbols {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
continued: "│",
|
||||
join_first: "├",
|
||||
join_inner: "├",
|
||||
join_last: "└",
|
||||
join_only: "└",
|
||||
branch: "─",
|
||||
leaf: "╼ ",
|
||||
multiline_first: None,
|
||||
multiline_continued: None,
|
||||
}
|
||||
}
|
||||
pub fn with_pipes() -> Self {
|
||||
Self {
|
||||
continued: "║",
|
||||
join_first: "╠",
|
||||
join_inner: "╠",
|
||||
join_last: "╚",
|
||||
join_only: "╚",
|
||||
branch: "═",
|
||||
leaf: "╼ ",
|
||||
multiline_first: None,
|
||||
multiline_continued: None,
|
||||
}
|
||||
}
|
||||
pub fn with_thick() -> Self {
|
||||
Self {
|
||||
continued: "┃",
|
||||
join_first: "┣",
|
||||
join_inner: "┣",
|
||||
join_last: "┗",
|
||||
join_only: "┗",
|
||||
branch: "━",
|
||||
leaf: "╼ ",
|
||||
multiline_first: None,
|
||||
multiline_continued: None,
|
||||
}
|
||||
}
|
||||
pub fn with_rounded() -> Self {
|
||||
Self {
|
||||
continued: "│",
|
||||
join_first: "├",
|
||||
join_inner: "├",
|
||||
join_last: "╰",
|
||||
join_only: "╰",
|
||||
branch: "─",
|
||||
leaf: "╼ ",
|
||||
multiline_first: None,
|
||||
multiline_continued: None,
|
||||
}
|
||||
}
|
||||
pub fn with_dashed() -> Self {
|
||||
Self {
|
||||
continued: "┊",
|
||||
join_first: "┊",
|
||||
join_inner: "┊",
|
||||
join_last: "'",
|
||||
join_only: "'",
|
||||
branch: "╌",
|
||||
leaf: "- ",
|
||||
multiline_first: None,
|
||||
multiline_continued: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn continued(mut self, sym: &'static str) -> Self {
|
||||
self.continued = sym;
|
||||
self
|
||||
}
|
||||
pub fn join_first(mut self, sym: &'static str) -> Self {
|
||||
self.join_first = sym;
|
||||
self
|
||||
}
|
||||
pub fn join_inner(mut self, sym: &'static str) -> Self {
|
||||
self.join_inner = sym;
|
||||
self
|
||||
}
|
||||
pub fn join_last(mut self, sym: &'static str) -> Self {
|
||||
self.join_last = sym;
|
||||
self
|
||||
}
|
||||
pub fn join_only(mut self, sym: &'static str) -> Self {
|
||||
self.join_only = sym;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn branch(mut self, sym: &'static str) -> Self {
|
||||
self.branch = sym;
|
||||
self
|
||||
}
|
||||
pub fn leaf(mut self, sym: &'static str) -> Self {
|
||||
self.leaf = sym;
|
||||
self
|
||||
}
|
||||
pub fn multiline_first(mut self, sym: &'static str) -> Self {
|
||||
self.multiline_first = Some(sym);
|
||||
self
|
||||
}
|
||||
pub fn multiline_continued(mut self, sym: &'static str) -> Self {
|
||||
self.multiline_continued = Some(sym);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl TreeConfig {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
symbols: TreeSymbols::new(),
|
||||
indent: 2,
|
||||
show_first_level: false,
|
||||
}
|
||||
}
|
||||
pub fn with_symbols(symbols: TreeSymbols) -> Self {
|
||||
Self {
|
||||
symbols,
|
||||
indent: 2,
|
||||
show_first_level: false,
|
||||
}
|
||||
}
|
||||
pub fn indent(mut self, x: usize) -> Self {
|
||||
self.indent = x;
|
||||
self
|
||||
}
|
||||
pub fn show_first_level(mut self) -> Self {
|
||||
self.show_first_level = true;
|
||||
self
|
||||
}
|
||||
pub fn hide_first_level(mut self) -> Self {
|
||||
self.show_first_level = false;
|
||||
self
|
||||
}
|
||||
pub fn symbols(mut self, x: TreeSymbols) -> Self {
|
||||
self.symbols = x;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for TreeSymbols {
|
||||
fn default() -> Self {
|
||||
tree_config_symbols()
|
||||
}
|
||||
}
|
||||
impl Default for TreeConfig {
|
||||
fn default() -> Self {
|
||||
tree_config()
|
||||
}
|
||||
}
|
||||
|
||||
static DEFAULT_CONFIG: Lazy<Arc<Mutex<TreeConfig>>> =
|
||||
Lazy::new(|| -> Arc<Mutex<TreeConfig>> { Arc::new(Mutex::new(TreeConfig::new())) });
|
||||
|
||||
/// Set the default tree config
|
||||
pub fn set_tree_config(x: TreeConfig) {
|
||||
*DEFAULT_CONFIG.lock().unwrap() = x;
|
||||
}
|
||||
|
||||
/// The default tree config
|
||||
pub fn tree_config() -> TreeConfig {
|
||||
DEFAULT_CONFIG.lock().unwrap().clone()
|
||||
}
|
||||
|
||||
/// Set the default tree symbols config
|
||||
pub fn set_tree_config_symbols(x: TreeSymbols) {
|
||||
DEFAULT_CONFIG.lock().unwrap().symbols = x;
|
||||
}
|
||||
|
||||
/// The default tree symbols config
|
||||
pub fn tree_config_symbols() -> TreeSymbols {
|
||||
DEFAULT_CONFIG.lock().unwrap().symbols.clone()
|
||||
}
|
||||
|
||||
/// The default tree symbols config
|
||||
pub fn update_tree_config<F: FnMut(&mut TreeConfig)>(mut update: F) {
|
||||
let mut x = DEFAULT_CONFIG.lock().unwrap();
|
||||
update(&mut x);
|
||||
}
|
||||
|
||||
/// The default tree symbols config
|
||||
pub fn update_tree_config_symbols<F: FnMut(&mut TreeSymbols)>(mut update: F) {
|
||||
let mut x = DEFAULT_CONFIG.lock().unwrap();
|
||||
update(&mut x.symbols);
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
{"files":{"CHANGELOG.md":"aa4370a41f9b2860b267324f4c0a16c80747ae418e62b3d6b79d59eaa334093a","Cargo.lock":"de0bb0bd4416bb51b01be208bc3c9b57db3b243099dbbe6a9c67f6c780cb90c1","Cargo.toml":"352bd903a8be234d62cc2e0a9b58d7fd682c74c008b0a9aa689ad5df781e6842","LICENSE":"233be68ab89eaccff940b48aa638fbecbf060f4da9148837f78d0c3146b1dd30","README.md":"61899f503016729b687071d4168933d49a958120e54eaf52d4f9026cc4375668","README.tpl":"a3a1fa6df2cd9d0dca6804e578754533c4a99f6f5b42f78c55865ba339d3e097","examples/service_client.rs":"b329a8314235744a8637a2ad6bbfd36fc0a49f72549bf2af372e2034e6699d1b","src/lib.rs":"95dd484abdbb2882be1ddebc8335eb14725e835b4cede5b9305685254d803a70","src/parse.rs":"09ee2754615b2923d92489b20dd6b50f3dce2d34f6830d669e299995638aa65a","src/visit.rs":"8d3586f3414a9720a86c3308a29b18d16ba7be84fdbe263f3600e7ac70033123","tests/test.rs":"06d71ea5e81a46a9f0be84230aafbdab15c13ba61d5579d403f736574daabf7b","tests/ui/01-maybe-async.rs":"d33ef273ad5f3e5f7979c37b58a2a753fc342429025622ccfcb83104d185701e","tests/ui/02-must-be-async.rs":"0659a11158be19d22f97005a3be70081ec872d3e63091455022565fcd959a8d2","tests/ui/03-must-be-sync.rs":"5dcdc928b099c9cec17e70993bf82c475388a88a73f432bd785484a318a0632e","tests/ui/04-unit-test-util.rs":"04f3f029900e2f5d086961d753ddf10f122dbebc709618303f83fab23f6d4c29","tests/ui/05-replace-future-generic-type-with-output.rs":"481f4afb978ffc7cd756765f72e3456813dc8748ada57ac200e1a02a3d9562c3","tests/ui/06-sync_impl_async_impl.rs":"a8e8ec6db193172061eed098396ab9a0b6b03707f9854eae8a5df45c2340c809","tests/ui/test_fail/01-empty-test.rs":"d87f35ae690dcd2851fea2bb8f15745bb86aa9301efd642f30aa6018654dcebb","tests/ui/test_fail/01-empty-test.stderr":"7bb5274635582533ba5a761cdb36a211c382ff57c973352f4a7ac5a6953f08b4","tests/ui/test_fail/02-unknown-path.rs":"fc7cd52d4d7107c779980386ed5f4d133a1dcdf74261fd0ea0df53d03d5c8b1b","tests/ui/test_fail/02-unknown-path.stderr":"28374dc1c4c44f6ba7eedff576e43c3d724dc4f204040d8cec5773e5f997a44b","tests/ui/test_fail/03-async-gt2.rs":"336d628e829e6fdf41764b3d13afe5d41d1100f46bfedb61b74a4723aecb0d40","tests/ui/test_fail/03-async-gt2.stderr":"677f322ece465459410c7fdc76001f76869f9b81906096b0ece6fd97247a1f1f","tests/ui/test_fail/04-bad-sync-cond.rs":"8698ab272081cb571fcdd163a847f815b153125932ee1837829778dbe769826b","tests/ui/test_fail/04-bad-sync-cond.stderr":"bd8bdf65b1cbba7610441341dd9212da066cdd2ebee65f83a772b0921cde1e38","tests/unit-test-util.rs":"a9e09c54e14c07e5d9f33f245d583455d9324be2e826b20ce2b882f990db937e"},"package":"5cf92c10c7e361d6b99666ec1c6f9805b0bea2c3bd8c78dc6fe98ac5bd78db11"}
|
||||
72
third_party/rust/maybe-async/CHANGELOG.md
vendored
72
third_party/rust/maybe-async/CHANGELOG.md
vendored
@@ -1,72 +0,0 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||
|
||||
### [0.2.10](https://github.com/fMeow/maybe-async-rs/compare/v0.2.9...v0.2.10) (2024-02-22)
|
||||
|
||||
### [0.2.9](https://github.com/fMeow/maybe-async-rs/compare/v0.2.8...v0.2.9) (2024-01-31)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* support `async fn` in traits ([282eb76](https://github.com/fMeow/maybe-async-rs/commit/282eb76c0be0433ade8d0a2a11646e09db2f37b7))
|
||||
|
||||
### [0.2.8](https://github.com/fMeow/maybe-async-rs/compare/v0.2.7...v0.2.8) (2024-01-30)
|
||||
|
||||
### [0.2.7](https://github.com/fMeow/maybe-async-rs/compare/v0.2.6...v0.2.7) (2023-02-01)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* allow `maybe_async` on static ([a08b112](https://github.com/fMeow/maybe-async-rs/commit/a08b11218bab0d1db304a4f68e0230c022632168))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* applying to pub(crate) trait fail ([8cf762f](https://github.com/fMeow/maybe-async-rs/commit/8cf762fdeb1d316716fa01fb2525e5a6f5d25987))
|
||||
|
||||
### [0.2.6](https://github.com/guoli-lyu/maybe-async-rs/compare/v0.2.4...v0.2.6) (2021-05-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* remove async test if condition not match ([0089daa](https://github.com/guoli-lyu/maybe-async-rs/commit/0089daad6e3419e11d123e8c5c87a1139880027f))
|
||||
* test is removed when is_sync ([377815a](https://github.com/guoli-lyu/maybe-async-rs/commit/377815a7a81efc4a0332cc2716a7d603b350ff03))
|
||||
|
||||
### [0.2.5](https://github.com/guoli-lyu/maybe-async-rs/compare/v0.2.4...v0.2.5) (2021-05-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* remove async test if condition not match ([0c49246](https://github.com/guoli-lyu/maybe-async-rs/commit/0c49246a3245773faff482f6b42d66522d2af208))
|
||||
|
||||
### [0.2.4](https://github.com/guoli-lyu/maybe-async-rs/compare/v0.2.3...v0.2.4) (2021-03-28)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* replace generic type of Future with Output ([f296cc0](https://github.com/guoli-lyu/maybe-async-rs/commit/f296cc05c90923ae3a3eeea3c5173d06d642c2ab))
|
||||
* search trait bound that ends with `Future` ([3508ff2](https://github.com/guoli-lyu/maybe-async-rs/commit/3508ff2987cce61808297aa920c522e0f2012a8a))
|
||||
|
||||
### [0.2.3](https://github.com/guoli-lyu/maybe-async-rs/compare/v0.2.2...v0.2.3) (2021-03-27)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* enable full feature gate for syn ([614c085](https://github.com/guoli-lyu/maybe-async-rs/commit/614c085444caf6d0d493422ca20f8ed3b86b7315))
|
||||
|
||||
### [0.2.2](https://github.com/guoli-lyu/maybe-async-rs/compare/v0.2.1...v0.2.2) (2020-10-19)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* avoid extra parenthesis and braces ([8d146f9](https://github.com/guoli-lyu/maybe-async-rs/commit/8d146f9a9234339de1ef6b9f7ffd44421a8d6c68))
|
||||
* remove parenthesis wrap in await ([bc5f460](https://github.com/guoli-lyu/maybe-async-rs/commit/bc5f46078bfb5ccc1599570303aa72a84cc5e2d7))
|
||||
* wrap await expr into block instead of paren ([5c4232a](https://github.com/guoli-lyu/maybe-async-rs/commit/5c4232a07035e9c2d4add280cc5b090a7bde471b))
|
||||
|
||||
### [0.2.1](https://github.com/guoli-lyu/maybe-async-rs/compare/v0.2.0...v0.2.1) (2020-10-05)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* allow unused_paren when convert to sync ([242ded2](https://github.com/guoli-lyu/maybe-async-rs/commit/242ded2fb9f1cc3c883e0f39a081a555e7a74198))
|
||||
1072
third_party/rust/maybe-async/Cargo.lock
generated
vendored
1072
third_party/rust/maybe-async/Cargo.lock
generated
vendored
File diff suppressed because it is too large
Load Diff
70
third_party/rust/maybe-async/Cargo.toml
vendored
70
third_party/rust/maybe-async/Cargo.toml
vendored
@@ -1,70 +0,0 @@
|
||||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g., crates.io) dependencies.
|
||||
#
|
||||
# If you are reading this file be aware that the original Cargo.toml
|
||||
# will likely look very different (and much more reasonable).
|
||||
# See Cargo.toml.orig for the original contents.
|
||||
|
||||
[package]
|
||||
edition = "2021"
|
||||
name = "maybe-async"
|
||||
version = "0.2.10"
|
||||
authors = ["Guoli Lyu <guoli-lv@hotmail.com>"]
|
||||
description = "A procedure macro to unify SYNC and ASYNC implementation"
|
||||
documentation = "https://docs.rs/maybe-async"
|
||||
readme = "README.md"
|
||||
keywords = [
|
||||
"maybe",
|
||||
"async",
|
||||
"futures",
|
||||
"macros",
|
||||
"proc_macro",
|
||||
]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/fMeow/maybe-async-rs"
|
||||
|
||||
[lib]
|
||||
path = "src/lib.rs"
|
||||
proc-macro = true
|
||||
|
||||
[dependencies.proc-macro2]
|
||||
version = "1.0"
|
||||
|
||||
[dependencies.quote]
|
||||
version = "1.0"
|
||||
|
||||
[dependencies.syn]
|
||||
version = "2.0"
|
||||
features = [
|
||||
"visit-mut",
|
||||
"full",
|
||||
]
|
||||
|
||||
[dev-dependencies.async-std]
|
||||
version = "1"
|
||||
features = ["attributes"]
|
||||
|
||||
[dev-dependencies.async-trait]
|
||||
version = "0.1"
|
||||
|
||||
[dev-dependencies.tokio]
|
||||
version = "1"
|
||||
features = [
|
||||
"macros",
|
||||
"rt-multi-thread",
|
||||
]
|
||||
|
||||
[dev-dependencies.trybuild]
|
||||
version = "1"
|
||||
features = ["diff"]
|
||||
|
||||
[features]
|
||||
default = []
|
||||
is_sync = []
|
||||
|
||||
[badges.maintenance]
|
||||
status = "actively-developed"
|
||||
19
third_party/rust/maybe-async/LICENSE
vendored
19
third_party/rust/maybe-async/LICENSE
vendored
@@ -1,19 +0,0 @@
|
||||
Copyright (c) 2020 Guoli Lyu
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
295
third_party/rust/maybe-async/README.md
vendored
295
third_party/rust/maybe-async/README.md
vendored
@@ -1,295 +0,0 @@
|
||||
# maybe-async
|
||||
|
||||
**Why bother writing similar code twice for blocking and async code?**
|
||||
|
||||
[](https://github.com/fMeow/maybe-async-rs/actions)
|
||||
[](./LICENSE)
|
||||
[](https://crates.io/crates/maybe-async)
|
||||
[](https://docs.rs/maybe-async)
|
||||
|
||||
When implementing both sync and async versions of API in a crate, most API
|
||||
of the two version are almost the same except for some async/await keyword.
|
||||
|
||||
`maybe-async` help unifying async and sync implementation by **procedural
|
||||
macro**.
|
||||
- Write async code with normal `async`, `await`, and let `maybe_async`
|
||||
handles
|
||||
those `async` and `await` when you need a blocking code.
|
||||
- Switch between sync and async by toggling `is_sync` feature gate in
|
||||
`Cargo.toml`.
|
||||
- use `must_be_async` and `must_be_sync` to keep code in specified version
|
||||
- use `async_impl` and `sync_impl` to only compile code block on specified
|
||||
version
|
||||
- A handy macro to unify unit test code is also provided.
|
||||
|
||||
These procedural macros can be applied to the following codes:
|
||||
- trait item declaration
|
||||
- trait implementation
|
||||
- function definition
|
||||
- struct definition
|
||||
|
||||
**RECOMMENDATION**: Enable **resolver ver2** in your crate, which is
|
||||
introduced in Rust 1.51. If not, two crates in dependency with conflict
|
||||
version (one async and another blocking) can fail compilation.
|
||||
|
||||
|
||||
### Motivation
|
||||
|
||||
The async/await language feature alters the async world of rust.
|
||||
Comparing with the map/and_then style, now the async code really resembles
|
||||
sync version code.
|
||||
|
||||
In many crates, the async and sync version of crates shares the same API,
|
||||
but the minor difference that all async code must be awaited prevent the
|
||||
unification of async and sync code. In other words, we are forced to write
|
||||
an async and a sync implementation respectively.
|
||||
|
||||
### Macros in Detail
|
||||
|
||||
`maybe-async` offers 4 set of attribute macros: `maybe_async`,
|
||||
`sync_impl`/`async_impl`, `must_be_sync`/`must_be_async`, and `test`.
|
||||
|
||||
To use `maybe-async`, we must know which block of codes is only used on
|
||||
blocking implementation, and which on async. These two implementation should
|
||||
share the same function signatures except for async/await keywords, and use
|
||||
`sync_impl` and `async_impl` to mark these implementation.
|
||||
|
||||
Use `maybe_async` macro on codes that share the same API on both async and
|
||||
blocking code except for async/await keywords. And use feature gate
|
||||
`is_sync` in `Cargo.toml` to toggle between async and blocking code.
|
||||
|
||||
- `maybe_async`
|
||||
|
||||
Offers a unified feature gate to provide sync and async conversion on
|
||||
demand by feature gate `is_sync`, with **async first** policy.
|
||||
|
||||
Want to keep async code? add `maybe_async` in dependencies with default
|
||||
features, which means `maybe_async` is the same as `must_be_async`:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
maybe_async = "0.2"
|
||||
```
|
||||
|
||||
Want to convert async code to sync? Add `maybe_async` to dependencies with
|
||||
an `is_sync` feature gate. In this way, `maybe_async` is the same as
|
||||
`must_be_sync`:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
maybe_async = { version = "0.2", features = ["is_sync"] }
|
||||
```
|
||||
|
||||
There are three usage variants for `maybe_async` attribute usage:
|
||||
- `#[maybe_async]` or `#[maybe_async(Send)]`
|
||||
|
||||
In this mode, `#[async_trait::async_trait]` is added to trait declarations and trait implementations
|
||||
to support async fn in traits.
|
||||
|
||||
- `#[maybe_async(?Send)]`
|
||||
|
||||
Not all async traits need futures that are `dyn Future + Send`.
|
||||
In this mode, `#[async_trait::async_trait(?Send)]` is added to trait declarations and trait implementations,
|
||||
to avoid having "Send" and "Sync" bounds placed on the async trait
|
||||
methods.
|
||||
|
||||
- `#[maybe_async(AFIT)]`
|
||||
|
||||
AFIT is acronym for **a**sync **f**unction **i**n **t**rait, stabilized from rust 1.74
|
||||
|
||||
For compatibility reasons, the `async fn` in traits is supported via a verbose `AFIT` flag. This will become
|
||||
the default mode for the next major release.
|
||||
|
||||
- `must_be_async`
|
||||
|
||||
**Keep async**.
|
||||
|
||||
There are three usage variants for `must_be_async` attribute usage:
|
||||
- `#[must_be_async]` or `#[must_be_async(Send)]`
|
||||
- `#[must_be_async(?Send)]`
|
||||
- `#[must_be_async(AFIT)]`
|
||||
|
||||
- `must_be_sync`
|
||||
|
||||
**Convert to sync code**. Convert the async code into sync code by
|
||||
removing all `async move`, `async` and `await` keyword
|
||||
|
||||
|
||||
- `sync_impl`
|
||||
|
||||
A sync implementation should compile on blocking implementation and
|
||||
must simply disappear when we want async version.
|
||||
|
||||
Although most of the API are almost the same, there definitely come to a
|
||||
point when the async and sync version should differ greatly. For
|
||||
example, a MongoDB client may use the same API for async and sync
|
||||
version, but the code to actually send reqeust are quite different.
|
||||
|
||||
Here, we can use `sync_impl` to mark a synchronous implementation, and a
|
||||
sync implementation should disappear when we want async version.
|
||||
|
||||
- `async_impl`
|
||||
|
||||
An async implementation should on compile on async implementation and
|
||||
must simply disappear when we want sync version.
|
||||
|
||||
There are three usage variants for `async_impl` attribute usage:
|
||||
- `#[async_impl]` or `#[async_impl(Send)]`
|
||||
- `#[async_impl(?Send)]`
|
||||
- `#[async_impl(AFIT)]`
|
||||
|
||||
- `test`
|
||||
|
||||
Handy macro to unify async and sync **unit and e2e test** code.
|
||||
|
||||
You can specify the condition to compile to sync test code
|
||||
and also the conditions to compile to async test code with given test
|
||||
macro, e.x. `tokio::test`, `async_std::test`, etc. When only sync
|
||||
condition is specified,the test code only compiles when sync condition
|
||||
is met.
|
||||
|
||||
```rust
|
||||
# #[maybe_async::maybe_async]
|
||||
# async fn async_fn() -> bool {
|
||||
# true
|
||||
# }
|
||||
|
||||
##[maybe_async::test(
|
||||
feature="is_sync",
|
||||
async(
|
||||
all(not(feature="is_sync"), feature="async_std"),
|
||||
async_std::test
|
||||
),
|
||||
async(
|
||||
all(not(feature="is_sync"), feature="tokio"),
|
||||
tokio::test
|
||||
)
|
||||
)]
|
||||
async fn test_async_fn() {
|
||||
let res = async_fn().await;
|
||||
assert_eq!(res, true);
|
||||
}
|
||||
```
|
||||
|
||||
### What's Under the Hook
|
||||
|
||||
`maybe-async` compiles your code in different way with the `is_sync` feature
|
||||
gate. It removes all `await` and `async` keywords in your code under
|
||||
`maybe_async` macro and conditionally compiles codes under `async_impl` and
|
||||
`sync_impl`.
|
||||
|
||||
Here is a detailed example on what's going on whe the `is_sync` feature
|
||||
gate set or not.
|
||||
|
||||
```rust
|
||||
#[maybe_async::maybe_async(AFIT)]
|
||||
trait A {
|
||||
async fn async_fn_name() -> Result<(), ()> {
|
||||
Ok(())
|
||||
}
|
||||
fn sync_fn_name() -> Result<(), ()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct Foo;
|
||||
|
||||
#[maybe_async::maybe_async(AFIT)]
|
||||
impl A for Foo {
|
||||
async fn async_fn_name() -> Result<(), ()> {
|
||||
Ok(())
|
||||
}
|
||||
fn sync_fn_name() -> Result<(), ()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[maybe_async::maybe_async]
|
||||
async fn maybe_async_fn() -> Result<(), ()> {
|
||||
let a = Foo::async_fn_name().await?;
|
||||
|
||||
let b = Foo::sync_fn_name()?;
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
When `maybe-async` feature gate `is_sync` is **NOT** set, the generated code
|
||||
is async code:
|
||||
|
||||
```rust
|
||||
// Compiled code when `is_sync` is toggled off.
|
||||
trait A {
|
||||
async fn maybe_async_fn_name() -> Result<(), ()> {
|
||||
Ok(())
|
||||
}
|
||||
fn sync_fn_name() -> Result<(), ()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct Foo;
|
||||
|
||||
impl A for Foo {
|
||||
async fn maybe_async_fn_name() -> Result<(), ()> {
|
||||
Ok(())
|
||||
}
|
||||
fn sync_fn_name() -> Result<(), ()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
async fn maybe_async_fn() -> Result<(), ()> {
|
||||
let a = Foo::maybe_async_fn_name().await?;
|
||||
let b = Foo::sync_fn_name()?;
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
When `maybe-async` feature gate `is_sync` is set, all async keyword is
|
||||
ignored and yields a sync version code:
|
||||
|
||||
```rust
|
||||
// Compiled code when `is_sync` is toggled on.
|
||||
trait A {
|
||||
fn maybe_async_fn_name() -> Result<(), ()> {
|
||||
Ok(())
|
||||
}
|
||||
fn sync_fn_name() -> Result<(), ()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct Foo;
|
||||
|
||||
impl A for Foo {
|
||||
fn maybe_async_fn_name() -> Result<(), ()> {
|
||||
Ok(())
|
||||
}
|
||||
fn sync_fn_name() -> Result<(), ()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn maybe_async_fn() -> Result<(), ()> {
|
||||
let a = Foo::maybe_async_fn_name()?;
|
||||
let b = Foo::sync_fn_name()?;
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
#### rust client for services
|
||||
|
||||
When implementing rust client for any services, like awz3. The higher level
|
||||
API of async and sync version is almost the same, such as creating or
|
||||
deleting a bucket, retrieving an object, etc.
|
||||
|
||||
The example `service_client` is a proof of concept that `maybe_async` can
|
||||
actually free us from writing almost the same code for sync and async. We
|
||||
can toggle between a sync AWZ3 client and async one by `is_sync` feature
|
||||
gate when we add `maybe-async` to dependency.
|
||||
|
||||
|
||||
## License
|
||||
MIT
|
||||
3
third_party/rust/maybe-async/README.tpl
vendored
3
third_party/rust/maybe-async/README.tpl
vendored
@@ -1,3 +0,0 @@
|
||||
# {{crate}}
|
||||
|
||||
{{readme}}
|
||||
@@ -1,77 +0,0 @@
|
||||
#![allow(dead_code, unused_variables)]
|
||||
/// To use `maybe-async`, we must know which block of codes is only used on
|
||||
/// blocking implementation, and which on async. These two implementation should
|
||||
/// share the same API except for async/await keywords, and use `sync_impl` and
|
||||
/// `async_impl` to mark these implementation.
|
||||
type Response = String;
|
||||
type Url = &'static str;
|
||||
type Method = String;
|
||||
|
||||
/// InnerClient are used to actually send request,
|
||||
/// which differ a lot between sync and async.
|
||||
///
|
||||
/// Use native async function in trait
|
||||
#[maybe_async::maybe_async(AFIT)]
|
||||
trait InnerClient {
|
||||
async fn request(method: Method, url: Url, data: String) -> Response;
|
||||
#[inline]
|
||||
async fn post(url: Url, data: String) -> Response {
|
||||
Self::request(String::from("post"), url, data).await
|
||||
}
|
||||
#[inline]
|
||||
async fn delete(url: Url, data: String) -> Response {
|
||||
Self::request(String::from("delete"), url, data).await
|
||||
}
|
||||
}
|
||||
|
||||
/// The higher level API for end user.
|
||||
pub struct ServiceClient;
|
||||
|
||||
/// Synchronous implementation, only compiles when `is_sync` feature is off.
|
||||
/// Else the compiler will complain that *request is defined multiple times* and
|
||||
/// blabla.
|
||||
#[maybe_async::sync_impl]
|
||||
impl InnerClient for ServiceClient {
|
||||
fn request(method: Method, url: Url, data: String) -> Response {
|
||||
// your implementation for sync, like use
|
||||
// `reqwest::blocking` to send request
|
||||
String::from("pretend we have a response")
|
||||
}
|
||||
}
|
||||
|
||||
/// Asynchronous implementation, only compiles when `is_sync` feature is off.
|
||||
#[maybe_async::async_impl(AFIT)]
|
||||
impl InnerClient for ServiceClient {
|
||||
async fn request(method: Method, url: Url, data: String) -> Response {
|
||||
// your implementation for async, like use `reqwest::client`
|
||||
// or `async_std` to send request
|
||||
String::from("pretend we have a response")
|
||||
}
|
||||
}
|
||||
|
||||
/// Code of upstream API are almost the same for sync and async,
|
||||
/// except for async/await keyword.
|
||||
impl ServiceClient {
|
||||
#[maybe_async::maybe_async]
|
||||
async fn create_bucket(name: String) -> Response {
|
||||
Self::post("http://correct_url4create", String::from("my_bucket")).await
|
||||
// When `is_sync` is toggle on, this block will compiles to:
|
||||
// Self::post("http://correct_url4create", String::from("my_bucket"))
|
||||
}
|
||||
#[maybe_async::maybe_async]
|
||||
async fn delete_bucket(name: String) -> Response {
|
||||
Self::delete("http://correct_url4delete", String::from("my_bucket")).await
|
||||
}
|
||||
// and another thousands of functions that interact with service side
|
||||
}
|
||||
|
||||
#[maybe_async::sync_impl]
|
||||
fn main() {
|
||||
let _ = ServiceClient::create_bucket("bucket".to_owned());
|
||||
}
|
||||
|
||||
#[maybe_async::async_impl]
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let _ = ServiceClient::create_bucket("bucket".to_owned()).await;
|
||||
}
|
||||
626
third_party/rust/maybe-async/src/lib.rs
vendored
626
third_party/rust/maybe-async/src/lib.rs
vendored
@@ -1,626 +0,0 @@
|
||||
//! **Why bother writing similar code twice for blocking and async code?**
|
||||
//!
|
||||
//! [](https://github.com/fMeow/maybe-async-rs/actions)
|
||||
//! [](./LICENSE)
|
||||
//! [](https://crates.io/crates/maybe-async)
|
||||
//! [](https://docs.rs/maybe-async)
|
||||
//!
|
||||
//! When implementing both sync and async versions of API in a crate, most API
|
||||
//! of the two version are almost the same except for some async/await keyword.
|
||||
//!
|
||||
//! `maybe-async` help unifying async and sync implementation by **procedural
|
||||
//! macro**.
|
||||
//! - Write async code with normal `async`, `await`, and let `maybe_async`
|
||||
//! handles
|
||||
//! those `async` and `await` when you need a blocking code.
|
||||
//! - Switch between sync and async by toggling `is_sync` feature gate in
|
||||
//! `Cargo.toml`.
|
||||
//! - use `must_be_async` and `must_be_sync` to keep code in specified version
|
||||
//! - use `async_impl` and `sync_impl` to only compile code block on specified
|
||||
//! version
|
||||
//! - A handy macro to unify unit test code is also provided.
|
||||
//!
|
||||
//! These procedural macros can be applied to the following codes:
|
||||
//! - trait item declaration
|
||||
//! - trait implementation
|
||||
//! - function definition
|
||||
//! - struct definition
|
||||
//!
|
||||
//! **RECOMMENDATION**: Enable **resolver ver2** in your crate, which is
|
||||
//! introduced in Rust 1.51. If not, two crates in dependency with conflict
|
||||
//! version (one async and another blocking) can fail compilation.
|
||||
//!
|
||||
//!
|
||||
//! ## Motivation
|
||||
//!
|
||||
//! The async/await language feature alters the async world of rust.
|
||||
//! Comparing with the map/and_then style, now the async code really resembles
|
||||
//! sync version code.
|
||||
//!
|
||||
//! In many crates, the async and sync version of crates shares the same API,
|
||||
//! but the minor difference that all async code must be awaited prevent the
|
||||
//! unification of async and sync code. In other words, we are forced to write
|
||||
//! an async and a sync implementation respectively.
|
||||
//!
|
||||
//! ## Macros in Detail
|
||||
//!
|
||||
//! `maybe-async` offers 4 set of attribute macros: `maybe_async`,
|
||||
//! `sync_impl`/`async_impl`, `must_be_sync`/`must_be_async`, and `test`.
|
||||
//!
|
||||
//! To use `maybe-async`, we must know which block of codes is only used on
|
||||
//! blocking implementation, and which on async. These two implementation should
|
||||
//! share the same function signatures except for async/await keywords, and use
|
||||
//! `sync_impl` and `async_impl` to mark these implementation.
|
||||
//!
|
||||
//! Use `maybe_async` macro on codes that share the same API on both async and
|
||||
//! blocking code except for async/await keywords. And use feature gate
|
||||
//! `is_sync` in `Cargo.toml` to toggle between async and blocking code.
|
||||
//!
|
||||
//! - `maybe_async`
|
||||
//!
|
||||
//! Offers a unified feature gate to provide sync and async conversion on
|
||||
//! demand by feature gate `is_sync`, with **async first** policy.
|
||||
//!
|
||||
//! Want to keep async code? add `maybe_async` in dependencies with default
|
||||
//! features, which means `maybe_async` is the same as `must_be_async`:
|
||||
//!
|
||||
//! ```toml
|
||||
//! [dependencies]
|
||||
//! maybe_async = "0.2"
|
||||
//! ```
|
||||
//!
|
||||
//! Want to convert async code to sync? Add `maybe_async` to dependencies with
|
||||
//! an `is_sync` feature gate. In this way, `maybe_async` is the same as
|
||||
//! `must_be_sync`:
|
||||
//!
|
||||
//! ```toml
|
||||
//! [dependencies]
|
||||
//! maybe_async = { version = "0.2", features = ["is_sync"] }
|
||||
//! ```
|
||||
//!
|
||||
//! There are three usage variants for `maybe_async` attribute usage:
|
||||
//! - `#[maybe_async]` or `#[maybe_async(Send)]`
|
||||
//!
|
||||
//! In this mode, `#[async_trait::async_trait]` is added to trait declarations and trait implementations
|
||||
//! to support async fn in traits.
|
||||
//!
|
||||
//! - `#[maybe_async(?Send)]`
|
||||
//!
|
||||
//! Not all async traits need futures that are `dyn Future + Send`.
|
||||
//! In this mode, `#[async_trait::async_trait(?Send)]` is added to trait declarations and trait implementations,
|
||||
//! to avoid having "Send" and "Sync" bounds placed on the async trait
|
||||
//! methods.
|
||||
//!
|
||||
//! - `#[maybe_async(AFIT)]`
|
||||
//!
|
||||
//! AFIT is acronym for **a**sync **f**unction **i**n **t**rait, stabilized from rust 1.74
|
||||
//!
|
||||
//! For compatibility reasons, the `async fn` in traits is supported via a verbose `AFIT` flag. This will become
|
||||
//! the default mode for the next major release.
|
||||
//!
|
||||
//! - `must_be_async`
|
||||
//!
|
||||
//! **Keep async**.
|
||||
//!
|
||||
//! There are three usage variants for `must_be_async` attribute usage:
|
||||
//! - `#[must_be_async]` or `#[must_be_async(Send)]`
|
||||
//! - `#[must_be_async(?Send)]`
|
||||
//! - `#[must_be_async(AFIT)]`
|
||||
//!
|
||||
//! - `must_be_sync`
|
||||
//!
|
||||
//! **Convert to sync code**. Convert the async code into sync code by
|
||||
//! removing all `async move`, `async` and `await` keyword
|
||||
//!
|
||||
//!
|
||||
//! - `sync_impl`
|
||||
//!
|
||||
//! A sync implementation should compile on blocking implementation and
|
||||
//! must simply disappear when we want async version.
|
||||
//!
|
||||
//! Although most of the API are almost the same, there definitely come to a
|
||||
//! point when the async and sync version should differ greatly. For
|
||||
//! example, a MongoDB client may use the same API for async and sync
|
||||
//! version, but the code to actually send reqeust are quite different.
|
||||
//!
|
||||
//! Here, we can use `sync_impl` to mark a synchronous implementation, and a
|
||||
//! sync implementation should disappear when we want async version.
|
||||
//!
|
||||
//! - `async_impl`
|
||||
//!
|
||||
//! An async implementation should on compile on async implementation and
|
||||
//! must simply disappear when we want sync version.
|
||||
//!
|
||||
//! There are three usage variants for `async_impl` attribute usage:
|
||||
//! - `#[async_impl]` or `#[async_impl(Send)]`
|
||||
//! - `#[async_impl(?Send)]`
|
||||
//! - `#[async_impl(AFIT)]`
|
||||
//!
|
||||
//! - `test`
|
||||
//!
|
||||
//! Handy macro to unify async and sync **unit and e2e test** code.
|
||||
//!
|
||||
//! You can specify the condition to compile to sync test code
|
||||
//! and also the conditions to compile to async test code with given test
|
||||
//! macro, e.x. `tokio::test`, `async_std::test`, etc. When only sync
|
||||
//! condition is specified,the test code only compiles when sync condition
|
||||
//! is met.
|
||||
//!
|
||||
//! ```rust
|
||||
//! # #[maybe_async::maybe_async]
|
||||
//! # async fn async_fn() -> bool {
|
||||
//! # true
|
||||
//! # }
|
||||
//!
|
||||
//! ##[maybe_async::test(
|
||||
//! feature="is_sync",
|
||||
//! async(
|
||||
//! all(not(feature="is_sync"), feature="async_std"),
|
||||
//! async_std::test
|
||||
//! ),
|
||||
//! async(
|
||||
//! all(not(feature="is_sync"), feature="tokio"),
|
||||
//! tokio::test
|
||||
//! )
|
||||
//! )]
|
||||
//! async fn test_async_fn() {
|
||||
//! let res = async_fn().await;
|
||||
//! assert_eq!(res, true);
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ## What's Under the Hook
|
||||
//!
|
||||
//! `maybe-async` compiles your code in different way with the `is_sync` feature
|
||||
//! gate. It removes all `await` and `async` keywords in your code under
|
||||
//! `maybe_async` macro and conditionally compiles codes under `async_impl` and
|
||||
//! `sync_impl`.
|
||||
//!
|
||||
//! Here is a detailed example on what's going on whe the `is_sync` feature
|
||||
//! gate set or not.
|
||||
//!
|
||||
//! ```rust
|
||||
//! #[maybe_async::maybe_async(AFIT)]
|
||||
//! trait A {
|
||||
//! async fn async_fn_name() -> Result<(), ()> {
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! fn sync_fn_name() -> Result<(), ()> {
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! struct Foo;
|
||||
//!
|
||||
//! #[maybe_async::maybe_async(AFIT)]
|
||||
//! impl A for Foo {
|
||||
//! async fn async_fn_name() -> Result<(), ()> {
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! fn sync_fn_name() -> Result<(), ()> {
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! #[maybe_async::maybe_async]
|
||||
//! async fn maybe_async_fn() -> Result<(), ()> {
|
||||
//! let a = Foo::async_fn_name().await?;
|
||||
//!
|
||||
//! let b = Foo::sync_fn_name()?;
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! When `maybe-async` feature gate `is_sync` is **NOT** set, the generated code
|
||||
//! is async code:
|
||||
//!
|
||||
//! ```rust
|
||||
//! // Compiled code when `is_sync` is toggled off.
|
||||
//! trait A {
|
||||
//! async fn maybe_async_fn_name() -> Result<(), ()> {
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! fn sync_fn_name() -> Result<(), ()> {
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! struct Foo;
|
||||
//!
|
||||
//! impl A for Foo {
|
||||
//! async fn maybe_async_fn_name() -> Result<(), ()> {
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! fn sync_fn_name() -> Result<(), ()> {
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! async fn maybe_async_fn() -> Result<(), ()> {
|
||||
//! let a = Foo::maybe_async_fn_name().await?;
|
||||
//! let b = Foo::sync_fn_name()?;
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! When `maybe-async` feature gate `is_sync` is set, all async keyword is
|
||||
//! ignored and yields a sync version code:
|
||||
//!
|
||||
//! ```rust
|
||||
//! // Compiled code when `is_sync` is toggled on.
|
||||
//! trait A {
|
||||
//! fn maybe_async_fn_name() -> Result<(), ()> {
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! fn sync_fn_name() -> Result<(), ()> {
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! struct Foo;
|
||||
//!
|
||||
//! impl A for Foo {
|
||||
//! fn maybe_async_fn_name() -> Result<(), ()> {
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! fn sync_fn_name() -> Result<(), ()> {
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! fn maybe_async_fn() -> Result<(), ()> {
|
||||
//! let a = Foo::maybe_async_fn_name()?;
|
||||
//! let b = Foo::sync_fn_name()?;
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ## Examples
|
||||
//!
|
||||
//! ### rust client for services
|
||||
//!
|
||||
//! When implementing rust client for any services, like awz3. The higher level
|
||||
//! API of async and sync version is almost the same, such as creating or
|
||||
//! deleting a bucket, retrieving an object, etc.
|
||||
//!
|
||||
//! The example `service_client` is a proof of concept that `maybe_async` can
|
||||
//! actually free us from writing almost the same code for sync and async. We
|
||||
//! can toggle between a sync AWZ3 client and async one by `is_sync` feature
|
||||
//! gate when we add `maybe-async` to dependency.
|
||||
//!
|
||||
//!
|
||||
//! # License
|
||||
//! MIT
|
||||
|
||||
extern crate proc_macro;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
|
||||
use proc_macro2::{Span, TokenStream as TokenStream2};
|
||||
use syn::{
|
||||
ext::IdentExt,
|
||||
parenthesized,
|
||||
parse::{ParseStream, Parser},
|
||||
parse_macro_input, token, Ident, ImplItem, LitStr, Meta, Result, Token, TraitItem,
|
||||
};
|
||||
|
||||
use quote::quote;
|
||||
|
||||
use crate::{parse::Item, visit::AsyncAwaitRemoval};
|
||||
|
||||
mod parse;
|
||||
mod visit;
|
||||
enum AsyncTraitMode {
|
||||
Send,
|
||||
NotSend,
|
||||
Off,
|
||||
}
|
||||
|
||||
fn convert_async(input: &mut Item, async_trait_mode: AsyncTraitMode) -> TokenStream2 {
|
||||
match input {
|
||||
Item::Trait(item) => match async_trait_mode {
|
||||
AsyncTraitMode::Send => quote!(#[async_trait::async_trait]#item),
|
||||
AsyncTraitMode::NotSend => quote!(#[async_trait::async_trait(?Send)]#item),
|
||||
AsyncTraitMode::Off => quote!(#item),
|
||||
},
|
||||
Item::Impl(item) => {
|
||||
let async_trait_mode = item
|
||||
.trait_
|
||||
.as_ref()
|
||||
.map_or(AsyncTraitMode::Off, |_| async_trait_mode);
|
||||
match async_trait_mode {
|
||||
AsyncTraitMode::Send => quote!(#[async_trait::async_trait]#item),
|
||||
AsyncTraitMode::NotSend => quote!(#[async_trait::async_trait(?Send)]#item),
|
||||
AsyncTraitMode::Off => quote!(#item),
|
||||
}
|
||||
}
|
||||
Item::Fn(item) => quote!(#item),
|
||||
Item::Static(item) => quote!(#item),
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_sync(input: &mut Item) -> TokenStream2 {
|
||||
match input {
|
||||
Item::Impl(item) => {
|
||||
for inner in &mut item.items {
|
||||
if let ImplItem::Fn(ref mut method) = inner {
|
||||
if method.sig.asyncness.is_some() {
|
||||
method.sig.asyncness = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
AsyncAwaitRemoval.remove_async_await(quote!(#item))
|
||||
}
|
||||
Item::Trait(item) => {
|
||||
for inner in &mut item.items {
|
||||
if let TraitItem::Fn(ref mut method) = inner {
|
||||
if method.sig.asyncness.is_some() {
|
||||
method.sig.asyncness = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
AsyncAwaitRemoval.remove_async_await(quote!(#item))
|
||||
}
|
||||
Item::Fn(item) => {
|
||||
if item.sig.asyncness.is_some() {
|
||||
item.sig.asyncness = None;
|
||||
}
|
||||
AsyncAwaitRemoval.remove_async_await(quote!(#item))
|
||||
}
|
||||
Item::Static(item) => AsyncAwaitRemoval.remove_async_await(quote!(#item)),
|
||||
}
|
||||
}
|
||||
|
||||
fn async_mode(arg: &str) -> Result<AsyncTraitMode> {
|
||||
match arg {
|
||||
"" | "Send" => Ok(AsyncTraitMode::Send),
|
||||
"?Send" => Ok(AsyncTraitMode::NotSend),
|
||||
// acronym for Async Function in Trait,
|
||||
// TODO make AFIT as default in future release
|
||||
"AFIT" => Ok(AsyncTraitMode::Off),
|
||||
_ => Err(syn::Error::new(
|
||||
Span::call_site(),
|
||||
"Only accepts `Send`, `?Send` or `AFIT` (native async function in trait)",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
/// maybe_async attribute macro
|
||||
///
|
||||
/// Can be applied to trait item, trait impl, functions and struct impls.
|
||||
#[proc_macro_attribute]
|
||||
pub fn maybe_async(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
let mode = match async_mode(args.to_string().replace(" ", "").as_str()) {
|
||||
Ok(m) => m,
|
||||
Err(e) => return e.to_compile_error().into(),
|
||||
};
|
||||
let mut item = parse_macro_input!(input as Item);
|
||||
|
||||
let token = if cfg!(feature = "is_sync") {
|
||||
convert_sync(&mut item)
|
||||
} else {
|
||||
convert_async(&mut item, mode)
|
||||
};
|
||||
token.into()
|
||||
}
|
||||
|
||||
/// convert marked async code to async code with `async-trait`
|
||||
#[proc_macro_attribute]
|
||||
pub fn must_be_async(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
let mode = match async_mode(args.to_string().replace(" ", "").as_str()) {
|
||||
Ok(m) => m,
|
||||
Err(e) => return e.to_compile_error().into(),
|
||||
};
|
||||
let mut item = parse_macro_input!(input as Item);
|
||||
convert_async(&mut item, mode).into()
|
||||
}
|
||||
|
||||
/// convert marked async code to sync code
|
||||
#[proc_macro_attribute]
|
||||
pub fn must_be_sync(_args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
let mut item = parse_macro_input!(input as Item);
|
||||
convert_sync(&mut item).into()
|
||||
}
|
||||
|
||||
/// mark sync implementation
|
||||
///
|
||||
/// only compiled when `is_sync` feature gate is set.
|
||||
/// When `is_sync` is not set, marked code is removed.
|
||||
#[proc_macro_attribute]
|
||||
pub fn sync_impl(_args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
let input = TokenStream2::from(input);
|
||||
let token = if cfg!(feature = "is_sync") {
|
||||
quote!(#input)
|
||||
} else {
|
||||
quote!()
|
||||
};
|
||||
token.into()
|
||||
}
|
||||
|
||||
/// mark async implementation
|
||||
///
|
||||
/// only compiled when `is_sync` feature gate is not set.
|
||||
/// When `is_sync` is set, marked code is removed.
|
||||
#[proc_macro_attribute]
|
||||
pub fn async_impl(args: TokenStream, _input: TokenStream) -> TokenStream {
|
||||
let mode = match async_mode(args.to_string().replace(" ", "").as_str()) {
|
||||
Ok(m) => m,
|
||||
Err(e) => return e.to_compile_error().into(),
|
||||
};
|
||||
let token = if cfg!(feature = "is_sync") {
|
||||
quote!()
|
||||
} else {
|
||||
let mut item = parse_macro_input!(_input as Item);
|
||||
convert_async(&mut item, mode)
|
||||
};
|
||||
token.into()
|
||||
}
|
||||
|
||||
fn parse_nested_meta_or_str(input: ParseStream) -> Result<TokenStream2> {
|
||||
if let Some(s) = input.parse::<Option<LitStr>>()? {
|
||||
let tokens = s.value().parse()?;
|
||||
Ok(tokens)
|
||||
} else {
|
||||
let meta: Meta = input.parse()?;
|
||||
Ok(quote!(#meta))
|
||||
}
|
||||
}
|
||||
|
||||
/// Handy macro to unify test code of sync and async code
|
||||
///
|
||||
/// Since the API of both sync and async code are the same,
|
||||
/// with only difference that async functions must be awaited.
|
||||
/// So it's tedious to write unit sync and async respectively.
|
||||
///
|
||||
/// This macro helps unify the sync and async unit test code.
|
||||
/// Pass the condition to treat test code as sync as the first
|
||||
/// argument. And specify the condition when to treat test code
|
||||
/// as async and the lib to run async test, e.x. `async-std::test`,
|
||||
/// `tokio::test`, or any valid attribute macro.
|
||||
///
|
||||
/// **ATTENTION**: do not write await inside a assert macro
|
||||
///
|
||||
/// - Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// #[maybe_async::maybe_async]
|
||||
/// async fn async_fn() -> bool {
|
||||
/// true
|
||||
/// }
|
||||
///
|
||||
/// #[maybe_async::test(
|
||||
/// // when to treat the test code as sync version
|
||||
/// feature="is_sync",
|
||||
/// // when to run async test
|
||||
/// async(all(not(feature="is_sync"), feature="async_std"), async_std::test),
|
||||
/// // you can specify multiple conditions for different async runtime
|
||||
/// async(all(not(feature="is_sync"), feature="tokio"), tokio::test)
|
||||
/// )]
|
||||
/// async fn test_async_fn() {
|
||||
/// let res = async_fn().await;
|
||||
/// assert_eq!(res, true);
|
||||
/// }
|
||||
///
|
||||
/// // Only run test in sync version
|
||||
/// #[maybe_async::test(feature = "is_sync")]
|
||||
/// async fn test_sync_fn() {
|
||||
/// let res = async_fn().await;
|
||||
/// assert_eq!(res, true);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// The above code is transcripted to the following code:
|
||||
///
|
||||
/// ```rust
|
||||
/// # use maybe_async::{must_be_async, must_be_sync, sync_impl};
|
||||
/// # #[maybe_async::maybe_async]
|
||||
/// # async fn async_fn() -> bool { true }
|
||||
///
|
||||
/// // convert to sync version when sync condition is met, keep in async version when corresponding
|
||||
/// // condition is met
|
||||
/// #[cfg_attr(feature = "is_sync", must_be_sync, test)]
|
||||
/// #[cfg_attr(
|
||||
/// all(not(feature = "is_sync"), feature = "async_std"),
|
||||
/// must_be_async,
|
||||
/// async_std::test
|
||||
/// )]
|
||||
/// #[cfg_attr(
|
||||
/// all(not(feature = "is_sync"), feature = "tokio"),
|
||||
/// must_be_async,
|
||||
/// tokio::test
|
||||
/// )]
|
||||
/// async fn test_async_fn() {
|
||||
/// let res = async_fn().await;
|
||||
/// assert_eq!(res, true);
|
||||
/// }
|
||||
///
|
||||
/// // force converted to sync function, and only compile on sync condition
|
||||
/// #[cfg(feature = "is_sync")]
|
||||
/// #[test]
|
||||
/// fn test_sync_fn() {
|
||||
/// let res = async_fn();
|
||||
/// assert_eq!(res, true);
|
||||
/// }
|
||||
/// ```
|
||||
#[proc_macro_attribute]
|
||||
pub fn test(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
match parse_test_cfg.parse(args) {
|
||||
Ok(test_cfg) => [test_cfg.into(), input].into_iter().collect(),
|
||||
Err(err) => err.to_compile_error().into(),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_test_cfg(input: ParseStream) -> Result<TokenStream2> {
|
||||
if input.is_empty() {
|
||||
return Err(syn::Error::new(
|
||||
Span::call_site(),
|
||||
"Arguments cannot be empty, at least specify the condition for sync code",
|
||||
));
|
||||
}
|
||||
|
||||
// The first attributes indicates sync condition
|
||||
let sync_cond = input.call(parse_nested_meta_or_str)?;
|
||||
let mut ts = quote!(#[cfg_attr(#sync_cond, maybe_async::must_be_sync, test)]);
|
||||
|
||||
// The rest attributes indicates async condition and async test macro
|
||||
// only accepts in the forms of `async(cond, test_macro)`, but `cond` and
|
||||
// `test_macro` can be either meta attributes or string literal
|
||||
let mut async_conditions = Vec::new();
|
||||
while !input.is_empty() {
|
||||
input.parse::<Token![,]>()?;
|
||||
if input.is_empty() {
|
||||
break;
|
||||
}
|
||||
|
||||
if !input.peek(Ident::peek_any) {
|
||||
return Err(
|
||||
input.error("Must be list of metas like: `async(condition, async_test_macro)`")
|
||||
);
|
||||
}
|
||||
let name = input.call(Ident::parse_any)?;
|
||||
if name != "async" {
|
||||
return Err(syn::Error::new(
|
||||
name.span(),
|
||||
format!("Unknown path: `{}`, must be `async`", name),
|
||||
));
|
||||
}
|
||||
|
||||
if !input.peek(token::Paren) {
|
||||
return Err(
|
||||
input.error("Must be list of metas like: `async(condition, async_test_macro)`")
|
||||
);
|
||||
}
|
||||
|
||||
let nested;
|
||||
parenthesized!(nested in input);
|
||||
let list = nested.parse_terminated(parse_nested_meta_or_str, Token![,])?;
|
||||
let len = list.len();
|
||||
let mut iter = list.into_iter();
|
||||
let (Some(async_cond), Some(async_test), None) = (iter.next(), iter.next(), iter.next())
|
||||
else {
|
||||
let msg = format!(
|
||||
"Must pass two metas or string literals like `async(condition, \
|
||||
async_test_macro)`, you passed {len} metas.",
|
||||
);
|
||||
return Err(syn::Error::new(name.span(), msg));
|
||||
};
|
||||
|
||||
let attr = quote!(
|
||||
#[cfg_attr(#async_cond, maybe_async::must_be_async, #async_test)]
|
||||
);
|
||||
async_conditions.push(async_cond);
|
||||
ts.extend(attr);
|
||||
}
|
||||
|
||||
Ok(if !async_conditions.is_empty() {
|
||||
quote! {
|
||||
#[cfg(any(#sync_cond, #(#async_conditions),*))]
|
||||
#ts
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
#[cfg(#sync_cond)]
|
||||
#ts
|
||||
}
|
||||
})
|
||||
}
|
||||
43
third_party/rust/maybe-async/src/parse.rs
vendored
43
third_party/rust/maybe-async/src/parse.rs
vendored
@@ -1,43 +0,0 @@
|
||||
use proc_macro2::Span;
|
||||
use syn::{
|
||||
parse::{discouraged::Speculative, Parse, ParseStream, Result},
|
||||
Attribute, Error, ItemFn, ItemImpl, ItemStatic, ItemTrait,
|
||||
};
|
||||
|
||||
pub enum Item {
|
||||
Trait(ItemTrait),
|
||||
Impl(ItemImpl),
|
||||
Fn(ItemFn),
|
||||
Static(ItemStatic),
|
||||
}
|
||||
|
||||
macro_rules! fork {
|
||||
($fork:ident = $input:ident) => {{
|
||||
$fork = $input.fork();
|
||||
&$fork
|
||||
}};
|
||||
}
|
||||
|
||||
impl Parse for Item {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let attrs = input.call(Attribute::parse_outer)?;
|
||||
let mut fork;
|
||||
let item = if let Ok(mut item) = fork!(fork = input).parse::<ItemImpl>() {
|
||||
item.attrs = attrs;
|
||||
Item::Impl(item)
|
||||
} else if let Ok(mut item) = fork!(fork = input).parse::<ItemTrait>() {
|
||||
item.attrs = attrs;
|
||||
Item::Trait(item)
|
||||
} else if let Ok(mut item) = fork!(fork = input).parse::<ItemFn>() {
|
||||
item.attrs = attrs;
|
||||
Item::Fn(item)
|
||||
} else if let Ok(mut item) = fork!(fork = input).parse::<ItemStatic>() {
|
||||
item.attrs = attrs;
|
||||
Item::Static(item)
|
||||
} else {
|
||||
return Err(Error::new(Span::call_site(), "expected impl, trait or fn"));
|
||||
};
|
||||
input.advance_to(&fork);
|
||||
Ok(item)
|
||||
}
|
||||
}
|
||||
187
third_party/rust/maybe-async/src/visit.rs
vendored
187
third_party/rust/maybe-async/src/visit.rs
vendored
@@ -1,187 +0,0 @@
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{
|
||||
visit_mut::{self, visit_item_mut, visit_path_segment_mut, VisitMut},
|
||||
Expr, ExprBlock, File, GenericArgument, GenericParam, Item, PathArguments, PathSegment, Stmt,
|
||||
Type, TypeParamBound, WherePredicate,
|
||||
};
|
||||
|
||||
pub struct ReplaceGenericType<'a> {
|
||||
generic_type: &'a str,
|
||||
arg_type: &'a PathSegment,
|
||||
}
|
||||
|
||||
impl<'a> ReplaceGenericType<'a> {
|
||||
pub fn new(generic_type: &'a str, arg_type: &'a PathSegment) -> Self {
|
||||
Self {
|
||||
generic_type,
|
||||
arg_type,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn replace_generic_type(item: &mut Item, generic_type: &'a str, arg_type: &'a PathSegment) {
|
||||
let mut s = Self::new(generic_type, arg_type);
|
||||
s.visit_item_mut(item);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> VisitMut for ReplaceGenericType<'a> {
|
||||
fn visit_item_mut(&mut self, i: &mut Item) {
|
||||
if let Item::Fn(item_fn) = i {
|
||||
// remove generic type from generics <T, F>
|
||||
let args = item_fn
|
||||
.sig
|
||||
.generics
|
||||
.params
|
||||
.iter()
|
||||
.filter_map(|param| {
|
||||
if let GenericParam::Type(type_param) = ¶m {
|
||||
if type_param.ident.to_string().eq(self.generic_type) {
|
||||
None
|
||||
} else {
|
||||
Some(param)
|
||||
}
|
||||
} else {
|
||||
Some(param)
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
item_fn.sig.generics.params = args.into_iter().cloned().collect();
|
||||
|
||||
// remove generic type from where clause
|
||||
if let Some(where_clause) = &mut item_fn.sig.generics.where_clause {
|
||||
let new_where_clause = where_clause
|
||||
.predicates
|
||||
.iter()
|
||||
.filter_map(|predicate| {
|
||||
if let WherePredicate::Type(predicate_type) = predicate {
|
||||
if let Type::Path(p) = &predicate_type.bounded_ty {
|
||||
if p.path.segments[0].ident.to_string().eq(self.generic_type) {
|
||||
None
|
||||
} else {
|
||||
Some(predicate)
|
||||
}
|
||||
} else {
|
||||
Some(predicate)
|
||||
}
|
||||
} else {
|
||||
Some(predicate)
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
where_clause.predicates = new_where_clause.into_iter().cloned().collect();
|
||||
};
|
||||
}
|
||||
visit_item_mut(self, i)
|
||||
}
|
||||
fn visit_path_segment_mut(&mut self, i: &mut PathSegment) {
|
||||
// replace generic type with target type
|
||||
if i.ident.to_string().eq(&self.generic_type) {
|
||||
*i = self.arg_type.clone();
|
||||
}
|
||||
visit_path_segment_mut(self, i);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AsyncAwaitRemoval;
|
||||
|
||||
impl AsyncAwaitRemoval {
|
||||
pub fn remove_async_await(&mut self, item: TokenStream) -> TokenStream {
|
||||
let mut syntax_tree: File = syn::parse(item.into()).unwrap();
|
||||
self.visit_file_mut(&mut syntax_tree);
|
||||
quote!(#syntax_tree)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitMut for AsyncAwaitRemoval {
|
||||
fn visit_expr_mut(&mut self, node: &mut Expr) {
|
||||
// Delegate to the default impl to visit nested expressions.
|
||||
visit_mut::visit_expr_mut(self, node);
|
||||
|
||||
match node {
|
||||
Expr::Await(expr) => *node = (*expr.base).clone(),
|
||||
|
||||
Expr::Async(expr) => {
|
||||
let inner = &expr.block;
|
||||
let sync_expr = if let [Stmt::Expr(expr, None)] = inner.stmts.as_slice() {
|
||||
// remove useless braces when there is only one statement
|
||||
expr.clone()
|
||||
} else {
|
||||
Expr::Block(ExprBlock {
|
||||
attrs: expr.attrs.clone(),
|
||||
block: inner.clone(),
|
||||
label: None,
|
||||
})
|
||||
};
|
||||
*node = sync_expr;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_item_mut(&mut self, i: &mut Item) {
|
||||
// find generic parameter of Future and replace it with its Output type
|
||||
if let Item::Fn(item_fn) = i {
|
||||
let mut inputs: Vec<(String, PathSegment)> = vec![];
|
||||
|
||||
// generic params: <T:Future<Output=()>, F>
|
||||
for param in &item_fn.sig.generics.params {
|
||||
// generic param: T:Future<Output=()>
|
||||
if let GenericParam::Type(type_param) = param {
|
||||
let generic_type_name = type_param.ident.to_string();
|
||||
|
||||
// bound: Future<Output=()>
|
||||
for bound in &type_param.bounds {
|
||||
inputs.extend(search_trait_bound(&generic_type_name, bound));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(where_clause) = &item_fn.sig.generics.where_clause {
|
||||
for predicate in &where_clause.predicates {
|
||||
if let WherePredicate::Type(predicate_type) = predicate {
|
||||
let generic_type_name = if let Type::Path(p) = &predicate_type.bounded_ty {
|
||||
p.path.segments[0].ident.to_string()
|
||||
} else {
|
||||
panic!("Please submit an issue");
|
||||
};
|
||||
|
||||
for bound in &predicate_type.bounds {
|
||||
inputs.extend(search_trait_bound(&generic_type_name, bound));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (generic_type_name, path_seg) in &inputs {
|
||||
ReplaceGenericType::replace_generic_type(i, generic_type_name, path_seg);
|
||||
}
|
||||
}
|
||||
visit_item_mut(self, i);
|
||||
}
|
||||
}
|
||||
|
||||
fn search_trait_bound(
|
||||
generic_type_name: &str,
|
||||
bound: &TypeParamBound,
|
||||
) -> Vec<(String, PathSegment)> {
|
||||
let mut inputs = vec![];
|
||||
|
||||
if let TypeParamBound::Trait(trait_bound) = bound {
|
||||
let segment = &trait_bound.path.segments[trait_bound.path.segments.len() - 1];
|
||||
let name = segment.ident.to_string();
|
||||
if name.eq("Future") {
|
||||
// match Future<Output=Type>
|
||||
if let PathArguments::AngleBracketed(args) = &segment.arguments {
|
||||
// binding: Output=Type
|
||||
if let GenericArgument::AssocType(binding) = &args.args[0] {
|
||||
if let Type::Path(p) = &binding.ty {
|
||||
inputs.push((generic_type_name.to_owned(), p.path.segments[0].clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
inputs
|
||||
}
|
||||
15
third_party/rust/maybe-async/tests/test.rs
vendored
15
third_party/rust/maybe-async/tests/test.rs
vendored
@@ -1,15 +0,0 @@
|
||||
#[test]
|
||||
fn ui() {
|
||||
let t = trybuild::TestCases::new();
|
||||
t.pass("tests/ui/01-maybe-async.rs");
|
||||
t.pass("tests/ui/02-must-be-async.rs");
|
||||
t.pass("tests/ui/03-must-be-sync.rs");
|
||||
t.pass("tests/ui/04-unit-test-util.rs");
|
||||
t.pass("tests/ui/05-replace-future-generic-type-with-output.rs");
|
||||
t.pass("tests/ui/06-sync_impl_async_impl.rs");
|
||||
|
||||
t.compile_fail("tests/ui/test_fail/01-empty-test.rs");
|
||||
t.compile_fail("tests/ui/test_fail/02-unknown-path.rs");
|
||||
t.compile_fail("tests/ui/test_fail/03-async-gt2.rs");
|
||||
t.compile_fail("tests/ui/test_fail/04-bad-sync-cond.rs");
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use maybe_async::maybe_async;
|
||||
|
||||
#[maybe_async(Send)]
|
||||
trait Trait {
|
||||
fn sync_fn() {}
|
||||
|
||||
async fn declare_async(&self);
|
||||
|
||||
async fn async_fn(&self) {
|
||||
self.declare_async().await
|
||||
}
|
||||
}
|
||||
|
||||
#[maybe_async(?Send)]
|
||||
pub trait PubTrait {
|
||||
fn sync_fn() {}
|
||||
|
||||
async fn declare_async(&self);
|
||||
|
||||
async fn async_fn(&self) {
|
||||
self.declare_async().await
|
||||
}
|
||||
}
|
||||
|
||||
#[maybe_async]
|
||||
pub(crate) trait PubCrateTrait {
|
||||
fn sync_fn() {}
|
||||
|
||||
async fn declare_async(&self);
|
||||
|
||||
async fn async_fn(&self) {
|
||||
self.declare_async().await
|
||||
}
|
||||
}
|
||||
|
||||
#[maybe_async(AFIT)]
|
||||
trait AfitTrait {
|
||||
fn sync_fn_afit() {}
|
||||
|
||||
async fn declare_async_afit(&self);
|
||||
|
||||
async fn async_fn_afit(&self) {
|
||||
self.declare_async_afit().await
|
||||
}
|
||||
}
|
||||
|
||||
#[maybe_async]
|
||||
async fn async_fn() {}
|
||||
|
||||
#[maybe_async]
|
||||
pub async fn pub_async_fn() {}
|
||||
|
||||
#[maybe_async]
|
||||
pub(crate) async fn pub_crate_async_fn() {}
|
||||
|
||||
#[maybe_async]
|
||||
unsafe fn unsafe_fn() {}
|
||||
|
||||
struct Struct;
|
||||
|
||||
#[maybe_async]
|
||||
impl Struct {
|
||||
fn sync_fn_inherent() {}
|
||||
|
||||
async fn declare_async_inherent(&self) {}
|
||||
|
||||
async fn async_fn_inherent(&self) {
|
||||
async { self.declare_async_inherent().await }.await
|
||||
}
|
||||
}
|
||||
|
||||
#[maybe_async]
|
||||
impl Trait for Struct {
|
||||
fn sync_fn() {}
|
||||
|
||||
async fn declare_async(&self) {}
|
||||
|
||||
async fn async_fn(&self) {
|
||||
async { self.declare_async().await }.await
|
||||
}
|
||||
}
|
||||
|
||||
#[maybe_async(AFIT)]
|
||||
impl AfitTrait for Struct {
|
||||
fn sync_fn_afit() {}
|
||||
|
||||
async fn declare_async_afit(&self) {}
|
||||
|
||||
async fn async_fn_afit(&self) {
|
||||
async { self.declare_async_afit().await }.await
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "is_sync")]
|
||||
fn main() -> std::result::Result<(), ()> {
|
||||
let s = Struct;
|
||||
s.declare_async_inherent();
|
||||
s.async_fn_inherent();
|
||||
s.declare_async();
|
||||
s.async_fn();
|
||||
s.declare_async_afit();
|
||||
s.async_fn_afit();
|
||||
async_fn();
|
||||
pub_async_fn();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "is_sync"))]
|
||||
#[async_std::main]
|
||||
async fn main() {
|
||||
let s = Struct;
|
||||
s.declare_async_inherent().await;
|
||||
s.async_fn_inherent().await;
|
||||
s.declare_async().await;
|
||||
s.async_fn().await;
|
||||
s.declare_async_afit().await;
|
||||
s.async_fn_afit().await;
|
||||
async_fn().await;
|
||||
pub_async_fn().await;
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
#[maybe_async::maybe_async]
|
||||
trait Trait {
|
||||
fn sync_fn() {}
|
||||
|
||||
async fn declare_async(&self);
|
||||
|
||||
async fn async_fn(&self) {
|
||||
self.declare_async().await
|
||||
}
|
||||
}
|
||||
|
||||
#[maybe_async::maybe_async(?Send)]
|
||||
trait NotSendTrait {
|
||||
async fn declare_async_not_send(&self);
|
||||
|
||||
async fn async_fn_not_send(&self) {
|
||||
self.declare_async_not_send().await
|
||||
}
|
||||
}
|
||||
|
||||
#[maybe_async::maybe_async]
|
||||
pub trait PubTrait {
|
||||
fn sync_fn() {}
|
||||
|
||||
async fn declare_async(&self);
|
||||
|
||||
async fn async_fn(&self) {
|
||||
self.declare_async().await
|
||||
}
|
||||
}
|
||||
|
||||
#[maybe_async::maybe_async]
|
||||
pub(crate) trait PubCrateTrait {
|
||||
fn sync_fn() {}
|
||||
|
||||
async fn declare_async(&self);
|
||||
|
||||
async fn async_fn(&self) {
|
||||
self.declare_async().await
|
||||
}
|
||||
}
|
||||
|
||||
#[maybe_async::maybe_async(AFIT)]
|
||||
trait AfitTrait {
|
||||
fn sync_fn_afit() {}
|
||||
|
||||
async fn declare_async_afit(&self);
|
||||
|
||||
async fn async_fn_afit(&self) {
|
||||
self.declare_async_afit().await
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "is_sync"))]
|
||||
#[maybe_async::must_be_async]
|
||||
async fn async_fn() {}
|
||||
|
||||
#[cfg(not(feature = "is_sync"))]
|
||||
#[maybe_async::must_be_async]
|
||||
pub async fn pub_async_fn() {}
|
||||
|
||||
#[cfg(not(feature = "is_sync"))]
|
||||
#[maybe_async::maybe_async]
|
||||
pub(crate) async fn pub_crate_async_fn() {}
|
||||
|
||||
#[cfg(not(feature = "is_sync"))]
|
||||
#[maybe_async::maybe_async]
|
||||
unsafe fn unsafe_fn() {}
|
||||
|
||||
struct Struct;
|
||||
|
||||
#[cfg(not(feature = "is_sync"))]
|
||||
#[maybe_async::must_be_async]
|
||||
impl Struct {
|
||||
fn sync_fn_inherent() {}
|
||||
|
||||
async fn declare_async_inherent(&self) {}
|
||||
|
||||
async fn async_fn_inherent(&self) {
|
||||
async { self.declare_async_inherent().await }.await
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "is_sync"))]
|
||||
#[maybe_async::must_be_async]
|
||||
impl Trait for Struct {
|
||||
fn sync_fn() {}
|
||||
|
||||
async fn declare_async(&self) {}
|
||||
|
||||
async fn async_fn(&self) {
|
||||
async { self.declare_async().await }.await
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "is_sync"))]
|
||||
#[maybe_async::must_be_async(?Send)]
|
||||
impl NotSendTrait for Struct {
|
||||
async fn declare_async_not_send(&self) {}
|
||||
|
||||
async fn async_fn_not_send(&self) {
|
||||
async { self.declare_async_not_send().await }.await
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "is_sync"))]
|
||||
#[maybe_async::must_be_async(AFIT)]
|
||||
impl AfitTrait for Struct {
|
||||
fn sync_fn_afit() {}
|
||||
|
||||
async fn declare_async_afit(&self) {}
|
||||
|
||||
async fn async_fn_afit(&self) {
|
||||
async { self.declare_async_afit().await }.await
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "is_sync")]
|
||||
fn main() {}
|
||||
|
||||
#[cfg(not(feature = "is_sync"))]
|
||||
#[async_std::main]
|
||||
async fn main() {
|
||||
let s = Struct;
|
||||
s.declare_async_inherent().await;
|
||||
s.async_fn_inherent().await;
|
||||
s.declare_async().await;
|
||||
s.async_fn().await;
|
||||
s.declare_async_afit().await;
|
||||
s.async_fn_afit().await;
|
||||
async_fn().await;
|
||||
pub_async_fn().await;
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
#[maybe_async::maybe_async]
|
||||
trait Trait {
|
||||
fn sync_fn() {}
|
||||
|
||||
async fn declare_async(&self);
|
||||
|
||||
async fn async_fn(&self) {
|
||||
self.declare_async().await
|
||||
}
|
||||
}
|
||||
|
||||
#[maybe_async::maybe_async]
|
||||
pub trait PubTrait {
|
||||
fn sync_fn() {}
|
||||
|
||||
async fn declare_async(&self);
|
||||
|
||||
async fn async_fn(&self) {
|
||||
self.declare_async().await
|
||||
}
|
||||
}
|
||||
|
||||
#[maybe_async::maybe_async]
|
||||
pub(crate) trait PubCrateTrait {
|
||||
fn sync_fn() {}
|
||||
|
||||
async fn declare_async(&self);
|
||||
|
||||
async fn async_fn(&self) {
|
||||
self.declare_async().await
|
||||
}
|
||||
}
|
||||
|
||||
#[maybe_async::maybe_async]
|
||||
async fn async_fn() {}
|
||||
|
||||
#[maybe_async::maybe_async]
|
||||
pub async fn pub_async_fn() {}
|
||||
|
||||
#[maybe_async::maybe_async]
|
||||
pub(crate) async fn pub_crate_async_fn() {}
|
||||
|
||||
#[maybe_async::maybe_async]
|
||||
unsafe fn unsafe_fn() {}
|
||||
|
||||
struct Struct;
|
||||
|
||||
#[cfg(feature = "is_sync")]
|
||||
#[maybe_async::must_be_sync]
|
||||
impl Struct {
|
||||
fn sync_fn_inherent() {}
|
||||
|
||||
async fn declare_async_inherent(&self) {}
|
||||
|
||||
async fn async_fn_inherent(&self) {
|
||||
async { self.declare_async_inherent().await }.await
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "is_sync")]
|
||||
#[maybe_async::must_be_sync]
|
||||
impl Trait for Struct {
|
||||
fn sync_fn() {}
|
||||
|
||||
async fn declare_async(&self) {}
|
||||
|
||||
async fn async_fn(&self) {
|
||||
async { self.declare_async().await }.await
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "is_sync")]
|
||||
fn main() -> std::result::Result<(), ()> {
|
||||
let s = Struct;
|
||||
s.declare_async_inherent();
|
||||
s.async_fn_inherent();
|
||||
s.declare_async();
|
||||
s.async_fn();
|
||||
async_fn();
|
||||
pub_async_fn();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "is_sync"))]
|
||||
#[async_std::main]
|
||||
async fn main() {}
|
||||
@@ -1,38 +0,0 @@
|
||||
use maybe_async::maybe_async;
|
||||
|
||||
#[maybe_async]
|
||||
async fn async_fn() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[maybe_async::test(
|
||||
feature = "is_sync",
|
||||
async(all(not(feature="is_sync"), feature = "async_std"), async_std::test),
|
||||
async(all(not(feature="is_sync"), feature = "tokio"), tokio::test)
|
||||
)]
|
||||
async fn test_async_fn() {
|
||||
let res = async_fn().await;
|
||||
assert_eq!(res, true);
|
||||
}
|
||||
|
||||
#[maybe_async::test(feature = "is_sync", async(not(feature = "is_sync"), async_std::test))]
|
||||
async fn test_async_fn2() {
|
||||
let res = async_fn().await;
|
||||
assert_eq!(res, true);
|
||||
}
|
||||
|
||||
#[maybe_async::test("feature=\"is_sync\"", async(not(feature = "is_sync"), async_std::test))]
|
||||
async fn test_async_fn3() {
|
||||
let res = async_fn().await;
|
||||
assert_eq!(res, true);
|
||||
}
|
||||
|
||||
#[maybe_async::test(feature = "is_sync", async("not(feature = \"is_sync\")", "async_std::test"))]
|
||||
async fn test_async_fn4() {
|
||||
let res = async_fn().await;
|
||||
assert_eq!(res, true);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
#![allow(unused_imports)]
|
||||
use std::future::Future;
|
||||
|
||||
#[maybe_async::maybe_async]
|
||||
pub async fn with_fn<T, F: Sync + std::future::Future<Output = Result<(), ()>>>(
|
||||
test: T,
|
||||
) -> Result<(), ()>
|
||||
where
|
||||
T: FnOnce() -> F,
|
||||
{
|
||||
test().await
|
||||
}
|
||||
|
||||
#[maybe_async::maybe_async]
|
||||
pub async fn with_fn_where<T, F>(test: T) -> Result<(), ()>
|
||||
where
|
||||
T: FnOnce() -> F,
|
||||
F: Sync + Future<Output = Result<(), ()>>,
|
||||
{
|
||||
test().await
|
||||
}
|
||||
|
||||
#[maybe_async::sync_impl]
|
||||
fn main() {
|
||||
with_fn(|| Ok(())).unwrap();
|
||||
with_fn_where(|| Ok(())).unwrap();
|
||||
}
|
||||
|
||||
#[maybe_async::async_impl]
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
with_fn(|| async { Ok(()) }).await.unwrap();
|
||||
with_fn_where(|| async { Ok(()) }).await.unwrap();
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
#![allow(dead_code, unused_variables)]
|
||||
|
||||
/// InnerClient differ a lot between sync and async.
|
||||
#[maybe_async::maybe_async]
|
||||
trait Trait {
|
||||
async fn maybe_async_fn();
|
||||
}
|
||||
|
||||
/// The higher level API for end user.
|
||||
pub struct Struct;
|
||||
|
||||
/// Synchronous implementation, only compiles when `is_sync` feature is off.
|
||||
/// Else the compiler will complain that *request is defined multiple times* and
|
||||
/// blabla.
|
||||
#[maybe_async::sync_impl]
|
||||
impl Trait for Struct {
|
||||
fn maybe_async_fn() { }
|
||||
}
|
||||
|
||||
/// Asynchronous implementation, only compiles when `is_sync` feature is off.
|
||||
#[maybe_async::async_impl]
|
||||
impl Trait for Struct {
|
||||
async fn maybe_async_fn() { }
|
||||
}
|
||||
|
||||
impl Struct {
|
||||
#[maybe_async::maybe_async]
|
||||
async fn another_maybe_async_fn() {
|
||||
Self::maybe_async_fn().await
|
||||
// When `is_sync` is toggle on, this block will compiles to:
|
||||
// Self::maybe_async_fn()
|
||||
}
|
||||
}
|
||||
|
||||
#[maybe_async::sync_impl]
|
||||
fn main() {
|
||||
let _ = Struct::another_maybe_async_fn();
|
||||
}
|
||||
|
||||
#[maybe_async::async_impl]
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let _ = Struct::another_maybe_async_fn().await;
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
use maybe_async::maybe_async;
|
||||
|
||||
#[maybe_async]
|
||||
async fn async_fn() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
// at least one sync condition should be specified
|
||||
#[maybe_async::test()]
|
||||
async fn test_async_fn() {
|
||||
let res = async_fn().await;
|
||||
assert_eq!(res, true);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
error: Arguments cannot be empty, at least specify the condition for sync code
|
||||
--> tests/ui/test_fail/01-empty-test.rs:9:1
|
||||
|
|
||||
9 | #[maybe_async::test()]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: this error originates in the attribute macro `maybe_async::test` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
@@ -1,17 +0,0 @@
|
||||
use maybe_async::maybe_async;
|
||||
|
||||
#[maybe_async]
|
||||
async fn async_fn() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
// should only accept `async`
|
||||
#[maybe_async::test(feature="is_sync", unknown(not(feature="is_sync"), async_std::test))]
|
||||
async fn test_async_fn() {
|
||||
let res = async_fn().await;
|
||||
assert_eq!(res, true);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
error: Unknown path: `unknown`, must be `async`
|
||||
--> tests/ui/test_fail/02-unknown-path.rs:9:40
|
||||
|
|
||||
9 | #[maybe_async::test(feature="is_sync", unknown(not(feature="is_sync"), async_std::test))]
|
||||
| ^^^^^^^
|
||||
@@ -1,16 +0,0 @@
|
||||
use maybe_async::maybe_async;
|
||||
|
||||
#[maybe_async]
|
||||
async fn async_fn() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[maybe_async::test(feature="is_sync", async(feature="async", async_std::test, added))]
|
||||
async fn test_async_fn() {
|
||||
let res = async_fn().await;
|
||||
assert_eq!(res, true);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
error: Must pass two metas or string literals like `async(condition, async_test_macro)`, you passed 3 metas.
|
||||
--> tests/ui/test_fail/03-async-gt2.rs:8:40
|
||||
|
|
||||
8 | #[maybe_async::test(feature="is_sync", async(feature="async", async_std::test, added))]
|
||||
| ^^^^^
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user